Husain Basrawala
Husain Basrawala

Reputation: 1751

Identify the position of a point on raphael path

I have created a polygon using raphael path. The vertices of the polygon can be dragged to stretched to change the shape/size of the polygon.

The test is running here

Now what I want to implement is that if I dblclick on the edge, it should create a new vertex. So that it can act as a dragging point.

Can anyone help me out on identifying the position of a point in the path:

var p = r.path("M100,300L100,100L250,300z");

and if a mouse event happens on 200,250, how to identify where in the path array, should the new point command fit?

var p = r.path("M100,300L200,250L100,100L250,300z");

OR

var p = r.path("M100,300L100,100L200,250L250,300z");

Upvotes: 0

Views: 926

Answers (1)

Mathieu Marques
Mathieu Marques

Reputation: 1748

My implementation is slightly different but you could always tweak it if that's too much (I actually insert new points in the polygon upon mouse click wherever it could be on the paper).

Some custom Raphael methods

  • Raphael.el.MakeDraggable: Add handler to dragg any element (namely points or polygons)
  • Raphael.el.InsertPoint: Insert the given point (argument) in the polygon

My points

  • Are made with Paper.circle()
  • Can be dragged with the mouse
var oPaper = Raphael('#paper', '100%', '100%');
var oPoint = oPaper.circle(nX, nY, nRadius);
oPoint.MakeDraggable();

My polygons

  • Are made with Paper.path()
  • Are bound to a set containing their points with oPolygon.data()
  • Can be dragged with the mouse
var oPaper = Raphael('#paper', '100%', '100%');
var oPolygon = oPaper.path(sPath);
oPolygon.InsertPoint(oPoint);

The maths

These are the 2 steps I followed in order to insert the newly created point in the path of the polygon:

  1. Loop over each side of the polygon and get the distance from that side and the point
  2. From the lower distance, assume the 2 points between which the newly created point should be inserted

Getting the distances of the new point

Those 2 steps are easy to understand, but hard to implement (especially the first one). Here is the first step detailed. Say you are looping over each side of the polygon (a side equals 2 points), we need to feed an array with all the distances so we can get the lowest.

C +-------+ A                                           + M
   \      |             The shortest                   /     The shortest
    \     |             distance is [MG]              /      distance is [MA]
     \  G +--------+ M                        C +----+ A
      \   |                                      \   |
       \  |                                       \  |
        \ |                                        \ |
         \|                                         \|
          + B                                        + B
  1. Your function which iterate over the sides fetches the points two by two, plus the newly create points thats 3 points, but let's write it for the first iteration (as an example)
  2. So oA and oB are the 2 points of the polygon's side, and oM is the newly created point
  3. Digging up your maths, you should be able to get the coordinates of oG, the translated point of oM upon the line made by oA and oB
  4. Once you have oG's coordinates, there are 2 cases:
    • oG is between oA and oB
      1. oM is in front of the side made by oA and oB
      2. So the distance to return is the distance between oM and oG
    • oG is outside the segment made by oA and oB
      1. oM is not in front of the side made by oA and oB
      2. So the distance to return is the distance between oM and either oA or oB, simply return the lowest of them 2
  5. Now you have 1 distance, repeat over each side of the polygon to get the others

Your array containing the distances should now contain all the distances between oM and the sides of the polygon. We need to find the lower ones (there can be multiple with the same value). So loop over it and build another array which will contain the indexes of the lowest distances.

Deciding which side is the right

Once you have this new array, check its length:

  • Length of 1: Means that your oM point was in front of the side. You have the index of the side, go ahead and insert your point in the polygon's datas
  • Length of 2: Means that your oM point was not in front of the side. You have 2 indexes, treat them as point indexes, 2 points that make a side, same as above you can now insert your point in the polygon's datas
  • Length of 3+ (not needed for you I believe): Special cases such as circles (with a lot of points) and square-like polygons where you could have inserted your point on the very center

Some more thingies

// Don't forget to bind points to their polygon
oPolygon.data('points', oPoints);  // oPoints is a Raphael set containing the points

// There are different approaches, mine was to bind the other way as well
oPoint.data('polygon', oPolygon);

Upvotes: 2

Related Questions