001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * JUMP is Copyright (C) 2003 Vivid Solutions
006: *
007: * This program implements extensions to JUMP and is
008: * Copyright (C) 2004 Integrated Systems Analysts, Inc.
009: *
010: * This program is free software; you can redistribute it and/or
011: * modify it under the terms of the GNU General Public License
012: * as published by the Free Software Foundation; either version 2
013: * of the License, or (at your option) any later version.
014: *
015: * This program is distributed in the hope that it will be useful,
016: * but WITHOUT ANY WARRANTY; without even the implied warranty of
017: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
018: * GNU General Public License for more details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with this program; if not, write to the Free Software
022: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
023: *
024: * For more information, contact:
025: *
026: * Integrated Systems Analysts, Inc.
027: * 630C Anchors St., Suite 101
028: * Fort Walton Beach, Florida
029: * USA
030: *
031: * (850)862-7321
032: */
033:
034: package org.openjump.core.ui.plugin.edittoolbox.tab;
035:
036: import java.awt.event.MouseEvent;
037: import java.awt.geom.NoninvertibleTransformException;
038: import java.util.List;
039:
040: import org.openjump.core.geomutils.GeoUtils;
041:
042: import com.vividsolutions.jts.geom.Coordinate;
043: import com.vividsolutions.jump.workbench.WorkbenchContext;
044: import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
045: import com.vividsolutions.jump.workbench.ui.plugin.PersistentBlackboardPlugIn;
046:
047: public class ConstraintManager {
048: public static final String CONSTRAIN_LENGTH_ENABLED_KEY = "CONSTRAIN_LENGTH - ENABLED";
049: public static final String CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY = "CONSTRAIN_INCREMENTAL_ANGLE - ENABLED";
050: public static final String CONSTRAIN_ANGLE_ENABLED_KEY = "CONSTRAIN_ANGLE - ENABLED";
051: public static final String LENGTH_CONSTRAINT_KEY = "LENGTH_CONSTRAINT";
052: public static final String INCREMENTAL_ANGLE_SIZE_KEY = "INCREMENTAL_ANGLE_CONSTRAINT";
053: public static final String ANGLE_SIZE_KEY = "ANGLE_CONSTRAINT";
054: public static final String RELATIVE_ANGLE_KEY = "RELATIVE_ANGLE_CONSTRAINT";
055: public static final String ABSOLUTE_ANGLE_KEY = "ABSOLUTE_ANGLE_CONSTRAINT";
056: public static final String CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY = "CONSTRAIN_RECTANGLE_RATIO - ENABLED";
057: public static final String RATIO_WIDTH_KEY = "RATIO_WIDTH_CONSTRAINT";
058: public static final String RATIO_HEIGHT_KEY = "RATIO_HEIGHT_CONSTRAINT";
059: protected LayerViewPanel panel;
060: WorkbenchContext workbenchContext;
061:
062: public ConstraintManager(WorkbenchContext workbenchContext) {
063: this .workbenchContext = workbenchContext;
064: }
065:
066: public Coordinate constrain(LayerViewPanel panel, List coordinates,
067: Coordinate targetPt, MouseEvent e) {
068: if (coordinates == null)
069: return targetPt;
070: this .panel = panel;
071: int numPts = coordinates.size();
072: boolean shiftConstrain = (e.isShiftDown() && (numPts > 1));
073: boolean ctrlConstrain = (e.isControlDown() && (numPts > 1));
074: Coordinate retPt = (Coordinate) targetPt.clone();
075:
076: if ((PersistentBlackboardPlugIn.get(workbenchContext).get(
077: CONSTRAIN_LENGTH_ENABLED_KEY, false))
078: && (numPts >= 1)) {
079: double lengthConstraint = PersistentBlackboardPlugIn.get(
080: workbenchContext).getDouble(LENGTH_CONSTRAINT_KEY);
081: if (lengthConstraint > 0) {
082: Coordinate anchorPt = (Coordinate) coordinates
083: .get(numPts - 1);
084: double run = targetPt.x - anchorPt.x;
085: double rise = targetPt.y - anchorPt.y;
086: double prevLength = anchorPt.distance(targetPt);
087: double t1 = Math.round(prevLength / lengthConstraint);
088: double newLength = t1 * lengthConstraint;
089: double ratio = 1;
090: if (prevLength != 0) //this happens when mouse clicked on first point without mouse move
091: ratio = newLength / prevLength;
092: retPt.x = anchorPt.x + (ratio * run);
093: retPt.y = anchorPt.y + (ratio * rise);
094: }
095: }
096:
097: if (PersistentBlackboardPlugIn.get(workbenchContext).get(
098: CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) {
099: if (shiftConstrain) {
100: int incrementalAngleConstraint = PersistentBlackboardPlugIn
101: .get(workbenchContext).getInt(
102: INCREMENTAL_ANGLE_SIZE_KEY);
103: Coordinate startPt = (Coordinate) coordinates
104: .get(numPts - 2);
105: Coordinate endPt = (Coordinate) coordinates
106: .get(numPts - 1);
107: retPt = constrainIncrementalAngle(startPt, endPt,
108: retPt, incrementalAngleConstraint);
109: }
110: }
111:
112: if (PersistentBlackboardPlugIn.get(workbenchContext).get(
113: CONSTRAIN_ANGLE_ENABLED_KEY, false)) {
114: double theta = -PersistentBlackboardPlugIn.get(
115: workbenchContext).getDouble(ANGLE_SIZE_KEY);
116:
117: if (PersistentBlackboardPlugIn.get(workbenchContext)
118: .getBoolean(RELATIVE_ANGLE_KEY)) {
119: if (shiftConstrain) {
120: Coordinate startPt = (Coordinate) coordinates
121: .get(numPts - 2);
122: Coordinate endPt = (Coordinate) coordinates
123: .get(numPts - 1);
124: double length = endPt.distance(retPt);
125: Coordinate newPt = constructVector(startPt, endPt,
126: length);
127: retPt = GeoUtils.rotPt(newPt, endPt, theta);
128: }
129: }
130:
131: else //ABSOLUTE_ANGLE_KEY
132: {
133: if (e.isShiftDown() && (numPts >= 1)) {
134: Coordinate startPt = (Coordinate) coordinates
135: .get(numPts - 1);
136: Coordinate endPt = (Coordinate) startPt.clone();
137: endPt.x += startPt.distance(retPt);
138: retPt = GeoUtils.rotPt(endPt, startPt, theta);
139: }
140: }
141: }
142:
143: if (PersistentBlackboardPlugIn.get(workbenchContext).get(
144: CONSTRAIN_INCREMENTAL_ANGLE_ENABLED_KEY, false)) {
145: if (ctrlConstrain) {
146: int incrementalAngleConstraint = PersistentBlackboardPlugIn
147: .get(workbenchContext).getInt(
148: INCREMENTAL_ANGLE_SIZE_KEY);
149: Coordinate startPt = (Coordinate) coordinates.get(1);
150: Coordinate endPt = (Coordinate) coordinates.get(0);
151: Coordinate p1 = (Coordinate) coordinates
152: .get(numPts - 1);
153: Coordinate p2 = (Coordinate) retPt.clone();
154: Coordinate p3 = (Coordinate) coordinates.get(0);
155: Coordinate p4 = constrainIncrementalAngle(startPt,
156: endPt, retPt, incrementalAngleConstraint);
157: Coordinate intxPt = GeoUtils.getIntersection(p1, p2,
158: p3, p4);
159: if (intxPt.z == 0) //z <> 0 means that the lines are parallel
160: retPt = new Coordinate(intxPt.x, intxPt.y);
161: }
162: }
163: return retPt;
164: }
165:
166: protected Coordinate constrainIncrementalAngle(Coordinate startPt,
167: Coordinate endPt, Coordinate targetPt, int angleConstraint) {
168: double targetLength = endPt.distance(targetPt);
169: Coordinate newPt = constructVector(startPt, endPt, targetLength);
170: Coordinate retPt = (Coordinate) newPt.clone();
171: double currDist = targetPt.distance(newPt);
172: double theta = 360 / angleConstraint;
173: for (int i = 0; i < angleConstraint - 1; i++) {
174: newPt = GeoUtils.rotPt(newPt, endPt, theta);
175: double newDist = targetPt.distance(newPt);
176: if (newDist < currDist) {
177: currDist = newDist;
178: retPt = (Coordinate) newPt.clone();
179: }
180: }
181: return retPt;
182: }
183:
184: public Coordinate constrainRectangleToRatio(LayerViewPanel panel,
185: List coordinates, Coordinate targetPt, MouseEvent e)
186: throws NoninvertibleTransformException {
187: if (coordinates == null)
188: return targetPt;
189: this .panel = panel;
190: boolean shiftConstrain = ((PersistentBlackboardPlugIn
191: .get(workbenchContext).get(
192: CONSTRAIN_RECTANGLE_RATIO_ENABLED_KEY, false)) && (e
193: .isShiftDown()));
194:
195: double ratioWidth = PersistentBlackboardPlugIn.get(
196: workbenchContext).get(RATIO_WIDTH_KEY, 1.0);
197: double ratioHeight = PersistentBlackboardPlugIn.get(
198: workbenchContext).get(RATIO_HEIGHT_KEY, 1.0);
199: double ratio = ratioWidth / ratioHeight;
200:
201: if ((coordinates.size() >= 1) && (shiftConstrain)) {
202: Coordinate firstCoordinate = (Coordinate) coordinates
203: .get(0);
204: double yLength = Math.abs((firstCoordinate.x - targetPt.x))
205: / ratio;
206: if (targetPt.y > firstCoordinate.y)
207: return new Coordinate(targetPt.x, firstCoordinate.y
208: + yLength);
209: else
210: return new Coordinate(targetPt.x, firstCoordinate.y
211: - yLength);
212: }
213: return targetPt;
214: }
215:
216: private Coordinate constructVector(Coordinate startPt,
217: Coordinate endPt, double dist)
218: //this routine will use startPt & endPt to determine the slope of the new vector
219: //it will calculate a new vector from endPt with the slope and dist
220: {
221: double run = endPt.x - startPt.x;
222: double rise = endPt.y - startPt.y;
223: double sideLength = startPt.distance(endPt);
224: double ratio = dist / sideLength;
225: return new Coordinate(endPt.x + (ratio * run), endPt.y
226: + (ratio * rise));
227: }
228:
229: public double getBearing(Coordinate startPt, Coordinate endPt)
230: //return Bearing in degrees (-180 to +180) from startPt to endPt
231: {
232: Coordinate r = new Coordinate(endPt.x - startPt.x, endPt.y
233: - startPt.y);
234: double rMag = Math.sqrt(r.x * r.x + r.y * r.y);
235: if (rMag == 0.0) {
236: return 0.0;
237: } else {
238: double rCos = r.x / rMag;
239: double rAng = Math.acos(rCos);
240:
241: if (r.y < 0.0)
242: rAng = -rAng;
243: return rAng * 360.0 / (2 * Math.PI);
244: }
245: }
246:
247: }
|