/* * @(#)AbstractLineDecoration.java * * Copyright (c) 1996-2010 The authors and contributors of JHotDraw. * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. * */ package org.jhotdraw.draw.decoration; import org.jhotdraw.draw.*; import java.awt.*; import java.awt.geom.*; import org.jhotdraw.geom.Geom; import static org.jhotdraw.draw.AttributeKeys.*; /** * This abstract class can be extended to implement a {@link LineDecoration}. * * @author Werner Randelshofer * @version $Id$ */ public abstract class AbstractLineDecoration implements LineDecoration { private static final long serialVersionUID = 1L; /** * If this is true, the decoration is filled. */ private boolean isFilled; /** * If this is true, the decoration is stroked. */ private boolean isStroked; /** * If this is true, the stroke color is used to fill the decoration. */ private boolean isSolid; /** * Constructs an arrow tip with the given angle and radius. */ public AbstractLineDecoration(boolean isFilled, boolean isStroked, boolean isSolid) { this.isFilled = isFilled; this.isStroked = isStroked; this.isSolid = isSolid; } protected boolean isFilled() { return isFilled; } protected boolean isStroked() { return isStroked; } protected boolean isSolid() { return isSolid; } /** * Draws the arrow tip in the direction specified by the given two * Points. (template method) */ @Override public void draw(Graphics2D g, Figure f, Point2D.Double p1, Point2D.Double p2) { Path2D.Double path = getTransformedDecoratorPath(f, p1, p2); Color color; if (isFilled) { if (isSolid) { color = f.get(STROKE_COLOR); } else { color = f.get(FILL_COLOR); } if (color != null) { g.setColor(color); g.fill(path); } } if (isStroked) { color = f.get(STROKE_COLOR); if (color != null) { g.setColor(color); g.setStroke(AttributeKeys.getStroke(f,AttributeKeys.getScaleFactorFromGraphics(g))); g.draw(path); } } } /** * Returns the drawing area of the decorator. */ @Override public Rectangle2D.Double getDrawingArea(Figure f, Point2D.Double p1, Point2D.Double p2) { Path2D.Double path = getTransformedDecoratorPath(f, p1, p2); Rectangle2D b = path.getBounds2D(); Rectangle2D.Double area = new Rectangle2D.Double(b.getX(), b.getY(), b.getWidth(), b.getHeight()); if (isStroked) { double strokeWidth = f.get(STROKE_WIDTH); int strokeJoin = f.get(STROKE_JOIN); double miterLimit = (f.get(STROKE_MITER_LIMIT) * strokeWidth); double grow; if (strokeJoin == BasicStroke.JOIN_MITER) { grow = (int) (1 + strokeWidth / 2 * miterLimit); } else { grow = (int) (1 + strokeWidth / 2); } Geom.grow(area, grow, grow); } else { Geom.grow(area, 1, 1); // grow due to antialiasing } return area; } @Override public double getDecorationRadius(Figure f) { double strokeWidth = f.get(STROKE_WIDTH); double scaleFactor; if (strokeWidth > 1f) { scaleFactor = 1d + (strokeWidth - 1d) / 2d; } else { scaleFactor = 1d; } return getDecoratorPathRadius(f) * scaleFactor; } private Path2D.Double getTransformedDecoratorPath(Figure f, Point2D.Double p1, Point2D.Double p2) { Path2D.Double path = getDecoratorPath(f); double strokeWidth = f.get(STROKE_WIDTH); AffineTransform transform = new AffineTransform(); transform.translate(p1.x, p1.y); transform.rotate(Math.atan2(p1.x - p2.x, p2.y - p1.y)); // transform.rotate(Math.PI / 2); if (strokeWidth > 1f) { transform.scale(1d + (strokeWidth - 1d) / 2d, 1d + (strokeWidth - 1d) / 2d); } path.transform(transform); return path; } protected void setFilled(boolean b) { isFilled = b; } protected void setStroked(boolean b) { isStroked = b; } protected void setSolid(boolean b) { isSolid = b; } /** * Hook method to calculate the path of the decorator. */ protected abstract Path2D.Double getDecoratorPath(Figure f); /** * Hook method to calculate the radius of the decorator path. */ protected abstract double getDecoratorPathRadius(Figure f); }