/*******************************************************************************
* 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.line;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
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.gui.util.MathUtil;
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.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 = "parallelLine")
@XmlRootElement(name = "parallelLine")
public class ParallelLineGraphic extends AbstractDragGraphic {
private static final long serialVersionUID = -3254108307003667982L;
public static final Integer POINTS_NUMBER = 6;
public static final Icon ICON =
new ImageIcon(ParallelLineGraphic.class.getResource("/icon/22x22/draw-parallel.png")); //$NON-NLS-1$
public static final Measurement DISTANCE =
new Measurement(Messages.getString("measure.distance"), 1, true, true, true); //$NON-NLS-1$
public static final Measurement ORIENTATION =
new Measurement(Messages.getString("measure.orientation"), 2, true, true, false); //$NON-NLS-1$
public static final Measurement AZIMUTH =
new Measurement(Messages.getString("measure.azimuth"), 3, true, true, false); //$NON-NLS-1$
protected static final List<Measurement> MEASUREMENT_LIST = new ArrayList<>();
static {
MEASUREMENT_LIST.add(DISTANCE);
MEASUREMENT_LIST.add(ORIENTATION);
MEASUREMENT_LIST.add(AZIMUTH);
}
// Let AB & CD two parallel line segments
protected Point2D.Double ptA;
protected Point2D.Double ptB;
protected Point2D.Double ptC;
protected Point2D.Double ptD;
// Let E,F middle points of AB & CD
protected Point2D.Double ptE;
protected Point2D.Double ptF;
// estimate if line segments are valid or not
protected Boolean lineABvalid;
protected Boolean lineCDvalid;
public ParallelLineGraphic() {
super(POINTS_NUMBER);
}
public ParallelLineGraphic(ParallelLineGraphic graphic) {
super(graphic);
}
@Override
public ParallelLineGraphic copy() {
return new ParallelLineGraphic(this);
}
@Override
public Icon getIcon() {
return ICON;
}
@Override
public String getUIName() {
return Messages.getString("measure.parallel"); //$NON-NLS-1$
}
@Override
protected void prepareShape() throws InvalidShapeException {
if (!isShapeValid()) {
throw new InvalidShapeException("This shape cannot be drawn"); //$NON-NLS-1$
}
buildShape(null);
}
@Override
public Integer moveAndResizeOnDrawing(Integer handlePointIndex, Double deltaX, Double deltaY,
MouseEventDouble mouseEvent) {
handlePointIndex = super.moveAndResizeOnDrawing(handlePointIndex, deltaX, deltaY, mouseEvent);
if (handlePointIndex >= 0 && handlePointIndex < getHandlePointListSize()) {
updateTool();
if (lineABvalid && lineCDvalid) {
if (handlePointIndex == 0 || handlePointIndex == 1) {
// drag point is A or B
Point2D anchor = (handlePointIndex == 0) ? ptB : ptA;
double theta =
GeomUtil.getSmallestAngleRad(GeomUtil.getAngleRad(ptC, ptD) - GeomUtil.getAngleRad(ptA, ptB));
// rotation angle around anchor point
AffineTransform rotate = AffineTransform.getRotateInstance(theta, anchor.getX(), anchor.getY());
rotate.transform(ptC, ptC);
rotate.transform(ptD, ptD);
setHandlePoint(2, ptC);
setHandlePoint(3, ptD);
} else if (handlePointIndex == 2 || handlePointIndex == 3) {
// drag point is C or D
Point2D.Double pt1 = (handlePointIndex == 2) ? ptC : ptD;
Point2D.Double pt2 = (handlePointIndex == 2) ? ptD : ptC;
int hIndex = (handlePointIndex == 2) ? 3 : 2;
Point2D ptI = GeomUtil.getPerpendicularPointToLine(ptA, ptB, pt1);
Point2D ptJ = GeomUtil.getPerpendicularPointToLine(ptA, ptB, pt2);
double transX = (pt1.getX() - ptI.getX()) - (pt2.getX() - ptJ.getX());
double transY = (pt1.getY() - ptI.getY()) - (pt2.getY() - ptJ.getY());
AffineTransform translate = AffineTransform.getTranslateInstance(transX, transY);
translate.transform(pt2, pt2);
setHandlePoint(hIndex, pt2);
} else if (handlePointIndex == 4 || handlePointIndex == 5) {
// drag point is E middle of AB or F middle of CD
Point2D.Double pt0 = (handlePointIndex == 4) ? ptE : ptF;
Point2D.Double pt1 = (handlePointIndex == 4) ? ptA : ptC;
Point2D.Double pt2 = (handlePointIndex == 4) ? ptB : ptD;
int hIndex1 = (handlePointIndex == 4) ? 0 : 2;
int hIndex2 = (handlePointIndex == 4) ? 1 : 3;
if (Objects.nonNull(pt0)) {
Point2D ptI = GeomUtil.getPerpendicularPointToLine(pt1, pt2, pt0);
AffineTransform translate =
AffineTransform.getTranslateInstance(pt0.getX() - ptI.getX(), pt0.getY() - ptI.getY());
translate.transform(pt1, pt1);
translate.transform(pt2, pt2);
setHandlePoint(hIndex1, pt1);
setHandlePoint(hIndex2, pt2);
}
}
setHandlePoint(4, GeomUtil.getMidPoint(ptA, ptB));
setHandlePoint(5, GeomUtil.getMidPoint(ptC, ptD));
}
}
return handlePointIndex;
}
@Override
public void buildShape(MouseEventDouble mouseEvent) {
updateTool();
Shape newShape = null;
Path2D path = new Path2D.Double(Path2D.WIND_NON_ZERO, 2);
if (lineABvalid) {
path.append(new Line2D.Double(ptA, ptB), false);
}
if (lineCDvalid) {
path.append(new Line2D.Double(ptC, ptD), false);
}
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<>(3);
if (DISTANCE.getComputed()) {
Double val =
ptC.distance(GeomUtil.getPerpendicularPointToLine(ptA, ptB, ptC)) * adapter.getCalibRatio();
measVal.add(new MeasureItem(DISTANCE, val, adapter.getUnit()));
}
if (ORIENTATION.getComputed()) {
measVal.add(new MeasureItem(ORIENTATION, MathUtil.getOrientation(ptA, ptB),
Messages.getString("measure.deg"))); //$NON-NLS-1$
}
if (AZIMUTH.getComputed()) {
measVal.add(
new MeasureItem(AZIMUTH, MathUtil.getAzimuth(ptA, ptB), Messages.getString("measure.deg"))); //$NON-NLS-1$
}
return measVal;
}
}
return Collections.emptyList();
}
@Override
public boolean isShapeValid() {
updateTool();
return lineABvalid && lineCDvalid;
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////
protected void updateTool() {
ptA = getHandlePoint(0);
ptB = getHandlePoint(1);
ptC = getHandlePoint(2);
ptD = getHandlePoint(3);
ptE = getHandlePoint(4);
ptF = getHandlePoint(5);
lineABvalid = ptA != null && ptB != null && !ptB.equals(ptA);
lineCDvalid = ptC != null && ptD != null && !ptC.equals(ptD);
}
@Override
public List<Measurement> getMeasurementList() {
return MEASUREMENT_LIST;
}
}