001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math3.geometry.euclidean.twod;
018
019 import org.apache.commons.math3.util.FastMath;
020
021 /** Simple container for a two-points segment.
022 * @version $Id: Segment.java 1422195 2012-12-15 06:45:18Z psteitz $
023 * @since 3.0
024 */
025 public class Segment {
026
027 /** Start point of the segment. */
028 private final Vector2D start;
029
030 /** End point of the segments. */
031 private final Vector2D end;
032
033 /** Line containing the segment. */
034 private final Line line;
035
036 /** Build a segment.
037 * @param start start point of the segment
038 * @param end end point of the segment
039 * @param line line containing the segment
040 */
041 public Segment(final Vector2D start, final Vector2D end, final Line line) {
042 this.start = start;
043 this.end = end;
044 this.line = line;
045 }
046
047 /** Get the start point of the segment.
048 * @return start point of the segment
049 */
050 public Vector2D getStart() {
051 return start;
052 }
053
054 /** Get the end point of the segment.
055 * @return end point of the segment
056 */
057 public Vector2D getEnd() {
058 return end;
059 }
060
061 /** Get the line containing the segment.
062 * @return line containing the segment
063 */
064 public Line getLine() {
065 return line;
066 }
067
068 /** Calculates the shortest distance from a point to this line segment.
069 * <p>
070 * If the perpendicular extension from the point to the line does not
071 * cross in the bounds of the line segment, the shortest distance to
072 * the two end points will be returned.
073 * </p>
074 *
075 * Algorithm adapted from:
076 * <a href="http://www.codeguru.com/forum/printthread.php?s=cc8cf0596231f9a7dba4da6e77c29db3&t=194400&pp=15&page=1">
077 * Thread @ Codeguru</a>
078 *
079 * @param p to check
080 * @return distance between the instance and the point
081 * @since 3.1
082 */
083 public double distance(final Vector2D p) {
084 final double deltaX = end.getX() - start.getX();
085 final double deltaY = end.getY() - start.getY();
086
087 final double r = ((p.getX() - start.getX()) * deltaX + (p.getY() - start.getY()) * deltaY) /
088 (deltaX * deltaX + deltaY * deltaY);
089
090 // r == 0 => P = startPt
091 // r == 1 => P = endPt
092 // r < 0 => P is on the backward extension of the segment
093 // r > 1 => P is on the forward extension of the segment
094 // 0 < r < 1 => P is on the segment
095
096 // if point isn't on the line segment, just return the shortest distance to the end points
097 if (r < 0 || r > 1) {
098 final double dist1 = getStart().distance(p);
099 final double dist2 = getEnd().distance(p);
100
101 return FastMath.min(dist1, dist2);
102 }
103 else {
104 // find point on line and see if it is in the line segment
105 final double px = start.getX() + r * deltaX;
106 final double py = start.getY() + r * deltaY;
107
108 final Vector2D interPt = new Vector2D(px, py);
109 return interPt.distance(p);
110 }
111 }
112 }