Skip to main content

More on picking

4 replies [Last post]
asantiago
Offline
Joined: 2006-11-09
Points: 0

Hi group,

thanks to the people replies my previous post new I'm able to draw polylines (finally I use SGShape subclass with GeneralPath).

Now the problem is if I draw a line only two points using SGShape.Mode.STROKE (without fill setted) it seems the picking process never "detects" my object.
In the same way, if I draw polylines with 3 points (imagine a triangle without the line in its base) the picking process "detects" the object when I'm "inside the triangle.

What I want is to know when I'm exactly over the line?

Thanks in advice for any ideas.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

Judging from the contains code in SGShape.contains() it should be
testing against just the outline, not the interior of the shape if you
have the Mode set to STROKE (without FILL). Also, it shouldn't have an
issue with just 2-point lines. It looks like it should do what you want.

Can you post a sample that shows the problem?

...jim

scenario@javadesktop.org wrote:
> Hi group,
>
> thanks to the people replies my previous post new I'm able to draw polylines (finally I use SGShape subclass with GeneralPath).
>
> Now the problem is if I draw a line only two points using SGShape.Mode.STROKE (without fill setted) it seems the picking process never "detects" my object.
> In the same way, if I draw polylines with 3 points (imagine a triangle without the line in its base) the picking process "detects" the object when I'm "inside the triangle.
>
> What I want is to know when I'm exactly over the line?
>
> Thanks in advice for any ideas.
> [Message sent by forum member 'asantiago' (asantiago)]
>
> http://forums.java.net/jive/thread.jspa?messageID=313284
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
> For additional commands, e-mail: dev-help@scenegraph.dev.java.net
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net

rexguo
Offline
Joined: 2004-06-21
Points: 0

You might have to implement your own point-line intersection test to
know if your cursor is exactly on or near the line in order to do the
selection. Here's how I do it (not scenario-based):

public boolean isSelectableBy(int x, int y)
{
mousePosition.setLocation(x, y);
outputPosition.setLocation(getOutputPoint().x, getOutputPoint().y);
inputPosition.setLocation(getInputPoint().x, getInputPoint().y);

double d = distancePointLine(mousePosition,
outputPosition,
inputPosition);

if(d < 5)
{
return(true);
}

return(false);
}

protected double distancePointLine( Point2D.Double point,
Point2D.Double lineStart,
Point2D.Double lineEnd)
{
double LineMag2;
double U;
final Point2D.Double intersection = new Point2D.Double();

LineMag2 = lineEnd.distanceSq(lineStart);

U = ( ( ( point.x - lineStart.x ) * ( lineEnd.x - lineStart.x ) ) +
( ( point.y - lineStart.y ) * ( lineEnd.y - lineStart.y ) ) ) /
LineMag2;

if( U < 0.0f || U > 1.0f ) return(Float.MAX_VALUE); // closest point does not fall within the line segment

intersection.x = (float)(lineStart.x + U * ( lineEnd.x - lineStart.x ));
intersection.y = (float)(lineStart.y + U * ( lineEnd.y - lineStart.y ));

return(point.distance(intersection));
}

The important method is distancePointLine. It measures the perpendicular
distance between a point and a line. If the distance is zero, then the point
is exactly on the line.

.rex

william13163
Offline
Joined: 2008-03-19
Points: 0

rexguo, asantiago

There is a another way to pick lines (aka line segments) if you are comfortable with using the Java Line2D class from awt.geom; of course, only if you have the option of using the awt.geom.Line2D class.

Below is my code for determining which segment the mouse is over:
You can control how "sensitive" the mouse is (how close it needs to be before the mouse is considered touching it) by changing the distance factor during the check "if (_segment.ptSegDist(cursor) < DISTANCE)".
Note: knots, in my code, are the same as the end points of the lines.

The two key lines are:
[code]
_segment.setLine(pSegStart, pSetEnd);
if (_segment.ptSegDist(cursor) < DISTANCE)
[/code]

[code]
private static final double DISTANCE = 3.0;
private Line2D _segment = new Line2D.Double();
...

public boolean determineSegment(Point2D cursor, ArrayList knots)
{
_onSegment = false;
if (knots.size() > 1)
{
pSegStart = knots.get(0);
for (Point2D p1 : knots.subList(1, knots.size()))
{
pSetEnd = p1;
_segment.setLine(pSegStart, pSetEnd);
if (_segment.ptSegDist(cursor) < DISTANCE)
{
_onSegment = true;
break;
}
pSegStart = pSetEnd;
}
}
return _onSegment;
}
[/code]

So, everytime the mouse moves while inside the wall node, I mark the segment the mouse is on, then have the wall repaint to show the highlight:
[code]
public void mouseMoved(MouseEvent e, SGNode node)
{
boolean onSegment = determineSegment(e.getPoint());
_wallLeaf.paint();
}
[/code]

The wall leaf's paint method only paints using its bounds--as mentioned in the other thread.:
[code]
public class SGWallLeaf extends SGAbstractVisual
{
... other code here

public void paint()
{
repaint(getBounds());
}
}
[/code]

To determine the segment the mouse point in view-space needs to mapped to local-space--that is the globalToLocal(...) call:
[code]
public boolean determineSegment(Point2D pCursor)
{
globalToLocal(pCursor, _pLocal);
ArrayList knots = _modelWall.getKnots();
return _segment.determineSegment(_pLocal, knots);
}
[/code]

When the wall node paints itself it also paints the last segment that a mouse was over. The wall has a reference to the segment that was marked:
[code]
_wallLeaf.setSegment(_segment);
[/code]

Then, when the wall paints itself:
[code]
public void paint(Graphics2D g)
{
// BEGIN State management
AffineTransform gt = g.getTransform();
Color c = g.getColor();
Stroke s = g.getStroke();

... ---- more code to paint other stuff

if (_segment.isOnSegment())
{
g.setColor(_segmentHighlightColor);
Point2D p0 = _segment.getSegStart();
Point2D p1 = _segment.getSetEnd();

g.drawLine((int) p0.getX(), (int) p0.getY(), (int) p1.getX(), (int) p1
.getY());
}
// END State management
g.setColor(c);
g.setStroke(s);
g.setTransform(gt);
}
[/code]

My simple segment class is:
[code]
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;

public class WallSegment
{
private static final double DISTANCE = 3.0;
private Line2D _segment = new Line2D.Double();
private Point2D pSegStart = null;
private Point2D pSetEnd = null;

private boolean _onSegment = false;

// These are references to the actual

public boolean determineSegment(Point2D cursor, ArrayList knots)
{
_onSegment = false;
if (knots.size() > 1)
{
pSegStart = knots.get(0);
for (Point2D p1 : knots.subList(1, knots.size()))
{
pSetEnd = p1;
_segment.setLine(pSegStart, pSetEnd);
if (_segment.ptSegDist(cursor) < DISTANCE)
{
_onSegment = true;
break;
}
pSegStart = pSetEnd;
}
}
return _onSegment;
}

public boolean isOnSegment()
{
return _onSegment;
}

public Point2D getSegStart()
{
return pSegStart;
}

public Point2D getSetEnd()
{
return pSetEnd;
}

}
[/code]

asantiago, if you need extra flexibility you may be better of using rexguo's implementation, but the Line2D class was created specifically for picking lines and segments. Of course, always remember what coordinate space you are working in. ;)

Cheers.

william13163
Offline
Joined: 2008-03-19
Points: 0

Forgive me,

I am trying to determine what you are trying to do. Are you trying to click and drag on a segment between two points and move it around, or are you trying to move the whole polygon?
And by "detect" you mean you don't get any SGMouseEvents, for example, mouseeenter or mouseexit.

It seems like you are looking for enter and exit events in your polygon. The scenegraph will not pick properly if you bounding rectangle is not correct, be it small or too large. If too large and you may already be in the polygon and you won't get an exit event. Anyway, I don't know exactly what you asking.

I too, draw polygons and am able to edit them by dragging on points, segments and the whole polygon.

P.S. It may help the rest of us, if it is possible, for you to post your solution to your previous problem. :-)