/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* 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:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.geom;
import java.util.Iterator;
import java.util.List;
import com.vividsolutions.jts.geom.*;
/**
* A heuristic used by Microscope to expand a single segment,
* while maintaining its orientation.
*/
public class SingleSegmentExpander {
private Coordinate[] adjPt = new Coordinate[2];
public SingleSegmentExpander() {
}
public static Envelope getInsetEnvelope(Envelope env, double insetPct) {
double insetX = (env.getWidth() * insetPct) / 2;
double insetY = (env.getWidth() * insetPct) / 2;
double inset = insetX;
if (insetY < inset) {
inset = insetY;
}
return new Envelope(env.getMinX() + inset, env.getMaxX() - inset,
env.getMinY() + inset, env.getMaxY() - inset);
}
public boolean isApplicable(List segList, List ptList) {
if (segList.size() < 1) {
return false;
}
LineSegment seg = (LineSegment) segList.get(0);
return allSegsEqual(seg, segList) && allPtsInSeg(seg, ptList);
}
private boolean allSegsEqual(LineSegment seg, List segList) {
for (Iterator i = segList.iterator(); i.hasNext();) {
LineSegment seg2 = (LineSegment) i.next();
if (!seg.equalsTopo(seg2)) {
return false;
}
}
return true;
}
private boolean allPtsInSeg(LineSegment seg, List ptList) {
for (Iterator i = ptList.iterator(); i.hasNext();) {
Coordinate pt = (Coordinate) i.next();
if (seg.p0.equals(pt)) {
return true;
}
if (seg.p1.equals(pt)) {
return true;
}
}
return false;
}
public Coordinate[] expandSegment(LineSegment seg, Envelope env) {
Envelope insetEnv = getInsetEnvelope(env, 0.2);
double dx = seg.p1.x - seg.p0.x;
double dy = seg.p1.y - seg.p0.y;
if (Math.abs(dx) <= 1.0E-6) {
double y0 = insetEnv.getMinY();
double y1 = insetEnv.getMaxY();
if (seg.p0.y < seg.p1.y) {
y0 = insetEnv.getMaxY();
y1 = insetEnv.getMinY();
}
adjPt[0] = new Coordinate(seg.p0.x, y0);
adjPt[1] = new Coordinate(seg.p0.x, y1);
return adjPt;
}
if (Math.abs(dy) <= 1.0E-6) {
double x0 = insetEnv.getMinX();
double x1 = insetEnv.getMaxX();
if (seg.p0.x < seg.p1.x) {
x0 = insetEnv.getMaxX();
x1 = insetEnv.getMinX();
}
adjPt[0] = new Coordinate(x0, seg.p0.y);
adjPt[1] = new Coordinate(x1, seg.p0.y);
return adjPt;
}
// compute intersection of ray with inset Envelope
adjPt[0] = rayEnvIntersection(seg.p0, seg.p1, insetEnv);
adjPt[1] = rayEnvIntersection(seg.p1, seg.p0, insetEnv);
return adjPt;
}
/**
* Computes the intersection of the ray p0-p1 with one of the edges
* of the envelope
*/
private Coordinate rayEnvIntersection(Coordinate p0, Coordinate p1,
Envelope env) {
Coordinate x0 = segIntX(p0, p1, env.getMinX(), env.getMinY(),
env.getMaxY());
if (x0 != null) {
return x0;
}
Coordinate x1 = segIntX(p0, p1, env.getMaxX(), env.getMinY(),
env.getMaxY());
if (x1 != null) {
return x1;
}
Coordinate y0 = segIntY(p0, p1, env.getMinY(), env.getMinX(),
env.getMaxX());
if (y0 != null) {
return y0;
}
Coordinate y1 = segIntY(p0, p1, env.getMaxY(), env.getMinX(),
env.getMaxX());
if (y1 != null) {
return y1;
}
// should never reach here - if we do, need a tolerance in segInt routines
return null;
}
/**
* Computes the dot product of the vectors p-p0 and p-p1.
* If the dot product is negative the vectors contain an obtuse angle,
* if positive they contain an acute angle. If the angle is acute,
* the vectors can be considered to be "in the same direction".
*/
private double dotProduct(Coordinate p, Coordinate p0, Coordinate p1) {
double dx0 = p0.x - p.x;
double dy0 = p0.y - p.y;
double dx1 = p1.x - p.x;
double dy1 = p1.y - p.y;
return (dx0 * dx1) + (dy0 * dy1);
}
private Coordinate segIntX(Coordinate p0, Coordinate p1, double x,
double miny, double maxy) {
double m = (p1.y - p0.y) / (p1.x - p0.x);
double y2 = (m * (x - p0.x)) + p0.y;
if ((y2 > miny) && (y2 < maxy)) {
Coordinate intPt = new Coordinate(x, y2);
if (dotProduct(p0, p1, intPt) < 0.0) {
return intPt;
}
}
return null;
}
private Coordinate segIntY(Coordinate p0, Coordinate p1, double y,
double minx, double maxx) {
double m = (p1.x - p0.x) / (p1.y - p0.y);
double x2 = (m * (y - p0.y)) + p0.x;
if ((x2 > minx) && (x2 < maxx)) {
Coordinate intPt = new Coordinate(x2, y);
if (dotProduct(p0, p1, intPt) < 0.0) {
return intPt;
}
}
return null;
}
}