| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides the object code for the BezCurve object.""" |
| | |
| | |
| | |
| |
|
| | |
| | |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| |
|
| | import FreeCAD as App |
| | from draftutils import gui_utils |
| | from draftutils import params |
| | from draftobjects.base import DraftObject |
| |
|
| |
|
| | class BezCurve(DraftObject): |
| | """The BezCurve object""" |
| |
|
| | def __init__(self, obj): |
| | super().__init__(obj, "BezCurve") |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "The points of the Bezier curve") |
| | obj.addProperty("App::PropertyVectorList", "Points", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "The degree of the Bezier function") |
| | obj.addProperty("App::PropertyInteger", "Degree", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "Continuity") |
| | obj.addProperty("App::PropertyIntegerList", "Continuity", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "If the Bezier curve should be closed or not") |
| | obj.addProperty("App::PropertyBool", "Closed", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "Create a face if this curve is closed") |
| | obj.addProperty("App::PropertyBool", "MakeFace", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "The length of this object") |
| | obj.addProperty("App::PropertyLength", "Length", "Draft", _tip, locked=True) |
| |
|
| | _tip = QT_TRANSLATE_NOOP("App::Property", "The area of this object") |
| | obj.addProperty("App::PropertyArea", "Area", "Draft", _tip, locked=True) |
| |
|
| | obj.MakeFace = params.get_param("MakeFaceMode") |
| | obj.Closed = False |
| | obj.Degree = 3 |
| | obj.Continuity = [] |
| | |
| | obj.setEditorMode("Continuity", 1) |
| |
|
| | def onDocumentRestored(self, obj): |
| | super().onDocumentRestored(obj) |
| | gui_utils.restore_view_object( |
| | obj, vp_module="view_bezcurve", vp_class="ViewProviderBezCurve" |
| | ) |
| |
|
| | def execute(self, fp): |
| | if self.props_changed_placement_only(): |
| | fp.positionBySupport() |
| | self.props_changed_clear() |
| | return |
| |
|
| | self.createGeometry(fp) |
| | fp.positionBySupport() |
| | self.props_changed_clear() |
| |
|
| | def _segpoleslst(self, fp): |
| | """Split the points into segments.""" |
| | if not fp.Closed and len(fp.Points) >= 2: |
| | poles = fp.Points[1:] |
| | elif fp.Closed and len(fp.Points) >= fp.Degree: |
| | |
| | poles = fp.Points[1:] + fp.Points[0:1] |
| | else: |
| | poles = [] |
| | return [poles[x : x + fp.Degree] for x in range(0, len(poles), (fp.Degree or 1))] |
| |
|
| | def resetcontinuity(self, fp): |
| | fp.Continuity = [0] * (len(self._segpoleslst(fp)) - 1 + 1 * fp.Closed) |
| | |
| | |
| | |
| |
|
| | def onChanged(self, fp, prop): |
| | self.props_changed_store(prop) |
| |
|
| | if prop == "Closed": |
| | |
| | oldlen = len(fp.Continuity) |
| | newlen = len(self._segpoleslst(fp)) - 1 + 1 * fp.Closed |
| | if oldlen > newlen: |
| | fp.Continuity = fp.Continuity[:newlen] |
| | if oldlen < newlen: |
| | fp.Continuity = fp.Continuity + [0] * (newlen - oldlen) |
| |
|
| | if ( |
| | hasattr(fp, "Closed") |
| | and fp.Closed |
| | and prop in ["Points", "Degree", "Closed"] |
| | and len(fp.Points) % fp.Degree |
| | ): |
| | |
| | fp.Points = fp.Points[: (fp.Degree * (len(fp.Points) // fp.Degree))] |
| | |
| |
|
| | if prop in ["Degree"] and fp.Degree >= 1: |
| | self.resetcontinuity(fp) |
| |
|
| | if prop in ["Points", "Degree", "Continuity", "Closed"]: |
| | self.createGeometry(fp) |
| |
|
| | def createGeometry(self, fp): |
| | import Part |
| |
|
| | plm = fp.Placement |
| | if fp.Points: |
| | startpoint = fp.Points[0] |
| | edges = [] |
| | for segpoles in self._segpoleslst(fp): |
| | |
| | c = Part.BezierCurve() |
| | c.increase(len(segpoles)) |
| | c.setPoles([startpoint] + segpoles) |
| | edges.append(Part.Edge(c)) |
| | startpoint = segpoles[-1] |
| | w = Part.Wire(edges) |
| | if fp.Closed and w.isClosed(): |
| | try: |
| | if hasattr(fp, "MakeFace"): |
| | if fp.MakeFace: |
| | w = Part.Face(w) |
| | else: |
| | w = Part.Face(w) |
| | except Part.OCCError: |
| | pass |
| | fp.Shape = w |
| | if hasattr(fp, "Area") and hasattr(w, "Area"): |
| | fp.Area = w.Area |
| | if hasattr(fp, "Length") and hasattr(w, "Length"): |
| | fp.Length = w.Length |
| | fp.Placement = plm |
| |
|
| | @classmethod |
| | def symmetricpoles(cls, knot, p1, p2): |
| | """Make two poles symmetric respective to the knot.""" |
| | p1h = App.Vector(p1) |
| | p2h = App.Vector(p2) |
| | p1h.multiply(0.5) |
| | p2h.multiply(0.5) |
| | return (knot + p1h - p2h, knot + p2h - p1h) |
| |
|
| | @classmethod |
| | def tangentpoles(cls, knot, p1, p2, allowsameside=False): |
| | """Make two poles have the same tangent at knot.""" |
| | p12n = p2.sub(p1) |
| | p12n.normalize() |
| | p1k = knot - p1 |
| | p2k = knot - p2 |
| | p1k_ = App.Vector(p12n) |
| | kon12 = p1k * p12n |
| | if allowsameside or not (kon12 < 0 or p2k * p12n > 0): |
| | p1k_.multiply(kon12) |
| | pk_k = knot - p1 - p1k_ |
| | return (p1 + pk_k, p2 + pk_k) |
| | else: |
| | return cls.symmetricpoles(knot, p1, p2) |
| |
|
| | @staticmethod |
| | def modifysymmetricpole(knot, p1): |
| | """calculate the coordinates of the opposite pole |
| | of a symmetric knot""" |
| | return knot + knot - p1 |
| |
|
| | @staticmethod |
| | def modifytangentpole(knot, p1, oldp2): |
| | """calculate the coordinates of the opposite pole |
| | of a tangent knot""" |
| | pn = knot - p1 |
| | pn.normalize() |
| | pn.multiply((knot - oldp2).Length) |
| | return pn + knot |
| |
|
| |
|
| | |
| | _BezCurve = BezCurve |
| |
|
| | |
| |
|