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 */
017package org.apache.commons.math3.geometry.euclidean.twod;
018
019import org.apache.commons.math3.geometry.Point;
020import org.apache.commons.math3.util.FastMath;
021
022/** Simple container for a two-points segment.
023 * @since 3.0
024 */
025public class Segment {
026
027    /** Start point of the segment. */
028    private final Vector2D start;
029
030    /** End point of the segment. */
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((Point<Euclidean2D>) p);
099            final double dist2 = getEnd().distance((Point<Euclidean2D>) 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((Point<Euclidean2D>) p);
110        }
111    }
112}