| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides the object code for the PathTwistedArray object. |
| | |
| | The copies will be placed along a path like a polyline, spline, or bezier |
| | curve, and the copies are twisted around the path by a given rotation |
| | parameter. |
| | |
| | This array was developed in order to build a `twisted bridge` object. |
| | |
| | See https://forum.freecad.org/viewtopic.php?f=23&t=49617 |
| | |
| | A `twisted bridge` would consist of three parts: |
| | 1. The ribcage composed of a twisted array generated from a frame |
| | and a path. |
| | 2. The `tunnel` object produced by lofting or sweeping the internal twisted |
| | profiles of the ribcage along the path. |
| | 3. The `walkway` object on which the person can stand; it is generated |
| | from the path, and the internal width of the ribcage profile. |
| | |
| | This module builds only the first element, the twisted ribcage. |
| | |
| | The tunnel and walkway are built with the `twisted bridge` |
| | object in the Arch Workbench. |
| | """ |
| | |
| | |
| | |
| |
|
| | import draftgeoutils.geo_arrays as geo |
| | from draftutils.messages import _log |
| |
|
| |
|
| | def QT_TRANSLATE_NOOP(ctx, txt): |
| | return txt |
| |
|
| |
|
| | from draftobjects.draftlink import DraftLink |
| |
|
| | |
| | |
| |
|
| |
|
| | class PathTwistedArray(DraftLink): |
| | """The PathTwistedArray object. |
| | |
| | This array distributes copies of an object along a path like a polyline, |
| | spline, or bezier curve, and the copies are twisted around the path |
| | by a given rotation parameter. |
| | """ |
| |
|
| | def __init__(self, obj): |
| | super().__init__(obj, "PathTwistedArray") |
| |
|
| | def attach(self, obj): |
| | """Set up the properties when the object is attached.""" |
| | self.set_properties(obj) |
| | super().attach(obj) |
| |
|
| | def set_properties(self, obj): |
| | """Set properties only if they don't exist.""" |
| | if hasattr(obj, "PropertiesList"): |
| | properties = obj.PropertiesList |
| | else: |
| | properties = [] |
| |
|
| | if "Base" not in properties: |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "Base", |
| | "Objects", |
| | QT_TRANSLATE_NOOP("App::Property", "The base object that will be duplicated."), |
| | locked=True, |
| | ) |
| | obj.Base = None |
| |
|
| | if "PathObject" not in properties: |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "PathObject", |
| | "Objects", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The object along which the copies will be distributed. It must contain 'Edges'.", |
| | ), |
| | locked=True, |
| | ) |
| | obj.PathObject = None |
| |
|
| | if "Fuse" not in properties: |
| | _tip = QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Specifies if the copies " |
| | "should be fused together " |
| | "if they touch each other (slower)", |
| | ) |
| | obj.addProperty("App::PropertyBool", "Fuse", "Objects", _tip, locked=True) |
| | obj.Fuse = False |
| |
|
| | if "Count" not in properties: |
| | obj.addProperty( |
| | "App::PropertyInteger", |
| | "Count", |
| | "Objects", |
| | QT_TRANSLATE_NOOP("App::Property", "Number of copies to create."), |
| | locked=True, |
| | ) |
| | obj.Count = 15 |
| |
|
| | if "RotationFactor" not in properties: |
| | obj.addProperty( |
| | "App::PropertyFloat", |
| | "RotationFactor", |
| | "Objects", |
| | QT_TRANSLATE_NOOP("App::Property", "Rotation factor of the twisted array."), |
| | locked=True, |
| | ) |
| | obj.RotationFactor = 0.25 |
| |
|
| | if self.use_link and "ExpandArray" not in properties: |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "ExpandArray", |
| | "Objects", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "Show the individual array elements (only for Link arrays)" |
| | ), |
| | locked=True, |
| | ) |
| | obj.ExpandArray = False |
| | obj.setPropertyStatus("Shape", "Transient") |
| |
|
| | if not self.use_link: |
| | if "PlacementList" not in properties: |
| | _tip = QT_TRANSLATE_NOOP("App::Property", "The placement for each array element") |
| | obj.addProperty( |
| | "App::PropertyPlacementList", "PlacementList", "Objects", _tip, locked=True |
| | ) |
| | obj.PlacementList = [] |
| |
|
| | def linkSetup(self, obj): |
| | """Set up the object as a link object.""" |
| | super().linkSetup(obj) |
| | obj.configLinkProperty(ElementCount="Count") |
| |
|
| | def onDocumentRestored(self, obj): |
| | super().onDocumentRestored(obj) |
| | |
| | |
| | if hasattr(obj, "Fuse") and hasattr(obj, "PlacementList"): |
| | return |
| |
|
| | if not hasattr(obj, "Fuse"): |
| | _log("v1.0, " + obj.Name + ", added 'Fuse' property") |
| | if not hasattr(obj, "PlacementList"): |
| | _log("v1.1, " + obj.Name + ", added hidden property 'PlacementList'") |
| |
|
| | self.set_properties(obj) |
| | self.execute(obj) |
| |
|
| | def execute(self, obj): |
| | """Execute when the object is created or recomputed.""" |
| | if self.props_changed_placement_only(obj) or not obj.Base or not obj.PathObject: |
| | self.props_changed_clear() |
| | return |
| |
|
| | |
| | array_placement = obj.Placement |
| |
|
| | path = obj.PathObject |
| | count = obj.Count |
| | rot_factor = obj.RotationFactor |
| |
|
| | copy_placements, _ = geo.get_twisted_placements(path, count=count, rot_factor=rot_factor) |
| |
|
| | self.buildShape(obj, array_placement, copy_placements) |
| | self.props_changed_clear() |
| | return not self.use_link |
| |
|
| |
|
| | |
| |
|