Author Topic: Flute... anyone ?  (Read 7819 times)

0 Members and 1 Guest are viewing this topic.

hendie

  • Guest
Flute... anyone ?
« on: December 18, 2003, 06:14:05 AM »
I was a bit apprehensive about posting this lisp ‘cos it’s still so much “in progress”. But I’ve been working on it over such a long time, I’ve sort of forgotten what I started out to do !
The original idea came about when I saw so many posts with questions about drawing drill bits and extruding a shape along a path but twisting it at the same time.
I now have the lisp able to “twist and extrude” any closed polyline along the Z axis of it’s centroid.
My methods was to gather the vertex info from the polyline, calculate the spiral resulting from each vertex in relation to the centroid, then draw a 3D spiral (polyline) from each vertex in turn. Once I have the “shape of the object, I just applied meshes to each face as required.

But now I have reached that stage, what do I do ?
Do I group them so they can be moved as a single entity ? is it possible to join the meshes together ? should I cap the ends ? Is it really necessary to try and convert into a solid ? or should I leave it as is and the user can decide what course of action to take ?

oh, here's a pretty picture....




So here it is, as I said, very much a work in progress. So, excuse the untidiness and unlocalised variables etc and try it out if you want.
Comments, ideas and suggestions welcomed…. And if I ever get back to it…..

Code: [Select]
;|ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
<                     A. Henderson 2003                            >
< routine to "flute" (extrude & revolve) any closed polyline about >
< it's own centroid given a height. i.e. for twist drills etc      >
<           should work for any closed polyline                    >
<                                                                  >
<     this programme may require the 'Swamp VL-toolkit'            >
<               in order to run successfully.                      >
<                                                                  >
<  =============================================================   >
<     http://theswamp.org/swamp.files/Public/AcadApp.lsp           >
<  =============================================================   >
<                                                                  >
< this programme is supplied "as is" with no guarantees whatsoever >
< it is recommended you trial this routine on non-critical work to >
<                  ensure it's fitness for use.                    >
<__________________________________________________________________>
|;

(DEFUN C:flute ()
;;;***************MAKE POINTS LIST****************************
  (defun gp:list->variantArray (ptsList / arraySpace sArray)
    (setq arraySpace
  (vlax-make-safearray
    vlax-vbdouble
    (cons 0 (- (length ptsList) 1))
  )
    )
    (setq sArray (vlax-safearray-fill arraySpace ptsList))
    (vlax-make-variant sArray)
  )
;;;==================END MAKE POINTS LIST==================
;;;***************MAKE REGION******************************
;;; make the polyline a region to get the centroid (point of rotation)
  (defun mkRegion ()
    (defun *error* (msg)
      (if
(not
 (member
   msg
   '("console break" "Function cancelled" "quit / exit abort")
 )
)
(princ (strcat "\nError: " msg))
      )
      (princ)
    )
    (while
      (while (not Obj)
(setq Obj (entsel "\nPlease select object to Flute: "))
(if
 (and Obj
      (progn
(/= "LWPOLYLINE" (cdr (assoc 0 (entget (car Obj)))))
(/= 1 (cdr (assoc 70 (entget (car obj)))))
      )
 )
  (progn
    (prompt "\nObject is not a closed polyline - try again")
    (setq Obj nil)
  )
)
      )
    )
    (setq obj (car Obj))
    (setq obj1 (vlax-ename->vla-object obj))
    (setq va (vlax-make-safearray vlax-vbObject '(0 . 0)))
    (vlax-safearray-put-element va 0 obj1)
    (setq reg (car (vlax-safearray->list (vlax-variant-value (vla-addregion (get-model-space) va))))
 cen (vla-get-centroid reg)
    )
    (vla-delete reg)
  )
;;;==================END MAKE REGION=======================
;;;*********************MAKE SPIRAL************************
  (defun SP ()
    (setq SP-ctr  0
 sp-lst  NIL
 rtangle (* pi (/ rtang 180.0))
    )
    (setq Stangle (angle
   (vlax-safearray->list (vlax-variant-value cen))
   (vlax-safearray->list (vlax-variant-value PT1))
 )
    )
    (setq base (vla-get-elevation obj1)
 dist1 (distance
 (vlax-safearray->list (vlax-variant-value PT1))
 (vlax-safearray->list (vlax-variant-value cen))
)
    )
;;; Create the points list
    (while
      (< SP-ctr segment)
       (if (= SP-ctr 0)
(progn
  (setq npt (vlax-safearray->list (vlax-variant-value pt1))
npt (list (cons (car npt) (cadr npt)))
SP-lst (append SP-lst npt)
stangle (+ stangle rtangle)
  )
)
       )
       (progn
(setq nPt (polar (vlax-safearray->list (vlax-variant-value cen)) stangle dist1)
      npt (list (cons (car npt) (cadr npt)))
)
       )
       (setq stangle (+ stangle rtangle))
       (setq SP-lst (append SP-lst nPt)) ; and add it to the list
       (setq SP-ctr (1+ SP-ctr))

    )  ; end while
;;;**************** CREATE POLYLINE POINTS LIST************
    (setq Nx 0
 polypoints
  nil
    )
    (while (< Nx (length sp-lst))
      (setq tmp (nth Nx sp-lst))
      (setq Plp (list (car tmp) (cdr tmp) base))
      (setq polypoints (append polypoints plp))
      (setq Nx (1+ Nx))
      (setq base (+ base incr))
    )
  )
;;;===================END CREATE POLYLINE==================
;;;===================END MAKE SPIRAL======================
;;; Copy the original shape to the correct elevation and rotate it
;;; and then explode to get separate entities ~ needed to create the mesh
  (defun CpUpR ()
    (setq tmpobj (vla-copy obj1)
 tmpt1 (vlax-safearray->list (vlax-variant-value cen))
 tmpt1 (vlax-3d-point tmpt1)
 tmpt2 (vlax-safearray->list (vlax-variant-value cen))
    )
    (setq tmpt2 (list
 (car (vlax-safearray->list (vlax-variant-value cen)))
 (cadr (vlax-safearray->list (vlax-variant-value cen)))
 height
)
    )
    (setq tmpt2 (vlax-3d-point tmpt2))
    (vla-move tmpobj tmpt1 tmpt2)
    (setq a1 (/ (* pi rotate) 180))
    (vla-rotate tmpobj tmpt2 a1)
    (setq Sep (vla-explode obj1)) ; need to delete the original
    (setq SepZ (vla-explode tmpobj)) ; need to delete the original
  )
;;;**************** MAIN FUNCTION *************************
 ;(defun C:flute ()
  (setq Obj NIL)
  (setq Sft1 (getvar "surftab1"))
  (setq Sft2 (getvar "surftab2"))
  (mkregion)
  (setq rotate (getreal "\nEnter rotation angle: "))
  (setq segment (getint "\nNumber of segments: "))
  (setq height (getreal "\nTotal height: "))
  (setvar "surftab1" (fix (* segment 1.5)))
  (setvar "surftab2" (fix (* segment 1.5)))
  (setq incr (/ height segment))
  (setq rtang (/ rotate segment))
  (setq PolyNo (cdr (assoc 90 (entget obj))))
  (setq L1 NIL)
  (setq L1 (vlax-make-safearray vlax-vbObject (cons 0 polyno)))
  (setq vtx 0)
  (while (< vtx polyno)
    (setq PT1 (vla-get-coordinate obj1 vtx))
    (SP)
    (setq VLADataPts (gp:list->variantArray polypoints)
 Pline3d    (vla-add3dpoly (get-model-space) VLADataPts)
    )
    (vlax-safearray-put-element L1 vtx Pline3d)
    (setq vtx (1+ vtx))
  )  ; end while
  ;; need to add the 0th member of L1 to the end of l1 for the mesh
  (setq l1a (car (vlax-safearray->list l1)))
  (vlax-safearray-put-element L1 vtx L1a)
  (CpUpR)
;;; now we need to create a list to add the mesh to
  (setq m-lst Nil)
  (setq vtx 0)
  (while (< vtx polyno)
    (setq m-lst (list
 (vlax-vla-object->ename (nth vtx (vlax-safearray->list l1)))
 (vlax-vla-object->ename (nth vtx (vlax-safearray->list (vlax-variant-value sep))))
 (vlax-vla-object->ename (nth vtx (vlax-safearray->list (vlax-variant-value sepz))))
 (vlax-vla-object->ename (nth (1+ vtx) (vlax-safearray->list l1)))
)

 Mno (length m-lst)
 vtx2 0
    )
 ; and create the mesh
    (setq oldecho (getvar "cmdecho"))
    (setvar "cmdecho" 0)
    (vl-cmdf "edgesurf"
    (nth vtx2 m-lst)
    (nth (1+ vtx2) m-lst)
    (nth (+ 2 vtx2) m-lst)
    (nth (+ 3 vtx2) m-lst)
    )
    (setq vtx (1+ vtx))
  )  ; end while
  (vla-erase tmpobj) ; delete the original closed polylines
  (vla-erase obj1)
  (setvar "surftab1" Sft1) ; reset surftab1 & 2
  (setvar "surftab2" Sft2)
  (setvar "cmdecho" oldecho)
  (prompt "Fluting completed ")
  (princ)
)



CAB

  • Global Moderator
  • Seagull
  • Posts: 10395
Flute... anyone ?
« Reply #1 on: December 18, 2003, 07:48:11 AM »
WOW..

Very good Hendie, looks like art to me.. :)

CAB
I've reached the age where the happy hour is a nap. (°ż°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

hyposmurf

  • Guest
Flute... anyone ?
« Reply #2 on: December 18, 2003, 08:02:07 AM »
You should put your thoughts together with Fuccaro,you could both come up with something pretty outstanding!He hasn't posted much,maybe he's a bit shy :) !

SMadsen

  • Guest
Re: Flute... anyone ?
« Reply #3 on: December 18, 2003, 08:47:46 AM »
Quote from: hendie
But now I have reached that stage, what do I do ?

Excellent stuff, Hendie!

Next stages could be:
- Cleaning up so that functions work as standalones. It hurts the eye that mkRegion doesn't return e.g. (list obj cen) and that SP doesn't take arguments.
- Avoiding VL-CMDF (or COMMAND) and write a revsurf mesh function so that the finished object comes out as an entire 3D mesh

Should only take you a couple of minutes  :)

hendie

  • Guest
Re: Flute... anyone ?
« Reply #4 on: December 18, 2003, 09:05:07 AM »
thanks for all the reponses guys !

this Lisp was very much a learning process for me.

Quote from: SMadsen
- Avoiding VL-CMDF (or COMMAND) and write a revsurf mesh function so that the finished object comes out as an entire 3D mesh

Should only take you a couple of minutes  :)


Initially I was trying to do that but I couldn't figure out a way to apply the mesh without using vl-cmdf. It would be great to be able to create the shape using a single mesh but my limited brain power could only think along the lines of : retrieve the vertex coordinates, sort them into some form of list and then recalculating the new mesh for and every vertex and then by that stage, syrup started pouring out  :shock:

Hopefully I'll be able to get back to it sometime but in the meantime, if any of you guys fancy adding to it or see anyway that things could be done better... feel free to mess about !

daron

  • Guest
Flute... anyone ?
« Reply #5 on: December 18, 2003, 09:48:42 AM »
The simplest way to cap the ends from what you have would be to take your pline and leave it as a region.

hendie

  • Guest
Flute... anyone ?
« Reply #6 on: December 18, 2003, 10:02:31 AM »
I had thought of that but for some reason I had always envisaged that something would go on either end of the fluted object (don't ask me why)

I'm still trying to get my mind around any method of creating a single mesh  :shock:  :roll:

daron

  • Guest
Flute... anyone ?
« Reply #7 on: December 18, 2003, 10:18:06 AM »
Have you looked into AddPolyfaceMesh Method?

RetVal = object.AddPolyfaceMesh(VerticesList, FaceLIst)

Object = ModelSpace etc.
VerticesList = Variant (array of doubles). Min of four points with two elements

FaceList = Variant (array of Integers) Representing the vertex numbers for each face.

hendie

  • Guest
Flute... anyone ?
« Reply #8 on: December 18, 2003, 10:36:26 AM »
I just had a quick look and call me bloody frightened, but wouldn't I need to provide each and every vertex coordinate using that method ? (that's a humungous number of vertices to calculate).

daron

  • Guest
Flute... anyone ?
« Reply #9 on: December 18, 2003, 12:38:01 PM »
What does polypoints return?

SMadsen

  • Guest
Flute... anyone ?
« Reply #10 on: December 18, 2003, 01:46:43 PM »
Polyfaces are bitches to work with, but it would be worth while to look at Add3Dmesh.

SMadsen

  • Guest
Flute... anyone ?
« Reply #11 on: December 18, 2003, 03:58:47 PM »
Below is a routine that twists a shape like the C:FLUTE routine, except that it builds a contiguous 3Dmesh on the fly. It doesn't (yet) calculate a center point or centroid but asks the user to select one. This makes it possible to twist around any point - but of course it should have a feature that calculates the centroid, also.
It has a linear taping feature so that the finished mesh can be tapered. It tends to produce unexpected results but is fun to play around with :)

Here's a pic of the result (sorry about the poor quality). The yellow thingy is a rect, twisted at an unsymmetric point and with a taper distance. The blue is straight and the red is the same shape but tapered. The pink thingy is .. well, it's just weird.



Code: [Select]
(defun getEnt (lst / ent)
  (setvar "ERRNO" 0)
  (while (and (not ent) (/= (getvar "ERRNO") 52))
    (cond ((setq ent (car (entsel)))
           (if (not (member (cdr (assoc 0 (entget ent))) lst))
             (setq ent nil)
           )
          )
    )
  )
  ent
)

(defun getSegments (/ s1 s2)
  (setq s1 (max (getvar "SURFTAB1") 6)
        s2 (max (getvar "SURFTAB2") 12)
  )
  (list (cond ((getint (strcat "\nSpecify horizontal segmentation <" (itoa s1) ">: ")))
              (s1)
        )
        (cond ((getint (strcat "\nSpecify vertical segments <" (itoa s2) ">: ")))
              (s2)
        )
  )
)

(defun getVertices (curve segh / vlst n v p offset osm)
  (setq n      (vlax-curve-getStartParam curve)
        v      (vlax-curve-getEndParam curve)
        offset (/ 1.0 segh)
        osm    (getvar "OSMODE")
  )
  (setvar "OSMODE" 0)
  (while (< n v)
    (setq p n)
    ;; use EQUAL to adjust for small decimal deviations
    ;; otherwise it could just be (< p (1+ n))
    (while (not (equal p (1+ n) 1.0E-6))
      (setq
        vlst (cons (vlax-curve-getPointAtParam curve p) vlst)
        p    (+ p offset)
      )
    )
    (setq n (1+ n))
  )
  (setvar "OSMODE" osm)
  (reverse vlst)
)

(defun dtr (ang)(/ (* ang pi) 180.0))

(defun C:FLUTEX (/       osm     mlist   angcnt  angincr cpt     end_ang ent     hgt
                 mlen    nlen    obj     ptlist  rot     result  seg1    seg2    st_ang
                 z
                )
  (cond ((setq ent (getEnt '("LWPOLYLINE")))
         (mapcar 'set '(seg1 seg2) (getSegments))
         (setq rot (getreal "\nSpeficy rotation in degr.: ")
               hgt (getdist "\nSpecify height: ")
         )
         (setq obj (vlax-ename->vla-object ent))
        )
  )
  ;; check if all stuff is ok
  (cond ((and obj (apply 'and (mapcar 'numberp (list seg1 seg2 rot hgt))))
         ;; ready to extract points from obj
         (cond ((and (setq ptlist (getVertices obj seg1))
                     (setq cpt (getpoint "\nCenter of rotation: "))
                     (setq taper (cond ((getdist "\nTaper distance <0.0>: "))
                                       (T 0.0)
                                 )
                     )
                )
                (setq mlist (cons ptlist mlist)
                      mlen  (length ptlist)
                )
                ;; first point set is already added to mlist,
                ;; so set hgtcnt to one step up the z-ladder
                (setq rot     (dtr rot)
                      hgtcnt  (/ hgt seg2)
                      hgtincr hgtcnt
                      cpt     (list (car cpt)(cadr cpt) hgtcnt)
                      angincr (/ rot seg2)
                      tcnt    0.0
                      tincr   (/ taper seg2)
                      stpt    (list (caar ptlist)(cadar ptlist) hgtcnt)
                      angcnt  (angle cpt stpt)
                      osm     (getvar "OSMODE")
                )
                (setvar "OSMODE" 0)
                (command "UNDO" "Begin")
                (while
                  (and (apply 'and ptlist) (<= hgtcnt hgt))
                   ;;(< angcnt end_ang))
                   (setq ptlist
                          (mapcar
                            (function (lambda (pt / npt)
                                        (setq npt (list (car pt) (cadr pt) hgtcnt))
                                        (setq npt (polar cpt
                                                         (+ (angle cpt npt) angincr)
                                                         (- (distance cpt npt) tcnt)))
                                      )
                            )
                            ptlist
                          )
                   )
                   (setq hgtcnt (+ hgtcnt hgtincr)
                         cpt    (list (car cpt) (cadr cpt) hgtcnt)
                         angcnt (+ angcnt angincr)
                         tcnt   (+ tcnt tincr)
                   )
                   (setq mlist (cons ptlist mlist))
                )
                (princ (setq nlen (length mlist)))
                (cond
                  ((entmake
                     (list '(0 . "POLYLINE")
                           '(100 . "AcDbEntity")
                           '(8 . "0")
                           '(100 . "AcDbPolygonMesh")
                           '(66 . 1)
                           '(10 0.0 0.0 0.0)
                           (cons 70 (+ 16 32))
                           '(40 . 0.0)
                           '(41 . 0.0)
                           '(210 0.0 0.0 1.0)
                           (cons 71 nlen)
                           (cons 72 mlen)
                           '(73 . 0)
                           '(74 . 0)
                           '(75 . 0)
                     )
                   )
                   (foreach mrow mlist
                     (foreach nrow mrow
                       (entmake (list '(0 . "VERTEX")
                                      '(100 . "AcDbEntity")
                                      '(100 . "AcDbPolygonMesh")
                                      '(100 . "AcDbPolygonMesh")
                                      (cons 10 nrow)
                                      '(70 . 64)
                                )
                       )
                     )
                   )
                   (setq result (entmake (list '(0 . "SEQEND"))))
                  )
                )
                (setvar "OSMODE" osm)
                (command "UNDO" "End")
               )
         )
        )
  )
)

SMadsen

  • Guest
Flute... anyone ?
« Reply #12 on: December 18, 2003, 05:04:20 PM »
How about making a little Xmas candle with it?



hendie

  • Guest
Flute... anyone ?
« Reply #13 on: December 19, 2003, 03:57:17 AM »
cool Stig ! I love it.

I'm going to have to steal that code and try and work my way through it. Another idea I had was to be able to pick a pline to follow as a path while still doing the twisty thingy, but I think that's maybe a bit advanced for me at the moment

SMadsen

  • Guest
Flute... anyone ?
« Reply #14 on: December 19, 2003, 05:13:06 AM »
Quote from: hendie
I'm going to have to steal that code and try and work my way through it. Another idea I had was to be able to pick a pline to follow as a path while still doing the twisty thingy, but I think that's maybe a bit advanced for me at the moment

Steal away :)

Following a path is exactly what the routine does, except it's a path calculated on the fly. It's actually quite simple if you study the vlax-curve functions a bit.

Here's the simplified pseudocode:
1. Pick a curve object
2. Divide the curve into points. Save all points into a list.
3. Pick a point and a height (in reality a straight-up path).
4. Divide height into equally spaced Z-incrementions.
5. Lift point in Z-direction in a distance of one incremention.
6. While point has not reached the Z-value of the height, do a. and b.:
6a.  Rotate all curve-points around the point. Save the new 'row' of points.
6b.  Lift point in Z-direction
7. Pass all rows of points to a 3Dmesh creation


1-2) This could be any curve object, except that parameters for each type of curve can differ and therefore can require a different calculation. E.g. splines have a more intricate set of parameters that would require another algorithm than lwpoly's.
Division is based on the horizontal segmentation set by the user (or SURFTAB1).

3) This actually constitutes a path that starts at the point, goes straight up and ends at the specified height. A skew or irregular path or one represented by an object wouldn't be much different, and it would be very easy to divide a path object the same way as the base curve in step 2. is divided.

4) Based on the vertical segmentation specified by the user (or SURFTAB2).

6a) Rotation is in this example done with POLAR. I suspect this to be a rather slow method and that it could be optimized by a matrix rotation.

7) Saving the points as they are calculated is the tricky part when passing it to a mesh. It depends on the creation method. ENTMAKE will require a point list for each vertex while Add3DMesh will require an array of doubles with the point structure represented by the sequence of doubles.
Notice that the example creates an n-closed mesh because, during division of the base curve, it considers the last vertex of each segment to belong to the following segment - which, in the last segment, will be the first segment. So it'll have to be n-closed "manually" by ENTMAKE.