Move the two circles to the line endpoints and trim the line in side the circle.

Move the two circles to the line endpoints and trim the line in side the circle.

arpansark0544TCX
Advocate Advocate
1,862 Views
22 Replies
Message 1 of 23

Move the two circles to the line endpoints and trim the line in side the circle.

arpansark0544TCX
Advocate
Advocate

Dear All, 

 

I am working on a project where I have to move a cluster of two circles to the endpoints of the line which is exactly 3 units (mtr lets say ) long. 

 

I tried to code a basic code which is attached below.

 

Kindly help me to do the following things.

1. the user can select two circles and the line (which can be at any angle).

2. As mention the line is 3 mtr long and the center of the circle should be at the endpoints of the line.

3. After the center are placed at the end points the user shall have option to trim/delete the line coming inside the circle.

 

I have posted the snap of  the after and before the code is applied.

I have also attached the DWG file for the same.

The dimension is for illustration only. there is no need for the dimension.

 

Kindly help me to rewrite the code attached.

 

Very much thank you for your support.

 

 

 

 

 

arpansark0544TCX_0-1722085991947.pngarpansark0544TCX_1-1722086004198.png

 

 

0 Likes
1,863 Views
22 Replies
Replies (22)
Message 2 of 23

john.uhden
Mentor
Mentor

@arpansark0544TCX ,

Do you think there is not a solution for the bottom right condition?

You said nearest endpoint.  That's no big deal.

John F. Uhden

0 Likes
Message 3 of 23

marko_ribar
Advisor
Advisor
Accepted solution

Try this mod. of your code...

 

(defun c:MoveAllCirclesToNearestLineEnd ( / circles lines circle line center p1 p2 dist1 dist2 closest-point min-dist closest-line pp )

  (or (not (vl-catch-all-error-p (vl-catch-all-apply (function vlax-get-acad-object) nil))) (vl-load-com))

  ;; Function to calculate the distance between two points
  (defun distance ( pt1 pt2 )
    (sqrt (+ (expt (- (car pt2) (car pt1)) 2)
             (expt (- (cadr pt2) (cadr pt1)) 2))))

  (prompt "\nSelect circles you wish to apply this moving to closest lines ends with trimming...")
  ;; Get all circles in the drawing
  (setq circles (ssget "_:L" '((0 . "CIRCLE"))))

  ;; Get all lines in the drawing
  (setq lines (ssget "X" '((0 . "LINE"))))

  ;; Check if circles and lines are found
  (if (and circles lines)
    (progn
      ;; Iterate through each circle
      (setq i 0)
      (repeat (sslength circles)
        (setq circle (ssname circles i))
        (setq center (cdr (assoc 10 (entget circle))))

        ;; Initialize minimum distance as a large number
        (setq min-dist 1e+99)

        ;; Iterate through each line to find the nearest endpoint
        (setq j 0)
        (repeat (sslength lines)
          (setq line (ssname lines j))
          (setq p1 (cdr (assoc 10 (entget line))))
          (setq p2 (cdr (assoc 11 (entget line))))

          ;; Calculate distances from the circle's center to each endpoint
          (setq dist1 (distance center p1))
          (setq dist2 (distance center p2))

          ;; Check if this line's endpoint is closer than the current closest
          (if (< dist1 min-dist)
            (progn
              (setq min-dist dist1)
              (setq closest-point p1)
            )
          )

          (if (< dist2 min-dist)
            (progn
              (setq min-dist dist2)
              (setq closest-point p2)
            )
          )

          (setq j (1+ j))
        )

        ;; Move the circle to the nearest endpoint of the nearest line
        (command "MOVE" circle "" center closest-point)

        ;; Find intersection between moved circle and line
        (if (ssget "_C" closest-point closest-point)
          (setq pp (vlax-invoke (vlax-ename->vla-object circle) (quote intersectwith) (vlax-ename->vla-object (setq line (ssname (ssget "_C" closest-point closest-point) 0))) acextendnone))
        )

        ;; Modify line to make it look like it was trimmed with circle
        (if line
          (entupd (cdr (assoc -1 (entmod (subst (cond ( (equal (cdr (assoc 10 (entget line))) closest-point 1e-6) (cons 10 pp) ) ( t (cons 11 pp) )) (cond ( (equal (cdr (assoc 10 (entget line))) closest-point 1e-6) (assoc 10 (entget line)) ) ( t (assoc 11 (entget line)) )) (entget line))))))
        )

        (setq i (1+ i))
      )
    )
    (princ "\nNo circles or lines found.")
  )
  (princ)
)

Minimally tested...

HTH.

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 4 of 23

Kent1Cooper
Consultant
Consultant
Accepted solution

Would the drawings always be "clean" like your image, without any other things that have ENDpoints closer to the centers of the Circles than the Line ENDpoints are?  If so, you don't need to select the Lines at all, and you can still do as many Circles as you want at once -- just Move each Circle to put its center at the nearest ENDpoint on anything.  In simplest terms:

 

(defun c:MCNE (/ aper cirs cir ctr endp)
  (setq aper (getvar 'aperture)); Osnap box
  (setvar 'aperture (* aper 3)); really big
  (if (setq cirs (ssget '((0 . "CIRCLE"))))
    (repeat (setq n (sslength cirs)); then
      (setq
        cir (ssname cirs (setq n (1- n)))
        ctr (cdr (assoc 10 (entget cir)))
        endp (osnap ctr "_end")
      ); setq
      (command
        "_.move" cir "" "_non" ctr "_non" endp
        "_.trim" cir "" endp ""
      ); command
    ); repeat
  ); if
  (setvar 'aperture aper); reset
  (prin1)
)

 

Of course it would get *error* handling added to ensure resetting the APERTURE System Variable.

 

Or does your "exactly 3 units long" mean that you want any selected Line(s) to be tested for being that length?  If, as you ask, the User should select the two Circles and the Line, is that supposed to allow selection of Lines of any length?

 

[Oddly, I first tried the command without the 'endp' variable, with the Osnapping to that built into the end of the Move command, but for some reason it won't accept being given it in that way.]

Kent Cooper, AIA
Message 5 of 23

Sea-Haven
Mentor
Mentor
Accepted solution

Not sure why you are drawing circles then moving try this. Do something similar for drainage pits.

 

 

 

; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/move-the-two-circles-to-the-line-endpoints-and-trim-the-line-in/td-p/12923337
  
(defun c:circend ( / rad dist oldsnap oldlay obj start end )

(setq rad 0.125)
(setq dist 1.5)

(setq oldsnap (getvar 'osmode))
(setvar 'osmode 0)
(setq oldlay (getvar 'clayer))

(setq obj (vlax-ename->vla-object (car (entsel "\nPick a line "))))

(setq start (vlax-curve-getstartPoint obj))
(setq end (vlax-curve-getEndPoint obj))
(setq ang (angle start end))
(setq mp (mapcar '* (mapcar '+ start end) '(0.5 0.5)))

(setq end (polar mp ang (- dist rad)))
(setq end (list (car end)(cadr end) 0.0))
(setq start (polar mp (+ ang pi)(- dist rad)))
(setq start (list (car start)(cadr start) 0.0))

(vlax-put obj 'startpoint start)
(vlax-put obj 'endpoint end)

(setvar 'clayer "EE-STS-EE-EARTHING-PIT")

(command "circle" (polar mp ang dist) rad)
(command "circle" (polar mp (+ pi ang) dist) rad)

(setvar 'osmode oldsnap)
(setvar 'clayer oldlay)
(princ)

)

 

 

 

0 Likes
Message 6 of 23

daniel_cadext
Advisor
Advisor

can I play?

 

 

from pyrx_imp import Ap, Db, Ed, Ge, Gi, Gs, Rx
from timeit import default_timer as timer
from pykdtree.kdtree import KDTree
import math;
import numpy

def PyRxCmd_doit():
    try:
        err, ss = Ed.Editor.select([(0, "CIRCLE,LINE")])
        start = timer()
        circles = [Db.Circle(id, Db.OpenMode.kForWrite) for id in ss.objectIds(Db.Circle.desc())]
        lines = [Db.Line(id, Db.OpenMode.kForWrite) for id in ss.objectIds(Db.Line.desc())]
        lpnts = [l.startPoint().toTuple() for l in lines] + [l.endPoint().toTuple() for l in lines]
        lmap = dict(zip(lpnts, lines + lines))
        tree = KDTree(numpy.array(lpnts))
        for circle in circles:
            dist, ind = tree.query(numpy.array([circle.center().toTuple()]), k=1)
            if len(ind):
                line = lmap[lpnts[ind[0]]]
                lp = (line.startPoint(), line.endPoint())
                vec = (lp[1]-lp[0])
                gepnt = Ge.Point3d(lpnts[ind[0]])
                if vec.length() <= (3 + 1e-05) - circle.diameter(): #warning magic
                    if gepnt == lp[0]:
                        circle.setCenter(gepnt - (vec.normalize() * circle.radius()))
                    else:
                        circle.setCenter(gepnt + (vec.normalize() * circle.radius()))
                else:
                    circle.setCenter(gepnt)
                    if gepnt == lp[0]:
                        line.setStartPoint(lp[0] + (vec.normalize() * circle.radius()))
                    else:
                        line.setEndPoint(lp[1] - (vec.normalize() * circle.radius()))             
        print("Moved {} circles, in {} Seconds".format(len(circles), (timer() - start)))
    except Exception as err:
        print(err)

 

 

Select objects:
items=36608, Seconds=2.1407388000006904

 

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 7 of 23

john.uhden
Mentor
Mentor

@Kent1Cooper ,

As I have recently learned, be careful about using the TRIM (and BREAK) commands.  Older versions of AutoCAD would not "find" points if they were offscreen (outside the view).  @beekee_CZ mentioned a newer setvar, something like "OFFSCREENDETECTION" that allegedly solves that problem.

John F. Uhden

0 Likes
Message 8 of 23

CADaSchtroumpf
Advisor
Advisor
Accepted solution

My quickly way:

(defun C:FOO ( / ss ss_c n ent dxf_ent dxf_10 dxf_11 l p_mid)
  (setq ss (ssget "_X" '((0 . "LINE") (8  . "EE-STS--EXTERNAL EARTHING") (62 . 82))))
  (cond
    (ss
      (setq ss_c (ssget "_X" '((0 . "CIRCLE") (8 . "EE-STS-EE-EARTHING-PIT") (62 . 240) (40 . 0.125))))
      (repeat (setq n (sslength ss_c))
        (entdel (ssname ss_c (setq n (1- n))))
      )
      (repeat (setq n (sslength ss))
        (setq
          ent (ssname ss (setq n (1- n)))
          dxf_ent (entget ent)
          dxf_10 (cdr (assoc 10 dxf_ent))
          dxf_11 (cdr (assoc 11 dxf_ent))
          l (distance dxf_10 dxf_11)
        )
        (cond
          ((equal l 3.00 1E-13)
            (setq
              p_mid (mapcar '* (mapcar '+ dxf_10 dxf_11) '(0.5 0.5 0.5))
            )
            (setq
              dxf_ent
              (entmod
                (subst
                  (cons 10 (polar p_mid (angle p_mid dxf_10) (* 0.5 2.75)))
                  (assoc 10 dxf_ent)
                  dxf_ent
                )
              )
              dxf_ent
              (entmod
                (subst
                  (cons 11 (polar p_mid (angle p_mid dxf_11) (* 0.5 2.75)))
                  (assoc 11 dxf_ent)
                  dxf_ent
                )
              )
            )
            (mapcar
              '(lambda (x)
                (entmake
                  (list
                    '(0 . "CIRCLE")
                    '(67 . 0)
                    '(8 . "EE-ST-EE-EARTHING-PIT")
                    '(62 . 240)
                    (cons 10 x)
                    '(40 . 0.125)
                  )
                )
              )
              (list dxf_10 dxf_11)
            )
          )
        )
      )
    )
  )
  (prin1)
)
Message 9 of 23

john.uhden
Mentor
Mentor

@arpansark0544TCX ,

After continuing to enjoy this topic and all the work put into it, it has reminded me of a situation long long ago when I was the CAD Manager for Birdsall (now defunct).  Birdsall had their own survey staff, but they bought up another nearby company, LGA, who also had a team of very good surveyors (and who were getting paid at a cheaper rate).

There became a rift between the two offices when the LGA guys would add circles at property corners to help visually recognize the boundaries, but then they would trim the lines/polylines within the circles so that it would look clean.  But when the Birdsall guys got LGA's drawings and started labeling the lines/arcs, the distances came up short.  So I intervened and came up with a solution...

I made a little block named "hole" consisting of a circle with a wipeout inside it.  When they inserted the holes at the property corners, the wipeout hid the endpoints without trimming the linework.  TA DA!

So, if the lengths of your lines are important, then I suggest using that trick.

John F. Uhden

0 Likes
Message 10 of 23

arpansark0544TCX
Advocate
Advocate

I can't thank you enough this is working like magic. Thank you very much for your time and support.

But I would like to add a point that every time we select the same arrangement the circle rearrange itself and the line gets shorter .

 

As mentioned can we not make the distance between the circles centers be constant 3 units.

arpansark0544TCX_0-1722229971806.png

 

0 Likes
Message 11 of 23

arpansark0544TCX
Advocate
Advocate

It worked but it deleted the circles which were already placed in place.

arpansark0544TCX_0-1722229736043.png

 

0 Likes
Message 12 of 23

arpansark0544TCX
Advocate
Advocate
getting error {{Error : AutoCAD.Application: Incorrect number of elements in SafeArray}}
0 Likes
Message 13 of 23

arpansark0544TCX
Advocate
Advocate
Dear Sir how to load your code. Can you let me know as I don't know python.
0 Likes
Message 14 of 23

arpansark0544TCX
Advocate
Advocate

While working on the drawing and applying the lisp code there were some instances where the code did not work and the circle got the value of z not equal to 0.   Attached is a video of the same.

 

0 Likes
Message 15 of 23

CADaSchtroumpf
Advisor
Advisor
Accepted solution

@arpansark0544TCX  a écrit :

It worked but it deleted the circles which were already placed in place.

 


And with this modications?

(defun C:FOO ( / ss ss_c n ent dxf_ent dxf_10 dxf_11 l p_mid)
  (setq ss (ssget "_X" '((0 . "LINE") (8  . "EE-STS--EXTERNAL EARTHING") (62 . 82))))
  (cond
    (ss
      (setq ss_c (ssget "_X" '((0 . "CIRCLE") (8 . "EE-STS-EE-EARTHING-PIT") (62 . 240) (40 . 0.125))))
      (repeat (setq n (sslength ss_c))
        (entdel (ssname ss_c (setq n (1- n))))
      )
      (repeat (setq n (sslength ss))
        (setq
          ent (ssname ss (setq n (1- n)))
          dxf_ent (entget ent)
          dxf_10 (cdr (assoc 10 dxf_ent))
          dxf_11 (cdr (assoc 11 dxf_ent))
          l (distance dxf_10 dxf_11)
        )
        (cond
          ((or (equal l 3.00 1E-13) (equal l 2.75 1E-13))
            (setq
              p_mid (mapcar '* (mapcar '+ dxf_10 dxf_11) '(0.5 0.5 0.5))
            )
            (setq
              dxf_ent
              (entmod
                (subst
                  (cons 10 (polar p_mid (angle p_mid dxf_10) (* 0.5 2.75)))
                  (assoc 10 dxf_ent)
                  dxf_ent
                )
              )
              dxf_ent
              (entmod
                (subst
                  (cons 11 (polar p_mid (angle p_mid dxf_11) (* 0.5 2.75)))
                  (assoc 11 dxf_ent)
                  dxf_ent
                )
              )
            )
            (mapcar
              '(lambda (x)
                (entmake
                  (list
                    '(0 . "CIRCLE")
                    '(67 . 0)
                    '(8 . "EE-ST-EE-EARTHING-PIT")
                    '(62 . 240)
                    (if (equal l 3.00 1E-13)
                      (cons 10 x)
                      (cons 10 (polar x (angle p_mid x) 0.125))
                    )
                    '(40 . 0.125)
                  )
                )
              )
              (list dxf_10 dxf_11)
            )
          )
        )
      )
    )
  )
  (prin1)
)
0 Likes
Message 16 of 23

marko_ribar
Advisor
Advisor

And what if you change this line :

        ;; Move the circle to the nearest endpoint of the nearest line
        (command "MOVE" circle "" center closest-point)

To this :

        ;; Move the circle to the nearest endpoint of the nearest line
        (command "MOVE" circle "" "_non" center "_non" closest-point)

 

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 17 of 23

ec-cad
Collaborator
Collaborator

In your MoveAllCirclesToNearestLineEnd.lsp, add the following to trim those lines to the circles.

 

        ;; Move the circle to the nearest endpoint of the nearest line
        (command "MOVE" circle "" center closest-point)
        ;; Added to trim lines at center of circle          <add this line
          (command "_trim" circle "" closest-point "");     <and this line

 

The remaining code is OK. Maybe a bit 'longwindedtocall' but works.

 

 

ECCAD 

Message 18 of 23

Sea-Haven
Mentor
Mentor

Its working for me. It makes the Circles so just pick any line at any angle. 

 

I have seen that error  {{Error : AutoCAD.Application: Incorrect number of elements in SafeArray}} in another post message it may be I am using Bricscad and it accepts a pointa as X,Y but Acad really needs X,Y,Z for point. I have updated the code above so the end and start points are now X,Y,Z. Can you please try it. Tested with X,Y,Z and works.

0 Likes
Message 19 of 23

arpansark0544TCX
Advocate
Advocate
let me check it out.
0 Likes
Message 20 of 23

arpansark0544TCX
Advocate
Advocate
Will run and update on that.
0 Likes