The length of a spline. There is a calculation error somewhere.

The length of a spline. There is a calculation error somewhere.

Is.000386.07
Contributor Contributor
6,686 Views
21 Replies
Message 1 of 22

The length of a spline. There is a calculation error somewhere.

Is.000386.07
Contributor
Contributor

I once needed to find out the length of a spline using Maxscript and assign its value to a variable. I'm very bad at Maxscript, so the advice I found was either to use curveLength() or to use the following code:

 

local a = getSegLengths spline01 1
local b = a.count
a[b]

 

As I understand it, both of these options produce the last value in the vector, which is the sum of all elementary lengths. But in the course of my research, I had to manually do a summing loop:

 

for i = 1 to b-1 do
(
   y = a[i]
   sum += y
   format "%\n" a[i]
)
sum

 

As it turned out, the value of sum was not equal to that obtained by the first two methods, but the values were close, the relative error being in the order of 1-2%. Moreover, even outputting the data array with the format "%\n" a[i] string and summing the values in Excel resulted in the same value as in sum.

In this regard, I have a question: either I am not correctly finding the spline length manually, or the first two functions, as well as the value in the tab Utillites - Measure - Lenght are lying.

0 Likes
Accepted solutions (2)
6,687 Views
21 Replies
Replies (21)
Message 2 of 22

spacefrog_
Advisor
Advisor

in your second loop, you gonna miss the last segment

because 3ds max uses 1 based array indices,

your for loop should read "for i = 1 to b do"


Update:
should read more carefully before posting ...
was not aware about the special structure of the returned array

* first 5 elements with fraction length ( sums to 1 )

* then 5 elements with segement length ( sums to total spline length)

* then 1 ( last array element ) with total length of spline

 

so your second loop should read
sum=0
for i = 6 to cnt-1 do
(
    y = a[i]
    sum += y
    format "%\n" a[i]
)
sum


Josef Wienerroither
Software Developer & 3d Artist Hybrid
0 Likes
Message 3 of 22

leeminardi
Mentor
Mentor

I found that the following gave the best agreement with the results of Measure in the Utilities panel.

spl = $line001
a = getSegLengths spl 1
b = a.count
a[b]
format "\n length = % \n" a[b]
lee.minardi
0 Likes
Message 4 of 22

Is.000386.07
Contributor
Contributor
Accepted solution

Yes, I agree, I didn't read the description of the function in the help. It first outputs the relative lengths of all the splines, and only AFTER it starts outputting their absolute lengths. Unfortunately I discovered this after I had posted the post. So in this case the loop has to be done like this:

a = getSegLengths $spline01 1
b = a.count
for i = b/2+1 to b-1 do
(
   y = a[i]
   sum += y
)
sum

0 Likes
Message 5 of 22

denisT.MaxDoctor
Advisor
Advisor

read the MXS help carefully... the getSegLengths method returns an array where the last element is the total length of the spline.

d = getSegLengths $ 1
total_length = d[d.count]

 

0 Likes
Message 6 of 22

Is.000386.07
Contributor
Contributor

Read my first post carefully... in it I described this method of obtaining the length.
At that point in time I needed to do the length calculation manually, as I hadn't figured out how to do it better.
Now the relevance is lost, because it has come to my attention that you can simply use
curveLength() * scale. This operation produces the same result that I needed.

0 Likes
Message 7 of 22

denisT.MaxDoctor
Advisor
Advisor

First, the original question didn't say anything about your spline having a scale. Which is quite atypical when working with splines.
Secondly, as far as I understand, this scale is very large... MAX calculates the length of the spline in floating point numbers, so some error, of course, may occur with large numbers. Same as with very small numbers.

 

on the almost dead CGTalk, I remember there was a discussion on how to calculate the length of the spline more accurately. If you're interested, try to find...

0 Likes
Message 8 of 22

Is.000386.07
Contributor
Contributor
Yes, I didn't specify the scale in order to simplify the question, as it didn't make sense for the issue I was interested in at the time.
If you are really interested in what I needed, then I can say that I tried to reproduce getting the spline length value, similar to the value in the Utilities - Measure - Length tab. But I found that when I changed the scale value, the length obtained by the above commands did not correspond to the value in that tab. So the course of my reasoning may seem strange to you, as I did not know how to solve this problem at that time.
0 Likes
Message 9 of 22

denisT.MaxDoctor
Advisor
Advisor

so what is the length? just tell me the values you get

0 Likes
Message 10 of 22

Is.000386.07
Contributor
Contributor

It would probably be easier to cite a sequence of actions:
For example, create a sphere, then create a spline with the Section function. The scale of this spline will be 1, and its length will be equal to the length of the circle of the section. Change the scale of this spline to 1.1, for example. The length of the circle will increase, but the length value obtained with the above commands will remain the same as at scale 1.

 

Is00038607_0-1683633444999.png

 

0 Likes
Message 11 of 22

denisT.MaxDoctor
Advisor
Advisor

everything is correct here... all spline methods are in the local coordinate system, that is, they do not take node transform into account.

0 Likes
Message 12 of 22

Is.000386.07
Contributor
Contributor

I needed to get the value of the spline length by transforming the nodes. By changing their scale to get a different length value at the output using code.

0 Likes
Message 13 of 22

denisT.MaxDoctor
Advisor
Advisor
Accepted solution

The Measure utility does not give the exact length of the spline. It uses the same #arcsteps. The accuracy depends on the number of arc steps along the Bézier spline, and that is predefined for the utility.

 

In case of nonuniform scaling here is a function to get scaled spline length:

 

 

 

 

 

fn getScaledCurveLength sp shape:1 numArcSteps:100 = 
(
	fn setKnotData sp index pp shape:1 scale:[1,1,1] = 
	(
		setInVec sp shape index 	(pp[1]*scale) 
		setKnotPoint sp shape index (pp[2]*scale)
		setOutVec sp shape index 	(pp[3]*scale)
	)
	with redraw off, undo off
	(
		pp = for k=1 to numknots sp collect
		(
			#(getInVec sp shape k, getKnotPoint sp shape k, getOutVec sp shape k)
		)
		sc = if isvalidnode sp then sp.objecttransform.scale else unsupplied
		
		for k=1 to numknots sp do setKnotData sp k pp[k] shape:shape scale:sc
		updateshape sp

		d = getSegLengths sp shape numArcSteps:numArcSteps -- try to play with numbers 
		len = curvelength sp
		
		for k=1 to numknots sp do setKnotData sp k pp[k] shape:shape
		updateshape sp
	)
	#(d[d.count], len)
)	

getScaledCurveLength $ shape:1 numArcSteps:1000

 

 

 

 

 

 

This is the most accurate calculation and best matches my internal "high precision methods" (c++ SDK).

 

You can play around with the number of #arcsteps used in getSegLengths and see some difference. As I can guess CurveLength also uses some predefined number of steps (most likely 100) which I don't know for sure. 

 

0 Likes
Message 14 of 22

Is.000386.07
Contributor
Contributor
I'm very grateful to you for taking your time to understand my question and write such an enlightening code. I'll look into it and see if it's needed, but frankly speaking the CurveLength() or getSegLengths() * scale construct is quite enough for me. I don't have enough time to develop the script so far. My final task doesn't require extremely high accuracy, for in the real world errors of measurement are much more than one millimetre let alone there may even be centimetres...

What is more difficult to implement in the form of some simplified algorithm is the process of creating the measurement objects themselves, as they are a complex geometric object absolutely not consisting of primitives like a sphere or a cube.
0 Likes
Message 15 of 22

denisT.MaxDoctor
Advisor
Advisor

@Is.000386.07 wrote:
I'm very grateful to you for taking your time to understand my question and write such an enlightening code. I'll look into it and see if it's needed, but frankly speaking the CurveLength() or getSegLengths() * scale construct is quite enough for me. I don't have enough time to develop the script so far. 

the problem is that the scale of the node is the value of Point3, which means that the scale can be nonuniform. Thus, multiplying length (float) and scale (point3) generally does not give the expected result.

 

PS. btw I fixed the bug...

so when you apply some scale, what scale do you mean by that? not only can it be both a node transform, and an object transform, but scale can also differ in time (be animated).

 

0 Likes
Message 16 of 22

StanleyNguyen2023
Participant
Participant
I've attempted to test it further, but it turned out that it doesn't work on objects whose base objects are standard shape primitives like rectangles and circles (even though I tried adding an edit spline modifier and modifying/adding/removing splines in this object). Another issue I've noticed is that when applying deform modifiers to this object, executing the script distorts the size of the spline segment (I suspect this is due to the setKnotData function). Do you have any solutions to address these issues?
0 Likes
Message 17 of 22

denisT.MaxDoctor
Advisor
Advisor

Who claims that my method will work in every possible case? Honestly, I don't see any point in my method posted above, and I would never use it myself.
If there is no solution within the built-in MaxScript methods, I usually write an extension using the SDK and c++.

0 Likes
Message 18 of 22

StanleyNguyen2023
Participant
Participant

I'm aware that further testing might exceed the script's limit as it falls outside of the initial context. But I'm really interested in expanding your code's function to make it work in most cases. Do you have any ideas to upgrade the code with maxscript?
What about using the cloning method to create temporary objects from the current selection, resetting their scaling with Reset Xform, converting it to editable spline to collapse all modifier stacks and avoid standard shape primitives, then measuring and deleting these objects?
My concern with this workaround is the potential slowdown of the system performance if cloning a large selection of shape objects, particularly if the function frequently updates through the callback event

0 Likes
Message 19 of 22

denisT.MaxDoctor
Advisor
Advisor

@StanleyNguyen2023 wrote:

 

What about using the cloning method to create temporary objects from the current selection, resetting their scaling with Reset Xform, converting it to editable spline to collapse all modifier stacks and avoid standard shape primitives, then measuring and deleting these objects?


This is pretty much what I do at the SDK level. But using the SDK, we can do it with node objects instead of the nodes themselves like we do in MXS. When we do all the conversions, collapses, and xform resets for the object, it's much faster because it doesn't cause most of the system and scene notifications that go with node modifications and cause slowdowns.

 

Let me ask you, why do you even need to measure the length of an arbitrary spline in real time?

0 Likes
Message 20 of 22

StanleyNguyen2023
Participant
Participant

During my modeling work, I consistently use the built-in measure tool to monitor my assets' dimensions and lengths. Its ability to refresh information in real-time, without the need to repeatedly run the code or press the button, makes it an excellent tool. If I had to activate the tool each time with a button,it would certainly lose much of its appeal and efficiency
Having a real-time updating feature in a custom shape length tool can certainly enhance efficiency. As for me, I'm currently working on crafting a simple script that includes shape length information and other object parameters to optimize and accelerate my workflow. 

0 Likes