/*******************************************************************************
* 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.event.KeyEvent;
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.Collections;
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.api.image.measure.MeasurementsAdapter;
import org.weasis.core.api.image.util.MeasurableLayer;
import org.weasis.core.api.image.util.Unit;
import org.weasis.core.ui.Messages;
import org.weasis.core.ui.model.graphic.AbstractDragGraphic;
import org.weasis.core.ui.model.utils.bean.AdvancedShape;
import org.weasis.core.ui.model.utils.bean.MeasureItem;
import org.weasis.core.ui.model.utils.bean.Measurement;
import org.weasis.core.ui.model.utils.exceptions.InvalidShapeException;
import org.weasis.core.ui.util.MouseEventDouble;
@XmlType(name = "angle")
@XmlRootElement(name = "angle")
public class AngleToolGraphic extends AbstractDragGraphic {
private static final long serialVersionUID = 1228359066740628659L;
public static final Integer POINTS_NUMBER = 3;
public static final Icon ICON = new ImageIcon(AngleToolGraphic.class.getResource("/icon/22x22/draw-angle.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$
public static final Measurement REFLEX_ANGLE =
new Measurement(Messages.getString("AngleToolGraphic.reflex_angle"), 3, 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);
MEASUREMENT_LIST.add(REFLEX_ANGLE);
}
// Let AOB be the triangle that represents the measured angle, O being the intersection point
Point2D ptA;
Point2D ptO;
Point2D ptB;
// estimate if OA & OB line segments are colinear not not
boolean lineColinear;
// estimate if line segments are valid or not
boolean lineOAvalid;
Boolean lineOBvalid;
// smallest angle in Degrees in the range of [-180 ; 180] between OA & OB line segments
double angleDeg;
public AngleToolGraphic() {
super(POINTS_NUMBER);
}
public AngleToolGraphic(AngleToolGraphic graphic) {
super(graphic);
}
@Override
public AngleToolGraphic copy() {
return new AngleToolGraphic(this);
}
protected void init() {
ptA = getHandlePoint(0);
ptO = getHandlePoint(1);
ptB = getHandlePoint(2);
lineColinear = false;
lineOAvalid = lineOBvalid = false;
angleDeg = 0d;
}
@Override
protected void prepareShape() throws InvalidShapeException {
if (!isShapeValid()) {
throw new InvalidShapeException("This shape cannot be drawn"); //$NON-NLS-1$
}
buildShape(null);
}
@Override
public boolean isShapeValid() {
updateTool();
return super.isShapeValid();
}
@Override
public void buildShape(MouseEventDouble mouseEvent) {
updateTool();
Shape newShape = null;
Path2D path = new Path2D.Double(Path2D.WIND_NON_ZERO, 2);
if (lineOAvalid) {
path.append(new Line2D.Double(ptA, ptO), false);
}
if (lineOBvalid) {
path.append(new Line2D.Double(ptO, ptB), false);
}
if (lineOAvalid && lineOBvalid && !lineColinear) {
newShape = new AdvancedShape(this, 2);
AdvancedShape aShape = (AdvancedShape) newShape;
aShape.addShape(path);
// Let arcAngle be the partial section of the ellipse that represents the measured angle
double startingAngle = GeomUtil.getAngleDeg(ptO, ptA);
double radius = 32;
Rectangle2D arcAngleBounds =
new Rectangle2D.Double(ptO.getX() - radius, ptO.getY() - radius, 2 * radius, 2 * radius);
Shape arcAngle = new Arc2D.Double(arcAngleBounds, startingAngle, angleDeg, Arc2D.OPEN);
double rMax = Math.min(ptO.distance(ptA), ptO.distance(ptB)) * 2 / 3;
double scalingMin = radius / rMax;
aShape.addScaleInvShape(arcAngle, ptO, scalingMin, true);
} else if (path.getCurrentPoint() != null) {
newShape = path;
}
setShape(newShape, mouseEvent);
updateLabel(mouseEvent, getDefaultView2d(mouseEvent));
}
@Override
public List<MeasureItem> computeMeasurements(MeasurableLayer layer, boolean releaseEvent, Unit displayUnit) {
if (layer != null && layer.hasContent() && isShapeValid()) {
MeasurementsAdapter adapter = layer.getMeasurementAdapter(displayUnit);
if (adapter != null) {
ArrayList<MeasureItem> measVal = new ArrayList<>();
double positiveAngle = Math.abs(angleDeg);
if (ANGLE.getComputed()) {
measVal.add(new MeasureItem(ANGLE, positiveAngle, Messages.getString("measure.deg"))); //$NON-NLS-1$
}
if (COMPLEMENTARY_ANGLE.getComputed()) {
measVal.add(
new MeasureItem(COMPLEMENTARY_ANGLE, 180.0 - positiveAngle, Messages.getString("measure.deg"))); //$NON-NLS-1$
}
if (REFLEX_ANGLE.getComputed()) {
measVal
.add(new MeasureItem(REFLEX_ANGLE, 360.0 - positiveAngle, Messages.getString("measure.deg"))); //$NON-NLS-1$
}
return measVal;
}
}
return Collections.emptyList();
}
@Override
public List<Measurement> getMeasurementList() {
return MEASUREMENT_LIST;
}
@Override
public Icon getIcon() {
return ICON;
}
@Override
public String getUIName() {
return Messages.getString("measure.angle"); //$NON-NLS-1$
}
@Override
public int getKeyCode() {
return KeyEvent.VK_A;
}
@Override
public int getModifier() {
return 0;
}
protected void updateTool() {
init();
lineOAvalid = (ptA != null && ptO != null && !ptO.equals(ptA));
lineOBvalid = (ptB != null && ptO != null && !ptO.equals(ptB));
if (lineOAvalid && lineOBvalid) {
angleDeg = GeomUtil.getSmallestRotationAngleDeg(GeomUtil.getAngleDeg(ptA, ptO, ptB));
lineColinear = GeomUtil.lineColinear(ptO, ptA, ptO, ptB);
}
}
}