# parametric3D.frink

``` /** This program extrudes 3-D models along a parametric curve.  It is quite     smart and creates its own adaptive step size to move along the parameter     with a minimum of steps but without gaps in the tool's position.     TODO:  Add a two-variable parametric equation which is trickier.  Ideally,     the function passed in should be differentiable by Frink which would allow     intelligent step sizes to be calculated directly. */ /** This calculates the tool path to move a tool along a parametric curve     specified by a function f that takes two arguments:      f[t, data]       where t is a value that increases from t0 to t1 and data is  an       arbitrary expression that can be used to pass additional data to the       function.  The function should return an array of [x, y, z] values with       dimensions of length.      res:  The resolution of the model with dimensions of inverse length, e.g.,         254/inch      This function is smart in that it adaptively adjusts the step used to move      the parameter t with a minimum of steps but without gaps in the tool's      position.      This returns a frink.graphics.Point3DIntList which specifies 3-D integer      coordinates where the tool should pass through.  The tool's path can be      instantiated into a 3-D model using VoxelArray.paintAlongPath  */ calculatePath[f, data, t0, t1, res] := {    points = newJava["frink.graphics.Point3DIntList", []]    tstep = (t1-t0)/1000.    // This timestep will be auto-adjusted.    t = t0    [x, y, z] = f[t, data]    //   println["\$x, \$y, \$z"]    ix = round[x res]    iy = round[y res]    iz = round[z res]    points.addPoint[ix, iy, iz]    lx = x    ly = y    lz = z        while t <= t1    {       do       {          tryagain = false          [x, y, z] = f[t+tstep, data]          ix = round[x res]          iy = round[y res]          iz = round[z res]          dx = abs[x - lx] res          dy = abs[y - ly] res          dz = abs[z - lz] res          idx = round[dx]          idy = round[dy]          idz = round[dz]          // Check to see if the voxel didn't move or if it moved by more than          // 1 pixel on any axis.  If so, adjust the step mathematically.          if ((idx== 0) and (idy == 0) and (idz==0)) or (abs[idx]>1) or (abs[idy]>1) or (abs[idz]>1)          {             d = sqrt[dx^2 + dy^2 + dz^2]             //d = max[[dx, dy, dz]]             if d > .98 and d < 1.02    // Prevent too-small adjustments                d = d^2             tstep = tstep / d             println["Adjusting tstep to \$tstep"]             tryagain = true          } else          { //            println["\$ix \$iy \$iz"]             points.addPoint[ix, iy, iz]          }       } while tryagain       lx = x       ly = y       lz = z       t = t + tstep    }    // Make sure we contain the exact last point.    [x, y, z] = f[t1, data]    ix = round[x res]    iy = round[y res]    iz = round[z res]    points.addPoint[ix, iy, iz]    return points } /** Sample parametric function to draw a helix.  The "data" parameter is     [radius, pitch, angle0]  where angle0 indicates the "start"     of the curve. */ helix[t, data] := {    [radius, pitch, angle0] = data    if angle0 == undef       angle0 = 0 deg    tt = t + angle0    x = radius cos[tt]    y = radius sin[tt]    z = (t / (2 pi)) pitch    return [x, y, z] } // Sample parametric function to draw a Moebius strip.  See // https://mathworld.wolfram.com/MoebiusStrip.html // s should vary from -w to w where w is the half-width // T should vary from 0 to 2 pi MobiusStrip[t, data] := {    [R, s] = data    x = (R + s cos[1/2 t]) cos[t]    y = (R + s cos[1/2 t]) sin[t]    z = s sin[1/2 t]    return [x,y,z] } ```