Author Topic: Code to auto-update blocks. . . help?  (Read 10319 times)

0 Members and 1 Guest are viewing this topic.

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #15 on: July 19, 2005, 01:07:11 PM »
Daron
If I understand ssget correctly, it allows you to click on subentities but it actually only adds the main entity (i.e. block reference...) to the selection set.  I'm sure you knew this, but I don't see how it is useful in selecting a single polyline as I am trying to do.

Edit - I'm referring to ssget with the :N argument

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #16 on: July 19, 2005, 03:16:40 PM »
CAB-
OK, I've looked at some of your code.  So far I understand how Save_Sys, Restore_Sys, and anno (error function and routine exit) work.    (I wish that meant I could write them off the top of my head!).

I'm looking at get_poly_pt:
Code: [Select]
;;  function to get a point along a polyline
;;  returns a list of (ent_name, point)
(defun get_poly_pt (/ loop ent)
  (setq loop t)
  (while loop
    (setq ent (entsel "\nPick a point on the pline to tag distance."))
    (if (and ent
             (= (cdr (assoc 0 (entget (car ent)))) "POLYLINE")
             ;;  may want to restrict to a specific layer name
        )
        (setq ent (list (car ent)
                        (vlax-curve-getClosestPointTo (car ent) (cadr ent)))
              loop nil) ; exit loop
        ;;  else nothing valid selected
        (setq loop nil) ; exit loop
   
    )
  )
  ent
)


This is how I'm reading the loop:
- display message and let user pick point (store object selected as ENT)
- IF an object was selected and it is a polyline
THEN
-->   store the entity name and user-clicked point in ENT
-->   exit loop
- ELSE selection is invalid --> exit loop
- return ENT

I would rather keep looping until the user selected a polyline by replacing
Code: [Select]
(setq loop nil) ; exit loop
with
Code: [Select]
(prompt "  Selection is not a polyline.")

The prompt could even display what the object-type selected was (would help if they were clicking on a block, spline etc.)  Am I looking at this accurately?

daron

  • Guest
Code to auto-update blocks. . . help?
« Reply #17 on: July 19, 2005, 03:22:11 PM »
Thanks Will. I knew it could be done. I just forgot how. tcdan, if you use the ":N" feature it will allow you to select a polyline within a block, if you or another user should feel so inclined. It's also not a bad safeguard for that future use if there is no need for one now. Also, if you use it how Will has correctly shown, it will allow you to feel like you're using nentsel, yet will allow you to select multiple object if you should hit non-graphical areas.

Just looking at the help file and it might HELP to add this:
(ssnamex (ssget ":N")) Read up on ssnamex.That's what will return the nested objects.

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #18 on: July 19, 2005, 03:52:04 PM »
OK, just looked at the get_dist subroutine and changed it a little:

Code: [Select]
(defun get_dist (lst / obj)
  (setq obj (vlax-ename->vla-object (car lst)))
  (vlax-curve-getdistatpoint obj (cadr lst))
)


I think this:
Code: [Select]
(vlax-curve-getdistatparam obj (vlax-curve-getendparam obj))
)

will always give you the total length of the object since vlax-curve-getdistatparam gives the length of the object from the start parameter to whatever parameter you give it (in this case the end parameter).

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Code to auto-update blocks. . . help?
« Reply #19 on: July 19, 2005, 05:51:07 PM »
Try this one
Code: [Select]
;;  function to get a point along a polyline
;;  returns a list of (ent_name, point) or
;;  nil if user pressed ENTER
(defun get_poly_pt (/ loop ent)
  (setvar "errno" 0) ; must pre set the errno to 0
  (setq loop t)
  (while loop
    (cond
      ((and (null
              (setq ent (entsel "\nPick a point on the pline to tag distance."))
            )
            (= (getvar "errno") 7) ; missed
       )
       (prompt "\nMissed, Try again.")

      )

      ((= (getvar "errno") 52) ; exit if user pressed ENTER
       (setq loop nil) ; exit with nil
      )
      ((null ent)
       (prompt "\nUnknown error, select ahgain.")
      )
      ((= (cdr (assoc 0 (entget (car ent)))) "LWPOLYLINE")
       (setq loop nil) ; exit with ent
      )
      (T
       (prompt
         (strcat "\nWrong object type: " (cdr (assoc 0 (entget (car ent)))))
       )
      )
    )
  )
  ent
)
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.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Code to auto-update blocks. . . help?
« Reply #20 on: July 19, 2005, 06:15:46 PM »
Dan I had to rework that function. That is because entsel does not return a point that is exactly on the entity.
Try this.
Code: [Select]
;;  function to return the distance of point from START
;;  
(defun get_dist (lst / obj)
  (setq obj (vlax-ename->vla-object (car lst)))
  (vlax-curve-getdistatparam obj
    (vlax-curve-getParamAtPoint obj
      (vlax-curve-getClosestPointTo obj (cadr lst))))
)
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.

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Code to auto-update blocks. . . help?
« Reply #21 on: July 19, 2005, 06:22:13 PM »
Quote from: tcdan
Daron
If I understand ssget correctly, it allows you to click on subentities but it actually only adds the main entity (i.e. block reference...) to the selection set.  I'm sure you knew this, but I don't see how it is useful in selecting a single polyline as I am trying to do.

Edit - I'm referring to ssget with the :N argument

You need to use the (ssnamex) function to get the nested selected object.

Here I select a circle that is in a block, that itself is in an xref:
Code: [Select]

_$ (setq ss (ssget ":S:N"))
<Selection set: 6e>

_$ (entget (ssname ss 0))
((-1 . <Entity name: 400bf588>) (0 . "INSERT") (330 . <Entity name: 400cacb8>) (5 . "11249") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "XRef") (100 . "AcDbBlockReference") (2 . "Sodut") (10 7.72704 -51.2706 0.0) (41 . 1.0) (42 . 1.0) (43 . 1.0) (50 . 2.85639e-005) (70 . 0) (71 . 0) (44 . 0.0) (45 . 0.0) (210 0.0 0.0 1.0))

_$ (setq ent (ssnamex ss 0))
((1 <Entity name: 40fb1e50> 0 (0 (1.7913e+006 269284.0 0.0)) (-0.00114256 40.0 0.0) (-40.0 -0.00114256 0.0) (0.0 0.0 40.0) (1.79129e+006 269283.0 0.0) <Entity name: 40fb1ed0> <Entity name: 400bf588>))
_$
_$ (entget (cadr (car ent))) ;;the actual circle
((-1 . <Entity name: 40fb1e50>) (0 . "CIRCLE") (330 . <Entity name: 40fb1e40>) (5 . "D1FA8D4E5DB16DA2") (100 . "AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDbCircle") (10 0.0 0.0 0.0) (40 . 0.0625) (210 0.0 0.0 1.0))
_$
_$ (entget (last (car ent))) ;; the xref object
((-1 . <Entity name: 400bf588>) (0 . "INSERT") (330 . <Entity name: 400cacb8>) (5 . "11249") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "XRef") (100 . "AcDbBlockReference") (2 . "Sodut") (10 7.72704 -51.2706 0.0) (41 . 1.0) (42 . 1.0) (43 . 1.0) (50 . 2.85639e-005) (70 . 0) (71 . 0) (44 . 0.0) (45 . 0.0) (210 0.0 0.0 1.0))

_$ (entget (car (cdr (reverse (car ent))))) ;;the block reference
((-1 . <Entity name: 40fb1ed0>) (0 . "INSERT") (330 . <Entity name: 40fc6e08>) (5 . "D1FA8D4E5DB16DB2") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "Sodut|SS") (100 . "AcDbBlockReference") (2 . "Sodut|SSMH1") (10 1.79129e+006 269283.0 0.0) (41 . 40.0) (42 . 40.0) (43 . 40.0) (50 . 1.5708) (70 . 0) (71 . 0) (44 . 0.0) (45 . 0.0) (210 0.0 0.0 1.0))

Soyou see, you can get the complete structure of that object from a single pick.

HTH,
Jeff

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Code to auto-update blocks. . . help?
« Reply #22 on: July 19, 2005, 06:26:43 PM »
CAB, why not just get the distance at the point, rather than get the param then the dist?
Code: [Select]

(defun get_dist (lst / obj)
  (setq obj (vlax-ename->vla-object (car lst)))
  (vlax-curve-getdistatpoint obj
    (vlax-curve-getClosestPointTo obj (cadr lst)))
)

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Code to auto-update blocks. . . help?
« Reply #23 on: July 19, 2005, 06:48:30 PM »
Yes that would be the best way. Too many choices & I get confused :)
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.

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #24 on: July 21, 2005, 12:56:38 PM »
Well, I've looked at reactors a bit - and they just plain intimidate me.  You have to not just attach one but multiple reactors to deal with the user starting and then finishing a command which modifies the block, you have to make sure having multiple blocks with reactors attached behave correctly (the reactors don't affect each other), and then you have to unload the reactor after you close CAD unless it is persistent (which I want it to be).  But it looks like reactors can easily crash your drawing, and, if I have say a couple hundred of these blocks all with reactors, I'm wondering if CAD's performance will suffer too.

So I think I'm going to skip the reactor portion and settle for a block that will set itself when you place it.  You have 2 options if you need to move the block:
1. Delete it and reinsert another one.
2. Manuualy run another routine which updates the block - perhaps a special 'move' routine or a routine which you can run once the block has been moved.

Thanks for your guys help thus far - it has been tremendous!  Helping me learn how to write code effectively and how different functions work. . . I find I learn much better from others as opposed to reading 'the book'.

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #25 on: July 29, 2005, 10:35:00 PM »
OK, I worked on the routine today and I think I've got code that's close to being finished (fingers crossed).  CAB I just rearranged your code a little so that it made sense to me and added the portion that updates the block attribute. . . I wasn't quite sure why you pute the error-handling and exit routine at the beginning of the entire function, so I moved it out of the main function.  I'd like to know if that was a bad idea.

Of course the code doesn't work. . . not sure why yet.  Well, here it is:

Code: [Select]
;;;  =================================================================
;;;---------------------------------------------------------------------;
;;; Anno.lsp      
;;;              
;;; Description: This function will input a block on a polyline.  This block
;;; has a text field which will display the distance along the polyline from
;;; the start of the polyline.  If the block is moved and placed on the polyline
;;; elsewhere, the text field will be automatically updated.

;;;        
;;; Arguments: (none)
;;;              

;;;          
;;; Usage Anno
;;;
;;;---------------------------------------------------------------------;

(defun c:anno ()
  (vl-load-com)
  (Save_Sys '("CMDECHO" "OSMODE" ))
  (setvar "cmdecho" 0)
  (setvar "osmode" 547) ;; turn on end, int, mid, nea
 
  ;; ****************************************
  ;;       Code starts here            
  ;; ****************************************
  (while (setq pt_ent (an:get_poly_pt))
    (setq dist (an:get_dist pt_ent))
    (command "insert" "ARV" (CADR pt_ent) "" "" "") ;; insert block ARV (no scaling/rotation)
    (an:attribute dist)  ;; update attribute NUM with dist
    ;;  next line is a test ;; ??????????
    ;; (command "point" "non" (cadr pt_ent)) ;; ???NOT SURE WHY PUTTING POINT THERE???

  ;; End of main routine and exit
  (*error* "") ;;(*error* "") ; call error routine to reset vars
  (princ)  ; exit quietly
  )  ; end while
)  ; end defun
;;;  =================================================================

 

;;;  =================================================================
;;  function to get a point along a polyline
;;  returns a list of (ent_name, point) or
;;  nil if user pressed ENTER
(defun an:get_poly_pt (/ loop ent)
  (setvar "errno" 0) ; must pre set the errno to 0
  (setq loop t)
  (while loop

    (cond
      ((and (null
              (setq ent (entsel "\nPick a point on the pline to tag distance."))
            )
            (= (getvar "errno") 7) ; missed
       )
       (prompt "\nMissed, Try again.")
      ) ; end cond 1

      ((= (getvar "errno") 52) ; exit if user pressed ENTER
       (setq loop nil) ; exit with nil
      ) ; end cond 2
      ((null ent)
       (prompt "\nUnknown error, select again.")
      ) ; end cond 3
      ((= (cdr (assoc 0 (entget (car ent)))) "LWPOLYLINE")
       (setq loop nil) ; exit with ent
      ) ; end cond 4
      ((= (cdr (assoc 0 (entget (car ent)))) "POLYLINE")
       (setq loop nil) ; exit with ent
      ) ; end cond 5
      (T
       (prompt
         (strcat "\nWrong object type: " (cdr (assoc 0 (entget (car ent)))))
       )
      ) ; end cond 6
    ) ; end cond
  ) ; end while
  ent ; return this
) ; end defun
;;;  =================================================================

 

;;;  =================================================================
;;  function to return the distance of point from START.
;;  
(defun an:get_dist (lst / obj)
  (setq obj (vlax-ename->vla-object (car lst)))
  (vlax-curve-getdistatpoint obj
    (vlax-curve-getClosestPointTo obj (cadr lst)))
) ; end defun
;;;  =================================================================



;;;  =================================================================
;;  function to update attribute NUM with distance along polyline
;;  
(defun an:attribute (dist / prefix suffix station dist_round dist_len ename elist)
  (setq prefix (an:round (fix (* dist 0.01)) )
  suffix (progn
  (setq dist_round (an:round dist)
dist_len (strlen dist_round)
  )  ;; end setq

  (if (> dist_len 2) (substr dist_round (- dist_len 1) dist_len)  dist_round)
      ) ; end progn
station (strcat prefix "+" suffix)  ;; create attribute text
  ) ; end setq

  (setq ename (entnext (entlast))
   elist (entget ename))

    ;; search for attribute with tag = "NUM"
  (while (and (= (cdr (assoc 0 elist))
         "ATTRIB")
         (/= (cdr (assoc 2 elist))
        "NUM")
    )
   
    (setq ename (entnext ename)  ;; try next attribute
     elist (entget ename)
    )

    ;; replace value with contents of variable 'dist'
    (if (= (cdr (assoc 2 elist)) "NUM")
      (progn
    (entmod
       (subst (cons 1 station) (assoc 1 elist) elist)   ;; (new data, old data, list to use)
        )
        (entupd ename)   ;; force regen of entity
;;station  ;; DEBUG return text of attribute
      )
      ;; else
      (*error* "Attribute NUM not found in inserted block.")
    ) ; end if
     
  ) ; end while
   
) ; end defun
;;;  =================================================================



;;;  =================================================================
;;  function to return real number as an integer string
;;  
(defun an:round (num)
  (rtos num 2 0)
) ; end defun
;;;  =================================================================



;;;  *****************************************************************
;;   UTILITY FUNCTIONS
;;;  *****************************************************************

;;;  =================================================================
;;   error function & Routine Exit
;;
  (defun *error* (msg)
    (if
      (not
        (member
            msg
           '("console break" "Function cancelled" "quit / exit abort" "")
        )
      )
       (princ (strcat "\nError: " msg))
    ) ; endif

    ;;reset all variables here
    (Restore_Sys)
  ) ;end error function
;;;  =================================================================

 

;;;  =================================================================
;;  Function to save system variables
;;  call to function
;;    (Save_Sys '("CMDECHO" "BLIPMODE" "CLAYER" "OSMODE" "CELTYPE" "CECOLOR"))
;;
(defun Save_Sys (sysvar)
  (setq *SysVar* '()) ; global var list of saved values
  (repeat (length sysvar)
    (setq *SysVar* (append *SysVar* (list (list (car sysvar) (getvar (car sysvar))))))
    (setq sysvar (cdr sysvar))
  )
)
;;;  =================================================================



;;;  =================================================================
;; Function to reset system variables
;;
(defun Restore_Sys ()
  (and (listp *SysVar*)
    (repeat (length *SysVar*)
      (setvar (caar *SysVar*) (cadar *SysVar*)) ;set 1st item (system variable) as 2nd item in first list
      (setq *SysVar* (cdr *SysVar*)) ; remove first list
    )
  )
)
;;;  =================================================================


updates:
- changed \= to /=
- allow user to select POLYLINE as well as LWPOLYLINE
- (command "insert" "ARV" pt_ent "" "" "") changed to (command "insert" "ARV" (CADR pt_ent) "" "" "")
- took out modifications to correct alleged "rounding error"
- fixed error when clicking on polyline smaller shorter than 10 units

Jürg Menzi

  • Swamp Rat
  • Posts: 599
  • Oberegg, Switzerland
Code to auto-update blocks. . . help?
« Reply #26 on: July 30, 2005, 05:19:26 AM »
Quote from: tcdan
(...)But it looks like reactors can easily crash your drawing, and, if I have say a couple hundred of these blocks all with reactors,...
Not with a clean programming. We are working with dwgs they contain thousands of reactors (object-, command-, drawing-) and have no probs with them.
Quote from: tcdan
I'm wondering if CAD's performance will suffer too.(...)
Yes, there is a minor slow down (especially with object reactors) but only if you apply them on thousands of objects.

Cheers
A computer's human touch is its unscrupulousness!
MENZI ENGINEERING GmbH
Current A2k16... A2k24 - Start R2.18

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Code to auto-update blocks. . . help?
« Reply #27 on: July 30, 2005, 02:06:28 PM »
Quote from: tcdan
. . . I wasn't quite sure why you pute the error-handling and exit routine at the beginning of the entire function, so I moved it out of the main function.  I'd like to know if that was a bad idea.
The reason for the (*error*) function being at the beginning of the routine is to make it a local error handler, and anything local must be (defun'ed prior to calling it.

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #28 on: July 30, 2005, 11:01:24 PM »
Quote
The reason for the (*error*) function being at the beginning of the routine is to make it a local error handler, and anything local must be (defun'ed prior to calling it.


So what does it mean to be 'local'?  I guess it just means you can only call the function from the function that it is defined within.

And does the code skip any locally defined functions (blocks of code surrounded by a (defun) statement)?

tcdan

  • Guest
Code to auto-update blocks. . . help?
« Reply #29 on: July 30, 2005, 11:32:13 PM »
And is it OK for me to make it global. . . or is it better to keep it local?  What does it matter?

Anyways, I was thinking it would be better to make it global cuz I would prolly use this with other functions.