RichardCoffey opened this issue on Oct 10, 2010 · 35 posts
bagginsbill posted Thu, 14 October 2010 at 12:41 PM
This is pretty straightforward. I changed the names of the variables, though, to match the way my brain works.
I define the line segment as going from point a to point b. I need 2D vector components, so point a will be held in ax, ay. Point b will be held in bx, by. The point being shaded will be point c, held in cx, cy.
I have attached the matmatic script, and it is commented, but I'll explain a little more here.
It begins with defining the function:
def LineSegmentClosestPoint(ax, ay, bx, by, cx, cy):
We'll be calling this with desired values for a, b, and c.
The first step is to calculate the x/y deltas from a to b.
# Calculate a-to-b vector components
abx = bx - ax
aby = by - ay
The next step is to calculate the x/y deltas from a to c.
# Calculate a-to-c vector components
acx = cx - ax
acy = cy - ay
In matmatic, these subtractions result in Math:Sub nodes, connected (or set) to whatever was passed in for ax, ay, bx, by, cx, and cy. So abx is the x distance from a to b. It might be a node or it might just be a constant. We don't care. Matmatic will take care of that for us. If, for example, bx = .5 and ax = .3, then abx will be .5 - .3 = .2. However, if either ax or bx is a node, then abx will be a Math:Sub node that calculates bx - ax. Similar logic is applied to the other three.
Now to solve the parametric equation of the line segment, we'll need the square of the distance from a to b.
# Square of the distance a-to-b
ab_distSquared = abx * abx + aby * aby
Then, using exactly the formula given in the linked page, the solution of the parametric value, t, for the closest point on the line (not the segment yet, but the whole line) is:
# Solve for parametric value t which defines
# the position of the closest point on the line
t = (acx * abx + acy * aby) / ab_distSquared
So t is a node that holds the parameter value. If the closest point is actually in the line segment, t will be between 0 and 1 (inclusive). If the closest point is before the beginning of the line segment, t is less than 0. If the closest point is after the end of the line segment, t is greater than 1.
We don't want to deal with the infinite line. We want to only consider points within the segment. If t is less than 0, we want the line segment start point which is at t=0. If t is greater than 1, we want the line segment end point which is at t=1. In other words, we want to "Clamp" t. Super - there's a node for that.
# Clamp it (limit it from 0 to 1) to to keep in the bounds of the segment
ct = Clamp(t)
So now ct holds the parametric value that is closest to c, but still within the line segment.
To calculate the position of that point, we do:
# Calculate point, p, that is closest to c, using the parametric equation
# of the line segment.
px = ax + ct * abx
py = ay + ct * aby
We're going to be needing the x/y deltas between this point, p, and the point we're rendering.
# Calculate p-to-c vector components
pcx = cx - px
pcy = cy - py
Finally, I need to return something. Being a generalist, I'm not yet sure how I'll be using all these values, so I'm going to return a bunch of them in a matmatic Parameters object.
# return all the important info produced
return Parameters(px=px, py=py, pcx=pcx, pcy=pcy, ct=ct)
That's the whole function. Pretty simple actually.
Turning that into a drop of water that left a twisty trail is going to be tricky, but this is a good start.
To test the math, I will make four parameter nodes that a user can manipulate experimentally in the material room.
ax = PM(.35, "AX")
ay = PM(.8, "AY")
bx = PM(.4, "BX")
by = PM(.15, "BY")
Now I must call the function to extract the information I want. I will use the U and V values as my point, c.
seg = LineSegmentClosestPoint(ax, ay, bx, by, U, V)
The return value, seg, holds all the info produced by the function.
For this first test, I'm just going to calculate the distance to the segment
d = Hypot(seg.pcx, seg.pcy)
And then as an easy visualization, I will draw this as a gradient, using a simple modulus arithmetic trick.
s = View(10 * d % 1)
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)