Author Topic: Help with locating block insertion point of blockA and moving it to the origin  (Read 2930 times)

0 Members and 1 Guest are viewing this topic.

kameron1967

  • Guest
Hi,

I'm wondering if someone can give me an idea on how I could get lisp to obtain the insertion of blockA.dwg. What I'd like to do is then move that insertion point to the origin (0,0).

Also, I use an MTB.lsp that prompts me to choose blockA.dwg then blockB.dwg in order to transfer the attributes.  I wonder how I could make it automatically choose blockA and then select blockB and update automatically.

Any help would be greatly appreciated. :-)

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
Welcome to TheSwamp Kameron :-)

Start with:

Code: [Select]
(ssget "_X" '((0 . "INSERT") (2 . "blockA")))
This will return a SelectionSet of all blocks with name "blockA" in the drawing (should there be any) - you could then iterate through this SelectionSet, updating the insertion point (DXF10) of each block entity to the origin.

Look into such functions as subst, entget, entmod, & entupd to perform the modification. (Unsure how to investigate these functions? See here). There are plenty of examples on the forum demonstrating how to modify the DXF codes of an entity - here is a recent thread on the topic.

As for the attributes, I would recommend creating an association list of attribute data from the 'source' block (perhaps see here for possible ways to do that); then, iterate through the attributes of the 'destination' block (perhaps study the link I just provided to see how to iterate through the attributes of the block), using the assoc function to pull the data entry using the attribute tag.

i.e.

Code: [Select]
;; Create a list of attribute data from 'blockA', something like:

(setq data (("TAG1" . "Value1") ("TAG2" . "Value2") ... ("TAGN" . "ValueN")))

;; Then, iterate through the attributes of 'blockB' and pull the relevant data from this association list,
;; hence if 'att' is an attribute entity in 'blockB':

;; Get the Attribute DXF Data:
(setq elist (entget att))

;; Get the Tag String of the attribute
(setq tag (cdr (assoc 2 elist)))

;; Get the relevant value from the attribute data collected from 'blockA':
(setq value (cdr (assoc tag data)))

;; Update the value of the attribute in 'blockB':
(entmod (subst (cons 1 value) (assoc 1 elist) elist))

;; Continue this process for all the attributes.
« Last Edit: January 29, 2011, 10:17:34 AM by Lee Mac »

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
kameron1967
Welcome to the Swamp.


Do you have any LISP experience?
This will tell us how much & what kind of help you need.

Do Block A & B have the same tag names in the attributes?


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.

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
I have constructed a function to accomplish the second described task.

Since I have voluntarily donated my time to help you understand, please take the time to carefully read the comments.

Code: [Select]
([color=BLUE]defun[/color] c:AutomaticallyCopyAttributes ( [color=BLUE]/[/color] SourceBlock DestBlock ss1 ss2 block elist alist i value )

  [color=GREEN];; Define function and localise variables.[/color]
  [color=GREEN];; The variable localisation is *especially* important in this case[/color]
  [color=GREEN];; since we are using a self-referencing variable ('alist') later on to[/color]
  [color=GREEN];; create our association list of attribute data.[/color]

  [color=GREEN];; To understand why this is so important, read my tutorial at:[/color]
  [color=GREEN];; [/color]
  [color=GREEN];; www.lee-mac.com/localising.html[/color]
  [color=GREEN];; [/color]
  [color=GREEN];; ---------------------------------------------------------------------;;[/color]
  [color=GREEN];; Example courtesy of Lee Mac 2011  -  www.lee-mac.com                 ;;[/color]
  [color=GREEN];; ---------------------------------------------------------------------;;[/color]


  [color=GREEN];; ---------------------------------------------------------------------;;[/color]
  [color=GREEN];; Program Description:                                                 ;;[/color]
  [color=GREEN];; ---------------------------------------------------------------------;;[/color]
  [color=GREEN];; This program will first retrieve a SelectionSet of all attributed[/color]
  [color=GREEN];; blocks with the name as held by the 'SourceBlock' variable.[/color]
  [color=GREEN];; An association list of ((<tag> . <value>) ... ) will then be[/color]
  [color=GREEN];; constructed using the attribute data of the first block in this[/color]
  [color=GREEN];; SelectionSet.[/color]

  [color=GREEN];; A SelectionSet of all attributed blocks with the name held by the[/color]
  [color=GREEN];; 'DestBlock' will then be retrieved. The program will proceed to[/color]
  [color=GREEN];; iterate through this SelectionSet and, for each attribute in each[/color]
  [color=GREEN];; block: if the attribute tag string is present in the association[/color]
  [color=GREEN];; list as collected from the source block, the value of such attribute[/color]
  [color=GREEN];; will be updated to display the value held by the respective attribute[/color]
  [color=GREEN];; in the source block - effectively copying all attributes.[/color]

  [color=GREEN];; [ Annotation follows each expression ][/color]

  [color=GREEN];; ---------------------------------------------------------------------;;[/color]
  [color=GREEN];;                             Main Function                            ;;[/color]
  [color=GREEN];; ---------------------------------------------------------------------;;[/color]
 
  ([color=BLUE]setq[/color] SourceBlock [color=MAROON]"blockA"[/color])
 
  [color=GREEN];; Source Block from which attribute data will be sourced.[/color]

 
  ([color=BLUE]setq[/color] DestBlock   [color=MAROON]"blockB"[/color])

  [color=GREEN];; Destination Blocks to which the attribute data will be copied.[/color]


  ([color=BLUE]if[/color]

    [color=GREEN];; IF requires two arguments and may take an optional third:[/color]
    [color=GREEN];;[/color]
    [color=GREEN];; (IF <test expr>[/color]
    [color=GREEN];;   <then expr>[/color]
    [color=GREEN];;   [else expr][/color]
    [color=GREEN];; )[/color]

    [color=GREEN];; IF the test expression returns a non-nil value (does not have to be[/color]
    [color=GREEN];; explicitely T, just anything non-nil; the THEN expression will be[/color]
    [color=GREEN];; evaluated. Otherwise, the ELSE expression will be evaluated if supplied;[/color]
    [color=GREEN];; if not supplied, the IF function will return nil.[/color]
   
    ([color=BLUE]and[/color]

      [color=GREEN];; Using the AND operator means ALL of the expressions passed to it must[/color]
      [color=GREEN];; return a non-nil value for the AND function to return T.[/color]
     
      ([color=BLUE]setq[/color] ss1 ([color=BLUE]ssget[/color] [color=MAROON]"_X"[/color] ([color=BLUE]list[/color] ([color=BLUE]cons[/color] 0 [color=MAROON]"INSERT"[/color]) ([color=BLUE]cons[/color] 2 SourceBlock) ([color=BLUE]cons[/color] 66 1))))
      ([color=BLUE]setq[/color] ss2 ([color=BLUE]ssget[/color] [color=MAROON]"_X"[/color] ([color=BLUE]list[/color] ([color=BLUE]cons[/color] 0 [color=MAROON]"INSERT"[/color]) ([color=BLUE]cons[/color] 2   DestBlock) ([color=BLUE]cons[/color] 66 1))))
     
    ) [color=GREEN];; End AND[/color]

    [color=GREEN];; IF we can retrieve a SelectionSet of all attributed blocks, [(66 . 1) is used[/color]
    [color=GREEN];; to ensure we retrieve only *attributed* blocks], with name equal to that[/color]
    [color=GREEN];; held by the 'SourceBlock' variable [Note that this will *not* include[/color]
    [color=GREEN];; Dynamic blocks as these may take an anonymous block name], AND a SelectionSet[/color]
    [color=GREEN];; of all attributed blocks with name equal to that held by the 'DestBlock' variable[/color]
    [color=GREEN];; then do the following:[/color]

    ([color=BLUE]progn[/color]

      [color=GREEN];; Use the progn function to wrap the following statements into a single[/color]
      [color=GREEN];; expression that we can pass to the IF function to constitute the 'Then' argument.[/color]

      ([color=BLUE]setq[/color] block ([color=BLUE]ssname[/color] ss1 0))

      [color=GREEN];; Retrieve the first entity in the SelectionSet (SelectionSets have a zero-based index).[/color]

      ([color=BLUE]while[/color] ([color=BLUE]eq[/color] [color=MAROON]"ATTRIB"[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 0 ([color=BLUE]entget[/color] ([color=BLUE]setq[/color] block ([color=BLUE]entnext[/color] block))))))

        [color=GREEN];; While we are dealing with ATTRIBute entities, do the following.[/color]

        [color=GREEN];; ---------------------------------------------------------------------;;[/color]

        [color=GREEN];; Note that we use entnext to view the ATTRIBute entities of the attributed[/color]
        [color=GREEN];; block. The entnext function takes an entity argument and merely returns the[/color]
        [color=GREEN];; entity that follows the supplied entity in the drawing database. When[/color]
        [color=GREEN];; entities are created in a drawing, they are added to this drawing database[/color]
        [color=GREEN];; in the order that they were created.[/color]

        [color=GREEN];; When an attributed block is created in the drawing, the INSERT entity is[/color]
        [color=GREEN];; created, then a number of ATTRIBute entities are created, and a terminating[/color]
        [color=GREEN];; SEQEND entity is finally created to signal the end of the block information.[/color]

        [color=GREEN];; Hence when we supply our INSERT entity (variable 'block') to the entnext[/color]
        [color=GREEN];; function, the function returns the next entity in the database following this[/color]
        [color=GREEN];; entity, which is our ATTRIBute entities. Notice that after we return the[/color]
        [color=GREEN];; ATTRIB entity using the entnext function, we bound it to the 'block' variable -[/color]
        [color=GREEN];; this ensures that, on the next evaluation of the WHILE test expression, the[/color]
        [color=GREEN];; entnext function is fed the previous ATTRIBute entity and so returns the entity[/color]
        [color=GREEN];; following this, whether it be another ATTRIBute or the SEQEND entity.[/color]

        [color=GREEN];; Notice that the WHILE test expression could also be phrased:[/color]
        [color=GREEN];;[/color]
        [color=GREEN];; (while (not (eq "SEQEND" (cdr (assoc 0 (entget (setq block (entnext block)))))))[/color]
        [color=GREEN];;[/color]
        [color=GREEN];; to test for the appearance of the terminating SEQEND entity.[/color]

        [color=GREEN];; ---------------------------------------------------------------------;;[/color]

        ([color=BLUE]setq[/color] elist ([color=BLUE]entget[/color] block))

        [color=GREEN];; Get the DXF data of the attribute entity.[/color]

        ([color=BLUE]setq[/color] alist ([color=BLUE]cons[/color] ([color=BLUE]cons[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 2 elist)) ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 1 elist))) alist))

        [color=GREEN];; If the cons function is supplied with two atoms, it will create a dotted pair.[/color]
        [color=GREEN];; Using this knowledge, we create a dotted pair using the Attribute Tag (DXF 2) and[/color]
        [color=GREEN];; the Attribute Value (DXF 1) of the ATTRIBute entity:[/color]

        [color=GREEN];; ("TAG1" . "VALUE1")[/color]

        [color=GREEN];; If the cons function is supplied with an atom and a list, it will add the atom[/color]
        [color=GREEN];; to the beginning of the list. In this way we create our association list:[/color]

        [color=GREEN];; alist = (("TAG1" . "VALUE1") ("TAG2" . "VALUE2") ... ("TAGN" . "VALUEN"))[/color]

      ) [color=GREEN];; End WHILE[/color]

      [color=GREEN];; Now that we have our association list containing the attribute data, we can[/color]
      [color=GREEN];; proceed to iterate through the SelectionSet containing the destination attributed[/color]
      [color=GREEN];; blocks.[/color]

      ([color=BLUE]repeat[/color] ([color=BLUE]setq[/color] i ([color=BLUE]sslength[/color] ss2))

        [color=GREEN];; Repeat the following set of expressions a number of times equal to the number[/color]
        [color=GREEN];; of objects in the SelectionSet 'ss2'.[/color]

        [color=GREEN];; At the same time we can initialise our counter variable 'i' since the integer[/color]
        [color=GREEN];; argument supplied to the repeat function is only evaluated once.[/color]

        ([color=BLUE]setq[/color] block ([color=BLUE]ssname[/color] ss2 ([color=BLUE]setq[/color] i ([color=BLUE]1-[/color] i))))

        [color=GREEN];; Retrieve the entity at index 'i' in the SelectionSet. We may as well use the[/color]
        [color=GREEN];; same variable since it still points to data of ename type (note that it wouldn't[/color]
        [color=GREEN];; matter if it didn't, as is the versatility offered by a high-level language such[/color]
        [color=GREEN];; as LISP).[/color]

        ([color=BLUE]while[/color] ([color=BLUE]eq[/color] [color=MAROON]"ATTRIB"[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 0 ([color=BLUE]entget[/color] ([color=BLUE]setq[/color] block ([color=BLUE]entnext[/color] block))))))

          [color=GREEN];; Using exactly the same logic as above, we can iterate through the[/color]
          [color=GREEN];; attributes of each block in the SelectionSet of 'destination' blocks.[/color]

          ([color=BLUE]setq[/color] elist ([color=BLUE]entget[/color] block))

          [color=GREEN];; Same as above, retrieving the DXF Data of the ATTRIBute entity. Re-using[/color]
          [color=GREEN];; the same variable name for clarity and ease of localisation...[/color]

          ([color=BLUE]if[/color] ([color=BLUE]setq[/color] value ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 2 elist)) alist)))

            [color=GREEN];; Attempt to locate an entry in our association data list with the[/color]
            [color=GREEN];; first item equal to the tag of the ATTRIBute entity. If found, bound[/color]
            [color=GREEN];; the second element in this entry (the value) to the variable 'value'.[/color]

            ([color=BLUE]entupd[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] -1 ([color=BLUE]entmod[/color] ([color=BLUE]subst[/color] ([color=BLUE]cons[/color] 1 value) ([color=BLUE]assoc[/color] 1 elist) elist)))))

            [color=GREEN];; Use the 'subst' function to SUBSTitute the pair (1 . <value>) for the existing[/color]
            [color=GREEN];; pair in the DXF Data of the ATTRIBute entity.[/color]

            [color=GREEN];; When substituted, the new DXF Data list is returned by the subst function.[/color]

            [color=GREEN];; ---------------------------------------------------------------------;;[/color]

            [color=GREEN];; Note that the subst function does *NOT* make any modification to the entity.[/color]
            [color=GREEN];; The subst function will perform the substitution on the supplied list[/color]
            [color=GREEN];; and return the new list.[/color]

            [color=GREEN];; I suppose the point I am trying to make is that the DXF Data list of the entity[/color]
            [color=GREEN];; is just like any other list, and in this way it can be manipulated[/color]
            [color=GREEN];; like any other list.[/color]

            [color=GREEN];; Similarly, the subst function can operate on any list, and is not[/color]
            [color=GREEN];; only restricted to operating on DXF data returned by the entget function.[/color]

            [color=GREEN];; ---------------------------------------------------------------------;;[/color]

            [color=GREEN];; Following the substitution of the new value under DXF group 1,[/color]
            [color=GREEN];; the DXF Data list is fed to the entmod function, which changes the entry[/color]
            [color=GREEN];; in the drawing database to reflect the modification.[/color]

            [color=GREEN];; Finally, the entupd function updates the entity on screen to display[/color]
            [color=GREEN];; the updated information in the drawing database.[/color]

            [color=GREEN];; DXF code -1 always points to the entity name for which the DXF data[/color]
            [color=GREEN];; is referencing, so its an easy way to ensure we are entupd'ing the[/color]
            [color=GREEN];; correct entity without worrying about variables.[/color]

          ) [color=GREEN];; End IF[/color]

        ) [color=GREEN];; End WHILE[/color]

      ) [color=GREEN];; End REPEAT[/color]

    ) [color=GREEN];; End PROGN[/color]

    [color=GREEN];; So, we've supplied THEN argument for the IF function, all wrapped up into[/color]
    [color=GREEN];; one expression by the progn function.[/color]

    [color=GREEN];; Now, if necessary, we can supply an ELSE argument (optional), to be evaluated[/color]
    [color=GREEN];; should the test expression return nil, hence indicating that the program[/color]
    [color=GREEN];; failed to retrieve both SelectionSets.[/color]

    ([color=BLUE]princ[/color] [color=MAROON]"\n** Blocks not Found **"[/color])

    [color=GREEN];; A simple message printed to inform the user of such a case.[/color]

  ) [color=GREEN];; End IF[/color]


  ([color=BLUE]princ[/color])

  [color=GREEN];; Finally, exit cleanly - suppressing the return of the last evaluated expression.[/color]

) [color=GREEN];; End DEFUN[/color]


[color=GREEN];; ---------------------------------------------------------------------;;[/color]
[color=GREEN];;                             End of Function                          ;;[/color]
[color=GREEN];; ---------------------------------------------------------------------;;[/color]


If you have any questions about the code, or the comments, please ask.
« Last Edit: February 12, 2011, 10:41:55 AM by Lee Mac »

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Nicely done Lee.  8-)
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.

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
Nicely done Lee.  8-)

Thanks Alan, I got a bit carried away with the explanation  :-)

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
Did my explanation help at all Kameron, or was I wasting my time?

kameron1967

  • Guest
Lee - I apologize.  I had no idea any of you responded to my inquiry because I never got any e-mail letting me know that there's been replies to my thread.

I have read through your explanation.  Thank you, by the way, for your time and full explanation of the routine you've written.  As always - it looks very professional.

My experience with lisp is limited.  I can only follow and change data and perhaps add additional routines that were already written to allow me to complete my task.

I want to learn lisp and be able to think through these programming challenges.  I think your website and a couple others are very valuable and will help me along the way.  So thanks again.  Your help is truly appreciated.

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
Thanks Kameron,

I apologise and retract my earlier remarks, I'm glad that you could benefit from my explanations  :-)

Lee

Jeff H

  • Needs a day job
  • Posts: 6151
Did my explanation help at all Kameron, or was I wasting my time?

Not for me.

Lee,

Thanks a bunch.
I have not really studied or have just glanced over literature about lisp but,
that is the first time I have read lisp and understood what the code was doing.

Lee Mac

  • Seagull
  • Posts: 12928
  • London, England
You're very welcome Jeff - I'm happy that my explanations were comprehensible  :-)