Merge pull request #23310 from Roy-043/Draft-more-logical-placement-for-3-point-arcs
Draft: more logical placement for 3 point arcs
This commit is contained in:
@@ -581,12 +581,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)")
|
||||
|
||||
@@ -31,18 +31,14 @@ import math
|
||||
|
||||
import FreeCAD as App
|
||||
import Part
|
||||
import Draft
|
||||
import draftutils.utils as utils
|
||||
from draftmake import make_circle
|
||||
from draftutils import utils
|
||||
from draftutils.messages import _err
|
||||
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 +46,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 +60,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 +93,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:
|
||||
@@ -148,42 +103,25 @@ def make_arc_3points(points, placement=None, face=False,
|
||||
_err(translate("draft","Wrong input: incorrect type of points."))
|
||||
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
|
||||
|
||||
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 make_circle.make_circle(edge, placement=placement, face=face, support=support)
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user