Added distance to point to 2D Line and Segment.

Patch provided by Curtis Jensen applied with minor modifications.

JIRA: MATH-641

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1392022 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2012-09-30 13:22:28 +00:00
parent 3246e42006
commit df92c3b6f1
6 changed files with 111 additions and 1 deletions

View File

@ -192,6 +192,9 @@
<contributor> <contributor>
<name>Matthias Hummel</name> <name>Matthias Hummel</name>
</contributor> </contributor>
<contributor>
<name>Curtis Jensen</name>
</contributor>
<contributor> <contributor>
<name>Ismael Juma</name> <name>Ismael Juma</name>
</contributor> </contributor>

View File

@ -52,8 +52,11 @@ If the output is not quite correct, check for invisible trailing spaces!
<body> <body>
<release version="3.1" date="TBD" description=" <release version="3.1" date="TBD" description="
"> ">
<action dev="luc" type="fix" issue="MATH-641" due-to="Curtis Jensen">
Added distance to point to 2D Line and Segment.
</action>
<action dev="erans" type="fix" issue="MATH-783"> <action dev="erans" type="fix" issue="MATH-783">
"PowellOptimizer" (package "o.a.c.m.optimization.direct") uses "PowellOptimizer" (package "o.a.c.m.optimization.direct") uses
"BrentOptimizer" as its internal line search optimizer. The fix "BrentOptimizer" as its internal line search optimizer. The fix
forces the convergence criterion of "BrentOptimizer" to use forces the convergence criterion of "BrentOptimizer" to use
function values (instead of domain values). function values (instead of domain values).

View File

@ -263,6 +263,18 @@ public class Line implements Hyperplane<Euclidean2D>, Embedding<Euclidean2D, Euc
return FastMath.abs(getOffset(p)) < 1.0e-10; return FastMath.abs(getOffset(p)) < 1.0e-10;
} }
/** Compute the distance between the instance and a point.
* This is a shortcut for invoking FastMath.abs(getOffset(p)),
* and provides consistency with what is in the
* org.apache.commons.math3.geometry.euclidean.threed.Line class.
*
* @param p to check
* @return distance between the instance and the point
*/
public double distance(final Vector2D p) {
return FastMath.abs(getOffset(p));
}
/** Check the instance is parallel to another line. /** Check the instance is parallel to another line.
* @param line other line to check * @param line other line to check
* @return true if the instance is parallel to the other line * @return true if the instance is parallel to the other line

View File

@ -16,6 +16,7 @@
*/ */
package org.apache.commons.math3.geometry.euclidean.twod; package org.apache.commons.math3.geometry.euclidean.twod;
import org.apache.commons.math3.util.FastMath;
/** Simple container for a two-points segment. /** Simple container for a two-points segment.
* @version $Id$ * @version $Id$
@ -64,4 +65,43 @@ public class Segment {
return line; return line;
} }
/**
* Calculates the shortest distance from a point to this line segment.
* <p>
* If the perpendicular extension from the point to the line does not
* cross in the bounds of the line segment, the shortest distance to
* the two end points will be returned.
* </p>
*
* Algorithm adapted from: http://www.codeguru.com/forum/printthread.php?s=cc8cf0596231f9a7dba4da6e77c29db3&t=194400&pp=15&page=1
*/
public double distance(final Vector2D p) {
final double deltaX = end.getX() - start.getX();
final double deltaY = end.getY() - start.getY();
final double r = ((p.getX() - start.getX()) * deltaX + (p.getY() - start.getY()) * deltaY) /
(deltaX * deltaX + deltaY * deltaY);
// r == 0 => P = startPt
// r == 1 => P = endPt
// r < 0 => P is on the backward extension of the segment
// r > 1 => P is on the forward extension of the segment
// 0 < r < 1 => P is on the segment
// if point isn't on the line segment, just return the shortest distance to the end points
if (r < 0 || r > 1) {
final double dist1 = getStart().distance(p);
final double dist2 = getEnd().distance(p);
return FastMath.min(dist1, dist2);
}
else {
// find point on line and see if it is in the line segment
final double px = start.getX() + r * deltaX;
final double py = start.getY() + r * deltaY;
final Vector2D interPt = new Vector2D(px, py);
return interPt.distance(p);
}
}
} }

View File

@ -63,6 +63,13 @@ public class LineTest {
Assert.assertEquals(+5.0, l.getOffset(new Vector2D(-5, 2)), 1.0e-10); Assert.assertEquals(+5.0, l.getOffset(new Vector2D(-5, 2)), 1.0e-10);
} }
@Test
public void testDistance() {
Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2));
Assert.assertEquals(+5.0, l.distance(new Vector2D(5, -3)), 1.0e-10);
Assert.assertEquals(+5.0, l.distance(new Vector2D(-5, 2)), 1.0e-10);
}
@Test @Test
public void testPointAt() { public void testPointAt() {
Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2)); Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2));

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.twod;
import org.apache.commons.math3.geometry.euclidean.twod.Line;
import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math3.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
public class SegmentTest {
@Test
public void testDistance() {
Vector2D start = new Vector2D(2, 2);
Vector2D end = new Vector2D(-2, -2);
Segment segment = new Segment(start, end, new Line(start, end));
// distance to center of segment
Assert.assertEquals(FastMath.sqrt(2), segment.distance(new Vector2D(1, -1)), 1.0e-10);
// distance a point on segment
Assert.assertEquals(FastMath.sin(Math.PI / 4.0), segment.distance(new Vector2D(0, -1)), 1.0e-10);
// distance to end point
Assert.assertEquals(FastMath.sqrt(8), segment.distance(new Vector2D(0, 4)), 1.0e-10);
// distance to start point
Assert.assertEquals(FastMath.sqrt(8), segment.distance(new Vector2D(0, -4)), 1.0e-10);
}
}