Revision: 5484 http://sourceforge.net/p/jump-pilot/code/5484 Author: michaudm Date: 2017-08-05 14:05:48 +0000 (Sat, 05 Aug 2017) Log Message: ----------- Improvement from ioan to honour IncrementalAngle constraint even on the first segment
Modified Paths: -------------- core/trunk/ChangeLog core/trunk/src/org/openjump/core/ui/plugin/edittoolbox/tab/ConstraintManager.java Modified: core/trunk/ChangeLog =================================================================== --- core/trunk/ChangeLog 2017-07-31 10:48:49 UTC (rev 5483) +++ core/trunk/ChangeLog 2017-08-05 14:05:48 UTC (rev 5484) @@ -3,6 +3,10 @@ # 2. make sure that lines break at 80 chars for constricted display situations #<-------------------------------- 80 chars ----------------------------------># +2017-08-05 + * Improvement from ioan to honour IncrementalAngle constraint even on the first + segment + 2017-07-29 mmichaud <m.michael.mich...@orange.fr> * BlendLineStringsPlugIn : improve quality, performance and make it undoable * Deactivate ReplaceValuePlugIn (use AutoAssignAttributePlugIn instead) Modified: core/trunk/src/org/openjump/core/ui/plugin/edittoolbox/tab/ConstraintManager.java =================================================================== --- core/trunk/src/org/openjump/core/ui/plugin/edittoolbox/tab/ConstraintManager.java 2017-07-31 10:48:49 UTC (rev 5483) +++ core/trunk/src/org/openjump/core/ui/plugin/edittoolbox/tab/ConstraintManager.java 2017-08-05 14:05:48 UTC (rev 5484) @@ -34,193 +34,228 @@ package org.openjump.core.ui.plugin.edittoolbox.tab; import java.awt.event.MouseEvent; -import java.awt.geom.NoninvertibleTransformException; import java.util.List; import org.openjump.core.geomutils.GeoUtils; import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.math.Vector2D; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.ui.LayerViewPanel; import com.vividsolutions.jump.workbench.ui.plugin.PersistentBlackboardPlugIn; +/** + * Constraint Manager + * Improved by ioan on 2017-08-05 to honour IncrementalAngle constraint even on the first segment + */ public class ConstraintManager { - public static final String CONSTRAIN_LENGTH_ENABLED_KEY = "CONSTRAIN_LENGTH - ENABLED"; - public static final String CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY = "CONSTRAIN_INCREMENTAL_ANGLE - ENABLED"; - public static final String CONSTRAIN_ANGLE_ENABLED_KEY = "CONSTRAIN_ANGLE - ENABLED"; - public static final String LENGTH_CONSTRAINT_KEY = "LENGTH_CONSTRAINT"; - public static final String INCREMENTAL_ANGLE_SIZE_KEY = "INCREMENTAL_ANGLE_CONSTRAINT"; - public static final String ANGLE_SIZE_KEY = "ANGLE_CONSTRAINT"; - public static final String RELATIVE_ANGLE_KEY = "RELATIVE_ANGLE_CONSTRAINT"; - public static final String ABSOLUTE_ANGLE_KEY = "ABSOLUTE_ANGLE_CONSTRAINT"; - public static final String CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY = "CONSTRAIN_RECTANGLE_RATIO - ENABLED"; - public static final String RATIO_WIDTH_KEY = "RATIO_WIDTH_CONSTRAINT"; - public static final String RATIO_HEIGHT_KEY = "RATIO_HEIGHT_CONSTRAINT"; - protected LayerViewPanel panel; - WorkbenchContext workbenchContext; + public static final String CONSTRAIN_LENGTH_ENABLED_KEY = "CONSTRAIN_LENGTH - ENABLED"; + public static final String CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY = "CONSTRAIN_INCREMENTAL_ANGLE - ENABLED"; + public static final String CONSTRAIN_ANGLE_ENABLED_KEY = "CONSTRAIN_ANGLE - ENABLED"; + public static final String LENGTH_CONSTRAINT_KEY = "LENGTH_CONSTRAINT"; + public static final String INCREMENTAL_ANGLE_SIZE_KEY = "INCREMENTAL_ANGLE_CONSTRAINT"; + public static final String ANGLE_SIZE_KEY = "ANGLE_CONSTRAINT"; + public static final String RELATIVE_ANGLE_KEY = "RELATIVE_ANGLE_CONSTRAINT"; + public static final String ABSOLUTE_ANGLE_KEY = "ABSOLUTE_ANGLE_CONSTRAINT"; + public static final String CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY = "CONSTRAIN_RECTANGLE_RATIO - ENABLED"; + public static final String RATIO_WIDTH_KEY = "RATIO_WIDTH_CONSTRAINT"; + public static final String RATIO_HEIGHT_KEY = "RATIO_HEIGHT_CONSTRAINT"; + protected LayerViewPanel panel; + WorkbenchContext workbenchContext; - public ConstraintManager(WorkbenchContext workbenchContext) - { - this.workbenchContext = workbenchContext; + public ConstraintManager(WorkbenchContext workbenchContext) { + this.workbenchContext = workbenchContext; + } + + public Coordinate constrain(LayerViewPanel panel, List coordinates, + Coordinate targetPt, MouseEvent e) { + if (coordinates == null) + return targetPt; + this.panel = panel; + int numPts = coordinates.size(); + boolean shiftConstrain = (e.isShiftDown() && (numPts > 0)); // was 1 + boolean ctrlConstrain = (e.isControlDown() && (numPts > 1)); + Coordinate retPt = (Coordinate) targetPt.clone(); + + if ((PersistentBlackboardPlugIn.get(workbenchContext) + .get(CONSTRAIN_LENGTH_ENABLED_KEY, false)) && (numPts >= 1)) { + double lengthConstraint = PersistentBlackboardPlugIn.get(workbenchContext) + .getDouble(LENGTH_CONSTRAINT_KEY); + if (lengthConstraint > 0) { + Coordinate anchorPt = (Coordinate) coordinates.get(numPts - 1); + double run = targetPt.x - anchorPt.x; + double rise = targetPt.y - anchorPt.y; + double prevLength = anchorPt.distance(targetPt); + double t1 = Math.round(prevLength / lengthConstraint); + double newLength = t1 * lengthConstraint; + double ratio = 1; + if (prevLength != 0) // this happens when mouse clicked on first point + // without mouse move + ratio = newLength / prevLength; + retPt.x = anchorPt.x + (ratio * run); + retPt.y = anchorPt.y + (ratio * rise); + } } - public Coordinate constrain(LayerViewPanel panel, List coordinates, Coordinate targetPt, MouseEvent e) - { - if (coordinates == null) return targetPt; - this.panel = panel; - int numPts = coordinates.size(); - boolean shiftConstrain = (e.isShiftDown() && (numPts > 1)); - boolean ctrlConstrain = (e.isControlDown() && (numPts > 1)); - Coordinate retPt = (Coordinate)targetPt.clone(); + if (PersistentBlackboardPlugIn.get(workbenchContext) + .get(CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) { + if (shiftConstrain) { + int incrementalAngleConstraint = PersistentBlackboardPlugIn + .get(workbenchContext).getInt(INCREMENTAL_ANGLE_SIZE_KEY); + // If only one point, set startPt to next to last point + Coordinate startPt = new Coordinate(); + if (numPts == 1) + startPt = (Coordinate) coordinates.get(numPts - 1); + else + startPt = (Coordinate) coordinates.get(numPts - 2); + Coordinate endPt = (Coordinate) coordinates.get(numPts - 1); + retPt = constrainIncrementalAngle(startPt, endPt, retPt, + incrementalAngleConstraint); + } + } - if ((PersistentBlackboardPlugIn.get(workbenchContext).get(CONSTRAIN_LENGTH_ENABLED_KEY, false)) && (numPts >= 1)) - { - double lengthConstraint = PersistentBlackboardPlugIn.get(workbenchContext).getDouble(LENGTH_CONSTRAINT_KEY); - if (lengthConstraint > 0) - { - Coordinate anchorPt = (Coordinate)coordinates.get(numPts - 1); - double run = targetPt.x - anchorPt.x; - double rise = targetPt.y - anchorPt.y; - double prevLength = anchorPt.distance(targetPt); - double t1 = Math.round(prevLength / lengthConstraint); - double newLength = t1 * lengthConstraint; - double ratio = 1; - if (prevLength != 0) //this happens when mouse clicked on first point without mouse move - ratio = newLength / prevLength; - retPt.x = anchorPt.x + (ratio * run); - retPt.y = anchorPt.y + (ratio * rise); - } + if (PersistentBlackboardPlugIn.get(workbenchContext) + .get(CONSTRAIN_ANGLE_ENABLED_KEY, false) && (numPts > 1)) { + double theta = -PersistentBlackboardPlugIn.get(workbenchContext) + .getDouble(ANGLE_SIZE_KEY); + + if (PersistentBlackboardPlugIn.get(workbenchContext) + .getBoolean(RELATIVE_ANGLE_KEY)) { + if (shiftConstrain) { + Coordinate startPt = (Coordinate) coordinates.get(numPts - 2); + Coordinate endPt = (Coordinate) coordinates.get(numPts - 1); + double length = endPt.distance(retPt); + Coordinate newPt = constructVector(startPt, endPt, length); + retPt = GeoUtils.rotPt(newPt, endPt, theta); } - - if (PersistentBlackboardPlugIn.get(workbenchContext).get(CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) - { - if(shiftConstrain) - { - int incrementalAngleConstraint = PersistentBlackboardPlugIn.get(workbenchContext).getInt(INCREMENTAL_ANGLE_SIZE_KEY); - Coordinate startPt = (Coordinate)coordinates.get(numPts - 2); - Coordinate endPt = (Coordinate)coordinates.get(numPts - 1); - retPt = constrainIncrementalAngle(startPt, endPt, retPt, incrementalAngleConstraint); - } + } + + else // ABSOLUTE_ANGLE_KEY + { + if (e.isShiftDown() && (numPts >= 1)) { + Coordinate startPt = (Coordinate) coordinates.get(numPts - 1); + Coordinate endPt = (Coordinate) startPt.clone(); + endPt.x += startPt.distance(retPt); + retPt = GeoUtils.rotPt(endPt, startPt, theta); } - - if (PersistentBlackboardPlugIn.get(workbenchContext).get(CONSTRAIN_ANGLE_ENABLED_KEY, false)) - { - double theta = -PersistentBlackboardPlugIn.get(workbenchContext).getDouble(ANGLE_SIZE_KEY); - - if (PersistentBlackboardPlugIn.get(workbenchContext).getBoolean(RELATIVE_ANGLE_KEY)) - { - if(shiftConstrain) - { - Coordinate startPt = (Coordinate)coordinates.get(numPts - 2); - Coordinate endPt = (Coordinate)coordinates.get(numPts - 1); - double length = endPt.distance(retPt); - Coordinate newPt = constructVector(startPt, endPt, length); - retPt = GeoUtils.rotPt(newPt, endPt, theta); - } - } - - else //ABSOLUTE_ANGLE_KEY - { - if (e.isShiftDown() && (numPts >= 1)) - { - Coordinate startPt = (Coordinate)coordinates.get(numPts - 1); - Coordinate endPt = (Coordinate)startPt.clone(); - endPt.x += startPt.distance(retPt); - retPt = GeoUtils.rotPt(endPt, startPt, theta); - } - } - } - - if (PersistentBlackboardPlugIn.get(workbenchContext).get(CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) - { - if(ctrlConstrain) - { - int incrementalAngleConstraint = PersistentBlackboardPlugIn.get(workbenchContext).getInt(INCREMENTAL_ANGLE_SIZE_KEY); - Coordinate startPt = (Coordinate)coordinates.get(1); - Coordinate endPt = (Coordinate)coordinates.get(0); - Coordinate p1 = (Coordinate)coordinates.get(numPts - 1); - Coordinate p2 = (Coordinate) retPt.clone(); - Coordinate p3 = (Coordinate)coordinates.get(0); - Coordinate p4 = constrainIncrementalAngle(startPt, endPt, retPt, incrementalAngleConstraint); - Coordinate intxPt = GeoUtils.getIntersection(p1, p2, p3, p4); - if (intxPt.z == 0) //z <> 0 means that the lines are parallel - retPt = new Coordinate(intxPt.x, intxPt.y); - } - } - return retPt; + } } - - protected Coordinate constrainIncrementalAngle(Coordinate startPt, Coordinate endPt, Coordinate targetPt, int angleConstraint) - { - double targetLength = endPt.distance(targetPt); - Coordinate newPt = constructVector(startPt, endPt, targetLength); - Coordinate retPt = (Coordinate)newPt.clone(); - double currDist = targetPt.distance(newPt); - double theta = 360 / angleConstraint; - for (int i = 0; i < angleConstraint - 1; i++) - { - newPt = GeoUtils.rotPt(newPt, endPt, theta); - double newDist = targetPt.distance(newPt); - if (newDist < currDist) - { - currDist = newDist; - retPt = (Coordinate)newPt.clone(); - } - } - return retPt; + + if (PersistentBlackboardPlugIn.get(workbenchContext) + .get(CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) { + if (ctrlConstrain) { + int incrementalAngleConstraint = PersistentBlackboardPlugIn + .get(workbenchContext).getInt(INCREMENTAL_ANGLE_SIZE_KEY); + Coordinate startPt = (Coordinate) coordinates.get(1); + Coordinate endPt = (Coordinate) coordinates.get(0); + Coordinate p1 = (Coordinate) coordinates.get(numPts - 1); + Coordinate p2 = (Coordinate) retPt.clone(); + Coordinate p3 = (Coordinate) coordinates.get(0); + Coordinate p4 = constrainIncrementalAngle(startPt, endPt, retPt, + incrementalAngleConstraint); + Coordinate intxPt = GeoUtils.getIntersection(p1, p2, p3, p4); + if (intxPt.z == 0) // z <> 0 means that the lines are parallel + retPt = new Coordinate(intxPt.x, intxPt.y); + } } - - public Coordinate constrainRectangleToRatio(LayerViewPanel panel, List coordinates, Coordinate targetPt, MouseEvent e) throws NoninvertibleTransformException - { - if (coordinates == null) return targetPt; - this.panel = panel; - boolean shiftConstrain = ((PersistentBlackboardPlugIn.get(workbenchContext).get(CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY, false)) && (e.isShiftDown())); + return retPt; + } - double ratioWidth = PersistentBlackboardPlugIn.get(workbenchContext).get(RATIO_WIDTH_KEY, 1.0); - double ratioHeight = PersistentBlackboardPlugIn.get(workbenchContext).get(RATIO_HEIGHT_KEY, 1.0); - double ratio = ratioWidth/ratioHeight; - - if ((coordinates.size() >= 1) && (shiftConstrain)) - { - Coordinate firstCoordinate = (Coordinate) coordinates.get(0); - double yLength = Math.abs((firstCoordinate.x - targetPt.x)) / ratio; - if (targetPt.y > firstCoordinate.y) - return new Coordinate(targetPt.x, firstCoordinate.y + yLength); - else - return new Coordinate(targetPt.x, firstCoordinate.y - yLength); - } - return targetPt; - } - - private Coordinate constructVector(Coordinate startPt, Coordinate endPt, double dist) - //this routine will use startPt & endPt to determine the slope of the new vector - //it will calculate a new vector from endPt with the slope and dist - { - double run = endPt.x - startPt.x; - double rise = endPt.y - startPt.y; - double sideLength = startPt.distance(endPt); - double ratio = dist / sideLength; - return new Coordinate(endPt.x + (ratio * run), endPt.y + (ratio * rise)); + protected Coordinate constrainIncrementalAngle(Coordinate startPt, + Coordinate endPt, Coordinate targetPt, int angleConstraint) { + + // this version uses the JTS Vector class to make a unit vector along + // the direction from the start to end point, + // if there's only one point then start along the x angle + Vector2D endVector = new Vector2D(endPt); + // unit direction vector along vector defined by start->end + Vector2D dirVect; + if (!startPt.equals2D(endPt)) + dirVect = new Vector2D(startPt, endPt).normalize(); + else + dirVect = new Vector2D(1, 0); // x axis + + Vector2D targetVect = new Vector2D(endPt, targetPt); + double theta = 360 / angleConstraint; + + double currDist = Double.POSITIVE_INFINITY; + Coordinate retPt = new Coordinate(); + Vector2D outVector; + + for (int i = 0; i < angleConstraint - 1; i++) { + dirVect = dirVect.rotate(theta * Math.PI / 180); + // use dot product to get component of target vector along the unit + // direction vector + double dotProduct = dirVect.dot(targetVect); + // Take the dot product times the unit direction vector and + // add it to the endpoint vector + outVector = endVector.add(dirVect.multiply(dotProduct)); + double newDist = targetPt.distance(outVector.toCoordinate()); + if (newDist < currDist) { + currDist = newDist; + retPt = outVector.toCoordinate(); + } } - - public double getBearing(Coordinate startPt, Coordinate endPt) - //return Bearing in degrees (-180 to +180) from startPt to endPt - { - Coordinate r = new Coordinate(endPt.x - startPt.x, endPt.y - startPt.y); - double rMag = Math.sqrt(r.x * r.x + r.y * r.y ); - if (rMag == 0.0) - { - return 0.0; - } - else - { - double rCos = r.x / rMag; - double rAng = Math.acos(rCos); - - if (r.y < 0.0) - rAng = -rAng; - return rAng * 360.0 / (2 * Math.PI); - } + return retPt; + } + + /* + public Coordinate constrainRectangleToRatio(LayerViewPanel panel, + List coordinates, Coordinate targetPt, MouseEvent e) + throws NoninvertibleTransformException { + if (coordinates == null) + return targetPt; + this.panel = panel; + boolean shiftConstrain = ((PersistentBlackboardPlugIn.get(workbenchContext) + .get(CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY, false)) + && (e.isShiftDown())); + + double ratioWidth = PersistentBlackboardPlugIn.get(workbenchContext) + .get(RATIO_WIDTH_KEY, 1.0); + double ratioHeight = PersistentBlackboardPlugIn.get(workbenchContext) + .get(RATIO_HEIGHT_KEY, 1.0); + double ratio = ratioWidth / ratioHeight; + + if ((coordinates.size() >= 1) && (shiftConstrain)) { + Coordinate firstCoordinate = (Coordinate) coordinates.get(0); + double yLength = Math.abs((firstCoordinate.x - targetPt.x)) / ratio; + if (targetPt.y > firstCoordinate.y) + return new Coordinate(targetPt.x, firstCoordinate.y + yLength); + else + return new Coordinate(targetPt.x, firstCoordinate.y - yLength); } + return targetPt; + } + */ + private Coordinate constructVector(Coordinate startPt, Coordinate endPt, + double dist) + // this routine will use startPt & endPt to determine the slope of the new + // vector + // it will calculate a new vector from endPt with the slope and dist + { + double run = endPt.x - startPt.x; + double rise = endPt.y - startPt.y; + double sideLength = startPt.distance(endPt); + double ratio = dist / sideLength; + return new Coordinate(endPt.x + (ratio * run), endPt.y + (ratio * rise)); + } + + public double getBearing(Coordinate startPt, Coordinate endPt) + // return Bearing in degrees (-180 to +180) from startPt to endPt + { + Coordinate r = new Coordinate(endPt.x - startPt.x, endPt.y - startPt.y); + double rMag = Math.sqrt(r.x * r.x + r.y * r.y); + if (rMag == 0.0) { + return 0.0; + } else { + double rCos = r.x / rMag; + double rAng = Math.acos(rCos); + + if (r.y < 0.0) + rAng = -rAng; + return rAng * 360.0 / (2 * Math.PI); + } + } + } ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel