/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* 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:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.core.ui.model.graphic.imp.angle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.weasis.core.api.gui.util.GeomUtil;
import org.weasis.core.ui.Messages;
import org.weasis.core.ui.model.utils.bean.AdvancedShape;
import org.weasis.core.ui.model.utils.bean.Measurement;
import org.weasis.core.ui.util.MouseEventDouble;
@XmlType(name = "cobbAngle")
@XmlRootElement(name = "cobbAngle")
public class CobbAngleToolGraphic extends OpenAngleToolGraphic {
private static final long serialVersionUID = -5516094184442711688L;
public static final Integer POINTS_NUMBER = 5;
public static final Icon ICON = new ImageIcon(CobbAngleToolGraphic.class.getResource("/icon/22x22/draw-cobb.png")); //$NON-NLS-1$
public static final Measurement ANGLE = new Measurement(Messages.getString("measure.angle"), 1, true); //$NON-NLS-1$
public static final Measurement COMPLEMENTARY_ANGLE =
new Measurement(Messages.getString("measure.complement_angle"), 2, true, true, false); //$NON-NLS-1$
protected static final List<Measurement> MEASUREMENT_LIST = new ArrayList<>();
static {
MEASUREMENT_LIST.add(ANGLE);
MEASUREMENT_LIST.add(COMPLEMENTARY_ANGLE);
}
// Let O be center of perpendicular projections in Cobb's angle
protected Point2D.Double ptO;
public CobbAngleToolGraphic() {
super(POINTS_NUMBER);
}
public CobbAngleToolGraphic(CobbAngleToolGraphic graphic) {
super(graphic);
}
@Override
public CobbAngleToolGraphic copy() {
return new CobbAngleToolGraphic(this);
}
@Override
public Icon getIcon() {
return ICON;
}
@Override
public String getUIName() {
return Messages.getString("measure.coobs"); //$NON-NLS-1$
}
@Override
public List<Measurement> getMeasurementList() {
return MEASUREMENT_LIST;
}
@Override
public Integer moveAndResizeOnDrawing(Integer handlePointIndex, Double deltaX, Double deltaY,
MouseEventDouble mouseEvent) {
handlePointIndex = super.moveAndResizeOnDrawing(handlePointIndex, deltaX, deltaY, mouseEvent);
if (handlePointIndex != -1 && pts.size() >= 4) {
updateTool();
if (lineABvalid && lineCDvalid) {
// Let MN be the bisector of the two line segments AB & CD, if parallel MN is the median line
Line2D lineMN;
if (linesParallel) {
lineMN = GeomUtil.getMedianLine(ptA, ptB, ptC, ptD);
} else {
AffineTransform rotate =
AffineTransform.getRotateInstance(-Math.toRadians(angleDeg) / 2, ptP.getX(), ptP.getY());
Point2D ptM = (Point2D) lineABP[0].clone();
rotate.transform(ptM, ptM);
lineMN = new Line2D.Double(ptM, ptP);
}
if (handlePointIndex == 4 && ptO != null) {
ptO = GeomUtil.getPerpendicularPointToLine(lineMN, ptO);
} else {
if (linesParallel) {
ptO = GeomUtil.getMidPoint(lineMN.getP1(), lineMN.getP1());
} else {
Point2D h1 = GeomUtil.getMidPoint(lineABP[1], GeomUtil.getMidPoint(lineABP[0], lineABP[1]));
Point2D h2 = GeomUtil.getMidPoint(lineCDP[1], GeomUtil.getMidPoint(lineCDP[0], lineCDP[1]));
Point2D o1 = GeomUtil.getPerpendicularPointToLine(lineMN, h1);
Point2D o2 = GeomUtil.getPerpendicularPointToLine(lineMN, h2);
ptO = GeomUtil.getMidPoint(o1, o2);
}
}
} else {
ptO = null;
}
if (pts.size() < 5) {
pts.add(ptO);
} else {
pts.set(4, ptO);
}
}
return handlePointIndex;
}
@Override
public void buildShape(MouseEventDouble mouseEvent) {
updateTool();
Shape newShape = null;
Path2D path = new Path2D.Double(Path2D.WIND_NON_ZERO, 6);
if (lineABvalid) {
path.append(new Line2D.Double(ptA, ptB), false);
}
if (lineCDvalid) {
path.append(new Line2D.Double(ptC, ptD), false);
}
if (lineABvalid && lineCDvalid && ptO != null) {
AdvancedShape aShape = (AdvancedShape) (newShape = new AdvancedShape(this, 10));
aShape.addShape(path);
double ax = ptA.getX(), ay = ptA.getY();
double bx = ptB.getX(), by = ptB.getY();
double cx = ptC.getX(), cy = ptC.getY();
double dx = ptD.getX(), dy = ptD.getY();
// Let I be the perpendicular projection of O onto AB
// Let J be the perpendicular projection of O onto CD
double distAB2 = Point2D.distanceSq(ax, ay, bx, by);
double distCD2 = Point2D.distanceSq(cx, cy, dx, dy);
double r1 = ((ay - ptO.getY()) * (ay - by) + (ax - ptO.getX()) * (ax - bx)) / distAB2;
double r2 = ((cy - ptO.getY()) * (cy - dy) + (cx - ptO.getX()) * (cx - dx)) / distCD2;
final Point2D ptI = new Point2D.Double(ax + r1 * (bx - ax), ay + r1 * (by - ay));
final Point2D ptJ = new Point2D.Double(cx + r2 * (dx - cx), cy + r2 * (dy - cy));
if (r1 < 0 || r1 > 1) {
aShape.addShape(new Line2D.Double(r1 > 1 ? ptB : ptA, ptI), getDashStroke(1.0f), true);
}
if (r2 < 0 || r2 > 1) {
aShape.addShape(new Line2D.Double(r1 > 1 ? ptD : ptC, ptJ), getDashStroke(1.0f), true);
}
aShape.addShape(new Line2D.Double(ptO, ptI));
aShape.addShape(new Line2D.Double(ptO, ptJ));
// Add angle corners where I & J lies on AB & CD line segments
double cLength = 10;
double cImax = (2.0 / 3.0) * Math.min(ptO.distance(ptI), Math.max(ptI.distance(ptA), ptI.distance(ptB)));
aShape.addScaleInvShape(GeomUtil.getCornerShape(GeomUtil.getMidPoint(ptA, ptB), ptI, ptO, cLength), ptI,
cLength / cImax, getStroke(1.0f), true);
double cJmax = (2.0 / 3.0) * Math.min(ptO.distance(ptJ), Math.max(ptJ.distance(ptC), ptJ.distance(ptD)));
aShape.addScaleInvShape(GeomUtil.getCornerShape(GeomUtil.getMidPoint(ptC, ptD), ptJ, ptO, cLength), ptJ,
cLength / cJmax, getStroke(1.0f), true);
if (!linesParallel) {
// Let K be the point on extension of IO segment
// Let L be the point on extension of JO segment
double extSegLength = 32;
Point2D ptK = GeomUtil.getColinearPointWithLength(ptI, ptO, ptO.distance(ptI) + extSegLength);
Point2D ptL = GeomUtil.getColinearPointWithLength(ptJ, ptO, ptO.distance(ptJ) + extSegLength);
double distOKmax = (1.0 / 2.0) * ptO.distance(ptI);
aShape.addScaleInvShape(new Line2D.Double(ptO, ptK), ptO, extSegLength / distOKmax, false);
double distOLmax = (1.0 / 2.0) * ptO.distance(ptJ);
aShape.addScaleInvShape(new Line2D.Double(ptO, ptL), ptO, extSegLength / distOLmax, false);
// Let arcAngle be the partial section of the ellipse that represents the measured angle
double startingAngle =
(ptK.getY() > ptL.getY()) ? GeomUtil.getAngleDeg(ptO, ptJ) : GeomUtil.getAngleDeg(ptO, ptI);
double angularExtent =
ptK.getY() > ptL.getY() ? GeomUtil.getAngleDeg(ptJ, ptO, ptK) : GeomUtil.getAngleDeg(ptI, ptO, ptL);
angularExtent = GeomUtil.getSmallestRotationAngleDeg(angularExtent);
double radius = (2.0 / 3.0) * extSegLength;
Rectangle2D arcAngleBounds =
new Rectangle2D.Double(ptO.getX() - radius, ptO.getY() - radius, 2 * radius, 2 * radius);
Arc2D arcAngle = new Arc2D.Double(arcAngleBounds, startingAngle, angularExtent, Arc2D.OPEN);
double rMax = (ptK.getY() > ptL.getY() ? distOKmax : distOLmax) * 2 / 3;
aShape.addScaleInvShape(arcAngle, ptO, radius / rMax, false);
}
} else if (path.getCurrentPoint() != null) {
newShape = path;
}
setShape(newShape, mouseEvent);
updateLabel(mouseEvent, getDefaultView2d(mouseEvent));
}
@Override
protected void updateTool() {
super.updateTool();
ptO = getHandlePoint(4);
}
}