Page 1 of 1

Finding the tangent points of a circle

Posted: Sun Jul 28, 2024 4:48 pm
by Justin
I need to find the 2 tangent points of 2 intersecting lines to a circle as shown in the image.
I had to use chatgpt because i could not do it. The following code draws the circle at the correct position you can increase or decrease the radius pressing up and down keys.
How to find the tangent points marked in the image?
Image

Code: Select all

EnableExplicit

Structure PointD
	x.d
	y.d
EndStructure

Global.d g_radius
Global.PointD g_intersection, g_endPoint1, g_endPoint2

;TODO 
Procedure findTangentPoint(*intersection.PointD, *endPoint.PointD, *circleCenter.PointD, radius.d, *outTangentPoint.PointD)

EndProcedure

Procedure drawCircle(*intersection.PointD, *endPoint1.PointD, *endPoint2.PointD, radius.d)
	Protected.d angle1, angle2, bisectorAngle, angleDifference, distance
	Protected.PointD circleCenter
	
	;Calculate the angles formed by the lines With respect To the intersection point
	angle1 = ATan2(*endPoint1\x - *intersection\x, *endPoint1\y - *intersection\y)
	angle2 = ATan2(*endPoint2\x - *intersection\x, *endPoint2\y - *intersection\y)

	;Find the bisector of the angle formed by the two lines
	bisectorAngle = (angle1 + angle2) / 2.0
	
	;Calculate the distance from the intersection point To the point where the circle is tangent To both lines
	angleDifference = Abs(angle1 - angle2)
	If angleDifference > #PI
		angleDifference = 2 * #PI - angleDifference
	EndIf
	
	distance = radius / Sin(angleDifference / 2.0)
	
	;Find the center of the circle using the bisector angle And the calculated distance
	circleCenter\x = *intersection\x + distance * Cos(bisectorAngle)
	circleCenter\y = *intersection\y + distance * Sin(bisectorAngle)

	AddPathCircle(circleCenter\x, circleCenter\y, radius)
	
	;TODO FIND TANGENT POINTS
	Protected.PointD tangentPoint1, tangentPoint2
	findTangentPoint(*intersection, *endPoint1, @circleCenter, radius, @tangentPoint1)
	findTangentPoint(*intersection, *endPoint2, @circleCenter, radius, @tangentPoint2)
EndProcedure

Procedure drawCanvas(*intersection.PointD, *endPoint1.PointD, *endPoint2.PointD, radius.d)
	StartVectorDrawing(CanvasVectorOutput(0))	
	;Clear
	VectorSourceColor(RGBA(255, 255, 255, 255))	
	FillVectorOutput()
	
	VectorSourceColor(RGBA(0, 0, 255, 255))	
		
	;Lines
	MovePathCursor(*intersection\x, *intersection\y)
	AddPathLine(*endPoint1\x, *endPoint1\y)

	MovePathCursor(*intersection\x, *intersection\y)
	AddPathLine(*endPoint2\x, *endPoint2\y)

	StrokePath(4)
	
	;Circle
	VectorSourceColor(RGBA(255, 0, 0, 255))	
	drawCircle(*intersection, *endPoint1, *endPoint2, radius)

	StrokePath(4)
	StopVectorDrawing()
EndProcedure

Procedure onKeyDown()
	If GetGadgetAttribute(0, #PB_Canvas_Key) = #PB_Shortcut_Up
		g_radius + 1.0
		drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)

	ElseIf GetGadgetAttribute(0, #PB_Canvas_Key) = #PB_Shortcut_Down
		g_radius - 1.0
		If g_radius < 0
			g_radius = 1
		EndIf
		drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)
	EndIf 
EndProcedure

Procedure main()
	Protected.l ev
	Protected.PointD intersection, endPoint1, endPoint2
	
	g_radius = DesktopScaledX(40)
	g_intersection\x = DesktopScaledX(400)
	g_intersection\y = DesktopScaledY(300)
	g_endPoint1\x = DesktopScaledX(500)
	g_endPoint1\y = DesktopScaledY(10)

	g_endPoint2\x = DesktopScaledX(600)
	g_endPoint2\y = DesktopScaledY(200)
	
	OpenWindow(0, 0, 0, 800, 600, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	CanvasGadget(0, 0, 0, WindowWidth(0), WindowHeight(0), #PB_Canvas_Keyboard)
	BindGadgetEvent(0, @onKeyDown(), #PB_EventType_KeyDown)
	
	drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)
	
	SetActiveGadget(0)
	
	Repeat
		ev = WaitWindowEvent()
	Until ev = #PB_Event_CloseWindow
EndProcedure

main()

Re: Finding the tangent points of a circle

Posted: Sun Jul 28, 2024 6:02 pm
by infratec

Re: Finding the tangent points of a circle

Posted: Sun Jul 28, 2024 6:30 pm
by firace
IMHO the top answer by Rory Daulton seems to be the easiest to implement in PB:

https://stackoverflow.com/questions/499 ... om-a-point

Re: Finding the tangent points of a circle

Posted: Sun Jul 28, 2024 6:34 pm
by infratec
https://stackoverflow.com/questions/499 ... om-a-point

Code: Select all

Procedure findTangentPoint(*intersection.PointD, *circleCenter.PointD, radius.d, *outTangentPoint1.PointD, *outTangentPoint2.PointD)
  
  Protected.d b, th, d, d1, d2
  
  
  b = Sqr(((*intersection\x - *circleCenter\x) * (*intersection\x - *circleCenter\x)) + ((*intersection\y - *circleCenter\y) * (*intersection\y - *circleCenter\y)))
  th = ACos(radius / b)
  d = ATan2(*intersection\x - *circleCenter\x, *intersection\y - *circleCenter\y)
  d1 = d + th
  d2 = d - th
  
  *outTangentPoint1\x = *circleCenter\x + radius * Cos(d1)
  *outTangentPoint1\y = *circleCenter\y + radius * Sin(d1)
  
  *outTangentPoint2\x = *circleCenter\x + radius * Cos(d2)
  *outTangentPoint2\y = *circleCenter\y + radius * Sin(d2)
  
EndProcedure

Procedure drawCircle(*intersection.PointD, *endPoint1.PointD, *endPoint2.PointD, radius.d)
	Protected.d angle1, angle2, bisectorAngle, angleDifference, distance
	Protected.PointD circleCenter
	
	;Calculate the angles formed by the lines With respect To the intersection point
	angle1 = ATan2(*endPoint1\x - *intersection\x, *endPoint1\y - *intersection\y)
	angle2 = ATan2(*endPoint2\x - *intersection\x, *endPoint2\y - *intersection\y)

	;Find the bisector of the angle formed by the two lines
	bisectorAngle = (angle1 + angle2) / 2.0
	
	;Calculate the distance from the intersection point To the point where the circle is tangent To both lines
	angleDifference = Abs(angle1 - angle2)
	If angleDifference > #PI
		angleDifference = 2 * #PI - angleDifference
	EndIf
	
	distance = radius / Sin(angleDifference / 2.0)
	
	;Find the center of the circle using the bisector angle And the calculated distance
	circleCenter\x = *intersection\x + distance * Cos(bisectorAngle)
	circleCenter\y = *intersection\y + distance * Sin(bisectorAngle)

	AddPathCircle(circleCenter\x, circleCenter\y, radius)
	
	StrokePath(4)
	
	
	
	;TODO FIND TANGENT POINTS
	Protected.PointD tangentPoint1, tangentPoint2
	findTangentPoint(*intersection, @circleCenter, radius, @tangentPoint1, @tangentPoint2)
	
	VectorSourceColor(RGBA(0, 0, 0, 255))
	
	AddPathCircle(tangentPoint1\x, tangentPoint1\y, 8)
	AddPathCircle(tangentPoint2\x, tangentPoint2\y, 8)
	
EndProcedure

Re: Finding the tangent points of a circle

Posted: Sun Jul 28, 2024 7:51 pm
by Justin
Hi infratec,

i did the same with firace's link, it was very helpful.
Thanks to both!
Complete code here:

Code: Select all

EnableExplicit

Structure PointD
	x.d
	y.d
EndStructure

Global.d g_radius
Global.PointD g_intersection, g_endPoint1, g_endPoint2

;distance : distance from circle center and intersection point
;ref : https://stackoverflow.com/questions/49968720/find-tangent-points-in-a-circle-from-a-point
Procedure findTangentPoints(*intersection.PointD, *circleCenter.PointD, radius.d, distance.d, *outTangentPoint1.PointD, *outTangentPoint2.PointD)
	Protected.d d2, th, d, d1
	Protected.PointD tangentPoint1, tangentPoint2

	th = ACos(radius / distance) ;angle theta
	d = ATan2(*intersection\x - *circleCenter\x, *intersection\y - *circleCenter\y) ;direction angle of point P from C
	d1 = d + th ;direction angle of point T1 from C
	d2 = d - th ;direction angle of point T2 from C
	
	*outTangentPoint1\x = *circleCenter\x + radius * Cos(d1)
	*outTangentPoint1\y = *circleCenter\y + radius * Sin(d1)
	
	*outTangentPoint2\x = *circleCenter\x + radius * Cos(d2)
	*outTangentPoint2\y = *circleCenter\y + radius * Sin(d2)
EndProcedure

Procedure drawCircle(*intersection.PointD, *endPoint1.PointD, *endPoint2.PointD, radius.d)
	Protected.d angle1, angle2, bisectorAngle, angleDifference, distance
	Protected.PointD circleCenter
	Protected.PointD tangentPoint1, tangentPoint2

	;Calculate the angles formed by the lines With respect To the intersection point
	angle1 = ATan2(*endPoint1\x - *intersection\x, *endPoint1\y - *intersection\y)
	angle2 = ATan2(*endPoint2\x - *intersection\x, *endPoint2\y - *intersection\y)

	;Find the bisector of the angle formed by the two lines
	bisectorAngle = (angle1 + angle2) / 2.0
	
	;Calculate the distance from the intersection point To the point where the circle is tangent To both lines
	angleDifference = Abs(angle1 - angle2)
	If angleDifference > #PI
		angleDifference = 2 * #PI - angleDifference
	EndIf
	
	distance = radius / Sin(angleDifference / 2.0)
	
	;Find the center of the circle using the bisector angle And the calculated distance
	circleCenter\x = *intersection\x + distance * Cos(bisectorAngle)
	circleCenter\y = *intersection\y + distance * Sin(bisectorAngle)
	
	AddPathCircle(circleCenter\x, circleCenter\y, radius)
	
	findTangentPoints(*intersection, @circleCenter, radius, distance, @tangentPoint1, @tangentPoint2)
	AddPathCircle(tangentPoint1\x, tangentPoint1\y, 4)
	AddPathCircle(tangentPoint2\x, tangentPoint2\y, 4)
EndProcedure

Procedure drawCanvas(*intersection.PointD, *endPoint1.PointD, *endPoint2.PointD, radius.d)
	StartVectorDrawing(CanvasVectorOutput(0))	
	;Clear
	VectorSourceColor(RGBA(255, 255, 255, 255))	
	FillVectorOutput()
	
	VectorSourceColor(RGBA(0, 0, 255, 255))	
		
	;Lines
	MovePathCursor(*intersection\x, *intersection\y)
	AddPathLine(*endPoint1\x, *endPoint1\y)

	MovePathCursor(*intersection\x, *intersection\y)
	AddPathLine(*endPoint2\x, *endPoint2\y)

	StrokePath(4)
	
	;Circle
	VectorSourceColor(RGBA(255, 0, 0, 255))	
	drawCircle(*intersection, *endPoint1, *endPoint2, radius)

	StrokePath(4)
	StopVectorDrawing()
EndProcedure

Procedure onKeyDown()
	If GetGadgetAttribute(0, #PB_Canvas_Key) = #PB_Shortcut_Up
		g_radius + 1.0
		drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)

	ElseIf GetGadgetAttribute(0, #PB_Canvas_Key) = #PB_Shortcut_Down
		g_radius - 1.0
		If g_radius < 0
			g_radius = 1
		EndIf
		drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)
	EndIf 
EndProcedure

Procedure main()
	Protected.l ev
	Protected.PointD intersection, endPoint1, endPoint2
	
	g_radius = DesktopScaledX(40)
	g_intersection\x = DesktopScaledX(400)
	g_intersection\y = DesktopScaledY(300)
	g_endPoint1\x = DesktopScaledX(500)
	g_endPoint1\y = DesktopScaledY(10)

	g_endPoint2\x = DesktopScaledX(600)
	g_endPoint2\y = DesktopScaledY(200)
	
	OpenWindow(0, 0, 0, 800, 600, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	CanvasGadget(0, 0, 0, WindowWidth(0), WindowHeight(0), #PB_Canvas_Keyboard)
	BindGadgetEvent(0, @onKeyDown(), #PB_EventType_KeyDown)
	
	drawCanvas(@g_intersection, @g_endPoint1, @g_endPoint2, g_radius)
	
	SetActiveGadget(0)
	
	Repeat
		ev = WaitWindowEvent()
	Until ev = #PB_Event_CloseWindow
EndProcedure

main()