need help with LSP to modify parametric block and its attributes

aridzv
edited February 2021 in LISP

Hi.
I have a parametric block of a pipe.
the parameter is the length.
I need help with writing a lisp that will do the follows (I've also attached a sample drawing):
1. select the parametric block (the Pipe Block).
2. use the DIST command to get the required length of the parameter (the required pipe length).
3. assign that value to the length parameter of the selected block (in the sample it is parameter "d1" of the pipe block).
4. assign that DIST value to the "value" field of the "QUANTITY" attribute of the selected block.

in the example file it is the transition from situation 1 to situation 2.

thanks,
Ari.

Comments

  • ALANH
    edited February 2021

    Dont double post, its confusing

    ; Pick a line and put its length in a attribute
    ; By Alan H Feb 2021 info@alanh.com.au

    (defun c:llen ( / obj objid str )
    (setq obj (vlax-ename->vla-object (car (entsel "\nPick line obj "))))
    (setq objid (rtos (vla-get-Objectid obj) 2 0))
    (setq str (strcat "%<\AcObjProp Object(%<\_ObjId " objid ">%).length \f \"%lu6\">%"))
    (setq obj (vlax-ename->vla-object (car (nentsel "\nPick attribute "))))
    (vla-put-textstring obj str)
    (command "regen")
    (princ)
    )

  • aridzv
    edited February 2021

    @ALANH
    Hi and thanks for the reply!
    the lisp is almost good.
    there 2 issues with it:
    1. when I'm inserting the block, the attribute ("QUANTITY" in that case) is invisible. is it possible to set the name of the attribute in the lsp code rather then prompt the lsp to choose it?
    2. the text inserted in to attribute is the code string ( "%%).length " ) in stand of the value it self. i tried to fix the code but I failed... (See Attached Screenshot).
    3. is it possible to write a code that use the "DIST" command to get the length value in stand of getting it from a line object?

    many thanks,
    Ari.

    1. The code can look inside the block and use Tagname then set the text value, insert 1st then update else field will appear as the att string.
    2. You removed the format \f
    3. Yeah can do dist but much easier pick line and use Vl-get-length function.
    4. If you change length the field will update re 3. If you never change length then use this.

    Something like this not tested

    (defun c:llen ( / len )
    (setq len (vlax-get (vlax-ename->vla-object (car (entsel "\nPick line obj"))) 'Length))

    (setvar 'attdia 0)
    (command "-insert" blkname pause 1 1 0)

    (foreach att (vlax-invoke (vlax-ename->vla-object (entlast )) 'getattributes)
    (if (= "QUANTITY"(strcase (vla-get-tagstring att)))
    (vla-put-textstring att (rtos len 2 2))
    )
    )

    (command "regen")
    (setvar 'attdia 1)
    (princ)
    )

  • aridzv
    edited February 2021

    @ALANH
    I've made small modification to your first code, note the use of the getpoint and getdist functions:

    (defun c:llen ( / obj p1 di )
    (setq p1 (getpoint "\nPoint 1: "))
    (setq di (getdist p1 "\nDistance: "))
    (setq obj (vlax-ename->vla-object (car (nentsel "\nPick attribute "))))
    (vla-put-textstring obj di)
    (command "regen")
    (princ)
    )

    now, with this code i pick the distance without the need for a guide line, and the attribute and the length value is correct.
    my question is that - is it possible to pick the block and not the attribute itself, and let the LSP to find the QUANTITY attribute and put the length value in to it?
    the issue is that:
    the block is already in the drawing (the insert command is redundant) and I set the QUANTITY attribute to be invisible (All my blocks have the QUANTITY attribute invisible),
    then i need to assign the length, sometimes to change it.
    Thanks,
    Ari.

  • Hi Ari,

    at least, to retrieve the properties, you might also use the "generic properties fucntions" :
    (getpropertyvalue ename property)

    "property" is the string name of a property here ... you can list all avaliable properties by :
    (dumpallproperties ename)

    That way it is probaly easier to get the requires Quantity value :-)
    many greetings !

  • aridzv
    edited February 2021

    O.K.
    with a little help...
    here is a code that do the work:

    (defun c:PipeLenBL (/ obj p1 di)
    (setq p1 (getpoint "\nPoint 1: "))
    (setq di (getdist p1 "\nDistance: "))
    (setq obj (car (entsel "\nSelect Block ")))
    (setpropertyvalue obj "QUANTITY" (rtos di))
    (setpropertyvalue obj "d1" (rtos di))
    (command "regen")
    (princ)
    )

    when executing the command:
    1. select the distance that will be the QUANTITY attribute value.
    2. select the block to set that value to the QUANTITY attribute value.
    3. update d1 parameter, and as a result of that the length of the block is also updated.

    *clarification: in all of my blocks I have "QUANTITY" attribute, and all of my pipe blocks are parametric with d1 parameter that controls the
    length, and that is why I can use those fixed names.
    obviously, it is limiting the demand for flexibility and make things much more simple...

    subject close.
    Ari.

  • :-)
    be careful with (rtos ...), it implicitly uses the system variables for unit and precision ...
    and the string passed-in is then converted into the double value as the parameter/property requires.

    For safety + stability, I would always use (rtos 2 10) to be independent on the system variables ...

    Just a hint for the "property name" : there might be multiple, same-named properties ... using only the "short name" might cuase trouble, if such a same-named property is found in a different property namespace ...
    better to always use the "long property name" (PropertyNameSpace~PropertyName), as shown by (dumpallproperties ename).

    many greetings !

  • ALANH
    edited February 2021

    This will fail if you miss pick say a line (setq obj (car (entsel "\nSelect Block "))) you can use

    (princ "\nSelect Block"))
    (setq ss (ssget "_+.:E:S" '((0 . "Insert")))) this forces select a block can add the blockname filter also.
    I would check that ss exists before continuing
    (setq obj (ssname ss 0))

  • aridzv
    edited February 2021

    @Torsten Moses said:
    :-)
    be careful with (rtos ...), it implicitly uses the system variables for unit and precision ...
    and the string passed-in is then converted into the double value as the parameter/property requires.

    For safety + stability, I would always use (rtos 2 10) to be independent on the system variables ...

    Just a hint for the "property name" : there might be multiple, same-named properties ... using only the "short name" might cuase trouble, if such a same-named property is found in a different property namespace ...
    better to always use the "long property name" (PropertyNameSpace~PropertyName), as shown by (dumpallproperties ename).

    many greetings !

    Torsten MANY THANKS!

    1. about the rtos line - do you mean that it is better to write the code like this: (setpropertyvalue obj "d1" (rtos di 2 10))?
    2. about the long property name - can you help me with an example of the code?

    thanks,
    Ari.

  • aridzv
    edited February 2021

    @ALANH said:
    This will fail if you miss pick say a line (setq obj (car (entsel "\nSelect Block "))) you can use

    (princ "\nSelect Block"))
    (setq ss (ssget "_+.:E:S" '((0 . "Insert")))) this forces select a block can add the blockname filter also.
    I would check that ss exists before continuing
    (setq obj (ssname ss 0))

    Hi alanh and thanks for the reply.
    The idea of the routine is that you MUST have a specific block to edit, and a specific distance to update that block length to, and it should be fast process with out too many clicks and verifications (there is a specific block and specific value for the length).
    in some way you can look on it as a substitute to dynamic blocks that bricscad don't have.
    one thing that can be interesting to do is maybe to create a LSP routine that will update group of blocks with d1 parameter to a specific length value,
    but this is not the case here.
    Ari.

  • Dear Ari,

    1. about the rtos line - do you mean that it is better to write the code like this: (setpropertyvalue obj "d1" (rtos di 2 10))?

    yes, the main idea is to create the string with enough precision : (rtos di 2 10)

    1. about the long property name - can you help me with an example of the code?

    example :
    (dumpallproperties (entlast))

    output :
    ----- Database Properties
    :
    Color (ident: "Color~Native") (AcCmColor [string]) = "ByLayer"
    :
    ----- BIM Properties
    :
    Centerline (ident: "Centerline~BIM") (integer) = ...

    "Color" is the short name, "Color~Native" is the long (and more specific) name ... "Color" might be a property of multiple NameSpaces
    "Centerline" is the short name, "Centerline~BIM" is the long name

    So my suggestion was to always use the "long name" (PropertyName~PropertyNameSpace) to prevent ambiguous cases.

    hope it will clarify ?
    many greetings !

  • aridzv
    edited February 2021

    @Torsten Moses said:
    ...
    yes, the main idea is to create the string with enough precision : (rtos di 2 10)

    Thanks - I've got it. (rtos number [mode [precision]])
    About the long property name - I'll take time to learn this and try to implement it, it looks quite important, Thanks!

    one more issue back to the code it self - my drawing units is mm, but I need the quantity to be in M, and only 3 digits of precision,
    so I've attach the revised code here with the relevant row highlighted:

    (defun c:PipeLenBL (/ obj p1 di )
    (setq obj (car (entsel "\nSelect Block ")))
    (setq p1 (getpoint "\nPoint 1: "))
    (setq di (getdist p1 "\nDistance: "))
    (setpropertyvalue obj "d1" (rtos di 2 10))
    (setpropertyvalue obj "QUANTITY" (rtos (* di 0.001) 2 3))
    (command "regen")
    (princ)
    )