/* * @(#)PolyLineDecorationLocator.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.locator; import org.jhotdraw.geom.Dimension2DDouble; import org.jhotdraw.draw.*; import java.awt.geom.*; import org.jhotdraw.xml.*; /** * A {@link Locator} which can be used to place a label on the path of * a {@link BezierFigure}. * <p> * The point is located at a distance and an angle relative to the total length * of the bezier path. * <p> * XXX - The angle should be perpendicular to the path. * * @author Werner Randelshofer * @version $Id$ */ public class BezierLabelLocator implements Locator, DOMStorable { private double relativePosition; private double angle; private double distance; /** * Creates a new instance. * This constructor is for use by DOMStorable only. */ public BezierLabelLocator() { } /** Creates a new locator. * * @param relativePosition The relative position of the label on the polyline. * 0.0 specifies the start of the bezier path, 1.0 the * end of the polyline. Values between 0.0 and 1.0 are relative positions * on the bezier path. * @param angle The angle of the distance vector. * @param distance The length of the distance vector. */ public BezierLabelLocator(double relativePosition, double angle, double distance) { this.relativePosition = relativePosition; this.angle = angle; this.distance = distance; } @Override public Point2D.Double locate(Figure owner) { return getRelativePoint((BezierFigure) owner); } @Override public Point2D.Double locate(Figure owner, Figure label) { Point2D.Double relativePoint = getRelativeLabelPoint((BezierFigure) owner, label); return relativePoint; } /** * Returns the coordinates of the relative point on the path * of the specified bezier figure. */ public Point2D.Double getRelativePoint(BezierFigure owner) { Point2D.Double point = owner.getPointOnPath((float) relativePosition, 3); Point2D.Double nextPoint = owner.getPointOnPath( (relativePosition < 0.5) ? (float) relativePosition + 0.1f : (float) relativePosition - 0.1f, 3); double dir = Math.atan2(nextPoint.y - point.y, nextPoint.x - point.x); if (relativePosition >= 0.5) { dir += Math.PI; } double alpha = dir + angle; Point2D.Double p = new Point2D.Double( point.x + distance * Math.cos(alpha), point.y + distance * Math.sin(alpha) ); if (Double.isNaN(p.x)) p = point; return p; } /** * Returns a Point2D.Double on the polyline that is at the provided relative position. * XXX - Implement this and move it to BezierPath */ public Point2D.Double getRelativeLabelPoint(BezierFigure owner, Figure label) { // Get a point on the path an the next point on the path Point2D.Double point = owner.getPointOnPath((float) relativePosition, 3); if (point == null) { return new Point2D.Double(0,0); } Point2D.Double nextPoint = owner.getPointOnPath( (relativePosition < 0.5) ? (float) relativePosition + 0.1f : (float) relativePosition - 0.1f, 3); double dir = Math.atan2(nextPoint.y - point.y, nextPoint.x - point.x); if (relativePosition >= 0.5) { dir += Math.PI; } double alpha = dir + angle; Point2D.Double p = new Point2D.Double( point.x + distance * Math.cos(alpha), point.y + distance * Math.sin(alpha) ); if (Double.isNaN(p.x)) p = point; Dimension2DDouble labelDim = label.getPreferredSize(); if (relativePosition == 0.5 && p.x >= point.x - distance / 2 && p.x <= point.x + distance / 2) { if (p.y >= point.y) { // South East return new Point2D.Double(p.x - labelDim.width / 2, p.y); } else { // North East return new Point2D.Double(p.x - labelDim.width / 2, p.y - labelDim.height); } } else { if (p.x >= point.x) { if (p.y >= point.y) { // South East return new Point2D.Double(p.x, p.y); } else { // North East return new Point2D.Double(p.x, p.y - labelDim.height); } } else { if (p.y >= point.y) { // South West return new Point2D.Double(p.x - labelDim.width, p.y); } else { // North West return new Point2D.Double(p.x - labelDim.width, p.y - labelDim.height); } } } /* int percentage = (int) (relativePosition * 100); int segment; // relative segment Point2D.Double segPoint; // relative Point2D.Double on the segment int nPoints = owner.getPointCount(); Point2D.Double[] Points = owner.getPoints(); if (nPoints < 2) return new Point2D.Double(0, 0); switch (percentage) { case 0 : segment = 0; segPoint = owner.getStartPoint(); break; case 100 : segment = owner.getPointCount() - 2; segPoint = owner.getEndPoint(); break; default : double totalLength = 0d; double[] segLength = new double[nPoints - 1]; for (int i=1; i < nPoints; i++) { segLength[i-1] = Geom.length(Points[i-1].x, Points[i-1].y, Points[i].x, Points[i].y); totalLength += segLength[i-1]; } double relativeProgress = percentage * totalLength / 101d; segment = 0; double segMin = 0d; for (segment=0; segment < segLength.length - 1; segment++) { if (segMin + segLength[segment] > relativeProgress) break; segMin += segLength[segment]; } // Compute the relative Point2D.Double on the line segPoint = new Point2D.Double(); relativeProgress -= segMin; segPoint.x = (int) ((Points[segment].x * (segLength[segment] - relativeProgress) + Points[segment + 1].x * relativeProgress) / segLength[segment] +.5); segPoint.y = (int) ((Points[segment].y * (segLength[segment] - relativeProgress) + Points[segment + 1].y * relativeProgress) / segLength[segment] +.5); break; } Dimension2DDouble labelDim = label.getPreferredSize(); Line2D.Double line = new Line2D.Double(Points[segment].x, Points[segment].y, Points[segment + 1].x, Points[segment + 1].y); double dir = Math.atan2(Points[segment + 1].y - Points[segment].y, Points[segment + 1].x - Points[segment].x); double alpha = dir + angle; Point2D.Double p = new Point2D.Double( (int) (segPoint.x + distance * Math.cos(alpha)), (int) (segPoint.y + distance * Math.sin(alpha)) ); if (p.x >= segPoint.x) { if (p.y >= segPoint.y) { // South East return new Point2D.Double(p.x, p.y); } else { // North East return new Point2D.Double(p.x, p.y - labelDim.height); } } else { if (p.y >= segPoint.y) { // South West return new Point2D.Double(p.x - labelDim.width, p.y); } else { // North West return new Point2D.Double(p.x - labelDim.width, p.y - labelDim.height); } }*/ } @Override public void read(DOMInput in) { relativePosition = in.getAttribute("relativePosition", 0d); angle = in.getAttribute("angle", 0d); distance = in.getAttribute("distance", 0); } @Override public void write(DOMOutput out) { out.addAttribute("relativePosition", relativePosition); out.addAttribute("angle", angle); out.addAttribute("distance", distance); } }