If a 3DPolyline with short [or however long you want] line segments is acceptable, here's my take on it:
(defun C:S3DP ; = Slope conversion to 3DPolyline
(/ osm esel ent elen bkw dp dz z pos)
(setq
osm (getvar 'osmode)
esel (entsel "\nSelect path object near end for high end of result: ")
bkw ; = backwards [high at downstream end]
(> ; picked closer to end than to start [T or nil]
(vlax-curve-getDistAtPoint (setq ent (car esel)) (osnap (cadr esel) "_nea"))
(/ (setq elen (vlax-curve-getDistAtParam ent (vlax-curve-getEndParam ent))) 2)
); >
dp (getdist "\nSpacing along original path per segment: "); delta along path
dz (* (/ (getdist "\nDownward Slope per Foot: ") 12) dp); delta in Z direction
z (getdist "\nStarting (high end) elevation: ")
pos (if bkw elen 0); position [as length along path]
); setq
(setvar 'osmode 0)
(command "_.3dpoly" ".XY" (vlax-curve-getPointAtDist ent pos) z); start 3DPolyline
(while (if bkw (>= (setq pos (- pos dp)) 0) (<= (setq pos (+ pos dp)) elen))
(command ".XY" (vlax-curve-getPointAtDist ent pos) (setq z (- z dz)))
); while
(setq pos ((if bkw + -) pos dp)); back to last within path length
(if (not (equal pos (if bkw 0 elen) 1e-8)); didn't hit other end exactly
(command; then -- last point [partial-dz drop]
".XY" (vlax-curve-getPointAtDist ent (if bkw 0 elen))
(- z (* dz (/ (if bkw pos (- elen pos)) dp)))
); command
); if
(command ""); end 3DPolyline
(setvar 'osmode osm)
(princ)
); defun
It works on any kind of finite object with linearity [Line, Arc, Circle, Polyline, Ellipse, Spline]. It takes advantage of point filtering [".XY"] so that it doesn't need to save a point location and extract X and Y coordinates from it, to apply the varying Z coordinate to.
It could easily be made to remember your choices for spacing and slope and starting elevation, and offer them as defaults on subsequent use, as well as the usual *error* handling, command-echo suppression, verification of selection of an appropriate object, etc.
Kent Cooper, AIA