Mini-Challenge #4


#1

well… the Party Of Math Lovers won. :slight_smile:

  So if you like to practice in math try to accomplish this task:

     setCommandPanelTaskMode mode:#create
     
     start = dummy name:"start" pos:[0,0,0] boxsize:[10,10,10]
     out_start = in start dummy name:"out_start" pos:[33.33,0,0] boxsize:[2.5,2.5,2.5]
     setTransformLockFlags out_start #{2..9} --#all
     end = dummy name:"end" pos:[100,0,0] boxsize:[10,10,10]
     in_end = in end dummy name:"in_end" pos:[66.66,0,0] boxsize:[2.5,2.5,2.5]
     setTransformLockFlags in_end #{2..9} --#all
     
     start.rotation.controller = Euler_XYZ()
     end.rotation.controller = Euler_XYZ()
     
     wire = splineShape name:"wire" adaptive:on wirecolor:yellow
     addnewSpline wire
     addKnot wire 1 #corner #curve start.pos
     addKnot wire 1 #corner #curve end.pos
     setKnotType wire 1 1 #bezierCorner
     in coordsys world
     (	
     	setOutVec wire 1 1 out_start.pos
     	setInVec wire 1 2 in_end.pos
     	setKnotType wire 1 2 #bezierCorner
     )
     updateShape wire
     
     animateVertex wire #all
     wire.controller = Transform_Script script:"matrix3 1"
     setTransformLockFlags wire #all
     
     cc = wire[#Object__Editable_Spline][#Master].controller
     
     ps1 = point3_script ()
     ps1.addNode "start" start
     ps1.setExpression "start.pos"
     						  
     ps2 = point3_script ()
     ps2.addNode "end" end
     ps2.setExpression "end.pos"
     
     cc[#Spline_1___Vertex_1].controller = ps1
     cc[#Spline_1___Vertex_2].controller = ps2
     
     ps3 = point3_script ()
     ps3.addNode "start" out_start
     ps3.setExpression "start.pos"
     						  
     ps4 = point3_script ()
     ps4.addNode "end" in_end
     ps4.setExpression "end.pos"
     
     cc[#Spline_1___OutVec_1].controller = ps3
     cc[#Spline_1___InVec_2].controller = ps4
     
     
     for k=1 to 9 do
     (
     	factor = 10*k
     	ghost = point name:("ghost" + k as string) axistripod:on box:on cross:off size:10 wirecolor:red
     	setTransformLockFlags ghost #all
     
     	ghost.rotation.controller = rc = Euler_XYZ()
     	
     	rc.x_rotation.controller = Float_List()
     	rc.x_rotation.controller.Bezier_Float.controller = start.rotation.controller.x_rotation.controller
     	rc.x_rotation.controller.Available.controller = end.rotation.controller.x_rotation.controller
     	
     	rc.x_rotation.controller.weight[1] = 100 - factor
     	rc.x_rotation.controller.weight[2] = factor
     
     	ghost.position.controller = pc = path_constraint path:wire percent:factor loop:off constantVel:on follow:on bank:off
     	deletekeys pc.percent.controller #allkeys
     )
     
 I made this rig using MAX features (spline shape, controllers, constraints, etc.) to   hide as well as possible any math behind. The goal is to do the same  but  using [b]MATH [/b]formulas only and [b]WHEN [/b]constructs. 
 Play with BIG boxes position and rotation, and SMALL boxes local X position. Get the logic and ... run the Math Adventure. 
 
 PS. I hope the rig that I made for this challenge will be interesting   itself. It's some sort of a combined answer on questions that I saw on   this forum.

[edit: bug in the rig fixed]


#2

As you see in some situations the point objects flip on their spin (X-axis Rotation). It’s a result of using Euler XYZ controller. I couldn’t find any universal way how to fix this problem and stay with controllers only. The pure math (quaternions, matrix3) really solves the problem.

After getting the math implementation of this task you will see how easy to make custom scripted Loft Object for example. It’s actually the reason to use Math in a situation where MAX seemingly works well.


#3

Tried running your code buy got this error…

K
$Dummy:start @ [0.000000,0.000000,0.000000]
$Dummy:out_start @ [33.330002,0.000000,0.000000]
OK
$Dummy:end @ [100.000000,0.000000,0.000000]
$Dummy:in_end @ [66.660004,0.000000,0.000000]
OK
Controller:Euler_XYZ
-- Unknown property: "rotation" in undefined
$Editable_Spline:wire @ [0.000000,0.000000,0.000000]
1
1
-- Unknown property: "pos" in 62940421
OK
-- Error occurred in anonymous codeblock
--  Frame:
-- Runtime error: spline knot index out of range: 2
-- Runtime error: updateShape: curve with insufficient knots, knots added: Editable Spline
OK
Controller:Transform_Script
OK
Controller:ReferenceTarget
Controller:Point3_Script
true
true
Controller:Point3_Script
-- Unable to convert: 62940421 to type: <node>
-- Error occurred in anonymous codeblock
--  Frame:
--   f: 0.0
--   this: Controller:Point3_Script
--   NT: 0.0
--   s: 0.0
--   t: 0.0
>> MAXScript Script Controller Exception: -- Unknown property: "pos" in 62940421 <<
-- Runtime error: IScriptCtrl::SetExpression - Expression evaluation error: 
Controller:Point3_Script
Controller:Point3_Script
Controller:Point3_Script
true
true
Controller:Point3_Script
true
true
Controller:Point3_Script
Controller:Point3_Script
-- Error occurred in k loop
--  Frame:
--   ghost: $ghost1
--   factor: 10
--   rc: Controller:Euler_XYZ
--   k: 1
--   PC: undefined
-- Unknown property: "rotation" in 62940421
OK

I’m on max 9


#4

Scrub that, needed to create an object first :wink:


#5

i fixed the bug. thanx!


#6

Is path constraint allowed or is that to be done with math too?


#7

MATH and PRS contollers only :slight_smile:


#8

INTERESTING! :slight_smile:

Another question (perhaps dumb, sorry, Mom always said I’m SPECIAL :wink: )
PRS controllers as in no Scripted Controllers?


#9

Do you really need scripted controllers? I don’t think so…


#10

:blush:
OK then, seems like I’m not nearly ready to take this on but will definitely follow the topic.
Thanks for doing this and sorry for cluttering up the thread a bit.


#11

Hey! I don’t want to lose players!
Use whatever you want: scripts controllers, ExposeTm nodes, etc. I just want to say that is not necessary.


#12

OK. I’ll do my best then. :slight_smile:


#13

I knew about the when constructor but never had any need to use it.
Can anyone hint me on how to make it work without the handleAt attribute?
Personally I would use an expression controller to do these things purely in math because that way the rig will be max script free which gives much faster results and it will still be pure math as well.
Here goes:

(
	max create mode
	
	local numSegs = 10
	local start = point name:"start" pos:[0,0,0] size:10 box:true cross:false wirecolor:green
	local end = point name:"end" pos:[100,0,0] size:10 box:true cross:false wirecolor:green
	local outStart = point name:"out-start" pos:[100.0 / 3,0,0] size:2.5 box:true cross:false wirecolor:green
	local inEnd = point name:"in-end" pos:[200.0 / 3,0,0] size:2.5 box:true cross:false wirecolor:green
	local ctrls = #(start, outStart, inEnd, end)
	
	setTransformLockFlags #(outStart, inEnd) #{2..9}
	outStart.parent = start
	inEnd.parent = end
	
	local ghosts = for i = 1 to numSegs collect
		point name:("ghost" + i as string) axistripod:on box:on cross:off size:10 wirecolor:red
	setTransformLockFlags ghosts #all
	
	fn getBezierInterpolation p t =
	(
		(1. - t) ^ 3 * p[1] + 3 * (1. - t) ^ 2 * t * p[2] + 3 * (1. - t) * t ^ 2 * p[3] + t ^ 3 * p[4]
	)
	
	when transform ctrls changes handleAt:#redrawViews do (
	with redraw off (
	with animate off (
		local pts = for o in ctrls collect o.pos
		for i = 1 to numSegs do (
			local t = (1. * i / (numSegs + 1))
			local p = getBezierInterpolation pts t
			
			ghosts[i].pos = p
			
			local x, y, z
			if i == 1 then
				x = normalize (ghosts[i].pos - start.pos)
			else
				x = normalize (ghosts[i].pos - ghosts[i - 1].pos)
			z = normalize [0,0,1]
			y = normalize (cross z x)
			z = normalize (cross x y)
			
			local m = matrix3 x y z p
			local a = start.rotation.controller.x_rotation * (1. - t) + end.rotation.controller.x_rotation * t
			preRotateX m a
			
			ghosts[i].transform = m
		)
	)))
	
	move start [0,0,0]
	forceCompleteRedraw()
)

#14

Great! One thing is missed. In my rig the ghost objects distributed by length (not by path). See the path constraint’s constantVel param…

PS. there is a trick:


-- instead of 
for k=1 to 10 do d_float = 1.*k/10
-- do  
for k=1. to 10 do d_float = k/10


#15

just remove it… where is a problem?


#16

bezier spline tangent might be calculated more accurate.


#17

another thing is to try not use the start’s and end’s rotation controllers. try to use their transforms.
in this case we will not be limited by type of assigned controllers. Our control nodes (start, end, out_start, and in_end) might be driven by script controller, or orientation_constraint for example…


#18

It might be my home computer, it’s 6 years old now :shrug:


#19

might be… in this case x-axis transform has to be odd. y- and z- have to work right.


#20

here is an improved version, still haven’t managed to figure out how to do the constant velocity yet. I tried Catmull-Rom and Hermite spline interpolations but it doesn’t help my situation…

(
	max create mode
	
	local numSegs = 10
	local start = point name:"start" pos:[0,0,0] size:10 box:true cross:false wirecolor:green
	local end = point name:"end" pos:[100,0,0] size:10 box:true cross:false wirecolor:green
	local outStart = point name:"out-start" pos:[100.0 / 3,0,0] size:2.5 box:true cross:false wirecolor:green
	local inEnd = point name:"in-end" pos:[200.0 / 3,0,0] size:2.5 box:true cross:false wirecolor:green
	local ctrls = #(start, outStart, inEnd, end)
	
	setTransformLockFlags #(outStart, inEnd) #{2..9}
	outStart.parent = start
	inEnd.parent = end
	
	local ghosts = for i = 1 to numSegs collect
		point name:("ghost" + i as string) axistripod:on box:on cross:off size:10 wirecolor:red
	setTransformLockFlags ghosts #all
	
	fn getBezierInterpolation p t =
	(
		(1. - t) ^ 3 * p[1] + 3 * (1. - t) ^ 2 * t * p[2] + 3 * (1. - t) * t ^ 2 * p[3] + t ^ 3 * p[4]
	)
	
	when transform ctrls changes handleAt:#redrawViews do (
	with redraw off (
	with animate off (
		local pts = for o in ctrls collect o.pos
		
		for i = 1. to numSegs do (
			local t = i / (numSegs + 1)
			local p1 = getBezierInterpolation pts t
			local p2 = getBezierInterpolation pts (t + 0.0001)
			
			
			local x, y, z
			x = normalize (p2 - p1)
			z = normalize [0,0,1]
			y = normalize (cross z x)
			z = normalize (cross x y)
			
			local m = matrix3 x y z p1
			local startXRot = (quatToEuler start.transform.rotationpart).x
			local endXRot = (quatToEuler end.transform.rotationpart).x
			local a = startXRot * (1. - t) + endXRot * t
			preRotateX m a
			
			ghosts[i].transform = m
		)
	)))
	
	move start [0,0,0]
	forceCompleteRedraw()
)