/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
/*
* @(#)PolyLineDecorationLocator.java 1.0 3. Februar 2004
*
* Copyright (c) 1996-2006 by the original authors of JHotDraw
* and all its contributors.
* All rights reserved.
*
* The copyright of this software is owned by the authors and
* contributors of the JHotDraw project ("the copyright holders").
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* the copyright holders. For details see accompanying license terms.
*/
package org.jhotdraw.draw;
import org.jhotdraw.geom.*;
import org.jhotdraw.util.*;
import java.awt.*;
import java.awt.geom.*;
import org.jhotdraw.xml.*;
/**
* This locator locates a position relative to a polyline.
* The position is chosen in a way, which is suitable for labeling the polyline.
* The preferredSize of the label is used to determine its location.
*
* @author Werner Randelshofer
* @version 2.0 2006-01-14 Changed to support double precison coordinates.
* <br>1.0 3. Februar 2004 Created.
*/
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 polyline, 1.0 the
* end of the polyline. Values between 0.0 and 1.0 are relative positions
* on the polyline.
* @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;
}
public Point2D.Double locate(Figure owner) {
return getRelativePoint((BezierFigure) owner);
}
public Point2D.Double locate(Figure owner, Figure label) {
Point2D.Double relativePoint = getRelativeLabelPoint((BezierFigure) owner, label);
return relativePoint;
}
/**
* Returns a Point2D.Double on the polyline that is at the provided relative position.
*/
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);
}
}*/
}
public void read(DOMInput in) {
relativePosition = in.getAttribute("relativePosition", 0d);
angle = in.getAttribute("angle", 0d);
distance = in.getAttribute("distance", 0);
}
public void write(DOMOutput out) {
out.addAttribute("relativePosition", relativePosition);
out.addAttribute("angle", angle);
out.addAttribute("distance", distance);
}
}