I’m guessing i need arc length here possibly. I’ve created a sample piece of code to demonstrate the issue here.
In short I’m trying to use the ‘refine’ method to insert a point along a path at a given distance. However it doesn’t appear the Spline plugin api has the same methods available to it that maxscript has. Let me simply explain.
I have spline A in my scene with 4 knots and 3 segments.
My spline plugin takes this spline and tries to insert a point along that path at a distance of 100 units. When I use vanilla maxscript this is the result which is circled in Green.
This is done by using just a few lines of maxscript code
dist = 100
len = curveLength sp 1
frac = dist / len as float
pathParam = amin (lengthToPathParam sp 1 frac steps:50) 1.0
segParam = findPathSegAndParam sp 1 pathParam
refineSegment sp 1 segParam.x segParam.y
Now the method lengthToPathParam and findPathSegAndParam are not available in the simpleSpline plugin API. So I tried to roll my own solution. I was wondering if you had any suggestions? By rolling my own I get this… which is close but not correct. I’m guessing the accuracy is off because there is no steps param like the native maxscript methods have.
The main differences are the values returned from findPathSegAndParam which doesn’t exist and I need that information. My custom methods values are different as show compared to the maxscript solution. You can ignore the first number of the param, that is the segment index, which maxscript is 1-index based and the spline plugin is 0-index based.
MXS:
Dist: 100
Curve Length: 178.56
FRAC: 0.560035
PARAM: 2.0 0.589999
Custom (Missing in SDK):
Dist: 100
Curve Length: 178.56
FRAC: 0.560035
PARAM: 1.0 0.464839
I’ve attached my code file which sets up the test scene and prints the values for you. Fairly simple script really. The code sample gives you a spinner to adjust the position of the knot along the path.
CODE SAMPLE
plugin simpleSpline SplineParam
name: "Param"
classID: #(0x43d4063a, 0x3c452295)
(
local vector3 = dotNetClass "Autodesk.Max.MaxPlus.Point3"
local splineKnot = dotNetClass "Autodesk.Max.MaxPlus.SplineKnot"
local cornerKnot = (dotNetClass "Autodesk.Max.MaxPlus.SplineKnot+KnotType").CornerKnot
local bezierKnot = (dotNetClass "Autodesk.Max.MaxPlus.SplineKnot+KnotType").BezierKnot
local autoKnot = (dotNetClass "Autodesk.Max.MaxPlus.SplineKnot+KnotType").AutoKnot
local straightLine = (dotNetClass "Autodesk.Max.MaxPlus.SplineKnot+LineType").LineLineType
local curveLine = (dotNetClass "Autodesk.Max.MaxPlus.SplineKnot+LineType").CurveLineType
local bezierShapeObj = dotNetClass "Autodesk.Max.MaxPlus.BezierShape"
parameters main rollout:ParamsRollout
(
dist type:#float default:100 animatable:on ui:uiDist
targetShape type:#node ui:uiTargetShape
)
rollout ParamsRollout "Parameters"
(
pickbutton uiTargetShape "Pick Shape" autodisplay:true width:140
spinner uiDist "Dist" range:[0,1e9,0] type:#float fieldwidth:55 align:#right
)
/* functions */
tool create numPoints:2
(
on mousePoint click do (#stop)
on mouseMove click do ()
)
fn vec3 xyz = (
dotNetObject vector3 xyz.x xyz.y xyz.z
)
fn getBezierKnot pos inVec outVec = (
dotNetObject splineKnot bezierKnot curveLine (vec3 pos) (vec3 inVec) (vec3 outVec)
)
fn getPathSegandParamFromDist sp theDistance =
(
/*
Returns a point2 value containing the segment and segment
fraction for the specified vertex-interpolated spline parameter.
*/
local param = [0,0]
local sumSegs = 0
local numSegs = sp.Segments()
for i = 1 to numSegs do
(
local segLength = sp.SegmentLength(i-1) -- zero index
sumSegs += segLength
if sumSegs > theDistance do
(
local delta = (theDistance - (sumSegs - segLength)) / segLength
param.y = delta
return param
)
param.x += 1
)
return param
)
on buildShape do
(
local shapeObj = bezierShapeObj._CreateWrapperFromFPValue bezierShape
shapeObj.NewShape()
if isValidNode targetShape do
(
for sidx = 1 to numSplines targetShape do
(
/* rebuild ref spline */
local sp = shapeObj.NewSpline()
for i = 1 to numKnots targetShape sidx do
(
local pos = getKnotPoint targetShape sidx i
local outVec = getOutVec targetShape sidx i
local inVec = getInVec targetShape sidx i
sp.AddKnot (getBezierKnot pos inVec outVec)
)
if isClosed targetShape sidx == true do sp.SetClosed()
/* add refine point on new spline - at given distance */
local len = sp.SplineLength()
local frac = dist / len as float
local param = getPathSegandParamFromDist sp dist
format "SDK: %/% = FRAC: % = PARAM: % %\n" dist len frac param.x param.y
if param.y > 0 do
(
sp.RefineSegment param.x param.y
)
)
)
shapeObj.UpdateSels()
shapeObj.InvalidateGeomCache()
)
)
fn test =
(
delete objects
clearlistener()
local ss = SplineShape wirecolor:red vertexticks:true
ss.adaptive = true
addNewSpline ss
addKnot ss 1 #bezier #curve [-10,-10,0] [-15,0,0] [15,0,0]
addKnot ss 1 #bezierCorner #curve [30,35,0] [0,35,0] [65,35,0]
addKnot ss 1 #bezier #curve [70,0,0] [10,0,0] [100,0,0]
addKnot ss 1 #corner #curve [100,20,0]
updateShape ss
/* General Maxscript */
local spref = copy ss
spref.vertexticks = true
spref.wirecolor = yellow
local dist = 100
local len = curveLength spref 1
local frac = dist / len as float
/* trying to recreate this code within plugin ... getPathSegandParamFromDist */
local pathParam = amin (lengthToPathParam spref 1 frac steps:100) 1.0
local segParam = findPathSegAndParam spref 1 pathParam
local knot = refineSegment spref 1 segParam.x segParam.y
format "MXS: %/% = FRAC: % = PARAM: % %\n" dist len frac segParam.x segParam.y
/* spline plugin */
local sparam = SplineParam targetShape:ss vertexticks:true wirecolor:green dist:dist
select sparam
)
test()