Tangent lines to two circles

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Tangent lines to two circles

Post by jfc4120 »

Code: Select all

OS: Windows 10 Version 2009
Word size of FreeCAD: 64-bit
Version: 0.20.29177 (Git)
Build type: Release
Branch: releases/FreeCAD-0-20
Hash: 68e337670e227889217652ddac593c93b5e8dc94
Python 3.8.10, Qt 5.15.2, Coin 4.0.1, Vtk 8.2.0, OCC 7.6.2
Locale: English/United States (en_US)
Installed mods: 
  * Help 1.0.3
I needed a macro that would accomplish this. And thanks to @edwilliams16 and his reply here:

https://forum.freecadweb.org/viewtopic. ... 27#p563627

I worked it out, in his example the values are hard coded, so I tweaked it to:

Code: Select all

from math import sqrt
# -*- coding: utf-8 -*-
import FreeCAD, FreeCADGui
import Draft
import math
# from math import cos, sin, radians
from PySide import QtGui

convert = 25.4

Placement = App.Placement
doc = App.ActiveDocument

#===============================================================================
vec = App.Vector


def tangent2C(p1, p2, rad1, rad2):
    """calculate four tangent point to join two circles

        Parameters:
        p1    Vector  Center of circle1
        p2    Vector  Center of circle2
        rad1  float   Radius of circle1
        rad2  float   Radius of circle2

        Return:
        [tg1, tg2, tg3, tg4] tangent points.
        If one circle strictly encloses the other or are they are concentric, return []
    """
    l = (p1 - p2).Length
    if abs(rad1 - rad2) > l or l == 0:
        return []
    else:
        c = (rad1 - rad2) / l
        s = sqrt(1 - c * c)
        n = (p2 - p1) / l  # unit vector along line of centers
        m = vec(0, 0, 1).cross(n)  # orthogonal vector
        vp = c * n + s * m  # unit vectors from center to tangent points
        vm = c * n - s * m
        return [c1 + rad1 * vp, c1 + rad1 * vm, c2 + rad2 * vp, c2 + rad2 * vm]
#============================================================================
r1 = Gui.Selection.getSelection()[0].Radius * .039370078
r2 = Gui.Selection.getSelection()[1].Radius * .039370078
r1 = float(r1)
r2 = float(r2)


selX1 = FreeCADGui.Selection.getSelectionEx()[2]
selX2 = FreeCADGui.Selection.getSelectionEx()[3]

v1 = selX1.Object.Shape
X = v1.Point.x * .039370078
Y = v1.Point.y * .039370078
Z = v1.Point.z * .039370078
multlist = [[X, Y, Z]]

v2 = selX2.Object.Shape
X = v2.Point.x * .039370078
Y = v2.Point.y * .039370078
Z = v2.Point.z * .039370078
multlist.append([X, Y, Z])


CLx = multlist[0][0]
CLy = multlist[0][1]
CSx = multlist[1][0]
CSy = multlist[1][1]

print('csx', CSx)
print('csy', CSy)

#============================================================================

c2 = vec(CSx, CSy, 0)
c1 = vec(CLx, CLy, 0)
#c1 = vec(-35.8, 9.07, 0)
#c2 = vec(-28.3, 16.4, 0)
#r1 = 3
#r2 = 5
pto = tangent2C(c1, c2, r1, r2)

hline1 = Draft.make_line(vec(pto[1][0] * convert , pto[1][1] * convert, 0), vec(pto[3][0] * convert , pto[3][1] * convert, 0))
hline2 = Draft.make_line(vec(pto[0][0] * convert , pto[0][1] * convert, 0), vec(pto[2][0] * convert , pto[2][1] * convert, 0))
print(pto[1][0])
print(pto[1][1])
for pt in pto:
 #   print(pt[0][0])
 #   print(pt[0][1])
    print(pt)
In example images, in order select big circle then small circle then big circle center then small circle center.

Thank you @edwilliams16 for all your help. And of course tweak macro as needed. And if only one tangent is needed just delete the unwanted one.

I will do another post with tangents from an external point to a circle.

Edit:

This is in draft mode.
Attachments
two_before.png
two_before.png (14.49 KiB) Viewed 973 times
two_selected.png
two_selected.png (16.85 KiB) Viewed 973 times
two_after.png
two_after.png (18.38 KiB) Viewed 973 times
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Re: Tangent lines to two circles

Post by Roy_043 »

This may be of interest:
Macro_Draft_Circle_Tangent
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Tangent lines to two circles

Post by edwilliams16 »

Note that the context of my code was that the circles both lay in the XY_plane. If not:

Code: Select all

m = vec(0, 0, 1).cross(n)
will need to be

Code: Select all

m = planeNormal.cross(n)
where planeNormal is the normal to the plane of the circles.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Tangent lines to two circles

Post by jfc4120 »

@Roy_043 can you help with this section of Macro_Draft_Circle_Tangent:

Code: Select all

   else:
        SINGLE = True
        circle = s1 if r1 else s2
        sx1, sx2 = Gui.Selection.getSelectionEx()
        sx = sx2 if r1 else sx1
        #point, = sx.PickedPoints         Currently in macro
        #print('point', point)
        
        ######## What I'm trying
        selX1 = FreeCADGui.Selection.getSelectionEx()[1]
        v9 = selX1.Object.Shape
        X = v9.Point.x
        Y = v9.Point.y
        Z = v9.Point.z
        point, = App.Vector(X, Y, Z)
        print('point', point)
With sx.PickedPoints the macro only works if the point is picked with mouse.
I'm trying to change it so I can select point from tree view. I admit I am stuck at changing the code.

I get:
File "C:/Users/Owner/AppData/Roaming/FreeCAD/Macro/Draft_Circle_Tangent_2.py", line 131, in <module>
point, = App.Vector(X, Y, Z)
<class 'ValueError'>: too many values to unpack (expected 1)
I am getting better, but still fairly new to Freecad Python, thanks.

Edit:

I solved it, changed the code to:

Code: Select all

        selX1 = FreeCADGui.Selection.getSelectionEx()[1]
        v9 = selX1.Object.Shape
        X = v9.Point.x
        Y = v9.Point.y
        Z = v9.Point.z
        newpt = App.Vector(X, Y, Z)
        point = newpt
        print('point', point)
For some reason removing the comma (point, to point) worked. But I do appreciate all the help you have given me. :D

Edit2:

@edwilliams16 Thanks for the update, and yes my usage will be xy plane.
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Tangent lines to two circles

Post by edwilliams16 »

jfc4120 wrote: Fri Nov 25, 2022 11:30 pm
else:
SINGLE = True
circle = s1 if r1 else s2
sx1, sx2 = Gui.Selection.getSelectionEx()
sx = sx2 if r1 else sx1
#point, = sx.PickedPoints Currently in macro
#print('point', point)

######## What I'm trying
selX1 = FreeCADGui.Selection.getSelectionEx()[1]
v9 = selX1.Object.Shape
X = v9.Point.x
Y = v9.Point.y
Z = v9.Point.z
point, = App.Vector(X, Y, Z)
print('point', point)
[/code]
sx.PickedPoints is a list. point, = sx.PickedPoints is a Python idiom for getting the first element. Others might use point = sx.PickedPoints[0]

OTOH, App.Vector(X, Y, Z) is the vector itself - no list indexing required.

Your try is very verbose

Code: Select all

    selX1 = FreeCADGui.Selection.getSelectionEx()[1]
    point = selX1.Object.Shape.Point
    print('point', point)
is equivalent. No need to take the vector apart and put it back together.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Tangent lines to two circles

Post by jfc4120 »

@edwilliams16

Thanks. Also concerning:

m = vec(0, 0, 1).cross(n)

will need to be

Code: Select all

m = planeNormal.cross(n)

where planeNormal is the normal to the plane of the circles.

Do you mean as example:

Code: Select all

m = vec(0, 1, 0).cross(n)
Or is there some other code here?
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Tangent lines to two circles

Post by edwilliams16 »

That's the only change. For the XY_Plane, the normal was hard-coded to App.Vector(0,0,1). Note that in the 3D case, you'll need to check (or be assured, by construction) the two circles are actually co-planar.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Tangent lines to two circles

Post by jfc4120 »

@heda

Thanks for the new tangent macro :D . I recommend you change the part discussed above so a user can select a point via mouse or tree view. But pretty neat Macro. I am still working on another that has a tangent line from a point to circle. I know yours already does as well, but it's just for fun. I have enjoyed programming stuff in python.
heda
Veteran
Posts: 1348
Joined: Sat Dec 12, 2015 5:49 pm

Re: Tangent lines to two circles

Post by heda »

Screenshot from 2022-12-04 13-38-00.png
Screenshot from 2022-12-04 13-38-00.png (12.02 KiB) Viewed 557 times
"I have enjoyed programming stuff in python."
welcome to a growing crowd :-)
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Tangent lines to two circles

Post by jfc4120 »

@heda thanks for the fix, now a point or line end works perfect.

I have programmed many sheet metal duct fitting in the past in "basiccad". And I am enjoying now programming many of them in python. One of the latest is a rectangular ogee offset.
Attachments
ogee.png
ogee.png (4.76 KiB) Viewed 526 times
Post Reply