I work with AutoCAD and Revit on a daily basis.
Often, I miss the the ability to align lines to each other in AutoCAD as you do in Revit.
If we take this situation for example:
In revit you'd go AL (the tool command= then pick what to align to, and then what to align. It would move to the place and have the same orientation too.
Is there any LISP solution for that?
You can use MOVE or STRETCH and go from an ENDpoint or INTersection involving the one you want to change, and PERpendicular to the one you want to align it with.
Thanks @Kent1Cooper
This in fact how I do it today. But once you've tasted the "revit way", its hard not to want it.
You call the command, then you press source and target and done.
You're trying to compare apples and oranges. You should compare the AutoCAD Architecture toolset to Revit, not plain AutoCAD. ACA has align, repositionfrom, walloffsetmove, and walloffsetset and automatically anchors walls to one another and doors and windows to walls. Download and experiment with a free trial version.
Hi @dbroad
I am not comparing apples and oranges 🙂
This is simple me looking for a solution that does something specific. Thank you for pointing out ALIGN for me, it is useful for other stuff (I used to move the whole object, and rotate etc, hence align is a great tool)
The rest of the tools seem also not to be the ones I am looking for.
If you take a look at this GIF I made, you will see what I am after. I only want to alter a sub-element, ie one segment of a polyline.
If they are polylines you can use the center grip and perpendicular snap. It's pretty close, but still not quite as fast. This is probably the easiest solution.
https://knowledge.autodesk.com/community/screencast/b43e6a44-a3ba-4726-8741-3bcf5a6c9937
Revit is a distinctly different program than AutoCAD. AutoCAD Architecture is comparable.
That said, AutoCAD does have parametric features. They just don't work with AEC walls.
The command illustrated above is gccollinear.
If we were to look at pick pline segment so get p1 p2 could do a update pline pts by new Y or X value, there is lisp for get pline segment which returns the vertice numbers, then update.
@ВeekeeCZ wrote:Not much slower HERE
Very good solution!
Were you using Temporary Override Keys? Or shift RMB and then P?
@Sea-Haven wrote:If we were to look at pick pline segment so get p1 p2 could do a update pline pts by new Y or X value, there is lisp for get pline segment which returns the vertice numbers, then update.
I was hoping for this type of answer, ie a LISP that asks for a pline segment and then does the segment alignment.
Any clue if there is a finished LISP script that does what I was looking for?
Try this hopefully self explaining.
; move a pline segment based on a x or y point
; By AlanH march 2021
(defun c:plseg( / plent pick plobj pick2 param segment co-ord pt1 pt2 pt3 xy1 xy2 x y new_coord1 oldsnap)
(setq plent (entsel "\nSelect Pline segment "))
(setq
pick (cadr plent)
plObj (vlax-ename->vla-object (car plent))
pick2 (vlax-curve-getclosestpointto plobj pick)
param (vlax-curve-getparamatpoint plObj pick2)
segment (fix param)
co-ord (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 10)) (entget (car plent))))
)
(setq pt1 (nth segment co-ord))
(setq pt2 (nth (+ segment 1) co-ord))
(setq oldsnap (getvar 'osmode))
(setvar 'osmode 1)
(setq pt3 (getpoint "\nPick point for reference "))
(setvar 'osmode 0)
(setq x (car pt3) y (cadr pt3))
(setq xy (getstring "\nDo you want X or Y "))
(if (= (strcase xy) "X")
(setq xy1 (list x (cadr pt1)) xy2 (list x (cadr pt2)))
)
(if (= (strcase xy) "Y")
(setq xy1 (list (car pt1) y) xy2 (list (car pt2) y))
)
(setq pt1 (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) xy1)))
(vla-put-coordinate plobj segment pt1)
(setq pt2 (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) xy2)))
(vla-put-coordinate plobj (+ segment 1) pt2)
(setvar 'osmode oldsnap)
(princ)
)
@Haider_of_Sweden wrote:
@ВeekeeCZ wrote:
Not much slower HERE
Very good solution!
Were you using Temporary Override Keys? Or shift RMB and then P?
Yes, Shift+RMT P to launch a perpendicular osnap mode as a temporary override.
Love this feature. I even customized the key letters to be easier to remember to me.
Contrarily the built-in Temp Override Shortcuts or keys I consider unreliable.
@john.uhden wrote:Mr. Haider,
Why not just use the OFFSET command?
I use FIELD to generate area tags and offset would create a totally new polyline which I need to reconnect to each FIELD text..
@ВeekeeCZ wrote:
Yes, Shift+RMT P to launch a perpendicular osnap mode as a temporary override.
Love this feature. I even customized the key letters to be easier to remember to me.
Contrarily the built-in Temp Override Shortcuts or keys I consider unreliable.
Indeed a good feature. Would you please share how you customized the key letters?
And regarding the Temp Override Shortcuts, what do you dislike there? I can see there are shortcuts defined, but I could never figure out how to use them
@Sea-Haven wrote:Try this hopefully self explaining.
; move a pline segment based on a x or y point
; By AlanH march 2021
(defun c:plseg( / plent pick plobj pick2 param segment co-ord pt1 pt2 pt3 xy1 xy2 x y new_coord1 oldsnap)
(setq plent (entsel "\nSelect Pline segment "))
(setq
pick (cadr plent)
plObj (vlax-ename->vla-object (car plent))
pick2 (vlax-curve-getclosestpointto plobj pick)
param (vlax-curve-getparamatpoint plObj pick2)
segment (fix param)
co-ord (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 10)) (entget (car plent))))
)
(setq pt1 (nth segment co-ord))
(setq pt2 (nth (+ segment 1) co-ord))
(setq oldsnap (getvar 'osmode))
(setvar 'osmode 1)
(setq pt3 (getpoint "\nPick point for reference "))
(setvar 'osmode 0)
(setq x (car pt3) y (cadr pt3))
(setq xy (getstring "\nDo you want X or Y "))
(if (= (strcase xy) "X")
(setq xy1 (list x (cadr pt1)) xy2 (list x (cadr pt2)))
)
(if (= (strcase xy) "Y")
(setq xy1 (list (car pt1) y) xy2 (list (car pt2) y))
)
(setq pt1 (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) xy1)))
(vla-put-coordinate plobj segment pt1)
(setq pt2 (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbdouble '(0 . 1)) xy2)))
(vla-put-coordinate plobj (+ segment 1) pt2)
(setvar 'osmode oldsnap)
(princ)
)
Great solution @Sea-Haven
Two questions:
1) On some polylines, this lisp halts. I get this
Do you want X or Y y
Cannot invoke (command) from *error* without prior call to (*push-error-using-command*).
Converting (command) calls to (command-s) is recommended.
What do you think the causes could be?
2) You think you can make the LISP understand the X or Y based on the picked reference line?
Right now, your script works on perpendicular angles in either X or Y. This means if the reference line's two verts are located in 0,0 and 10,0 then we could calculate that the alignment should be X.
While if the verts would be 0,0 and 0,10 then we have Y alignment.
From the examples above, if both X values are the same then we have Y alignment and if both Y values are similar we have X alignment, right?
You could even put in a threshold value, so that if you have 0,0 and 0.1,10 it would still be considered as X. Or maybe not, then the user would know that the reference line isn't 100% perpendicular and need to act on it.
John F. Uhden
I have it renamed so just to see the tree place. The key letter is defined by & (ampersand).
Try this. It doesn't work (yet) with polylines, and it may not retain the line's original length, but it's a start.
But, you will find that lines can be at any angle.
(defun c:Realign ( / *error* cmd e1 e2 ent1 ent2 p1 p2 p3 p4 ang)
(gc)
(vl-load-com)
(prompt "\nREALIGN (c)2021, John F. Uhden for @Haider_of_Sweden")
(defun *error* (err)
(if (= (type cmd) 'INT)(setvar "cmdecho" cmd))
(vla-endundomark *doc*)
(cond
((not err))
((wcmatch (strcase err) "*CANCEL*,*QUIT*")
(vl-exit-with-error "\r ")
)
(1 (vl-exit-with-error (strcat "\r*ERROR*: " err)))
)
(princ)
)
;;-----------------------------------------------
;; Initialize some drawing and program variables:
;;
(setq *doc* (vlax-get (vlax-get-acad-object) 'Activedocument)
cmd (getvar "cmdecho")
)
(vla-endundomark *doc*)
(vla-startundomark *doc*)
(setvar "cmdecho" 0)
(command "_.expert" (getvar "expert")) ;; dummy command
(and
(setq e1 (car (entsel "\nSelect primary line: ")))
(setq ent1 (entget e1))
(or
(= (cdr (assoc 0 ent1)) "LINE")
(prompt "\nRntity selected is not a line.")
)
(setq p1 (cdr (assoc 10 ent1))
p2 (cdr (assoc 11 ent1))
)
(setq e2 (car (entsel "\nSelect line to realign to primary: ")))
(or (not (equal e1 e2))
(prompt " Same entity selected.")
)
(setq ent2 (entget e2))
(or
(= (cdr (assoc 0 ent2)) "LINE")
(prompt "\nRntity selected is not a line.")
)
(setq p3 (cdr (assoc 10 ent2))
p4 (cdr (assoc 11 ent2))
)
(setq ang (+ (angle p1 p2)(* pi 0.5)))
(setq p3 (inters p3 (polar p3 ang 10) p1 p2 nil)
p4 (inters p4 (polar p4 ang 10) p1 p2 nil)
)
(setq ent2 (subst (cons 10 p3)(assoc 10 ent2) ent2))
(setq ent2 (subst (cons 11 p4)(assoc 11 ent2) ent2))
(entmod ent2)
)
(*error* nil)
)
John F. Uhden
You are right you could look at segment selected and compare the 2 points making the segment x1,y1 and x2,y2 if y1 = y2 then yes its horizontal.
You have pt1 pt2 so as you say no need for question x or y just need to work it out. Your more than welcome to change the code.
I couldn't resist.
This version realigns lines with polyline segments as well (if not bulged).
Of course with a little more work it could realign with tangents to bulges or with text/mtext, etc.
(defun c:Realign ( / *error* cmd e1 e2 ent1 ent2 etype p obj param p1 p2 p3 p4 ang)
(gc)
(vl-load-com)
(prompt "\nREALIGN v1.1 (c)2021, John F. Uhden for @Haider_of_Sweden")
(defun *error* (err)
(if (= (type cmd) 'INT)(setvar "cmdecho" cmd))
(vla-endundomark *doc*)
(cond
((not err))
((wcmatch (strcase err) "*CANCEL*,*QUIT*")
(vl-exit-with-error "\r ")
)
(1 (vl-exit-with-error (strcat "\r*ERROR*: " err)))
)
(princ)
)
;;-----------------------------------------------
;; Initialize some drawing and program variables:
;;
(setq *doc* (vlax-get (vlax-get-acad-object) 'Activedocument)
cmd (getvar "cmdecho")
)
(vla-endundomark *doc*)
(vla-startundomark *doc*)
(setvar "cmdecho" 0)
(command "_.expert" (getvar "expert")) ;; dummy command
(and
(setq p (entsel "\nSelect source line segment: "))
(setq e1 (car p))
(setq ent1 (entget e1))
(setq etype (cdr (assoc 0 ent1)))
(cond
((= etype "LINE")
(setq p1 (cdr (assoc 10 ent1))
p2 (cdr (assoc 11 ent1))
)
)
((= etype "LWPOLYLINE")
(setq p (cadr p)
obj (vlax-ename->vla-object e1)
p (vlax-curve-getclosestpointto obj p)
param (vlax-curve-getparamatpoint obj p)
)
(if (not (zerop (vla-getbulge obj param)))
(prompt "\n Segment selected is bulged.")
(setq p1 (vlax-curve-getpointatparam obj (fix param))
p2 (vlax-curve-getpointatparam obj (1+ (fix param)))
)
)
)
(1 (prompt (strcat "\n Entity selected is a(n) " etype)))
)
(setq e2 (car (entsel "\nSelect line to realign to primary: ")))
(or (not (equal e1 e2))
(prompt "\n Same entity selected.")
)
(setq ent2 (entget e2))
(or
(= (cdr (assoc 0 ent2)) "LINE")
(prompt "\n Rntity selected is not a line.")
)
(setq p3 (cdr (assoc 10 ent2))
p4 (cdr (assoc 11 ent2))
)
(setq ang (+ (angle p1 p2)(* pi 0.5)))
(setq p3 (inters p3 (polar p3 ang 10) p1 p2 nil)
p4 (inters p4 (polar p4 ang 10) p1 p2 nil)
)
(setq ent2 (subst (cons 10 p3)(assoc 10 ent2) ent2))
(setq ent2 (subst (cons 11 p4)(assoc 11 ent2) ent2))
(entmod ent2)
)
(*error* nil)
)
John F. Uhden
Hi John just not sure am I doing something wrong running your code ? My code uses a pline segment and a point. I had to explode the original pline etc to get anything to work.
Went back and reread some posts by Haider
"If you take a look at this GIF I made, you will see what I am after. I only want to alter a sub-element, ie one segment of a polyline."
Select line to realign to primary:
Rntity selected is not a line.
You are correct for angled plines which is correct pt, X Y or is it actually the extension of the destination pline direction. I think we need Haider to confirm.
Can't find what you're looking for? Ask the community or share your knowledge.