normal intersection

normal intersection

scott_bolton
Advocate Advocate
891 Views
28 Replies
Message 1 of 29

normal intersection

scott_bolton
Advocate
Advocate

I'd like to find the intersection of a normal FROM a reference line and not TO a secondary line. (vlax-curve-getClosestPointTo ent2 pt) returns the normal to obj2 from pt (grey in the attached image), but I want the red coordinate.
I could offset a line from ent1 then use (vlax-curve-getClosestPointTo ent3 pt) to create the dashed red line then use (vla-IntersectWith ent2 ent4) but if there's a quicker method I'd rather use that.

0 Likes
Accepted solutions (1)
892 Views
28 Replies
Replies (28)
Message 2 of 29

ВeekeeCZ
Consultant
Consultant

Both are actual LINEs? If so, use (inters) func.

Message 3 of 29

daniel_cadext
Advisor
Advisor

I think Lee Mac might have some helpers for vectors, Assuming the lines are planar, you will want to:

-  find the direction of the line as a vector, or angle so you can use polar point

-  create a ray or xline perpendicular to the line

- find the intersection of the ray/xline with your target line

 

proof of concept 

 

from pyrx import Db, Ed, Ge, Ap, Rx, Gs

@Ap.Command()
def doit():
    try:
        ps1, id1, pnt1 = Ed.Editor.entSel("\nSelect 1: ", Db.Curve.desc())
        ps2, id2, pnt2 = Ed.Editor.entSel("\nSelect 2: ", Db.Curve.desc())

        c1 = Db.Curve(id1)
        c2 = Db.Curve(id2)

        dir1 = c1.getPointAtParam(0) - c1.getPointAtParam(c1.getEndParam())
        geline = Ge.Line3d(pnt1, dir1.perpVector())

        gec2 = c2.getAcGeCurve()
        if not gec2.isKindOf(Ge.EntityId.kLinearEnt3d):
            raise RuntimeError("oof not a kLinearEnt3d")

        flag, inters = geline.intersectWith(Ge.LinearEnt3d.cast(gec2))
        if flag:
            Ed.Core.grDraw(pnt1, inters, 1, 0)
    except Exception as err:
        print(err)

 

inter.png

 

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
Message 4 of 29

komondormrex
Mentor
Mentor

try this one

(setq this_point (vlax-curve-getclosestpointtoprojection (car (entsel "\nPick ent2: ")) (getpoint "\nPick point pt: ") '(0 1 0)))
Message 5 of 29

pkenewell6347
Advocate
Advocate

If you are in 2D - here's the basic code example to do what you want for lines or polylines:

(defun c:foo (/ a e1 e2 et1 et2 ep1 ep2 g1 g2 n1 p1 p2 pp o1 o2 sp1 sp2)
   (if
     (and
        (setq g1 (entsel "\nSelect first line: "))
        (setq g2 (entsel "\nSelect second line: "))
     )
     (progn
         (setq p1  (cadr g1) p2 (cadr g2)
               e1  (car  g1) e2 (car  g2)
               et1 (cdr (assoc 0 (entget e1)))
               et2 (cdr (assoc 0 (entget e2)))
               o1  (vlax-ename->vla-object e1)
               o2  (vlax-ename->vla-object e2)
               p1  (vlax-curve-getclosestpointto o1 p1)
               p2  (vlax-curve-getclosestpointto o2 p2)
         )
         (cond
            ((= et1 "LINE")
               (setq sp1 (vlax-curve-Getstartpoint o1)
                     ep1 (vlax-curve-Getendpoint   o1) 
               )
            )
            ((= et1 "LWPOLYLINE")
               (setq pp  (vlax-curve-getparamatpoint o1 p1)
                     sp1 (vlax-curve-getpointatparam o1 (fix pp))
                     ep1 (vlax-curve-getpointatparam o1 (1+ (fix pp)))
               )
            )
         )
         (setq a1  (angle sp1 ep1)
               n1  (polar p1 (+ a1 (/ pi 2)) 1)
         )
         (cond
            ((= et2 "LINE")
               (setq sp2 (vlax-curve-Getstartpoint o2)
                     ep2 (vlax-curve-Getendpoint   o2) 
               )
            )
            ((= et2 "LWPOLYLINE")
               (setq pp  (vlax-curve-getparamatpoint o2 p2)
                     sp2 (vlax-curve-getpointatparam o2 (fix pp))
                     ep2 (vlax-curve-getpointatparam o2 (1+ (fix pp)))
               )
            )
         )
         (if (setq p3 (inters p1 n1 sp2 ep2 nil))
            (command "._line" "_non" p1 "_non" p3)
         )
      )
   )
)

  

Message 6 of 29

leeminardi
Mentor
Mentor

The following will work in 3D but assumes that the two line lie in the same plane.

(defun c:project2line ( / line1 line2 ent1 ent2 normal pt p1 p2 p3 p4 p5 p6 )
; projects a point from line 1 to line 2 
  (vl-load-com) ; Load Visual LISP extensions

  ; Prompt user to select the first line
  (setq line1 (entsel "\nSelect the first line: "))
  (if line1
    (progn
      (setq ent1 (car line1)) 
      (if (= (cdr (assoc 0 (entget ent1))) "LINE") ; Check if it's a LINE entity
        (progn
          (setq obj1 (vlax-ename->vla-object ent1)) ; Convert entity name to VLA-object
          (setq p1 (vlax-safearray->list (vlax-variant-value (vla-get-startpoint obj1))))
          (setq p2 (vlax-safearray->list (vlax-variant-value (vla-get-endpoint obj1))))
        )
        (princ "\nSelected entity is not a line.")
      )
    )
    (princ "\nNo first line selected.")
  )
  (setq line2 (entsel "\nSelect the second line: "))
  (if line2
    (progn
      (setq ent2 (car line2)) 
      (if (= (cdr (assoc 0 (entget ent2))) "LINE") ; Check if it's a LINE entity
        (progn
          (setq obj2 (vlax-ename->vla-object ent2)) ; Convert entity name to VLA-object
          (setq p3 (vlax-safearray->list (vlax-variant-value (vla-get-startpoint obj2))))
          (setq p4 (vlax-safearray->list (vlax-variant-value (vla-get-endpoint obj2))))
        )
        (princ "\nSelected entity is not a line.")
      )
    )
    (princ "\nNo second line selected.")
  )
(setq pt (getpoint "\nIdentify point on LINE 1 to project to LINE 2. "))
(setq normal (cross (mapcar '- p2 pt) (mapcar '- pt p3)))
  (setq p5 (mapcar '+ pt (cross normal (mapcar '- p2 pt))))
  (setq p6 (inters p3 p4 pt p5 nil))
  ;(command "_point" "_non" p6)
(command "_line" "_non" pt "_non" p6 "")
  (princ) 
)

;;; Compute the cross product of vectors a and b
(defun cross (a b / crs)
  (setq	crs (list
	      (- (* (nth 1 a) (nth 2 b))
		 (* (nth 1 b) (nth 2 a))
	      )
	      (- (* (nth 0 b) (nth 2 a))
		 (* (nth 0 a) (nth 2 b))
	      )
	      (- (* (nth 0 a) (nth 1 b))
		 (* (nth 0 b) (nth 1 a))
	      )
	    )				;end list
  )					;end setq c
)					;end cross

[Code edited 10/9/2025 to add line instead of point and change program name]

lee.minardi
Message 7 of 29

devitg
Advisor
Advisor

Please try it 

 

(defun c:vert-line (/ AUX-LINE G2 PT1 PT@G2 PTAUX XYZ@G2 )
    (setq pt1 (cadr (entsel "\nSelect Point at  first line: ")))
  (setq g2 (entsel "\nSelect second line: "))
  (setq ptaux (polar pt1 (* 0.5 pi) 10))
  (command "_line" pt1 ptaux "")
  (setq aux-line (entlast))
  (setq	pt@g2 (VLA-INTERSECTWITH
		(VLAX-ENAME->VLA-OBJECT aux-line)
		(VLAX-ENAME->VLA-OBJECT (car g2))
		1
	      ) ;_ end of VLA-INTERSECTWITH
  ) ;_ end of setq
  (Setq xyz@g2 (VLAX-SAFEARRAY->LIST (VLAX-VARIANT-VALUE pt@g2)))
  (command "line" pt1 xyz@g2 "")
  (command "erase" aux-line "")
) ;_ end of defun
Message 8 of 29

scott_bolton
Advocate
Advocate

They could be LINEs or LWPOLYLINEs with arc elements so inters won't work, and this is where a couple of the ones below fail.

0 Likes
Message 9 of 29

scott_bolton
Advocate
Advocate

This isn't LISP so is beyond me; I'm not able to see if it works or not.

Message 10 of 29

scott_bolton
Advocate
Advocate

This works for straight LINEs or LWPOLYLINEs but not if the LWP contains arcs.

0 Likes
Message 11 of 29

scott_bolton
Advocate
Advocate

This works for straight LINEs or LWPOLYLINEs but not if the LWP contains arcs.

0 Likes
Message 12 of 29

scott_bolton
Advocate
Advocate

I haven't checked this because I notice it creates a temporary LINE which I was hoping to avoid. However, it looks like this is the (only) way to do it.

0 Likes
Message 13 of 29

Kent1Cooper
Consultant
Consultant

If a temporary Line is to be drawn anyway, you could try my (pf) [= perpendicular from] function, >here<.  See the description at Message 6 of that topic, but don't use the code there -- use the code attached at the linked Message.

It works to draw something like a Line [or as a Move or Copy displacement, or...] perpendicular from anything with linearity -- Line, Polyline [including arc segments], Circle, Arc, Ellipse, Spline, Ray, or Xline.  If you use it to draw a Line, just draw it past the other object, and take the intersection.

Kent Cooper, AIA
Message 14 of 29

pkenewell6347
Advocate
Advocate

FWIW: Here's a new version of my code that uses a temp line and (vla-intersectwith):

(defun c:foo (/ a doc e1 e2 et1 ep1 g1 g2 n1 p1 p2 p3 pp o1 o2 sp1 spc)
   (vl-load-com)
   (setq doc (vla-get-activedocument (vlax-get-acad-object))
         spc (if (> (getvar "CVPORT") 1)(vla-get-modelspace doc)(vla-get-paperspace doc))
   )
   (if
     (and
        (setq g1 (entsel "\nSelect first line: "))
        (setq g2 (entsel "\nSelect second line: "))
     )
     (progn
         (setq p1  (cadr g1) p2 (cadr g2)
               e1  (car  g1) e2 (car  g2)
               et1 (cdr (assoc 0 (entget e1)))
               o1  (vlax-ename->vla-object e1)
               o2  (vlax-ename->vla-object e2)
               p1  (vlax-curve-getclosestpointto o1 p1)
         )
         (cond
            ((= et1 "LINE")
               (setq sp1 (vlax-curve-Getstartpoint o1)
                     ep1 (vlax-curve-Getendpoint   o1) 
               )
            )
            ((= et1 "LWPOLYLINE")
               (setq pp  (vlax-curve-getparamatpoint o1 p1)
                     sp1 (vlax-curve-getpointatparam o1 (fix pp))
                     ep1 (vlax-curve-getpointatparam o1 (1+ (fix pp)))
               )
            )
         )
         (setq a1 (angle sp1 ep1)
               n1 (polar p1 (+ a1 (/ pi 2)) 1)
               o3 (vla-addline spc (vlax-3d-point p1) (vlax-3d-point n1))
         )
         (if (setq p3 (vlax-safearray->list (vlax-variant-value (vla-intersectwith o3 o2 acExtendBoth))))
            (vla-addline spc (vlax-3d-point p1) (vlax-3d-point p3))
         )
         (vla-delete o3)
      )
   )
)
Message 15 of 29

calderg1000
Mentor
Mentor

Try this code...

(defun c:Int(/)     
    (command
      "_point"
      "_non"
      (vlax-curve-getClosestPointToProjection (car (entsel "\nSelect Object 2"))
                                              (getpoint "\nPick Point: ")
                                              '(0 -1 0)
                                              t
      )
    )
     )

 


Carlos Calderon G
EESignature
>Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

0 Likes
Message 16 of 29

Moshe-A
Mentor
Mentor

@scott_bolton  hi,

 

here is my simple version , without the need of temporary line 😀

 

(MA:ginters ....) function, accepts 5 arguments, the first 4 represent the 4 points of the selected segments and the last is the picked reference point.

 

the geometric idea is to create a right angled (90) triangle on the first selected line (base) with one of the points on the second selected line, finding the direction of the face edge [ using (cos) function ] from the second point putting this vector on the picked reference point.

 

the (c:xxx) command is to show how to use the function - supports lines or\and pline segment.

 

enjoy

Moshe

 

;;;  MA:ginters
;;;  OCT 09, 2025 by Moshe-A

;;; Argumenst
;;; t0 - first  pt of first vector
;;; t1 - second pt of first vector
;;; t2 - first pt of second vector
;;; rpt  -  reference\picked point on first vector

(defun MA:ginters (t0 t1 t2 t3 rpt / right_angle 	       ; local function
		                     ang2 ang3 ax bx cx t4 t5) ; local variables

 ; triangle right angle
 (defun right_angle (/ a0 a1 a2)
  (setq a0 (angle t0 t1))
  (setq a1 (angle t0 t2))

  (if (> a1 a0)
   (setq a2 (- a1 a0))
   (setq a2 (- a0 a1))
  )
   
  (if (> a2 (* pi 1.5))
   (- (* pi 2) a2)
   a2
  ); if
 ); right_angle

  
 ; here start ginters
 (setq ang2 (right_angle))		; triangle right angle
  
 (setq cx (distance t0 t2))  		; triangle hypotenuse
 (setq bx (* cx (cos ang2)))		; near edge length

 (setq t4 (polar p0 (angle t0 t1) bx))
  
 (setq ax (distance t4 t2))		; face edge length
 (setq ang3 (angle t4 t2))		; face edge angle from t4
  
 (setq t5 (polar rpt ang3 ax))		; paralle vector from rpt

 (inters t2 t3 rpt t5 nil) 		; return intersection 
); MA:ginters


(vl-load-com) ; load avtivex support

(defun c:xxx (/ getVector ; local function
	        pick0 pick1 ename0 ename1 ptx0 ptx1) ; p0 p1 p2 p3 ptr PTM cr)

 ; ent - ename of selected object
 ; ppt - picked point
 (defun getVector (ent ppt Qt0 Qt1 / elist prm)
  (setq elist (entget ent))
    
  (cond
   ((eq (cdr (assoc '0 elist)) "LINE")
    (set Qt0 (vlax-curve-getStartPoint ent)) ; indirect set
    (set Qt1 (vlax-curve-getEndPoint ent))   ; indirect set
   ); case
   ((eq (cdr (assoc '0 elist)) "LWPOLYLINE")
    (setq prm (vlax-curve-getParamAtPoint ent ppt))
    (set Qt0 (vlax-curve-getPointAtParam ent (fix prm)))	; indirect set
    (set Qt1 (vlax-curve-getPointAtParam ent (1+ (fix prm))))	; indirect set
   ); case
   ( t
    (vlr-beep-reaction)
    (prompt "\n* Require line or pline *")
   )
  ); cond
 ); getVector


 ; here start c:xxx
  
 (if (and
       (setq pick0 (entsel "\nPick first line: "))
       (setq ename0 (car pick0))
       (setq ptx0 (vlax-curve-getClosestPointToProjection ename0 (cadr pick0) '(0.0 0.0 1.0)))
       (getVector ename0 ptx0 'p0 'p1)
	
       (setq pick1 (entsel "\nPick second line: "))
       (setq ename1 (car pick1))
       (setq ptx1 (vlax-curve-getClosestPointToProjection ename1 (cadr pick1) '(0.0 0.0 1.0)))
       (getVector ename1 ptx1 'p2 'p3)
	
       (setq ptr (getpoint "\nSpecify reference point on first line: "))
       (setq PTM (osnap ptr "_Nearest"))
     )
  (if (not (equal (+ (distance p0 PTM) (distance PTM p1)) (distance p0 p1) 1e-4))
   (progn
    (vlr-beep-reaction)
    (prompt "\n* Reference point is not found on first line *")
   ); progn
   ; else
   (if (setq cr (MA:ginters p0 p1 p2 p3 PTM))
    (command ".line" PTM cr "")
   )
  ); if
 ); if
  
 (princ)
); c:xxx

 

Message 17 of 29

pkenewell6347
Advocate
Advocate

This will only get the projected point if the first line is horizontal. However - I see where you are thinking. you just have to get the normal correctly from the first line segment.

Message 18 of 29

pkenewell6347
Advocate
Advocate

Based on @calderg1000 idea, This does not use a temporary line:

 

EDIT: Updated to ignore if there is not an intersect at the normal.

(defun c:foo (/ _getunitvect a e1 e2 et1 ep1 g1 g2 n1 p1 p2 p3 pp o1 o2 sp1)
   
   (defun _getunitvect (p1 p2 / v)
      (setq v (mapcar '- p1 p2))
      (
        (lambda (l)(if (/= 0 l) (mapcar (function (lambda (x) (/ x l))) v)))
        (distance '(0 0 0) v)
      )
   )
   
   (vl-load-com)

   (if
     (and
        (setq g1 (entsel "\nSelect first line: "))
        (setq g2 (entsel "\nSelect second line: "))
     )
     (progn
         (setq p1  (cadr g1)
               e1  (car  g1)
               e2  (car  g2)
               et1 (cdr (assoc 0 (entget e1)))
               o1  (vlax-ename->vla-object e1)
               o2  (vlax-ename->vla-object e2)
               p1  (vlax-curve-getclosestpointto o1 p1)
         )
         (cond
            ((= et1 "LINE")
               (setq sp1 (vlax-curve-Getstartpoint o1)
                     ep1 (vlax-curve-Getendpoint   o1) 
               )
            )
            ((= et1 "LWPOLYLINE")
               (setq pp  (vlax-curve-getparamatpoint o1 p1)
                     sp1 (vlax-curve-getpointatparam o1 (fix pp))
                     ep1 (vlax-curve-getpointatparam o1 (1+ (fix pp)))
               )
            )
         )
         (setq a1 (angle sp1 ep1)
               n1 (_getunitvect p1 (polar p1 (- a1 (/ pi 2)) 1))
         )
         (if
            (and 
               (setq p2 (vlax-curve-getClosestPointToProjection o2 p1 n1 T))
               (equal
                  n1
                  (_getunitvect p1 p2)
                  0.0001
               )
            )
            (command "._line" "non" p1 "_non" p2 "")
         )
      )
   )
)

 

Message 19 of 29

ВeekeeCZ
Consultant
Consultant

Projection...

 

(defun c:foo nil
  
  (setq e1 (Car (entsel "1: ")))
  (setq e2 (car (entsel "2: ")))
  (setq p (getpoint "point on 1: "))
  
  (setq d (vlax-curve-getfirstderiv e1 (vlax-curve-getparamatpoint e1 p)))
  (setq n (list (- (cadr d)) (car d) 0))
  
  (command "_point" "_non" (vlax-curve-getclosestpointtoprojection e2 p n t))
  
  )

 

Message 20 of 29

pkenewell6347
Advocate
Advocate

That works, However - I've noticed that (vlax-curve-getclosestpointtoprojection) still returns a value even if the projection does not intersect (usually the start or end of the curve). Have to figure out a way to compare the normals and ignore if the the curve does not actually intersect.

I admit - I'm not good with how derivatives work.

0 Likes