Fillet two edges of polyline the closest to a clicked point

Fillet two edges of polyline the closest to a clicked point

T2ioTD
Enthusiast Enthusiast
1,834 Views
15 Replies
Message 1 of 16

Fillet two edges of polyline the closest to a clicked point

T2ioTD
Enthusiast
Enthusiast

Dir All,

 

I have found that tracing images with polylines can be greatly made easier if one first made polylines with hard corners, then (with a proper lisp) he can just click a point near two edges and the routine would fillet them to the radius of a circle passing by (or close?) to the point being clicked.

 

The polyline would be the one last drawn (or chosen).

 

Is this possible?

 

0 Likes
1,835 Views
15 Replies
Replies (15)
Message 2 of 16

Kent1Cooper
Consultant
Consultant

@T2ioTD wrote:

.... if one first made polylines with hard corners, then (with a proper lisp) he can just click a point near two edges and the routine would fillet them to the radius of a circle passing by (or close?) to the point being clicked.

....


FilletPlineVert.lsp with its FPV command, available here, may work for you.  You pick at a vertex, or at the midpoint of an intermediate segment [such as to re-Fillet a corner at a different radius] -- it sets END and MID Osnap modes for you to make that easy.  It works on any Polyline, not just the latest-drawn.  It's not clear to me whether your idea is that the Circle for the radius is already drawn, but FPV doesn't involve that.  I suppose it could be altered to ask you to pick a Circle or Arc from which to get the radius, if you really want it to work that way.  See additional comments at the top of and within the file.

Kent Cooper, AIA
0 Likes
Message 3 of 16

T2ioTD
Enthusiast
Enthusiast

Thanks Kent Cooper

I will check the lisp you are talking about, and will try to modify it a little bit. This will be take a week or so, since i am slow at lisp... but will post my trials so I hope you will help me then trying to make it closer to my needs.

Just to explain it better, the radius of the fillet would be calculated by the lisp as the distance from clicked point to the closest vertex of the polyline.

0 Likes
Message 4 of 16

Kent1Cooper
Consultant
Consultant

@T2ioTD wrote:

.... the radius of the fillet would be calculated by the lisp as the distance from clicked point to the closest vertex of the polyline.


I'd be interested in an illustration, since it seems to me the results can be so different depending on the geometry [red are the clicked points]:

FilletCorner.PNG

Kent Cooper, AIA
0 Likes
Message 5 of 16

john.uhden
Mentor
Mentor

As to the radius, what you said would make sense if the vertex angle were about 90° because the point of tangency would fall near the pick point.  But if the angle were very obtuse, the the point of tangency would be much closer to the vertex.  What happened to using the radius of the nearby circle?  Presuming the circle would have to be drawn, wouldn't it be easier to just enter the desired radius?  Then again, if you want to base the radius on the pick point, then how about your picking it at about where you think the point of tangency should be?  That way we can just get the tangent value as the distance from the vertex to the pick and easily compute the radius, the actual tangent points (which become new vertices) and the bulge, so we can add/move the vertices with VLisp and no (command)s.  It's all in my head right now.

John F. Uhden

0 Likes
Message 6 of 16

steve_carson
Enthusiast
Enthusiast

Another complexity to consider is when you have a relatively large radius and short straight segment. The picked point will actually be closer to the wrong vertex. So you either need to pick two points (one on either side of the vertex to be filleted), or take the direction of the pline into consideration and always pick in front of (or behind) the vertex.

0 Likes
Message 7 of 16

john.uhden
Mentor
Mentor

Very good point!  Sounds more as though he should just enter the radius.  At least we can make the previous value the default.  Plus, that way he can pick anywhere near the target vertex.  It's a good thing that Agent007 isn't involved.  He would want to be picking a point near the starting (same as ending) vertex of an "apparently" closed polyline.  I have no doubt we could do it, but it would be a PITA.

John F. Uhden

0 Likes
Message 8 of 16

stevor
Collaborator
Collaborator

If passed the entity Name and the selected tangent point,

and attached, maybe.

 

[code]

 ; PL-OppoTanPt-.lsp Auscadd.com
 
 (princ"\n Subrs: ")
  (Defun U_W (p) (if (pnt_p p) (trans p 1 0)))
  (Defun W_U (p) (if (pnt_p p) (trans p 0 1)))
  (Defun Cu_NatP (Ob p) (vLax-curve-getParamAtPoint Ob (u_w p)) )
  (Defun CU_PatD (EN D) (W_U (vlax-curve-getpointatdist EN D )))
  (Defun C_DistN (Ob N) (vlax-curve-getDistAtParam ob N))
  (Defun CU_PatN (Ob N) (w_u (vLax-curve-getPointAtParam Ob N)) )
  (defun C_EndN (Ob) (vlax-curve-getEndParam ob ) )
  (defun C_StaN (Ob) (vlax-curve-getStartParam ob ) )
  ; cleaner exit, at last.
  (Defun Exit_s (S) (prin1 (vl-exit-with-value (princ S))) )
 
 ; geometry to closer vertex and equal distance on opposite segment
 (Defun PL_OppoTP (EN PT1 / RL  )
  ; Auscadd.com CU_PatN CU_PatD C_DistN  Exit_S C_EndN C_StaN DXF_
  (and (setq EDL (entget EN)) (setq Lyn (DXF_ 8 EDL))
       (OR (= (logand 4 (DXF_ 70 (tblsearch "layer" LYN))) 0)
           (and (princ (strcat "\n Locked Layer: " LYN)) nil) )   
       (or (not (assoc 75 EDL)) (= (DXF_ 75 EDL) 0)
           (and (princ"\n 75 ! " nil) ) ); not Fit-/Spline-curved
       ; Param at Tan Pt1
       (setq NPT1 (Cu_NatP EN PT1) )  ; has curve info, or not near
       ; Curve Distances
       (setq DPT1 (C_DistN EN NPT1))
       (setq Nv- (fix NPT1)   DV- (C_DistN EN Nv-)  D- (- DPT1 DV- )
             Nv+ (1+ Nv-)     DV+ (C_DistN EN Nv+)  D+ (- DV+ DPT1 )
             DNV (if (> D+ D-) DV- DV+) ) ; Near Vert Dist  
       ;
       (setq NNV (if (> D+ D-) Nv- Nv+)  ; lesser dist to PT1
             PNV (CU_PatN EN NNV) ) ; near Vert Param, Pt
       ; Error trap for anY end as the nearer
       (or (and (/= (C_EndN EN) NNV) (/= (C_StaN EN) NNV))
           (progn (princ "\n An End is the Closer ") nil))
       (setq NVT2 (if (> D+ D-) (1- Nv-) (1+ Nv+))) ; param far oppo Leg
       (setq PTV2 (CU_PatN EN NVT2) ) ; vert pt of Leg 2  
       ; Distance to Tan Pt 2 = Dist to Near Vert +/- Dist of Tan Pt 1  
       (setq DDN1 (if (> D+ D-) (- DPT1 DNV) (- DNV DPT1)) ) ; PT1 to PNV  
       ; ?? Error trap for 2nd leg over 2nd vert pt
       (setq DVT2 (C_DistN EN NVT2)   DDN2 (abs (- DVT2 DNV)))  
       (or (> DDN2 DDN1)
           (progn (princ "\n Leg 2 longer than Segment ") nil) )
       ; Results
       (setq DPT2 (if (> D+ D-) (- DNV DDN1) (+ DNV DDN1)) ) ; to TP2
       (setq PT2 (CU_PatD EN DPT2)   RL (list PNV PT2 ))
    ) RL  ) ; def        
   
[/code]
S
0 Likes
Message 9 of 16

john.uhden
Mentor
Mentor

Um, I missed finding the pt_p function, though I think we know what it would be, like maybe...

(defun pt_p (p)
  (or
    (= (mapcar 'type p) '(REAL REAL REAL))
    (= (mapcar 'type p) '(REAL REAL))
  )
)

or (vl-every something)

John F. Uhden

0 Likes
Message 10 of 16

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

Um, I missed finding the pt_p function, though I think we know what it would be, like maybe...

(defun pt_p (p)
  (or
    (= (mapcar 'type p) '(REAL REAL REAL))
    (= (mapcar 'type p) '(REAL REAL))
  )
)
...

I think that needs two modifications:

 

The (=) function is for numbers and strings, not lists.  That should be (equal).

 

If the variable contains something entirely inappropriate, such as a string or entity name, that doesn't just return nil, but causes an error.

 

This gets around both of those:

 

(defun pt_p (p)
  (and
    (listp p)
    (or
      (equal (mapcar 'type p) '(REAL REAL REAL))
      (equal (mapcar 'type p) '(REAL REAL))
    ); or
  ); and
); defun
Kent Cooper, AIA
0 Likes
Message 11 of 16

CADaSchtroumpf
Advisor
Advisor

and the routine would fillet them to the radius of a circle passing by (or close?) to the point being clicked.

 

I have making this but only with lines, can be interese you?

(defun z_dir (p1 p2 / )
  (trans
    '(0.0 1.0 0.0)
    (mapcar
      '(lambda (k)
        (/ k
          (sqrt
            (apply '+
              (mapcar
                '(lambda (x) (* x x))
                (mapcar '- p2 p1)
              )
            )
          )
        )
      )
      (mapcar '- p2 p1)
    )
    0
  )
)
(defun ang_x (px p1 p2 / l_pt l_d p ang)
  (setq
    l_pt (mapcar '(lambda (x) (list (car x) (cadr x) (caddr x))) (list px p1 p2))
    l_d (mapcar 'distance l_pt (append (cdr l_pt) (list (car l_pt))))
    p (/ (apply '+ l_d) 2.0)
    ang (* (atan (sqrt (/ (* (- p (car l_d)) (- p (caddr l_d))) (* p (- p (cadr l_d)))))) 2.0)
  )
)
(defun det_wh (px p1 p2 / v1 v2 det_or)
  (setq
    v1 (mapcar '- p2 p1)
    v2 (mapcar '- px p1)
  )
  (if (> (apply '(lambda (x1 y1 z1 x2 y2 z2) (- (* x1 y2) (* y1 x2))) (append v1 v2)) 0.0) 1 -1)
)
(defun k_th (p1 p2 c / k)
  (setq k (/ c (distance p1 p2)))
  (mapcar '+ (mapcar '* (mapcar '- p2 p1) (list k k k)) p1)
)
(defun c:my_filletrad ( / js1 js2 ps1 ps2 pt_lst pt l3 alpha chord p_o a1 a2) ; key_fillet
  (princ "\nSélectionnez la première ligne: ")
  (while (or (null (setq js1 (entsel))) (/= (cdr (assoc 0 (entget (car js1)))) "LINE")))
  (princ "\nSélectionnez la deuxième ligne: ")
  (while (or (null (setq js2 (entsel))) (/= (cdr (assoc 0 (entget (car js2)))) "LINE")))
  (setq
    ps1 (trans (cadr js1) 1 0)
    ps2 (trans (cadr js2) 1 0)
    js1 (entget (car js1))
    js2 (entget (car js2))
    pt_lst (list (cdr (assoc 10 js1)) (cdr (assoc 11 js1)) (cdr (assoc 10 js2)) (cdr (assoc 11 js2)))
    pt (inters (car pt_lst) (cadr pt_lst) (caddr pt_lst) (cadddr pt_lst) nil)
  )
  (cond
    (pt
      (if (eq (det_wh (cadddr pt_lst) (car pt_lst) (cadr pt_lst)) (det_wh (caddr pt_lst) (car pt_lst) (cadr pt_lst)))
        (if (> (distance pt (caddr pt_lst)) (distance pt (cadddr pt_lst)))
          (setq l3 (list pt (caddr pt_lst)))
          (setq l3 (list pt (cadddr pt_lst)))
        )
        (if (eq (det_wh ps2 (car pt_lst) (cadr pt_lst)) (det_wh (caddr pt_lst) (car pt_lst) (cadr pt_lst)))
          (setq l3 (list pt (caddr pt_lst)))
          (setq l3 (list pt (cadddr pt_lst)))
        )
      )
      (if (eq (det_wh (cadr pt_lst) (caddr pt_lst) (cadddr pt_lst)) (det_wh (car pt_lst) (caddr pt_lst) (cadddr pt_lst)))
        (if (> (distance pt (car pt_lst)) (distance pt (cadr pt_lst)))
          (setq l3 (cons (car pt_lst) l3))
          (setq l3 (cons (cadr pt_lst) l3))
        )
        (if (eq (det_wh ps1 (caddr pt_lst) (cadddr pt_lst)) (det_wh (car pt_lst) (caddr pt_lst) (cadddr pt_lst)))
          (setq l3 (cons (car pt_lst) l3))
          (setq l3 (cons (cadr pt_lst) l3))
        )
      )
;|      (initget 4)
      (setq key_fillet
        (getdist
          (strcat
            "\nSpécifiez le rayon du raccord <"
            (rtos (getvar "FILLETRAD"))
            ">:"
          )
        )
      )
      (if (null key_fillet) (setq key_fillet (getvar "FILLETRAD")) (setvar "FILLETRAD" key_fillet))|;
      (setvar "FILLETRAD"
        (distance
          (inters
            ps1
            (polar ps1 (+ (angle (car l3) (cadr l3)) (* pi 0.5)) (distance (cadr l3) ps1))
            (polar (cadr l3) (angle (cadr l3) (caddr l3)) (distance (cadr l3) ps1))
            (polar
              (polar (cadr l3) (angle (cadr l3) (caddr l3)) (distance (cadr l3) ps1))
              (+ (angle (cadr l3) (caddr l3)) (* pi 0.5))
              (distance (cadr l3) ps1)
            )
            nil
          )
          ps1
        )
      )
      (setq
        alpha (ang_x (cadr l3) (car l3) (caddr l3))
        l_tg (* (getvar "FILLETRAD") (/ 1.0 (/ (sin (* alpha 0.5)) (cos (* alpha 0.5)))))
        js1 (subst (cons 10 (car l3)) (assoc 10 js1) js1)
        js2 (subst (cons 10 (caddr l3)) (assoc 10 js2) js2)
      )
      (entmod ; mettre juste entmod en rem pour ne pas ajuster
        (setq js1 (subst (cons 11 (k_th pt (car l3) l_tg)) (assoc 11 js1) js1))
      )
      (entmod ; mettre juste entmod en rem pour ne pas ajuster
        (setq js2 (subst (cons 11 (k_th pt (caddr l3) l_tg))  (assoc 11 js2) js2))
      )
      (setq
        p_o (k_th pt (mapcar '* (mapcar '+ (cdr (assoc 11 js1)) (cdr (assoc 11 js2))) '(0.5 0.5 0.5)) (+ (getvar "FILLETRAD") (* (getvar "FILLETRAD") (1- (/ 1.0 (sin (* alpha 0.5)))))))
        a1 (angle p_o (cdr (assoc 11 js1)))
        a2 (angle p_o (cdr (assoc 11 js2)))
        dxf_210 (z_dir p_o (cdr (assoc 11 js1)))
      )
      (entmake
        (list
          (cons 0 "ARC")
          (cons 100 "AcDbEntity")
          (assoc 67 js1)
          (assoc 410 js1)
          (cons 8 (getvar "CLAYER"))
          (if (assoc 62 js1) (assoc 62 js1) (cons 62 256))
          (if (assoc 6 js1) (assoc 6 js1) (cons 6 "BYLAYER"))
          (cons 38 (+ (cadddr (assoc 10 js1)) (getvar "ELEVATION")))
          (cons 39 (getvar "THICKNESS"))
          (cons 100 "AcDbCircle")
          (cons 10 (trans p_o 0 dxf_210))
          (cons 40 (getvar "FILLETRAD"))
          (cons 210 dxf_210) 
          (cons 100 "AcDbArc")
          (cons 50
            (if (equal (* (+ (max a1 a2) (min a1 a2)) 0.5) (angle p_o pt) 1E-8)
              (min a1 a2)
              (max a1 a2)
            )
          )
          (cons 51
            (if (equal (* (+ (max a1 a2) (min a1 a2)) 0.5) (angle p_o pt) 1E-8)
              (max a1 a2)
              (min a1 a2)
            )
          )
        )
      )
    )
    (T
      (princ "\nNe peut raccorder des lignes dans des plans différents!")
    )
  )
  (prin1)
)

 NB: The first select point fixe the fillet radius.

0 Likes
Message 12 of 16

john.uhden
Mentor
Mentor

Thank you, @Kent1Cooper, for reminding me of the difference between = and equal.  We all need to remember that.

 

(vl-every 'numberp pt) would work too, but it could include inetgers.

 

(defun realp (#)(equal (type #) 'REAL)) could work too, as in

 

(vl-every 'realp pt)

which would work for both 2D and 3D points.

John F. Uhden

0 Likes
Message 13 of 16

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

.... 

(vl-every 'numberp pt) would work too, but it could include inetgers.

 

(defun realp (#)(equal (type #) 'REAL)) could work too, as in

 

(vl-every 'realp pt)

which would work for both 2D and 3D points.


Those would return T for a list containing only one number, or a list of twenty numbers -- they would need to be combined with testing for the length of the list if it's supposed to be a point [2 or 3 real numbers only].

Kent Cooper, AIA
0 Likes
Message 14 of 16

stevor
Collaborator
Collaborator

1. Yep, it was left out, same reason I lose at tennis.

2. Your method may be better.

3. Mine was;

 ; Point Proof  Ret: 3D list from 2D or 3D numbr list, or NIL
 (DEFUN Pnt_P (P)  (cond
   ((and P (ListP P) (= (Length P) 3) (vl-every 'numberP P)) P )
   ((and P (ListP P) (= (Length P) 2) (vl-every 'numberP P) )
      (list (car P) (cadr P) 0. ))  ) ) ; def

 

S
0 Likes
Message 15 of 16

Kent1Cooper
Consultant
Consultant

Every time I see a significant amount of code repeated, I figure there's probably a way to simplify.  Just playing around, here's one way, that uses those repeated tests only once, and does one test to determine whether it's either 2 or 3 numbers long:

 

(defun 3dp (p); returns XYZ list from XY or XYZ point list, otherwise nil

  (if (and p (listp p) (< 1 (length p) 4) (vl-every 'numberp p))

    (append p (nth (length p) '(0 0 (0.0) nil)))

  )

)

 

It doesn't process in a different way depending on whether it's a 2-coordinate or 3-coordinate point list.  That difference just determines whether what it appends is 0.0 [to make an XY list into an XYZ list] or nothing [to leave an XYZ list alone].  Come to think of it, that (append) line could be a little shorter:

 

    (append p (if (= (length p) 2) '(0.0) nil))

 

or shorter still, the version that does process in a different way, but has to repeat the p to feed it out if unappended:

 

    (if (= (length p) 2) (append p '(0.0)) p)

Kent Cooper, AIA
0 Likes
Message 16 of 16

john.uhden
Mentor
Mentor

Very nice, but did you intend to allow integers?

 

How about:

(defun pt_p (p D / realp)
  ;; where p is the point as a list '(X Y [Z])
  ;;       D is either 2 or 3 for 2D or 3D
  ;;       realp is a function
  (defun realp (#) (equal (type #) 'REAL))
  (and
(<= 2 D 3) (= (length p) D) (vl-every 'realp p) ) )

?

John F. Uhden

0 Likes