__Name__ = 'Measure Circle' __Comment__ = 'Compute the radius of a circle by 3 points or a circular edge' __Author__ = 'peepsalot, galou_breizh' __Version__ = '0.10.1' __Date__ = '2023-03-11' __License__ = 'LGPL-2.0-or-later' __Web__ = 'http://www.freecadweb.org/wiki/Macro_MeasureCircle' __Wiki__ = 'http://www.freecadweb.org/wiki/Macro_MeasureCircle' __Icon__ = 'MeasureCircle.png' __Help__ = 'Make a selection of 3 points or an edge and launch, or launch first' __Status__ = 'Beta' __Requires__ = 'FreeCAD >= 0.18' __Communication__ = '' __Files__ = 'MeasureCircle.png' import FreeCAD as app import FreeCADGui as gui import Part def get_global_placement(obj): try: # If obj is part of a Part or Body. return obj.getParentGeoFeatureGroup().getGlobalPlacement() except AttributeError: return app.Placement() class MeasureCircle: """Report the radius and center of a circle This class will report the computed radius and center of a circle given 3 vertices or a circular edge. Just select the vertices and the result will be shown in the Report View. Edges may also be selected and count as two vertices. A circular edge can also be selected. If two edges are selected the end vertex of the second edge is not used in the calculation. """ def __init__(self): self.points = [] self.curve = None self.observer_needed = False self.get_elements_from_selection_ex() # We add the observer only if necessary. self.observer_needed = (self.curve is None) if self.observer_needed: gui.Selection.addObserver(self) app.Console.PrintMessage( 'Select {} vertices on a circle\n'.format( 3 - len(self.points))) def addSelection(self, doc, obj, sub, pos): """Callback of the selection observer Do not rename. """ o = app.getDocument(doc).getObject(obj) # Get the shape of selected object, if any. try: shape = o.Shape except AttributeError: app.Console.PrintMessage('Object is not a shape\n') return # Get the selected subshape. try: subshape = getattr(shape, sub) except: app.Console.PrintMessage( '"{}" is not a subshape of "{}"\n'.format(sub, shape)) return if subshape.ShapeType == 'Vertex': self.add_point(o, subshape) elif subshape.ShapeType == 'Edge': self.on_edge(o, subshape) # Exit if we have enough points. if len(self.points) >= 3: self.on_three_points() def get_elements_from_selection_ex(self): for sel_object in gui.Selection.getSelectionEx(): obj = sel_object.Object for subobject in sel_object.SubObjects: if type(subobject) == Part.Vertex: self.add_point(obj, subobject) elif type(subobject) == Part.Edge: self.on_edge(obj, subobject) # Exit if we have enough points. if len(self.points) >= 3: self.on_three_points() return def add_point(self, obj, v): """Add a point at the location of the selected vertex obj is the object the vertex belongs to. If obj belongs to a body, the body placement must be taken into account. """ placement = get_global_placement(obj) point = placement.multVec(v.Point) if point in self.points: # Don't allow duplicate points. app.Console.PrintWarning('Point already chosen, ignoring\n') return self.points.append(point) def on_edge(self, obj, edge): """Add either two points or a circle obj is the object the edge belongs to. If obj belongs to a body, the body placement must be taken into account. """ curve_type = type(edge.Curve) if curve_type == Part.Circle: self.on_circle(obj, edge) elif curve_type == Part.LineSegment: self.add_point(obj, edge.Vertexes[0]) self.add_point(obj, edge.Vertexes[1]) def finish(self): if self.curve: c = self.curve # line = Part.makeLine(c.Center, c.Center + c.XAxis * c.Radius) # obj = app.ActiveDocument.addObject('Part::Feature', 'Radius') # obj.Shape = line app.Console.PrintMessage('Diameter: {} mm\n'.format(2 * c.Radius)) app.Console.PrintMessage('Radius: {} mm\n'.format(c.Radius)) app.Console.PrintMessage( 'Center: (X, Y, Z) = ({}, {}, {})\n'.format( *list(c.Location))) if self.observer_needed: gui.Selection.removeObserver(self) def on_circle(self, obj, edge): """Add a circle and finish obj is the object the curve belongs to. If obj belongs to a body, the body placement must be taken into account. """ placement = get_global_placement(obj) edge = edge.copy() edge.Placement = edge.Placement.multiply(placement) self.curve = edge.Curve self.finish() def on_three_points(self): v1 = self.points[0] v2 = self.points[1] v3 = self.points[2] self.curve = Part.Arc(v1, v2, v3).toShape().Curve self.finish() if __name__ == '__main__': MeasureCircle()