Oups, classic : forgot the attachment... :-S
Sorry for spamming...
De : Lecomte, J-Francois
Envoyé : 15 March 2012 15:47
À : 'openmap-users_at_bbn.com'
Objet : BasicGeometry.distanceToEdge doesn't handle SEG_CLOSE properly.
The method distanceToEdge(int x, int y) in class BasicGeometry does not handle the SEG_CLOSE properly, here is a JUnit4 test to illustrate the problem:
public class BasicGeometryTest
{
private static final float DELTA = 0.00001f;
/**
* Test method for {_at_link BasicGeometry#distanceToEdge(int, int)}.
*
* The shape tested looks like this:
*
* <pre>
* |
* | p2__p3
* | | |
* | |__|
* | p1 p4
* |__________________
*
* p1:(1, 1)
* p2:(1, 3)
* p3:(3, 3)
* p4:(3, 1)
* </pre>
*/
_at_Test
public void testDistanceToEdge()
{
final Polygon shape = new Polygon();
shape.addPoint(1, 1);
shape.addPoint(1, 3);
shape.addPoint(3, 3);
shape.addPoint(3, 1);
final MyBasicGeometry geometry = new MyBasicGeometry(new GeneralPath(shape));
// Test points on line
assertEquals(0, geometry.distanceToEdge(1, 1), 0);
assertEquals(0, geometry.distanceToEdge(1, 2), 0);
assertEquals(0, geometry.distanceToEdge(1, 3), 0);
assertEquals(0, geometry.distanceToEdge(2, 1), 0); // Fails here but should not!
assertEquals(0, geometry.distanceToEdge(2, 3), 0);
assertEquals(0, geometry.distanceToEdge(3, 1), 0);
assertEquals(0, geometry.distanceToEdge(3, 2), 0);
assertEquals(0, geometry.distanceToEdge(3, 3), 0);
// Test point inside
assertEquals(1, geometry.distanceToEdge(2, 2), 0);
// Test distance from the left
assertEquals(Math.sqrt(2), geometry.distanceToEdge(0, 0), DELTA);
assertEquals(1, geometry.distanceToEdge(0, 1), 0);
assertEquals(1, geometry.distanceToEdge(0, 2), 0);
assertEquals(1, geometry.distanceToEdge(0, 3), 0);
assertEquals(Math.sqrt(2), geometry.distanceToEdge(0, 4), DELTA);
// Test distance from the right
assertEquals(Math.sqrt(2), geometry.distanceToEdge(4, 0), DELTA);
assertEquals(1, geometry.distanceToEdge(4, 1), 0);
assertEquals(1, geometry.distanceToEdge(4, 2), 0);
assertEquals(1, geometry.distanceToEdge(4, 3), 0);
assertEquals(Math.sqrt(2), geometry.distanceToEdge(4, 4), DELTA);
// Test distance from the bottom
assertEquals(1, geometry.distanceToEdge(1, 0), 0);
assertEquals(1, geometry.distanceToEdge(2, 0), 0);
assertEquals(1, geometry.distanceToEdge(3, 0), 0);
// Test distance from the top
assertEquals(1, geometry.distanceToEdge(1, 4), 0);
assertEquals(1, geometry.distanceToEdge(2, 4), 0);
assertEquals(1, geometry.distanceToEdge(3, 4), 0);
}
private static class MyBasicGeometry extends OMGraphic
{
public MyBasicGeometry(final GeneralPath shape)
{
super.shape = shape;
super.needToRegenerate = false;
}
_at_Override
public boolean generate(final Projection proj)
{
return false; // Whatever, I'm not using it...
}
}
}
Here is an implementation of the method that fixes the problem:
/**
* Return the shortest distance from the edge of a shape to an XY-point.
* <p>
* Method taken and adapted from {_at_link BasicGeometry#distanceToEdge(int, int)}
*
* _at_param x X coordinate of the point.
* _at_param y Y coordinate of the point.
* _at_return float distance, in pixels, from graphic to the point. Returns
* Float.POSITIVE_INFINITY if the graphic isn't ready (ungenerated).
*/
_at_Override
public float distanceToEdge(final int x, final int y)
{
float distance = Float.POSITIVE_INFINITY;
if(getNeedToRegenerate() || shape == null)
{
return distance;
}
final PathIterator pi2 = shape.getPathIterator(null);
final FlatteningPathIterator pathIt = new FlatteningPathIterator(pi2, .25);
final double[] coords = new double[6];
double endPntX = Double.NaN;
double endPntY = Double.NaN;
double lastMovedToPntX = Double.NaN;
double lastMovedToPntY = Double.NaN;
while(!pathIt.isDone())
{
final int type = pathIt.currentSegment(coords);
if(type == PathIterator.SEG_LINETO)
{
final double startPntX = endPntX;
final double startPntY = endPntY;
endPntX = coords[0];
endPntY = coords[1];
final float dist = (float) Line2D.ptSegDist(startPntX, startPntY, endPntX,
endPntY, x, y);
if(dist < distance)
{
distance = dist;
}
}
else if(type == PathIterator.SEG_MOVETO)
{
endPntX = coords[0];
endPntY = coords[1];
lastMovedToPntX = coords[0];
lastMovedToPntY = coords[1];
}
else if(type == PathIterator.SEG_CLOSE)
{
final double startPntX = lastMovedToPntX;
final double startPntY = lastMovedToPntY;
endPntX = coords[0];
endPntY = coords[1];
final float dist = (float) Line2D.ptSegDist(startPntX, startPntY, endPntX,
endPntY, x, y);
if(dist < distance)
{
distance = dist;
}
}
pathIt.next();
}
return distance;
}
I joined as attachment a bunch of JUnit4 tests to validate this method.
Also, I suggest this method be moved as a static public method in a Shape Utility class so it can be used without a "BasicGeometry" object (the shape object obviously should be received as parameter).
--
[To unsubscribe to this list send an email to "majdart_at_bbn.com"
with the following text in the BODY of the message "unsubscribe openmap-users"]
Received on Thu Mar 15 2012 - 15:54:58 EDT