Author Topic: Find normal vectors  (Read 6538 times)

0 Members and 1 Guest are viewing this topic.

PrinceLISPalot

  • Newt
  • Posts: 34
  • perfectionist trapped inside the mind of an idiot.
Find normal vectors
« on: October 16, 2012, 05:35:18 AM »
Hi,

I'm trying to replicate mimic the SWEEP command in Bricscad (doesn't have) using LISP. Approach is to select a polyline profile, and a basepoint. Then select line(s) to sweep. Routine then orientates the profile at the end of the selected lines using the ALIGN command (no align function in Bricscad LISP). Finally the EXTRUDE command is used to generate a solid.

I have this working except my method for orientating my profile at the start of the line is flawed, so in some instances the profile is normal to the sweep line, but in the wrong rotation.

To fix I've tried to create a function that provides 3 direction vectors based on two points of the line to be swept. Plan was to use this to correctly orientate my profile. I generated the function below, but maths isn't my strong point, so I have a function that doesn't work in all cases (divide by zero etc.), and that has all the elegance of a pig in a dress.

Would appreciate some pointers on how to approach this differently. Would also be interested if I could use transformby to perform the profile alignment step.

Code: [Select]
;returns a list of 3 Normal Vectors based on 2 points provided
; X direction is to the right when looking at point 1 from point 2, and is parallel to the XY plane (Z always 0)
; Y direction is straight when looking at point 1 from point 2
; Z direction is in the same direction as the line, positive in the direction of point 2

(defun FINDNORMALVECTORS (point1 point2
/
ptx1 ptx2 distx1 distx2 slopex normalx normalanglex normalvectx
disty1 disty2 slopey normaly normalangley distnormalx anglexy normalvecty
distp1p2 relvectz normalvectz
)
;X direction vector
(setq
ptx1 (append (list (car point2)) (cdr point1)) ; find point on x axis relative to first point
ptx2 (list (car point2) (cadr point2) (caddr point1)) ; find point on xy plane relative to first point
distx1 (distance point1 ptx1) ; find distance on x axis relative to first point
distx2 (distance ptx1 ptx2) ; find distance on xy plane relative to x axis
slopex (/ distx2 distx1) ; calculate slope of line in xy plane
normalx (/ -1.0 slopex) ; calculate normal of line in xy plane
normalanglex (+ pi (atan normalx)) ; find normal angle in xy plane, adding pi to flip normal 180 deg
normalvectx (list (cos normalanglex) (sin normalanglex) 0) ; create a normal vector in xy plane relative to first point
);end Setq

;Y direction vector
(setq
disty1 (distance point1 ptx2) ; find distance on xy plane relative to first point
disty2 (distance ptx2 point2) ; find distance from xy plane to second point
slopey (/ disty2 disty1) ; calculate slope of line from xy plane
normaly (/ -1.0 slopey) ; calculate normal of line from xy plane
normalangley (+ pi (atan normaly)) ; find normal angle from xy plane , adding pi to flip normal 180 deg
distnormalx (cos normalangley) ; find distance to normal in xy plane
anglexy (atan slopex) ; find angle of line from xy plane
normalvecty (list (* distnormalx (cos anglexy)) (* distnormalx (sin anglexy)) (sin normalangley)) ; create a normal vector from xy plane relative to first point
);end Setq

;Z direction vector
(setq distp1p2 (* 1.0 (distance point1 point2))) ; find the distance between the two points
(setq relvectz ; find the relative vector between the two points
(list
(- (car point2)(car point1)) ; X point
(- (cadr point2)(cadr point1)) ; Y point
(- (caddr point2)(caddr point1)) ; Z point
)
)
; divide the relative vector by the distance to find the normal vector
(setq normalvectz
(mapcar '(lambda (x)
(/ x distp1p2); divide by distance for each
)
relvectz
) ; end mapcar
)
; create a list of the X Y & Z vectors
(list normalvectx normalvecty normalvectz)
); end DEFUN FINDNORMALVECTORS


ribarm

  • Gator
  • Posts: 3225
  • Marko Ribar, architect
Re: Find normal vectors
« Reply #1 on: October 16, 2012, 05:51:18 AM »
Marko Ribar, d.i.a. (graduated engineer of architecture)

:)

M.R. on Youtube

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Find normal vectors
« Reply #2 on: October 16, 2012, 06:29:36 AM »
Code - Auto/Visual Lisp: [Select]
  1. (defun normalvectors ( p1 p2 / z )
  2.     (setq z (mapcar '- p2 p1))
  3.     (mapcar '(lambda ( v ) (trans v z 0 t))
  4.        '(
  5.             (1.0 0.0 0.0)
  6.             (0.0 1.0 0.0)
  7.             (0.0 0.0 1.0)
  8.         )
  9.     )
  10. )

Since two points cannot define a plane (as there are infinitely many vectors perpendicular to the two given points), AutoCAD uses the Arbitrary Axis Algorithm to determine the coordinate frame from a supplied normal vector; the trans function also implements this algorithm, facilitating the above function.

PrinceLISPalot

  • Newt
  • Posts: 34
  • perfectionist trapped inside the mind of an idiot.
Re: Find normal vectors
« Reply #3 on: October 16, 2012, 03:49:49 PM »
Thanks to you both for your help. Will give it another go.

PrinceLISPalot

  • Newt
  • Posts: 34
  • perfectionist trapped inside the mind of an idiot.
Re: Find normal vectors
« Reply #4 on: October 21, 2012, 06:52:01 PM »
After some work I have got something working that meets my requirements. I believe it is functionally correct, but I'm sure that it could be better optimised. I ended up utilising the normalvectors function provided by Lee, and Align3D by Highflybird. I modified Align3d so that I could call it as function, as well as a command. This is all included in the zip attachment.

It works by finding source and destination vectors for the profile to be aligned. At the source it uses a basepoint provided by the user, and the Normal of the profile to generate 3 vectors (via normalvectors). At the destination it uses the first two points along the selected path, to again generate 3 vectors. This is then fed into Align3D to do the alignment.

I think what I should actually be doing is using the basepoint and profile normal at the source, and at the destination use the path start point and a vector representing the tangent direction of the path (I assume that I may be able to get this by using vlax-curve-getFirstDeriv or some other vlax-curve function?). This would provide a more direct way to transform the profile into postion, and allow alignment to any path (my function doesn't currently work correctly on ARCs).

It works differently to the official SWEEP in that it doesn't matter what the alignment the profile is in, it still works the same. It's also been written to work with multiple, rather than single paths. Attach some screengrabs of it in action in AutoCAD, and BricsCAD. It is very quick in BricsCAD :lol:

I didn't look at providing SWEEPs default option which uses the centroid of the profile instead of a basepoint, that's a whole other mathematical adventure. One approach to this may be to convert the profile to a region first, then hopefully extract its centroid from its properties.

I also think that you could use a modified form to align a profile to a face similar to Lees C:MAP
http://www.theswamp.org/index.php?topic=42767.0. Except Source would use Centroid + Normal, Destination would use Dynamic UCS + selected point.

Appreciated the first class help, many thanks.

Code - Auto/Visual Lisp: [Select]
  1. ;------------------------------------------------------------------------------
  2. ; Copyright (C) 2012 CAD Concepts Limited.
  3. ;
  4. ; MYSWEEP
  5. ;
  6. ; Apart from exceptions (listed below) This work by CAD Concepts Ltd is
  7. ; licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
  8. ; http://creativecommons.org/licenses/by-sa/3.0/nz/deed.en
  9. ; For options available to you under this license.
  10. ;
  11. ; EXCEPTIONS
  12. ; The following are not the work of CAD Concepts Limited. And are subject
  13. ; to the copyright conditions of the respective Authors.
  14. ;
  15. ; Function                              Author
  16. ; CCL:NORMALVECTORS             Lee Mac, http://www.lee-mac.com
  17. ; Align3d.lsp                   Highflybird, http://www.theswamp.org/index.php?action=profile;u=2346
  18. ;
  19. ;
  20. ; This software is provided "as is". No liability is taken of
  21. ; FITNESS FOR ANY PARTICULAR PURPOSE.
  22.  
  23. ;------------------------------------------------------------------------------
  24. ; File          : MYSWEEP.lsp
  25. ; Author        : Jason Bourhill
  26. ; Web                   : http://www.cadconcepts.co.nz
  27. ; Date          : 21/October/2012
  28. ; CAD Ver(s)    : Tested on BricsCAD V12 & V13, AutoCAD 2012
  29. ;
  30. ; PURPOSE:
  31. ; Extrudes selected profile(s) along a path(s). Done to mimic the SWEEP command
  32. ; which is currently not available in BricsCAD
  33. ;                                
  34. ;
  35. ; USAGE:
  36. ; To load type (load "MYSWEEP.LSP") from the command line or drag and drop the
  37. ; file onto your drawing using explorer. To run the routine type MYSWEEP at the
  38. ; command line. You will be prompted to select polyline profiles to extrude.
  39. ; You will then be asked to pick a base point (alignment to the path(s) will be
  40. ; relative to the selected point), you are then asked to select path(s) to
  41. ; extrude the profile along.
  42. ;
  43. ; REQUIRES:
  44. ; Load these routines if the are not loaded already
  45. (if (not CCL:GSVAR)(load "ccl-gsvar.lsp")) ; CCL:GSVARS ; defined in on_doc_load / acaddoc.lsp
  46. (if (not align3d) (load "align3d.lsp")) ; ALIGN3D ; defined in align3d.lsp
  47.  
  48. ;------------------------------------------------------------------------------
  49. ; Rev no   : A
  50. ; Reason   : First release
  51. ; Rev Date : 21/October/2012
  52. ; Rev by   : Jason Bourhill
  53. ; Email    : jason@cadconcepts.co.nz
  54. ;
  55. ; Description:
  56. ; First release.
  57. ;------------------------------------------------------------------------------
  58.  
  59. ; CCL:DUPLICATE
  60. ; Create a duplicate copy of the given entity
  61. ; returns the entity name of the new object
  62. (defun CCL:DUPLICATE (ent / obj copyobj)
  63.  (setq obj (vlax-ename->vla-object ent)) ; retrieve the object
  64.  (setq copyobj (vla-copy obj)) ; create a copy
  65.  (vlax-vla-object->ename copyobj); return the entity name of the new object
  66. ); end DEFUN CCL:DUPLICATE
  67.  
  68. ; CCL:SUMV
  69. ; returns the sum of two vectors
  70. ; e.g. use to combine a base point with a relative direction vector to find target point
  71. (defun CCL:SUMV (p1 p2)
  72.  (mapcar '+ p1 p2)
  73. )
  74.  
  75. ; CCL:GETPOINTS
  76. ; use to get points relative to a basepoint
  77. (defun CCL:GETPOINTS (basepoint dirvecs)
  78.    (mapcar '(lambda ( v ) (CCL:SUMV basepoint v)) ; call to calc point for each direction vector
  79.       dirvecs
  80.    )
  81. )
  82.  
  83. ; CCL:NORMALVECTORS
  84. ; returns a list of 3 Normal Vectors representing the
  85. ; X, Y, and Z direction relative to 2 given points using AutoDESKs Arbitrary Axis Algorithm
  86. ;
  87. ; Source Lee Mac
  88. ; http://www.theswamp.org/index.php?topic=43000.0
  89. ; see also
  90. ; Detail on AutoCAD Method http://docs.autodesk.com/ACD/2011/ENU/filesDXF/WS1a9193826455f5ff18cb41610ec0a2e719-793d.htm
  91. ; http://en.wikipedia.org/wiki/Cross_product
  92. ; http://www.wolframalpha.com/input/?i=Cross+product
  93. ; http://mathworld.wolfram.com/CrossProduct.html
  94.  
  95. (defun CCL:NORMALVECTORS ( p1 p2 / z ) 
  96.    (setq z (mapcar '- p2 p1)) ; find the relative direction vector from p2 to p1. This identifies the Z axis direction
  97.    (mapcar '(lambda ( v ) (trans v z 0 T)) ; call trans to calc cross product for each direction
  98.       '(
  99.            (1.0 0.0 0.0) ; X Normal
  100.            (0.0 1.0 0.0) ; Y Normal
  101.            (0.0 0.0 1.0) ; Z Normal
  102.        )
  103.    )
  104. ); end DEFUN CCL:NORMALVECTORS
  105.  
  106. ; CCL:LISTCOORDS
  107. ; Returns a list of 3D coordinates for a given entity
  108. ; Polylines returns a list with a point for each coordinate
  109. ; Lightweight Polylines uses the elevation for Z coordinate
  110. ; Closed polylines will have the start point appended to the end of the list
  111. ; all other objects checks whether a start and end point is available and returns a list of these points if available
  112.  
  113. (defun CCL:LISTCOORDS (ent / obj objtype objcoords coorList coordtype elevation numcoords num xval yval zval pointlst)
  114.         ; Using visual lisp. Make sure the visual lisp extensions are available
  115.         (vl-load-com) ; this isn't required by BricsCAD
  116.         (setq obj (vlax-ename->vla-object ent)) ; convert to vl object
  117.         (setq objType (vlax-get-property obj 'ObjectName)) ; find what type of object it is
  118.         (cond
  119.                 ; CONDITION 1
  120.                 ; Object is a Lightweight, 2d, or 3d Polyline
  121.                 ; return a list of the coordinates
  122.                 ((or (= objtype "AcDbPolyline") (= objtype "AcDb2dPolyline") (= objtype "AcDb3dPolyline"))
  123.                         (setq objcoords (vla-get-coordinates obj)) ; retrieve the Coordinates
  124.                         ; (setq objcoords (vlax-get-property obj 'coordinates); Alternative method to retrieve these properties
  125.                         (setq coordList (vlax-safearray->list (vlax-variant-value objcoords))) ; convert coordinates from an array to a list
  126.                         (if     (= objtype "AcDbPolyline") ; If the object is a Lightweight Polyline
  127.                                         (setq
  128.                                                 coordtype 2 ; THEN set number of coordinates to 2
  129.                                                 elevation (vlax-get-property obj 'Elevation) ; and find the Polylines Elevation
  130.                                         )
  131.                                         (setq coordtype 3) ; ELSE set number of coordinates to 3
  132.                         ); end if
  133.                         (setq numcoords (/ (length CoordList) coordtype)) ; calculate the number off coordinates in the list
  134.                         ;(princ (strcat "\nNumber coords " (itoa numcoords) "\n"))
  135.                         (setq num 0); zero counter
  136.                         (repeat numcoords
  137.                                 (setq xval (nth num coordlist)) ; Get the X value
  138.                                 (setq num (1+ num)); Iterate counter to the Y value
  139.                                 (setq yval (nth num coordlist)) ; Get the Y value
  140.                                 (if (= coordtype 2) ; if this is a 2D coordinate list
  141.                                         (setq zval elevation) ; then set the Z to the given elevation
  142.                                         (setq num (1+ num) ; otherwise iterate the counter and get the Z from the coordinate list
  143.                                                   zval (nth num coordlist)
  144.                                         )
  145.                                 )
  146.                                 (setq pointlst (cons (list xval yval zval) pointlst)) ; THEN add it to our points list
  147.                         (setq num (1+ num)); Iterate counter to the next X Value
  148.                         ); end Repeat
  149.                         (setq pointlst (reverse pointlst)) ; reverse our list
  150.                         (if (= ':VLAX-TRUE (vlax-get-property obj 'Closed)) ; If the polyline is closed
  151.                                 (setq pointlst (append pointlst (list (car pointlst)))) ; Then add the start point to the end of the list
  152.                         ); end if
  153.                 ) ; end CONDITION 1
  154.                 ; CONDITION 2
  155.                 ; For all other objects
  156.                 ; return a list of the start & end point if available
  157.                 (T
  158.                         (if (and (vlax-property-available-p obj 'StartPoint)(vlax-property-available-p obj 'EndPoint)) ; check if the object has a start point & end point
  159.                                 (setq pointlst
  160.                                         (mapcar
  161.                                                 (function
  162.                                                         (lambda (objPoint)
  163.                                                                 (vlax-safearray->list (vlax-variant-value objPoint)) ; convert the point from an array to a list
  164.                                                         )
  165.                                                 )
  166.                                                 (list (vlax-get-property obj 'StartPoint)(vlax-get-property obj 'EndPoint)) ; create a list of the two points
  167.                                         ) ;end mapcar
  168.                                 )
  169.                         ); end if
  170.                 ); end CONDITION 2
  171.         ); end COND Statement
  172. ; return the points list
  173. pointlst
  174. ); end CCL:LISTCOORDS
  175.  
  176. ; CCL:SWEEPPROFILE
  177. ; Extrudes a profile along a path(s) relative to a basepoint
  178. ; will work with multiple profiles & paths
  179. (defun CCL:SWEEPPROFILE (profiles basepoint paths
  180.                                                 /
  181.                                                 objAcad objDoc
  182.                                                 1stSourcePt prfnum prfent pathnum
  183.                                                 ThisPrf ThisPrfProps ThisPrfNormal 2ndSourcePt SourceNormals SourcePts
  184.                                                 pathent pathpoints 1stDestPt 2ndDestPt DestNormals DestPts
  185.                                         )
  186.         (setq objAcad (vlax-get-acad-object)) ; retrieve application Object
  187.         (setq objDoc (vla-get-ActiveDocument objAcad)); retrieve active document
  188.         (vla-StartUndoMark objDoc) ; Start Undo Mark
  189.         (setq 1stSourcePt (trans basepoint 1 0)); translate the basepoint from the current UCS to World
  190.         (setq prfnum 0) ; zero counter
  191.         (repeat (sslength profiles) ; repeat for each profile in the selection set
  192.           (setq prfent (ssname profiles prfnum)) ; retrieve the entity name
  193.           (setq pathnum 0) ; zero counter
  194.                 (repeat (sslength paths) ; repeat for each path in the selection set
  195.                         ; Set source details
  196.                         (setq ThisPrf (CCL:DUPLICATE prfent)) ; create a copy of our object
  197.                         (setq ThisPrfProps (entget ThisPrf)) ; retrieve the properties of the copied object
  198.                         (setq ThisPrfNormal (cdr (assoc 210 ThisPrfProps))) ; retrieve the normal direction vector
  199.                         (setq 2ndSourcePt (CCL:SUMV 1stSourcePt ThisPrfNormal)) ; retrieve point normal to our 1stSourcePt
  200.                         (setq SourceNormals (CCL:NORMALVECTORS 1stSourcePt 2ndSourcePt)) ; Find source normal vectors
  201.                         (setq SourcePts (CCL:GETPOINTS 1stSourcePt SourceNormals)) ; find source points
  202.                         ; Set destination details
  203.                         (setq pathent (ssname paths pathnum)) ; retrieve the entity name
  204.                         (setq pathpoints (CCL:LISTCOORDS pathent)) ; retrieve path points
  205.                         (setq 1stDestPt (car pathpoints)) ; get start point on path
  206.                         (setq 2ndDestPt (cadr pathpoints)) ; get second point on path
  207.                         (setq DestNormals (CCL:NORMALVECTORS 1stDestPt 2ndDestPt)) ; Find destination normal vectors
  208.                         (setq DestPts (CCL:GETPOINTS 1stDestPt DestNormals)) ; find destination points
  209.                         ; Call Align3d to align profile to the end of our path
  210.                         ; This works, but the profile rotation doesn't match requirements
  211.                         ;(ALIGN3D (ssadd ThisPrf) 1stSourcePt 1stDestPt 2ndSourcePt 2ndDestPt nil nil nil)
  212.                         ; Align profile to the end of our path based on Normal vectors
  213.                         (ALIGN3D (ssadd ThisPrf) (car SourcePts) (car DestPts) (cadr SourcePts) (cadr DestPts) (caddr SourcePts) (caddr DestPts) nil)
  214.                         ; Extrude profile along the path
  215.                         (command "._EXTRUDE" ThisPrf "" "_PATH" pathent)
  216.                         (setq pathnum (1+ pathnum)) ; Iterate counter to next object in selection set
  217.                 );end path repeat
  218.           (setq prfnum (1+ prfnum)) ; Iterate counter to next object in selection set
  219.         ) ;end profile repeat
  220.   (vla-EndUndoMark objDoc) ; End Undo Mark
  221.   (vlax-release-object objDoc) ; release document object
  222.   (vlax-release-object objAcad) ; release application object
  223.  (prin1) ;make a quiet exit
  224. ); end DEFUN CCL:SWEEPPROFILE
  225.  
  226. ; Extrude a multiple beam profiles along a selected path to create 3d Solid Beams
  227. (defun C:MYSWEEP ( / SSetProfiles basepoint SSetPaths)
  228.  (CCL:GSVAR '(cmdecho delobj)) ; Save environment variables
  229.         (setvar "CMDECHO" 0) ; turn off command echo
  230.         (setvar "DELOBJ" 1) ; Delete profile entities on extrusion
  231.         (princ "\nSelect Profile(s) to use") ; Provide prompt
  232.                 (setq SSetProfiles (ssget)) ; Select profiles
  233.                 (if (= (type SSetProfiles) 'PICKSET) ; if user has made a valid selection
  234.                         (progn
  235.                                 (setq basepoint (getpoint "\nSelect a base point on the profile:")) ; Request user to pick a reference point
  236.                                 (if (= (type basepoint) 'LIST) ; if user has made a valid selection
  237.                                         (progn ; Then
  238.                                                 (princ "\nSelect the path(s) to sweep:")
  239.                                                 (setq SSetPaths (ssget '((-4 . "<or")(0 . "LINE")(0 . "ARC")(0 . "POLYLINE")(0 . "LWPOLYLINE")(-4 . "or>")))) ; Allow selection of only Line & Polyline objects
  240.                                                 (if (= (type SSetPaths) 'PICKSET) ; if user has made a valid selection
  241.                                                         (progn
  242.                                                                 (CCL:SWEEPPROFILE SSetProfiles basepoint SSetPaths) ; extrude each profile along each of the paths using the given basepoint.
  243.                                                         ); end progn
  244.                                                         (princ "\nNo profile paths selected")
  245.                                                 ); end if
  246.                                         ); end progn
  247.                                         (princ "\nNo Base Point selected")
  248.                                 ); end if                      
  249.                         )
  250.                         (princ "\nNo profile selected")
  251.                 ); end if
  252. (CCL:GSVAR T) ; Reset environment variables
  253. (prin1); make a quiet exit                     
  254. ); end DEFUN C:MYSWEEP
  255.  
  256. (princ "\nCommand is: MYSWEEP")

PrinceLISPalot

  • Newt
  • Posts: 34
  • perfectionist trapped inside the mind of an idiot.
Re: Find normal vectors
« Reply #5 on: October 21, 2012, 11:27:00 PM »
Just to follow up on my previous post. I've now read AutoCAD Help for SWEEP. And under Alignment it states:

Note: If the profile is not perpendicular (normal) to the tangent of the start point of the path, then the profile automatically aligns. Enter No at the alignment prompt to prevent this.

So I should be using tangent for direction of the path. I guess I should of also read the instructions first :-)
« Last Edit: October 21, 2012, 11:39:09 PM by BricscadBoy »

PrinceLISPalot

  • Newt
  • Posts: 34
  • perfectionist trapped inside the mind of an idiot.
Re: Find normal vectors
« Reply #6 on: October 22, 2012, 08:01:57 PM »
I've updated my sweep function (see attached) to use the tangent direction of the path. So it now works for any path that the EXTRUDE command will accept. ARCs, CIRCLES, ELLIPSE and the like.

found this post on VLAX-CURVE functions most useful.
http://www.theswamp.org/index.php?topic=467.0