Unknown Command Problem

Unknown Command Problem

Anonymous
Not applicable
6,779 Views
48 Replies
Message 1 of 49

Unknown Command Problem

Anonymous
Not applicable

I'm brand new to the forums and to Visual LISP, so forgive me if this is in the wrong area or if I seem a bit lost.

 

I'm working in Carlson/Civil 3D and I'm trying to write a LISP that lets me select some polylines and then it sets them to the zero elevation and reduces the vertices. However, every time I try to call the LISP in the command line, it says "unknown command '3dto2d'". I know 3dto2d is a valid command in Carlson, so I assume I'm missing something in my code to make it look there. Any help would be greatly appreciated.

 

(DEFUN C:ZR ()
	(setq ss (ssget))
	(Command-s "._3dto2d" "0" ss "")
	(Command-s "._reduce" "5" ss "")
	(princ)
)

 

Thanks,

Mike

0 Likes
Accepted solutions (1)
6,780 Views
48 Replies
Replies (48)
Message 41 of 49

steve_carson
Enthusiast
Enthusiast

Yeah, the code I posted was meant more as an idea to get around dealing with a selection set of newly created objects. He would have to add the vlax-add-cmd stuff. I've never used that command myself, but he did say he got it to sort-of work.

 

That said, if the above doesn't work, here's a vertex reducing program that I believe works the same way as Mike's Reduce. It would be easy to tweek it to use Mike defaults. I don't have anything to convert 3dPlines to LWPlines though.

 

Spoiler

;;Program to Simplify Pline vertices given a polyline and Max allowed error.
;;Will remove bulges (arcs).
;;
;;By Steve Carson
;;
;;
;;
;;

(vl-load-com)

(defun C:Simplify ( / SS MAXERR COUNTS TOT RTOT)
  (setq TOT 0 RTOT 0)
  (princ "\nSelect Polyline(s) to process (<Enter> for all): ")
  (cond
   ((setq SS (ssget '((0 . "POLYLINE,LWPOLYLINE")) )) (princ))
   ((setq SS (ssget "_A" '((0 . "POLYLINE,LWPOLYLINE")) )) (princ))
   (T (princ "\nNo Polylines exist!"))
  )
  (if SS
   (progn
     (setq MAXERR (getreal "\nEnter maximum error: "))
     (if (< (abs MAXERR) 0.00000001)
         (setq MAXERR 0.000000005)
         (setq MAXERR (abs MAXERR))
     );if
     (repeat (sslength SS)
       (setq COUNTS (SC:Simplify MAXERR (ssname SS 0) (sslength SS)))
       (setq TOT (+ (car COUNTS) TOT) RTOT (+ (cdr COUNTS) RTOT))
       (ssdel (ssname SS 0) SS)
     );repeat
     (princ (strcat "\nA total of " (itoa TOT) " vertices were simplified to " (itoa RTOT)))
   );progn
  );if
  (princ)
);defun

 


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Main Simplification code
;;
;;By Steve Carson
;;

(defun SC:Simplify ( MaxErr Pline ObjNum / ERR OBJ PL1 PL2 S E EINX CHK NL A SA CNT I)
 
;Set Variables
  (setq ERR MaxErr
        OBJ Pline)
 
  (if (= (cdr (assoc 0 (entget Pline))) "LWPOLYLINE")
    (setq PL1 (SC:IndexPline OBJ))
    (setq PL1 (SC:Index3DPline OBJ))
  )
 
  (setq PL2 (list (car PL1) (last PL1))                       ; New Pline
          S (car PL2)                                         ; First Element of Pline
          E (cadr PL2)                                        ; Last Element of Pline
       EINX (car E)                                           ; Ending Index
        CHK nil
         NL '()
          I 0
  )

;Remove Bulges
  (cond
   ((= (cdr (assoc 0 (entget Pline))) "LWPOLYLINE")
     (repeat (length PL1)
      (vla-SetBulge (vlax-ename->vla-object OBJ) I 0.0)
      (setq I (1+ I))
     )
   )
   ((= (cdr (assoc 0 (entget Pline))) "POLYLINE")
     (if (and (= (vla-get-type (vlax-ename->vla-object OBJ)) 0)
              (vlax-method-applicable-p (vlax-ename->vla-object OBJ) 'SetBulge)
         )
        (repeat (length PL1)
          (vla-SetBulge (vlax-ename->vla-object OBJ) I 0.0)
          (setq I (1+ I))
        )
     );if
   )
  )

(if acet-ui-progress (acet-ui-progress (strcat (itoa ObjNum) " objects remaining. Current object progress: ") EINX))

(while (null CHK)

  (if acet-ui-progress (acet-ui-progress (car S)))

 (if (> (- (car E) (car S)) 1)
  (progn
   ;Determine point on PL1 that is farthest away from PL2
   (setq A (SC:GetMaxDist (cdr S) (cdr E) (SC:ListBetween (car S) (car E) PL1)))
   (cond

    ;If the max distance is less than the max error AND the second element equals the end point, setq CHK to T
    ( (and (< (car A) ERR) (= (car E) EINX))   (setq CHK T) )

    ;If the max dist is greater than max error, add point to list and set new point to E
    ( (> (car A) ERR)   (setq PL2 (SC:SortByFirst (append (list (cdr A)) PL2)) E (cdr A)) )

    ;If the max dist is less than max error, set S and E to next points
    ( (< (car A) ERR)   (setq S E E (SC:ListNext E PL2))  )

   );cond
  );progn
  (if (= (car E) EINX) (setq CHK T) (setq S E E (SC:ListNext E PL2)))

 );if

);while

(if acet-ui-progress (acet-ui-progress))
(setq CNT (length PL2))

;Create new Pline from PL2 list
(foreach P (reverse PL2)
  (setq NL (append (cdr P) NL))
);foreach

;Make a safearray of the coordinates
(setq SA (vlax-safearray-fill
            (vlax-make-safearray vlax-vbdouble (cons 0 (1- (length NL))))
                NL
         )
)

;Modify Pline
(vlax-put-property (vlax-ename->vla-object OBJ) 'Coordinates (vlax-make-variant SA))
(princ (strcat "\n" (itoa (1+ EINX)) " points simplified to " (itoa CNT)))
(cons (1+ EINX) CNT)
);defun

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Sort a list by first element
;;
;;By Steve Carson
;;

(defun SC:SortByFirst (L / )
  (vl-sort L (function (lambda (a b) (< (car a) (car b)))))
)

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Get Maximum Distance
;;By Steve Carson
;;
;;Returns list element and distance that is farthest away from a line drawn between 2 points
;;List needs to be in the form ((1 X1 Y1) (2 X2 Y2) ... (n Xn Yn))
;;Returned list is in the form (d n Xn Yn)
;;Also works for lists including a Z value and returns a list with a Z value.

(defun SC:GetMaxDist (p1 p2 lst / d d2 i)
   (setq d 0)
   (foreach l lst
     (if (> (setq d2 (SC:DistToLine (cdr l) p1 p2)) d)
         (setq d d2 i l)
     )
   )
   (cons d i)

);defun

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;List between indices
;;By Steve Carson
;;
;;Returns a non-inclusive list of items between 2 indices given 2 indices and a list
;;List needs to be in the form ((1 X1 Y1) (2 X2 Y2) ... (n Xn Yn))
;;or ((1 X1 Y1 Z1) (2 X2 Y2 Z2) ... (n Xn Yn Zn))

(defun SC:ListBetween (indx1 indx2 lst / n i l)
  (setq n (1- (- indx2 indx1))
        i indx1
        l '()
  )
  (repeat n
    (setq l (cons (nth (setq i (1+ i)) lst) l))
  )
  (reverse l)

)

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Perpendicular Distance of a point (p1) to a line defined by 2 points (p2 p3)
;;By Steve Carson
;;
;;Uses the numerically stable version of "Heron's Formula" shown on Wikipedia to find
;;the area of the triangle formed by the 3 points, then multiplies it by 2 to get the
;;area of the rectangle, then divides by the length of the line to get the width of the
;;rectangle, which is the perpendicular distance required.

(defun SC:DistToLine ( pt1 pt2 pt3 / LIN A B C A1 B1 C1)


(if (equal pt2 pt3 0.0001)
(distance pt1 pt2)
(progn
 (setq LIN (distance pt2 pt3) A (distance pt1 pt2) B (distance pt1 pt3) C LIN)

;Sorts lengths so A1<=B1<=C1
 (cond
  ((<= A B C) (setq A1 A B1 B C1 C))
  ((<= A C B) (setq A1 A B1 C C1 B))
  ((<= B A C) (setq A1 B B1 A C1 C))
  ((<= B C A) (setq A1 B B1 C C1 A))
  ((<= C A B) (setq A1 C B1 A C1 B))
  ((<= C B A) (setq A1 C B1 B C1 A))
  (T (setq A1 A B1 B C1 C))
 );cond

 (if (and (not (equal A1 0.0 0.0001))
          (not (equal B1 0.0 0.0001))
          (not (equal C1 0.0 0.0001)))
  (/
          (sqrt
    (abs
            (*
             (+ A1 (+ B1 C1))
             (- C1 (- A1 B1))
             (+ C1 (- A1 B1))
             (+ A1 (- B1 C1))
            );*
    );abs
          );sqrt

          (* 2 LIN)
  );/
  0
 );if
);progn
);if
)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Index Pline vertices
;;By Steve Carson
;;
;;
;;Returns a list of coordinates in the form:
;;((1 X1 Y1) (2 X2 Y2) ... (n Xn Yn))

(defun SC:IndexPline (ent / P C1 C2 IDX)
  (setq C1 (vlax-safearray->list
               (vlax-variant-value
                   (vla-get-coordinates
                       (vlax-ename->vla-object ent)
                   )
               )
           )
        IDX 0
        C2 (list (list IDX (car C1) (cadr C1)))
        C1 (cddr C1)
  )
  (repeat (/ (length C1) 2)
    (setq C2 (cons (list (setq IDX (1+ IDX))  (car C1) (cadr C1)) C2)
          C1 (cddr C1)
    )
  );repeat
  (reverse C2)
);defun

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Index 3DPline vertices
;;By Steve Carson
;;
;;
;;Returns a list of coordinates in the form:
;;((1 X1 Y1 Z1) (2 X2 Y2 Z2) ... (n Xn Yn Zn))

(defun SC:Index3DPline (ent / P C1 C2 IDX)
  (setq C1 (vlax-safearray->list
               (vlax-variant-value
                   (vla-get-coordinates
                       (vlax-ename->vla-object ent)
                    )
                )
            )
        IDX 0
        C2 (list (list IDX (car C1) (cadr C1) (caddr C1)))
        C1 (cdddr C1)
  )
  (repeat (/ (length C1) 3)
    (setq C2 (cons (list (setq IDX (1+ IDX))  (car C1) (cadr C1) (caddr C1)) C2)
          C1 (cdddr C1)
    )
  );repeat
  (reverse C2)
);defun

 


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;Next List Element
;;By Steve Carson
;;
;;Given an element and a list, returns the next element in the list.
;;Returns nil if element is last element of list, or is not in the list.

(defun SC:ListNext (E L / A N)
  (if (setq A (member E L))
       (progn
        (setq N (1+ (- (length L) (length A))))
        (if (< N (length L))
            (nth N L)
            nil
        )
       )
       nil
  )
);defun

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(princ "\nType \"SIMPLIFY\" to invoke.")
(princ)

0 Likes
Message 42 of 49

john.uhden
Mentor
Mentor
I found a "Simplify" function in my arsenal to convert 3DPs to LWPs. It looks old (as in before ActiveX) but it does work well.

John F. Uhden

0 Likes
Message 43 of 49

Kent1Cooper
Consultant
Consultant

@Kent1Cooper wrote:

@Anonymous wrote:

... what does the "5" mean in your lines (assoc 5...), (cons 5...)?

 

And the idea of the hexadecimal counter is to just count up until it can't find any elements with a higher value and whatever the highest value element is, that is the most recent entity created?


The 5 is the DXF code number in entity data lists for the entity handle.  ....

 

Since there can be gaps between entity handle numbers [such as if something has been eliminated by something like 3Dto2D, and replaced with a new entity], if it counts up until it doesn't find the next handle number in the drawing, that doesn't mean it's gotten to the most recent entity.  .... I would check, for each thing it finds, whether that is also the same entity as (entlast) -- if so, stop looking, but if not, keep looking.....


To give you an idea of what I mean, try this handles-based routine to find everything newer than [in this case -- not for your application] a User-selected object:

 

(defun C:SSAP (/ int2hex hndl int tmp FoundLast); = Selection Set of All Past [selected object]
  ;; Base-10 Integer to Hexadecimal converter:
  (defun int2hex (int / power hndl div remain posval)
    (setq
      power 1
      hndl ""
    ); setq
    (while (> int 0)
      (setq
        div (expt 16 power)
        remain (rem int div)
        posval (/ remain (expt 16 (1- power))); POSition VALue
        int (- int remain)
        hndl
          (strcat
            (if (< posval 10)
              (itoa posval)
              (chr (+ 55 posval))
            ); if
            hndl
          ); strcat & hndl
        power (1+ power)
      ); setq
    ); while
    hndl ; return calculated string
  ); defun - int2hex
  (setq SSAPss (ssadd)); initially empty
  (if
    (and
      (setq esel (entsel "\nSelect object to find All Past it: "))
      (not (equal (car esel) (entlast))); didn't pick the last entity itself
    ); and
    (progn ; then
      (setq
        hndl (cdr (assoc 5 (entget (car esel)))); entity handle [hexadecimal]
        int 0 ; to start
      ); setq
      (repeat (setq power (strlen hndl)) ; calculate base-10 equivalent of handle
        (setq int
          (+
            int
            (*
              (-
                (setq tmp (ascii (substr hndl 1 1)))
                (if (> tmp 64) 55 48); letters vs. digits
              ); -
              (expt 16 (setq power (1- power)))
            ); *
          ); +
          hndl (substr hndl 2)
        ); setq
      ); repeat
      (while (not FoundLast)
        (if (setq ent (handent (int2hex (setq int (1+ int)))))
          (progn
            (ssadd ent SSAPss)
            (setq FoundLast (equal ent (entlast))); T stops (while)
          ); progn
        ); if
      ); while
      (sssetfirst nil SSAPss); select/highlight/grip
    ); progn -- then
    (prompt "\nThat is the last entity in the drawing."); else
  ); if
  (princ)
); defun -- SSAP

It asks the User to select an object, and it selects/grips/highlights all objects that are newer than that one, if any, and whether or not there are missing handle numbers in between entities that still exist.

 

In your case, this would be split up.  You would first get the handle from (entlast) rather than from User selection, before running something like 3Dto2D.  Then after running that, you would use the rest of this.  [Whether whatever routine works on what this finds can pick it up from the selected/highlighted/gripped selection set directly, or would need to take the selection-set variable as an argument, possibly stepping through it processing one thing at a time rather than all together, etc., I leave to you.]

 

Of course, the same could normally be done using (entnext) repeatedly following from the selected object, until it gets to the end.  This kind of thing is only suggested because of the possibility that (entlast) before running some other processing routine may not still be there by the time you get to looking for newer things.  But @hmsilva's clue that the entity name survives even after the entity is gone means it may not be necessary to do it this way.  However, entity names are more volatile than handles, and it's remotely possible that something could happen to that entity name in between [e.g. however unlikely, I believe AutoCAD could re-use the old one for a different newer entity], but the handle should be a fool-proof starting point.

Kent Cooper, AIA
0 Likes
Message 44 of 49

Anonymous
Not applicable

This Worked!

 

I now have just one issue left to resolve.

 

I've added my lisp file to the startup suite thing in APPLOAD. Correct me if I'm wrong, but my understanding is that when I add a lisp file in the startup suite, it automatically loads that file every time a drawing is opened. Correct?

*edit* ... It turns out this issue comes up whenever I load and use the command, whether it's in the startup suite or not.

 

Anyways, the issue is as follows:

 

I open AutoCAD, it loads the lisps, and everything works fine. BUT... If I open a new drawing after using my ZR command in the first drawing, I get an error message saying "error: Visual LISP command document mismatch: 3DTO2D" every time I try to use 3Dto2D in the new drawing (same error when trying to use REDUCE). I can still use the commands in the original drawing, but I can't use them in any new drawings.

 

When I looked around for a solution to this issue, I discovered that it has something to do with the vlax-add-cmd lines. I didn't completely understand the solutions mentioned, but I copied in some lines that one solution said should fix the problem. However, without being able to understand what exactly the lines do, I guess I didn't input them right, or I'm not applying them properly because the issue persists.

 

Below is the code for my ZR command, which works completely. The top bit (with the comments) is what I copied over when trying to remedy the above mentioned issue. Any guidance on that would be awesome!

;; Comments by JRF: This code should be
;;; run before adding any other commands with vlr-add-cmd.
;;; Otherwise, when using added commands in multiple documents
;;; (MDI mode), sometimes the commands fail with a "Visual LISP
;;; command document mismatch" error.  Apparently vlax-add-cmd
;;; must be called in a document in order to activate commands
;;; in that document.
(defun DummyCommand () NIL)
(vlax-add-cmd "DummyCommand" 'DummyCommand)
(defun AddCommandsHelper (a b)
  (vlax-add-cmd "DummyCommand" 'DummyCommand)
)
;; Install dummy command reactor only if it's not
;; defined already
(or DummyCommandReactor
    (setq DummyCommandReactor
           (vlr-docmanager-reactor
             NIL
             '((:vlr-documentBecameCurrent . AddCommandsHelper))
           )
    )
)

(DEFUN C:ZR ( / ss i lastobj obj)
	(setq ss (ssget)
	 lastobj (entlast))

	(vl-load-com)
	(vlax-add-cmd "REDUCE" 'c:reduce)
	(vlax-add-cmd "3DTO2D" 'c:3dto2d)

	(repeat (setq i (sslength ss))
		(setq obj (ssname ss (setq i (1- i))))
		(Command "reduce" "5" obj "")
		(if (= lastobj (entlast))
			(Command "3dto2d" "0" ss "")
			(progn
				(Command "3dto2d" "0" (entlast) "")
				(setq lastobj (entlast))
			)
		)
	)
)

 

 

 

 

Thanks again for being so helpful on this. The guidance has definitely helped me figure out some of the basics and syntax for writing this stuff.

0 Likes
Message 45 of 49

john.uhden
Mentor
Mentor
One way to work around such apparent limitations is to load everything via acaddoc.lsp, e.g.
(vl-load-com)
(vlax-add-cmd etc.)
(load "this")
(load "that")

John F. Uhden

0 Likes
Message 46 of 49

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

I've added my lisp file to the startup suite thing in APPLOAD. Correct me if I'm wrong, but my understanding is that when I add a lisp file in the startup suite, it automatically loads that file every time a drawing is opened. Correct?....


No, those things are loaded every time you start AutoCAD, not every time you open a drawing.  As @john.uhden suggested, acaddoc.lsp is the way to go for every-drawing loading.

Kent Cooper, AIA
0 Likes
Message 47 of 49

Anonymous
Not applicable

Oh ok, I see. So I put the vlax-add-cmd lines in the acaddoc.lsp file as well as the zr.lsp file? Or do I remove them from the zr.lsp file and the (load "zr.lsp") line from acaddoc.lsp would replace the need for them in zr.lsp?

0 Likes
Message 48 of 49

john.uhden
Mentor
Mentor
Yes, put the vlax-add-cmd in acaddoc.lsp. then you have the choice of putting you ZR code in acaddoc.lsp or having acaddoc.lsp load it via (load ...). It's the same difference. Such as I do with my client is to compile all the different LSP files into .FAS files and then load them via acaddoc.lsp. The reason there is that the programs I wrote are their intellectual property. We can't allow any of the draftspeople to snag the code and take it to another company. Of course I built into each file a little bit of security so that they won't work except within my client's network, but if I expose the source code then anyone could remove the security.

John F. Uhden

0 Likes
Message 49 of 49

Anonymous
Not applicable

Gotcha. Ok, I did that and it loads nicely each time I open a new drawing. However, I still run into the same issue. I'm not sure I'm doing a great job of explaining it, but below is the link that I came across where some folks are discussing the same problem. Their solution is way over my head, so maybe one of you could dumb it down for me. From what little I understand, it seems like using the vlax-add-cmd... stuff doesn't work when switching between multiple documents unless you reload/redefine the commands each time you switch.

 

The Swamp - Transparent Lisp Command

0 Likes