Given two lines, create two tangent arcs that connect them, with the connections beeing tangent too.

Given two lines, create two tangent arcs that connect them, with the connections beeing tangent too.

aitorm
Advocate Advocate
6,865 Views
54 Replies
Message 1 of 55

Given two lines, create two tangent arcs that connect them, with the connections beeing tangent too.

aitorm
Advocate
Advocate

Hi there!

 

I usually face this situation where I need to connect two lines with a curve and I don't like the blend command because it is not a polyline and I don't like to transform a spline to a polyline because it is not accurate and simple.

 

The best would be to create two curves, connected to each other and the two lines and that are tangent to each other and to the lines.

 

Do you think it is possible to create a Lisp for that? I couldn't even imagine how to start. Thank you so much

Accepted solutions (1)
6,866 Views
54 Replies
Replies (54)
Message 21 of 55

john.uhden
Mentor
Mentor

@leeminardi 

With that example, some day I'll learn too doo that voodoo that you doo so well.

[taken from the movie “The Manchurian Candidate” (1962)]

I think Harvey Korman (as Hedley Lamarr) or Gene Wilder (as The Waco Kid) used the line in "Blazing Saddles" (1974)

John F. Uhden

Message 22 of 55

leeminardi
Mentor
Mentor

Thanks @ВeekeeCZ .  I was in a hurry and didn't test it.  I've modified the code for the smooth factor.

lee.minardi
Message 23 of 55

Sea-Haven
Mentor
Mentor

A couple of comments the code by leeminardi is good, a simple select near end and can check is pick point near start or end point so no need for second pick .

(SETQ TP1 (entsel "\nSelect Line near end: ")) ; implies which way 
(setq tpp1 (entget (car tp1))
	p1 (cdr (assoc 10 tpp1))
	p2 (cdr (assoc 11 tpp1))
	p3 (cadr tp1)
)

(setq d1 (distance p1 p3) d2 (distance p2 p3) )

(if (> d1 d2)
    (setq temp p1 p1 p2 p2 temp)
)
	

If the arc radius are known which as a civil engineer I would expect, then the solution by CalderG1000 is the way to go. This will move end points. 

 

Maybe 2 methods are required.

Message 24 of 55

leeminardi
Mentor
Mentor

Thanks @Sea-Haven for the code to simplify the users interface.  Picking the line near the end was my initial goal but I did not know how to code it efficiently.   I've modified the code in my post #19 to add this feature.

 

I was under the impression that the OP did not want to modify the line end points.  I'll leave it to someone else to code that method.

lee.minardi
Message 25 of 55

Sea-Haven
Mentor
Mentor

That (< d1 d2) is from like 1990. Oh yeah can use on plines as well a bit more complicated. 

 

The request is really  like a few options, so dont get, please change next post, more a global answer. 

 

Guess 2 radii as per code.

Input rad1 slight adjustment to code say rad = 0 for auto answer

Enter rad1 rad2 new code.

Message 26 of 55

leeminardi
Mentor
Mentor

@Sea-Haven wrote:

That (< d1 d2) is from like 1990. Oh yeah can use on plines as well a bit more complicated. 

 

I agree.

 

The request is really  like a few options, so dont get, please change next post, more a global answer. 

 

It's not clear to me what you are asking or stating here.  "Dont get" what?

 

Guess 2 radii as per code.

Input rad1 slight adjustment to code say rad = 0 for auto answer

Enter rad1 rad2 new code.


 

lee.minardi
Message 27 of 55

ВeekeeCZ
Consultant
Consultant

(setq s (if (= s nil)
(setq s 4)
)
)

Still not right.

 

Besides, you should follow standard format conventions where <enter> option looks like that.

  (setq	s  (cond ((getreal "\nEnter smooth factor <4>: "))
		 (4)))

 

Message 28 of 55

Sea-Haven
Mentor
Mentor

Going back to original poster Aitorm just asked connect 2 arcs, provided no rules about how to go about it,

option 1 is just guess a starting radius and use a ratio to work out an answer.

option 2 is given rad1 and rad2 work out an answer similar to a fillet. End points move.

option 3 is peg a start point and rad1 enter rad2 2nd endpoint moves. 

option 4 ? Hmm S bend non parallel 

Option 5 anyone ??

 

For me I try to write a global answer saves the next requests, when some one else looks at the post. Just look at how many get a solution and version 5 later.

 

For us Civil engineers we need rules. But we do bend them sometimes look at Tacoma narrows bridge.

Message 29 of 55

leeminardi
Mentor
Mentor

Thanks @ВeekeeCZ .  I was not familiar with the cond function.  I've made the changes to the code that you suggested and also tested for the invalid input of 0. Negative values for "smooth" yield interesting results.  I'm sure there could be a suggested value for s based on the characteristics of the selected line but this, hopefully, will suffice for now.

lee.minardi
Message 30 of 55

ВeekeeCZ
Consultant
Consultant

FYI

The cond does not need progn.

If you want to restrict user input, you should use (initget). (initget 2) for non-zero input.

 

Your skill level is kinda confusing to me. One mapcar after another but some basics not so much.

Message 31 of 55

leeminardi
Mentor
Mentor

@ВeekeeCZ wrote:

 

Your skill level is kinda confusing to me. One mapcar after another but some basics not so much.


@ВeekeeCZ, a fair observation.  I'm what you might call an Occasional User of VLISP. I enjoy solving 2D and 3D geometry problems and am more comfortable dealing with vectors than the user I/O nuances of a what I feel is a clumsy programming language.  Mapcar, as you know, is the primary tool in VLISP for vector addition and multiplication.   I've learned to deal with it. I bounce between VLISP, Max Scripts (which provides a much friendlier environment for vectors and matrices) and VBA (not so user friendly for vector math).   

For the bi-arc task, I was so happy to get an acceptable geometry solution that I didn't take the time to  make the user interface as good as it should have been.  I noted that when I posted the program. 

 

Note,  I usually keep in mind when coding in VLISP the rule that an operation must follow a "(".   The "(4)" you had in your code threw me.   I was aware of the ' exception (e.g., '(1 2 3)) but obviously there's another.  When I was debugging my code that added the "cond" you suggested, with my modifications to test for 0,  I got an error message of an invalid operation (or something like that) and thought that perhaps that was the cause.  It wasn't.  It was something else.

 

I appreciate your response.  We're all still learning.

 

lee.minardi
Message 32 of 55

john.uhden
Mentor
Mentor

@leeminardi 

I was very impressed with @ВeekeeCZ 's implementation of the (cond ...) function.

Note that in her example the 4 was merely encased in parentheses which is the proper syntax because the cond function expects to evaluate an operation (within parentheses).  The 4 was a self-evaluating operation (if that's the correct phraseology).  I'm not too hip on mathematical nouns other than like sum or product.  Then there are all those div* thingies.  Like, what's an "expression?"

John F. Uhden

Message 33 of 55

ВeekeeCZ
Consultant
Consultant

Yeah, I've noticed your hobbies in geometry before. Unlike me...

I have this my the most favorite and the only useful grread routine, where I only wrote the I/O.

The routine works only with lines. Or it can edit the arc between them. Can't fillet arcs.

Message 34 of 55

leeminardi
Mentor
Mentor

@ВeekeeCZ your Filletgrreadprogram is pretty cool. Although I do not follow all of the code I can get a feel for the challenges you faced. I was not aware of the grread function but have seen the results in some of Lee Mac's programs. 

 

As for filleting between arcs I like to use a numerical solution as it is easy to define which of the two intersection points between two arcs that you want to find (or line and arc).  

 

I used the technique in post #8 of this thread. 

Here's the code for the intersection of two circles.

; calculate the intersection point of two circles that is
; closest to a bias point.
(defun circleintr (cenA rA cenB rB biaspt / tol uA guess1 uB guess2 d i ans)
  (setq tol 0.0001) ; set tolerance for intersection point
  (setq	uA (uvec cenA biaspt)
	guess1 (mapcar '+ cenA (mapcar '* uA (list rA ra rA)))
	uB (uvec cenB guess1)
  )
  (setq	guess2
	 (mapcar '+ cenB (mapcar '* uB (list rB rB rB)))
  )
  (setq d (distance guess1 guess2))
  (setq i 0)
  (while (and (> d tol) (< i 21)) ; limit to 20 iterations
    (setq uA (uvec cenA guess2)
	  guess1 (mapcar '+ cenA (mapcar '* uA (list rA ra rA)))
	  uB (uvec cenB guess1)
    )
    (setq guess2
	   (mapcar '+ cenB (mapcar '* uB (list rB rB rB)))
    )
    (setq i (+ i 1))
    (setq d (distance guess1 guess2))
  )					; end while
  (if (> i 20)
    (setq ans nil)
    (setq ans guess2)
  )
)

The function finds the intersection of two circles closest to a bias point. The circles have centers at cenA and cenB , the radii are  rA and rB .

The program  finds the intersection of a line (red) from bias point to  cenA with circle 1.  This is guess1. The intersection of a line through guess1 and cenB with circle 2 is guess2. A guess1 is redefined as the intersection of a line passing through guess2 and cenA on circle 1.  The process is repeated until the distance between successive points guess1 and guess2 is less than a specified tolerance.  The process usually  converges in only a few iterations.   I set a limit of 20 iterations.  More could be done for error checking for example checking if the 2 circles actually intersect. (< (+ rA rB)  (distance cenA cenB)). For a line and an arc I would find guess1 in the same manner put then use the projection of guess1 onto the line to define guess2.  This process would also converge to a solution very quickly.

Of course you could fins intersections analytically but that is less intuitive and less clean for me.  

leeminardi_0-1648049668915.png

 

lee.minardi
Message 35 of 55

john.uhden
Mentor
Mentor

@leeminardi 

Don't know if this is any better, but I've used it for decades.

  ;;------------------------------------------------------
  ;; Function to return the arccos of an angle in radians:
  ;;
  (defun @acos (cosine / sine)
     (cond
        ((zerop cosine)(* pi 0.5))
        ((<= cosine -1.0) pi)
        ((>= cosine 1.0) 0.0)
        (1 (atan (/ (sqrt (- 1.0 (expt cosine 2))) cosine)))
     )
  )
  ;;------------------------------------------------------
  ;; Function to return the arcsin of an angle in radians:
  ;;
  (defun @asin (sine / cosine)
     (setq cosine (sqrt (- 1.0 (expt sine 2))))
     (if (zerop cosine)(setq cosine 0.000000000000000000000000000001))
     (atan (/ sine cosine))
  )
  ;;----------------------------------------------------
  ;; Function to find a point common to 2 arcs,
  ;; given both centers and radii, and a nearest point:
  (defun @aai (rp1 r1 rp2 r2 near / base x ang a p1 p2)
     (setq base (distance rp1 rp2))
     (cond
        ((and (equal r1 0.0 fuzz)(equal r2 base fuzz)) rp1)
        ((and (equal r2 0.0 fuzz)(equal r1 base fuzz)) rp2)
        ((> (+ r1 r2) base)
           (setq x (/ (- (+ (* base base)(* r1 r1)) (* r2 r2)) base 2.0))
           (if (minusp x)
              (setq ang (angle rp2 rp1) x (- x))
              (setq ang (angle rp1 rp2))
           )
           (setq a (@acos (/ x r1))
                p1 (polar rp1 (+ ang a) r1)
                p2 (polar rp1 (- ang a) r1)
                p1 (if (< (distance p1 near)(distance p2 near)) p1 p2)
           )
           p1
        )
        (1 (prompt "\nInvalid. Radii too small."))
     )
  )

John F. Uhden

Message 36 of 55

Kent1Cooper
Consultant
Consultant

@aitorm wrote:

.... to create two curves, connected to each other and the two lines and that are tangent to each other and to the lines. ....


You need to stipulate some additional criterion [or more than one].  As asked, there are an infinite number of solutions, here with the red being the same pair of Lines with known endpoints, and the yellow and green being Arcs that meet each other and the Lines tangentially:

Kent1Cooper_0-1648060484845.png

and everything in between and beyond those two approaching-the-extreme cases, the extremes being where the yellow Arc approaches zero length vs. where the green Arc approaches infinite radius [i.e. becomes a line].

 

Are you looking for the solution in which [for example] the two Arcs are closest to the same length?  Closest to the same radius [in this case the green must always be of larger radius than the yellow, but by how much...]?  Etc.

 

Kent Cooper, AIA
Message 37 of 55

john.uhden
Mentor
Mentor

@Kent1Cooper 

Good questions.

Of course, I don't see why the lines segments have to end where they are.

Seeing as the two arc segments must join at a point of compound curvature, I don't see why he can't just draw a 3-point circle (arc?) that is tangent to both lines and passes through the desired point on the arc.

I'm not sure how you figure that out programatically, but you're the master of the command function, so I don't have to.

John F. Uhden

Message 38 of 55

aitorm
Advocate
Advocate

Thank you all, you are amazing. I wish I knew a tenth of what you know.

 

I don't understand how the lisp works, but it is really cool. And works mostly the way I would like: I don't need a very precise tool but rather something that let me draw beautiful and very simple polylines.

 

If I could choose, I would love the input to be the relation between arc radius. For example, a 2 would give the first radius the double than the second one.

 

🙌

0 Likes
Message 39 of 55

leeminardi
Mentor
Mentor

Well @john.uhden if you had given me that code when I needed it I would not have had to create my own routine!😁  Thanks.  I will keep a copy for future use.

 

 

 

 

 

lee.minardi
0 Likes
Message 40 of 55

john.uhden
Mentor
Mentor

@leeminardi 

My mistake.  I had trusted that you knew more than you let on, your being such a high flyer and everything. 😄

Ya gotta let me know if you need something.

OR... if you need me I'll call you.

John F. Uhden

0 Likes