Board index » delphi » PolyLine component help

PolyLine component help

I want to create a polyline that I can interact with a mouse.  I've
considered a few ways to do this and I have run into a wall.  The problem is
the rectangular nature of TControl.  Let's say I have a layer containing
lines superimposed over a layer of squares.  If a line object in the top
layer is clicked someplace in the rectangle of the line component, but isn't
actually on the line, a square component visible but below the line's
rectangle doesn't receive the click.  But what I want to happen is for the
line component to actually be the line itself not the encompassing
rectangle.  So a click that misses the actual line would just pass through
to the next control in the z order.

To help you understand my exact problem I'll provide a little background.
I'm working on a program that creates a vector based navigation chart (like
a road map) and I want to draw interactive course lines.  The chart would
contain other interactive objects, such as ship wreck or reef locations.

One way I've thought to solve this problem is to create a line layer that
actually handles all click events (loops the line list looking for click
matches) and then passes the click off to the next layer in the z order.
Each layer would check for objects that match the click event and until a
match was found the event could just be passed down the layer stack.  The
problem with this is that there could potentially be hundreds of interactive
objects visible on each layer.

Any ideas would be greatly appreciated.

--
Mark Lauter

Irreverence is the champion of liberty and its only sure defense.
  - Mark Twain

 

Re:PolyLine component help


Quote
> I want to create a polyline that I can interact with a mouse.

<snip>

I am unfamiliar with something I just read about - clipping.  Does this
sound like something that might help me?

--
Mark Lauter

Irreverence is the champion of liberty and its only sure defense.
  - Mark Twain

Re:PolyLine component help


Quote
> I want to create a polyline that I can interact with a mouse.  I've
> considered a few ways to do this and I have run into a wall.  The problem
is
> the rectangular nature of TControl.  Let's say I have a layer containing
> lines superimposed over a layer of squares.  If a line object in the top
> layer is clicked someplace in the rectangle of the line component, but
isn't
> actually on the line, a square component visible but below the line's
> rectangle doesn't receive the click.  But what I want to happen is for the
> line component to actually be the line itself not the encompassing
> rectangle.  So a click that misses the actual line would just pass through
> to the next control in the z order.

I guess there are a lot of different ways to solve your problem. If you can
I would avoid using separate controls for each polyline. Can't you use one
control that holds a list of all polylines and handles the click event
internally? TList can be sorted the way you like, then if you receive a
click, check whether the first polyline is clicked, if not, check the second
one and so on. Of course you need your own algorithm for detecting whether a
point (the mouse coordinates) is on (or near) a line, this was discussed
here a few weeks ago (if you don't find it, ask again, maybe someone
remembers how it goes). The paint method would also loop through the list
and paint all polylines. You could also easily add a load and save method to
your control that loads / saves all of your polylines (instead of an
external method that loops through each control).

Jens

Re:PolyLine component help


Quote
> I guess there are a lot of different ways to solve your problem. If you
can
> I would avoid using separate controls for each polyline.

<snip>

Thanks for the feedback.  I guess we're leaning that direction now with all
of our visual controls for the chart.  If the perfomance seems too slow,
we'll figure out a way to speed it up.

Cheers,

--
Mark Lauter

Irreverence is the champion of liberty and its only sure defense.
  - Mark Twain

Re:PolyLine component help


Quote
"Mark Lauter" <marklau...@hotmail.com> wrote in message

news:3cfe1aa3$1_2@dnews...

Quote
> > I guess there are a lot of different ways to solve your problem. If you
> can
> > I would avoid using separate controls for each polyline.
> <snip>
> Thanks for the feedback.  I guess we're leaning that direction now with
all
> of our visual controls for the chart.  If the perfomance seems too slow,
> we'll figure out a way to speed it up.

One way to speed up the process, at the expense of memory, would be
creating a 2D grid structure where each cell points to a list of
polylines/indices
of those polylines that are within (or crossing) the grid. Of course you
have to
update the grid each time polylines change so you might want to link back
from
the polylines to the grids. I would only go this far if there are a total of
more
than several thousand vertices.

Re:PolyLine component help


Hi Mark,

What you need to do is build in some tolerance when checking where the user
clicks. In other words, you need to find out how far the click is from a
line, and if this is within the desired tolerance, you will accept it as a
click on the line.

There is two situations
1) Polylines with straight line segments
2) Polylines with curved segments, aka Bezier curves (Polybezier).

For 1) I have a solution for you directly. I wrote this algorithm for a
project quite some time ago and up till now I have never encountered
anywhere a faster method. See code below.

For 2) I want you to have a look at this example (posted on my website). An
executable, but certified virus free :)
http://www.abc-view.com/downloads/bezier.exe

It shows how you can approximate a user-drawn line, with a certain
tolerance. As a part of it, there's code that will detect how far a certain
point (click) is from the bezier curve. I can't give you the source to this
example for free though. Contact me if you're interested. It could fit well
into your map drawing.

Nils Haeck
http://www.abc-view.com/

This is the code for finding a point closest to a line segment. Run it for
each of the segments in your polyline

{
  MinDistPointLine calculates the minimum distance of a point to a line.
  P is the point, the line is between points A and B.
   It is based on the distance of P to the parametrised point Q = (1-q)A+qB
  where 0 <= q <= 1.
  The distance PQ is sqrt( ((1-q)Ax + qBx - Px)^2 + (... Y term) ).
  Differentiating gives dPQ/dq = 2((Bx-Ax)q + (Ax-Px))(Bx - Ax) + (... Y term).
  dPQ/dq must be zero for minimum so
  q = (Px-Ax)(Bx-Ax)+(Py-Ay)(By-Ay) / ((Bx-Ax)^2+(By-Ay)^2)

Quote
}

function MinDistPointLine(Px, Py, Ax, Ay, Bx, By: double): double;

implementation

function PointToPointDist(Ax, Ay, Bx, By: double): double;
begin
  Result := sqrt(sqr(Bx-Ax) + sqr(By-Ay));
end;

function MinDistPointLine(Px, Py, Ax, Ay, Bx, By: double): double;
var
  q: double;
begin
  if (Ax=Bx) and (Ay=By) then begin

   // Point to point
    Result := PointToPointDist(Px, Py, Ax, Ay);

  end else begin

    // Minimum
    q := ((Px-Ax)*(Bx-Ax) + (Py-Ay)*(By-Ay)) / (sqr(Bx-Ax) + sqr(By-Ay));

    // Limit q to 0 <= q <= 1
    if q < 0 then q := 0;
    if q > 1 then q := 1;

    // Distance
    Result := PointToPointDist(Px, Py, (1-q)*Ax + q*Bx, (1-q)*Ay + q*By);

  end;
end;

Quote
Mark Lauter <marklau...@hotmail.com> wrote in message

news:3cfd2034$1_2@dnews...
Quote
> I want to create a polyline that I can interact with a mouse.  I've
> considered a few ways to do this and I have run into a wall.  The problem
is
> the rectangular nature of TControl.  Let's say I have a layer containing
> lines superimposed over a layer of squares.  If a line object in the top
> layer is clicked someplace in the rectangle of the line component, but
isn't
> actually on the line, a square component visible but below the line's
> rectangle doesn't receive the click.  But what I want to happen is for the
> line component to actually be the line itself not the encompassing
> rectangle.  So a click that misses the actual line would just pass through
> to the next control in the z order.

> To help you understand my exact problem I'll provide a little background.
> I'm working on a program that creates a vector based navigation chart
(like
> a road map) and I want to draw interactive course lines.  The chart would
> contain other interactive objects, such as ship wreck or reef locations.

> One way I've thought to solve this problem is to create a line layer that
> actually handles all click events (loops the line list looking for click
> matches) and then passes the click off to the next layer in the z order.
> Each layer would check for objects that match the click event and until a
> match was found the event could just be passed down the layer stack.  The
> problem with this is that there could potentially be hundreds of
interactive
> objects visible on each layer.

> Any ideas would be greatly appreciated.

> --
> Mark Lauter

> Irreverence is the champion of liberty and its only sure defense.
>   - Mark Twain

Other Threads