Author Topic: Draw 3d cylinder based on a line  (Read 3786 times)

0 Members and 1 Guest are viewing this topic.

David Hall

  • Automatic Duh Generator
  • King Gator
  • Posts: 4043
Draw 3d cylinder based on a line
« on: February 22, 2013, 03:25:49 PM »
I am trying to reinvent the wheel by converting old VBA code to VLisp.  I have gotten to the point of being able to draw a cylinder in WCS, but cant figure out how to read a line to determine the vector to create the cylinder.  In VBA, we had to get the normal or the circle, and in VLisp, I am not seeing where this is located.


Code: [Select]
Public Sub DrawBus()


      On Error GoTo err_control


     
      Dim InsPt As Variant, dblRot As Double, strPrompt As String, intBusCenterLine As Integer
      Dim strDrawing As String, dblBusDia As Double
      Dim oLayer As AcadLayer, oLine As AcadLine
Dim newPT1 As Variant
Dim newPT2 As Variant
Dim P1, P2
Dim V(2) As Double, Unit As Double, Vn(2) As Double
Dim oCyl As Acad3DSolid, oCircle As AcadCircle
Dim regent(0) As AcadEntity
Dim oReg


Start:
      IsSetup
      frmBus.Show
      If frmBus.optHighBus.Value = True Then
            intBusCenterLine = RegistryEdit.getRegVal(HighBus)
      Else
            intBusCenterLine = RegistryEdit.getRegVal(LowBus)
      End If
     
      Select Case (getRegVal(BusSize))
      Case "30"
      dblBusDia = (3# / 2) + 0.25
      Case "35"
      dblBusDia = (3.5 / 2) + 0.25
      Case "40"
      dblBusDia = (4# / 2) + 0.25
      Case "50"
      dblBusDia = (5# / 2) + 0.25
      End Select


     
      Set oLayer = ThisDrawing.Layers.Add("3D-BUSS-CALC")
      oLayer.color = 234
      Dim objSelected As Object
      Dim objSelSet As AcadSelectionSet
           
      Set oLayer = ThisDrawing.Layers.Add("3D-BUSS")
      oLayer.color = 3
      Set objSelSet = ThisDrawing.SelectionSets.Add("Bus")
      objSelSet.SelectOnScreen
     
For Each objSelected In objSelSet
            If Not TypeOf objSelected Is AcadLine Then
                  MsgBox "That was not a Layout Line"
                  Exit Sub
            End If
            Set oLine = objSelected
            newPT1 = oLine.StartPoint
            newPT2 = oLine.EndPoint


            newPT1(2) = intBusCenterLine
            newPT2(2) = intBusCenterLine
            Set oLine = ThisDrawing.ModelSpace.AddLine(newPT1, newPT2)
            oLine.Layer = "3D-BUSS-CALC"
            P1 = oLine.StartPoint: P2 = oLine.EndPoint
            V(0) = P2(0) - P1(0): V(1) = P2(1) - P1(1): V(2) = P2(2) - P1(2)
      'Normalise the vector(It's length=1)
            Unit = Sqr(V(0) * V(0) + V(1) * V(1) + V(2) * V(2))
            Vn(0) = V(0) / Unit: Vn(1) = V(1) / Unit: Vn(2) = V(2) / Unit
            Set oCircle = ThisDrawing.ModelSpace.AddCircle(oLine.StartPoint, dblBusDia)
            oCircle.Normal = Vn    ' Vn or V both work here.
            Set regent(0) = oCircle
            oReg = ThisDrawing.ModelSpace.AddRegion(regent)
            Set oCyl = ThisDrawing.ModelSpace.AddExtrudedSolid(oReg(0), oLine.Length, 0)
            oCyl.Layer = "3D-BUSS"
            oCircle.Delete
            oReg(0).Delete
      Next
      ThisDrawing.SelectionSets.Item("Bus").Delete
      ThisDrawing.Application.Update
Exit_Here:
     
      ThisDrawing.SetVariable "INSUNITS", 1
      Unload frmBus
      Exit Sub
err_control:
      Select Case Err.Number


      Case "-2145320851"
            ThisDrawing.SelectionSets.Item("Bus").Delete
            Err.Clear
            Resume
      Case Else
            MsgBox Err.Description
            Resume Exit_Here
      End Select
End Sub


In the middle of the above is the normalization of the vector.  Can anyone point me in the direction to do this in VLisp
Everyone has a photographic memory, Some just don't have film.
They say money can't buy happiness, but it can buy Bacon and that's a close second

ribarm

  • Gator
  • Posts: 2747
  • Marko Ribar, architect
Re: Draw 3d cylinder based on a line
« Reply #1 on: February 22, 2013, 04:33:05 PM »
When you draw line with WCS set, line have at the end of DXF code values normal vector stored at 210 code as : (210 0.0 0.0 1.0), and as you can see normal of line is also WCS Z axis no matter if its points are in 3D (start and end point)... Only if you draw line in some relative UCS, 210 code will take normal z axis as vector of that particular UCS that was active when line was drawn... But I see that you are not seeking for this, but how to obtain vector of line entity defined by its start and end points... So, you take start point as :
Code - Auto/Visual Lisp: [Select]
  1. (setq stpt (cdr (assoc 10 (entget lin))))
  2.  
And end point as :
Code - Auto/Visual Lisp: [Select]
  1. (setq enpt (cdr (assoc 11 (entget lin))))
  2.  
Where lin is variable that represent ENAME of line entity. You can obtain it by selecting with :
Code - Auto/Visual Lisp: [Select]
  1. (setq lin (car (entsel "\nPick line")))
  2.  
Or you can get lin with cycling through sel. set of lines as :
Code - Auto/Visual Lisp: [Select]
  1. (prompt "\nSelect lines")
  2. (setq sslin (ssget '(0 . "LINE")))
  3. (repeat (setq n (sslength sslin))
  4.   (setq lin (ssname sslin (setq n (1- n))))
  5.   ... perform other operations with obtained ENAME of LINE object stored in variable lin ...
  6. )
  7.  

And to get vector of line, you just subtract vector of enpt with vector of stpt :
Code - Auto/Visual Lisp: [Select]
  1. (setq vec (mapcar '- enpt stpt))
  2.  

You can then entmake circle with unit vector of vector vec as DXF 210 code, with center at stpt of line as DXF 10 code, and with radius you desire as DXF 40 code...

Then you can do extrude command on that circle with length as length of line :
Code - Auto/Visual Lisp: [Select]
  1. (setq len (distance '(0.0 0.0 0.0) vec))
  2.  

Or all this witch I strongly suggest do through VLISP ActivX with AddCylinder method :
Code - Auto/Visual Lisp: [Select]
  1. (defun trp (m) (apply 'mapcar (cons 'list m)))
  2.  
  3. (defun mxv (m v)
  4.   (mapcar (function (lambda (r) (apply '+ (mapcar '* r v)))) m)
  5. )
  6.  
  7. (defun mxm (m q)
  8.   (mapcar (function (lambda (r) (mxv (trp q) r))) m)
  9. )
  10.  
  11. (defun OCSMatrix (zvec)
  12.   (mapcar '(lambda (x) (trans x 0 zvec))
  13.           '((1. 0. 0.) (0. 1. 0.) (0. 0. 1.))
  14.   )
  15. )
  16.  
  17. (defun c:obl-lin ( / ss n r ent entA ptlista len cyl zvec tmat1 tmat2 tmat3 tmat )
  18.         adoc (vla-get-activedocument aobj)
  19.         mspc (vla-get-modelspace adoc)
  20.   )
  21.   (command "ucs" "w")
  22.   (prompt "\nSelect lines")
  23.   (setq ss (ssget "_:L" (list (cons 0 "LINE"))))
  24.   (setq n -1)
  25.   (setq r (getdist "\nPick radius of circle that will sweep along lines (2 points) : "))
  26.   (repeat (sslength ss)
  27.     (setq n (+ n 1))
  28.     (setq ent (ssname ss n))
  29.     (setq entA (vlax-ename->vla-object ent))
  30.     (setq ptlista (cons (cdr (assoc 10 (entget ent))) ptlista) ptlista (cons (cdr (assoc 11 (entget ent))) ptlista))
  31.     (setq ptlista (reverse ptlista))
  32.     (setq len (distance (car ptlista) (cadr ptlista)))
  33.     (setq cyl (vla-addcylinder mspc (vlax-3d-point (mapcar '- (list (caar ptlista) (cadar ptlista) (caddar ptlista)) (list 0. 0. (- (/ len 2))))) r len))
  34.     (setq zvec (mapcar '- (cadr ptlista) (car ptlista)))
  35.     (setq tmat2 (append (mapcar '(lambda (x) (append x '(0.))) (OCSMatrix zvec)) '((0. 0. 0. 1.))))
  36.     (setq tmat1 (list
  37.                   (list 1. 0. 0. (- (caar ptlista)))
  38.                   (list 0. 1. 0. (- (cadar ptlista)))
  39.                   (list 0. 0. 1. (- (caddar ptlista)))
  40.                   (list 0. 0. 0. 1.)
  41.                 ))
  42.     (setq tmat3 (list
  43.                   (list 1. 0. 0. (caar ptlista))
  44.                   (list 0. 1. 0. (cadar ptlista))
  45.                   (list 0. 0. 1. (caddar ptlista))
  46.                   (list 0. 0. 0. 1.)
  47.                 ))
  48.     (setq tmat (mxm tmat3 (mxm tmat2 tmat1)))
  49.     (vla-transformby cyl (vlax-tmatrix tmat))
  50.     (setq ptlista nil)
  51.   )
  52. )
  53.  

I hope I explained well - here is my version I used to use often...

M.R.
Marko Ribar, d.i.a. (graduated engineer of architecture)

:)

M.R. on Youtube

Lee Mac

  • Seagull
  • Posts: 12696
  • London, England
Re: Draw 3d cylinder based on a line
« Reply #2 on: February 22, 2013, 06:07:13 PM »
I'm not sure that the task requires as much matrix multiplication as shown in ribarm's example...  :-o

Here is some quickly written example code, the cylinder radius & height are noted at the top of the code:
Code: [Select]
(defun c:test ( / enx hgt ins mat obj rad sel vec )
    (setq rad 2.5 ;; Cylinder Radius
          hgt 5.0 ;; Cylinder Height
    )
    (if (setq sel (ssget "_+.:E:S" '((0 . "LINE") (410 . "Model"))))
        (progn
            (setq enx (entget (ssname sel 0))
                  ins (cdr (assoc 10 enx))
                  vec (mapcar '- (cdr (assoc 11 enx)) ins)
                  obj (vla-addcylinder (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object))) (vlax-3D-point ins) rad hgt)
                  mat (mapcar '(lambda ( v ) (trans v 0 vec t)) '((1.0 0.0 0.0) (0.0 1.0 0.0) (0.0 0.0 1.0)))
            )
            (vla-transformby obj
                (vlax-tmatrix
                    (append
                        (mapcar 'append mat
                            (mapcar 'list
                                (mapcar '+ (trans (list 0.0 0.0 (/ hgt 2.0)) vec 0 t)
                                    (mapcar '- ins (mxv mat ins))
                                )
                            )
                        )
                       '((0.0 0.0 0.0 1.0))
                    )
                )
            )
        )
    )
    (princ)
)

;; Matrix x Vector  -  Vladimir Nesterovsky
;; Args: m - nxn matrix, v - vector in R^n

(defun mxv ( m v )
    (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m)
)
(vl-load-com) (princ)

The above should also work in all UCS & Views.

David Hall

  • Automatic Duh Generator
  • King Gator
  • Posts: 4043
Re: Draw 3d cylinder based on a line
« Reply #3 on: February 25, 2013, 11:40:36 AM »
Lee, it did work, but trying to follow along, I am not sure what all the abv.s are.
Everyone has a photographic memory, Some just don't have film.
They say money can't buy happiness, but it can buy Bacon and that's a close second

David Hall

  • Automatic Duh Generator
  • King Gator
  • Posts: 4043
Re: Draw 3d cylinder based on a line
« Reply #4 on: February 25, 2013, 11:48:42 AM »
OK, so enx is the line, ins is the startpoint of line, vec is the vector of line gotten by subtracting endpoint from startpoint?  obj is the new cylinder, mat is the trans matrix? Which makes the normal 1 unit long in the direction of the vector?

I guess what is so frusterating is I cant find a good book that explains each of these functions.  F1 is no help.  It keeps loading Autocad help and I cant find the Lisp/Vlisp commands.  I bought the 2011 Vlisp Bible, but it is too generic and doesn't give good examples.
Everyone has a photographic memory, Some just don't have film.
They say money can't buy happiness, but it can buy Bacon and that's a close second

Lee Mac

  • Seagull
  • Posts: 12696
  • London, England
Re: Draw 3d cylinder based on a line
« Reply #5 on: February 25, 2013, 11:56:04 AM »
Here is a quickly commented version:

Code: [Select]
(defun c:test ( / enx hgt ins mat obj rad sel vec )
    (setq rad 2.5 ;; Cylinder Radius
          hgt 5.0 ;; Cylinder Height
    )
    ;; Retrieve single selection of LINE entity
    (if (setq sel (ssget "_+.:E:S" '((0 . "LINE") (410 . "Model"))))
        (progn
            (setq
                ;; DXF data of selected Line
                enx (entget (ssname sel 0))
                ;; Start point of Line (insertion point for Cylinder)
                ins (cdr (assoc 10 enx))
                ;; Line vector
                vec (mapcar '- (cdr (assoc 11 enx)) ins)
                ;; VLA Cylinder Object with insertion point at start point of line and radius and height as specified above.
                ;; Note: at this point, the cylinder has extrusion vector 0,0,1 and the insertion point is at the midpoint.
                obj (vla-addcylinder (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object))) (vlax-3D-point ins) rad hgt)
                ;; Change of basis matrix to transform to coordinate system defined by the
                ;; line vector under the Arbitrary Axis Algorithm.
                mat (mapcar '(lambda ( v ) (trans v 0 vec t)) '((1.0 0.0 0.0) (0.0 1.0 0.0) (0.0 0.0 1.0)))
            )
            ;; Transform the Cylinder by the following transformation matrix
            (vla-transformby obj
                ;; Convert the following 4x4 list to a transformation matrix variant
                (vlax-tmatrix
                    ;; Append the last row to the 3x4 matrix
                    (append
                        ;; Append the translation vector to the 3x3 change of basis matrix
                        (mapcar 'append mat
                            ;; Convert the translation vector to a list of lists for appending
                            (mapcar 'list
                                ;; Include a translation by half the cylinder height in the Z-direction
                                ;; to ensure the Line start point is at the cylinder base
                                (mapcar '+ (trans (list 0.0 0.0 (/ hgt 2.0)) vec 0 t)
                                    ;; Calculate the translation vector to retain the cylinder position
                                    ;; following application of the change of basis matrix
                                    (mapcar '- ins (mxv mat ins))
                                )
                            )
                        )
                        ;; The last row of the transformation matrix to obtain a square 4x4 matrix
                       '((0.0 0.0 0.0 1.0))
                    )
                )
            )
        )
    )
    (princ)
)

;; Matrix x Vector  -  Vladimir Nesterovsky
;; Args: m - nxn matrix, v - vector in R^n

(defun mxv ( m v )
    (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m)
)
(vl-load-com) (princ)

Lee Mac

  • Seagull
  • Posts: 12696
  • London, England
Re: Draw 3d cylinder based on a line
« Reply #6 on: February 25, 2013, 12:01:20 PM »
so enx is the line

Correct, enx = DXF data of Line

ins is the startpoint of line

Correct, ins = start point of Line = Insertion point for Cylinder

vec is the vector of line gotten by subtracting endpoint from startpoint?

Correct, the line vector - a vector with direction in the same direction as the line, and magnitude equal to the length of the line.

obj is the new cylinder

Correct, the cylinder will always be created with extrusion vector 0,0,1, with the insertion point located at the (x,y) center of the circle, and z value at the midpoint of the cylinder length.

mat is the trans matrix?

mat is the matrix to transform from WCS to the coordinate system defined by the line vector under the Arbitrary Axis Algorithm (since a single vector cannot define a coordinate system). Note that trans does not need a unit vector, but may operate with a vector of any length.

Which makes the normal 1 unit long in the direction of the vector?

This operation is not required since the trans function does not need a unit vector, but may operate with a vector of any length.