Notifications
Clear all

[Closed] Nodes facing each other

I want to calculate if two nodes are facing each other within a given angle threshold.
I’ve gotten a start on it but I’m not entirely sure if this method is an ideal solution. I was hoping to get some feedback from the community and see their thoughts.


fn GetPointVector ptA ptB =
(
	return (Dot ptA ptB)
)

fn IsNodeFacing nodeA:undefined nodeB:undefined axis:#X angleThreshold:0 =
(
	-- if dot == 1 then directly facing each other
	val = case axis of
	(
		#X: (GetPointVector nodeA.transform.row1 nodeB.transform.row1)
		#Y: (GetPointVector nodeA.transform.row2 nodeB.transform.row2)
		#Z: (GetPointVector nodeA.transform.row3 nodeB.transform.row3)
	)
	-- if val == 0 then the node is perpendicular
	-- if val is negative then they are facing each other
	-- if val is positive then they are facing away from each other
	--(Dot(nodeA.transform.X, nodeB.transform.X) < -0.8) 
)

IsNodeFacing nodeA:selection[1] nodeB:selection[2]

7 Replies

What do you mean by ‘two nodes facing each other’? Their local axis pointing each other?

correct. on the X y or z axis

Perhaps you can use the ‘intersectRay’ function, creating the ray following the object desired axis.
With the distance between the two nodes and the angle threshold, you can easily create a temporary plane and check the intersection in both directions.

Check this simple example out. Perhaps you can get something useful out of it.

(
	local viewAngle = 45
	local viewDistance = 150

	fn BuildScene =
	(
		delete objects
		
		center = dummy()
		
		obj1 = converttomesh (ngon radius:15 cornerRadius:0 nsides:3 pos:[0,0,0] wirecolor:red name:"OBJ1")
		meshop.chamferVerts obj1 #{2,3} 10

		obj2 = copy obj1 pos:[100,0,0] wirecolor:green name:"OBJ2" parent:center
		
		view1 = arc radius:viewDistance from:(360-(viewAngle/2.0)) to:(viewAngle/2.0) pie:on pos:[0,0,0] wirecolor:black name:"VIEW1" parent:obj1
		view2 = copy view1 pos:[100,0,0] name:"VIEW2" parent:obj2

		with animate on
		(
			for j = 1 to 2 do at time (j*50)
			(
				rotate obj1 (angleaxis 180 [0,0,1])
				rotate obj2 (angleaxis -180 [0,0,1])
				rotate center (angleaxis -180 [0,0,1])
			)
		)
		
		for j in #(obj1,obj2,center) do
		(
			for i = 1 to 3 do
			(
				j.rotation.controller.Z_Rotation.controller.keys[i].inTangentType = #slow
				j.rotation.controller.Z_Rotation.controller.keys[i].outTangentType = #slow
			)
		)
	)
	
	fn IsNodeAFacingNodeB nodeA nodeB angleThreshold:0 =
	(
		ang = dot nodeA.transform.row1 (normalize (nodeA.pos-nodeB.pos))
		return (acos (ang * -1) <= (angleThreshold/2.0))
	)

	fn RunTest =
	(
		isFacingA = IsNodeAFacingNodeB $OBJ1 $OBJ2 angleThreshold:viewAngle
		isFacingB = IsNodeAFacingNodeB $OBJ2 $OBJ1 angleThreshold:viewAngle

		if isFacingA and isFacingB then
		(
			$VIEW1.wirecolor = $VIEW2.wirecolor = yellow
		)else(
			if isFacingA then $VIEW1.wirecolor = red else $VIEW1.wirecolor = black
			if isFacingB then $VIEW2.wirecolor = green else $VIEW2.wirecolor = black
		)
	)

	BuildScene()
	
	unRegisterTimeCallback RunTest
	registerTimeCallback RunTest
	
	playAnimation()
)

Great example, PolyTools3D!

Yes, fantastic example.

It works fine in 3D too. I mean, it checks the viewing of the other object in the cone generated from its position, following its X local axis in this case (row1) and with the threshold angle.
Here’s an image of what I mean.

Really very nice.

Here is a minor update, a little better as it does not use the Time Callback and you can change the View Angle in real time.

Aditionally the IsNodeAFacingNodeB() function takes any vector as direction, not just X, Y or Z.

It would be better to use Controllers, but the code wouldn’t be as easy to read.

(
	
	local viewAngle = 45
	local viewDistance = 150

	local obj1, obj2, view1, view2, cone1, cone2
	
	fn BuildScene =
	(
		delete objects
		
		center = dummy()
		
		obj1 = converttomesh (ngon radius:15 cornerRadius:0 nsides:3 pos:[0,0,0] wirecolor:red name:"OBJ1")
		meshop.chamferVerts obj1 #{2,3} 10

		obj2 = copy obj1 pos:[100,0,0] wirecolor:green name:"OBJ2" parent:center
		
		view1 = arc radius:viewDistance from:(360-(viewAngle/2.0)) to:(viewAngle/2.0) pie:on pos:[0,0,-1] wirecolor:black name:"VIEW1" parent:obj1
		view2 = copy view1 pos:[100,0,-1] wirecolor:black name:"VIEW2" parent:obj2
			
		cone1 = cone heightsegs:1 capsegs:1 sides:20 height:viewDistance radius1:0 radius2:((tan (viewAngle/2.0))*viewDistance)wirecolor:black name:"CONE1" parent:obj1 isHidden:true
		cone2 = copy cone1 pos:[100,0,0] wirecolor:black name:"CONE2" parent:obj2 isHidden:true

		rotate cone1 (angleaxis 90 [0,1,0])
		rotate cone2 (angleaxis 90 [0,1,0])
			
		with animate on
		(
			for j = 1 to 2 do at time (j*50)
			(
				rotate obj1 (angleaxis 180 [0,0,1])
				rotate obj2 (angleaxis -180 [0,0,1])
				rotate center (angleaxis -180 [0,0,1])
			)
		)
		
		for j in #(obj1,obj2,center) do
		(
			for i = 1 to 3 do
			(
				j.rotation.controller.Z_Rotation.controller.keys[i].inTangentType = #slow
				j.rotation.controller.Z_Rotation.controller.keys[i].outTangentType = #slow
			)
		)
		slidertime = 0
	)
	
	fn IsNodeAFacingNodeB nodeA nodeB vector:[1,0,0] angleThreshold:0 useNodeCenter:false =
	(
		if useNodeCenter then
		(
			posA = nodeA.center
			posB = nodeB.center
		)else(
			posA = nodeA.pos
			posB = nodeB.pos			
		)
		
		tm = nodeA.transform
		tm.row4 = [0,0,0]
		
		ang = dot (vector * tm) (normalize (posA-posB))
		return (acos (ang * -1) <= (angleThreshold/2.0))
	)

	fn RunTest =
	(
		isFacingA = IsNodeAFacingNodeB obj1 obj2 angleThreshold:viewAngle
		isFacingB = IsNodeAFacingNodeB obj2 obj1 angleThreshold:viewAngle

		if isFacingA and isFacingB then
		(
			#(view1, view2, cone1, cone2).wirecolor = yellow
		)else(
			if isFacingA then #(view1,cone1).wirecolor = red else #(view1,cone1).wirecolor = black
			if isFacingB then #(view2,cone2).wirecolor = green else #(view2,cone2).wirecolor = black
		)
	)

	BuildScene()

	try destroydialog ::RO_FACING_NODES catch()

	rollout RO_FACING_NODES "Facing Nodes" width:146 height:100
	(
		spinner spn_fps "FPS:" pos:[46,8] fieldwidth:56 range:[1,30,15] scale:1
		spinner spn_angle "View Angle:" pos:[12,36] fieldwidth:56 range:[0,180,45] scale:1
		checkbutton bt_play "Play" pos:[8,60] width:128 height:32
		timer tm_player "" interval:66 active:false
		
		local end = animationrange.end
		
		on tm_player tick do
		(
			if currenttime == end do slidertime = 0
			slidertime += 1
			RunTest()
		)
		
		on bt_play changed arg do
		(
			tm_player.active = not tm_player.active
			if arg then bt_play.text = "Stop" else bt_play.text = "Play"
		)
		
		on spn_fps changed arg do tm_player.interval = 1000.0 / arg
		
		on spn_angle changed arg do
		(
			viewAngle = arg
			view1.from = 360-(viewAngle/2.0)
			view1.to = viewAngle/2.0
			
			view2.from = 360-(viewAngle/2.0)
			view2.to = viewAngle/2.0
		)
	)
	
	createdialog RO_FACING_NODES
	
)