Notifications
Clear all

[Closed] Position within range

I want to be able to find out where a position is relative to 2 other points. In the 2 diagrams: On the left because point A and B are right above each other, I can just use the Z position to figure that point C is about 71.4% of the way to b on the z plane. But what if the position of point A and B are offset. I can’t figure how to calculate the same value when the measuring angle is not aligned to the world. I have a rough idea that I’ll have to build a transform matrix out of the vector from A to B and somehow figure another vector that helps me get a right angle from the green axis (yellow line) that’s also inline with point C.

I’m stuck at the start trying to build a useful matrix from one axis.

Cheers,
Cg.

9 Replies

You don’t need a matrix at all, for projection there’s dot product: normalize the vector you are projecting to, get the dotproduct which will give you the projected distance, then multiply it by the normalized vector and add to the vector origin:

pA + (vA * (dot (pC - pA) vA))

where pA is the position of A, vA is the NORMALIZED vector (B – A) and pC is the position of C.

Hey Swordslayer thanks for your reply. I’m not sure whether I don’t understand your answer properly, or I didn’t explain myself properly. But I’ve tried this and not getting the result I was looking for. In the code below, I make the 3 points (A,B and C) and then make a root point to link them all to. I do the calculation as you have supplied, then I rotate the root point 45 degrees on y then do the calculation again.

With this setup, I was looking for was a value of 50 units if it’s measuring the distance from A or 150 if it’s returning distance from B. Or if it’s returning percentage it’d be 25% from A or 75% from B.

But as well as not returning a value that I was expecting, when I rotate the whole thing 45 degrees on the Y axis, I was expecting the same result because the relationships between points A B and C haven’t changed. But as you can see if you run the code, I first get a value of:

[0,0,50]

And then after the rotation, it returns a value of:

[35.3554,0,35.3554]


(
	clearlistener()
	delete Objects
	
	fn calcPos PointA PointB PointC =
	(
		pA = PointA.transform.row4
		pB = PointB.transform.row4
		pC = PointC.transform.row4
		vA = normalize(pB - pA)
		pA + (vA * (dot (pC - pA) vA))
	)
	
	PointA = point name:"PointA" size:20 cross:on box:off
	
	PointB = instance PointA
	PointB.name = "PointB"
	PointB.pos = [0,0,200]
	
	PointC = instance PointA
	PointC.name = "PointC"
	PointC.pos = [100,0,50]

	PointRoot = point name:"PointRoot" size:50 cross:off box:on
	PointRoot.wirecolor = blue
	
	PointA.wirecolor = PointB.wirecolor = PointC.wirecolor = red
	PointA.parent = PointB.parent = PointC.parent = PointRoot
	
	format "%
"(calcPos PointA PointB PointC)
	
	PointRoot.transform = (matrix3 [0.707107,0,-0.707107] [0,1,0] [0.707107,0,0.707107] [0,0,0])
	
	format "%
"(calcPos PointA PointB PointC)
)

Well, since all your source positions are in world space, it will be a point in space in world coordinates as well. It would be the same value only if you rotated about it or if you supplied the positions in parent space. If you want it in a different coordsys, transform it accordingly, for example for the PointRoot:

(calcPos PointA PointB PointC) * inverse PointRoot.transform

Btw. instead of guessing if the values are what you expected, why not create a point there?

Point pos:(calcPos PointA PointB PointC)

I just looked up vector projection and found this link at khan academy.

https://www.khanacademy.org/math/linear-algebra/matrix-transformations/lin-trans-examples/v/introduction-to-projections

I didn’t even get half way through the vid before it all clicked into place and I realised that all I need to do was measure the distance from pointA to the returned value to get what I need.

Thanks again.

Cg.

Just to make sure you don’t make it unnecessarily complicated, by ‘measure the distance to the returned point’, you don’t mean really measure the distance, right? The distance is the dot product part:

dot (pC – pA) vA

Yup, I get it now. No distance needed. Just length of returned vector.

Thanks for your persistence.

Cg.

Here’s a version of the code that returns both ways. vector length and distance tested.

Not sure if one is better than the other.

Your help is much appreciated.


(
	clearlistener()
	delete Objects
	
	fn calcPos PointA PointB PointC =
	(
		pA = PointA.transform.row4
		pB = PointB.transform.row4
		pC = PointC.transform.row4
		vA = normalize(pB - pA)
		projVec = (pA + (vA * (dot (pC - pA) vA)))
		#((length (projVec - pA)),(distance projVec pA))
	)
	
	PointA = point name:"PointA" size:20 cross:on box:off
	
	PointB = instance PointA
	PointB.name = "PointB"
	PointB.pos = [0,0,200]
	
	PointC = instance PointA
	PointC.name = "PointC"
	PointC.pos = [100,0,50]

	PointRoot = point name:"PointRoot" size:50 cross:off box:on
	PointRoot.wirecolor = blue
	
	PointA.wirecolor = PointB.wirecolor = PointC.wirecolor = red
	PointA.parent = PointB.parent = PointC.parent = PointRoot
	
	PointRoot.pos = [20,40,60]
	
	format "%
"(calcPos PointA PointB PointC)
	
	PointRoot.transform = (matrix3 [0.707107,0,-0.707107] [0,1,0] [0.707107,0,0.707107] [20,40,60])
	
	format "%
"(calcPos PointA PointB PointC)
)

try this little toy:

try (destroydialog RelativePoint) catch()
rollout RelativePoint "Relative Point" width:200
(
	--checkbox use_scale_ch "Use Scale"
	
	button create_bt "Create" width:180 align:#center
	button reset_bt "Reset" width:180 align:#center
	
	
	local targets = #()
	local rpoint = undefined
	local ini_tm = matrix3 1
	local rel_tm = matrix3 1
	local scl_unit = 1
	
	fn makeMatrix a b = 
	(
		front = normalize (b.pos - a.pos)
		up = z_axis	
		side = cross front up
		matrix3 front side up a.pos
	)
	fn updatePoint use_scale:on = try
	(
		scl = distance targets[1] targets[2]
		tm = makeMatrix targets[1] targets[2]
		if use_scale do prescale tm ([1,1,1] * scl / scl_unit)  
		rpoint.pos = (rel_tm * tm).pos
	)
	catch()
		
	fn setupUpdate = 
	(
		deleteAllChangeHandlers id:#RelativePoint 
		when transform targets change id:#RelativePoint do updatePoint() 
	)
	
	on reset_bt pressed do --undo "Reset" on
	(
		a = targets[1]
		b = targets[2]
		c = rpoint
		
		scl_unit = distance a b
		ini_tm = makeMatrix a b
		rel_tm = c.transform * inverse ini_tm

		setupUpdate()
	)

	on create_bt pressed do undo "Create" on
	(
		delete objects
		targets[1] = point name:#A pos:(random [100,100,0] -[100,100,0]) wirecolor:green size:10
		targets[2] = point name:#B pos:(random [100,100,0] -[100,100,0]) wirecolor:green size:10
			
		rpoint = point name:#C pos:(random [100,100,0] -[100,100,0]) wirecolor:yellow size:15	
			
		reset_bt.pressed()
	)
	
	on RelativePoint open do
	(
		setupUpdate()
	)
)
createdialog RelativePoint

ALL POINTS LIVE IN XY WORLD

OK, I just re-read and understand that the returned point was made by getting the distance and figuring out the ratio of the original vector and then offsetting it by point A. So yes I was still over complicating it.

This is working perfectly


(
	clearlistener()
	delete Objects
	
	fn calcPos PointA PointB PointC =
	(
		pA = PointA.transform.row4
		pB = PointB.transform.row4
		pC = PointC.transform.row4
		vA = normalize(pB - pA)
		dot (pC - pA) vA
	)
	
	PointA = point name:"PointA" size:20 cross:on box:off
	
	PointB = instance PointA
	PointB.name = "PointB"
	PointB.pos = [0,0,200]
	
	PointC = instance PointA
	PointC.name = "PointC"
	PointC.pos = [100,0,50]

	PointRoot = point name:"PointRoot" size:50 cross:off box:on
	PointRoot.wirecolor = blue
	
	PointA.wirecolor = PointB.wirecolor = PointC.wirecolor = red
	PointA.parent = PointB.parent = PointC.parent = PointRoot
	
	PointRoot.pos = [20,40,60]
	
	format "%
"(calcPos PointA PointB PointC)
	
	PointRoot.transform = (matrix3 [0.707107,0,-0.707107] [0,1,0] [0.707107,0,0.707107] [20,40,60])
	
	format "%
"(calcPos PointA PointB PointC)
)