Announcements

Community notifications may experience intermittent interruptions between 10–12 November during scheduled maintenance. We appreciate your patience.

Blocks along spline - How do you use already selected block?

Blocks along spline - How do you use already selected block?

leeminardi
Mentor Mentor
472 Views
8 Replies
Message 1 of 9

Blocks along spline - How do you use already selected block?

leeminardi
Mentor
Mentor

I feel the sweep command does not give the user much control in twist so I've written a couple of LISP programs to help a user position cross sections along the spline.   The sections are defined as blocks which after positioning and rotating can be exploded to be used with the LOFT command.

The first program "BlocksOnSpline" let's the user position multiple blocks that define the section along a spline.  The orientation of the sections are controlled by the first and second derivative functions.  The user may also specify the start and end twist angles.  I am satisfied with this program.

 

The second program "twist" lets the user select a block which is then rotated about its local z axis such that the block's x direction is parallel to the XY plane.  Nentsel is used to extract the needed block data. Later in the program I want to use rotate3d to rotate the block about the block's z axis.  I've tried using "last" and "p" and even the block's entity name to select the block for rotation but could not get the block to rotate.  I finally resorted to using the block's base point location (p0).  This usually works well but if the viewing angle is such that another object is visually in line with the select point it may get selected instead of the block that was orignally selected via nentsel.

What is the best way to reference the block orginally selected via netsel for use in the rotate3d command?

 

The attached drawing has a block named "section5x1".  It's base point is at the center point.  The other point is used to distinguish rotation of an additional 180°. Here are the results of using "blocksonspline"

leeminardi_0-1729176497133.png

 

In the following image the first section has been succesfully rotated.  However, if the third section is picked the rotate3d will be done to the second section since part of its geometry is in line with the pick point (the base point) of the third section resulting in the wrong blocck being rotated.

leeminardi_1-1729176830789.png

(defun C:BlocksOnSpline (/ nsec blockname twist tottwist path inc n osm par der1
	 der2 p p1 p2 vy vz vx p3)
;  Adds blocks along a spline.  The user specifies the number of blocks and
; the total amount of twist over the length of the spline.   The Z axis of the block
; is tangent to the spline.  
; LRM 5/1/2023 revised 10/17/2024  
(command "ucs" "w")
(setvar 'osmode 0)
(setq nsec (getint "\nEnter number of sections: "))
(setq nsec (- nsec 1))  
(setq blockname (getstring "\nEnter block name: "))
(setq twist (getreal "\nEnter start twist angle: "))
(setq tottwist (getreal "\nENter total twist start to end: "))  
(setq
  path (car (entsel))
  inc  (/ (vlax-curve-getEndParam path) nsec)
  n    0
  osm  (getvar 'osmode)
)
(setvar "cmdecho" 0)
(repeat	(+ nsec 1)
  (setq par (* inc n))
  (setq	der1 (vlax-curve-getfirstDeriv path par)
	der2 (vlax-curve-getSecondDeriv path par)
	p    (vlax-curve-getPointAtParam path par)
  )
  
(if (> (distance '(0 0 0) der2) 1e-10)
  (setq vx (cross (cross der1 der2) der1))
  (setq vx (cross '(0 0 1) der1))
)
(setq vz der1)
(setq p1 (mapcar '+ p vx))
(setq p2 (mapcar '+ p vz))

  (command "-insert" blockname '(0 0 0) 1 1 0)
 (command "_align" "last" "" '(0 0 0) p '(-1 0  0) p1 '(0 0 -1) p2 "n")
 ; (command "_align" "last" "" '(0 0 0) '(1 0 0)  '(0 0 l) p p1 p2 "n")
  (if (>
	(abs twist) 0.00001)
   (command "rotate3d" "last" "" "2" p p2 twist)
  )
  (setq twist (- twist (/ tottwist nsec)))
  (setq n (1+ n))
)					; end repeat
(setvar 'osmode osm)
(setvar "cmdecho" 1)
(princ)
)

  ;;; Compute the cross product of 2 vectors a and b
(defun cross (a b / crs)
  (setq	crs (list
	      (- (* (nth 1 a) (nth 2 b))
		 (* (nth 1 b) (nth 2 a))
	      )
	      (- (* (nth 0 b) (nth 2 a))
		 (* (nth 0 a) (nth 2 b))
	      )
	      (- (* (nth 0 a) (nth 1 b))
		 (* (nth 0 b) (nth 1 a))
	      )
	    )				;end list
  )					;end setq c
)					;end cross
(defun unitV (v / d)
  (setq	d (distance '(0 0 0) v)
	d (mapcar '/ v (list d d d))
  )
)
(defun c:twist (/ blockdata entName blockmatrix blockx blockz p0 p1 alpha alphadeg h d theta thetadeg)
; calculates the angle to rotate a block about its z axis to make the block's x axis
; parallel to the XY plane. It then rotates the block about its z axis.
; L. Minardi  10/6/2024  
(setq blockdata	  (nentsel "\nSelect block:")
      blockmatrix (nth 2 blockdata)
      blockx	  (nth 0 blockmatrix) ; block x axis vector
      blockz	  (nth 2 blockmatrix) ; block z axis vector
      p0	  (nth 3 blockmatrix) ; block position
      p1	  (mapcar '+ p0 (nth 2 blockmatrix)) ; point on block z axis
      alpha	  (acos (dot '(0 0 1) blockz)) 
      alphadeg	  (- 90 (/ (* alpha 180.) pi)) ; angle between world z and block z
      h		  (sin alpha)
      d		  (caddr blockx)
      theta	  (* -1 (asin (/ d h)))  ; rotation angle about z (radians)
      thetadeg	  (/ (* theta 180.) pi)
)
(princ "\nTwist angle is: ")
(princ thetadeg)
(if (equal theta 0.0 0.001)
  (princ
    "\nX axis of block is already parallel to the XY plane!"
  )
  (progn
 	  (command "_rotate3d" "_non" p0 "" "2" "_non" p0 "_non" p1 thetadeg) 
    (princ "\nDone!")
  )
)
(princ)
)

(defun unitV (v / d)
  (setq	d (distance '(0 0 0) v)
	d (mapcar '/ v (list d d d))
  )
)

;; ArcCosine  -  Lee Mac
;; Args: -1 <= x <= 1

(defun acos ( x )
    (if (<= -1.0 x 1.0)
        (atan (sqrt (- 1.0 (* x x))) x)
    )
)
					; dot product of vectors a and b
(defun dot (a b / dd)
  (setq dd (mapcar '* a b))
  (setq dd (+ (nth 0 dd) (nth 1 dd) (nth 2 dd)))
)					;end of dot

;; ArcSine  -  Lee Mac
;; Args: -1 <= x <= 1

(defun asin ( x )
    (if (<= -1.0 x 1.0)
        (atan x (sqrt (- 1.0 (* x x))))
    )
)

 

 

 

lee.minardi
0 Likes
473 Views
8 Replies
Replies (8)
Message 2 of 9

john.uhden
Mentor
Mentor

@leeminardi ,

Is part of what you are saying that the entity or object name of the first pick isn't retained properly?

This an embarrassing supposition, considering your professionalism, but might you be overwiting the previous symbol value?

I find myself almost always using particular names, like e0, e1, e2, etc.

John F. Uhden

0 Likes
Message 3 of 9

leeminardi
Mentor
Mentor

@john.uhden 

 

How are you and what are you doing looking at year old posts?  DO you have a need for "twist"? 

 

YOu know I'm a vector guy and the nuances of user interface issues of LISP are often beynd me.

 

You ask "Is part of what you are saying that the entity or object name of the first pick isn't retained properly?"  

 

Yes!

 

I tried the following.

(setq entname (car blockdata))

When I use it as follows:

 	  (command "_rotate3d"  entname "" "2" "_non" p0 "_non" p1 thetadeg) 

 

I get the following:

Select objects: <Bad Entity name: 9C15B980>

 

Altough the value of entname is:   <Entity name: 1a69c15b980>

 

What gives?

 

;(defun c:twist (/ blockdata entName blockmatrix blockx blockz p0 p1 alpha alphadeg h d theta thetadeg)
(defun c:twist (/ )
; calculates the angle to rotate a block about its z axis to make the block's x axis
; parallel to the XY plane. It then rotates the block about its z axis.
; L. Minardi  10/6/2024  
(setq blockdata	  (nentsel "\nSelect block:")
      blockmatrix (nth 2 blockdata)
      blockx	  (nth 0 blockmatrix) ; block x axis vector
      blockz	  (nth 2 blockmatrix) ; block z axis vector
      p0	  (nth 3 blockmatrix) ; block position
      p1	  (mapcar '+ p0 (nth 2 blockmatrix)) ; point on block z axis
      alpha	  (acos (dot '(0 0 1) blockz)) 
      alphadeg	  (- 90 (/ (* alpha 180.) pi)) ; angle between world z and block z
      h		  (sin alpha)
      d		  (caddr blockx)
      theta	  (* -1 (asin (/ d h)))  ; rotation angle about z (radians)
      thetadeg	  (/ (* theta 180.) pi)
      entname (car blockdata)
)
(princ "\nTwist angle is: ")
(princ thetadeg)
(if (equal theta 0.0 0.001)
  (princ
    "\nX axis of block is already parallel to the XY plane!"
  )
  (progn
 	  ;(command "_rotate3d" "_non" p0 "" "2" "_non" p0 "_non" p1 thetadeg) 
 	  (command "_rotate3d"  entname "" "2" "_non" p0 "_non" p1 thetadeg) 
    (princ "\nDone!")
  )
)
(princ)
)

(defun unitV (v / d)
  (setq	d (distance '(0 0 0) v)
	d (mapcar '/ v (list d d d))
  )
)

;; ArcCosine  -  Lee Mac
;; Args: -1 <= x <= 1

(defun acos ( x )
    (if (<= -1.0 x 1.0)
        (atan (sqrt (- 1.0 (* x x))) x)
    )
)
					; dot product of vectors a and b
(defun dot (a b / dd)
  (setq dd (mapcar '* a b))
  (setq dd (+ (nth 0 dd) (nth 1 dd) (nth 2 dd)))
)					;end of dot

;; ArcSine  -  Lee Mac
;; Args: -1 <= x <= 1

(defun asin ( x )
    (if (<= -1.0 x 1.0)
        (atan x (sqrt (- 1.0 (* x x))))
    )
)
lee.minardi
0 Likes
Message 4 of 9

vladimir_michl
Advisor
Advisor

Maybe another approach to the original task (and may simplify the vector geometry computations) - you can place aligned blocks along a spline also with the ARRAY command. And then just do the twists. See the option 4 at:

https://www.cadforum.cz/en/display-complex-linetypes-on-3d-curves-in-plain-autocad-tip14665

 

Vladimir Michl, www.arkance.world  -  www.cadforum.cz 

0 Likes
Message 5 of 9

komondormrex
Mentor
Mentor

@leeminardi 

hey there,

check if the following could help.

(defun c:BlocksOnSpline ()
	(setq nsec (getint "\nEnter number of sections: "))
	(setq blockname (getstring "\nEnter block name: "))
	(setq twist (/ (* pi (getreal "\nEnter start twist angle: ")) 180))
	(setq tottwist (/ (* pi (getreal "\nENter total twist start to end: ")) 180))  
	(setq path (car (entsel "\nPick control path: "))
	      inc  (/ (vlax-curve-getEndParam path) (1- nsec))
	      twist_inc (/ (- tottwist twist) (1- nsec))  
	      n -1
	)
	(repeat	nsec
	  (setq par (* inc (setq n (1+ n))))
	  (setq	der1 (vlax-curve-getfirstDeriv path par)
		p    (vlax-curve-getPointAtParam path par)
	  )
	  (setq insert (vla-insertblock (vla-get-block (vla-get-activelayout (vla-get-activedocument (vlax-get-acad-object))))
			                (vlax-3d-point 0 0 0)
			 		blockname
			 		1 1 1
			 		(* twist_inc n)
		       )
	  )
	  (vla-put-normal insert (vlax-3d-point der1))
	  (vla-move insert (vla-get-insertionpoint insert) (vlax-3d-point p))
	)
)

 

0 Likes
Message 6 of 9

leeminardi
Mentor
Mentor

@komondormrex Thanks for looking at my code.  Unfortunately, you looked at the wrong program.  As I noted, I am satisfied with my  Blocksonspline code.   My primary issue is with the program Twist (although I should reassess Blocksonspline as it yields different results than your version) . 

 

The following line of code in Twist has the problem.

(command "_rotate3d" "_non" p0 "" "2" "_non" p0 "_non" p1 thetadeg) 

po is the point that is used to select the block.  The problem arises when p0 overlaps some other geometry thus causing the wrong block to be selected.  I woud like to use the object name instead of using a point for selecting it.  Note that in the following example if the base point of the thrid section is used for p0 then the second section may be selected by mistake as it has part of its geometry in line with p0.  I could not find a way to use the object name in the rotate3d command.

leeminardi_0-1759711701092.png

 

 

lee.minardi
0 Likes
Message 7 of 9

komondormrex
Mentor
Mentor

@leeminardi,

do i understand you correctly, with the blockonspline you are positioning blocks along a spline, blocks are twisted. and with the twist you are untwisting those blocks? what is the general idea of that? 

0 Likes
Message 8 of 9

leeminardi
Mentor
Mentor

@komondormrex 

As I mention in my first message:

          I feel the sweep command does not give the user much control in twist so I've written a couple of LISP programs to help a user position cross sections along the spline.

 

The first goal was to write a program that would position blocks, that define a cross section, along a spline such that the Z axis of the block was tangent to the spline. Once satifactorily positioned and oriented the blocks could be exploded and their polyline components could then by use with the LOFT command to create a better swept shape. Using blocks alos enabled the user to easily rotate indivdiual cross section about the block's z axis providing further control.

 

A user requested a second goal.  The ability to select one of the blocks and rotate it about its z axis such that the resulting orientation would have the block's X axis parallel to the World XY plane.  This is what the program Twist does except that it can run into problems because it selects the the block by using a point (p0) for selection which might select something other than the desired block if there is other geometry located at p0.    Bottom line?  What is the command syntax for selecting the block for use in the rotae3d command?

 

How should the following be modified to reference the block object name instead of using a point, i.e., p0?

 	  (command "_rotate3d" "_non" p0 "" "2" "_non" p0 "_non" p1 thetadeg) 

I tried the following without success.

(setq entname (car blockdata))
(command "_rotate3d"  entname "" "2" "_non" p0 "_non" p1 thetadeg) 

 

lee.minardi
0 Likes
Message 9 of 9

komondormrex
Mentor
Mentor

@leeminardi,

clear enough.

 


@leeminardi wrote:

What is the command syntax for selecting the block for use in the rotate3d command?

it is a pandora box)  i've tried it (and vla* also) quickly with no success(  Kent, what do you think?

you may not like the following but it does what a user wants via block picking.

(defun c:untwist (/ b p n)
  (setq b (vlax-ename->vla-object (car (entsel "\nPick block to untwist: ")))
	n (vla-get-normal b)
	p (vla-get-insertionpoint b)
  )
  (vla-put-normal b (vlax-3d-point 1 0 0))
  (vla-put-rotation b 0)
  (vla-put-normal b n)
  (vla-put-insertionpoint b p)
)

 

0 Likes