diff --git a/src/Mod/Draft/draftguitools/gui_arcs.py b/src/Mod/Draft/draftguitools/gui_arcs.py index 01599b376d..cc22899f4c 100644 --- a/src/Mod/Draft/draftguitools/gui_arcs.py +++ b/src/Mod/Draft/draftguitools/gui_arcs.py @@ -584,12 +584,17 @@ class Arc_3Points(gui_base.GuiCommandBase): # proceed with creating the final object. # Draw a simple `Part::Feature` if the parameter is `True`. Gui.addModule("Draft") + Gui.addModule("WorkingPlane") _cmd = "Draft.make_arc_3points([" _cmd += "FreeCAD." + str(self.points[0]) _cmd += ", FreeCAD." + str(self.points[1]) _cmd += ", FreeCAD." + str(self.points[2]) - _cmd += "], primitive=" + str(params.get_param("UsePartPrimitives")) + ")" - _cmd_list = ["circle = " + _cmd, + _cmd += "]" + _cmd += ", placement=pl" + _cmd += ", primitive=" + str(params.get_param("UsePartPrimitives")) + _cmd += ")" + _cmd_list = ["pl = WorkingPlane.get_working_plane().get_placement()", + "circle = " + _cmd, "Draft.autogroup(circle)"] if params.get_param("UsePartPrimitives"): _cmd_list.append("Draft.select(circle)") diff --git a/src/Mod/Draft/draftmake/make_arc_3points.py b/src/Mod/Draft/draftmake/make_arc_3points.py index 26167deca7..02d37c6bf8 100644 --- a/src/Mod/Draft/draftmake/make_arc_3points.py +++ b/src/Mod/Draft/draftmake/make_arc_3points.py @@ -39,10 +39,8 @@ from draftutils.translate import translate import draftutils.gui_utils as gui_utils -def make_arc_3points(points, placement=None, face=False, - support=None, map_mode="Deactivated", - primitive=False): - """Draw a circular arc defined by three points in the circumference. +def make_arc_3points(points, placement=None, face=False, support=None, primitive=False): + """Draw a circular arc defined by three points on the circumference. Parameters ---------- @@ -50,17 +48,9 @@ def make_arc_3points(points, placement=None, face=False, A list that must be three points. placement: Base::Placement, optional - It defaults to `None`. - It is a placement, comprised of a `Base` (`Base::Vector3`), - and a `Rotation` (`Base::Rotation`). - If it exists it moves the center of the new object to the point - indicated by `placement.Base`, while `placement.Rotation` - is ignored so that the arc keeps the same orientation - with which it was created. - - If both `support` and `placement` are given, - `placement.Base` is used for the `AttachmentOffset.Base`, - and again `placement.Rotation` is ignored. + It is adjusted to match the geometry of the created edge. + The Z axis of the adjusted placement will be parallel to the + (negative) edge axis. Its Base will match the edge center. face: bool, optional It defaults to `False`. @@ -72,39 +62,14 @@ def make_arc_3points(points, placement=None, face=False, It is a list containing tuples to define the attachment of the new object. - A tuple in the list needs two elements; - the first is an external object, and the second is another tuple - with the names of sub-elements on that external object - likes vertices or faces. - :: - support = [(obj, ("Face1"))] - support = [(obj, ("Vertex1", "Vertex5", "Vertex8"))] - - This parameter sets the `Support` property but it only really affects - the position of the new object when the `map_mode` - is set to other than `'Deactivated'`. - - map_mode: str, optional - It defaults to `'Deactivated'`. - It defines the type of `'MapMode'` of the new object. - This parameter only works when a `support` is also provided. - - Example: place the new object on a face or another object. - :: - support = [(obj, ("Face1"))] - map_mode = 'FlatFace' - - Example: place the new object on a plane created by three vertices - of an object. - :: - support = [(obj, ("Vertex1", "Vertex5", "Vertex8"))] - map_mode = 'ThreePointsPlane' + This parameter sets the `Support` property but it only really + affects the position of the new object if its `MapMode` is + set to other than `'Deactivated'`. primitive: bool, optional It defaults to `False`. If it is `True`, it will create a Part primitive instead of a Draft object. - In this case, `placement`, `face`, `support`, and `map_mode` - are ignored. + In this case, `placement`, `face` and `support` are ignored. Returns ------- @@ -130,14 +95,6 @@ def make_arc_3points(points, placement=None, face=False, _err(translate("draft","Wrong input: must be list or tuple of 3 points exactly.")) return None - if placement is not None: - try: - utils.type_check([(placement, App.Placement)], name=_name) - except TypeError: - _err(translate("draft","Placement:") + " {}".format(placement)) - _err(translate("draft","Wrong input: incorrect type of placement.")) - return None - p1, p2, p3 = points try: @@ -149,41 +106,16 @@ def make_arc_3points(points, placement=None, face=False, return None try: - _edge = Part.Arc(p1, p2, p3) + edge = Part.Arc(p1, p2, p3).toShape() except Part.OCCError as error: _err(translate("draft","Cannot generate shape:") + " " + "{}".format(error)) return None - edge = _edge.toShape() - radius = edge.Curve.Radius - center = edge.Curve.Center - if primitive: obj = App.ActiveDocument.addObject("Part::Feature", "Arc") obj.Shape = edge return obj - rot = App.Rotation(edge.Curve.XAxis, - edge.Curve.YAxis, - edge.Curve.Axis, "ZXY") - _placement = App.Placement(center, rot) - start = edge.FirstParameter - end = math.degrees(edge.LastParameter) - obj = Draft.make_circle(radius, - placement=_placement, face=face, - startangle=start, endangle=end, - support=support) - - original_placement = obj.Placement - - if placement and not support: - obj.Placement.Base = placement.Base - if support: - obj.MapMode = map_mode - if placement: - obj.AttachmentOffset.Base = placement.Base - obj.AttachmentOffset.Rotation = original_placement.Rotation - - return obj + return Draft.make_circle(edge, placement=placement, face=face, support=support) ## @} diff --git a/src/Mod/Draft/draftmake/make_circle.py b/src/Mod/Draft/draftmake/make_circle.py index de8bacb0af..71f02e6e1c 100644 --- a/src/Mod/Draft/draftmake/make_circle.py +++ b/src/Mod/Draft/draftmake/make_circle.py @@ -32,6 +32,7 @@ import math import FreeCAD as App import Part import DraftGeomUtils +import DraftVecUtils import draftutils.utils as utils import draftutils.gui_utils as gui_utils @@ -41,37 +42,61 @@ if App.GuiUp: from draftviewproviders.view_base import ViewProviderDraft +def _get_normal(axis, ref_rot): + local_axis = ref_rot.inverted().multVec(axis) + x, y, z = [abs(coord) for coord in list(local_axis)] + # Use local X, Y or Z axis for comparison: + if z >= x and z >= y: + local_comp_vec = App.Vector(0, 0, 1) + elif y >= x and y >= z: + local_comp_vec = App.Vector(0, -1, 0) # -Y to match the Front view + else: + local_comp_vec = App.Vector(1, 0, 0) + comp_vec = ref_rot.multVec(local_comp_vec) + axis = App.Vector(axis) # create independent copy + if axis.getAngle(comp_vec) > math.pi/2: + axis = axis.negative() + return axis + + def make_circle(radius, placement=None, face=None, startangle=None, endangle=None, support=None): - """make_circle(radius, [placement, face, startangle, endangle]) - or make_circle(edge,[face]): + """make_circle(radius, [placement], [face], [startangle], [endangle]) + or make_circle(edge, [placement], [face]): Creates a circle object with given parameters. - If startangle and endangle are provided and not equal, the object will show + If startangle and endangle are provided and not equal, the object will be an arc instead of a full circle. Parameters ---------- - radius : the radius of the circle. + radius: the radius of the circle or the shape of a circular edge + If it is an edge, startangle and endangle are ignored. + edge.Curve must be a Part.Circle. - placement : - If placement is given, it is used. + placement: optional + If radius is an edge, placement is adjusted to match the geometry + of the edge. The Z axis of the adjusted placement will be parallel + to the (negative) edge axis. Its Base will match the edge center. - face : Bool + face: Bool If face is False, the circle is shown as a wireframe, otherwise as a face. - startangle : start angle of the circle (in degrees) + startangle: start angle of the circle (in degrees) Recalculated if not in the -360 to 360 range. - endangle : end angle of the circle (in degrees) + endangle: end angle of the circle (in degrees) Recalculated if not in the -360 to 360 range. - edge : edge.Curve must be a 'Part.Circle' - The circle is created from the given edge. + support: App::PropertyLinkSubList, optional + It defaults to `None`. + It is a list containing tuples to define the attachment + of the new object. - support : - TODO: Describe + This parameter sets the `Support` property but it only really + affects the position of the new object if its `MapMode` is + set to other than `'Deactivated'`. """ if not App.ActiveDocument: @@ -81,38 +106,36 @@ def make_circle(radius, placement=None, face=None, startangle=None, endangle=Non if placement: utils.type_check([(placement,App.Placement)], "make_circle") - if startangle != endangle: - _name = "Arc" + if (isinstance(radius, Part.Edge) and len(radius.Vertexes) > 1) \ + or startangle != endangle: + name = "Arc" else: - _name = "Circle" + name = "Circle" - obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", _name) + obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", name) Circle(obj) if face is not None: obj.MakeFace = face - if isinstance(radius,Part.Edge): + if isinstance(radius, Part.Edge) and DraftGeomUtils.geomType(radius) == "Circle": edge = radius - if DraftGeomUtils.geomType(edge) == "Circle": - obj.Radius = edge.Curve.Radius - placement = App.Placement(edge.Placement) - delta = edge.Curve.Center.sub(placement.Base) - placement.move(delta) - # Rotation of the edge - rotOk = App.Rotation(edge.Curve.XAxis, edge.Curve.YAxis, edge.Curve.Axis, "ZXY") - placement.Rotation = rotOk - if len(edge.Vertexes) > 1: - v0 = edge.Curve.XAxis - v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) - v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) - # Angle between edge.Curve.XAxis and the vector from center to start of arc - a0 = math.degrees(App.Vector.getAngle(v0, v1)) - # Angle between edge.Curve.XAxis and the vector from center to end of arc - a1 = math.degrees(App.Vector.getAngle(v0, v2)) - obj.FirstAngle = a0 - obj.LastAngle = a1 + obj.Radius = edge.Curve.Radius + axis = edge.Curve.Axis + ref_rot = App.Rotation() if placement is None else placement.Rotation + normal = _get_normal(axis, ref_rot) + x_axis = ref_rot.multVec(App.Vector(1, 0, 0)) + y_axis = ref_rot.multVec(App.Vector(0, 1, 0)) + rot = App.Rotation(x_axis, y_axis, normal, "ZXY") + placement = App.Placement(edge.Curve.Center, rot) + if len(edge.Vertexes) > 1: + v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) + v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) + if not axis.isEqual(normal, 1e-4): + v1, v2 = v2, v1 + obj.FirstAngle = math.degrees(DraftVecUtils.angle(x_axis, v1, normal)) + obj.LastAngle = math.degrees(DraftVecUtils.angle(x_axis, v2, normal)) else: obj.Radius = radius if (startangle is not None) and (endangle is not None):