Placing point along path at given distance


#1

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. :blush:

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()