/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.views; import java.net.URL; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.internal.util.BundleUtility; import org.xmind.core.internal.dom.NumberUtils; import org.xmind.core.style.IStyle; import org.xmind.core.style.IStyleSheet; import org.xmind.gef.draw2d.geometry.Geometry; import org.xmind.gef.draw2d.geometry.PrecisionPoint; import org.xmind.gef.draw2d.geometry.PrecisionPointPair; import org.xmind.gef.draw2d.geometry.PrecisionRectangle; import org.xmind.gef.draw2d.graphics.GradientPattern; import org.xmind.gef.draw2d.graphics.GraphicsUtils; import org.xmind.gef.draw2d.graphics.Path; import org.xmind.ui.internal.svgsupport.SvgFileLoader; import org.xmind.ui.internal.svgsupport.SvgPathParser; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.resources.ColorUtils; import org.xmind.ui.resources.FontUtils; import org.xmind.ui.style.StyleUtils; import org.xmind.ui.style.Styles; public class StyleFigureUtils { public static final int BOUNDARY_STEP = 16; public static final int BOUNDARY_PADDING = 8; public static final float CALLOUT_RRECT_PARAM = 0.2f; public static final float CALLOUT_ELLIPSE_STARTANGLE = -130; public static final float CALLOUT_ELLIPSE_ARCANGLE = 345; public static final int SPINY_WIDTH = 2; public static final int ROUNDED_CORNER = 7; public static final int ROUNDED_CORNER_ADAPTER = 30; private static final IStyleSheet defaultStyles = MindMapUI .getResourceManager().getDefaultStyleSheet(); public static final IStyle defaultSheetStyle = findOrCreateDefaultStyle( Styles.FAMILY_MAP, IStyle.MAP); public static final IStyle defaultCentralStyle = findOrCreateDefaultStyle( Styles.FAMILY_CENTRAL_TOPIC, IStyle.TOPIC); public static final IStyle defaultMainStyle = findOrCreateDefaultStyle( Styles.FAMILY_MAIN_TOPIC, IStyle.TOPIC); public static final IStyle defaultRelationshipStyle = findOrCreateDefaultStyle( Styles.FAMILY_RELATIONSHIP, IStyle.RELATIONSHIP); private StyleFigureUtils() { } private static IStyle findOrCreateDefaultStyle(String family, String type) { IStyle style = defaultStyles.findStyle(family); if (style == null) style = defaultStyles.createStyle(type); return style; } public static void angledRel(Path shape, Rectangle relBounds, Point c1, Point c2) { int dx = relBounds.width / 8; int dy = relBounds.height / 8; shape.moveTo(relBounds.getBottomLeft()); c1.setLocation(relBounds.getCenter().translate(-dx, -dy)); shape.lineTo(c1); c2.setLocation(relBounds.getCenter().translate(dx, dy)); shape.lineTo(c2); shape.lineTo(relBounds.getTopRight()); } public static void calloutEllipse(Path shape, Rectangle r) { Rectangle outlineBox = r; shape.addArc(outlineBox.x, outlineBox.y, outlineBox.width, outlineBox.height, CALLOUT_ELLIPSE_STARTANGLE, CALLOUT_ELLIPSE_ARCANGLE); float h = outlineBox.height; shape.lineTo(outlineBox.x, outlineBox.y + h); shape.close(); } public static void calloutRoundRect(Path shape, Rectangle r) { Rectangle box = r; float x = box.x; float y = box.y; float w = box.width; float h = box.height; float dy = h - box.height / 4.0f; float c = getAppliedCorner(r); shape.moveTo(x + w * CALLOUT_RRECT_PARAM, y + dy); shape.lineTo(x + w - c, y + dy); shape.addArc(x + w - c, y + dy - c, c, c, -90, 90); shape.lineTo(x + w, y + c); shape.addArc(x + w - c, y, c, c, 0, 90); shape.lineTo(x + c, y); shape.addArc(x, y, c, c, 90, 90); shape.lineTo(x, y + dy - c); shape.addArc(x, y + dy - c, c, c, 180, 90); shape.lineTo(box.x, box.y + h); shape.close(); } protected static int getAppliedCorner(Rectangle r) { int t = Math.min(r.height, r.width); return ROUNDED_CORNER * t / ROUNDED_CORNER_ADAPTER; } public static void circle(Path shape, Rectangle r) { shape.addArc(r, 0, 360); } public static void parallelogram(Path shape, Rectangle r) { shape.moveTo(r.x + r.height * 0.5f, r.y); shape.lineTo(r.getBottomLeft()); shape.lineTo(r.right() - r.height * 0.5f, r.bottom()); shape.lineTo(r.getTopRight()); shape.close(); } public static void cloud(Path path, Rectangle r) { URL url = BundleUtility.find("org.xmind.ui", //$NON-NLS-1$ "shapes/topic-shape-cloud.svg"); //$NON-NLS-1$ SvgFileLoader loader = SvgFileLoader.getInstance(); String svgPath = loader.loadSvgFile(url); SvgPathParser parser = SvgPathParser.getInstance(); parser.parseSvgPath(path, r.getCenter().x, r.getCenter().y, r.width, r.height, svgPath); } public static void strokeCircle(Path path, Rectangle r) { URL url = BundleUtility.find("org.xmind.ui", //$NON-NLS-1$ "shapes/topic-shape-stroke-circle.svg"); //$NON-NLS-1$ SvgFileLoader loader = SvgFileLoader.getInstance(); String svgPath = loader.loadSvgFile(url); SvgPathParser parser = SvgPathParser.getInstance(); parser.parseSvgPath(path, r.getCenter().x, r.getCenter().y, r.width, r.height, svgPath); } public static void curvedRel(Path shape, Rectangle relBounds, Point c1, Point c2) { // int dx = -relBounds.width / 10; // int dy = relBounds.height / 10; // shape.moveTo(relBounds.getBottomLeft()); int dx = -relBounds.width / 4; int dy = relBounds.height / 4; shape.moveTo(relBounds.getTopLeft()); Point p1 = relBounds.getTop().translate(-dx, -dy); Point p2 = relBounds.getBottom().translate(dx, dy); shape.cubicTo(p1, p2, relBounds.getTopRight()); c1.setLocation(p1.translate(0, dy)); c2.setLocation(p2.translate(0, -dy)); } public static void diamondTopic(Path shape, Rectangle r) { Rectangle r2 = r; shape.moveTo(r2.getLeft()); shape.lineTo(r2.getBottom()); shape.lineTo(r2.getRight()); shape.lineTo(r2.getTop()); shape.close(); } public static void diamondArrow(Path shape, Point head, double angle, int lineWidth) { int side1 = lineWidth + 3; int side2 = lineWidth + 2; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle, side1); PrecisionPoint p2 = p.getMoved(angle - Math.PI / 2, side2); PrecisionPoint p3 = p.getMoved(angle + Math.PI, side1); PrecisionPoint p4 = p.getMoved(angle + Math.PI / 2, side2); shape.moveTo(p1); shape.lineTo(p2); shape.lineTo(p3); shape.lineTo(p4); shape.close(); } public static void dotArrow(Path shape, Point head, double angle, int lineWidth) { PrecisionRectangle bounds = new PrecisionRectangle(head.x, head.y, 0, 0) .expand(lineWidth, lineWidth); shape.addArc(bounds, 0, 360); } public static void drawArrow(Graphics graphics, String arrowValue, Point head, Point tail, int lineWidth) { Path shape = new Path(Display.getCurrent()); boolean fill = true; double angle = new PrecisionPoint(tail) .getAngle(new PrecisionPoint(head)); if (Styles.ARROW_SHAPE_DIAMOND.equals(arrowValue)) { diamondArrow(shape, head, angle, lineWidth); } else if (Styles.ARROW_SHAPE_DOT.equals(arrowValue)) { dotArrow(shape, head, angle, lineWidth); } else if (Styles.ARROW_SHAPE_HERRINGBONE.equals(arrowValue)) { herringBone(shape, head, angle, lineWidth); fill = false; } else if (Styles.ARROW_SHAPE_SPEARHEAD.equals(arrowValue)) { spearhead(shape, head, angle, lineWidth); } else if (Styles.ARROW_SHAPE_SQUARE.equals(arrowValue)) { square(shape, head, angle, lineWidth); } else if (Styles.ARROW_SHAPE_TRIANGLE.equals(arrowValue)) { triangle(shape, head, angle, lineWidth); } else { normalArrow(shape, head, angle, lineWidth); fill = false; } Color fgColor = graphics.getForegroundColor(); if (fgColor != null) { if (fill) { graphics.setBackgroundColor(fgColor); graphics.fillPath(shape); } graphics.drawPath(shape); } shape.dispose(); } public static void drawBoundary(Graphics graphics, Rectangle bounds, IStyle style, IStyle template) { drawBoundary(graphics, bounds, null, style, template); } public static void drawBoundary(Graphics graphics, Rectangle bounds, HashMap<String, String> existedStyle, IStyle style, IStyle template) { Rectangle boundaryBounds = boundaryBounds(bounds); Path shape = new Path(Display.getCurrent()); String shapeValue = getValue(existedStyle, Styles.ShapeClass, style, template); if (shapeValue == null || Styles.BOUNDARY_SHAPE_ROUNDEDRECT.equals(shapeValue)) { roundedRect(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_RECT.equals(shapeValue)) { rectangle(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_SCALLOPS.equals(shapeValue)) { scallops(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_TENSION.equals(shapeValue)) { tension(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_WAVES.equals(shapeValue)) { waves(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_POLYGON.equals(shapeValue)) { polygon(shape, boundaryBounds); } else if (Styles.BOUNDARY_SHAPE_ROUNDEDPOLYGON.equals(shapeValue)) { roundedPolygon(shape, boundaryBounds); } else { roundedRect(shape, boundaryBounds); } String fillColorValue = getValue(existedStyle, Styles.FillColor, style, template); if (fillColorValue != null) { Color fillColor = ColorUtils.getColor(fillColorValue); if (fillColor != null) { String opacityValue = getValue(existedStyle, Styles.Opacity, style, template); double opacity = NumberUtils.safeParseDouble(opacityValue, 1); int alpha = (int) (opacity * 0xff); graphics.setAlpha(alpha); graphics.setBackgroundColor(fillColor); graphics.fillPath(shape); } } Color lineColor = getLineColor(existedStyle, style, template, ColorConstants.gray); String lineWidthValue = getValue(existedStyle, Styles.LineWidth, style, template); lineWidthValue = StyleUtils.trimNumber(lineWidthValue); int lineWidth = NumberUtils.safeParseInt(lineWidthValue, 3); graphics.setLineWidth(lineWidth); String linePatternValue = getValue(existedStyle, Styles.LinePattern, style, template); int linePattern = StyleUtils.toSWTLineStyle(linePatternValue, SWT.LINE_DASH); graphics.setLineStyle(linePattern); graphics.setAlpha(0xff); graphics.setForegroundColor(lineColor); graphics.drawPath(shape); shape.dispose(); } public static void drawMainBranches(Graphics graphics, Rectangle bounds, boolean spiny, boolean rainbow) { PrecisionPoint center = new PrecisionPoint(bounds.getCenter()); double length = Math.min(bounds.width, bounds.height) / 3; for (int i = 0; i < 6; i++) { double angle = Math.PI * (i - 1) / 3; PrecisionPoint p = center.getMoved(angle, length); if (p.y < center.y) p.y += (center.y - p.y) / 6; else if (p.y > center.y) p.y -= (p.y - center.y) / 6; if (Math.abs(p.y - center.y) > 0.000001) { if (p.x < center.x) p.x -= (center.x - p.x) / 6; else if (p.y > center.x) p.x += (p.x - center.x) / 6; } Color c = rainbow ? ColorUtils.getRainbowColor(i, 6) : ColorConstants.gray; graphics.setAlpha(0xff); graphics.setForegroundColor(c); graphics.setLineWidth(1); graphics.setLineStyle(SWT.LINE_SOLID); if (spiny) { PrecisionPoint c1 = center.getMoved(angle + Math.PI / 3, SPINY_WIDTH); PrecisionPoint c2 = center.getMoved(angle - Math.PI / 3, SPINY_WIDTH); Path shape = new Path(Display.getCurrent()); shape.moveTo(p); shape.lineTo(c1); shape.lineTo(c2); shape.close(); graphics.setBackgroundColor(c); graphics.fillPath(shape); graphics.drawPath(shape); shape.dispose(); } else { graphics.drawLine(center.toDraw2DPoint(), p.toDraw2DPoint()); } graphics.setBackgroundColor(ColorConstants.white); Rectangle oval = new PrecisionRectangle(center, center) .getExpanded(4, 3).toDraw2DRectangle(); graphics.fillOval(oval); } } public static void drawSheetBackground(Graphics graphics, Rectangle bounds, IStyle style, IStyle template) { drawSheetBackground(graphics, bounds, null, style, template, true); } public static void drawSheetBackground(Graphics graphics, Rectangle bounds, IStyle style, IStyle template, boolean withMainBranches) { drawSheetBackground(graphics, bounds, null, style, template, withMainBranches); } public static void drawSheetBackground(Graphics graphics, Rectangle bounds, HashMap<String, String> existedStyle, IStyle style, IStyle template, boolean withMainBranches) { Rectangle sheetBounds = sheetBounds(bounds); Color fillColor = null; String fillColorValue = getValue(existedStyle, Styles.FillColor, style, template); if (fillColorValue != null) fillColor = ColorUtils.getColor(fillColorValue); if (fillColor != null) { graphics.setAlpha(0xff); graphics.setBackgroundColor(fillColor); graphics.fillRectangle(sheetBounds); } // if (fillColor == null) // fillColor = ColorUtils.getColor("#e0e0e0"); //$NON-NLS-1$ // if (withMainBranches) { // String spinyValue = getValue(Styles.SPINY_LINES, style, template); // String rainbowValue = getValue(Styles.RAINBOWCOLOR, style, template); // boolean spiny = Boolean.parseBoolean(spinyValue); // boolean rainbow = Boolean.parseBoolean(rainbowValue); // if (spiny || rainbow) { // drawMainBranches(graphics, bounds, spiny, rainbow); // } // } } public static void drawRelationship(Graphics graphics, Rectangle bounds, IStyle style, IStyle template) { drawRelationship(graphics, bounds, null, style, template); } public static void drawRelationship(Graphics graphics, Rectangle bounds, HashMap<String, String> existedStyle, IStyle style, IStyle template) { Rectangle relBounds = relBounds(bounds); Path shape = new Path(Display.getCurrent()); Point c1 = new Point(); Point c2 = new Point(); String shapeValue = getValue(existedStyle, Styles.ShapeClass, style, template); if (Styles.REL_SHAPE_ANGLED.equals(shapeValue)) { angledRel(shape, relBounds, c1, c2); } else if (Styles.REL_SHAPE_STRAIGHT.equals(shapeValue)) { straightRel(shape, relBounds, c1, c2); } else { curvedRel(shape, relBounds, c1, c2); } Color lineColor = getLineColor(existedStyle, style, template, ColorConstants.gray); String lineWidthValue = getValue(existedStyle, Styles.LineWidth, style, template); lineWidthValue = StyleUtils.trimNumber(lineWidthValue); int lineWidth = NumberUtils.safeParseInt(lineWidthValue, 3); graphics.setLineWidth(lineWidth); String linePatternValue = getValue(existedStyle, Styles.LinePattern, style, template); int linePattern = StyleUtils.toSWTLineStyle(linePatternValue, SWT.LINE_DOT); graphics.setLineStyle(linePattern); graphics.setAlpha(0xff); graphics.setForegroundColor(lineColor); graphics.drawPath(shape); shape.dispose(); graphics.setLineStyle(SWT.LINE_SOLID); String beginArrowValue = getValue(existedStyle, Styles.ArrowBeginClass, style, template); if (beginArrowValue != null && !Styles.ARROW_SHAPE_NONE.equals(beginArrowValue)) { drawArrow(graphics, beginArrowValue, relBounds.getBottomLeft(), c1, lineWidth); } String endArrowValue = getValue(existedStyle, Styles.ArrowEndClass, style, template); if (endArrowValue == null) endArrowValue = Styles.ARROW_SHAPE_NORMAL; if (!Styles.ARROW_SHAPE_NONE.equals(endArrowValue)) { drawArrow(graphics, endArrowValue, relBounds.getTopRight(), c2, lineWidth); } } public static Color getBranchConnectionColor(IStyle style, IStyle template, IStyle parentStyle, IStyle parentTemplate, int preferredIndex, Color defaultLineColor) { Color lineColor = null; if (preferredIndex >= 0 && parentStyle != null) { String multiColors = getValue(Styles.MultiLineColors, parentStyle, parentTemplate); if (multiColors == null) multiColors = template.getProperty(Styles.MultiLineColors); if (multiColors != null) { multiColors = multiColors.trim(); String[] colors = multiColors.split("[\\s]+"); //$NON-NLS-1$ if (colors.length > 0) { preferredIndex %= colors.length; String color = colors[preferredIndex].trim(); lineColor = ColorUtils.getColor(color); } } } if (lineColor == null) { lineColor = getLineColor(style, template, defaultLineColor); } return lineColor; } public static Color getLineColor(IStyle style, IStyle template, Color defaultLineColor) { return getLineColor(null, style, template, defaultLineColor); } public static Color getLineColor(HashMap<String, String> existedStyle, IStyle style, IStyle template, Color defaultLineColor) { Color lineColor = null; String lineColorValue = getValue(existedStyle, Styles.LineColor, style, template); if (lineColorValue != null) lineColor = ColorUtils.getColor(lineColorValue); if (lineColor == null) { lineColor = defaultLineColor; } return lineColor; } public static String getValue(String key, IStyle style, IStyle template) { return getValue(null, key, style, template); } public static String getValue(HashMap<String, String> existedStyle, String key, IStyle style, IStyle template) { String value = existedStyle == null ? null : existedStyle.get(key); value = style != null ? style.getProperty(key, value) : value; if (value == null) { value = template == null ? null : template.getProperty(key); } return value; } public static void drawLine(Graphics g, Rectangle srcBounds, IStyle srcStyle, IStyle srcTemplate, boolean srcCenterUnderline, Rectangle tgtBounds, IStyle tgtStyle, IStyle tgtTemplate, boolean tgtCenterUnderline, boolean tapered) { String line = getValue(Styles.LineClass, srcStyle, srcTemplate); if (Styles.BRANCH_CONN_NONE.equals(line)) return; String lineWidth = getValue(Styles.LineWidth, srcStyle, srcTemplate); lineWidth = StyleUtils.trimNumber(lineWidth); int width = NumberUtils.safeParseInt(lineWidth, 1); srcBounds = srcBounds.getExpanded(-width / 2, -width / 2); int tgtWidth = NumberUtils.safeParseInt(StyleUtils.trimNumber( getValue(Styles.LineWidth, tgtStyle, tgtTemplate)), 1); tgtBounds = tgtBounds.getExpanded(-tgtWidth / 2, -tgtWidth / 2); String srcShape = getValue(Styles.ShapeClass, srcStyle, srcTemplate); String tgtShape = getValue(Styles.ShapeClass, tgtStyle, tgtTemplate); Point srcPos = getSourcePos(srcBounds, srcShape, tgtBounds, tgtShape, srcCenterUnderline); Point tgtPos = getTargetPos(tgtBounds, tgtShape, srcBounds, srcShape, tgtCenterUnderline); Path shape = new Path(Display.getCurrent()); if (Styles.BRANCH_CONN_ELBOW.equals(line)) { elbow(shape, srcPos, tgtPos, tapered, width); } else if (Styles.BRANCH_CONN_ROUNDEDELBOW.equals(line)) { roundElbow(shape, srcPos, tgtPos, tapered, width); } else if (Styles.BRANCH_CONN_CURVE.equals(line) || Styles.BRANCH_CONN_ARROWED_CURVE.equals(line)) { curveConn(shape, srcPos, tgtPos, tapered, width); } else { // Straight and other unidentifiable line types straightConn(shape, srcPos, tgtPos, tapered, width); } g.setLineWidth(width); g.setLineStyle(SWT.LINE_SOLID); g.setAlpha(0xff); Color fgColor = g.getForegroundColor(); if (fgColor != null) { if (tapered) { g.setBackgroundColor(fgColor); g.fillPath(shape); } else g.drawPath(shape); } shape.dispose(); } public static Point getSourcePos(Rectangle srcBounds, String srcShape, Rectangle tgtBounds, String tgtShape, boolean centerUnderline) { if (Styles.TOPIC_SHAPE_UNDERLINE.equals(srcShape)) { if (centerUnderline) { if (tgtBounds.getCenter().x < srcBounds.getCenter().x) return srcBounds.getLeft(); return srcBounds.getRight(); } else { if (tgtBounds.getCenter().x < srcBounds.getCenter().x) return srcBounds.getBottomLeft(); return srcBounds.getBottomRight(); } } return Geometry.getChopBoxLocation(tgtBounds.getCenter(), srcBounds); } public static Point getTargetPos(Rectangle tgtBounds, String tgtShape, Rectangle srcBounds, String srcShape, boolean centerUnderline) { if (Styles.TOPIC_SHAPE_UNDERLINE.equals(tgtShape)) { if (centerUnderline) { if (tgtBounds.getCenter().x < srcBounds.getCenter().x) return tgtBounds.getRight(); return tgtBounds.getLeft(); } else { if (tgtBounds.getCenter().x < srcBounds.getCenter().x) return tgtBounds.getBottomRight(); return tgtBounds.getBottomLeft(); } } if (tgtBounds.getCenter().x < srcBounds.getCenter().x) return tgtBounds.getRight(); return tgtBounds.getLeft(); // return Geometry.getChopBoxLocation( srcBounds.getCenter(), tgtBounds ); } public static void drawTopic(Graphics graphics, Rectangle bounds, IStyle style, IStyle template, boolean centerUnderline) { drawTopic(graphics, bounds, style, template, centerUnderline, false); } public static void drawTopic(Graphics graphics, Rectangle bounds, IStyle style, IStyle template, boolean centerUnderline, boolean isGradientColor) { drawTopic(graphics, bounds, null, style, template, centerUnderline, isGradientColor); } public static void drawTopic(Graphics graphics, Rectangle bounds, HashMap<String, String> existedStyle, IStyle style, IStyle template, boolean centerUnderline, boolean isGradientColor) { Rectangle topicBounds = topicBounds(bounds); Path shape = new Path(Display.getCurrent()); boolean outline = true; boolean fill = true; String shapeValue = getValue(existedStyle, Styles.ShapeClass, style, template); if (shapeValue == null || Styles.TOPIC_SHAPE_ROUNDEDRECT.equals(shapeValue)) { roundedRect(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_ELLIPSE.equals(shapeValue)) { ellipse(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_RECT.equals(shapeValue)) { rectangle(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_UNDERLINE.equals(shapeValue)) { underline(shape, topicBounds, centerUnderline); fill = false; } else if (Styles.TOPIC_SHAPE_NO_BORDER.equals(shapeValue)) { noBorder(shape, topicBounds); outline = false; } else if (Styles.TOPIC_SHAPE_DIAMOND.equals(shapeValue)) { diamondTopic(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_CALLOUT_ELLIPSE.equals(shapeValue)) { calloutEllipse(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_CALLOUT_ROUNDEDRECT.equals(shapeValue)) { calloutRoundRect(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_CIRCLE.equals(shapeValue)) { Rectangle circleTopicBounds = circleTopicBounds(bounds); circle(shape, circleTopicBounds); } else if (Styles.TOPIC_SHAPE_PARALLELOGRAM.equals(shapeValue)) { parallelogram(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_CLOUD.equals(shapeValue)) { cloud(shape, topicBounds); } else if (Styles.TOPIC_SHAPE_STROKE_CIRCLE.equals(shapeValue)) { strokeCircle(shape, topicBounds); } else { roundedRect(shape, topicBounds); } String fillColorValue = getValue(existedStyle, Styles.FillColor, style, template); graphics.setAlpha(0xff); if (fillColorValue != null) { Color fillColor = ColorUtils.getColor(fillColorValue); if (fillColor != null) { Path newPath = new Path(Display.getCurrent(), shape.getPathData()); if (!fill) { rectangle(newPath, topicBounds.expand(0, -1)); } int x = topicBounds.x; int y1 = topicBounds.y - topicBounds.height / 4; int y2 = topicBounds.y + topicBounds.height; if (isGradientColor) { GradientPattern bgPattern = new GradientPattern( Display.getCurrent(), x, y1, x, y2, ColorConstants.white, 0xff, fillColor, 0xff); graphics.setBackgroundPattern(bgPattern); graphics.fillPath(newPath); bgPattern.dispose(); } else { graphics.setBackgroundColor(fillColor); graphics.fillPath(newPath); } newPath.dispose(); } } if (outline) { Color lineColor = getLineColor(style, template, ColorConstants.gray); String lineColorValue = getValue(existedStyle, Styles.BorderLineColor, style, template); if (lineColorValue != null) lineColor = ColorUtils.getColor(lineColorValue); if (lineColor != null) { String lineWidthValue = getValue(existedStyle, Styles.BorderLineWidth, style, template); lineWidthValue = StyleUtils.trimNumber(lineWidthValue); if (lineWidthValue == null) lineWidthValue = getValue(existedStyle, Styles.LineWidth, style, template); int lineWidth = NumberUtils.safeParseInt(lineWidthValue, 1); if (lineWidth > 0) { graphics.setLineWidth(lineWidth); graphics.setLineStyle(SWT.LINE_SOLID); graphics.setForegroundColor(lineColor); graphics.drawPath(shape); } } } shape.dispose(); } public static void ellipse(Path shape, Rectangle r) { shape.addArc(r, 0, 360); } public static int getBoundaryPadding() { return BOUNDARY_PADDING; } public static void elbow(Path shape, Point p1, Point p2, boolean tapered, int width) { Point c = new Point(p1.x, p2.y); if (tapered) { PrecisionPoint _c = new PrecisionPoint(c); PrecisionPoint _p1 = new PrecisionPoint(p1); PrecisionPoint _p2 = new PrecisionPoint(p2); PrecisionPointPair _cc = Geometry.calculatePositionPair(_c, _p2, 0.5); PrecisionPointPair _pp2 = Geometry .calculatePositionPair(_p2, _c, 0.5).swap(); PrecisionPointPair _pp1 = Geometry.calculatePositionPair(_p1, _c, width); double d = (p1.x > p2.x == p1.y > p2.y) ? width * 0.5 : -width * 0.5; _cc.p1().x -= d; _cc.p2().x += d; shape.moveTo(_pp1.p1()); shape.lineTo(_cc.p1()); shape.lineTo(_pp2.p1()); shape.lineTo(_pp2.p2()); shape.lineTo(_cc.p2()); shape.lineTo(_pp1.p2()); shape.close(); } else { shape.moveTo(p1); shape.lineTo(c); shape.lineTo(p2); } } public static void roundElbow(Path shape, Point p1, Point p2, boolean tapered, int width) { Point c = new Point(p1.x, p2.y); int corner = getAppliedCorner(new Rectangle(p1, p2)) * 2; Point q1 = new Point(c.x, p1.y > p2.y ? c.y + corner : c.y - corner); Point q2 = new Point(p1.x > p2.x ? c.x - corner : c.x + corner, c.y); if (tapered) { PrecisionPoint _p1 = new PrecisionPoint(p1); PrecisionPoint _p2 = new PrecisionPoint(p2); PrecisionPoint _q1 = new PrecisionPoint(q1); PrecisionPoint _q2 = new PrecisionPoint(q2); PrecisionPoint _c1 = new PrecisionPoint(_q1.x, _q1.y + (c.y - _q1.y) * 3 / 4); PrecisionPoint _c2 = new PrecisionPoint( _q2.x + (c.x - _q2.x) * 3 / 4, _q2.y); PrecisionPoint _pc1 = new PrecisionPoint(_p1.x, _p1.y + (_c1.y - _p1.y) * 2); PrecisionPoint _pc2 = new PrecisionPoint( _p2.x + (_c2.x - _p2.x) * 2, _p2.y); PrecisionPointPair _pp1 = Geometry.calculatePositionPair(_p1, _c1, width); PrecisionPointPair _qq1 = Geometry.calculatePositionPair(_q1, _c1, width); PrecisionPointPair _cc1 = Geometry.calculatePositionPair(_c1, _pc1, width); PrecisionPointPair _pp2 = Geometry .calculatePositionPair(_p2, _c2, 0.5).swap(); PrecisionPointPair _qq2 = Geometry .calculatePositionPair(_q2, _c2, 0.5).swap(); PrecisionPointPair _cc2 = Geometry .calculatePositionPair(_c2, _pc2, 0.5).swap(); double d = (p1.x > p2.x == p1.y > p2.y) ? width * 0.5 : -width * 0.5; _qq2.p1().x -= d; _qq2.p2().x += d; _cc2.p1().x -= d; _cc2.p2().x += d; shape.moveTo(_pp1.p1()); shape.lineTo(_qq1.p1()); shape.cubicTo(_cc1.p1(), _cc2.p1(), _qq2.p1()); shape.lineTo(_pp2.p1()); shape.lineTo(_pp2.p2()); shape.lineTo(_qq2.p2()); shape.cubicTo(_cc2.p2(), _cc1.p2(), _qq1.p2()); shape.lineTo(_pp1.p2()); shape.close(); } else { shape.moveTo(p1); shape.lineTo(q1); shape.cubicTo(q1.x, q1.y + (c.y - q1.y) * 3 / 4, q2.x + (c.x - q2.x) * 3 / 4, q2.y, q2.x, q2.y); shape.lineTo(p2); } } public static void straightConn(Path shape, Point p1, Point p2, boolean tapered, int width) { if (tapered) { PrecisionPoint _p1 = new PrecisionPoint(p1); PrecisionPoint _p2 = new PrecisionPoint(p2); PrecisionPointPair _pp1 = Geometry.calculatePositionPair(_p1, _p2, width); PrecisionPointPair _pp2 = Geometry .calculatePositionPair(_p2, _p1, 0.5).swap(); shape.moveTo(_pp1.p1()); shape.lineTo(_pp2.p1()); shape.lineTo(_pp2.p2()); shape.moveTo(_pp1.p2()); shape.close(); } else { shape.moveTo(p1); shape.lineTo(p2); } } public static void curveConn(Path shape, Point p1, Point p2, boolean tapered, int width) { if (tapered) { PrecisionPoint _p1 = new PrecisionPoint(p1); PrecisionPoint _p2 = new PrecisionPoint(p2); PrecisionPoint _c = new PrecisionPoint( _p1.x + (_p2.x - _p1.x) * 2 / 10, _p2.y); PrecisionPointPair _pp1 = Geometry.calculatePositionPair(_p1, _p2, width); PrecisionPointPair _pp2 = Geometry .calculatePositionPair(_p2, _c, 0.5).swap(); PrecisionPointPair _cc = Geometry.calculatePositionPair(_c, _p2, 0.5); double d = (p1.x > p2.x == p1.y > p2.y) ? width * 0.5 : -width * 0.5; _cc.p1().x -= d; _cc.p2().x += d; shape.moveTo(_pp1.p1()); shape.quadTo(_cc.p1(), _pp2.p1()); shape.lineTo(_pp2.p2()); shape.quadTo(_cc.p2(), _pp1.p2()); shape.close(); } else { Point c = new Point(p1.x, p2.y); c.x += (p2.x - c.x) * 2 / 10; shape.moveTo(p1); shape.quadTo(c, p2); } } public static void herringBone(Path shape, Point head, double angle, int lineWidth) { int l = lineWidth * 2 + 4; int w = lineWidth * 2 + 2; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle, l / 2); PrecisionPoint p2 = p.getMoved(angle, l); PrecisionPoint p01 = p.getMoved(angle - Math.PI * 2 / 3, w); PrecisionPoint p02 = p.getMoved(angle + Math.PI * 2 / 3, w); PrecisionPoint p11 = p1.getMoved(angle - Math.PI * 2 / 3, w); PrecisionPoint p12 = p1.getMoved(angle + Math.PI * 2 / 3, w); PrecisionPoint p21 = p2.getMoved(angle - Math.PI * 2 / 3, w); PrecisionPoint p22 = p2.getMoved(angle + Math.PI * 2 / 3, w); shape.moveTo(p01); shape.lineTo(head); shape.lineTo(p02); shape.moveTo(p11); shape.lineTo(p1); shape.lineTo(p12); shape.moveTo(p21); shape.lineTo(p2); shape.lineTo(p22); shape.moveTo(head); shape.lineTo(p2); } public static void noBorder(Path shape, Rectangle r) { shape.addRectangle(r); } public static void normalArrow(Path shape, Point head, double angle, int lineWidth) { int side = lineWidth * 2 + 4; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle - Math.PI / 6, side); PrecisionPoint p2 = p.getMoved(angle + Math.PI / 6, side); shape.moveTo(p1); shape.lineTo(head); shape.lineTo(p2); } public static void rectangle(Path shape, Rectangle r) { shape.addRectangle(r); } public static void roundedRect(Path shape, Rectangle r) { shape.addRoundedRectangle(r, getAppliedCorner(r)); } public static void scallops(Path shape, Rectangle box) { int margin = getBoundaryPadding() * 3 / 5; if (box.width <= margin * 2 || box.height <= margin * 2) return; float width = box.width - margin * 2; float height = box.height - margin * 2; float stepX = BOUNDARY_STEP; float stepY = BOUNDARY_STEP * 6 / 8; int numX = Math.max(1, (int) (width / stepX)); int numY = Math.max(1, (int) (height / stepY)); stepX = width / numX; stepY = height / numY; float x = box.x + margin; float y = box.y + margin; shape.moveTo(x, y); for (int i = 0; i < numX; i++) { shape.cubicTo(x + stepX / 4, y - margin, x + stepX * 3 / 4, y - margin, x + stepX, y); x += stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x + margin, y + stepY / 4, x + margin, y + stepY * 3 / 4, x, y + stepY); y += stepY; } for (int i = 0; i < numX; i++) { shape.cubicTo(x - stepX / 4, y + margin, x - stepX * 3 / 4, y + margin, x - stepX, y); x -= stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x - margin, y - stepY / 4, x - margin, y - stepY * 3 / 4, x, y - stepY); y -= stepY; } shape.close(); } public static void spearhead(Path shape, Point head, double angle, int lineWidth) { int side = lineWidth * 2 + 6; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle - Math.PI / 8, side); PrecisionPoint p2 = p.getMoved(angle + Math.PI / 8, side); PrecisionPoint cp = p.getMoved(angle, side / 2); shape.moveTo(head); shape.lineTo(p1); shape.quadTo(cp, p2); shape.close(); } public static void square(Path shape, Point head, double angle, int lineWidth) { int side = lineWidth + 2; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle - Math.PI / 4, side); PrecisionPoint p2 = p.getMoved(angle - Math.PI * 3 / 4, side); PrecisionPoint p3 = p.getMoved(angle + Math.PI * 3 / 4, side); PrecisionPoint p4 = p.getMoved(angle + Math.PI / 4, side); shape.moveTo(p1); shape.lineTo(p2); shape.lineTo(p3); shape.lineTo(p4); shape.close(); } public static void straightRel(Path shape, Rectangle relBounds, Point c1, Point c2) { Point p = relBounds.getBottomLeft(); shape.moveTo(p); c2.setLocation(p); p = relBounds.getTopRight(); c1.setLocation(p); shape.lineTo(p); } public static void tension(Path shape, Rectangle box) { int margin = getBoundaryPadding() / 2; int margin2 = Math.max(1, margin / 4); if (box.width <= margin * 2 || box.height <= margin * 2) return; float width = box.width - margin2 * 2; float height = box.height - margin2 * 2; float stepX = BOUNDARY_STEP; float stepY = BOUNDARY_STEP; int numX = Math.max(1, (int) (width / stepX)); int numY = Math.max(1, (int) (height / stepY)); stepX = width / numX; stepY = height / numY; float x = box.x + margin2; float y = box.y + margin2; shape.moveTo(x, y); for (int i = 0; i < numX; i++) { shape.cubicTo(x + stepX / 4, y + margin, x + stepX * 3 / 4, y + margin, x + stepX, y); x += stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x - margin, y + stepY / 4, x - margin, y + stepY * 3 / 4, x, y + stepY); y += stepY; } for (int i = 0; i < numX; i++) { shape.cubicTo(x - stepX / 4, y - margin, x - stepX * 3 / 4, y - margin, x - stepX, y); x -= stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x + margin, y - stepY / 4, x + margin, y - stepY * 3 / 4, x, y - stepY); y -= stepY; } shape.close(); } public static void triangle(Path shape, Point head, double angle, int lineWidth) { int side = lineWidth * 2 + 4; PrecisionPoint p = new PrecisionPoint(head); PrecisionPoint p1 = p.getMoved(angle - Math.PI / 6, side); PrecisionPoint p2 = p.getMoved(angle + Math.PI / 6, side); shape.moveTo(p1); shape.lineTo(head); shape.lineTo(p2); shape.close(); } public static void underline(Path shape, Rectangle r, boolean center) { Rectangle r2 = r; if (center) { shape.moveTo(r2.getLeft()); shape.lineTo(r2.getRight()); } else { shape.moveTo(r2.getBottomLeft()); shape.lineTo(r2.getBottomRight()); } } public static void waves(Path shape, Rectangle box) { int margin = getBoundaryPadding() / 4; if (box.width <= margin * 2 || box.height <= margin * 2) return; float width = box.width - margin * 2; float height = box.height - margin * 2; float stepX = BOUNDARY_STEP; float stepY = BOUNDARY_STEP; int numX = Math.max(1, (int) (width / stepX)); int numY = Math.max(1, (int) (height / stepY)); stepX = width / numX; stepY = height / numY; float x = box.x + margin; float y = box.y + margin; float h = ((float) getBoundaryPadding()) / 4; shape.moveTo(x, y); for (int i = 0; i < numX; i++) { shape.cubicTo(x + stepX / 8, y - h, x + stepX * 3 / 8, y - h, x + stepX / 2, y); shape.cubicTo(x + stepX * 5 / 8, y + h, x + stepX * 7 / 8, y + h, x + stepX, y); x += stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x + h, y + stepY / 8, x + h, y + stepY * 3 / 8, x, y + stepY / 2); shape.cubicTo(x - h, y + stepY * 5 / 8, x - h, y + stepY * 7 / 8, x, y + stepY); y += stepY; } for (int i = 0; i < numX; i++) { shape.cubicTo(x - stepX / 8, y + h, x - stepX * 3 / 8, y + h, x - stepX / 2, y); shape.cubicTo(x - stepX * 5 / 8, y - h, x - stepX * 7 / 8, y - h, x - stepX, y); x -= stepX; } for (int i = 0; i < numY; i++) { shape.cubicTo(x - h, y - stepY / 8, x - h, y - stepY * 3 / 8, x, y - stepY / 2); shape.cubicTo(x + h, y - stepY * 5 / 8, x + h, y - stepY * 7 / 8, x, y - stepY); y -= stepY; } shape.close(); } public static void polygon(Path shape, Rectangle box) { shape.moveTo(box.x + box.width / 2, box.y); shape.lineTo(box.x, box.y + box.height / 4); shape.lineTo(box.x, box.bottom() - box.height / 4); shape.lineTo(box.x + box.width / 2, box.bottom()); shape.lineTo(box.getBottomRight()); shape.lineTo(box.getTopRight()); shape.close(); } public static void roundedPolygon(Path shape, Rectangle box) { int corner = 4; Point p0 = calcRoundedPoint(box.getTop(), box.getTopRight(), corner); shape.moveTo(p0); cubicAndLine(shape, box.getTopRight(), box.getTop(), box.getTopLeft().getTranslated(0, box.height / 4), corner); cubicAndLine(shape, box.getTop(), box.getTopLeft().getTranslated(0, box.height / 4), box.getBottomLeft().getTranslated(0, -box.height / 4), corner); cubicAndLine(shape, box.getTopLeft().getTranslated(0, box.height / 4), box.getBottomLeft().getTranslated(0, -box.height / 4), box.getBottom(), corner); cubicAndLine(shape, box.getBottomLeft().getTranslated(0, -box.height / 4), box.getBottom(), box.getBottomRight(), corner); cubicAndLine(shape, box.getBottom(), box.getBottomRight(), box.getTopRight(), corner); cubicAndLine(shape, box.getBottomRight(), box.getTopRight(), box.getTop(), corner); shape.close(); } private static void cubicAndLine(Path shape, Point p0, Point p1, Point p2, int corner) { Point p3 = calcRoundedPoint(p1, p0, corner); Point p4 = calcRoundedPoint(p1, p2, corner); Point c1 = calcControlPoint(p3, p1); Point c2 = calcControlPoint(p4, p1); shape.cubicTo(c1, c2, p4); shape.lineTo(calcRoundedPoint(p2, p1, corner)); } private static Point calcRoundedPoint(Point p1, Point p2, int corner) { int dx = p2.x - p1.x; int dy = p2.y - p1.y; if (dx == 0) { if (dy > 0) return p1.getTranslated(0, corner); return p1.getTranslated(0, -corner); } else if (dy == 0) { if (dx > 0) return p1.getTranslated(corner, 0); return p1.getTranslated(-corner, 0); } else { double l = p1.getDistance(p2); double x = dx / l * corner; double y = dy / l * corner; return p1.getTranslated(x, y); } } private static Point calcControlPoint(Point p1, Point p2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; return p1.getTranslated(dx * 0.447715f, dy * 0.447715f); } private static void drawtext(Graphics graphics, String text, Rectangle parentRect, IStyle style, IStyle template, Font font) { drawtext(graphics, text, parentRect, null, style, template, font); } private static void drawtext(Graphics graphics, String text, Rectangle parentRect, HashMap<String, String> existedStyle, IStyle style, IStyle template, Font font) { String fontColor = getValue(existedStyle, Styles.TextColor, style, template); RGB fontColorRGB = ColorUtils.toRGB(fontColor); if (fontColorRGB == null) return; graphics.setForegroundColor(ColorUtils.getColor(fontColorRGB)); String textAlign = getValue(existedStyle, Styles.TextAlign, style, template); String textCase = getValue(existedStyle, Styles.TextCase, style, template); boolean isStrikedThrough = isStrikeout(existedStyle, style); boolean isUnderlined = isUnderline(existedStyle, style); if (Styles.UPPERCASE.equals(textCase)) { text = text.toUpperCase(); } else if (Styles.LOWERCASE.equals(textCase)) { text = text.toLowerCase(); } else if (Styles.CAPITALIZE.equals(textCase)) { text = capitalize(text); } Dimension textSize = GraphicsUtils.getAdvanced().getTextSize(text, font); graphics.setFont(font); int x = parentRect.x + (parentRect.width - textSize.width) / 4; int y = parentRect.y + (parentRect.height - textSize.height) / 2; if (Styles.ALIGN_CENTER.equals(textAlign)) { x += (parentRect.width - textSize.width) / 4; } else if (Styles.ALIGN_RIGHT.equals(textAlign)) { x += (parentRect.width - textSize.width) / 2; } graphics.drawText(text, x, y); y += textSize.height; if (isUnderlined) { Path underline = new Path(Display.getCurrent()); underline.moveTo(x, y - 1); underline.lineTo(x + textSize.width, y - 1); graphics.drawPath(underline); underline.dispose(); } if (isStrikedThrough) { Path strikeOutLine = new Path(Display.getCurrent()); strikeOutLine.moveTo(x, y - textSize.height / 2); strikeOutLine.lineTo(x + textSize.width, y - textSize.height / 2); graphics.drawPath(strikeOutLine); strikeOutLine.dispose(); } font.dispose(); } private static String capitalize(String str) { StringBuffer stringbf = new StringBuffer(); Matcher m = Pattern.compile("([a-z])([a-z]*)", Pattern.CASE_INSENSITIVE) //$NON-NLS-1$ .matcher(str); while (m.find()) { m.appendReplacement(stringbf, m.group(1).toUpperCase() + m.group(2).toLowerCase()); } return m.appendTail(stringbf).toString(); } public static void drawThemeText(Graphics graphics, String text, Rectangle parentRect, IStyle style, IStyle template) { String fontName = getFontName(style, template); int fontDataStyle = getFontDataStyle(style); Font font = new Font(null, fontName, 2, fontDataStyle); drawtext(graphics, text, parentRect, style, template, font); font.dispose(); } public static void drawStyleText(Graphics graphics, String text, Rectangle bounds, HashMap<String, String> existedStyle, IStyle style, IStyle template) { Rectangle parentRect = topicBounds(bounds); String fontName = getFontName(existedStyle, style, template); int fontDataStyle = getFontDataStyle(style); Font font = new Font(null, fontName, 7, fontDataStyle); drawtext(graphics, text, parentRect, existedStyle, style, template, font); font.dispose(); } private static int getFontDataStyle(HashMap<String, String> existedStyle, IStyle style) { boolean isBold = isBold(existedStyle, style); boolean isItalic = isItalic(existedStyle, style); int fontDataStyle = SWT.NORMAL; if (isBold) fontDataStyle |= SWT.BOLD; if (isItalic) fontDataStyle |= SWT.ITALIC; return fontDataStyle; } private static boolean isBold(HashMap<String, String> existedStyle, IStyle style) { String weight = getValue(existedStyle, Styles.FontWeight, style, null); return weight != null && weight.contains(Styles.FONT_WEIGHT_BOLD); } private static boolean isItalic(HashMap<String, String> existedStyle, IStyle style) { String weight = getValue(existedStyle, Styles.FontStyle, style, null); return weight != null && weight.contains(Styles.FONT_STYLE_ITALIC); } private static boolean isUnderline(HashMap<String, String> existedStyle, IStyle style) { String weight = getValue(existedStyle, Styles.TextDecoration, style, null); return weight != null && weight.contains(Styles.TEXT_DECORATION_UNDERLINE); } private static boolean isStrikeout(HashMap<String, String> existedStyle, IStyle style) { String weight = getValue(existedStyle, Styles.TextDecoration, style, null); return weight != null && weight.contains(Styles.TEXT_DECORATION_LINE_THROUGH); } private static int getFontDataStyle(IStyle style) { return getFontDataStyle(null, style); } private static String getFontName(IStyle style, IStyle template) { return getFontName(null, style, template); } private static String getFontName(HashMap<String, String> existedStyle, IStyle style, IStyle template) { String fontName = getValue(existedStyle, Styles.FontFamily, style, template); String availableFontName = FontUtils.getAAvailableFontNameFor(fontName); fontName = availableFontName != null ? availableFontName : fontName; if (Styles.SYSTEM.equals(fontName)) { fontName = JFaceResources.getDefaultFont().getFontData()[0] .getName(); } return fontName; } private static Rectangle topicBounds(Rectangle r) { int width = r.width * 7 / 8; int height = width * 9 / 20; int x = r.x + r.width / 2 - width / 2; int y = r.y + r.height / 2 - height / 2; return new Rectangle(x, y, width, height); } private static Rectangle circleTopicBounds(Rectangle r) { int width = Math.min(r.width * 6 / 8, r.height * 6 / 8); int height = width; int x = r.x + r.width / 2 - width / 2; int y = r.y + r.height / 2 - height / 2; return new Rectangle(x, y, width, height); } private static Rectangle boundaryBounds(Rectangle r) { int width = r.width * 7 / 8; int height = width * 7 / 10; int x = r.x + r.width / 2 - width / 2; int y = r.y + r.height / 2 - height / 2; return new Rectangle(x, y, width, height); } private static Rectangle relBounds(Rectangle r) { int width = r.width * 6 / 8; int height = width * 7 / 10; int x = r.x + r.width / 2 - width / 2; int y = r.y + r.height / 2 - height / 2; return new Rectangle(x, y, width, height); } private static Rectangle sheetBounds(Rectangle r) { int width = r.width * 7 / 8; int height = width * 7 / 8; int x = r.x + r.width / 2 - width / 2; int y = r.y + r.height / 2 - height / 2; return new Rectangle(x, y, width, height); } }