/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* JUMP is Copyright (C) 2003 Vivid Solutions
*
* This program implements extensions to JUMP and is
* Copyright (C) 2004 Integrated Systems Analysts, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Integrated Systems Analysts, Inc.
* 630C Anchors St., Suite 101
* Fort Walton Beach, Florida
* USA
*
* (850)862-7321
*/
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.jump.workbench.WorkbenchContext;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.plugin.PersistentBlackboardPlugIn;
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 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 > 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);
}
}
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);
}
}
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;
}
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);
}
}
}