/** Ported by David Turner from Visilibity, by Karl J. Obermeyer This port undoubtedly introduced a number of bugs (and removed some features). Bug reports should be directed to the OpenTripPlanner project, unless they can be reproduced in the original VisiLibity. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.visibility; public class LineSegment { VLPoint[] endpoints; double distance(VLPoint point_temp) { return point_temp.distance(this); } LineSegment() { endpoints = null; } public int size() { if (endpoints == null) { return 0; } else { return endpoints.length; } } LineSegment(LineSegment line_segment_temp) { switch (line_segment_temp.size()) { case 0: endpoints = null; break; case 1: endpoints = new VLPoint[1]; endpoints[0] = line_segment_temp.endpoints[0].clone(); break; case 2: endpoints = new VLPoint[2]; endpoints[0] = line_segment_temp.endpoints[0].clone(); endpoints[1] = line_segment_temp.endpoints[1].clone(); } } LineSegment(VLPoint point_temp) { endpoints = new VLPoint[1]; endpoints[0] = point_temp; } LineSegment(VLPoint first_point_temp, VLPoint second_point_temp) { this(first_point_temp, second_point_temp, 0); } LineSegment(VLPoint first_point_temp, VLPoint second_point_temp, double epsilon) { if (first_point_temp.distance(second_point_temp) <= epsilon) { endpoints = new VLPoint[1]; endpoints[0] = first_point_temp; } else { endpoints = new VLPoint[2]; endpoints[0] = first_point_temp; endpoints[1] = second_point_temp; } } VLPoint first() { assert (size() > 0); return endpoints[0]; } VLPoint second() { assert (size() > 0); if (size() == 2) return endpoints[1]; else return endpoints[0]; } VLPoint midpoint() { assert (size() > 0); return first().plus(second()).times(0.5); } double length() { assert (size() > 0); return first().distance(second()); } boolean is_in_standard_form() { assert (size() > 0); if (size() < 2) return true; return first().compareTo(second()) <= 0; } /* * assignment operator -- need to audit what's going on with this -DMT LineSegment& operator = * (const LineSegment& line_segment_temp) { //Makes sure not to delete dynamic vars before * they're copied. if(this==&line_segment_temp) return *this; delete [] endpoints; * switch(line_segment_temp.size_){ case 0: endpoints = null; size_ = 0; break; case 1: * endpoints = new Point[1]; endpoints[0] = line_segment_temp.endpoints[0]; size_ = 1; break; * case 2: endpoints = new Point[2]; endpoints[0] = line_segment_temp.endpoints[0]; endpoints[1] * = line_segment_temp.endpoints[1]; size_ = 2; } return *this; } */ void set_first(VLPoint point_temp, double epsilon) { VLPoint second_point_temp; switch (size()) { case 0: endpoints = new VLPoint[1]; endpoints[0] = point_temp; break; case 1: if (endpoints[0].distance(point_temp) <= epsilon) { endpoints[0] = point_temp; return; } second_point_temp = endpoints[0]; endpoints = new VLPoint[2]; endpoints[0] = point_temp; endpoints[1] = second_point_temp; break; case 2: if (point_temp.distance(endpoints[1]) > epsilon) { endpoints[0] = point_temp; return; } endpoints = new VLPoint[1]; endpoints[0] = point_temp; } } void set_second(VLPoint point_temp, double epsilon) { VLPoint first_point_temp; switch (size()) { case 0: endpoints = new VLPoint[1]; endpoints[0] = point_temp; break; case 1: if (endpoints[0].distance(point_temp) <= epsilon) { endpoints[0] = point_temp; return; } first_point_temp = endpoints[0]; endpoints = new VLPoint[2]; endpoints[0] = first_point_temp; endpoints[1] = point_temp; break; case 2: if (endpoints[0].distance(point_temp) > epsilon) { endpoints[1] = point_temp; return; } endpoints = new VLPoint[1]; endpoints[0] = point_temp; break; } } void reverse() { if (size() < 2) return; VLPoint point_temp = endpoints[0]; endpoints[0] = endpoints[1]; endpoints[1] = point_temp; } void enforce_standard_form() { if (first().compareTo(second()) > 0) reverse(); } void clear() { endpoints = null; } public boolean equals(Object o) { if (!(o instanceof LineSegment)) { return false; } LineSegment line_segment2 = (LineSegment) o; if (size() != line_segment2.size() || size() == 0 || line_segment2.size() == 0) return false; else return (first().equals(line_segment2.first()) && second() .equals(line_segment2.second())); } boolean equivalent(LineSegment line_segment2, double epsilon) { if (size() != line_segment2.size() || size() == 0 || line_segment2.size() == 0) return false; else return (first().distance(line_segment2.first()) <= epsilon && second().distance( line_segment2.second()) <= epsilon) || (first().distance(line_segment2.second()) <= epsilon && second().distance( line_segment2.first()) <= epsilon); } double distance(LineSegment line_segment2) { assert (size() > 0 && line_segment2.size() > 0); if (intersect_proper(line_segment2)) return 0; // But if two line segments intersect improperly, the distance // between them is equal to the minimum of the distances between // all 4 endpoints and their respective projections onto the line // segment they don't belong to. double running_min, distance_temp; running_min = first().distance(line_segment2); distance_temp = second().distance(line_segment2); if (distance_temp < running_min) running_min = distance_temp; distance_temp = line_segment2.first().distance(this); if (distance_temp < running_min) running_min = distance_temp; distance_temp = line_segment2.second().distance(this); if (distance_temp < running_min) return distance_temp; return running_min; } double boundary_distance(VLPolygon polygon) { assert (size() > 0 && polygon.n() > 0); double running_min = distance(polygon.get(0)); if (polygon.n() > 1) for (int i = 0; i < polygon.n(); i++) { double d = distance(new LineSegment(polygon.get(i), polygon.get(i + 1))); if (running_min > d) running_min = d; } return running_min; } boolean intersect(LineSegment line_segment2, double epsilon) { if (size() == 0 || line_segment2.size() == 0) return false; if (distance(line_segment2) <= epsilon) return true; return false; } boolean intersect_proper(LineSegment line_segment2) { return intersect_proper(line_segment2, 0); } boolean intersect_proper(LineSegment line_segment2, double epsilon) { if (size() == 0 || line_segment2.size() == 0) return false; // Declare new vars just for readability. VLPoint a = new VLPoint(first()); VLPoint b = new VLPoint(second()); VLPoint c = new VLPoint(line_segment2.first()); VLPoint d = new VLPoint(line_segment2.second()); // First find the minimum of the distances between all 4 endpoints // and their respective projections onto the opposite line segment. double running_min, distance_temp; running_min = a.distance(line_segment2); distance_temp = b.distance(line_segment2); if (distance_temp < running_min) running_min = distance_temp; distance_temp = c.distance(this); if (distance_temp < running_min) running_min = distance_temp; distance_temp = d.distance(this); if (distance_temp < running_min) running_min = distance_temp; // If an endpoint is close enough to the other segment, the // intersection is not considered proper. if (running_min <= epsilon) return false; // This test is from O'Rourke's "Computational Geometry in C", // p.30. Checks left and right turns. if (b.minus(a).cross(c.minus(b)) * b.minus(a).cross(d.minus(b)) < 0 && d.minus(c).cross(b.minus(d)) * d.minus(c).cross(a.minus(d)) < 0) return true; return false; } LineSegment intersection(LineSegment line_segment2, double epsilon) { // Initially empty. LineSegment line_segment_temp = new LineSegment(); if (size() == 0 || line_segment2.size() == 0) return line_segment_temp; // No intersection => return empty segment. if (!intersect(line_segment2, epsilon)) return line_segment_temp; // Declare new vars just for readability. VLPoint a = new VLPoint(first()); VLPoint b = new VLPoint(second()); VLPoint c = new VLPoint(line_segment2.first()); VLPoint d = new VLPoint(line_segment2.second()); if (intersect_proper(line_segment2, epsilon)) { // Use formula from O'Rourke's "Computational Geometry in C", p. 221. // Note D=0 iff the line segments are parallel. double D = a.x * (d.y - c.y) + b.x * (c.y - d.y) + d.x * (b.y - a.y) + c.x * (a.y - b.y); double s = (a.x * (d.y - c.y) + c.x * (a.y - d.y) + d.x * (c.y - a.y)) / D; line_segment_temp.set_first(a.plus(b.minus(a).times(s)), epsilon); return line_segment_temp; } // Otherwise if improper... double distance_temp_a = a.distance(line_segment2); double distance_temp_b = b.distance(line_segment2); double distance_temp_c = c.distance(this); double distance_temp_d = d.distance(this); // Check if the intersection is nondegenerate segment. if (distance_temp_a <= epsilon && distance_temp_b <= epsilon) { line_segment_temp.set_first(a, epsilon); line_segment_temp.set_second(b, epsilon); return line_segment_temp; } else if (distance_temp_c <= epsilon && distance_temp_d <= epsilon) { line_segment_temp.set_first(c, epsilon); line_segment_temp.set_second(d, epsilon); return line_segment_temp; } else if (distance_temp_a <= epsilon && distance_temp_c <= epsilon) { line_segment_temp.set_first(a, epsilon); line_segment_temp.set_second(c, epsilon); return line_segment_temp; } else if (distance_temp_a <= epsilon && distance_temp_d <= epsilon) { line_segment_temp.set_first(a, epsilon); line_segment_temp.set_second(d, epsilon); return line_segment_temp; } else if (distance_temp_b <= epsilon && distance_temp_c <= epsilon) { line_segment_temp.set_first(b, epsilon); line_segment_temp.set_second(c, epsilon); return line_segment_temp; } else if (distance_temp_b <= epsilon && distance_temp_d <= epsilon) { line_segment_temp.set_first(b, epsilon); line_segment_temp.set_second(d, epsilon); return line_segment_temp; } // Check if the intersection is a single point. else if (distance_temp_a <= epsilon) { line_segment_temp.set_first(a, epsilon); return line_segment_temp; } else if (distance_temp_b <= epsilon) { line_segment_temp.set_first(b, epsilon); return line_segment_temp; } else if (distance_temp_c <= epsilon) { line_segment_temp.set_first(c, epsilon); return line_segment_temp; } else if (distance_temp_d <= epsilon) { line_segment_temp.set_first(d, epsilon); return line_segment_temp; } return line_segment_temp; } LineSegment intersection(Ray ray_temp, double epsilon) { return ray_temp.intersection(this, epsilon); } LineSegment intersection(Ray ray_temp) { return ray_temp.intersection(this, 0); } public String toString() { switch (size()) { case 0: return ""; case 1: case 2: return first() + "\n" + second() + "\n"; default: throw new IllegalArgumentException(); } } }