Forum: Poser - OFFICIAL


Subject: Material Room - Draw a line on a Surface

RichardCoffey opened this issue on Oct 10, 2010 · 35 posts


bagginsbill posted Thu, 14 October 2010 at 12:41 PM

I built a matmatic script which contains a function incorporating the math found in the page I linked previously.

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.

Make user-adjustable shader parameter nodes for the input values.

The initial values are arbitrary.

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.

get info for current UV coordinate

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

calculate the distance

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.

For previewing purposes, draw gradients of the distance, 10 per unit.

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)