diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86512f2fe..5456761dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,11 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
* Fixed `ValueErrorException` in `as_dict()` method of `BTLxProcessingParams` class by ensuring precision specifiers are used with floats.
+* Removed model argument from `BTLxWriter` in the GH component and updated it to always return the BTLx string.
+* Fixed a bug in `compas_timber.Fabrication.StepJointNotch` related to the `orientation` and `strut_inclination` parameters.
* Fixed the error message when beam endpoints coincide, e.g. when a closed polyline is used as input.
* Changed `index` input of `ShowFeatureErrors` and `ShowJoiningErrors` do have default value of 0.
* Fixed spelling of `BeamJoinningError` to `BeamJoiningError`.
* Changed `process_joinery()` method to handle `BeamJoiningError` exceptions and return them. Also updated `Model` GH component.
* Updated `add_joint_error()` method in `DebugInformation` class to handle lists.
+* Changed `compas_timber.fabrication.Lap` so that the volume is generated fully from the relevant BTLx params.
+* Refactored `compas_timber.connections.LapJoint` to comply with the new system.
+* Changed `THalfLapJoint`, `LHalfLapJoint`, `XHalfLapJoint` from `compas_timber.connections` so that they use the `Lap` BTLx processing.
+* Renamed all `X/T/LHalfLapJoint` classes to `X/T/LLapJoint`.
### Removed
@@ -37,8 +43,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Updated the API documentation for `connections`, `elements`, `fabrication`, `ghpython`, `planning` packages.
* Refactored all btlx `process` references to `processing`, including base classes, properties, variables, and docstrings.
* Refactored `BTLx` to `BTLxWriter` in the `compas_timber.Fabrication` package.
-* Removed model argument from `BTLxWriter` in the GH component and updated it to always return the BTLx string.
-* Fixed a bug in `compas_timber.Fabrication.StepJointNotch` related to the `orientation` and `strut_inclination` parameters.
### Removed
diff --git a/docs/api/compas_timber.connections.rst b/docs/api/compas_timber.connections.rst
index 6657aa9c3..dc7ae2d7f 100644
--- a/docs/api/compas_timber.connections.rst
+++ b/docs/api/compas_timber.connections.rst
@@ -18,16 +18,16 @@ Classes
LapJoint
LButtJoint
LFrenchRidgeLapJoint
- LHalfLapJoint
+ LLapJoint
LMiterJoint
NullJoint
TBirdsmouthJoint
TButtJoint
TDovetailJoint
- THalfLapJoint
+ TLapJoint
TStepJoint
TenonMortiseJoint
- XHalfLapJoint
+ XLapJoint
Functions
=========
diff --git a/docs/tutorials/grasshopper/joint_rules.rst b/docs/tutorials/grasshopper/joint_rules.rst
index 55fc8f329..3703e6759 100644
--- a/docs/tutorials/grasshopper/joint_rules.rst
+++ b/docs/tutorials/grasshopper/joint_rules.rst
@@ -7,7 +7,7 @@ The Joints between :doc:`beams` are defined by Joint Rules. There are four kinds
.. note::
**Joint Topologies**
-
+
There are three main topologies of how beams can connect to each other: **L**, **T** and **X**.
.. image:: ../images/joint_topologies_diagramm.png
@@ -23,7 +23,7 @@ Joint Rules Components
**Dynamic Components**
Joint Rules Components are dynamic: First place them on the Grasshopper Canvas. Now you can define the Joint they should apply by Right-Click & Selection from the Drop-Down List. The Inputs might change because every Joint has its own specific settings.
-
+
.. image:: ../images/joint_rules_dynamic.gif
:width: 55%
@@ -32,11 +32,11 @@ Joint Rules Components
Default Joint Rules
^^^^^^^^^^^^^^^^^^^
-This Component applies a L-Miter to all L-Topologies, a T-Butt to all T-Topologies and a X-HalfLap to all X-Topologies.
+This Component applies a L-Miter to all L-Topologies, a T-Butt to all T-Topologies and a X-HalfLap to all X-Topologies.
.. image:: ../images/gh_joint_rules_default.png
:width: 20%
-
+
|
Topological Joint Rules
@@ -57,7 +57,7 @@ These Joint Rules are more specific and will overwrite the Default Joint Rules.
Category Joint Rules
^^^^^^^^^^^^^^^^^^^^
-This Joint Rule will overwrite all Topological Joint Rules. The Component defines a Joint type for all Joints between two beam Categories. The Categories are assigned through the string-input `Category` in the component :code:`Beam`. The inputs are variable and depend on the joint type.
+This Joint Rule will overwrite all Topological Joint Rules. The Component defines a Joint type for all Joints between two beam Categories. The Categories are assigned through the string-input `Category` in the component :code:`Beam`. The inputs are variable and depend on the joint type.
.. image:: ../images/gh_joint_rules_category.png
:width: 40%
@@ -104,7 +104,7 @@ Inputs:
|
-L-HalfLap
+L-Lap
^^^^^^^^^
The *L-Half Lap* topology is when two beams meet at their ends at an angle. An L-Half Lap joint extends the two beams while removing the upper half of the overlap of one beam and the lower half of the overlaps the other to create a clean corner joint.
@@ -166,7 +166,7 @@ Inputs:
|
-T-HalfLap
+T-Lap
^^^^^^^^^
A T-Half Lap joint crates an overlap between the *main beam* and the *cross beam*. The *cross beam* is extended to the opposite face of the *main beam* and cut flush with it to create a planar surface.
@@ -182,7 +182,7 @@ Inputs:
|
-X-HalfLap
+X-Lap
^^^^^^^^^
The X-Half Lap joint removes the upper half of the overlap from one beam and the lower half from the other.
@@ -207,7 +207,7 @@ Joint L Topology T Topology X Topology
============ =========== =========== ===========
Butt X X
Miter x
-HalfLap X X X
+Lap X X X
French Ridge X
============ =========== =========== ===========
diff --git a/examples/Grasshopper/tests/test_halflap.ghx b/examples/Grasshopper/tests/test_halflap.ghx
new file mode 100644
index 000000000..5d02de605
--- /dev/null
+++ b/examples/Grasshopper/tests/test_halflap.ghx
@@ -0,0 +1,11780 @@
+
+
+
+
+
+
+ -
+ 0
+ 2
+ 2
+
+
+
+
+
+ -
+ 1
+ 0
+ 7
+
+
+
+
+
+ - fa5d732c-b31a-4064-bc30-fdd75169872b
+ - Shaded
+ - Selection
+ - 1
+ -
+ 100;150;0;0
+
+ -
+ 100;0;150;0
+
+
+
+
+
+ - 638584536051543966
+
+ - test_halflap.ghx
+
+
+
+
+ - 0
+
+
+
+
+ -
+ -378
+ -1252
+
+ - 1.2750001
+
+
+
+
+ - 0
+
+
+
+
+
+
+ - 0
+
+
+
+
+ - 2
+
+
+
+
+ - GhPython, Version=7.37.24107.15001, Culture=neutral, PublicKeyToken=null
+ - 7.37.24107.15001
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+
+
+ - Pufferfish, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null
+ - 3.0.0.0
+ - Michael Pryor
+ - 1c9de8a1-315f-4c56-af06-8f69fee80a7a
+ - Pufferfish
+ - 3.0.0.0
+
+
+
+
+
+
+ - 102
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;255;255;255
+
+ - A group of Grasshopper objects
+ - 18d2fa4a-de1b-4cfd-bf80-4b8d5fa0182f
+ - e22e52d8-5a24-494f-8bd6-2f5c368cced0
+ - 9d82d577-6636-40a6-89a2-6f58d4a8a308
+ - 7f188938-0adc-40a1-aaa1-c82bf68c0621
+ - 13ac4c40-1353-44eb-ba9c-dfde3983e024
+ - 5f1aeb7c-013f-4d68-9e03-9abc339aaf8d
+ - 6
+ - fe7c137b-ed91-4aea-b1db-a11f5362c7b3
+ - Group
+
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;255;255;255
+
+ - A group of Grasshopper objects
+ - b86d8575-a631-44ae-831c-a26d103050c7
+ - d785f7da-1ae4-4767-9398-648e133a5bd2
+ - 98e83dd4-f0d0-4701-ba42-e474164cfd82
+ - 8fa01d08-afaf-4258-bace-d765ebf9577f
+ - d475e429-c48f-4bea-b061-06ab3815cfd3
+ - 702b1bbc-2b58-478b-b3ba-16a48af876bd
+ - bbf2811a-72c7-4b27-aabe-22952d508aa7
+ - ed5575ff-1733-49b5-87a3-30e4e79a9149
+ - 60dc433a-3e4b-4822-9277-ffa1be8ca93b
+ - 2858ecb7-755a-4a0e-be46-21a44003594a
+ - 1bf6c7ca-f719-453d-ae24-8fb4402a0f6e
+ - a733d08f-1f60-40f4-b07a-a99476c2a8ab
+ - 2cbb77cb-271b-4c58-b3b5-67700d482c61
+ - 13
+ - f1faf354-f330-48d3-84f1-385f98665086
+ - Group
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - d9d51b18-d5af-4f8f-822f-9fbd0f30d642
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 842
+ 1368
+ 166
+ 20
+
+ -
+ 842.9432
+ 1368.29
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 120
+ - 0
+ - 0
+ - 120
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - true
+ - 18d2fa4a-de1b-4cfd-bf80-4b8d5fa0182f
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ 85
+ 1307
+ 50
+ 24
+
+ -
+ 110.3377
+ 1319.804
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZK6UdYMd5/q7Dh/+/5cPK155QKIotjs+Ncjh+PWXjcKMDx0+oYkzMSBABPuUNsuyJQ4gl7yqvNL2v56pASbHzTCYAAA=
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - 478fc3b1-c4f8-4087-8fe6-c2905e21be9a
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 869
+ 1583
+ 166
+ 20
+
+ -
+ 869.7459
+ 1583.228
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 100
+ - 0
+ - 0
+ - 80
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - da50cc8d-94c7-4348-b6e9-2f46272647d3
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 869
+ 1607
+ 169
+ 20
+
+ -
+ 869.5651
+ 1607.486
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 100
+ - 0
+ - 0
+ - 100
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;255;255;255
+
+ - A group of Grasshopper objects
+ - 1631fe30-c80f-49ec-9128-96481121ad52
+ - ca0ea429-34fb-4ce1-a30e-2c14ddc1552e
+ - 645777e9-3e35-4980-b9de-1af150e02dd0
+ - 0427415b-7064-4ecf-875e-ad8efb142eee
+ - c285f14a-7ead-4d46-9545-12950096c0b4
+ - 5
+ - 1dc4a321-0021-4a13-90df-e852c7e4e773
+ - Group
+ - MainBeam
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;255;255;255
+
+ - A group of Grasshopper objects
+ - 18d2fa4a-de1b-4cfd-bf80-4b8d5fa0182f
+ - 1
+ - e22e52d8-5a24-494f-8bd6-2f5c368cced0
+ - Group
+ - CrossBeam
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - c64fa590-0923-4f6c-bc02-60bf9bf5e33c
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 844
+ 1343
+ 166
+ 20
+
+ -
+ 844.4375
+ 1343.346
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 100
+ - 0
+ - 0
+ - 60
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: Beam
+
+
+
+
+ - """Creates a Beam from a LineCurve."""
+
+import rhinoscriptsyntax as rs
+from compas.scene import Scene
+from compas_rhino.conversions import line_to_compas
+from compas_rhino.conversions import vector_to_compas
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Error
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+from Rhino.RhinoDoc import ActiveDoc
+
+from compas_timber.elements import Beam as CTBeam
+from compas_timber.ghpython.rhino_object_name_attributes import update_rhobj_attributes_name
+
+
+class Beam_fromCurve(component):
+ def RunScript(self, centerline, z_vector, width, height, category, updateRefObj):
+ # minimum inputs required
+ if not centerline:
+ self.AddRuntimeMessage(Warning, "Input parameter 'Centerline' failed to collect data")
+ if not width:
+ length = self._get_centerline_length(centerline)
+ width = [length / 20]
+ if not height:
+ length = self._get_centerline_length(centerline)
+ height = [length / 10]
+
+ # reformat unset parameters for consistency
+ if not z_vector:
+ z_vector = [None]
+ if not category:
+ category = [None]
+
+ beams = []
+ blanks = []
+ scene = Scene()
+
+ if centerline and height and width:
+ # check list lengths for consistency
+ N = len(centerline)
+ if len(z_vector) not in (0, 1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'ZVector' I need either none, one or the same number of inputs as the Crv parameter."
+ )
+ if len(width) not in (1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'W' I need either one or the same number of inputs as the Crv parameter."
+ )
+ if len(height) not in (1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'H' I need either one or the same number of inputs as the Crv parameter."
+ )
+ if len(category) not in (0, 1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'Category' I need either none, one or the same number of inputs as the Crv parameter."
+ )
+
+ # duplicate data if None or single value
+ if len(z_vector) != N:
+ z_vector = [z_vector[0] for _ in range(N)]
+ if len(width) != N:
+ width = [width[0] for _ in range(N)]
+ if len(height) != N:
+ height = [height[0] for _ in range(N)]
+ if len(category) != N:
+ category = [category[0] for _ in range(N)]
+
+ for line, z, w, h, c in zip(centerline, z_vector, width, height, category):
+ guid, geometry = self._get_guid_and_geometry(line)
+ rhino_line = rs.coerceline(geometry)
+ line = line_to_compas(rhino_line)
+
+ z = vector_to_compas(z) if z else None
+ beam = CTBeam.from_centerline(centerline=line, width=w, height=h, z_vector=z)
+ beam.attributes["rhino_guid"] = str(guid) if guid else None
+ beam.attributes["category"] = c
+ print(guid)
+ if updateRefObj and guid:
+ update_rhobj_attributes_name(guid, "width", str(w))
+ update_rhobj_attributes_name(guid, "height", str(h))
+ update_rhobj_attributes_name(guid, "zvector", str(list(beam.frame.zaxis)))
+ update_rhobj_attributes_name(guid, "category", c)
+
+ beams.append(beam)
+ scene.add(beam.blank)
+
+ blanks = scene.draw()
+
+ return beams, blanks
+
+ def _get_guid_and_geometry(self, line):
+ # internalized curves and GH geometry will not have persistent GUIDs, referenced Rhino objects will
+ # type hint on the input has to be 'ghdoc' for this to work
+ guid = None
+ geometry = line
+ rhino_obj = ActiveDoc.Objects.FindId(line)
+ if rhino_obj:
+ guid = line
+ geometry = rhino_obj.Geometry
+ return guid, geometry
+
+ def _get_centerline_length(self, centerline):
+ centerline_length = []
+ for i in centerline:
+ centerline_length.append(rs.CurveLength(i))
+ if centerline_length:
+ length_average = sum(centerline_length) / len(centerline_length)
+ else:
+ length_average = 1
+ return length_average
+
+ - Creates a Beam from a LineCurve.
+ -
+ 190
+ 190
+
+ -
+ 835
+ 874
+
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAAWtJREFUSEutlDFqhEAYhecEegALS0vBOmJnqZWkEQQrGyHYWFhY5hIBj+AR3Bt4gYBlSrs0gUx4Q5aYf2Z2xmSFbwtn5v3sm2+Xcc7Zf+m67jEIgnfGGD9SVZW8+Sx1Xb84jvNJw6dp4tu2yQdsGYbBT9P0lQb7vs/XdeXLsnDXdeWDNugqSZKE7/vOx3H8eU8PmyjLcqbBAKEIz/P89xoN0IFK4jh+o8GoYZ5nUQvqoetSkApU4nneBz0chqG4SFyo6JuG2wwoiuIiHfpWEJUIFRXrxgGoJIqiXTpwUBDfgK5J0GDQtu2Tym1JQRqmgobrKlEqaMM1uO/7B5XbQKugAfzKRXjTNM+qSowKaoBxMA/ZLMuylW4AVgoqgBgQ5NoMPrSPUUEC7o/eKR5pI8BD3+lAvaiZht9lAMSAIDT4LgPwd33sW8WfB0BBGqbi9ICjgjacGkAVtMF6gEpBG4wDbilow80BJgVt+AJsC3YHyBQqjwAAAABJRU5ErkJggg==
+
+ - false
+ - bf722eeb-fc58-4d41-9636-a4abe4ff1b05
+ - true
+ - true
+ - CT: Beam
+ - Beam
+
+
+
+
+ -
+ 1057
+ 1288
+ 145
+ 124
+
+ -
+ 1148
+ 1350
+
+
+
+
+
+ - 6
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - true
+ - Referenced curve or line, Guid of curve or line in the active Rhino document.
+ - a1e698be-e5cc-4007-ad45-7ad242daacb1
+ - Centerline
+ - Centerline
+ - true
+ - 1
+ - true
+ - 22a7c548-2a3a-46f3-9aed-df6d798fd1fe
+ - 1
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1059
+ 1290
+ 74
+ 20
+
+ -
+ 1097.5
+ 1300
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Vector defining the orientation of the cross-section (corresponds to the height dimension). If None, a default will be taken.
+ - ea847ed7-5489-459a-97d1-5c8d044128c3
+ - ZVector
+ - ZVector
+ - true
+ - 1
+ - true
+ - 0
+ - 15a50725-e3d3-4075-9f7c-142ba5f40747
+
+
+
+
+ -
+ 1059
+ 1310
+ 74
+ 20
+
+ -
+ 1097.5
+ 1320
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Width of the cross-section (usually the smaller dimension).
+ - 019cea01-9ec7-4a75-b6f2-0c67c3be7aef
+ - Width
+ - Width
+ - true
+ - 1
+ - true
+ - da50cc8d-94c7-4348-b6e9-2f46272647d3
+ - 1
+ - 39fbc626-7a01-46ab-a18e-ec1c0c41685b
+
+
+
+
+ -
+ 1059
+ 1330
+ 74
+ 20
+
+ -
+ 1097.5
+ 1340
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Height of the cross-section (usually the larger dimension).
+ - 24087a33-ea83-440f-83af-61b49d8d317c
+ - Height
+ - Height
+ - true
+ - 1
+ - true
+ - da50cc8d-94c7-4348-b6e9-2f46272647d3
+ - 1
+ - 39fbc626-7a01-46ab-a18e-ec1c0c41685b
+
+
+
+
+ -
+ 1059
+ 1350
+ 74
+ 20
+
+ -
+ 1097.5
+ 1360
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Category of a beam.
+ - 0c93adef-0bc0-421a-9c08-5af17ce6be3c
+ - Category
+ - Category
+ - true
+ - 1
+ - true
+ - 0
+ - 37261734-eec7-4f50-b6a8-b8d1f3c4396b
+
+
+
+
+ -
+ 1059
+ 1370
+ 74
+ 20
+
+ -
+ 1097.5
+ 1380
+
+
+
+
+
+
+
+ - true
+ - (optional) If True, the attributes in the referenced object will be updated/overwritten with the new values given here.
+ - 8203ee9f-c2d2-42b4-b17c-51f97c6eddd2
+ - updateRefObj
+ - updateRefObj
+ - true
+ - 0
+ - true
+ - 0
+ - d60527f5-b5af-4ef6-8970-5f96fe412559
+
+
+
+
+ -
+ 1059
+ 1390
+ 74
+ 20
+
+ -
+ 1097.5
+ 1400
+
+
+
+
+
+
+
+ - Beam object(s).
+ - d2365ef0-f85c-4709-9361-522790ff3ed5
+ - Beam
+ - Beam
+ - false
+ - 0
+
+
+
+
+ -
+ 1163
+ 1290
+ 37
+ 60
+
+ -
+ 1181.5
+ 1320
+
+
+
+
+
+
+
+ - Shape of the beam's blank.
+ - b8e540ec-b143-467b-a0d4-9b8c96f0c6ee
+ - Blank
+ - Blank
+ - false
+ - 0
+
+
+
+
+ -
+ 1163
+ 1350
+ 37
+ 60
+
+ -
+ 1181.5
+ 1380
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: Beam
+
+
+
+
+ - """Creates a Beam from a LineCurve."""
+
+import rhinoscriptsyntax as rs
+from compas.scene import Scene
+from compas_rhino.conversions import line_to_compas
+from compas_rhino.conversions import vector_to_compas
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Error
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+from Rhino.RhinoDoc import ActiveDoc
+
+from compas_timber.elements import Beam as CTBeam
+from compas_timber.ghpython.rhino_object_name_attributes import update_rhobj_attributes_name
+
+
+class Beam_fromCurve(component):
+ def RunScript(self, centerline, z_vector, width, height, category, updateRefObj):
+ # minimum inputs required
+ if not centerline:
+ self.AddRuntimeMessage(Warning, "Input parameter 'Centerline' failed to collect data")
+ if not width:
+ length = self._get_centerline_length(centerline)
+ width = [length / 20]
+ if not height:
+ length = self._get_centerline_length(centerline)
+ height = [length / 10]
+
+ # reformat unset parameters for consistency
+ if not z_vector:
+ z_vector = [None]
+ if not category:
+ category = [None]
+
+ beams = []
+ blanks = []
+ scene = Scene()
+
+ if centerline and height and width:
+ # check list lengths for consistency
+ N = len(centerline)
+ if len(z_vector) not in (0, 1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'ZVector' I need either none, one or the same number of inputs as the Crv parameter."
+ )
+ if len(width) not in (1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'W' I need either one or the same number of inputs as the Crv parameter."
+ )
+ if len(height) not in (1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'H' I need either one or the same number of inputs as the Crv parameter."
+ )
+ if len(category) not in (0, 1, N):
+ self.AddRuntimeMessage(
+ Error, " In 'Category' I need either none, one or the same number of inputs as the Crv parameter."
+ )
+
+ # duplicate data if None or single value
+ if len(z_vector) != N:
+ z_vector = [z_vector[0] for _ in range(N)]
+ if len(width) != N:
+ width = [width[0] for _ in range(N)]
+ if len(height) != N:
+ height = [height[0] for _ in range(N)]
+ if len(category) != N:
+ category = [category[0] for _ in range(N)]
+
+ for line, z, w, h, c in zip(centerline, z_vector, width, height, category):
+ guid, geometry = self._get_guid_and_geometry(line)
+ rhino_line = rs.coerceline(geometry)
+ line = line_to_compas(rhino_line)
+
+ z = vector_to_compas(z) if z else None
+ beam = CTBeam.from_centerline(centerline=line, width=w, height=h, z_vector=z)
+ beam.attributes["rhino_guid"] = str(guid) if guid else None
+ beam.attributes["category"] = c
+ print(guid)
+ if updateRefObj and guid:
+ update_rhobj_attributes_name(guid, "width", str(w))
+ update_rhobj_attributes_name(guid, "height", str(h))
+ update_rhobj_attributes_name(guid, "zvector", str(list(beam.frame.zaxis)))
+ update_rhobj_attributes_name(guid, "category", c)
+
+ beams.append(beam)
+ scene.add(beam.blank)
+
+ blanks = scene.draw()
+
+ return beams, blanks
+
+ def _get_guid_and_geometry(self, line):
+ # internalized curves and GH geometry will not have persistent GUIDs, referenced Rhino objects will
+ # type hint on the input has to be 'ghdoc' for this to work
+ guid = None
+ geometry = line
+ rhino_obj = ActiveDoc.Objects.FindId(line)
+ if rhino_obj:
+ guid = line
+ geometry = rhino_obj.Geometry
+ return guid, geometry
+
+ def _get_centerline_length(self, centerline):
+ centerline_length = []
+ for i in centerline:
+ centerline_length.append(rs.CurveLength(i))
+ if centerline_length:
+ length_average = sum(centerline_length) / len(centerline_length)
+ else:
+ length_average = 1
+ return length_average
+
+ - Creates a Beam from a LineCurve.
+ -
+ 1352
+ 1040
+
+ -
+ 835
+ 874
+
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAAWtJREFUSEutlDFqhEAYhecEegALS0vBOmJnqZWkEQQrGyHYWFhY5hIBj+AR3Bt4gYBlSrs0gUx4Q5aYf2Z2xmSFbwtn5v3sm2+Xcc7Zf+m67jEIgnfGGD9SVZW8+Sx1Xb84jvNJw6dp4tu2yQdsGYbBT9P0lQb7vs/XdeXLsnDXdeWDNugqSZKE7/vOx3H8eU8PmyjLcqbBAKEIz/P89xoN0IFK4jh+o8GoYZ5nUQvqoetSkApU4nneBz0chqG4SFyo6JuG2wwoiuIiHfpWEJUIFRXrxgGoJIqiXTpwUBDfgK5J0GDQtu2Tym1JQRqmgobrKlEqaMM1uO/7B5XbQKugAfzKRXjTNM+qSowKaoBxMA/ZLMuylW4AVgoqgBgQ5NoMPrSPUUEC7o/eKR5pI8BD3+lAvaiZht9lAMSAIDT4LgPwd33sW8WfB0BBGqbi9ICjgjacGkAVtMF6gEpBG4wDbilow80BJgVt+AJsC3YHyBQqjwAAAABJRU5ErkJggg==
+
+ - false
+ - da647de1-dd10-4e78-b1d6-8bd3064b4856
+ - true
+ - true
+ - CT: Beam
+ - Beam
+
+
+
+
+ -
+ 1047
+ 1495
+ 145
+ 124
+
+ -
+ 1138
+ 1557
+
+
+
+
+
+ - 6
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - true
+ - Referenced curve or line, Guid of curve or line in the active Rhino document.
+ - 8fbbdfc0-ddf1-497d-bab5-127859a8497d
+ - Centerline
+ - Centerline
+ - true
+ - 1
+ - true
+ - bd53ef51-7947-47ac-ad07-27b174cf4ead
+ - 1
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1049
+ 1497
+ 74
+ 20
+
+ -
+ 1087.5
+ 1507
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Vector defining the orientation of the cross-section (corresponds to the height dimension). If None, a default will be taken.
+ - 30b9a2e1-c92a-4d4f-bfb7-b4e28b54225b
+ - ZVector
+ - ZVector
+ - true
+ - 1
+ - true
+ - 0
+ - 15a50725-e3d3-4075-9f7c-142ba5f40747
+
+
+
+
+ -
+ 1049
+ 1517
+ 74
+ 20
+
+ -
+ 1087.5
+ 1527
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Width of the cross-section (usually the smaller dimension).
+ - 99f9429c-66da-4746-9d35-3608cee70f2a
+ - Width
+ - Width
+ - true
+ - 1
+ - true
+ - da50cc8d-94c7-4348-b6e9-2f46272647d3
+ - 1
+ - 39fbc626-7a01-46ab-a18e-ec1c0c41685b
+
+
+
+
+ -
+ 1049
+ 1537
+ 74
+ 20
+
+ -
+ 1087.5
+ 1547
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Height of the cross-section (usually the larger dimension).
+ - c72cc71d-f13e-4d93-a0f9-0ae82b991a79
+ - Height
+ - Height
+ - true
+ - 1
+ - true
+ - da50cc8d-94c7-4348-b6e9-2f46272647d3
+ - 1
+ - 39fbc626-7a01-46ab-a18e-ec1c0c41685b
+
+
+
+
+ -
+ 1049
+ 1557
+ 74
+ 20
+
+ -
+ 1087.5
+ 1567
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Category of a beam.
+ - 87270c6b-2d61-415d-a9e6-2b8997847e24
+ - Category
+ - Category
+ - true
+ - 1
+ - true
+ - 0
+ - 37261734-eec7-4f50-b6a8-b8d1f3c4396b
+
+
+
+
+ -
+ 1049
+ 1577
+ 74
+ 20
+
+ -
+ 1087.5
+ 1587
+
+
+
+
+
+
+
+ - true
+ - (optional) If True, the attributes in the referenced object will be updated/overwritten with the new values given here.
+ - 24a45657-2e86-4574-b96b-fdfc1e768096
+ - updateRefObj
+ - updateRefObj
+ - true
+ - 0
+ - true
+ - 0
+ - d60527f5-b5af-4ef6-8970-5f96fe412559
+
+
+
+
+ -
+ 1049
+ 1597
+ 74
+ 20
+
+ -
+ 1087.5
+ 1607
+
+
+
+
+
+
+
+ - Beam object(s).
+ - 287d3ad7-dfd0-4415-b5a0-ff41819a7aba
+ - Beam
+ - Beam
+ - false
+ - 0
+
+
+
+
+ -
+ 1153
+ 1497
+ 37
+ 60
+
+ -
+ 1171.5
+ 1527
+
+
+
+
+
+
+
+ - Shape of the beam's blank.
+ - 1c9b9019-ed24-499d-a988-36f7f95ea194
+ - Blank
+ - Blank
+ - false
+ - 0
+
+
+
+
+ -
+ 1153
+ 1557
+ 37
+ 60
+
+ -
+ 1171.5
+ 1587
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - GhPython Script
+
+
+
+
+ - from compas_timber.fabrication import Lap
+from compas_timber.fabrication import JackRafterCut
+from compas_timber.connections import TLapJoint
+from compas_timber.model import TimberModel
+from compas_timber.connections.utilities import beam_ref_side_incidence
+from compas_rhino.conversions import frame_to_rhino, brep_to_rhino, plane_to_rhino, box_to_rhino
+from compas_rhino import unload_modules
+
+unload_modules('compas_timber')
+
+#cross
+cross_ref_side_dict = beam_ref_side_incidence(main_beam, cross_beam, ignore_ends=True)
+cross_ref_side_index = min(cross_ref_side_dict, key=cross_ref_side_dict.get)
+# main
+main_ref_side_dict = beam_ref_side_incidence(cross_beam, main_beam, ignore_ends=True)
+main_ref_side_index = max(main_ref_side_dict, key=main_ref_side_dict.get)
+#main_ref_side_indices = main_ref_side_index, (main_ref_side_index+2)%4
+main_plane = main_beam.ref_sides[main_ref_side_index]
+cross_plane = cross_beam.ref_sides[cross_ref_side_index]
+lap_width = main_beam.height if main_ref_side_index % 2 == 0 else main_beam.width
+
+# instanciate lap processing from plane and beam
+lap = Lap.from_plane_and_beam(main_plane, cross_beam, lap_width, depth, cross_ref_side_index)
+lap_frame = lap.planes_from_params_and_beam(cross_beam)
+volume = lap.volume_from_params_and_beam(cross_beam)
+cross_geo = cross_beam.compute_geometry()
+cross_geo = lap.apply(cross_geo, cross_beam)
+
+
+# get cutting plane for JackRafterCut processing
+cross_plane = cross_beam.ref_sides[cross_ref_side_index]
+cross_plane.xaxis = -cross_plane.xaxis
+cross_plane.translate(cross_plane.normal*depth)
+# instanciate jack cut processing
+jack_cut = JackRafterCut.from_plane_and_beam(cross_plane, main_beam, main_ref_side_index)
+cutting_plane_main = jack_cut.plane_from_params_and_beam(main_beam)
+main_geo = main_beam.compute_geometry()
+main_geo = jack_cut.apply(main_geo, main_beam)
+lap_frame = [plane_to_rhino(frame) for frame in lap_frame]
+
+# viz frames
+cross_ref_side = frame_to_rhino(cross_beam.ref_sides[3])
+main_ref_side = frame_to_rhino(main_beam.ref_sides[1])
+
+cutting_plane_main = plane_to_rhino(cutting_plane_main)
+# viz volumes
+rg_cross = brep_to_rhino(cross_geo)
+rg_main = brep_to_rhino(main_geo)
+#rg_volume = brep_to_rhino(volume)
+
+
+
+
+
+
+
+
+
+
+
+
+model = TimberModel()
+model.add_element(main_beam)
+model.add_element(cross_beam)
+joint = TLapJoint.create(model, main_beam, cross_beam)
+
+model.process_joinery()
+
+
+
+
+
+
+
+
+
+
+ - GhPython provides a Python script component
+ -
+ 1181
+ 104
+
+ -
+ 1298
+ 1537
+
+ - true
+ - true
+ - false
+ - false
+ - d8159329-9c95-4b5c-9f51-eccf5cfb85ed
+ - false
+ - true
+ - GhPython Script
+ - Python
+
+
+
+
+ -
+ 1742
+ 1123
+ 202
+ 164
+
+ -
+ 1823
+ 1205
+
+
+
+
+
+ - 3
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 8
+ - 3ede854e-c753-40eb-84cb-b48008f14fd4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - true
+ - Script variable Python
+ - 18146bfd-6418-49f9-bdb0-cbb5fa7aa00d
+ - main_beam
+ - main_beam
+ - true
+ - 0
+ - true
+ - 287d3ad7-dfd0-4415-b5a0-ff41819a7aba
+ - 1
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1744
+ 1125
+ 64
+ 53
+
+ -
+ 1777.5
+ 1151.667
+
+
+
+
+
+
+
+ - true
+ - Script input cross_beam.
+ - 340c8917-a319-4ccc-b2f4-c78f4fe6b675
+ - cross_beam
+ - cross_beam
+ - true
+ - 0
+ - true
+ - d2365ef0-f85c-4709-9361-522790ff3ed5
+ - 1
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1744
+ 1178
+ 64
+ 53
+
+ -
+ 1777.5
+ 1205
+
+
+
+
+
+
+
+ - true
+ - Script input depth.
+ - d8004857-38e9-4a24-b6c1-564c840d7d92
+ - depth
+ - depth
+ - true
+ - 0
+ - true
+ - aa01afa4-173a-4459-9bf1-9dc2e745c93f
+ - 1
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1744
+ 1231
+ 64
+ 54
+
+ -
+ 1777.5
+ 1258.333
+
+
+
+
+
+
+
+ - The execution information, as output and error streams
+ - 457734f7-3478-4434-a17d-636820c0980c
+ - out
+ - out
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1125
+ 104
+ 20
+
+ -
+ 1890
+ 1135
+
+
+
+
+
+
+
+ - Script output main_ref_side.
+ - 2ed0ebc1-6a52-4a3c-84b2-d3f4a4b66f37
+ - main_ref_side
+ - main_ref_side
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1145
+ 104
+ 20
+
+ -
+ 1890
+ 1155
+
+
+
+
+
+
+
+ - Script output cross_ref_side.
+ - bf27e312-db21-4a25-83f4-81fba65f4e87
+ - cross_ref_side
+ - cross_ref_side
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1165
+ 104
+ 20
+
+ -
+ 1890
+ 1175
+
+
+
+
+
+
+
+ - Script output rg_cross.
+ - bac24a63-74e8-44a4-99df-2c4905b3d163
+ - rg_cross
+ - rg_cross
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1185
+ 104
+ 20
+
+ -
+ 1890
+ 1195
+
+
+
+
+
+
+
+ - Script output rg_main.
+ - e274454e-7ad5-42e7-9751-2366475812a4
+ - rg_main
+ - rg_main
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1205
+ 104
+ 20
+
+ -
+ 1890
+ 1215
+
+
+
+
+
+
+
+ - Script output cutting_plane_main.
+ - 5daa01c6-d25f-4807-ad0c-40e0e1768710
+ - cutting_plane_main
+ - cutting_plane_main
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1225
+ 104
+ 20
+
+ -
+ 1890
+ 1235
+
+
+
+
+
+
+
+ - Script output rg_volume.
+ - 857693c0-ad1b-4e96-a368-7b18c111a11c
+ - rg_volume
+ - rg_volume
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1245
+ 104
+ 20
+
+ -
+ 1890
+ 1255
+
+
+
+
+
+
+
+ - Script output lap_frame.
+ - 502370ce-eac6-4444-b153-000dda5f7ad2
+ - lap_frame
+ - lap_frame
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1265
+ 104
+ 20
+
+ -
+ 1890
+ 1275
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - 9381ae59-064b-40ba-81bc-88ceddf37ae8
+ - Brep
+ - Brep
+ - false
+ - bac24a63-74e8-44a4-99df-2c4905b3d163
+ - 1
+
+
+
+
+ -
+ 1984
+ 1201
+ 50
+ 24
+
+ -
+ 2009.32
+ 1213.15
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: Model
+
+
+
+
+ - from compas.scene import Scene
+from compas.tolerance import TOL
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.errors import BeamJoiningError
+from compas_timber.connections import ConnectionSolver
+from compas_timber.connections import JointTopology
+from compas_timber.connections import LMiterJoint
+from compas_timber.connections import TButtJoint
+from compas_timber.connections import XLapJoint
+from compas_timber.design import CategoryRule
+from compas_timber.design import DebugInfomation
+from compas_timber.design import DirectRule
+from compas_timber.design import JointDefinition
+from compas_timber.design import TopologyRule
+from compas_timber.model import TimberModel
+import warnings
+
+JOINT_DEFAULTS = {
+ JointTopology.TOPO_X: XLapJoint,
+ JointTopology.TOPO_T: TButtJoint,
+ JointTopology.TOPO_L: LMiterJoint,
+}
+
+
+# workaround for https://github.com/gramaziokohler/compas_timber/issues/280
+TOL.absolute = 1e-6
+
+
+class ModelComponent(component):
+ def get_joints_from_rules(self, beams, rules, topologies):
+ if not isinstance(rules, list):
+ rules = [rules]
+ rules = [r for r in rules if r is not None]
+
+ joints = []
+ # rules have to be resolved into joint definitions
+ topo_rules = {}
+ cat_rules = []
+ direct_rules = []
+
+ # TODO: refactor this into some kind of a rule reloving class/function
+ for r in rules: # separate category and topo and direct joint rules
+ if isinstance(r, TopologyRule):
+ if topo_rules.get(r.topology_type, None): # if rule for this Topo exists
+ if (r.joint_type != JOINT_DEFAULTS[r.topology_type]) or (
+ len(r.kwargs) != 0
+ ): # if this rule is NOT default
+ topo_rules[r.topology_type] = r
+ else:
+ topo_rules[r.topology_type] = r
+ elif isinstance(r, CategoryRule):
+ cat_rules.append(r)
+ if isinstance(r, DirectRule):
+ direct_rules.append(r)
+
+ for topo in topologies:
+ beam_a = topo["beam_a"]
+ beam_b = topo["beam_b"]
+ detected_topo = topo["detected_topo"]
+ pair = beam_a, beam_b
+ pair_joined = False
+
+ if detected_topo == JointTopology.TOPO_UNKNOWN:
+ continue
+
+ for rule in direct_rules: # apply direct rules first
+ if rule.comply(pair):
+ joints.append(JointDefinition(rule.joint_type, rule.beams, **rule.kwargs))
+ pair_joined = True
+ break
+
+ if not pair_joined: # if no direct rule applies, apply category rules next
+ for rule in cat_rules:
+ if not rule.comply(pair):
+ continue
+ if rule.joint_type.SUPPORTED_TOPOLOGY != detected_topo:
+ msg = "Conflict detected! Beams: {}, {} meet with topology: {} but rule assigns: {}"
+ self.AddRuntimeMessage(
+ Warning,
+ msg.format(
+ beam_a.guid,
+ beam_b.guid,
+ JointTopology.get_name(detected_topo),
+ rule.joint_type.__name__,
+ ),
+ )
+ continue
+ if rule.topos and detected_topo not in rule.topos:
+ msg = "Conflict detected! Beams: {}, {} meet with topology: {} but rule allows: {}"
+ self.AddRuntimeMessage(
+ Warning,
+ msg.format(
+ beam_a.guid,
+ beam_b.guid,
+ JointTopology.get_name(detected_topo),
+ [JointTopology.get_name(topo) for topo in rule.topos],
+ ),
+ )
+ continue
+ # sort by category to allow beam role by order (main beam first, cross beam second)
+ beam_a, beam_b = rule.reorder([beam_a, beam_b])
+ joints.append(JointDefinition(rule.joint_type, [beam_a, beam_b], **rule.kwargs))
+ break # first matching rule
+
+ else: # no category rule applies, apply topology rules
+ if detected_topo not in topo_rules:
+ continue
+ else:
+ joints.append(
+ JointDefinition(
+ topo_rules[detected_topo].joint_type,
+ [beam_a, beam_b],
+ **topo_rules[detected_topo].kwargs
+ )
+ )
+ return joints
+
+ def RunScript(self, Elements, JointRules, Features, MaxDistance, CreateGeometry):
+ if not Elements:
+ self.AddRuntimeMessage(Warning, "Input parameter Beams failed to collect data")
+ if not JointRules:
+ self.AddRuntimeMessage(Warning, "Input parameter JointRules failed to collect data")
+ if not (Elements): # shows beams even if no joints are found
+ return
+ if MaxDistance is None:
+ MaxDistance = TOL.ABSOLUTE # compared to calculted distance, so shouldn't be just 0.0
+
+ Model = TimberModel()
+ debug_info = DebugInfomation()
+ for element in Elements:
+ # prepare elements for downstream processing
+ if element is None:
+ continue
+ element.remove_features()
+ if hasattr(element, "remove_blank_extension"):
+ element.remove_blank_extension()
+ element.debug_info = []
+ Model.add_element(element)
+
+ topologies = []
+ solver = ConnectionSolver()
+ found_pairs = solver.find_intersecting_pairs(list(Model.beams), rtree=True, max_distance=MaxDistance)
+ for pair in found_pairs:
+ beam_a, beam_b = pair
+ detected_topo, beam_a, beam_b = solver.find_topology(beam_a, beam_b, max_distance=MaxDistance)
+ if not detected_topo == JointTopology.TOPO_UNKNOWN:
+ topologies.append({"detected_topo": detected_topo, "beam_a": beam_a, "beam_b": beam_b})
+ Model.set_topologies(topologies)
+
+ joints = self.get_joints_from_rules(Model.beams, JointRules, topologies)
+
+ if joints:
+ handled_beams = []
+ joints = [j for j in joints if j is not None]
+ # apply reversed. later joints in orginal list override ealier ones
+ for joint in joints[::-1]:
+ beams_to_pair = joint.elements
+ beam_pair_ids = set([id(beam) for beam in beams_to_pair])
+ if beam_pair_ids in handled_beams:
+ continue
+ try:
+ joint.joint_type.create(Model, *beams_to_pair, **joint.kwargs)
+ except BeamJoiningError as bje:
+ debug_info.add_joint_error(bje)
+ else:
+ handled_beams.append(beam_pair_ids)
+
+# # applies extensions and features resulting from joints
+# Model.process_joinery()
+# Catch warnings during the process_joinery method
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+ Model.process_joinery()
+ for warning in w:
+ debug_info.add_joint_error(str(warning.message))
+ if Features:
+ features = [f for f in Features if f is not None]
+ for f_def in features:
+ for element in f_def.elements:
+ element.add_features(f_def.feature)
+
+ Geometry = None
+ scene = Scene()
+ for element in Model.elements():
+ if CreateGeometry:
+ scene.add(element.geometry)
+ if element.debug_info:
+ debug_info.add_feature_error(element.debug_info)
+ else:
+ scene.add(element.blank)
+
+ if debug_info.has_errors:
+ self.AddRuntimeMessage(Warning, "Error found during joint creation. See DebugInfo output for details.")
+
+ Geometry = scene.draw()
+ return Model, Geometry, debug_info
+
+ - GhPython provides a Python script component
+ -
+ 142
+ 217
+
+ -
+ 1130
+ 1501
+
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAAY5JREFUSEu1lL1tAzEMhd8IHsEjeIQbIWVKj5ARPEJG8AguU3oEFy5cBpkgQLoggINPIQ80Jd2dkaR4gMSf9yhKlK7Xq5ZC0gPI9ilUhh4k7SSxALvs76EyZEhaSToY8cXAGtsqx2dUhkS+lnTyqhOw4VvnvEUCkgZJ74bSdye3Nffh/iHnTwpI2lqFr5I2wT4K2H5jMWy2macpIGlvCcfc4yxgNu6IWDb7ii8Fer+rwJ5A8HlhcIyFuXP2qHMC5q9amy9r7HcLcwIWQ7Hj48Dg/fsPwK1nSV/WQ55mBDZ66vtHg+/x9fLghLscm55xpJuhMd8xtyH4qTC/KoYTLjjJ/yExw+EPBPhC4Cq+KMDRiBin8l6ByFEJhITTLwS4k8hVCdA/Fk/3CpBjueUemwKBlEtishcJWCw58TRdAQ/miS0VILYU1fDVJGHkXxrDk+ExN1/MpEAIeEuDloGPmF7+pIA/uXP2hZhzfto9AfqHIeND0mfD7sBHTLaDculeKYsW+FOYzGx34CMm2x3DN86+0jzsl4EUAAAAAElFTkSuQmCC
+
+ - false
+ - ff35723b-1edb-429a-9de3-da7ac4b770dc
+ - true
+ - true
+ - CT: Model
+ - Model
+
+
+
+
+ -
+ 1844
+ 1473
+ 198
+ 104
+
+ -
+ 1963
+ 1525
+
+
+
+
+
+ - 5
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - true
+ - Collection of Beams
+ - 8024edd6-0fa4-4ea8-9c75-08cd185bd595
+ - 1
+ - Elements
+ - Elements
+ - true
+ - 1
+ - true
+ - bf762047-bfb8-4813-a506-3cf4fb2c17bc
+ - 1
+ - 35915213-5534-4277-81b8-1bdc9e7383d2
+
+
+
+
+ -
+ 1846
+ 1475
+ 102
+ 20
+
+ -
+ 1906.5
+ 1485
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Script input JointRules.
+ - 207cda69-0cde-4ea9-a043-815a192121ed
+ - 1
+ - JointRules
+ - JointRules
+ - true
+ - 1
+ - true
+ - debfa811-a60a-402d-a198-bd648e1658ee
+ - 1
+ - 35915213-5534-4277-81b8-1bdc9e7383d2
+
+
+
+
+ -
+ 1846
+ 1495
+ 102
+ 20
+
+ -
+ 1906.5
+ 1505
+
+
+
+
+
+
+
+ - 1
+ - true
+ - Script input Features.
+ - bcf5d52c-f476-47f1-9b19-897ef988289d
+ - Features
+ - Features
+ - true
+ - 1
+ - true
+ - 0
+ - 35915213-5534-4277-81b8-1bdc9e7383d2
+
+
+
+
+ -
+ 1846
+ 1515
+ 102
+ 20
+
+ -
+ 1906.5
+ 1525
+
+
+
+
+
+
+
+ - true
+ - Script input MaxDistance.
+ - 82534af0-5b75-42d2-a57b-575ee6d58878
+ - MaxDistance
+ - MaxDistance
+ - true
+ - 0
+ - true
+ - 10636707-b664-4e14-ba9a-d85ce2a0c401
+ - 1
+ - 39fbc626-7a01-46ab-a18e-ec1c0c41685b
+
+
+
+
+ -
+ 1846
+ 1535
+ 102
+ 20
+
+ -
+ 1906.5
+ 1545
+
+
+
+
+
+
+
+ - true
+ - Script input CreateGeometry.
+ - a60a06af-12b9-40bf-804b-4fc8d061019c
+ - CreateGeometry
+ - CreateGeometry
+ - true
+ - 0
+ - true
+ - 88d6deff-1f0b-49e8-894d-0a981b9177f1
+ - 1
+ - d60527f5-b5af-4ef6-8970-5f96fe412559
+
+
+
+
+ -
+ 1846
+ 1555
+ 102
+ 20
+
+ -
+ 1906.5
+ 1565
+
+
+
+
+
+
+
+ - Script output Model.
+ - e36de40d-2fa1-4227-a9c2-9cdb47530003
+ - Model
+ - Model
+ - false
+ - 0
+
+
+
+
+ -
+ 1978
+ 1475
+ 62
+ 33
+
+ -
+ 2009
+ 1491.667
+
+
+
+
+
+
+
+ - Script output Geometry.
+ - 725ac33d-374c-4cc0-8f18-3e15182f1323
+ - Geometry
+ - Geometry
+ - false
+ - 0
+
+
+
+
+ -
+ 1978
+ 1508
+ 62
+ 33
+
+ -
+ 2009
+ 1525
+
+
+
+
+
+
+
+ - Script output DebugInfo.
+ - 2fb7adff-9b5d-45fd-bbca-5f311bb6fa46
+ - DebugInfo
+ - DebugInfo
+ - false
+ - 0
+
+
+
+
+ -
+ 1978
+ 1541
+ 62
+ 34
+
+ -
+ 2009
+ 1558.333
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 2e78987b-9dfb-42a2-8b76-3923ac8bd91a
+ - Boolean Toggle
+
+
+
+
+ - Boolean (true/false) toggle
+ - 88d6deff-1f0b-49e8-894d-0a981b9177f1
+ - Boolean Toggle
+ - Toggle
+ - false
+ - 0
+ - true
+
+
+
+
+ -
+ 1697
+ 1554
+ 104
+ 22
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: WriteBTLx
+
+
+
+
+ - import Rhino
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.fabrication import BTLx
+
+
+class WriteBTLx(component):
+ def RunScript(self, model, path, write):
+ if not model:
+ self.AddRuntimeMessage(Warning, "Input parameter Model failed to collect data")
+ return
+
+ btlx = BTLx(model)
+ btlx.history["FileName"] = Rhino.RhinoDoc.ActiveDoc.Name
+
+ if write:
+ if not path:
+ self.AddRuntimeMessage(Warning, "Input parameter Path failed to collect data")
+ return
+ if path[-5:] != ".btlx":
+ path += ".btlx"
+ with open(path, "w") as f:
+ f.write(btlx.btlx_string())
+ return btlx.btlx_string()
+
+ - GhPython provides a Python script component
+ -
+ 152
+ 152
+
+ -
+ 938
+ 975
+
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAARtJREFUSEu1lFERwjAQRFcCAiICCZWABCQgoQoylVAJlcBHBFRCJdRBmQ0Nc71L0o+GjwfM5u72klzAtm34J0ZojRFaY4TWGKEEgD6h12oYoQQAfkT0Wg04H0bnw00vaK4YbM6H+czkqgFZnA93HfALVAYAOnEvz13j70MNaUBW50OnixcMWCxpbxUzAYgnog0SsaMLBiROW8mADCcGuSOSBit3UTOYawY5aKRMXiWDMZMsE2vDMIu4PmeQfak8Z5mo1/cYHptsxBiYyxXJnAyZzE6HdAcAHgAWFdMlA45ncduF7iTcnZwqMsW8/RVXiwsTdsrpODM4vIPqX4Qmjt7XiAV5RCzO7zS2h4dqCrTGCK0xQmuM0BojtOYDGi90WTOaCA8AAAAASUVORK5CYII=
+
+ - false
+ - 93817d7e-6043-4c58-8076-042c11e1fa95
+ - true
+ - true
+ - CT: WriteBTLx
+ - WriteBTLx
+
+
+
+
+ -
+ 2284
+ 1651
+ 104
+ 64
+
+ -
+ 2339
+ 1683
+
+
+
+
+
+ - 3
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - true
+ - Model object.
+ - 4de66b25-f265-4ab5-9f21-a98ac1f5a933
+ - Model
+ - Model
+ - true
+ - 0
+ - true
+ - e36de40d-2fa1-4227-a9c2-9cdb47530003
+ - 1
+ - 35915213-5534-4277-81b8-1bdc9e7383d2
+
+
+
+
+ -
+ 2286
+ 1653
+ 38
+ 20
+
+ -
+ 2306.5
+ 1663
+
+
+
+
+
+
+
+ - true
+ - Script input Path.
+ - 0b21d3af-2bd5-4b08-821e-59e6e2d20e69
+ - Path
+ - Path
+ - true
+ - 0
+ - true
+ - 52d39943-0ed8-46fe-b90e-5867d152bd71
+ - 1
+ - 35915213-5534-4277-81b8-1bdc9e7383d2
+
+
+
+
+ -
+ 2286
+ 1673
+ 38
+ 20
+
+ -
+ 2306.5
+ 1683
+
+
+
+
+
+
+
+ - true
+ - Script input Write.
+ - c7de61e4-ce28-45dc-9066-0f8e614137cd
+ - Write
+ - Write
+ - true
+ - 0
+ - true
+ - d5a06678-cdf7-40ed-8120-797a2d71424d
+ - 1
+ - d60527f5-b5af-4ef6-8970-5f96fe412559
+
+
+
+
+ -
+ 2286
+ 1693
+ 38
+ 20
+
+ -
+ 2306.5
+ 1703
+
+
+
+
+
+
+
+ - Script output BTLx.
+ - 5946e9dc-f13c-4e3c-8873-1cc4030c843d
+ - BTLx
+ - BTLx
+ - false
+ - 0
+
+
+
+
+ -
+ 2354
+ 1653
+ 32
+ 60
+
+ -
+ 2370
+ 1683
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59e0b89a-e487-49f8-bab8-b5bab16be14c
+ - Panel
+
+
+
+
+ - A panel for custom notes and text values
+ - 52d39943-0ed8-46fe-b90e-5867d152bd71
+ - Panel
+
+ - false
+ - 0
+ - 0
+ - C:\Users\kapso\OneDrive\Desktop\BTLx\TButtJoint.btlx
+
+
+
+
+ -
+ 1967
+ 1617
+ 258
+ 116
+
+ - 0
+ - 0
+ - 0
+ -
+ 1967.904
+ 1617.126
+
+
+
+
+
+ -
+ 255;255;250;90
+
+ - true
+ - true
+ - true
+ - false
+ - false
+ - true
+
+
+
+
+
+
+
+
+ - 2e78987b-9dfb-42a2-8b76-3923ac8bd91a
+ - Boolean Toggle
+
+
+
+
+ - Boolean (true/false) toggle
+ - d5a06678-cdf7-40ed-8120-797a2d71424d
+ - Boolean Toggle
+ - Toggle
+ - false
+ - 0
+ - true
+
+
+
+
+ -
+ 2121
+ 1741
+ 104
+ 22
+
+
+
+
+
+
+
+
+
+ - 2a5cfb31-028a-4b34-b4e1-9b20ae15312e
+ - Cross Product
+
+
+
+
+ - Compute vector cross product.
+ - true
+ - 8c89d0b6-be81-4671-8ea9-35eff688cefc
+ - Cross Product
+ - XProd
+
+
+
+
+ -
+ 632
+ 1438
+ 66
+ 64
+
+ -
+ 664
+ 1470
+
+
+
+
+
+ - First vector
+ - a1dec88f-fa57-42d9-848b-54a7703cf14d
+ - Vector A
+ - A
+ - false
+ - 22a7c548-2a3a-46f3-9aed-df6d798fd1fe
+ - 1
+
+
+
+
+ -
+ 634
+ 1440
+ 15
+ 20
+
+ -
+ 643
+ 1450
+
+
+
+
+
+
+
+ - Second vector
+ - e92373e4-0242-4076-8288-84d920d1a8db
+ - Vector B
+ - B
+ - false
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 634
+ 1460
+ 15
+ 20
+
+ -
+ 643
+ 1470
+
+
+
+
+
+
+
+ - Unitize output
+ - f80c9f76-42a2-4e06-bee0-b5f229d457da
+ - Unitize
+ - U
+ - false
+ - 0
+
+
+
+
+ -
+ 634
+ 1480
+ 15
+ 20
+
+ -
+ 643
+ 1490
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - false
+
+
+
+
+
+
+
+
+
+
+ - Cross product vector
+ - 8384eec4-4bfc-4566-847f-bdd110cca2fa
+ - Vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ 679
+ 1440
+ 17
+ 30
+
+ -
+ 687.5
+ 1455
+
+
+
+
+
+
+
+ - Vector length
+ - d85af46a-4140-44dc-ad79-8c4d6676cd48
+ - Length
+ - L
+ - false
+ - 0
+
+
+
+
+ -
+ 679
+ 1470
+ 17
+ 30
+
+ -
+ 687.5
+ 1485
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - aa01afa4-173a-4459-9bf1-9dc2e745c93f
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 1340
+ 1436
+ 184
+ 20
+
+ -
+ 1340.726
+ 1436.466
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 100
+ - 0
+ - 0
+ - 20
+
+
+
+
+
+
+
+
+ - 2a3f7078-2e25-4dd4-96f7-0efb491bd61c
+ - Vector Display
+
+
+
+
+ - false
+ - 0
+ - Preview vectors in the viewport
+ - 0.1
+ - 15
+ - true
+ - 08b55ce3-618a-4e25-8173-6488cf47d35b
+ - Vector Display
+ - VDis
+
+
+
+
+ - 3
+ - false
+ - false
+
+
+
+
+ -
+ 255;255;0;0
+
+ -
+ 255;255;0;0
+
+ - 0
+ - 0113de22-d3ba-4eb3-adaa-506038766107
+
+
+
+
+ -
+ 255;255;165;0
+
+ -
+ 255;255;165;0
+
+ - 0.5
+ - cff317c6-83ff-42c6-9147-f440a2b1be97
+
+
+
+
+ -
+ 255;124;252;0
+
+ -
+ 255;124;252;0
+
+ - 1
+ - 89ecc415-fbe0-43d1-99f7-0e341f4caa6c
+
+
+
+
+
+
+ -
+ 722
+ 1423
+ 61
+ 44
+
+ -
+ 769
+ 1445
+
+
+
+
+
+ - Anchor point for preview vector
+ - eb44ba95-b3ee-4f85-80b4-0e6c59c56c20
+ - Anchor
+ - A
+ - true
+ - 34c88e7e-45d4-4ff8-adfe-4ada2b4c2f45
+ - 1
+
+
+
+
+ -
+ 724
+ 1425
+ 30
+ 20
+
+ -
+ 748.5
+ 1435
+
+
+
+
+
+
+
+ - Vector to preview
+ - 7188ccec-d71b-41dc-9e8b-fe742466db3a
+ - x*100
+ - Vector
+ - V
+ - true
+ - 8384eec4-4bfc-4566-847f-bdd110cca2fa
+ - 1
+
+
+
+
+ -
+ 724
+ 1445
+ 30
+ 20
+
+ -
+ 748.5
+ 1455
+
+
+
+
+
+
+
+
+
+
+
+ - 6b7ba278-5c9d-42f1-a61d-6209cbd44907
+ - Curve Proximity
+
+
+
+
+ - Find the pair of closest points between two curves.
+ - true
+ - 5d91225d-b2dc-4281-95d1-144b18e603c4
+ - Curve Proximity
+ - CrvProx
+
+
+
+
+ -
+ 631
+ 1371
+ 66
+ 64
+
+ -
+ 662
+ 1403
+
+
+
+
+
+ - First curve
+ - 37fedea4-997f-42c0-9dd2-1d78203bad4f
+ - Curve A
+ - A
+ - false
+ - 22a7c548-2a3a-46f3-9aed-df6d798fd1fe
+ - 1
+
+
+
+
+ -
+ 633
+ 1373
+ 14
+ 30
+
+ -
+ 641.5
+ 1388
+
+
+
+
+
+
+
+ - Second curve
+ - fe20acfa-1112-4edf-80d9-67b518716863
+ - Curve B
+ - B
+ - false
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 633
+ 1403
+ 14
+ 30
+
+ -
+ 641.5
+ 1418
+
+
+
+
+
+
+
+ - Point on curve A closest to curve B
+ - 34c88e7e-45d4-4ff8-adfe-4ada2b4c2f45
+ - Point A
+ - A
+ - false
+ - 0
+
+
+
+
+ -
+ 677
+ 1373
+ 18
+ 20
+
+ -
+ 686
+ 1383
+
+
+
+
+
+
+
+ - Point on curve B closest to curve A
+ - 7c0c8311-8374-409b-93e4-37e40bf928f7
+ - Point B
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 677
+ 1393
+ 18
+ 20
+
+ -
+ 686
+ 1403
+
+
+
+
+
+
+
+ - Smallest distance between two curves
+ - acf2f4d0-c597-4176-a1b7-f5be911ca208
+ - Distance
+ - D
+ - false
+ - 0
+
+
+
+
+ -
+ 677
+ 1413
+ 18
+ 20
+
+ -
+ 686
+ 1423
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 00dffbd2-d634-4815-8e48-989dfd6df288
+ - Plane
+ - Pln
+ - false
+ - bf27e312-db21-4a25-83f4-81fba65f4e87
+ - 1
+
+
+
+
+ -
+ 1998
+ 1175
+ 50
+ 24
+
+ -
+ 2023.607
+ 1187.121
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 4d439952-5ee0-415f-bf9a-9558d86e4efd
+ - Plane
+ - Pln
+ - false
+ - 2ed0ebc1-6a52-4a3c-84b2-d3f4a4b66f37
+ - 1
+
+
+
+
+ -
+ 1987
+ 1139
+ 50
+ 24
+
+ -
+ 2012.421
+ 1151.553
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - a6425828-9538-4783-86e1-0c73c651f302
+ - Brep
+ - Brep
+ - false
+ - e274454e-7ad5-42e7-9751-2366475812a4
+ - 1
+
+
+
+
+ -
+ 1981
+ 1229
+ 50
+ 24
+
+ -
+ 2006.616
+ 1241.926
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - b3588f0a-414e-4a3d-87e6-79cde3ae279b
+ - Brep
+ - Brep
+ - false
+ - 857693c0-ad1b-4e96-a368-7b18c111a11c
+ - 1
+
+
+
+
+ -
+ 1989
+ 1288
+ 50
+ 24
+
+ -
+ 2014.739
+ 1300.79
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 9f99b4bb-612a-492f-b8e1-a954c0e0a960
+ - Plane
+ - Pln
+ - false
+ - 5daa01c6-d25f-4807-ad0c-40e0e1768710
+ - 1
+
+
+
+
+ -
+ 1989
+ 1261
+ 50
+ 24
+
+ -
+ 2014.707
+ 1273.677
+
+
+
+
+
+
+
+
+
+ - 11bbd48b-bb0a-4f1b-8167-fa297590390d
+ - End Points
+
+
+
+
+ - Extract the end points of a curve.
+ - true
+ - d785f7da-1ae4-4767-9398-648e133a5bd2
+ - End Points
+ - End
+
+
+
+
+ -
+ -129
+ 1505
+ 64
+ 44
+
+ -
+ -98
+ 1527
+
+
+
+
+
+ - Curve to evaluate
+ - 70403e50-270d-44b1-a9d0-6f61a843054a
+ - Curve
+ - C
+ - false
+ - 2cbb77cb-271b-4c58-b3b5-67700d482c61
+ - 1
+
+
+
+
+ -
+ -127
+ 1507
+ 14
+ 40
+
+ -
+ -118.5
+ 1527
+
+
+
+
+
+
+
+ - Curve start point
+ - bd842e1f-5ad6-4c4e-b40d-1faf08bfd10a
+ - Start
+ - S
+ - false
+ - 0
+
+
+
+
+ -
+ -83
+ 1507
+ 16
+ 20
+
+ -
+ -75
+ 1517
+
+
+
+
+
+
+
+ - Curve end point
+ - 1aa64117-983f-4f8f-88a7-89988c38d0a5
+ - End
+ - E
+ - false
+ - 0
+
+
+
+
+ -
+ -83
+ 1527
+ 16
+ 20
+
+ -
+ -75
+ 1537
+
+
+
+
+
+
+
+
+
+
+
+ - e9eb1dcf-92f6-4d4d-84ae-96222d60f56b
+ - Move
+
+
+
+
+ - Translate (move) an object along a vector.
+ - true
+ - 98e83dd4-f0d0-4701-ba42-e474164cfd82
+ - Move
+ - Move
+
+
+
+
+ -
+ -43
+ 1552
+ 83
+ 44
+
+ -
+ 5
+ 1574
+
+
+
+
+
+ - Base geometry
+ - a2458f66-4a60-4e07-97e4-fa1e384391ff
+ - Geometry
+ - G
+ - true
+ - 1aa64117-983f-4f8f-88a7-89988c38d0a5
+ - 1
+
+
+
+
+ -
+ -41
+ 1554
+ 31
+ 20
+
+ -
+ -16
+ 1564
+
+
+
+
+
+
+
+ - Translation vector
+ - fed7b565-45a1-4f02-b7e2-f6a966754fe6
+ - -x
+ - Motion
+ - T
+ - false
+ - 5c2a0f01-6a04-4bef-bd00-1e3da3fb5dc5
+ - 1
+
+
+
+
+ -
+ -41
+ 1574
+ 31
+ 20
+
+ -
+ -16
+ 1584
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 0
+ 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ - Translated geometry
+ - 73968214-4027-410c-a9b5-a33b92bba8d9
+ - Geometry
+ - G
+ - false
+ - 0
+
+
+
+
+ -
+ 20
+ 1554
+ 18
+ 20
+
+ -
+ 29
+ 1564
+
+
+
+
+
+
+
+ - Transformation data
+ - 042d236f-ef03-4870-acc9-d502f01eaf6d
+ - Transform
+ - X
+ - false
+ - 0
+
+
+
+
+ -
+ 20
+ 1574
+ 18
+ 20
+
+ -
+ 29
+ 1584
+
+
+
+
+
+
+
+
+
+
+
+ - 79f9fbb3-8f1d-4d9a-88a9-f7961b1012cd
+ - Unit X
+
+
+
+
+ - Unit vector parallel to the world {x} axis.
+ - true
+ - 8fa01d08-afaf-4258-bace-d765ebf9577f
+ - Unit X
+ - X
+
+
+
+
+ -
+ -231
+ 1550
+ 79
+ 28
+
+ -
+ -186
+ 1564
+
+
+
+
+
+ - Unit multiplication
+ - a7304964-d322-488c-865b-62f9088e559b
+ - -x
+ - Factor
+ - F
+ - false
+ - d475e429-c48f-4bea-b061-06ab3815cfd3
+ - 1
+
+
+
+
+ -
+ -229
+ 1552
+ 28
+ 24
+
+ -
+ -205.5
+ 1564
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {x} vector
+ - cc80d251-10bb-4f29-8711-e24349bac9f2
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ -171
+ 1552
+ 17
+ 24
+
+ -
+ -162.5
+ 1564
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - d475e429-c48f-4bea-b061-06ab3815cfd3
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ -400
+ 1558
+ 163
+ 20
+
+ -
+ -399.7307
+ 1558.41
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 4000
+ - 0
+ - 0
+ - 1000
+
+
+
+
+
+
+
+
+ - 4c4e56eb-2f04-43f9-95a3-cc46a14f495a
+ - Line
+
+
+
+
+ - Create a line between two points.
+ - true
+ - 702b1bbc-2b58-478b-b3ba-16a48af876bd
+ - Line
+ - Ln
+
+
+
+
+ -
+ 65
+ 1532
+ 63
+ 44
+
+ -
+ 96
+ 1554
+
+
+
+
+
+ - Line start point
+ - 6a018da5-25e2-4649-8e94-3e060a9f2195
+ - Start Point
+ - A
+ - false
+ - bd842e1f-5ad6-4c4e-b40d-1faf08bfd10a
+ - 1
+
+
+
+
+ -
+ 67
+ 1534
+ 14
+ 20
+
+ -
+ 75.5
+ 1544
+
+
+
+
+
+
+
+ - Line end point
+ - 2d65d7c6-7c91-45db-b3ba-55405300a7c8
+ - End Point
+ - B
+ - false
+ - 73968214-4027-410c-a9b5-a33b92bba8d9
+ - 1
+
+
+
+
+ -
+ 67
+ 1554
+ 14
+ 20
+
+ -
+ 75.5
+ 1564
+
+
+
+
+
+
+
+ - Line segment
+ - 86caf817-445e-4796-b29c-e1d7203996ce
+ - Line
+ - L
+ - false
+ - 0
+
+
+
+
+ -
+ 111
+ 1534
+ 15
+ 40
+
+ -
+ 118.5
+ 1554
+
+
+
+
+
+
+
+
+
+
+
+ - 9103c240-a6a9-4223-9b42-dbd19bf38e2b
+ - Unit Z
+
+
+
+
+ - Unit vector parallel to the world {z} axis.
+ - true
+ - bbf2811a-72c7-4b27-aabe-22952d508aa7
+ - Unit Z
+ - Z
+
+
+
+
+ -
+ -229
+ 1584
+ 79
+ 28
+
+ -
+ -184
+ 1598
+
+
+
+
+
+ - Unit multiplication
+ - 827ec58a-dd71-44e4-b991-26acf25b63f9
+ - -x
+ - Factor
+ - F
+ - false
+ - ed5575ff-1733-49b5-87a3-30e4e79a9149
+ - 1
+
+
+
+
+ -
+ -227
+ 1586
+ 28
+ 24
+
+ -
+ -203.5
+ 1598
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {z} vector
+ - b34169da-06f4-4b1b-a640-4619f7c456ef
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ -169
+ 1586
+ 17
+ 24
+
+ -
+ -160.5
+ 1598
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - ed5575ff-1733-49b5-87a3-30e4e79a9149
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ -402
+ 1593
+ 163
+ 20
+
+ -
+ -401.2582
+ 1593.316
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 2000
+ - 0
+ - 0
+ - 0
+
+
+
+
+
+
+
+
+ - a0d62394-a118-422d-abb3-6af115c75b25
+ - Addition
+
+
+
+
+ - Mathematical addition
+ - true
+ - 60dc433a-3e4b-4822-9277-ffa1be8ca93b
+ - Addition
+ - A+B
+
+
+
+
+ -
+ -127
+ 1552
+ 65
+ 64
+
+ -
+ -96
+ 1584
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - First item for addition
+ - cc645842-011a-4863-b694-2f3420ff364e
+ - A
+ - A
+ - true
+ - cc80d251-10bb-4f29-8711-e24349bac9f2
+ - 1
+
+
+
+
+ -
+ -125
+ 1554
+ 14
+ 20
+
+ -
+ -116.5
+ 1564
+
+
+
+
+
+
+
+ - Second item for addition
+ - 367929e0-5239-49bb-8eb9-620c98911008
+ - B
+ - B
+ - true
+ - b34169da-06f4-4b1b-a640-4619f7c456ef
+ - 1
+
+
+
+
+ -
+ -125
+ 1574
+ 14
+ 20
+
+ -
+ -116.5
+ 1584
+
+
+
+
+
+
+
+ - Third item for addition
+ - f4ffb200-2275-4f34-8b77-096a6fa4eaa5
+ - C
+ - C
+ - true
+ - 69d3e566-54c8-406e-b254-8c7c985b1f55
+ - 1
+
+
+
+
+ -
+ -125
+ 1594
+ 14
+ 20
+
+ -
+ -116.5
+ 1604
+
+
+
+
+
+
+
+ - Result of addition
+ - 5c2a0f01-6a04-4bef-bd00-1e3da3fb5dc5
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ -81
+ 1554
+ 17
+ 60
+
+ -
+ -72.5
+ 1584
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;255;255;255
+
+ - A group of Grasshopper objects
+ - 8fa01d08-afaf-4258-bace-d765ebf9577f
+ - d475e429-c48f-4bea-b061-06ab3815cfd3
+ - bbf2811a-72c7-4b27-aabe-22952d508aa7
+ - ed5575ff-1733-49b5-87a3-30e4e79a9149
+ - 1bf6c7ca-f719-453d-ae24-8fb4402a0f6e
+ - a733d08f-1f60-40f4-b07a-a99476c2a8ab
+ - 6
+ - 2858ecb7-755a-4a0e-be46-21a44003594a
+ - Group
+
+
+
+
+
+
+
+
+
+
+ - d3d195ea-2d59-4ffa-90b1-8b7ff3369f69
+ - Unit Y
+
+
+
+
+ - Unit vector parallel to the world {y} axis.
+ - true
+ - 1bf6c7ca-f719-453d-ae24-8fb4402a0f6e
+ - Unit Y
+ - Y
+
+
+
+
+ -
+ -228
+ 1618
+ 79
+ 28
+
+ -
+ -183
+ 1632
+
+
+
+
+
+ - Unit multiplication
+ - 5a12a759-a526-4b6d-9a80-940d6314b66f
+ - -x
+ - Factor
+ - F
+ - false
+ - a733d08f-1f60-40f4-b07a-a99476c2a8ab
+ - 1
+
+
+
+
+ -
+ -226
+ 1620
+ 28
+ 24
+
+ -
+ -202.5
+ 1632
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {y} vector
+ - 69d3e566-54c8-406e-b254-8c7c985b1f55
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ -168
+ 1620
+ 17
+ 24
+
+ -
+ -159.5
+ 1632
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - a733d08f-1f60-40f4-b07a-a99476c2a8ab
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ -396
+ 1626
+ 163
+ 20
+
+ -
+ -395.5972
+ 1626.688
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 4000
+ - 0
+ - 0
+ - 0
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - true
+ - 2cbb77cb-271b-4c58-b3b5-67700d482c61
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ -202
+ 1515
+ 50
+ 24
+
+ -
+ -176.9542
+ 1527.093
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZG5KV7LLreD7Dh/+/5cPK155QKIotjs+NcgBJu5Y1f/vevQGuDgTAwJ4XKouSRfsdQC5JL/F7Mz/eqYGmBw3w2ACAA==
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+ - eeafc956-268e-461d-8e73-ee05c6f72c01
+ - Stream Filter
+
+
+
+
+ - Filters a collection of input streams
+ - true
+ - db6d25a1-f31e-40f5-b194-fe0d4468cf6b
+ - Stream Filter
+ - Filter
+
+
+
+
+ -
+ 244
+ 1522
+ 77
+ 64
+
+ -
+ 276
+ 1554
+
+
+
+
+
+ - 3
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - Index of Gate stream
+ - cc69945a-bbbb-4131-905d-9d3c0fbd8438
+ - Gate
+ - G
+ - false
+ - 1af3d673-3732-4271-8c59-52c07949abb2
+ - 1
+
+
+
+
+ -
+ 246
+ 1524
+ 15
+ 20
+
+ -
+ 255
+ 1534
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 0
+ - 8101c4eb-81c2-4bcb-a591-85ccea8356c5
+ - false
+ - Stream 0
+ - 0
+ - true
+ - 86caf817-445e-4796-b29c-e1d7203996ce
+ - 1
+
+
+
+
+ -
+ 246
+ 1544
+ 15
+ 20
+
+ -
+ 255
+ 1554
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 1
+ - 2dc73a0a-c8da-43b5-ad87-6a8728ba0b8b
+ - false
+ - Stream 1
+ - 1
+ - true
+ - 876f4851-1798-41c3-97d4-25f54e4be852
+ - 1
+
+
+
+
+ -
+ 246
+ 1564
+ 15
+ 20
+
+ -
+ 255
+ 1574
+
+
+
+
+
+
+
+ - 2
+ - Filtered stream
+ - d0f79279-4a97-4aab-8e91-aeb5eceb2957
+ - false
+ - Stream
+ - S(1)
+ - false
+ - 0
+
+
+
+
+ -
+ 291
+ 1524
+ 28
+ 60
+
+ -
+ 305
+ 1554
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 22990b1f-9be6-477c-ad89-f775cd347105
+ - Flip Curve
+
+
+
+
+ - Flip a curve using an optional guide curve.
+ - true
+ - e664175e-8617-41e1-988b-43fb771f53ba
+ - Flip Curve
+ - Flip
+
+
+
+
+ -
+ 156
+ 1562
+ 66
+ 44
+
+ -
+ 188
+ 1584
+
+
+
+
+
+ - Curve to flip
+ - 603ef112-78dd-43bd-9877-ef4b969b5ac9
+ - Curve
+ - C
+ - false
+ - 86caf817-445e-4796-b29c-e1d7203996ce
+ - 1
+
+
+
+
+ -
+ 158
+ 1564
+ 15
+ 20
+
+ -
+ 167
+ 1574
+
+
+
+
+
+
+
+ - Optional guide curve
+ - b0daa22c-4428-4040-a3ca-800c9fe78aa2
+ - Guide
+ - G
+ - true
+ - 0
+
+
+
+
+ -
+ 158
+ 1584
+ 15
+ 20
+
+ -
+ 167
+ 1594
+
+
+
+
+
+
+
+ - Flipped curve
+ - 876f4851-1798-41c3-97d4-25f54e4be852
+ - Curve
+ - C
+ - false
+ - 0
+
+
+
+
+ -
+ 203
+ 1564
+ 17
+ 20
+
+ -
+ 211.5
+ 1574
+
+
+
+
+
+
+
+ - Flip action
+ - bb44d615-60b7-4dd3-a0b5-fcdc3f854a28
+ - Flag
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 203
+ 1584
+ 17
+ 20
+
+ -
+ 211.5
+ 1594
+
+
+
+
+
+
+
+
+
+
+
+ - 2e78987b-9dfb-42a2-8b76-3923ac8bd91a
+ - Boolean Toggle
+
+
+
+
+ - Boolean (true/false) toggle
+ - 1af3d673-3732-4271-8c59-52c07949abb2
+ - Boolean Toggle
+ - Toggle
+ - false
+ - 0
+ - true
+
+
+
+
+ -
+ -230
+ 1442
+ 104
+ 22
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;170;135;255
+
+ - A group of Grasshopper objects
+ - 1af3d673-3732-4271-8c59-52c07949abb2
+ - 1
+ - 7c27fde1-5d5b-46e1-bf58-79fb5f2a792e
+ - Group
+ - FLIP
+
+
+
+
+
+
+
+
+
+ - 8529dbdf-9b6f-42e9-8e1f-c7a2bde56a70
+ - Line
+
+
+
+
+ - Contains a collection of line segments
+ - true
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - Line
+ - Line
+ - false
+ - 11ee2cb8-fea9-407e-8b4b-cbcf1fe58761
+ - 1
+
+
+
+
+ -
+ 472
+ 1545
+ 50
+ 24
+
+ -
+ 497.7238
+ 1557.053
+
+
+
+
+
+
+
+
+
+ - eeafc956-268e-461d-8e73-ee05c6f72c01
+ - Stream Filter
+
+
+
+
+ - Filters a collection of input streams
+ - true
+ - 9d82d577-6636-40a6-89a2-6f58d4a8a308
+ - Stream Filter
+ - Filter
+
+
+
+
+ -
+ 244
+ 1287
+ 77
+ 64
+
+ -
+ 276
+ 1319
+
+
+
+
+
+ - 3
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - Index of Gate stream
+ - df6a3ca1-e96c-4208-ad74-c94740172476
+ - Gate
+ - G
+ - false
+ - 13ac4c40-1353-44eb-ba9c-dfde3983e024
+ - 1
+
+
+
+
+ -
+ 246
+ 1289
+ 15
+ 20
+
+ -
+ 255
+ 1299
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 0
+ - 43b0fae7-669c-4839-b79c-f8e495099b62
+ - false
+ - Stream 0
+ - 0
+ - true
+ - 18d2fa4a-de1b-4cfd-bf80-4b8d5fa0182f
+ - 1
+
+
+
+
+ -
+ 246
+ 1309
+ 15
+ 20
+
+ -
+ 255
+ 1319
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 1
+ - ad90ebc4-1f44-4b98-b4b3-0977a5671062
+ - false
+ - Stream 1
+ - 1
+ - true
+ - cb376a9b-3404-4cd3-8d2d-4d51d2a446ae
+ - 1
+
+
+
+
+ -
+ 246
+ 1329
+ 15
+ 20
+
+ -
+ 255
+ 1339
+
+
+
+
+
+
+
+ - 2
+ - Filtered stream
+ - af62623b-9273-42c0-b386-24cd70a88cce
+ - false
+ - Stream
+ - S(1)
+ - false
+ - 0
+
+
+
+
+ -
+ 291
+ 1289
+ 28
+ 60
+
+ -
+ 305
+ 1319
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 22990b1f-9be6-477c-ad89-f775cd347105
+ - Flip Curve
+
+
+
+
+ - Flip a curve using an optional guide curve.
+ - true
+ - 7f188938-0adc-40a1-aaa1-c82bf68c0621
+ - Flip Curve
+ - Flip
+
+
+
+
+ -
+ 166
+ 1327
+ 66
+ 44
+
+ -
+ 198
+ 1349
+
+
+
+
+
+ - Curve to flip
+ - 03f77cf3-47f0-4a80-8365-cc8ae15440fa
+ - Curve
+ - C
+ - false
+ - 18d2fa4a-de1b-4cfd-bf80-4b8d5fa0182f
+ - 1
+
+
+
+
+ -
+ 168
+ 1329
+ 15
+ 20
+
+ -
+ 177
+ 1339
+
+
+
+
+
+
+
+ - Optional guide curve
+ - e7359af5-c6bc-4783-824b-3a94bae25799
+ - Guide
+ - G
+ - true
+ - 0
+
+
+
+
+ -
+ 168
+ 1349
+ 15
+ 20
+
+ -
+ 177
+ 1359
+
+
+
+
+
+
+
+ - Flipped curve
+ - cb376a9b-3404-4cd3-8d2d-4d51d2a446ae
+ - Curve
+ - C
+ - false
+ - 0
+
+
+
+
+ -
+ 213
+ 1329
+ 17
+ 20
+
+ -
+ 221.5
+ 1339
+
+
+
+
+
+
+
+ - Flip action
+ - 761c1b3d-b418-4360-9bf9-d5af6a50cf40
+ - Flag
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 213
+ 1349
+ 17
+ 20
+
+ -
+ 221.5
+ 1359
+
+
+
+
+
+
+
+
+
+
+
+ - 2e78987b-9dfb-42a2-8b76-3923ac8bd91a
+ - Boolean Toggle
+
+
+
+
+ - Boolean (true/false) toggle
+ - 13ac4c40-1353-44eb-ba9c-dfde3983e024
+ - Boolean Toggle
+ - Toggle
+ - false
+ - 0
+ - true
+
+
+
+
+ -
+ 30
+ 1236
+ 104
+ 22
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;170;135;255
+
+ - A group of Grasshopper objects
+ - 13ac4c40-1353-44eb-ba9c-dfde3983e024
+ - 1
+ - 5f1aeb7c-013f-4d68-9e03-9abc339aaf8d
+ - Group
+ - FLIP
+
+
+
+
+
+
+
+
+
+ - 8529dbdf-9b6f-42e9-8e1f-c7a2bde56a70
+ - Line
+
+
+
+
+ - Contains a collection of line segments
+ - true
+ - 22a7c548-2a3a-46f3-9aed-df6d798fd1fe
+ - Line
+ - Line
+ - false
+ - af62623b-9273-42c0-b386-24cd70a88cce
+ - 1
+
+
+
+
+ -
+ 478
+ 1307
+ 50
+ 24
+
+ -
+ 503.1081
+ 1319.313
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;170;135;255
+
+ - A group of Grasshopper objects
+ - aa01afa4-173a-4459-9bf1-9dc2e745c93f
+ - 1
+ - 6fd57bc8-9d7e-48e5-9e88-112b89d3d3ff
+ - Group
+
+
+
+
+
+
+
+
+
+
+ - 59e0b89a-e487-49f8-bab8-b5bab16be14c
+ - Panel
+
+
+
+
+ - A panel for custom notes and text values
+ - fcab9592-e2af-4a82-ba96-1a6ae865e464
+ - Panel
+
+ - false
+ - 0
+ - 457734f7-3478-4434-a17d-636820c0980c
+ - 1
+ - Double click to edit panel content…
+
+
+
+
+ -
+ 1742
+ 1079
+ 201
+ 53
+
+ - 0
+ - 0
+ - 0
+ -
+ 1742.733
+ 1079.09
+
+
+
+
+
+ -
+ 255;255;250;90
+
+ - true
+ - true
+ - true
+ - false
+ - false
+ - true
+
+
+
+
+
+
+
+
+ - 59e0b89a-e487-49f8-bab8-b5bab16be14c
+ - Panel
+
+
+
+
+ - A panel for custom notes and text values
+ - dc5d40be-772e-4800-9d83-6ba9f705272e
+ - Panel
+
+ - false
+ - 1
+ - 5946e9dc-f13c-4e3c-8873-1cc4030c843d
+ - 1
+ - Double click to edit panel content…
+
+
+
+
+ -
+ 2465
+ 1467
+ 941
+ 630
+
+ - 0
+ - 0
+ - 0
+ -
+ 2465.919
+ 1467.103
+
+
+
+
+
+ -
+ 255;255;250;90
+
+ - true
+ - true
+ - true
+ - false
+ - false
+ - true
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - true
+ - 6758fb57-bfa5-452b-afb1-29c442b80cae
+ - List Item
+ - Item
+
+
+
+
+ -
+ 386
+ 1770
+ 64
+ 64
+
+ -
+ 420
+ 1802
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - d59fad60-6959-4751-b1d2-d1f1508e8b63
+ - List
+ - L
+ - false
+ - 65a4e860-1e2f-40f1-811c-db669445260d
+ - 1
+
+
+
+
+ -
+ 388
+ 1772
+ 17
+ 20
+
+ -
+ 398
+ 1782
+
+
+
+
+
+
+
+ - Item index
+ - fb30abd5-e590-4c92-8c2c-8ddd83edbd3c
+ - Index
+ - i
+ - false
+ - e23ba652-99eb-4b68-8aed-303901e36c47
+ - 1
+
+
+
+
+ -
+ 388
+ 1792
+ 17
+ 20
+
+ -
+ 398
+ 1802
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - da645836-39aa-4154-84c1-cba3540cdb57
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 388
+ 1812
+ 17
+ 20
+
+ -
+ 398
+ 1822
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - d5fd1fb2-6a4b-451c-9dfc-5936004b2a19
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 435
+ 1772
+ 13
+ 60
+
+ -
+ 441.5
+ 1802
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - true
+ - 65a4e860-1e2f-40f1-811c-db669445260d
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ 245
+ 1770
+ 50
+ 24
+
+ -
+ 270.2979
+ 1782.105
+
+
+
+
+
+ - 1
+
+
+
+
+ - 4
+ - {0;0}
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZG5KV7LLreD7Dh/+/5cPK155QKIotjs+Ncjh9P7Z4r4RDxyWMVQaXru16UDzAbkVds4zHZgYEMDjUnVJumCvA8glmRHv9/6vZ2qAyXEzDCYAAA==
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZO6HeysX9uU8cGg93/ps/YwFB6ByDAm/T9hlbLnv8On/f/mw4pUHJIpiu+NTgxxAethULxQvqpkJU9sAcskak6dX/tczNcD0czMMJgAA
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZK7ijvjIvjUPHD7+/y8fVrzyQIGZ50mu61Mc2HTP3rgfjBCXKIrtjk8NcgDp8YuffGHLhskHoOY0gFwSWJWm8r+eqQEqxsDNMJgAAA==
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZC6TTnuX7OEHDh///5cPK155QKIotjs+NcihSmdq9CaXhw5RNoJsPupLDnyq/pDxhWXBASYGBNjZ33f2nvFiB5BLKs/eTv5fz9QAk+NmGEwAAA==
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - e23ba652-99eb-4b68-8aed-303901e36c47
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 195
+ 1832
+ 160
+ 20
+
+ -
+ 195.4534
+ 1832.229
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 3
+ - 0
+ - 0
+ - 3
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - GhPython Script
+
+
+
+
+ - from compas.geometry import Line, Plane, Vector, Point
+from compas_timber.elements import Beam
+from compas_timber._fabrication import JackRafterCut, JackRafterCutParams
+from compas.scene import Scene, SceneObject
+from compas_rhino.conversions import brep_to_rhino, plane_to_rhino
+
+centerline = Line(Point(x=270.0, y=270.0, z=590.0), Point(x=1220.0, y=680.0, z=590.0))
+cross_section = (60, 120)
+beam = Beam.from_centerline(centerline, cross_section[0], cross_section[1])
+
+# cut the start of the beam
+normal = Vector(x=-0.996194698092, y=-0.0, z=-0.0871557427477)
+plane = Plane(Point(x=460.346635340, y=445.167151490, z=473.942755901), normal)
+instance = JackRafterCut.from_plane_and_beam(plane, beam, ref_side_index = 0)
+
+beam_geo = brep_to_rhino(beam.compute_geometry())
+plane_geo = plane_to_rhino(plane)
+effective_plane = plane_to_rhino(instance.plane_from_params_and_beam(beam))
+
+params = JackRafterCutParams(instance)
+print(params.as_dict())
+print(centerline.length)
+ - GhPython provides a Python script component
+ -
+ 332
+ 180
+
+ -
+ 1339
+ 874
+
+ - true
+ - true
+ - false
+ - false
+ - 396034ae-3e76-4e48-90f5-696776da7e33
+ - false
+ - true
+ - GhPython Script
+ - Python
+
+
+
+
+ -
+ 108
+ 511
+ 128
+ 124
+
+ -
+ 137
+ 573
+
+
+
+
+
+ - 2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 6
+ - 3ede854e-c753-40eb-84cb-b48008f14fd4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - true
+ - Script variable Python
+ - 1f9df9f8-daeb-42f9-83e1-371ac54fcc49
+ - x
+ - x
+ - true
+ - 0
+ - true
+ - 0
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 110
+ 513
+ 12
+ 60
+
+ -
+ 117.5
+ 543
+
+
+
+
+
+
+
+ - true
+ - Script input y.
+ - ceb6a202-7655-4be9-bf4b-3520bc4f863d
+ - y
+ - y
+ - true
+ - 0
+ - true
+ - 0
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 110
+ 573
+ 12
+ 60
+
+ -
+ 117.5
+ 603
+
+
+
+
+
+
+
+ - The execution information, as output and error streams
+ - 141f06bc-8242-4c91-9cb6-fbb9f9b31a8f
+ - out
+ - out
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 513
+ 82
+ 20
+
+ -
+ 193
+ 523
+
+
+
+
+
+
+
+ - Script output beam.
+ - 145cad7b-4504-469e-a3dc-217e6685571a
+ - beam
+ - beam
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 533
+ 82
+ 20
+
+ -
+ 193
+ 543
+
+
+
+
+
+
+
+ - Script output beam_geo.
+ - 76eade1f-e1f8-4e17-8a12-abe10ba07b02
+ - beam_geo
+ - beam_geo
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 553
+ 82
+ 20
+
+ -
+ 193
+ 563
+
+
+
+
+
+
+
+ - Script output plane_geo.
+ - 4a992867-df7d-43b8-a147-78af83357f87
+ - plane_geo
+ - plane_geo
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 573
+ 82
+ 20
+
+ -
+ 193
+ 583
+
+
+
+
+
+
+
+ - Script output effective_plane.
+ - f0bb5cab-87bf-44e9-86bc-dcbb216e11ea
+ - effective_plane
+ - effective_plane
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 593
+ 82
+ 20
+
+ -
+ 193
+ 603
+
+
+
+
+
+
+
+ - Script output centerline.
+ - 63f119c4-bdf9-493f-8439-e08299b6407c
+ - centerline
+ - centerline
+ - false
+ - 0
+
+
+
+
+ -
+ 152
+ 613
+ 82
+ 20
+
+ -
+ 193
+ 623
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - 8223b16e-766a-4b72-9d1a-8fb8729b5e30
+ - Brep
+ - Brep
+ - false
+ - 76eade1f-e1f8-4e17-8a12-abe10ba07b02
+ - 1
+
+
+
+
+ -
+ 421
+ 727
+ 50
+ 24
+
+ -
+ 446.2088
+ 739.606
+
+
+
+
+
+
+
+
+
+ - 439a55a5-2f9e-4f66-9de2-32f24fec2ef5
+ - Plane Surface
+
+
+
+
+ - Create a plane surface
+ - true
+ - 3a33f254-d8e1-4caf-b1ca-12eb3546c7c6
+ - Plane Surface
+ - PlaneSrf
+
+
+
+
+ -
+ 379
+ 494
+ 64
+ 64
+
+ -
+ 410
+ 526
+
+
+
+
+
+ - Surface base plane
+ - dcc16ef7-f494-4461-a6f4-695c12e7fb79
+ - Plane
+ - P
+ - false
+ - f0bb5cab-87bf-44e9-86bc-dcbb216e11ea
+ - 1
+
+
+
+
+ -
+ 381
+ 496
+ 14
+ 20
+
+ -
+ 389.5
+ 506
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ - Dimensions in X direction
+ - 73bce102-4815-413a-bbe4-0f6ff3396161
+ - X Size
+ - X
+ - false
+ - 0
+
+
+
+
+ -
+ 381
+ 516
+ 14
+ 20
+
+ -
+ 389.5
+ 526
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ -10
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ - Dimensions in Y direction
+ - e87b4ed0-673f-428f-875f-e660dc9463f8
+ - Y Size
+ - Y
+ - false
+ - 0
+
+
+
+
+ -
+ 381
+ 536
+ 14
+ 20
+
+ -
+ 389.5
+ 546
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ -10
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ - Resulting plane surface
+ - 27b3c795-713e-4696-8198-6aa099881517
+ - Plane
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 425
+ 496
+ 16
+ 60
+
+ -
+ 433
+ 526
+
+
+
+
+
+
+
+
+
+
+
+ - 75eec078-a905-47a1-b0d2-0934182b1e3d
+ - Plane Origin
+
+
+
+
+ - Change the origin point of a plane
+ - true
+ - 14d73357-a3db-43f9-b9f1-5a7e03d07931
+ - Plane Origin
+ - Pl Origin
+
+
+
+
+ -
+ 423
+ 580
+ 68
+ 44
+
+ -
+ 455
+ 602
+
+
+
+
+
+ - Base plane
+ - 2b165994-e2b6-4270-a4c0-16201f3f097b
+ - Base
+ - B
+ - false
+ - 31719b32-f767-421c-b6dd-31299a177e0c
+ - 1
+
+
+
+
+ -
+ 425
+ 582
+ 15
+ 20
+
+ -
+ 434
+ 592
+
+
+
+
+
+
+
+ - New origin point of plane
+ - f6084a8b-5068-4842-9bf9-48fff940f3f6
+ - Origin
+ - O
+ - false
+ - 53c5d372-4bbd-4caa-81c7-e73e707d2b36
+ - 1
+
+
+
+
+ -
+ 425
+ 602
+ 15
+ 20
+
+ -
+ 434
+ 612
+
+
+
+
+
+
+
+ - Plane definition
+ - 0b5e337e-6ab5-4409-b9c8-6b10e7706a1a
+ - Plane
+ - Pl
+ - false
+ - 0
+
+
+
+
+ -
+ 470
+ 582
+ 19
+ 40
+
+ -
+ 479.5
+ 602
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 53c5d372-4bbd-4caa-81c7-e73e707d2b36
+ - Plane
+ - Pln
+ - false
+ - f0bb5cab-87bf-44e9-86bc-dcbb216e11ea
+ - 1
+
+
+
+
+ -
+ 303
+ 607
+ 50
+ 24
+
+ -
+ 328.3172
+ 619.2989
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 31719b32-f767-421c-b6dd-31299a177e0c
+ - Plane
+ - Pln
+ - false
+ - 4a992867-df7d-43b8-a147-78af83357f87
+ - 1
+
+
+
+
+ -
+ 301
+ 572
+ 50
+ 24
+
+ -
+ 326.9252
+ 584.1504
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - GhPython Script
+
+
+
+
+ - from compas.geometry import Line, Plane, Vector, Point
+from compas_timber.elements import Beam
+from compas_timber._fabrication import JackRafterCut, JackRafterCutParams
+from compas.scene import Scene, SceneObject
+from compas_rhino.conversions import brep_to_rhino, plane_to_rhino
+from compas_rhino import unload_modules
+
+unload_modules('compas_timber')
+
+centerline = Line(Point(x=270.0, y=270.0, z=590.0), Point(x=1220.0, y=680.0, z=590.0))
+cross_section = (60, 120)
+beam = Beam.from_centerline(centerline, cross_section[0], cross_section[1])
+
+# cut the end of the beam
+normal = Vector(x=-0.996194698092, y=-0.0, z=-0.0871557427477) * -1.0
+plane = Plane(Point(x=460.346635340, y=445.167151490, z=473.942755901), normal)
+instance = JackRafterCut.from_plane_and_beam(plane, beam)
+
+beam_geo = brep_to_rhino(beam.compute_geometry())
+plane_geo = plane_to_rhino(plane)
+effective_plane = plane_to_rhino(instance.plane_from_params_and_beam(beam))
+
+params = JackRafterCutParams(instance)
+print(params.as_dict())
+print(centerline.length)
+ - GhPython provides a Python script component
+ -
+ 332
+ 180
+
+ -
+ 1754
+ 874
+
+ - true
+ - true
+ - false
+ - false
+ - 402482d0-c6fd-4f93-bdbc-53a84b48429e
+ - false
+ - true
+ - GhPython Script
+ - Python
+
+
+
+
+ -
+ 113
+ 203
+ 128
+ 124
+
+ -
+ 142
+ 265
+
+
+
+
+
+ - 2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 6
+ - 3ede854e-c753-40eb-84cb-b48008f14fd4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - true
+ - Script variable Python
+ - ea8d2d0d-ffda-4ac1-b623-ee1e6c690ab5
+ - x
+ - x
+ - true
+ - 0
+ - true
+ - 0
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 115
+ 205
+ 12
+ 60
+
+ -
+ 122.5
+ 235
+
+
+
+
+
+
+
+ - true
+ - Script input y.
+ - 247b07da-edc9-4524-a26b-d09fa901f787
+ - y
+ - y
+ - true
+ - 0
+ - true
+ - 0
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 115
+ 265
+ 12
+ 60
+
+ -
+ 122.5
+ 295
+
+
+
+
+
+
+
+ - The execution information, as output and error streams
+ - c100d79b-36ed-4030-b49a-62ed31700446
+ - out
+ - out
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 205
+ 82
+ 20
+
+ -
+ 198
+ 215
+
+
+
+
+
+
+
+ - Script output beam.
+ - bd27d88c-317b-4f3b-8a6f-cb07235a5294
+ - beam
+ - beam
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 225
+ 82
+ 20
+
+ -
+ 198
+ 235
+
+
+
+
+
+
+
+ - Script output beam_geo.
+ - 1e704349-7141-4809-b27a-136d687976c9
+ - beam_geo
+ - beam_geo
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 245
+ 82
+ 20
+
+ -
+ 198
+ 255
+
+
+
+
+
+
+
+ - Script output plane_geo.
+ - 6ec5a5cf-4370-4b4b-a834-8355f200001d
+ - plane_geo
+ - plane_geo
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 265
+ 82
+ 20
+
+ -
+ 198
+ 275
+
+
+
+
+
+
+
+ - Script output effective_plane.
+ - 818d5658-15cd-4b2f-9914-36bdad5ac468
+ - effective_plane
+ - effective_plane
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 285
+ 82
+ 20
+
+ -
+ 198
+ 295
+
+
+
+
+
+
+
+ - Script output centerline.
+ - 38caa466-2565-4d39-92db-9f048224a6cf
+ - centerline
+ - centerline
+ - false
+ - 0
+
+
+
+
+ -
+ 157
+ 305
+ 82
+ 20
+
+ -
+ 198
+ 315
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - 43f8eb2f-323f-4139-aa8d-d6bfe960cd52
+ - Brep
+ - Brep
+ - false
+ - 1e704349-7141-4809-b27a-136d687976c9
+ - 1
+
+
+
+
+ -
+ 431
+ 415
+ 50
+ 24
+
+ -
+ 456.555
+ 427.7627
+
+
+
+
+
+
+
+
+
+ - 439a55a5-2f9e-4f66-9de2-32f24fec2ef5
+ - Plane Surface
+
+
+
+
+ - Create a plane surface
+ - true
+ - 89082f8d-7656-4fb6-b348-0d927b2649b7
+ - Plane Surface
+ - PlaneSrf
+
+
+
+
+ -
+ 388
+ 181
+ 64
+ 64
+
+ -
+ 419
+ 213
+
+
+
+
+
+ - Surface base plane
+ - 3e791009-e91f-4f92-abfe-584fa83a6404
+ - Plane
+ - P
+ - false
+ - 818d5658-15cd-4b2f-9914-36bdad5ac468
+ - 1
+
+
+
+
+ -
+ 390
+ 183
+ 14
+ 20
+
+ -
+ 398.5
+ 193
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ - Dimensions in X direction
+ - f0b18f6f-9d53-401d-937f-964c25e28fb3
+ - X Size
+ - X
+ - false
+ - 0
+
+
+
+
+ -
+ 390
+ 203
+ 14
+ 20
+
+ -
+ 398.5
+ 213
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ -10
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ - Dimensions in Y direction
+ - a33ad62e-5734-4be8-9f61-5e3c126cb2f3
+ - Y Size
+ - Y
+ - false
+ - 0
+
+
+
+
+ -
+ 390
+ 223
+ 14
+ 20
+
+ -
+ 398.5
+ 233
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ -10
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ - Resulting plane surface
+ - cf2e8590-6cf8-44a5-b0f5-6042a0e8d9a2
+ - Plane
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 434
+ 183
+ 16
+ 60
+
+ -
+ 442
+ 213
+
+
+
+
+
+
+
+
+
+
+
+ - 75eec078-a905-47a1-b0d2-0934182b1e3d
+ - Plane Origin
+
+
+
+
+ - Change the origin point of a plane
+ - true
+ - c910a426-fe39-49f4-bea5-e1758e95c6e7
+ - Plane Origin
+ - Pl Origin
+
+
+
+
+ -
+ 432
+ 267
+ 68
+ 44
+
+ -
+ 464
+ 289
+
+
+
+
+
+ - Base plane
+ - d66867b8-bf29-4872-bce1-af56bb6d3f48
+ - Base
+ - B
+ - false
+ - e1b388f1-a19b-4f43-9a94-4e186be610e4
+ - 1
+
+
+
+
+ -
+ 434
+ 269
+ 15
+ 20
+
+ -
+ 443
+ 279
+
+
+
+
+
+
+
+ - New origin point of plane
+ - 0b687328-839e-4777-8935-5a12e96ba70a
+ - Origin
+ - O
+ - false
+ - 6ec314ef-fad6-4428-9aa9-a6fb595fed58
+ - 1
+
+
+
+
+ -
+ 434
+ 289
+ 15
+ 20
+
+ -
+ 443
+ 299
+
+
+
+
+
+
+
+ - Plane definition
+ - c85ef330-28fe-4365-9455-37d100bc359f
+ - Plane
+ - Pl
+ - false
+ - 0
+
+
+
+
+ -
+ 479
+ 269
+ 19
+ 40
+
+ -
+ 488.5
+ 289
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 6ec314ef-fad6-4428-9aa9-a6fb595fed58
+ - Plane
+ - Pln
+ - false
+ - 818d5658-15cd-4b2f-9914-36bdad5ac468
+ - 1
+
+
+
+
+ -
+ 313
+ 295
+ 50
+ 24
+
+ -
+ 338.6637
+ 307.4556
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - e1b388f1-a19b-4f43-9a94-4e186be610e4
+ - Plane
+ - Pln
+ - false
+ - 6ec5a5cf-4370-4b4b-a834-8355f200001d
+ - 1
+
+
+
+
+ -
+ 312
+ 260
+ 50
+ 24
+
+ -
+ 337.2716
+ 272.3071
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 3
+ -
+ 150;255;135;135
+
+ - A group of Grasshopper objects
+ - 65a4e860-1e2f-40f1-811c-db669445260d
+ - 1
+ - 12377c43-e633-4311-af90-b012405de6f3
+ - Group
+ - UNITTEST LINES
+
+
+
+
+
+
+
+
+
+ - c552a431-af5b-46a9-a8a4-0fcbc27ef596
+ - Group
+
+
+
+
+ - 1
+ -
+ 150;170;135;255
+
+ - A group of Grasshopper objects
+ - 396034ae-3e76-4e48-90f5-696776da7e33
+ - 8223b16e-766a-4b72-9d1a-8fb8729b5e30
+ - 3a33f254-d8e1-4caf-b1ca-12eb3546c7c6
+ - 14d73357-a3db-43f9-b9f1-5a7e03d07931
+ - 53c5d372-4bbd-4caa-81c7-e73e707d2b36
+ - 31719b32-f767-421c-b6dd-31299a177e0c
+ - 402482d0-c6fd-4f93-bdbc-53a84b48429e
+ - 43f8eb2f-323f-4139-aa8d-d6bfe960cd52
+ - 89082f8d-7656-4fb6-b348-0d927b2649b7
+ - c910a426-fe39-49f4-bea5-e1758e95c6e7
+ - 6ec314ef-fad6-4428-9aa9-a6fb595fed58
+ - e1b388f1-a19b-4f43-9a94-4e186be610e4
+ - 12
+ - 9a873447-cf41-4dfc-a683-20af98a54e52
+ - Group
+ - JackRafterCut Unittest
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: T Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import TButtJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class T_TopologyJointRule(component):
+ def __init__(self):
+ super(T_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ if cls.SUPPORTED_TOPOLOGY == JointTopology.TOPO_T:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = TButtJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: TButtJoint"
+ self.AddRuntimeMessage(Warning, "TButtJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_T, TButtJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ if self.joint_type.SUPPORTED_TOPOLOGY != JointTopology.TOPO_T:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_T, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAANVJREFUSEvtk70NwjAQhTNC6CP5JC/AKIzACHS0TGAxQmqqjJDCJQUjMAIbGJ3kk8yzsePIFEgpviL+eZ/0zumcc90viRYYrWjWipznivs1hKGkFfVa0SUIF054cSkSfkiEpnhqRXsMySECvohh35gwJIcIws6Z0dfFtT1gb8aQHCKYIKSXA1xJCwEOtrmAKwlD+JvraVYRziDHqiEvfaavVc/US7jrVCXMUc4Nxu4GY12BWyQIgZl8/MVNBDUMxt596Bn3mGihlk1QZBMU+X9BiWihNW9f8MWtv55wzQAAAABJRU5ErkJggg==
+
+ - false
+ - 7934091a-9835-424d-9d20-74a1f5070b9a
+ - true
+ - true
+ - CT: T Topological Joint Rules
+ - T_Topo_Joint
+
+
+
+
+ -
+ 1620
+ 1479
+ 151
+ 28
+
+ -
+ 1695
+ 1493
+
+
+
+
+
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - mill_depth
+ - 24baab98-afb4-4250-90ab-3223abbc9a79
+ - mill_depth
+ - mill_depth
+ - true
+ - aa01afa4-173a-4459-9bf1-9dc2e745c93f
+ - 1
+
+
+
+
+ -
+ 1622
+ 1481
+ 58
+ 24
+
+ -
+ 1652.5
+ 1493
+
+
+
+
+
+
+
+ - Script output TButtJoint.
+ - f4bbf57e-0b0b-4949-958c-0cd0f6560057
+ - TButtJoint
+ - TButtJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1710
+ 1481
+ 59
+ 24
+
+ -
+ 1739.5
+ 1493
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - true
+ - c752484d-c8b1-4dfe-b95c-052b1b9086ea
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ 252
+ 1891
+ 50
+ 24
+
+ -
+ 277.9901
+ 1903.796
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - -1
+ -
+ Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erbmesbmekYmhgbmeoamBgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFhmw4Xruqfm5qSVFlXoB+TmVOZl5qc6lRWWpLEAF7GUQC7kSi5IzMstSjVNyOfMLUvPySouSillSEksSQYo4ODiYQG4VUGdgMALS7oUCPJzMQAY/iJgKxEy/6pkYOqH++v2fiUEEyn525bqf+8vLgvufMgg0Myp98Juz+/YfoHwgVF4AZO5Snxl77xs8cLi0+An/ha7NB55+jbgjuX+Ww/HrLxuFGR86fPr/Xz6seOWBI69XzPm3x8EBpGfS3F93NjIsOwA1pwHkkq0P80X/1zM1QMUYuBkGEwAA
+
+ - 00000000-0000-0000-0000-000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: L Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import LMiterJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class L_TopologyJointRule(component):
+ def __init__(self):
+ super(L_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ if cls.SUPPORTED_TOPOLOGY == JointTopology.TOPO_L:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = LMiterJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: LMiterJoint"
+ self.AddRuntimeMessage(Warning, "LMiterJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_L, LMiterJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ if self.joint_type.SUPPORTED_TOPOLOGY != JointTopology.TOPO_L:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_L, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ - true
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAANBJREFUSEvtk70NwjAQhTNCBrDkk7wAo9DQMwIjMIHFCIyQEVK4TMEIjMAGRif5JOvlcEwwiCLFV8Q/75PeOV2MsfsmswXGWRqdpZi44P475KHkLPXO0jkLF054sRYJ3yuhGndnaYchJUTAFzHsFQOGlBBB3jlzTXVxbTfYGzGkhAgGCOnlAFfSQoCDbS7gSvIQ/uZ6mlWEMyixasi1z/Sx6pkmCXetVcIc8aLxYTI+ROPDAfdUQQ7MRP2LPxLUsAkW2QSL/FKgMf2/oJbZQmue50vIddCJVREAAAAASUVORK5CYII=
+
+ - false
+ - 0944aec4-9862-4758-8cf6-b1210d2c9ec3
+ - true
+ - true
+ - CT: L Topological Joint Rules
+ - L_Topo_Joint
+
+
+
+
+ -
+ 1600
+ 1342
+ 185
+ 84
+
+ -
+ 1710
+ 1384
+
+
+
+
+
+ - 4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - mill_depth
+ - ed7bbabd-08ec-4457-b980-695ea636abec
+ - mill_depth
+ - mill_depth
+ - true
+ - aa01afa4-173a-4459-9bf1-9dc2e745c93f
+ - 1
+
+
+
+
+ -
+ 1602
+ 1344
+ 93
+ 20
+
+ -
+ 1650
+ 1354
+
+
+
+
+
+
+
+ - Script input small_beam_butts.
+ - 210d71de-22eb-4ef2-8dff-379dd889a511
+ - small_beam_butts
+ - small_beam_butts
+ - true
+ - 0
+
+
+
+
+ -
+ 1602
+ 1364
+ 93
+ 20
+
+ -
+ 1650
+ 1374
+
+
+
+
+
+
+
+ - Script input modify_cross.
+ - 4eeec5ec-06dd-4521-a720-5bfdc8d73b06
+ - modify_cross
+ - modify_cross
+ - true
+ - 0
+
+
+
+
+ -
+ 1602
+ 1384
+ 93
+ 20
+
+ -
+ 1650
+ 1394
+
+
+
+
+
+
+
+ - Script input reject_i.
+ - 68e5ce57-5068-4a59-b91c-b6abbf1a6370
+ - reject_i
+ - reject_i
+ - true
+ - 0
+
+
+
+
+ -
+ 1602
+ 1404
+ 93
+ 20
+
+ -
+ 1650
+ 1414
+
+
+
+
+
+
+
+ - Script output LButtJoint.
+ - 9e06f057-b0d5-495c-880e-79b82c83a1fe
+ - LButtJoint
+ - LButtJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1725
+ 1344
+ 58
+ 80
+
+ -
+ 1754
+ 1384
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - 10636707-b664-4e14-ba9a-d85ce2a0c401
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 1611
+ 1530
+ 195
+ 20
+
+ -
+ 1611.303
+ 1530.793
+
+
+
+
+
+ - 3
+ - 1
+ - 1
+ - 100
+ - 0
+ - 0
+ - 50
+
+
+
+
+
+
+
+
+ - 3cadddef-1e2b-4c09-9390-0e8f78f7609f
+ - Merge
+
+
+
+
+ - Merge a bunch of data streams
+ - true
+ - 50b4d783-2e1c-4a34-ba17-9ce6f72fbd7b
+ - Merge
+ - Merge
+
+
+
+
+ -
+ 378
+ 1871
+ 88
+ 64
+
+ -
+ 416
+ 1903
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 2
+ - Data stream 1
+ - 42be7b8d-e20c-46e5-8570-12c12619037d
+ - false
+ - Data 1
+ - D1
+ - true
+ - 65a4e860-1e2f-40f1-811c-db669445260d
+ - 1
+
+
+
+
+ -
+ 380
+ 1873
+ 21
+ 20
+
+ -
+ 392
+ 1883
+
+
+
+
+
+
+
+ - 2
+ - Data stream 2
+ - 7acfd55e-46af-4e26-8693-ddbe69099f6d
+ - false
+ - Data 2
+ - D2
+ - true
+ - c752484d-c8b1-4dfe-b95c-052b1b9086ea
+ - 1
+
+
+
+
+ -
+ 380
+ 1893
+ 21
+ 20
+
+ -
+ 392
+ 1903
+
+
+
+
+
+
+
+ - 2
+ - Data stream 3
+ - d269cf8e-d381-46f9-81ea-22e91d57bbfa
+ - false
+ - Data 3
+ - D3
+ - true
+ - 0
+
+
+
+
+ -
+ 380
+ 1913
+ 21
+ 20
+
+ -
+ 392
+ 1923
+
+
+
+
+
+
+
+ - 2
+ - Result of merge
+ - 11ee2cb8-fea9-407e-8b4b-cbcf1fe58761
+ - 1
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ 431
+ 1873
+ 33
+ 60
+
+ -
+ 439.5
+ 1903
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - eeafc956-268e-461d-8e73-ee05c6f72c01
+ - Stream Filter
+
+
+
+
+ - Filters a collection of input streams
+ - true
+ - 21576f20-ed8e-4c3d-ac74-f4c6622726c1
+ - Stream Filter
+ - Filter
+
+
+
+
+ -
+ 631
+ 1582
+ 77
+ 64
+
+ -
+ 663
+ 1614
+
+
+
+
+
+ - 3
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - Index of Gate stream
+ - 5654947d-2496-4fda-aa9d-464d6556a204
+ - Gate
+ - G
+ - false
+ - 1af3d673-3732-4271-8c59-52c07949abb2
+ - 1
+
+
+
+
+ -
+ 633
+ 1584
+ 15
+ 20
+
+ -
+ 642
+ 1594
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 0
+ - 754ae8d9-c10a-4fd4-9620-5e5a7054c6f6
+ - false
+ - Stream 0
+ - 0
+ - true
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 633
+ 1604
+ 15
+ 20
+
+ -
+ 642
+ 1614
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 1
+ - d482ec97-792e-4152-90f0-4e5000ccf833
+ - false
+ - Stream 1
+ - 1
+ - true
+ - 353d7621-3cd9-4d30-ac48-2ba4334ede62
+ - 1
+
+
+
+
+ -
+ 633
+ 1624
+ 15
+ 20
+
+ -
+ 642
+ 1634
+
+
+
+
+
+
+
+ - 2
+ - Filtered stream
+ - 5e8cc817-6357-4d57-8f5c-bb9d6ce9669c
+ - false
+ - Stream
+ - S(1)
+ - false
+ - 0
+
+
+
+
+ -
+ 678
+ 1584
+ 28
+ 60
+
+ -
+ 692
+ 1614
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 22990b1f-9be6-477c-ad89-f775cd347105
+ - Flip Curve
+
+
+
+
+ - Flip a curve using an optional guide curve.
+ - true
+ - 016c3b71-d2f1-4a70-be07-535a52db8a79
+ - Flip Curve
+ - Flip
+
+
+
+
+ -
+ 543
+ 1622
+ 66
+ 44
+
+ -
+ 575
+ 1644
+
+
+
+
+
+ - Curve to flip
+ - 603a86f3-4555-4985-bfc4-a523884cf415
+ - Curve
+ - C
+ - false
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 545
+ 1624
+ 15
+ 20
+
+ -
+ 554
+ 1634
+
+
+
+
+
+
+
+ - Optional guide curve
+ - e612372e-97c9-4956-83c1-7c0c87cd1d5f
+ - Guide
+ - G
+ - true
+ - 0
+
+
+
+
+ -
+ 545
+ 1644
+ 15
+ 20
+
+ -
+ 554
+ 1654
+
+
+
+
+
+
+
+ - Flipped curve
+ - 353d7621-3cd9-4d30-ac48-2ba4334ede62
+ - Curve
+ - C
+ - false
+ - 0
+
+
+
+
+ -
+ 590
+ 1624
+ 17
+ 20
+
+ -
+ 598.5
+ 1634
+
+
+
+
+
+
+
+ - Flip action
+ - 3a7a88e4-e7f8-460d-ab66-2f6e0538533a
+ - Flag
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 590
+ 1644
+ 17
+ 20
+
+ -
+ 598.5
+ 1654
+
+
+
+
+
+
+
+
+
+
+
+ - 28061aae-04fb-4cb5-ac45-16f3b66bc0a4
+ - Center Box
+
+
+
+
+ - Create a box centered on a plane.
+ - true
+ - b914bb37-7b5a-46a9-a1fb-6b230797d09b
+ - Center Box
+ - Box
+
+
+
+
+ -
+ 2280
+ 1273
+ 64
+ 84
+
+ -
+ 2311
+ 1315
+
+
+
+
+
+ - Base plane
+ - 65a01b3f-9641-4df1-a831-f0ac4ddbc0c8
+ - Base
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1275
+ 14
+ 20
+
+ -
+ 2290.5
+ 1285
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {x} direction.
+ - be885264-6145-4d8b-84ce-3158683407e8
+ - X
+ - X
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1295
+ 14
+ 20
+
+ -
+ 2290.5
+ 1305
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {y} direction.
+ - b9985f26-d4b2-41fe-9963-3b6ad745502d
+ - Y
+ - Y
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1315
+ 14
+ 20
+
+ -
+ 2290.5
+ 1325
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {z} direction.
+ - 7c5c3d48-fede-49be-a769-5b4472f051c3
+ - Z
+ - Z
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1335
+ 14
+ 20
+
+ -
+ 2290.5
+ 1345
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Resulting box
+ - 9db4d62a-2de4-4b49-b116-d91d1fb3fbc1
+ - Box
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2326
+ 1275
+ 16
+ 80
+
+ -
+ 2334
+ 1315
+
+
+
+
+
+
+
+
+
+
+
+ - 7c0523e8-79c9-45a2-8777-cf0d46bc5432
+ - Volume
+
+
+
+
+ - Solve volume properties for closed breps and meshes.
+ - true
+ - 87adbf20-4372-408f-afd2-8de0206c319f
+ - Volume
+ - Volume
+
+
+
+
+ -
+ 2086
+ 1278
+ 66
+ 44
+
+ -
+ 2118
+ 1300
+
+
+
+
+
+ - 1
+ - ac2bc2cb-70fb-4dd5-9c78-7e1ea97fe278
+ - 2
+ - 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312
+ - fbac3e32-f100-4292-8692-77240a42fd1a
+
+
+
+
+ - Closed brep or mesh for volume computation
+ - 6653e031-0118-4585-9a5a-3c23bddc56e5
+ - Geometry
+ - G
+ - false
+ - b3588f0a-414e-4a3d-87e6-79cde3ae279b
+ - 1
+
+
+
+
+ -
+ 2088
+ 1280
+ 15
+ 40
+
+ -
+ 2097
+ 1300
+
+
+
+
+
+
+
+ - Volume of geometry
+ - 84b4bf60-4f2c-410a-abe2-275faa062d0f
+ - Volume
+ - V
+ - true
+ - 0
+
+
+
+
+ -
+ 2133
+ 1280
+ 17
+ 20
+
+ -
+ 2141.5
+ 1290
+
+
+
+
+
+
+
+ - Volume centroid of geometry
+ - c4969727-a064-4123-ae15-e612faccd299
+ - Centroid
+ - C
+ - true
+ - 0
+
+
+
+
+ -
+ 2133
+ 1300
+ 17
+ 20
+
+ -
+ 2141.5
+ 1310
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 63256972-bef4-4fe4-b953-922687ce5e7c
+ - Plane
+ - Pln
+ - false
+ - 502370ce-eac6-4444-b153-000dda5f7ad2
+ - 1
+
+
+
+
+ -
+ 1925
+ 1329
+ 50
+ 24
+
+ -
+ 1950.909
+ 1341.371
+
+
+
+
+
+
+
+
+
+ - 9103c240-a6a9-4223-9b42-dbd19bf38e2b
+ - Unit Z
+
+
+
+
+ - Unit vector parallel to the world {z} axis.
+ - true
+ - 69a1047d-365a-4696-ac21-d66891c3cd01
+ - Unit Z
+ - Z
+
+
+
+
+ -
+ 724
+ 1525
+ 63
+ 28
+
+ -
+ 753
+ 1539
+
+
+
+
+
+ - Unit multiplication
+ - b815eb40-79e9-4d59-baf5-789e4733642b
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 726
+ 1527
+ 12
+ 24
+
+ -
+ 733.5
+ 1539
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {z} vector
+ - 7d37dfc6-37e3-4d21-81f9-feb71a9e7daf
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ 768
+ 1527
+ 17
+ 24
+
+ -
+ 776.5
+ 1539
+
+
+
+
+
+
+
+
+
+
+
+ - 79f9fbb3-8f1d-4d9a-88a9-f7961b1012cd
+ - Unit X
+
+
+
+
+ - Unit vector parallel to the world {x} axis.
+ - true
+ - 0ee70b32-a69d-44dc-a0ef-f78b3accb6a6
+ - Unit X
+ - X
+
+
+
+
+ -
+ 744
+ 1499
+ 63
+ 28
+
+ -
+ 773
+ 1513
+
+
+
+
+
+ - Unit multiplication
+ - 84f4a5d7-05c6-4323-aa8c-0f3aaafe88ab
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 746
+ 1501
+ 12
+ 24
+
+ -
+ 753.5
+ 1513
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {x} vector
+ - 985674c1-93e7-4bfa-a6b2-fa372e7b66a4
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ 788
+ 1501
+ 17
+ 24
+
+ -
+ 796.5
+ 1513
+
+
+
+
+
+
+
+
+
+
+
+ - a0d62394-a118-422d-abb3-6af115c75b25
+ - Addition
+
+
+
+
+ - Mathematical addition
+ - true
+ - 3da3f49a-976d-4d1a-9944-be7361193132
+ - Addition
+ - A+B
+
+
+
+
+ -
+ 822
+ 1499
+ 65
+ 44
+
+ -
+ 853
+ 1521
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - First item for addition
+ - 5e583fc7-a180-47d9-806a-9671969c74cd
+ - A
+ - A
+ - true
+ - 985674c1-93e7-4bfa-a6b2-fa372e7b66a4
+ - 1
+
+
+
+
+ -
+ 824
+ 1501
+ 14
+ 20
+
+ -
+ 832.5
+ 1511
+
+
+
+
+
+
+
+ - Second item for addition
+ - 1258f834-4314-420b-92a4-2ea8f8149a09
+ - B
+ - B
+ - true
+ - 7d37dfc6-37e3-4d21-81f9-feb71a9e7daf
+ - 1
+
+
+
+
+ -
+ 824
+ 1521
+ 14
+ 20
+
+ -
+ 832.5
+ 1531
+
+
+
+
+
+
+
+ - Result of addition
+ - f0b81979-f783-450f-b862-9b53febcbe90
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ 868
+ 1501
+ 17
+ 40
+
+ -
+ 876.5
+ 1521
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - true
+ - 303f3345-f7ae-4d8a-a427-00633f5158c9
+ - List Item
+ - Item
+
+
+
+
+ -
+ 2261
+ 1383
+ 64
+ 64
+
+ -
+ 2295
+ 1415
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - 02198cbf-b46c-409a-876a-ab7137c2c7c3
+ - List
+ - L
+ - false
+ - 725ac33d-374c-4cc0-8f18-3e15182f1323
+ - 1
+
+
+
+
+ -
+ 2263
+ 1385
+ 17
+ 20
+
+ -
+ 2273
+ 1395
+
+
+
+
+
+
+
+ - Item index
+ - a58fb182-d690-47fe-a531-395d7a504986
+ - Index
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2263
+ 1405
+ 17
+ 20
+
+ -
+ 2273
+ 1415
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - cfacaccd-8c41-4b49-b062-31e2775b1e82
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 2263
+ 1425
+ 17
+ 20
+
+ -
+ 2273
+ 1435
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - 53f6b1d1-c010-483d-b5aa-7c83fc14f9b6
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2310
+ 1385
+ 13
+ 60
+
+ -
+ 2316.5
+ 1415
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - true
+ - cf135943-38be-4663-ab8d-0adadaca146c
+ - List Item
+ - Item
+
+
+
+
+ -
+ 2029
+ 1336
+ 74
+ 84
+
+ -
+ 2063
+ 1378
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - 1522da3f-4c46-480e-9c80-988a4ab52cfd
+ - List
+ - L
+ - false
+ - 63256972-bef4-4fe4-b953-922687ce5e7c
+ - 1
+
+
+
+
+ -
+ 2031
+ 1338
+ 17
+ 26
+
+ -
+ 2041
+ 1351.333
+
+
+
+
+
+
+
+ - Item index
+ - 4b65d389-6766-4850-b495-164cce1c42de
+ - Index
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2031
+ 1364
+ 17
+ 27
+
+ -
+ 2041
+ 1378
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - b48d0cca-8f71-499e-a857-6d7f31da9575
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 2031
+ 1391
+ 17
+ 27
+
+ -
+ 2041
+ 1404.667
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - 6a0268e5-201a-44ec-89af-473ef88f105f
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1338
+ 23
+ 20
+
+ -
+ 2089.5
+ 1348
+
+
+
+
+
+
+
+ - Item at {+1'}
+ - d6d70e65-f2b9-4f33-807b-e4851b80613a
+ - false
+ - Item +1
+ - +1
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1358
+ 23
+ 20
+
+ -
+ 2089.5
+ 1368
+
+
+
+
+
+
+
+ - Item at {+2'}
+ - a97d3de6-311e-495d-903f-7d5145e85a2a
+ - false
+ - Item +2
+ - +2
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1378
+ 23
+ 20
+
+ -
+ 2089.5
+ 1388
+
+
+
+
+
+
+
+ - Item at {+3'}
+ - b54d49ae-2637-4951-a942-5be0ff2aeac8
+ - false
+ - Item +3
+ - +3
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1398
+ 23
+ 20
+
+ -
+ 2089.5
+ 1408
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - d8332545-21b2-4716-96e3-8559a9876e17
+ - Dispatch
+
+
+
+
+ - Dispatch the items in a list into two target lists.
+ - true
+ - cb623ee7-06c3-4400-aaba-a9487b4ee237
+ - Dispatch
+ - Dispatch
+
+
+
+
+ -
+ 2145
+ 1507
+ 64
+ 44
+
+ -
+ 2175
+ 1529
+
+
+
+
+
+ - 1
+ - List to filter
+ - f301c669-eabb-4535-b5ed-b3bf1b42699b
+ - List
+ - L
+ - false
+ - 725ac33d-374c-4cc0-8f18-3e15182f1323
+ - 1
+
+
+
+
+ -
+ 2147
+ 1509
+ 13
+ 20
+
+ -
+ 2155
+ 1519
+
+
+
+
+
+
+
+ - 1
+ - Dispatch pattern
+ - 4b1f1469-cc85-4ab1-9812-a58999856e66
+ - Dispatch pattern
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 2147
+ 1529
+ 13
+ 20
+
+ -
+ 2155
+ 1539
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+ - false
+
+
+
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for True values
+ - 070a7853-fe38-4e8a-b6d2-caeaaa0ae003
+ - List A
+ - A
+ - false
+ - 0
+
+
+
+
+ -
+ 2190
+ 1509
+ 17
+ 20
+
+ -
+ 2198.5
+ 1519
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for False values
+ - 90efa295-d4df-4bf6-9596-5f0142788873
+ - List B
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2190
+ 1529
+ 17
+ 20
+
+ -
+ 2198.5
+ 1539
+
+
+
+
+
+
+
+
+
+
+
+ - 537b0419-bbc2-4ff4-bf08-afe526367b2c
+ - Custom Preview
+
+
+
+
+ - Allows for customized geometry previews
+ - true
+ - 5969f9b6-e599-48a8-a77a-9967f0e908ee
+ - Custom Preview
+ - Preview
+
+
+
+
+
+ -
+ 2300
+ 1481
+ 48
+ 44
+
+ -
+ 2334
+ 1503
+
+
+
+
+
+ - Geometry to preview
+ - true
+ - 6ffcc6ca-c0f2-4057-80d1-9394a508e988
+ - Geometry
+ - G
+ - false
+ - 070a7853-fe38-4e8a-b6d2-caeaaa0ae003
+ - 1
+
+
+
+
+ -
+ 2302
+ 1483
+ 17
+ 20
+
+ -
+ 2312
+ 1493
+
+
+
+
+
+
+
+ - The material override
+ - 4add57de-5031-447f-a497-bb416da407be
+ - Material
+ - M
+ - false
+ - 0
+
+
+
+
+ -
+ 2302
+ 1503
+ 17
+ 20
+
+ -
+ 2312
+ 1513
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 255;221;160;221
+
+ -
+ 255;66;48;66
+
+ - 0.5
+ -
+ 255;255;255;255
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - 05edbc97-20e7-47f4-ba39-16e952aca10f
+ - Brep
+ - Brep
+ - false
+ - 90efa295-d4df-4bf6-9596-5f0142788873
+ - 1
+
+
+
+
+ -
+ 2230
+ 1577
+ 50
+ 24
+
+ -
+ 2255.067
+ 1589.683
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - c17ecd4e-0c95-4f30-a30e-9d892e6426cc
+ - Brep
+ - Brep
+ - false
+ - 0
+
+
+
+
+ -
+ 2229
+ 928
+ 50
+ 24
+
+ -
+ 2254.2
+ 940.6667
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - 29681234-09cf-497f-ae28-cbb735e46f22
+
+
+
+
+ - ee1a0b75-9176-4b97-b45c-8f17b5c16e99
+
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - 380a46d2-43bc-4a8c-8a22-b7aef89bbf72
+ - Plane
+ - Pln
+ - false
+ - c17ecd4e-0c95-4f30-a30e-9d892e6426cc
+ - 1
+
+
+
+
+ -
+ 2313
+ 928
+ 50
+ 24
+
+ -
+ 2338.067
+ 940.9667
+
+
+
+
+
+
+
+
+
+ - 6ea4c4c7-ddef-4313-a21f-8b445c20220c
+ - 1c9de8a1-315f-4c56-af06-8f69fee80a7a
+ - Tween Two Planes
+
+
+
+
+ - Tween between two planes.
+ - 8a7ee619-ca42-4727-a2cc-5cf2035c36c8
+ - Tween Two Planes
+ - Twn2Plns
+
+
+
+
+ -
+ 2493
+ 939
+ 65
+ 84
+
+ -
+ 2525
+ 981
+
+
+
+
+
+ - Plane to tween from
+ - eaae4cc6-ae0d-4898-a8ae-a582ca50ce29
+ - Plane A
+ - A
+ - false
+ - b70b10e6-e22b-4abe-a2d5-45a65332177c
+ - 1
+
+
+
+
+ -
+ 2495
+ 941
+ 15
+ 20
+
+ -
+ 2504
+ 951
+
+
+
+
+
+
+
+ - Plane to tween to
+ - 9d069cc0-02f3-475f-9b9b-4dd32f2ea790
+ - Plane B
+ - B
+ - false
+ - fc33ea5e-8457-40ab-8a5f-4f6de1e193d7
+ - 1
+
+
+
+
+ -
+ 2495
+ 961
+ 15
+ 20
+
+ -
+ 2504
+ 971
+
+
+
+
+
+
+
+ - Tween factor (0.0 = Plane A, 1.0 = Plane B)
+ - 06cd748f-8bc3-48ec-91f1-e33ddd966045
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 2495
+ 981
+ 15
+ 20
+
+ -
+ 2504
+ 991
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0.5
+
+
+
+
+
+
+
+
+
+
+ - Interpolate with quaternion rotation
+ - 012b355a-6715-4ddf-9169-8b9e68bfcacb
+ - Quaternion
+ - Q
+ - false
+ - 0
+
+
+
+
+ -
+ 2495
+ 1001
+ 15
+ 20
+
+ -
+ 2504
+ 1011
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Resulting tween plane
+ - 0a7cd565-45a8-4c93-8f75-04d086baa295
+ - Tween
+ - T
+ - false
+ - 0
+
+
+
+
+ -
+ 2540
+ 941
+ 16
+ 80
+
+ -
+ 2548
+ 981
+
+
+
+
+
+
+
+
+
+
+
+ - d8332545-21b2-4716-96e3-8559a9876e17
+ - Dispatch
+
+
+
+
+ - Dispatch the items in a list into two target lists.
+ - 687ce77d-9faf-470b-aaea-ccbcc0ba4326
+ - Dispatch
+ - Dispatch
+
+
+
+
+ -
+ 2379
+ 939
+ 64
+ 44
+
+ -
+ 2409
+ 961
+
+
+
+
+
+ - 1
+ - List to filter
+ - 8997e329-8f57-4af9-b7d4-64a6d829387b
+ - List
+ - L
+ - false
+ - 380a46d2-43bc-4a8c-8a22-b7aef89bbf72
+ - 1
+
+
+
+
+ -
+ 2381
+ 941
+ 13
+ 20
+
+ -
+ 2389
+ 951
+
+
+
+
+
+
+
+ - 1
+ - Dispatch pattern
+ - 22526779-5169-4e54-9c8d-4e398ca1638c
+ - Dispatch pattern
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 2381
+ 961
+ 13
+ 20
+
+ -
+ 2389
+ 971
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+ - false
+
+
+
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for True values
+ - b70b10e6-e22b-4abe-a2d5-45a65332177c
+ - List A
+ - A
+ - false
+ - 0
+
+
+
+
+ -
+ 2424
+ 941
+ 17
+ 20
+
+ -
+ 2432.5
+ 951
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for False values
+ - fc33ea5e-8457-40ab-8a5f-4f6de1e193d7
+ - List B
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2424
+ 961
+ 17
+ 20
+
+ -
+ 2432.5
+ 971
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - ed4080d4-774f-4f9f-9f0d-d8ea182a21c7
+ - Brep
+ - Brep
+ - false
+ - 0
+
+
+
+
+ -
+ 2225
+ 1066
+ 50
+ 24
+
+ -
+ 2250.564
+ 1078.813
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 5903e3bf-023e-4163-8e83-1513748adbcf
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - GhPython Script
+
+
+
+
+ - print(x)
+print(not x)
+ - GhPython provides a Python script component
+ -
+ 152
+ 152
+
+ -
+ 835
+ 874
+
+ - true
+ - false
+ - false
+ - 06d438ea-a8ad-4008-8d87-0fc64ee0605c
+ - false
+ - true
+ - GhPython Script
+ - Python
+
+
+
+
+ -
+ 1194
+ 1911
+ 72
+ 44
+
+ -
+ 1223
+ 1933
+
+
+
+
+
+ - 2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
+ - 2
+ - 3ede854e-c753-40eb-84cb-b48008f14fd4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - true
+ - Script variable Python
+ - 172a9d92-bdb0-4c5f-9ac6-67776b2ba60a
+ - x
+ - x
+ - true
+ - 0
+ - true
+ - 78604bce-20bd-4b08-9cdd-ce914039a739
+ - 1
+ - d60527f5-b5af-4ef6-8970-5f96fe412559
+
+
+
+
+ -
+ 1196
+ 1913
+ 12
+ 20
+
+ -
+ 1203.5
+ 1923
+
+
+
+
+
+
+
+ - true
+ - Script input y.
+ - 360aaafe-bde7-44ec-9e56-d3dad003f712
+ - y
+ - y
+ - true
+ - 0
+ - true
+ - 0
+ - 87f87f55-5b71-41f4-8aea-21d494016f81
+
+
+
+
+ -
+ 1196
+ 1933
+ 12
+ 20
+
+ -
+ 1203.5
+ 1943
+
+
+
+
+
+
+
+ - The execution information, as output and error streams
+ - 0a6920dc-ecd8-4d84-a7a0-b42f6f1108db
+ - out
+ - out
+ - false
+ - 0
+
+
+
+
+ -
+ 1238
+ 1913
+ 26
+ 20
+
+ -
+ 1251
+ 1923
+
+
+
+
+
+
+
+ - Script output a.
+ - d52d55cd-bd7e-4e1d-91ed-05a4839349c7
+ - a
+ - a
+ - false
+ - 0
+
+
+
+
+ -
+ 1238
+ 1933
+ 26
+ 20
+
+ -
+ 1251
+ 1943
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 2e78987b-9dfb-42a2-8b76-3923ac8bd91a
+ - Boolean Toggle
+
+
+
+
+ - Boolean (true/false) toggle
+ - 78604bce-20bd-4b08-9cdd-ce914039a739
+ - Boolean Toggle
+ - Toggle
+ - false
+ - 0
+ - false
+
+
+
+
+ -
+ 1063
+ 1845
+ 104
+ 22
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: L Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import LMiterJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class L_TopologyJointRule(component):
+ def __init__(self):
+ super(L_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ supported_topo = cls.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_L in supported_topo:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = LMiterJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: LMiterJoint"
+ self.AddRuntimeMessage(Warning, "LMiterJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_L, LMiterJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ supported_topo = self.joint_type.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_L not in supported_topo:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_L, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ -
+ 456
+ 456
+
+ -
+ 1826
+ 1208
+
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAANBJREFUSEvtk70NwjAQhTNCBrDkk7wAo9DQMwIjMIHFCIyQEVK4TMEIjMAGRif5JOvlcEwwiCLFV8Q/75PeOV2MsfsmswXGWRqdpZi44P475KHkLPXO0jkLF054sRYJ3yuhGndnaYchJUTAFzHsFQOGlBBB3jlzTXVxbTfYGzGkhAgGCOnlAFfSQoCDbS7gSvIQ/uZ6mlWEMyixasi1z/Sx6pkmCXetVcIc8aLxYTI+ROPDAfdUQQ7MRP2LPxLUsAkW2QSL/FKgMf2/oJbZQmue50vIddCJVREAAAAASUVORK5CYII=
+
+ - false
+ - ef35e456-a7dc-48b3-b82d-b710f70fc8bb
+ - true
+ - true
+ - CT: L Topological Joint Rules
+ - L_Topo_Joint
+
+
+
+
+ -
+ 1338
+ 1644
+ 166
+ 44
+
+ -
+ 1432
+ 1666
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - flip_lap_side
+ - f8c7172a-482a-44b2-a271-28dd581abec2
+ - flip_lap_side
+ - flip_lap_side
+ - true
+ - 78604bce-20bd-4b08-9cdd-ce914039a739
+ - 1
+
+
+
+
+ -
+ 1340
+ 1646
+ 77
+ 20
+
+ -
+ 1380
+ 1656
+
+
+
+
+
+
+
+ - Script input cut_plane_bias.
+ - e6d6fabc-c329-41ae-b3f0-405db9ad2379
+ - cut_plane_bias
+ - cut_plane_bias
+ - true
+ - 0
+
+
+
+
+ -
+ 1340
+ 1666
+ 77
+ 20
+
+ -
+ 1380
+ 1676
+
+
+
+
+
+
+
+ - Script output LLapJoint.
+ - 51ae2a95-d349-4d07-9cd2-c6bc6a9d3544
+ - LLapJoint
+ - LLapJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1447
+ 1646
+ 55
+ 40
+
+ -
+ 1474.5
+ 1666
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: T Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import TButtJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class T_TopologyJointRule(component):
+ def __init__(self):
+ super(T_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ supported_topo = cls.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_T in supported_topo:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = TButtJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: TButtJoint"
+ self.AddRuntimeMessage(Warning, "TButtJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_T, TButtJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ supported_topo = self.joint_type.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_T not in supported_topo:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_T, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAANVJREFUSEvtk70NwjAQhTNC6CP5JC/AKIzACHS0TGAxQmqqjJDCJQUjMAIbGJ3kk8yzsePIFEgpviL+eZ/0zumcc90viRYYrWjWipznivs1hKGkFfVa0SUIF054cSkSfkiEpnhqRXsMySECvohh35gwJIcIws6Z0dfFtT1gb8aQHCKYIKSXA1xJCwEOtrmAKwlD+JvraVYRziDHqiEvfaavVc/US7jrVCXMUc4Nxu4GY12BWyQIgZl8/MVNBDUMxt596Bn3mGihlk1QZBMU+X9BiWihNW9f8MWtv55wzQAAAABJRU5ErkJggg==
+
+ - false
+ - ec8833a5-9777-4c3d-be2d-7255ad159bae
+ - true
+ - true
+ - CT: T Topological Joint Rules
+ - T_Topo_Joint
+
+
+
+
+ -
+ 1348
+ 1757
+ 167
+ 44
+
+ -
+ 1442
+ 1779
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - flip_lap_side
+ - 1f179e6f-7230-4012-869a-62abf9a15ab2
+ - flip_lap_side
+ - flip_lap_side
+ - true
+ - 78604bce-20bd-4b08-9cdd-ce914039a739
+ - 1
+
+
+
+
+ -
+ 1350
+ 1759
+ 77
+ 20
+
+ -
+ 1390
+ 1769
+
+
+
+
+
+
+
+ - Script input cut_plane_bias.
+ - 4e714825-0a92-461c-beb9-8768403d1bc8
+ - cut_plane_bias
+ - cut_plane_bias
+ - true
+ - e759cdbb-486d-4827-8fb6-1ddc9a863872
+ - 1
+
+
+
+
+ -
+ 1350
+ 1779
+ 77
+ 20
+
+ -
+ 1390
+ 1789
+
+
+
+
+
+
+
+ - Script output TLapJoint.
+ - 53e57ab2-3b0f-4b55-bec0-7f58bf6e923c
+ - TLapJoint
+ - TLapJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1457
+ 1759
+ 56
+ 40
+
+ -
+ 1485
+ 1779
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: X Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import XLapJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class X_TopologyJointRule(component):
+ def __init__(self):
+ super(X_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ supported_topo = cls.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_X in supported_topo:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = XLapJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: XLapJoint"
+ self.AddRuntimeMessage(Warning, "XLapJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_X, XLapJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ supported_topo = self.joint_type.SUPPORTED_TOPOLOGY
+ if not isinstance(supported_topo, list):
+ supported_topo = [supported_topo]
+ if JointTopology.TOPO_X not in supported_topo:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_X, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ -
+ 494
+ 494
+
+ -
+ 835
+ 874
+
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAAUBJREFUSEu1kyFuAzEQRXOEhFuyFV8gUq7QA5SEFxWXhQYGWaVlxUXhIQGGBb1ApJyg6g1cfWlmNZpxtt7EBU+7Gs/8r/3jnZVSZv+JKYDowyn6UIhXfT4FKRqiD/Pow06IMy96sBUWf6yI1rhEH1ZaZAw2wKAWu8ZBi4zBBjJz8E5xIbYvdXbSImOwwUGJzLkBkfQw0IvtboBIpAi+CPF0i0jvYIybltx6TX9uuqZkgqxrkYAn7nMpP7uUC7ER9U2tbhzJTO7E/MUu5SMJnV3KCwLvqH3IXiPeAgl+k+AbgXfUFncbAJfyVkTCbHWfGZyCS/lTiB/1OTCFKbiU98Jgr8+BKbTiUl5XIlrrPjPYiogHz+Fd95nBFtSC8SUP1xZthv/CpbwUV3TIXV3V5T0GHMe5csY/23CjjEBvTKE3vzBOrcjlhJXzAAAAAElFTkSuQmCC
+
+ - false
+ - 280b9454-d1a2-4ad3-aad6-af62e475b7ef
+ - true
+ - true
+ - CT: X Topological Joint Rules
+ - X_Topo_Joint
+
+
+
+
+ -
+ 1394
+ 1852
+ 168
+ 44
+
+ -
+ 1488
+ 1874
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - flip_lap_side
+ - 0225dff4-43d0-47ac-a7d1-b942d29fe8bb
+ - flip_lap_side
+ - flip_lap_side
+ - true
+ - 78604bce-20bd-4b08-9cdd-ce914039a739
+ - 1
+
+
+
+
+ -
+ 1396
+ 1854
+ 77
+ 20
+
+ -
+ 1436
+ 1864
+
+
+
+
+
+
+
+ - Script input cut_plane_bias.
+ - 03afd280-26e1-44cb-a284-ae00b75e6859
+ - cut_plane_bias
+ - cut_plane_bias
+ - true
+ - e759cdbb-486d-4827-8fb6-1ddc9a863872
+ - 1
+
+
+
+
+ -
+ 1396
+ 1874
+ 77
+ 20
+
+ -
+ 1436
+ 1884
+
+
+
+
+
+
+
+ - Script output XLapJoint.
+ - 9117a2a2-1d2b-4cc7-ad6d-4872f949c9f0
+ - XLapJoint
+ - XLapJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1503
+ 1854
+ 57
+ 40
+
+ -
+ 1531.5
+ 1874
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 3cadddef-1e2b-4c09-9390-0e8f78f7609f
+ - Merge
+
+
+
+
+ - Merge a bunch of data streams
+ - 86edab8b-8fe6-404f-8e9d-a8534911d7d6
+ - Merge
+ - Merge
+
+
+
+
+ -
+ 1666
+ 1770
+ 72
+ 84
+
+ -
+ 1704
+ 1812
+
+
+
+
+
+ - 4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 2
+ - Data stream 1
+ - 65317895-314f-4fbf-bdca-9a5fc9b7ff95
+ - false
+ - Data 1
+ - D1
+ - true
+ - 9117a2a2-1d2b-4cc7-ad6d-4872f949c9f0
+ - 1
+
+
+
+
+ -
+ 1668
+ 1772
+ 21
+ 20
+
+ -
+ 1680
+ 1782
+
+
+
+
+
+
+
+ - 2
+ - Data stream 2
+ - 4b79f66f-2b38-425f-9781-f3d34adb0a3d
+ - false
+ - Data 2
+ - D2
+ - true
+ - 53e57ab2-3b0f-4b55-bec0-7f58bf6e923c
+ - 1
+
+
+
+
+ -
+ 1668
+ 1792
+ 21
+ 20
+
+ -
+ 1680
+ 1802
+
+
+
+
+
+
+
+ - 2
+ - Data stream 3
+ - 700bc5fa-2a1d-44ac-81b7-88bef1fbeb2a
+ - false
+ - Data 3
+ - D3
+ - true
+ - 51ae2a95-d349-4d07-9cd2-c6bc6a9d3544
+ - 1
+
+
+
+
+ -
+ 1668
+ 1812
+ 21
+ 20
+
+ -
+ 1680
+ 1822
+
+
+
+
+
+
+
+ - 2
+ - Data stream 4
+ - 5b84ee25-69fb-419f-bf2c-70556d0fc3b3
+ - false
+ - Data 4
+ - D4
+ - true
+ - 0
+
+
+
+
+ -
+ 1668
+ 1832
+ 21
+ 20
+
+ -
+ 1680
+ 1842
+
+
+
+
+
+
+
+ - 2
+ - Result of merge
+ - debfa811-a60a-402d-a198-bd648e1658ee
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ 1719
+ 1772
+ 17
+ 80
+
+ -
+ 1727.5
+ 1812
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 3cadddef-1e2b-4c09-9390-0e8f78f7609f
+ - Merge
+
+
+
+
+ - Merge a bunch of data streams
+ - bd2b9a40-f16e-4ed9-ae3c-f89ef94e847f
+ - Merge
+ - Merge
+
+
+
+
+ -
+ 1294
+ 1467
+ 72
+ 64
+
+ -
+ 1332
+ 1499
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 2
+ - Data stream 1
+ - e77f140c-abc3-441e-aa6a-42ae5863807a
+ - false
+ - Data 1
+ - D1
+ - true
+ - d2365ef0-f85c-4709-9361-522790ff3ed5
+ - 1
+
+
+
+
+ -
+ 1296
+ 1469
+ 21
+ 20
+
+ -
+ 1308
+ 1479
+
+
+
+
+
+
+
+ - 2
+ - Data stream 2
+ - 42a09df3-4b16-4c1d-923e-5b185de638fb
+ - false
+ - Data 2
+ - D2
+ - true
+ - 287d3ad7-dfd0-4415-b5a0-ff41819a7aba
+ - 1
+
+
+
+
+ -
+ 1296
+ 1489
+ 21
+ 20
+
+ -
+ 1308
+ 1499
+
+
+
+
+
+
+
+ - 2
+ - Data stream 3
+ - 6b7420b5-71f0-454a-a83d-e060fbc0a2b3
+ - false
+ - Data 3
+ - D3
+ - true
+ - 0
+
+
+
+
+ -
+ 1296
+ 1509
+ 21
+ 20
+
+ -
+ 1308
+ 1519
+
+
+
+
+
+
+
+ - 2
+ - Result of merge
+ - bf762047-bfb8-4813-a506-3cf4fb2c17bc
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ 1347
+ 1469
+ 17
+ 60
+
+ -
+ 1355.5
+ 1499
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - de54e310-969d-4c72-ba7d-ecaf11317449
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ 258
+ 1460
+ 50
+ 24
+
+ -
+ 283.5297
+ 1472.018
+
+
+
+
+
+ - 1
+
+
+
+
+ - 3
+ - {0}
+
+
+
+
+ - -1
+ - 81b39e2c-86d1-49cd-bb21-ca528b17dd02
+
+
+
+
+ - -1
+ - 42711433-1a94-4a41-9af9-65953b894aeb
+
+
+
+
+ - -1
+ - a6c72b9c-b92c-4bdc-aec2-d4a5e8a42c1e
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - 0bc55f85-874d-476f-b20f-3bb2f9cb267a
+ - List Item
+ - Item
+
+
+
+
+ -
+ 338
+ 1441
+ 74
+ 64
+
+ -
+ 372
+ 1473
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - 3fe5eb37-2607-4061-9cf6-a1904015e985
+ - List
+ - L
+ - false
+ - de54e310-969d-4c72-ba7d-ecaf11317449
+ - 1
+
+
+
+
+ -
+ 340
+ 1443
+ 17
+ 20
+
+ -
+ 350
+ 1453
+
+
+
+
+
+
+
+ - Item index
+ - 8e8f3ff1-c74a-4c16-bde6-aeaab9f5b9c8
+ - Index
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 340
+ 1463
+ 17
+ 20
+
+ -
+ 350
+ 1473
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - e76ba91a-1cf3-4fe3-afbd-315559ed2bc1
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 340
+ 1483
+ 17
+ 20
+
+ -
+ 350
+ 1493
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - 73f83a29-896b-4021-bc42-1f65db517ae2
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 387
+ 1443
+ 23
+ 20
+
+ -
+ 398.5
+ 1453
+
+
+
+
+
+
+
+ - Item at {+1'}
+ - bd53ef51-7947-47ac-ad07-27b174cf4ead
+ - false
+ - Item +1
+ - +1
+ - false
+ - 0
+
+
+
+
+ -
+ 387
+ 1463
+ 23
+ 20
+
+ -
+ 398.5
+ 1473
+
+
+
+
+
+
+
+ - Item at {+2'}
+ - 0f1eae77-2136-460f-9f03-f19b3c29f7b8
+ - false
+ - Item +2
+ - +2
+ - false
+ - 0
+
+
+
+
+ -
+ 387
+ 1483
+ 23
+ 20
+
+ -
+ 398.5
+ 1493
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 57da07bd-ecab-415d-9d86-af36d7073abc
+ - Number Slider
+
+
+
+
+ - Numeric slider for single values
+ - e759cdbb-486d-4827-8fb6-1ddc9a863872
+ - Number Slider
+
+ - false
+ - 0
+
+
+
+
+ -
+ 1045
+ 1790
+ 203
+ 20
+
+ -
+ 1045.411
+ 1790.174
+
+
+
+
+
+ - 1
+ - 1
+ - 0
+ - 1
+ - 0
+ - 0
+ - 0.6
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 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
+
+
+
+
+
diff --git a/examples/Grasshopper/tests/test_lap.ghx b/examples/Grasshopper/tests/test_lap.ghx
index 3b9e80d9f..836c1b9a5 100644
--- a/examples/Grasshopper/tests/test_lap.ghx
+++ b/examples/Grasshopper/tests/test_lap.ghx
@@ -49,10 +49,10 @@
-
- -3280
- -2238
+ -718
+ -919
- - 1.76470578
+ - 1.2750001
@@ -69,9 +69,9 @@
- - 1
+ - 2
-
+
- GhPython, Version=7.37.24107.15001, Culture=neutral, PublicKeyToken=null
@@ -82,13 +82,23 @@
+
+
+ - Pufferfish, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null
+ - 3.0.0.0
+ - Michael Pryor
+ - 1c9de8a1-315f-4c56-af06-8f69fee80a7a
+ - Pufferfish
+ - 3.0.0.0
+
+
- - 74
+ - 94
-
+
- c552a431-af5b-46a9-a8a4-0fcbc27ef596
@@ -175,13 +185,13 @@
-
- 843
+ 842
1368
166
20
-
- 843.639
+ 842.9432
1368.29
@@ -221,13 +231,13 @@
-
- 86
+ 85
1307
50
24
-
- 111.0335
+ 110.3377
1319.804
@@ -279,13 +289,13 @@
-
- 870
+ 869
1583
166
20
-
- 870.4417
+ 869.7459
1583.228
@@ -324,13 +334,13 @@
-
- 870
+ 869
1607
166
20
-
- 870.251
+ 869.5552
1607.486
@@ -423,13 +433,13 @@
-
- 845
+ 844
1343
166
20
-
- 845.1333
+ 844.4375
1343.346
@@ -442,7 +452,7 @@
- 100
- 0
- 0
- - 100
+ - 60
@@ -457,7 +467,7 @@
-
+
- """Creates a Beam from a LineCurve."""
import rhinoscriptsyntax as rs
@@ -578,6 +588,7 @@ class Beam_fromCurve(component):
835
874
+ - true
- true
- true
-
@@ -594,14 +605,14 @@ class Beam_fromCurve(component):
-
- 1056
- 1305
+ 1055
+ 1307
145
124
-
- 1147
- 1367
+ 1146
+ 1369
@@ -638,14 +649,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1307
+ 1057
+ 1309
74
20
-
- 1096.5
- 1317
+ 1095.5
+ 1319
@@ -669,14 +680,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1327
+ 1057
+ 1329
74
20
-
- 1096.5
- 1337
+ 1095.5
+ 1339
@@ -701,14 +712,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1347
+ 1057
+ 1349
74
20
-
- 1096.5
- 1357
+ 1095.5
+ 1359
@@ -733,14 +744,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1367
+ 1057
+ 1369
74
20
-
- 1096.5
- 1377
+ 1095.5
+ 1379
@@ -764,14 +775,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1387
+ 1057
+ 1389
74
20
-
- 1096.5
- 1397
+ 1095.5
+ 1399
@@ -794,14 +805,14 @@ class Beam_fromCurve(component):
-
- 1058
- 1407
+ 1057
+ 1409
74
20
-
- 1096.5
- 1417
+ 1095.5
+ 1419
@@ -820,14 +831,14 @@ class Beam_fromCurve(component):
-
- 1162
- 1307
+ 1161
+ 1309
37
60
-
- 1180.5
- 1337
+ 1179.5
+ 1339
@@ -846,14 +857,14 @@ class Beam_fromCurve(component):
-
- 1162
- 1367
+ 1161
+ 1369
37
60
-
- 1180.5
- 1397
+ 1179.5
+ 1399
@@ -873,7 +884,7 @@ class Beam_fromCurve(component):
-
+
- """Creates a Beam from a LineCurve."""
import rhinoscriptsyntax as rs
@@ -994,6 +1005,7 @@ class Beam_fromCurve(component):
835
874
+ - true
- true
- true
-
@@ -1010,14 +1022,14 @@ class Beam_fromCurve(component):
-
- 1052
- 1542
+ 1051
+ 1544
145
124
-
- 1143
- 1604
+ 1142
+ 1606
@@ -1046,7 +1058,7 @@ class Beam_fromCurve(component):
- true
- 1
- true
- - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 5e8cc817-6357-4d57-8f5c-bb9d6ce9669c
- 1
- 87f87f55-5b71-41f4-8aea-21d494016f81
@@ -1054,14 +1066,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1544
+ 1053
+ 1546
74
20
-
- 1092.5
- 1554
+ 1091.5
+ 1556
@@ -1085,14 +1097,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1564
+ 1053
+ 1566
74
20
-
- 1092.5
- 1574
+ 1091.5
+ 1576
@@ -1117,14 +1129,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1584
+ 1053
+ 1586
74
20
-
- 1092.5
- 1594
+ 1091.5
+ 1596
@@ -1149,14 +1161,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1604
+ 1053
+ 1606
74
20
-
- 1092.5
- 1614
+ 1091.5
+ 1616
@@ -1180,14 +1192,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1624
+ 1053
+ 1626
74
20
-
- 1092.5
- 1634
+ 1091.5
+ 1636
@@ -1210,14 +1222,14 @@ class Beam_fromCurve(component):
-
- 1054
- 1644
+ 1053
+ 1646
74
20
-
- 1092.5
- 1654
+ 1091.5
+ 1656
@@ -1236,14 +1248,14 @@ class Beam_fromCurve(component):
-
- 1158
- 1544
+ 1157
+ 1546
37
60
-
- 1176.5
- 1574
+ 1175.5
+ 1576
@@ -1262,14 +1274,14 @@ class Beam_fromCurve(component):
-
- 1158
- 1604
+ 1157
+ 1606
37
60
-
- 1176.5
- 1634
+ 1175.5
+ 1636
@@ -1289,11 +1301,11 @@ class Beam_fromCurve(component):
-
+
- from compas_timber._fabrication import Lap
from compas_timber._fabrication import JackRafterCut
from compas_timber.connections.utilities import beam_ref_side_incidence
-from compas_rhino.conversions import frame_to_rhino, brep_to_rhino, plane_to_rhino
+from compas_rhino.conversions import frame_to_rhino, brep_to_rhino, plane_to_rhino, box_to_rhino
from compas_rhino import unload_modules
unload_modules('compas_timber')
@@ -1307,9 +1319,11 @@ main_ref_side_index = min(main_ref_side_dict, key=main_ref_side_dict.get)
main_ref_side_indices = main_ref_side_index, (main_ref_side_index+2)%4
main_plane = main_beam.ref_sides[main_ref_side_index]
lap_width = main_beam.height if main_ref_side_index % 2 == 0 else main_beam.width
+print(lap_width)
# instanciate lap processing from plane and beam
lap = Lap.from_plane_and_beam(main_plane, cross_beam, lap_width, depth, cross_ref_side_index)
+lap_frame = lap.planes_from_params_and_beam(cross_beam)
volume = lap.volume_from_params_and_beam(cross_beam)
cross_geo = cross_beam.compute_geometry()
cross_geo = lap.apply(cross_geo, cross_beam)
@@ -1324,7 +1338,7 @@ jack_cut = JackRafterCut.from_plane_and_beam(cross_plane, main_beam, main_ref_si
cutting_plane_main = jack_cut.plane_from_params_and_beam(main_beam)
main_geo = main_beam.compute_geometry()
main_geo = jack_cut.apply(main_geo, main_beam)
-
+lap_frame = [plane_to_rhino(frame) for frame in lap_frame]
# viz frames
cross_ref_side = frame_to_rhino(cross_beam.ref_sides[cross_ref_side_index])
@@ -1334,16 +1348,17 @@ cutting_plane_main = plane_to_rhino(cutting_plane_main)
# viz volumes
rg_cross = brep_to_rhino(cross_geo)
rg_main = brep_to_rhino(main_geo)
-#rg_volume = brep_to_rhino(volume)
+rg_volume = brep_to_rhino(volume)
- GhPython provides a Python script component
-
- 2335
- 49
+ 1181
+ 104
-
1298
1537
+ - true
- true
- false
- false
@@ -1358,23 +1373,23 @@ rg_main = brep_to_rhino(main_geo)
-
1742
- 1134
+ 1123
202
- 144
+ 164
-
1823
- 1206
+ 1205
-
+
- 3
- 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
- 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
- 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2
- - 7
+ - 8
- 3ede854e-c753-40eb-84cb-b48008f14fd4
- 8ec86459-bf01-4409-baee-174d0d2b13d0
- 8ec86459-bf01-4409-baee-174d0d2b13d0
@@ -1382,8 +1397,9 @@ rg_main = brep_to_rhino(main_geo)
- 8ec86459-bf01-4409-baee-174d0d2b13d0
- 8ec86459-bf01-4409-baee-174d0d2b13d0
- 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
-
+
- true
@@ -1403,13 +1419,13 @@ rg_main = brep_to_rhino(main_geo)
-
1744
- 1136
+ 1125
64
- 46
+ 53
-
1777.5
- 1159.333
+ 1151.667
@@ -1434,13 +1450,13 @@ rg_main = brep_to_rhino(main_geo)
-
1744
- 1182
+ 1178
64
- 47
+ 53
-
1777.5
- 1206
+ 1205
@@ -1465,13 +1481,13 @@ rg_main = brep_to_rhino(main_geo)
-
1744
- 1229
+ 1231
64
- 47
+ 54
-
1777.5
- 1252.667
+ 1258.333
@@ -1491,13 +1507,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1136
+ 1125
104
20
-
1890
- 1146
+ 1135
@@ -1517,13 +1533,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1156
+ 1145
104
20
-
1890
- 1166
+ 1155
@@ -1543,13 +1559,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1176
+ 1165
104
20
-
1890
- 1186
+ 1175
@@ -1569,13 +1585,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1196
+ 1185
104
20
-
1890
- 1206
+ 1195
@@ -1595,13 +1611,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1216
+ 1205
104
20
-
1890
- 1226
+ 1215
@@ -1621,13 +1637,13 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1236
+ 1225
104
20
-
1890
- 1246
+ 1235
@@ -1647,13 +1663,39 @@ rg_main = brep_to_rhino(main_geo)
-
1838
- 1256
+ 1245
+ 104
+ 20
+
+ -
+ 1890
+ 1255
+
+
+
+
+
+
+
+ - Script output lap_frame.
+ - 502370ce-eac6-4444-b153-000dda5f7ad2
+ - lap_frame
+ - lap_frame
+ - false
+ - 0
+
+
+
+
+ -
+ 1838
+ 1265
104
20
-
1890
- 1266
+ 1275
@@ -1672,11 +1714,10 @@ rg_main = brep_to_rhino(main_geo)
-
+
- Contains a collection of Breps (Boundary REPresentations)
- true
- 9381ae59-064b-40ba-81bc-88ceddf37ae8
- - true
- Brep
- Brep
- false
@@ -1687,13 +1728,13 @@ rg_main = brep_to_rhino(main_geo)
-
- 1985
+ 1984
1201
50
24
-
- 2010.016
+ 2009.32
1213.15
@@ -1710,7 +1751,7 @@ rg_main = brep_to_rhino(main_geo)
-
+
- from compas.scene import Scene
from compas.tolerance import TOL
from ghpythonlib.componentbase import executingcomponent as component
@@ -1913,6 +1954,7 @@ class ModelComponent(component):
835
1486
+ - true
- true
- true
-
@@ -2219,7 +2261,7 @@ class ModelComponent(component):
-
- 1698
+ 1697
1554
104
22
@@ -2458,7 +2500,7 @@ class WriteBTLx(component):
-
- 1968
+ 1967
1617
258
116
@@ -2467,7 +2509,7 @@ class WriteBTLx(component):
- 0
- 0
-
- 1968.6
+ 1967.904
1617.126
@@ -2509,7 +2551,7 @@ class WriteBTLx(component):
-
- 2122
+ 2121
1741
104
22
@@ -2538,13 +2580,13 @@ class WriteBTLx(component):
-
- 633
+ 632
1438
66
64
-
- 665
+ 664
1470
@@ -2563,13 +2605,13 @@ class WriteBTLx(component):
-
- 635
+ 634
1440
15
20
-
- 644
+ 643
1450
@@ -2590,13 +2632,13 @@ class WriteBTLx(component):
-
- 635
+ 634
1460
15
20
-
- 644
+ 643
1470
@@ -2616,13 +2658,13 @@ class WriteBTLx(component):
-
- 635
+ 634
1480
15
20
-
- 644
+ 643
1490
@@ -2662,13 +2704,13 @@ class WriteBTLx(component):
-
- 680
+ 679
1440
17
30
-
- 688.5
+ 687.5
1455
@@ -2688,13 +2730,13 @@ class WriteBTLx(component):
-
- 680
+ 679
1470
17
30
-
- 688.5
+ 687.5
1485
@@ -2724,13 +2766,13 @@ class WriteBTLx(component):
-
- 1341
+ 1340
1436
- 192
+ 184
20
-
- 1341.447
+ 1340.726
1436.466
@@ -2743,7 +2785,7 @@ class WriteBTLx(component):
- 100
- 0
- 0
- - 10
+ - 20
@@ -2817,13 +2859,13 @@ class WriteBTLx(component):
-
- 723
+ 722
1423
61
44
-
- 770
+ 769
1445
@@ -2842,13 +2884,13 @@ class WriteBTLx(component):
-
- 725
+ 724
1425
30
20
-
- 749.5
+ 748.5
1435
@@ -2870,13 +2912,13 @@ class WriteBTLx(component):
-
- 725
+ 724
1445
30
20
-
- 749.5
+ 748.5
1455
@@ -2905,13 +2947,13 @@ class WriteBTLx(component):
-
- 632
+ 631
1371
66
64
-
- 663
+ 662
1403
@@ -2930,13 +2972,13 @@ class WriteBTLx(component):
-
- 634
+ 633
1373
14
30
-
- 642.5
+ 641.5
1388
@@ -2957,13 +2999,13 @@ class WriteBTLx(component):
-
- 634
+ 633
1403
14
30
-
- 642.5
+ 641.5
1418
@@ -2983,13 +3025,13 @@ class WriteBTLx(component):
-
- 678
+ 677
1373
18
20
-
- 687
+ 686
1383
@@ -3009,13 +3051,13 @@ class WriteBTLx(component):
-
- 678
+ 677
1393
18
20
-
- 687
+ 686
1403
@@ -3035,13 +3077,13 @@ class WriteBTLx(component):
-
- 678
+ 677
1413
18
20
-
- 687
+ 686
1423
@@ -3061,8 +3103,8 @@ class WriteBTLx(component):
- Contains a collection of three-dimensional axis-systems
+ - true
- 00dffbd2-d634-4815-8e48-989dfd6df288
- - true
- Plane
- Pln
- false
@@ -3073,13 +3115,13 @@ class WriteBTLx(component):
-
- 1999
+ 1998
1175
50
24
-
- 2024.303
+ 2023.607
1187.121
@@ -3095,11 +3137,10 @@ class WriteBTLx(component):
-
+
- Contains a collection of three-dimensional axis-systems
- true
- 4d439952-5ee0-415f-bf9a-9558d86e4efd
- - true
- Plane
- Pln
- false
@@ -3110,13 +3151,13 @@ class WriteBTLx(component):
-
- 1988
+ 1987
1139
50
24
-
- 2013.117
+ 2012.421
1151.553
@@ -3132,11 +3173,10 @@ class WriteBTLx(component):
-
+
- Contains a collection of Breps (Boundary REPresentations)
- true
- a6425828-9538-4783-86e1-0c73c651f302
- - true
- Brep
- Brep
- false
@@ -3147,13 +3187,13 @@ class WriteBTLx(component):
-
- 1982
+ 1981
1229
50
24
-
- 2007.312
+ 2006.616
1241.926
@@ -3183,13 +3223,13 @@ class WriteBTLx(component):
-
- 1990
+ 1989
1288
50
24
-
- 2015.435
+ 2014.739
1300.79
@@ -3205,11 +3245,10 @@ class WriteBTLx(component):
-
+
- Contains a collection of three-dimensional axis-systems
- true
- 9f99b4bb-612a-492f-b8e1-a954c0e0a960
- - true
- Plane
- Pln
- false
@@ -3220,13 +3259,13 @@ class WriteBTLx(component):
-
- 1990
+ 1989
1261
50
24
-
- 2015.403
+ 2014.707
1273.677
@@ -3253,13 +3292,13 @@ class WriteBTLx(component):
-
- -128
+ -129
1505
64
44
-
- -97
+ -98
1527
@@ -3278,13 +3317,13 @@ class WriteBTLx(component):
-
- -126
+ -127
1507
14
40
-
- -117.5
+ -118.5
1527
@@ -3304,13 +3343,13 @@ class WriteBTLx(component):
-
- -82
+ -83
1507
16
20
-
- -74
+ -75
1517
@@ -3330,13 +3369,13 @@ class WriteBTLx(component):
-
- -82
+ -83
1527
16
20
-
- -74
+ -75
1537
@@ -3365,13 +3404,13 @@ class WriteBTLx(component):
-
- -42
+ -43
1552
83
44
-
- 6
+ 5
1574
@@ -3390,13 +3429,13 @@ class WriteBTLx(component):
-
- -40
+ -41
1554
31
20
-
- -15
+ -16
1564
@@ -3418,13 +3457,13 @@ class WriteBTLx(component):
-
- -40
+ -41
1574
31
20
-
- -15
+ -16
1584
@@ -3468,13 +3507,13 @@ class WriteBTLx(component):
-
- 21
+ 20
1554
18
20
-
- 30
+ 29
1564
@@ -3494,13 +3533,13 @@ class WriteBTLx(component):
-
- 21
+ 20
1574
18
20
-
- 30
+ 29
1584
@@ -3529,21 +3568,22 @@ class WriteBTLx(component):
-
- -214
+ -231
1550
- 63
+ 79
28
-
- -185
+ -186
1564
-
+
- Unit multiplication
- a7304964-d322-488c-865b-62f9088e559b
+ - -x
- Factor
- F
- false
@@ -3554,13 +3594,13 @@ class WriteBTLx(component):
-
- -212
+ -229
1552
- 12
+ 28
24
-
- -204.5
+ -205.5
1564
@@ -3600,13 +3640,13 @@ class WriteBTLx(component):
-
- -170
+ -171
1552
17
24
-
- -161.5
+ -162.5
1564
@@ -3642,7 +3682,7 @@ class WriteBTLx(component):
20
-
- -399.0349
+ -399.7307
1558.41
@@ -3655,7 +3695,7 @@ class WriteBTLx(component):
- 4000
- 0
- 0
- - 2000
+ - 1000
@@ -3680,13 +3720,13 @@ class WriteBTLx(component):
-
- 66
+ 65
1532
63
44
-
- 97
+ 96
1554
@@ -3705,13 +3745,13 @@ class WriteBTLx(component):
-
- 68
+ 67
1534
14
20
-
- 76.5
+ 75.5
1544
@@ -3732,13 +3772,13 @@ class WriteBTLx(component):
-
- 68
+ 67
1554
14
20
-
- 76.5
+ 75.5
1564
@@ -3758,13 +3798,13 @@ class WriteBTLx(component):
-
- 112
+ 111
1534
15
40
-
- 119.5
+ 118.5
1554
@@ -3793,21 +3833,22 @@ class WriteBTLx(component):
-
- -212
+ -229
1584
- 63
+ 79
28
-
- -183
+ -184
1598
-
+
- Unit multiplication
- 827ec58a-dd71-44e4-b991-26acf25b63f9
+ - -x
- Factor
- F
- false
@@ -3818,13 +3859,13 @@ class WriteBTLx(component):
-
- -210
+ -227
1586
- 12
+ 28
24
-
- -202.5
+ -203.5
1598
@@ -3864,13 +3905,13 @@ class WriteBTLx(component):
-
- -168
+ -169
1586
17
24
-
- -159.5
+ -160.5
1598
@@ -3900,13 +3941,13 @@ class WriteBTLx(component):
-
- -401
+ -402
1593
163
20
-
- -400.5624
+ -401.2582
1593.316
@@ -3919,7 +3960,7 @@ class WriteBTLx(component):
- 2000
- 0
- 0
- - 2000
+ - 0
@@ -3944,13 +3985,13 @@ class WriteBTLx(component):
-
- -126
+ -127
1552
65
64
-
- -95
+ -96
1584
@@ -3979,13 +4020,13 @@ class WriteBTLx(component):
-
- -124
+ -125
1554
14
20
-
- -115.5
+ -116.5
1564
@@ -4006,13 +4047,13 @@ class WriteBTLx(component):
-
- -124
+ -125
1574
14
20
-
- -115.5
+ -116.5
1584
@@ -4033,13 +4074,13 @@ class WriteBTLx(component):
-
- -124
+ -125
1594
14
20
-
- -115.5
+ -116.5
1604
@@ -4059,13 +4100,13 @@ class WriteBTLx(component):
-
- -80
+ -81
1554
17
60
-
- -71.5
+ -72.5
1584
@@ -4126,13 +4167,13 @@ class WriteBTLx(component):
-
- -227
+ -228
1618
79
28
-
- -182
+ -183
1632
@@ -4152,13 +4193,13 @@ class WriteBTLx(component):
-
- -225
+ -226
1620
28
24
-
- -201.5
+ -202.5
1632
@@ -4198,13 +4239,13 @@ class WriteBTLx(component):
-
- -167
+ -168
1620
17
24
-
- -158.5
+ -159.5
1632
@@ -4234,13 +4275,13 @@ class WriteBTLx(component):
-
- -395
+ -396
1626
163
20
-
- -394.9014
+ -395.5972
1626.688
@@ -4286,7 +4327,7 @@ class WriteBTLx(component):
24
-
- -176.2584
+ -176.9542
1527.093
@@ -4337,13 +4378,13 @@ class WriteBTLx(component):
-
- 245
+ 244
1522
77
64
-
- 277
+ 276
1554
@@ -4372,13 +4413,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1524
15
20
-
- 256
+ 255
1534
@@ -4421,13 +4462,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1544
15
20
-
- 256
+ 255
1554
@@ -4450,13 +4491,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1564
15
20
-
- 256
+ 255
1574
@@ -4478,13 +4519,13 @@ class WriteBTLx(component):
-
- 292
+ 291
1524
28
60
-
- 306
+ 305
1554
@@ -4515,13 +4556,13 @@ class WriteBTLx(component):
-
- 157
+ 156
1562
66
44
-
- 189
+ 188
1584
@@ -4540,13 +4581,13 @@ class WriteBTLx(component):
-
- 159
+ 158
1564
15
20
-
- 168
+ 167
1574
@@ -4566,13 +4607,13 @@ class WriteBTLx(component):
-
- 159
+ 158
1584
15
20
-
- 168
+ 167
1594
@@ -4592,13 +4633,13 @@ class WriteBTLx(component):
-
- 204
+ 203
1564
17
20
-
- 212.5
+ 211.5
1574
@@ -4618,13 +4659,13 @@ class WriteBTLx(component):
-
- 204
+ 203
1584
17
20
-
- 212.5
+ 211.5
1594
@@ -4655,7 +4696,7 @@ class WriteBTLx(component):
-
- -229
+ -230
1442
104
22
@@ -4712,14 +4753,14 @@ class WriteBTLx(component):
-
- 481
- 1542
+ 472
+ 1545
50
24
-
- 506.3529
- 1554.078
+ 497.7238
+ 1557.053
@@ -4745,13 +4786,13 @@ class WriteBTLx(component):
-
- 245
+ 244
1287
77
64
-
- 277
+ 276
1319
@@ -4780,13 +4821,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1289
15
20
-
- 256
+ 255
1299
@@ -4829,13 +4870,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1309
15
20
-
- 256
+ 255
1319
@@ -4858,13 +4899,13 @@ class WriteBTLx(component):
-
- 247
+ 246
1329
15
20
-
- 256
+ 255
1339
@@ -4886,13 +4927,13 @@ class WriteBTLx(component):
-
- 292
+ 291
1289
28
60
-
- 306
+ 305
1319
@@ -4923,13 +4964,13 @@ class WriteBTLx(component):
-
- 167
+ 166
1327
66
44
-
- 199
+ 198
1349
@@ -4948,13 +4989,13 @@ class WriteBTLx(component):
-
- 169
+ 168
1329
15
20
-
- 178
+ 177
1339
@@ -4974,13 +5015,13 @@ class WriteBTLx(component):
-
- 169
+ 168
1349
15
20
-
- 178
+ 177
1359
@@ -5000,13 +5041,13 @@ class WriteBTLx(component):
-
- 214
+ 213
1329
17
20
-
- 222.5
+ 221.5
1339
@@ -5026,13 +5067,13 @@ class WriteBTLx(component):
-
- 214
+ 213
1349
17
20
-
- 222.5
+ 221.5
1359
@@ -5063,7 +5104,7 @@ class WriteBTLx(component):
-
- 31
+ 30
1236
104
22
@@ -5126,7 +5167,7 @@ class WriteBTLx(component):
24
-
- 503.8039
+ 503.1081
1319.313
@@ -5173,7 +5214,7 @@ class WriteBTLx(component):
- Panel
- false
- - 0.64158892631530762
+ - 0
- 457734f7-3478-4434-a17d-636820c0980c
- 1
- Double click to edit panel content…
@@ -5182,7 +5223,7 @@ class WriteBTLx(component):
-
- 1743
+ 1742
1079
201
53
@@ -5191,7 +5232,7 @@ class WriteBTLx(component):
- 0
- 0
-
- 1743.429
+ 1742.733
1079.09
@@ -5226,7 +5267,7 @@ class WriteBTLx(component):
- Panel
- false
- - 0
+ - 1
- 5946e9dc-f13c-4e3c-8873-1cc4030c843d
- 1
- Double click to edit panel content…
@@ -5273,9 +5314,10 @@ class WriteBTLx(component):
-
+
- 0
- Retrieve a specific item from a list.
+ - true
- 6758fb57-bfa5-452b-afb1-29c442b80cae
- List Item
- Item
@@ -5284,13 +5326,13 @@ class WriteBTLx(component):
-
- 387
+ 386
1770
64
64
-
- 421
+ 420
1802
@@ -5320,13 +5362,13 @@ class WriteBTLx(component):
-
- 389
+ 388
1772
17
20
-
- 399
+ 398
1782
@@ -5347,13 +5389,13 @@ class WriteBTLx(component):
-
- 389
+ 388
1792
17
20
-
- 399
+ 398
1802
@@ -5393,13 +5435,13 @@ class WriteBTLx(component):
-
- 389
+ 388
1812
17
20
-
- 399
+ 398
1822
@@ -5440,13 +5482,13 @@ class WriteBTLx(component):
-
- 436
+ 435
1772
13
60
-
- 442.5
+ 441.5
1802
@@ -5485,7 +5527,7 @@ class WriteBTLx(component):
24
-
- 270.9937
+ 270.2979
1782.105
@@ -5564,13 +5606,13 @@ class WriteBTLx(component):
-
- 196
+ 195
1832
160
20
-
- 196.1492
+ 195.4534
1832.229
@@ -5583,7 +5625,7 @@ class WriteBTLx(component):
- 3
- 0
- 0
- - 2
+ - 3
@@ -5644,13 +5686,13 @@ print(centerline.length)
-
- 109
+ 108
511
128
124
-
- 138
+ 137
573
@@ -5686,13 +5728,13 @@ print(centerline.length)
-
- 111
+ 110
513
12
60
-
- 118.5
+ 117.5
543
@@ -5716,13 +5758,13 @@ print(centerline.length)
-
- 111
+ 110
573
12
60
-
- 118.5
+ 117.5
603
@@ -5742,13 +5784,13 @@ print(centerline.length)
-
- 153
+ 152
513
82
20
-
- 194
+ 193
523
@@ -5768,13 +5810,13 @@ print(centerline.length)
-
- 153
+ 152
533
82
20
-
- 194
+ 193
543
@@ -5794,13 +5836,13 @@ print(centerline.length)
-
- 153
+ 152
553
82
20
-
- 194
+ 193
563
@@ -5820,13 +5862,13 @@ print(centerline.length)
-
- 153
+ 152
573
82
20
-
- 194
+ 193
583
@@ -5846,13 +5888,13 @@ print(centerline.length)
-
- 153
+ 152
593
82
20
-
- 194
+ 193
603
@@ -5872,13 +5914,13 @@ print(centerline.length)
-
- 153
+ 152
613
82
20
-
- 194
+ 193
623
@@ -5918,7 +5960,7 @@ print(centerline.length)
24
-
- 446.9046
+ 446.2088
739.606
@@ -5945,13 +5987,13 @@ print(centerline.length)
-
- 380
+ 379
494
64
64
-
- 411
+ 410
526
@@ -5970,13 +6012,13 @@ print(centerline.length)
-
- 382
+ 381
496
14
20
-
- 390.5
+ 389.5
506
@@ -6026,13 +6068,13 @@ print(centerline.length)
-
- 382
+ 381
516
14
20
-
- 390.5
+ 389.5
526
@@ -6075,13 +6117,13 @@ print(centerline.length)
-
- 382
+ 381
536
14
20
-
- 390.5
+ 389.5
546
@@ -6124,13 +6166,13 @@ print(centerline.length)
-
- 426
+ 425
496
16
60
-
- 434
+ 433
526
@@ -6159,13 +6201,13 @@ print(centerline.length)
-
- 424
+ 423
580
68
44
-
- 456
+ 455
602
@@ -6184,13 +6226,13 @@ print(centerline.length)
-
- 426
+ 425
582
15
20
-
- 435
+ 434
592
@@ -6211,13 +6253,13 @@ print(centerline.length)
-
- 426
+ 425
602
15
20
-
- 435
+ 434
612
@@ -6237,13 +6279,13 @@ print(centerline.length)
-
- 471
+ 470
582
19
40
-
- 480.5
+ 479.5
602
@@ -6275,13 +6317,13 @@ print(centerline.length)
-
- 304
+ 303
607
50
24
-
- 329.013
+ 328.3172
619.2989
@@ -6311,13 +6353,13 @@ print(centerline.length)
-
- 302
+ 301
572
50
24
-
- 327.621
+ 326.9252
584.1504
@@ -6383,13 +6425,13 @@ print(centerline.length)
-
- 114
+ 113
203
128
124
-
- 143
+ 142
265
@@ -6425,13 +6467,13 @@ print(centerline.length)
-
- 116
+ 115
205
12
60
-
- 123.5
+ 122.5
235
@@ -6455,13 +6497,13 @@ print(centerline.length)
-
- 116
+ 115
265
12
60
-
- 123.5
+ 122.5
295
@@ -6481,13 +6523,13 @@ print(centerline.length)
-
- 158
+ 157
205
82
20
-
- 199
+ 198
215
@@ -6507,13 +6549,13 @@ print(centerline.length)
-
- 158
+ 157
225
82
20
-
- 199
+ 198
235
@@ -6533,13 +6575,13 @@ print(centerline.length)
-
- 158
+ 157
245
82
20
-
- 199
+ 198
255
@@ -6559,13 +6601,13 @@ print(centerline.length)
-
- 158
+ 157
265
82
20
-
- 199
+ 198
275
@@ -6585,13 +6627,13 @@ print(centerline.length)
-
- 158
+ 157
285
82
20
-
- 199
+ 198
295
@@ -6611,13 +6653,13 @@ print(centerline.length)
-
- 158
+ 157
305
82
20
-
- 199
+ 198
315
@@ -6651,13 +6693,13 @@ print(centerline.length)
-
- 432
+ 431
415
50
24
-
- 457.2508
+ 456.555
427.7627
@@ -6684,13 +6726,13 @@ print(centerline.length)
-
- 389
+ 388
181
64
64
-
- 420
+ 419
213
@@ -6709,13 +6751,13 @@ print(centerline.length)
-
- 391
+ 390
183
14
20
-
- 399.5
+ 398.5
193
@@ -6765,13 +6807,13 @@ print(centerline.length)
-
- 391
+ 390
203
14
20
-
- 399.5
+ 398.5
213
@@ -6814,13 +6856,13 @@ print(centerline.length)
-
- 391
+ 390
223
14
20
-
- 399.5
+ 398.5
233
@@ -6863,13 +6905,13 @@ print(centerline.length)
-
- 435
+ 434
183
16
60
-
- 443
+ 442
213
@@ -6898,13 +6940,13 @@ print(centerline.length)
-
- 433
+ 432
267
68
44
-
- 465
+ 464
289
@@ -6923,13 +6965,13 @@ print(centerline.length)
-
- 435
+ 434
269
15
20
-
- 444
+ 443
279
@@ -6950,13 +6992,13 @@ print(centerline.length)
-
- 435
+ 434
289
15
20
-
- 444
+ 443
299
@@ -6976,13 +7018,13 @@ print(centerline.length)
-
- 480
+ 479
269
19
40
-
- 489.5
+ 488.5
289
@@ -7014,13 +7056,13 @@ print(centerline.length)
-
- 314
+ 313
295
50
24
-
- 339.3595
+ 338.6637
307.4556
@@ -7056,7 +7098,7 @@ print(centerline.length)
24
-
- 337.9674
+ 337.2716
272.3071
@@ -7134,7 +7176,7 @@ print(centerline.length)
-
+
- import inspect
from ghpythonlib.componentbase import executingcomponent as component
@@ -7196,6 +7238,7 @@ class T_TopologyJointRule(component):
ghenv.Component.ExpireSolution(True)
- GhPython provides a Python script component
+ - true
- true
- true
-
@@ -7212,13 +7255,13 @@ class T_TopologyJointRule(component):
-
- 1621
+ 1620
1479
151
28
-
- 1696
+ 1695
1493
@@ -7245,13 +7288,13 @@ class T_TopologyJointRule(component):
-
- 1623
+ 1622
1481
58
24
-
- 1653.5
+ 1652.5
1493
@@ -7271,13 +7314,13 @@ class T_TopologyJointRule(component):
-
- 1711
+ 1710
1481
59
24
-
- 1740.5
+ 1739.5
1493
@@ -7310,13 +7353,13 @@ class T_TopologyJointRule(component):
-
- 253
+ 252
1891
50
24
-
- 278.6859
+ 277.9901
1903.796
@@ -7357,7 +7400,7 @@ class T_TopologyJointRule(component):
-
+
- import inspect
from ghpythonlib.componentbase import executingcomponent as component
@@ -7419,10 +7462,11 @@ class L_TopologyJointRule(component):
ghenv.Component.ExpireSolution(True)
- GhPython provides a Python script component
+ - true
- true
- true
-
- iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDwAACw8BkvkDpQAAANBJREFUSEvtk70NwjAQhTNCBrDkk7wAo9DQMwIjMIHFCIyQEVK4TMEIjMAGRif5JOvlcEwwiCLFV8Q/75PeOV2MsfsmswXGWRqdpZi44P475KHkLPXO0jkLF054sRYJ3yuhGndnaYchJUTAFzHsFQOGlBBB3jlzTXVxbTfYGzGkhAgGCOnlAFfSQoCDbS7gSvIQ/uZ6mlWEMyixasi1z/Sx6pkmCXetVcIc8aLxYTI+ROPDAfdUQQ7MRP2LPxLUsAkW2QSL/FKgMf2/oJbZQmue50vIddCJVREAAAAASUVORK5CYII=
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAANBJREFUSEvtk70NwjAQhTNCBrDkk7wAo9DQMwIjMIHFCIyQEVK4TMEIjMAGRif5JOvlcEwwiCLFV8Q/75PeOV2MsfsmswXGWRqdpZi44P475KHkLPXO0jkLF054sRYJ3yuhGndnaYchJUTAFzHsFQOGlBBB3jlzTXVxbTfYGzGkhAgGCOnlAFfSQoCDbS7gSvIQ/uZ6mlWEMyixasi1z/Sx6pkmCXetVcIc8aLxYTI+ROPDAfdUQQ7MRP2LPxLUsAkW2QSL/FKgMf2/oJbZQmue50vIddCJVREAAAAASUVORK5CYII=
- false
- 0944aec4-9862-4758-8cf6-b1210d2c9ec3
@@ -7435,14 +7479,14 @@ class L_TopologyJointRule(component):
-
- 1603
- 1370
+ 1600
+ 1342
185
84
-
- 1713
- 1412
+ 1710
+ 1384
@@ -7471,14 +7515,14 @@ class L_TopologyJointRule(component):
-
- 1605
- 1372
+ 1602
+ 1344
93
20
-
- 1653
- 1382
+ 1650
+ 1354
@@ -7497,14 +7541,14 @@ class L_TopologyJointRule(component):
-
- 1605
- 1392
+ 1602
+ 1364
93
20
-
- 1653
- 1402
+ 1650
+ 1374
@@ -7523,14 +7567,14 @@ class L_TopologyJointRule(component):
-
- 1605
- 1412
+ 1602
+ 1384
93
20
-
- 1653
- 1422
+ 1650
+ 1394
@@ -7549,14 +7593,14 @@ class L_TopologyJointRule(component):
-
- 1605
- 1432
+ 1602
+ 1404
93
20
-
- 1653
- 1442
+ 1650
+ 1414
@@ -7575,14 +7619,14 @@ class L_TopologyJointRule(component):
-
- 1728
- 1372
+ 1725
+ 1344
58
80
-
- 1757
- 1412
+ 1754
+ 1384
@@ -7613,13 +7657,13 @@ class L_TopologyJointRule(component):
-
- 1610
+ 1611
1530
195
20
-
- 1610.072
+ 1611.303
1530.793
@@ -7632,7 +7676,7 @@ class L_TopologyJointRule(component):
- 100
- 0
- 0
- - 30
+ - 50
@@ -7646,8 +7690,9 @@ class L_TopologyJointRule(component):
-
+
- Merge a bunch of data streams
+ - true
- 50b4d783-2e1c-4a34-ba17-9ce6f72fbd7b
- Merge
- Merge
@@ -7656,13 +7701,13 @@ class L_TopologyJointRule(component):
-
- 379
+ 378
1871
88
64
-
- 417
+ 416
1903
@@ -7693,13 +7738,13 @@ class L_TopologyJointRule(component):
-
- 381
+ 380
1873
21
20
-
- 393
+ 392
1883
@@ -7722,13 +7767,13 @@ class L_TopologyJointRule(component):
-
- 381
+ 380
1893
21
20
-
- 393
+ 392
1903
@@ -7750,13 +7795,13 @@ class L_TopologyJointRule(component):
-
- 381
+ 380
1913
21
20
-
- 393
+ 392
1923
@@ -7778,13 +7823,13 @@ class L_TopologyJointRule(component):
-
- 432
+ 431
1873
33
60
-
- 440.5
+ 439.5
1903
@@ -7797,6 +7842,2623 @@ class L_TopologyJointRule(component):
+
+
+ - eeafc956-268e-461d-8e73-ee05c6f72c01
+ - Stream Filter
+
+
+
+
+ - Filters a collection of input streams
+ - true
+ - 21576f20-ed8e-4c3d-ac74-f4c6622726c1
+ - Stream Filter
+ - Filter
+
+
+
+
+ -
+ 631
+ 1582
+ 77
+ 64
+
+ -
+ 663
+ 1614
+
+
+
+
+
+ - 3
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - Index of Gate stream
+ - 5654947d-2496-4fda-aa9d-464d6556a204
+ - Gate
+ - G
+ - false
+ - 1af3d673-3732-4271-8c59-52c07949abb2
+ - 1
+
+
+
+
+ -
+ 633
+ 1584
+ 15
+ 20
+
+ -
+ 642
+ 1594
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 0
+ - 754ae8d9-c10a-4fd4-9620-5e5a7054c6f6
+ - false
+ - Stream 0
+ - 0
+ - true
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 633
+ 1604
+ 15
+ 20
+
+ -
+ 642
+ 1614
+
+
+
+
+
+
+
+ - 2
+ - Input stream at index 1
+ - d482ec97-792e-4152-90f0-4e5000ccf833
+ - false
+ - Stream 1
+ - 1
+ - true
+ - 353d7621-3cd9-4d30-ac48-2ba4334ede62
+ - 1
+
+
+
+
+ -
+ 633
+ 1624
+ 15
+ 20
+
+ -
+ 642
+ 1634
+
+
+
+
+
+
+
+ - 2
+ - Filtered stream
+ - 5e8cc817-6357-4d57-8f5c-bb9d6ce9669c
+ - false
+ - Stream
+ - S(1)
+ - false
+ - 0
+
+
+
+
+ -
+ 678
+ 1584
+ 28
+ 60
+
+ -
+ 692
+ 1614
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 22990b1f-9be6-477c-ad89-f775cd347105
+ - Flip Curve
+
+
+
+
+ - Flip a curve using an optional guide curve.
+ - true
+ - 016c3b71-d2f1-4a70-be07-535a52db8a79
+ - Flip Curve
+ - Flip
+
+
+
+
+ -
+ 543
+ 1622
+ 66
+ 44
+
+ -
+ 575
+ 1644
+
+
+
+
+
+ - Curve to flip
+ - 603a86f3-4555-4985-bfc4-a523884cf415
+ - Curve
+ - C
+ - false
+ - 0bc447d1-0b47-4d44-88e5-1c1a33db80fc
+ - 1
+
+
+
+
+ -
+ 545
+ 1624
+ 15
+ 20
+
+ -
+ 554
+ 1634
+
+
+
+
+
+
+
+ - Optional guide curve
+ - e612372e-97c9-4956-83c1-7c0c87cd1d5f
+ - Guide
+ - G
+ - true
+ - 0
+
+
+
+
+ -
+ 545
+ 1644
+ 15
+ 20
+
+ -
+ 554
+ 1654
+
+
+
+
+
+
+
+ - Flipped curve
+ - 353d7621-3cd9-4d30-ac48-2ba4334ede62
+ - Curve
+ - C
+ - false
+ - 0
+
+
+
+
+ -
+ 590
+ 1624
+ 17
+ 20
+
+ -
+ 598.5
+ 1634
+
+
+
+
+
+
+
+ - Flip action
+ - 3a7a88e4-e7f8-460d-ab66-2f6e0538533a
+ - Flag
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 590
+ 1644
+ 17
+ 20
+
+ -
+ 598.5
+ 1654
+
+
+
+
+
+
+
+
+
+
+
+ - 28061aae-04fb-4cb5-ac45-16f3b66bc0a4
+ - Center Box
+
+
+
+
+ - Create a box centered on a plane.
+ - true
+ - b914bb37-7b5a-46a9-a1fb-6b230797d09b
+ - Center Box
+ - Box
+
+
+
+
+ -
+ 2280
+ 1273
+ 64
+ 84
+
+ -
+ 2311
+ 1315
+
+
+
+
+
+ - Base plane
+ - 65a01b3f-9641-4df1-a831-f0ac4ddbc0c8
+ - Base
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1275
+ 14
+ 20
+
+ -
+ 2290.5
+ 1285
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {x} direction.
+ - be885264-6145-4d8b-84ce-3158683407e8
+ - X
+ - X
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1295
+ 14
+ 20
+
+ -
+ 2290.5
+ 1305
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {y} direction.
+ - b9985f26-d4b2-41fe-9963-3b6ad745502d
+ - Y
+ - Y
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1315
+ 14
+ 20
+
+ -
+ 2290.5
+ 1325
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Size of box in {z} direction.
+ - 7c5c3d48-fede-49be-a769-5b4472f051c3
+ - Z
+ - Z
+ - false
+ - 0
+
+
+
+
+ -
+ 2282
+ 1335
+ 14
+ 20
+
+ -
+ 2290.5
+ 1345
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - Resulting box
+ - 9db4d62a-2de4-4b49-b116-d91d1fb3fbc1
+ - Box
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2326
+ 1275
+ 16
+ 80
+
+ -
+ 2334
+ 1315
+
+
+
+
+
+
+
+
+
+
+
+ - 7c0523e8-79c9-45a2-8777-cf0d46bc5432
+ - Volume
+
+
+
+
+ - Solve volume properties for closed breps and meshes.
+ - true
+ - 87adbf20-4372-408f-afd2-8de0206c319f
+ - Volume
+ - Volume
+
+
+
+
+ -
+ 2086
+ 1278
+ 66
+ 44
+
+ -
+ 2118
+ 1300
+
+
+
+
+
+ - 1
+ - ac2bc2cb-70fb-4dd5-9c78-7e1ea97fe278
+ - 2
+ - 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312
+ - fbac3e32-f100-4292-8692-77240a42fd1a
+
+
+
+
+ - Closed brep or mesh for volume computation
+ - 6653e031-0118-4585-9a5a-3c23bddc56e5
+ - Geometry
+ - G
+ - false
+ - b3588f0a-414e-4a3d-87e6-79cde3ae279b
+ - 1
+
+
+
+
+ -
+ 2088
+ 1280
+ 15
+ 40
+
+ -
+ 2097
+ 1300
+
+
+
+
+
+
+
+ - Volume of geometry
+ - 84b4bf60-4f2c-410a-abe2-275faa062d0f
+ - Volume
+ - V
+ - true
+ - 0
+
+
+
+
+ -
+ 2133
+ 1280
+ 17
+ 20
+
+ -
+ 2141.5
+ 1290
+
+
+
+
+
+
+
+ - Volume centroid of geometry
+ - c4969727-a064-4123-ae15-e612faccd299
+ - Centroid
+ - C
+ - true
+ - 0
+
+
+
+
+ -
+ 2133
+ 1300
+ 17
+ 20
+
+ -
+ 2141.5
+ 1310
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - true
+ - 63256972-bef4-4fe4-b953-922687ce5e7c
+ - Plane
+ - Pln
+ - false
+ - 502370ce-eac6-4444-b153-000dda5f7ad2
+ - 1
+
+
+
+
+ -
+ 1925
+ 1329
+ 50
+ 24
+
+ -
+ 1950.909
+ 1341.371
+
+
+
+
+
+
+
+
+
+ - 9103c240-a6a9-4223-9b42-dbd19bf38e2b
+ - Unit Z
+
+
+
+
+ - Unit vector parallel to the world {z} axis.
+ - true
+ - 69a1047d-365a-4696-ac21-d66891c3cd01
+ - Unit Z
+ - Z
+
+
+
+
+ -
+ 724
+ 1525
+ 63
+ 28
+
+ -
+ 753
+ 1539
+
+
+
+
+
+ - Unit multiplication
+ - b815eb40-79e9-4d59-baf5-789e4733642b
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 726
+ 1527
+ 12
+ 24
+
+ -
+ 733.5
+ 1539
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {z} vector
+ - 7d37dfc6-37e3-4d21-81f9-feb71a9e7daf
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ 768
+ 1527
+ 17
+ 24
+
+ -
+ 776.5
+ 1539
+
+
+
+
+
+
+
+
+
+
+
+ - 79f9fbb3-8f1d-4d9a-88a9-f7961b1012cd
+ - Unit X
+
+
+
+
+ - Unit vector parallel to the world {x} axis.
+ - true
+ - 0ee70b32-a69d-44dc-a0ef-f78b3accb6a6
+ - Unit X
+ - X
+
+
+
+
+ -
+ 744
+ 1499
+ 63
+ 28
+
+ -
+ 773
+ 1513
+
+
+
+
+
+ - Unit multiplication
+ - 84f4a5d7-05c6-4323-aa8c-0f3aaafe88ab
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 746
+ 1501
+ 12
+ 24
+
+ -
+ 753.5
+ 1513
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 1
+
+
+
+
+
+
+
+
+
+
+ - World {x} vector
+ - 985674c1-93e7-4bfa-a6b2-fa372e7b66a4
+ - Unit vector
+ - V
+ - false
+ - 0
+
+
+
+
+ -
+ 788
+ 1501
+ 17
+ 24
+
+ -
+ 796.5
+ 1513
+
+
+
+
+
+
+
+
+
+
+
+ - a0d62394-a118-422d-abb3-6af115c75b25
+ - Addition
+
+
+
+
+ - Mathematical addition
+ - true
+ - 3da3f49a-976d-4d1a-9944-be7361193132
+ - Addition
+ - A+B
+
+
+
+
+ -
+ 822
+ 1499
+ 65
+ 44
+
+ -
+ 853
+ 1521
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - First item for addition
+ - 5e583fc7-a180-47d9-806a-9671969c74cd
+ - A
+ - A
+ - true
+ - 985674c1-93e7-4bfa-a6b2-fa372e7b66a4
+ - 1
+
+
+
+
+ -
+ 824
+ 1501
+ 14
+ 20
+
+ -
+ 832.5
+ 1511
+
+
+
+
+
+
+
+ - Second item for addition
+ - 1258f834-4314-420b-92a4-2ea8f8149a09
+ - B
+ - B
+ - true
+ - 7d37dfc6-37e3-4d21-81f9-feb71a9e7daf
+ - 1
+
+
+
+
+ -
+ 824
+ 1521
+ 14
+ 20
+
+ -
+ 832.5
+ 1531
+
+
+
+
+
+
+
+ - Result of addition
+ - f0b81979-f783-450f-b862-9b53febcbe90
+ - Result
+ - R
+ - false
+ - 0
+
+
+
+
+ -
+ 868
+ 1501
+ 17
+ 40
+
+ -
+ 876.5
+ 1521
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - true
+ - 303f3345-f7ae-4d8a-a427-00633f5158c9
+ - List Item
+ - Item
+
+
+
+
+ -
+ 2261
+ 1383
+ 64
+ 64
+
+ -
+ 2295
+ 1415
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - 02198cbf-b46c-409a-876a-ab7137c2c7c3
+ - List
+ - L
+ - false
+ - 725ac33d-374c-4cc0-8f18-3e15182f1323
+ - 1
+
+
+
+
+ -
+ 2263
+ 1385
+ 17
+ 20
+
+ -
+ 2273
+ 1395
+
+
+
+
+
+
+
+ - Item index
+ - a58fb182-d690-47fe-a531-395d7a504986
+ - Index
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2263
+ 1405
+ 17
+ 20
+
+ -
+ 2273
+ 1415
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - cfacaccd-8c41-4b49-b062-31e2775b1e82
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 2263
+ 1425
+ 17
+ 20
+
+ -
+ 2273
+ 1435
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - 53f6b1d1-c010-483d-b5aa-7c83fc14f9b6
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2310
+ 1385
+ 13
+ 60
+
+ -
+ 2316.5
+ 1415
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 59daf374-bc21-4a5e-8282-5504fb7ae9ae
+ - List Item
+
+
+
+
+ - 0
+ - Retrieve a specific item from a list.
+ - true
+ - cf135943-38be-4663-ab8d-0adadaca146c
+ - List Item
+ - Item
+
+
+
+
+ -
+ 2029
+ 1336
+ 74
+ 84
+
+ -
+ 2063
+ 1378
+
+
+
+
+
+ - 3
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 2e3ab970-8545-46bb-836c-1c11e5610bce
+ - cb95db89-6165-43b6-9c41-5702bc5bf137
+ - 4
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - 1
+ - Base list
+ - 1522da3f-4c46-480e-9c80-988a4ab52cfd
+ - List
+ - L
+ - false
+ - 63256972-bef4-4fe4-b953-922687ce5e7c
+ - 1
+
+
+
+
+ -
+ 2031
+ 1338
+ 17
+ 26
+
+ -
+ 2041
+ 1351.333
+
+
+
+
+
+
+
+ - Item index
+ - 4b65d389-6766-4850-b495-164cce1c42de
+ - Index
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2031
+ 1364
+ 17
+ 27
+
+ -
+ 2041
+ 1378
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+ - Wrap index to list bounds
+ - b48d0cca-8f71-499e-a857-6d7f31da9575
+ - Wrap
+ - W
+ - false
+ - 0
+
+
+
+
+ -
+ 2031
+ 1391
+ 17
+ 27
+
+ -
+ 2041
+ 1404.667
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Item at {i'}
+ - 6a0268e5-201a-44ec-89af-473ef88f105f
+ - false
+ - Item
+ - i
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1338
+ 23
+ 20
+
+ -
+ 2089.5
+ 1348
+
+
+
+
+
+
+
+ - Item at {+1'}
+ - d6d70e65-f2b9-4f33-807b-e4851b80613a
+ - false
+ - Item +1
+ - +1
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1358
+ 23
+ 20
+
+ -
+ 2089.5
+ 1368
+
+
+
+
+
+
+
+ - Item at {+2'}
+ - a97d3de6-311e-495d-903f-7d5145e85a2a
+ - false
+ - Item +2
+ - +2
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1378
+ 23
+ 20
+
+ -
+ 2089.5
+ 1388
+
+
+
+
+
+
+
+ - Item at {+3'}
+ - b54d49ae-2637-4951-a942-5be0ff2aeac8
+ - false
+ - Item +3
+ - +3
+ - false
+ - 0
+
+
+
+
+ -
+ 2078
+ 1398
+ 23
+ 20
+
+ -
+ 2089.5
+ 1408
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - d8332545-21b2-4716-96e3-8559a9876e17
+ - Dispatch
+
+
+
+
+ - Dispatch the items in a list into two target lists.
+ - true
+ - cb623ee7-06c3-4400-aaba-a9487b4ee237
+ - Dispatch
+ - Dispatch
+
+
+
+
+ -
+ 2145
+ 1507
+ 64
+ 44
+
+ -
+ 2175
+ 1529
+
+
+
+
+
+ - 1
+ - List to filter
+ - f301c669-eabb-4535-b5ed-b3bf1b42699b
+ - List
+ - L
+ - false
+ - 725ac33d-374c-4cc0-8f18-3e15182f1323
+ - 1
+
+
+
+
+ -
+ 2147
+ 1509
+ 13
+ 20
+
+ -
+ 2155
+ 1519
+
+
+
+
+
+
+
+ - 1
+ - Dispatch pattern
+ - 4b1f1469-cc85-4ab1-9812-a58999856e66
+ - Dispatch pattern
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 2147
+ 1529
+ 13
+ 20
+
+ -
+ 2155
+ 1539
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+ - false
+
+
+
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for True values
+ - 070a7853-fe38-4e8a-b6d2-caeaaa0ae003
+ - List A
+ - A
+ - false
+ - 0
+
+
+
+
+ -
+ 2190
+ 1509
+ 17
+ 20
+
+ -
+ 2198.5
+ 1519
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for False values
+ - 90efa295-d4df-4bf6-9596-5f0142788873
+ - List B
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2190
+ 1529
+ 17
+ 20
+
+ -
+ 2198.5
+ 1539
+
+
+
+
+
+
+
+
+
+
+
+ - 537b0419-bbc2-4ff4-bf08-afe526367b2c
+ - Custom Preview
+
+
+
+
+ - Allows for customized geometry previews
+ - true
+ - 5969f9b6-e599-48a8-a77a-9967f0e908ee
+ - Custom Preview
+ - Preview
+
+
+
+
+
+ -
+ 2300
+ 1481
+ 48
+ 44
+
+ -
+ 2334
+ 1503
+
+
+
+
+
+ - Geometry to preview
+ - true
+ - 6ffcc6ca-c0f2-4057-80d1-9394a508e988
+ - Geometry
+ - G
+ - false
+ - 070a7853-fe38-4e8a-b6d2-caeaaa0ae003
+ - 1
+
+
+
+
+ -
+ 2302
+ 1483
+ 17
+ 20
+
+ -
+ 2312
+ 1493
+
+
+
+
+
+
+
+ - The material override
+ - 4add57de-5031-447f-a497-bb416da407be
+ - Material
+ - M
+ - false
+ - 0
+
+
+
+
+ -
+ 2302
+ 1503
+ 17
+ 20
+
+ -
+ 2312
+ 1513
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ -
+ 255;221;160;221
+
+ -
+ 255;66;48;66
+
+ - 0.5
+ -
+ 255;255;255;255
+
+ - 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - true
+ - 05edbc97-20e7-47f4-ba39-16e952aca10f
+ - Brep
+ - Brep
+ - false
+ - 90efa295-d4df-4bf6-9596-5f0142788873
+ - 1
+
+
+
+
+ -
+ 2230
+ 1577
+ 50
+ 24
+
+ -
+ 2255.067
+ 1589.683
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - c17ecd4e-0c95-4f30-a30e-9d892e6426cc
+ - Brep
+ - Brep
+ - false
+ - 0
+
+
+
+
+ -
+ 2229
+ 928
+ 50
+ 24
+
+ -
+ 2254.2
+ 940.6667
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - 29681234-09cf-497f-ae28-cbb735e46f22
+
+
+
+
+ - ee1a0b75-9176-4b97-b45c-8f17b5c16e99
+
+
+
+
+
+
+
+
+
+
+
+
+ - 4f8984c4-7c7a-4d69-b0a2-183cbb330d20
+ - Plane
+
+
+
+
+ - Contains a collection of three-dimensional axis-systems
+ - 380a46d2-43bc-4a8c-8a22-b7aef89bbf72
+ - Plane
+ - Pln
+ - false
+ - c17ecd4e-0c95-4f30-a30e-9d892e6426cc
+ - 1
+
+
+
+
+ -
+ 2313
+ 928
+ 50
+ 24
+
+ -
+ 2338.067
+ 940.9667
+
+
+
+
+
+
+
+
+
+ - 6ea4c4c7-ddef-4313-a21f-8b445c20220c
+ - 1c9de8a1-315f-4c56-af06-8f69fee80a7a
+ - Tween Two Planes
+
+
+
+
+ - Tween between two planes.
+ - 8a7ee619-ca42-4727-a2cc-5cf2035c36c8
+ - Tween Two Planes
+ - Twn2Plns
+
+
+
+
+ -
+ 2493
+ 939
+ 65
+ 84
+
+ -
+ 2525
+ 981
+
+
+
+
+
+ - Plane to tween from
+ - eaae4cc6-ae0d-4898-a8ae-a582ca50ce29
+ - Plane A
+ - A
+ - false
+ - b70b10e6-e22b-4abe-a2d5-45a65332177c
+ - 1
+
+
+
+
+ -
+ 2495
+ 941
+ 15
+ 20
+
+ -
+ 2504
+ 951
+
+
+
+
+
+
+
+ - Plane to tween to
+ - 9d069cc0-02f3-475f-9b9b-4dd32f2ea790
+ - Plane B
+ - B
+ - false
+ - fc33ea5e-8457-40ab-8a5f-4f6de1e193d7
+ - 1
+
+
+
+
+ -
+ 2495
+ 961
+ 15
+ 20
+
+ -
+ 2504
+ 971
+
+
+
+
+
+
+
+ - Tween factor (0.0 = Plane A, 1.0 = Plane B)
+ - 06cd748f-8bc3-48ec-91f1-e33ddd966045
+ - Factor
+ - F
+ - false
+ - 0
+
+
+
+
+ -
+ 2495
+ 981
+ 15
+ 20
+
+ -
+ 2504
+ 991
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 0.5
+
+
+
+
+
+
+
+
+
+
+ - Interpolate with quaternion rotation
+ - 012b355a-6715-4ddf-9169-8b9e68bfcacb
+ - Quaternion
+ - Q
+ - false
+ - 0
+
+
+
+
+ -
+ 2495
+ 1001
+ 15
+ 20
+
+ -
+ 2504
+ 1011
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+
+
+
+
+
+
+ - Resulting tween plane
+ - 0a7cd565-45a8-4c93-8f75-04d086baa295
+ - Tween
+ - T
+ - false
+ - 0
+
+
+
+
+ -
+ 2540
+ 941
+ 16
+ 80
+
+ -
+ 2548
+ 981
+
+
+
+
+
+
+
+
+
+
+
+ - d8332545-21b2-4716-96e3-8559a9876e17
+ - Dispatch
+
+
+
+
+ - Dispatch the items in a list into two target lists.
+ - 687ce77d-9faf-470b-aaea-ccbcc0ba4326
+ - Dispatch
+ - Dispatch
+
+
+
+
+ -
+ 2379
+ 939
+ 64
+ 44
+
+ -
+ 2409
+ 961
+
+
+
+
+
+ - 1
+ - List to filter
+ - 8997e329-8f57-4af9-b7d4-64a6d829387b
+ - List
+ - L
+ - false
+ - 380a46d2-43bc-4a8c-8a22-b7aef89bbf72
+ - 1
+
+
+
+
+ -
+ 2381
+ 941
+ 13
+ 20
+
+ -
+ 2389
+ 951
+
+
+
+
+
+
+
+ - 1
+ - Dispatch pattern
+ - 22526779-5169-4e54-9c8d-4e398ca1638c
+ - Dispatch pattern
+ - P
+ - false
+ - 0
+
+
+
+
+ -
+ 2381
+ 961
+ 13
+ 20
+
+ -
+ 2389
+ 971
+
+
+
+
+
+ - 1
+
+
+
+
+ - 2
+ - {0}
+
+
+
+
+ - true
+
+
+
+
+ - false
+
+
+
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for True values
+ - b70b10e6-e22b-4abe-a2d5-45a65332177c
+ - List A
+ - A
+ - false
+ - 0
+
+
+
+
+ -
+ 2424
+ 941
+ 17
+ 20
+
+ -
+ 2432.5
+ 951
+
+
+
+
+
+
+
+ - 1
+ - Dispatch target for False values
+ - fc33ea5e-8457-40ab-8a5f-4f6de1e193d7
+ - List B
+ - B
+ - false
+ - 0
+
+
+
+
+ -
+ 2424
+ 961
+ 17
+ 20
+
+ -
+ 2432.5
+ 971
+
+
+
+
+
+
+
+
+
+
+
+ - 919e146f-30ae-4aae-be34-4d72f555e7da
+ - Brep
+
+
+
+
+ - Contains a collection of Breps (Boundary REPresentations)
+ - ed4080d4-774f-4f9f-9f0d-d8ea182a21c7
+ - Brep
+ - Brep
+ - false
+ - 0
+
+
+
+
+ -
+ 2225
+ 1066
+ 50
+ 24
+
+ -
+ 2250.564
+ 1078.813
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - 5903e3bf-023e-4163-8e83-1513748adbcf
+
+
+
+
+
+
+
+
+
+
+
+
+ - d5967b9f-e8ee-436b-a8ad-29fdcecf32d5
+ - Curve
+
+
+
+
+ - Contains a collection of generic curves
+ - 3258285e-9db1-4918-93f8-5a397ec40239
+ - Curve
+ - Crv
+ - false
+ - 0
+
+
+
+
+ -
+ 342
+ 1481
+ 50
+ 24
+
+ -
+ 367.2433
+ 1493.886
+
+
+
+
+
+ - 1
+
+
+
+
+ - 1
+ - {0}
+
+
+
+
+ - -1
+ - 2d99496e-781f-446b-b1f4-9cd8b275b220
+
+
+
+
+
+
+
+
+
+
+
+
+ - 410755b1-224a-4c1e-a407-bf32fb45ea7e
+ - 00000000-0000-0000-0000-000000000000
+ - CT: X Topological Joint Rules
+
+
+
+
+ - import inspect
+
+from ghpythonlib.componentbase import executingcomponent as component
+from Grasshopper.Kernel.GH_RuntimeMessageLevel import Warning
+
+from compas_timber.connections import Joint
+from compas_timber.connections import JointTopology
+from compas_timber.connections import XHalfLapJoint
+from compas_timber.design import TopologyRule
+from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
+from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
+from compas_timber.ghpython.ghcomponent_helpers import rename_gh_output
+
+
+class X_TopologyJointRule(component):
+ def __init__(self):
+ super(X_TopologyJointRule, self).__init__()
+ self.classes = {}
+ for cls in get_leaf_subclasses(Joint):
+ if cls.SUPPORTED_TOPOLOGY == JointTopology.TOPO_X:
+ self.classes[cls.__name__] = cls
+ if ghenv.Component.Params.Output[0].NickName == "Rule":
+ self.joint_type = XHalfLapJoint
+ self.clicked = False
+ else:
+ self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
+ self.clicked = True
+
+ def RunScript(self, *args):
+ if not self.clicked:
+ ghenv.Component.Message = "Default: XHalfLapJoint"
+ self.AddRuntimeMessage(Warning, "XHalfLapJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_X, XHalfLapJoint)
+ else:
+ ghenv.Component.Message = self.joint_type.__name__
+ kwargs = {}
+ for i, val in enumerate(args):
+ if val is not None:
+ kwargs[self.arg_names()[i]] = val
+ if self.joint_type.SUPPORTED_TOPOLOGY != JointTopology.TOPO_X:
+ self.AddRuntimeMessage(Warning, "Joint type does not match topology. Joint may not be generated.")
+ return TopologyRule(JointTopology.TOPO_X, self.joint_type, **kwargs)
+
+ def arg_names(self):
+ names = inspect.getargspec(self.joint_type.__init__)[0][3:]
+ return [name for name in names if (name != "key") and (name != "frame")]
+
+ def AppendAdditionalMenuItems(self, menu):
+ for name in self.classes.keys():
+ item = menu.Items.Add(name, None, self.on_item_click)
+ if self.joint_type and name == self.joint_type.__name__:
+ item.Checked = True
+
+ def on_item_click(self, sender, event_info):
+ self.clicked = True
+ self.joint_type = self.classes[str(sender)]
+ rename_gh_output(self.joint_type.__name__, 0, ghenv)
+ manage_dynamic_params(self.arg_names(), ghenv, rename_count=0, permanent_param_count=0)
+ ghenv.Component.ExpireSolution(True)
+
+ - GhPython provides a Python script component
+ - true
+ - true
+ -
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALDAAACwwBP0AiyAAAAUBJREFUSEu1kyFuAzEQRXOEhFuyFV8gUq7QA5SEFxWXhQYGWaVlxUXhIQGGBb1ApJyg6g1cfWlmNZpxtt7EBU+7Gs/8r/3jnZVSZv+JKYDowyn6UIhXfT4FKRqiD/Pow06IMy96sBUWf6yI1rhEH1ZaZAw2wKAWu8ZBi4zBBjJz8E5xIbYvdXbSImOwwUGJzLkBkfQw0IvtboBIpAi+CPF0i0jvYIybltx6TX9uuqZkgqxrkYAn7nMpP7uUC7ER9U2tbhzJTO7E/MUu5SMJnV3KCwLvqH3IXiPeAgl+k+AbgXfUFncbAJfyVkTCbHWfGZyCS/lTiB/1OTCFKbiU98Jgr8+BKbTiUl5XIlrrPjPYiogHz+Fd95nBFtSC8SUP1xZthv/CpbwUV3TIXV3V5T0GHMe5csY/23CjjEBvTKE3vzBOrcjlhJXzAAAAAElFTkSuQmCC
+
+ - false
+ - 4b75bd16-28c5-4eff-8a7a-4e93bb804e8c
+ - true
+ - true
+ - CT: X Topological Joint Rules
+ - X_Topo_Joint
+
+
+
+
+ -
+ 1383
+ 1547
+ 187
+ 44
+
+ -
+ 1477
+ 1569
+
+
+
+
+
+ - 2
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+ - 1
+ - 8ec86459-bf01-4409-baee-174d0d2b13d0
+
+
+
+
+ - flip_lap_side
+ - 441ab140-096b-48fc-9343-101e952d3710
+ - flip_lap_side
+ - flip_lap_side
+ - true
+ - 0
+
+
+
+
+ -
+ 1385
+ 1549
+ 77
+ 20
+
+ -
+ 1425
+ 1559
+
+
+
+
+
+
+
+ - Script input cut_plane_bias.
+ - 8fd480bd-7940-4f8d-8495-522bc62e232a
+ - cut_plane_bias
+ - cut_plane_bias
+ - true
+ - 0
+
+
+
+
+ -
+ 1385
+ 1569
+ 77
+ 20
+
+ -
+ 1425
+ 1579
+
+
+
+
+
+
+
+ - Script output XHalfLapJoint.
+ - d3300e29-859b-48e3-9c4d-cb903ab46c0c
+ - XHalfLapJoint
+ - XHalfLapJoint
+ - false
+ - 0
+
+
+
+
+ -
+ 1492
+ 1549
+ 76
+ 40
+
+ -
+ 1530
+ 1569
+
+
+
+
+
+
+
+
+
+
+
@@ -7804,7 +10466,7 @@ class L_TopologyJointRule(component):
-
- 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
+ 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
diff --git a/examples/model/0001_model.py b/examples/model/0001_model.py
index 389134474..089ae2514 100644
--- a/examples/model/0001_model.py
+++ b/examples/model/0001_model.py
@@ -7,7 +7,7 @@
from compas_timber.elements import Beam
from compas_timber.connections import LMiterJoint
from compas_timber.connections import TButtJoint
-from compas_timber.connections import THalfLapJoint
+from compas_timber.connections import TLapJoint
HERE = os.path.dirname(__file__)
@@ -43,7 +43,7 @@ def create_viewer():
LMiterJoint.create(model, beams[0], beams[5])
# Assign joints - Inner - Inner
-THalfLapJoint.create(model, beams[2], beams[1])
+TLapJoint.create(model, beams[2], beams[1])
# Assign joints - Frame - Inner
diff --git a/src/compas_timber/connections/__init__.py b/src/compas_timber/connections/__init__.py
index 76258bae2..328e35389 100644
--- a/src/compas_timber/connections/__init__.py
+++ b/src/compas_timber/connections/__init__.py
@@ -1,6 +1,6 @@
from .joint import Joint
from .l_butt import LButtJoint
-from .l_halflap import LHalfLapJoint
+from .l_lap import LLapJoint
from .l_miter import LMiterJoint
from .l_french_ridge_lap import LFrenchRidgeLapJoint
from .lap_joint import LapJoint
@@ -11,11 +11,13 @@
from .t_butt import TButtJoint
from .t_step_joint import TStepJoint
from .t_birdsmouth import TBirdsmouthJoint
-from .t_halflap import THalfLapJoint
-from .x_halflap import XHalfLapJoint
+from .t_lap import TLapJoint
+from .x_lap import XLapJoint
from .t_dovetail import TDovetailJoint
from .t_tenon_mortise import TenonMortiseJoint
from .ball_node import BallNodeJoint
+from .utilities import beam_ref_side_incidence
+from .utilities import beam_ref_side_incidence_with_vector
__all__ = [
"Joint",
@@ -26,9 +28,9 @@
"TStepJoint",
"TBirdsmouthJoint",
"LMiterJoint",
- "XHalfLapJoint",
- "THalfLapJoint",
- "LHalfLapJoint",
+ "XLapJoint",
+ "TLapJoint",
+ "LLapJoint",
"NullJoint",
"LFrenchRidgeLapJoint",
"JointTopology",
@@ -37,4 +39,6 @@
"TDovetailJoint",
"BallNodeJoint",
"TenonMortiseJoint",
+ "beam_ref_side_incidence",
+ "beam_ref_side_incidence_with_vector",
]
diff --git a/src/compas_timber/connections/l_butt.py b/src/compas_timber/connections/l_butt.py
index 251bce416..b318aadf8 100644
--- a/src/compas_timber/connections/l_butt.py
+++ b/src/compas_timber/connections/l_butt.py
@@ -176,8 +176,15 @@ def add_features(self):
# apply the pocket on the cross beam
if self.mill_depth:
cross_cutting_plane = self.main_beam.ref_sides[self.main_beam_ref_side_index]
- lap_width = self.main_beam.height if self.main_beam_ref_side_index % 2 == 0 else self.main_beam.width
- cross_feature = Lap.from_plane_and_beam(cross_cutting_plane, self.cross_beam, lap_width, self.mill_depth, self.cross_beam_ref_side_index)
+ lap_width = self.main_beam.get_dimensions_relative_to_side(self.main_beam_ref_side_index)[1]
+ cross_feature = Lap.from_plane_and_beam(
+ cross_cutting_plane,
+ self.cross_beam,
+ lap_width,
+ self.mill_depth,
+ is_pocket=True,
+ ref_side_index=self.cross_beam_ref_side_index,
+ )
self.cross_beam.add_features(cross_feature)
self.features.append(cross_feature)
diff --git a/src/compas_timber/connections/l_french_ridge_lap.py b/src/compas_timber/connections/l_french_ridge_lap.py
index 106587c1f..b95e23eed 100644
--- a/src/compas_timber/connections/l_french_ridge_lap.py
+++ b/src/compas_timber/connections/l_french_ridge_lap.py
@@ -1,16 +1,13 @@
-from compas.tolerance import TOL
-
-from compas_timber.connections.utilities import beam_ref_side_incidence
-from compas_timber.connections.utilities import beam_ref_side_incidence_with_vector
from compas_timber.errors import BeamJoiningError
from compas_timber.fabrication import FrenchRidgeLap
-from .joint import Joint
+from .lap_joint import LapJoint
from .solver import JointTopology
+from .utilities import are_beams_coplanar
-class LFrenchRidgeLapJoint(Joint):
- """Represents an L-FrenchRidgeLap type joint which joins two beams in their ends, by lapping them with a ridge.
+class LFrenchRidgeLapJoint(LapJoint):
+ """Represents an L-FrenchRidgeLap type joint which joins two beams at their ends, by lapping them with a ridge.
The joint can only be created between two beams that are aligned and have the same dimensions.
This joint type is compatible with beams in L topology.
@@ -19,89 +16,39 @@ class LFrenchRidgeLapJoint(Joint):
Parameters
----------
- beam_a : :class:`~compas_timber.parts.Beam`
- First beam to be joined.
- beam_b : :class:`~compas_timber.parts.Beam`
- Second beam to be joined.
+ main_beam : :class:`~compas_timber.elements.Beam`
+ The main beam to be joined.
+ cross_beam : :class:`~compas_timber.elements.Beam`
+ The cross beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
drillhole_diam : float
Diameter of the drill hole to be made in the joint.
- flip_beams : bool
- If True, the beams will be flipped in the joint. Default is False.
Attributes
----------
- beam_a : :class:`~compas_timber.parts.Beam`
- First beam to be joined.
- beam_b : :class:`~compas_timber.parts.Beam`
- Second beam to be joined.
+ main_beam : :class:`~compas_timber.elements.Beam`
+ The main beam to be joined.
+ cross_beam : :class:`~compas_timber.elements.Beam`
+ The cross beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
drillhole_diam : float
Diameter of the drill hole to be made in the joint.
- flip_beams : bool
- If True, the beams will be flipped in the joint. Default is False.
"""
SUPPORTED_TOPOLOGY = JointTopology.TOPO_L
- @property
- def __data__(self):
- data = super(LFrenchRidgeLapJoint, self).__data__
- data["beam_a_guid"] = self.beam_a_guid
- data["beam_b_guid"] = self.beam_b_guid
- data["drillhole_diam"] = self.drillhole_diam
- data["flip_beams"] = self.flip_beams
- return data
-
- def __init__(self, beam_a=None, beam_b=None, drillhole_diam=None, flip_beams=None, **kwargs):
- super(LFrenchRidgeLapJoint, self).__init__(**kwargs)
- self.beam_a = beam_a
- self.beam_b = beam_b
- self.beam_a_guid = kwargs.get("beam_a_guid", None) or str(beam_a.guid)
- self.beam_b_guid = kwargs.get("beam_b_guid", None) or str(beam_b.guid)
+ def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, drillhole_diam=None, **kwargs):
+ super(LFrenchRidgeLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, drillhole_diam, **kwargs)
self.drillhole_diam = drillhole_diam
- self.flip_beams = flip_beams
- self.features = []
-
- @property
- def elements(self):
- return [self.beam_a, self.beam_b]
-
- @property
- def beam_a_ref_side_index(self):
- cross_vector = self.beam_a.centerline.direction.cross(self.beam_b.centerline.direction)
- ref_side_dict = beam_ref_side_incidence_with_vector(self.beam_a, cross_vector, ignore_ends=True)
- if self.flip_beams:
- return max(ref_side_dict, key=ref_side_dict.get)
- return min(ref_side_dict, key=ref_side_dict.get)
-
- @property
- def beam_b_ref_side_index(self):
- cross_vector = self.beam_a.centerline.direction.cross(self.beam_b.centerline.direction)
- ref_side_dict = beam_ref_side_incidence_with_vector(self.beam_b, cross_vector, ignore_ends=True)
- if self.flip_beams:
- return min(ref_side_dict, key=ref_side_dict.get)
- return max(ref_side_dict, key=ref_side_dict.get)
-
- @property
- def cutting_plane_a(self):
- # the plane that cuts beam_b
- ref_side_dict = beam_ref_side_incidence(self.beam_b, self.beam_a, ignore_ends=True)
- ref_side_index = max(ref_side_dict, key=ref_side_dict.get)
- return self.beam_a.ref_sides[ref_side_index]
-
- @property
- def cutting_plane_b(self):
- # the plane that cuts beam_a
- ref_side_dict = beam_ref_side_incidence(self.beam_a, self.beam_b, ignore_ends=True)
- ref_side_index = max(ref_side_dict, key=ref_side_dict.get)
- return self.beam_b.ref_sides[ref_side_index]
def add_extensions(self):
"""Calculates and adds the necessary extensions to the beams.
- This method is called during the `Model.process_joinery()` process after the joint
- has been instantiated and added to the model.
+ This method is automatically called when joint is created by the call to `Joint.create()`.
Raises
------
@@ -109,20 +56,20 @@ def add_extensions(self):
If the extension could not be calculated.
"""
+ assert self.main_beam and self.cross_beam
- assert self.beam_a and self.beam_b
- start_a, start_b = None, None
+ start_main, start_cross = None, None
try:
- start_a, end_a = self.beam_a.extension_to_plane(self.cutting_plane_b)
- start_b, end_b = self.beam_b.extension_to_plane(self.cutting_plane_a)
+ start_main, end_main = self.main_beam.extension_to_plane(self.main_cutting_plane)
+ start_cross, end_cross = self.cross_beam.extension_to_plane(self.cross_cutting_plane)
except AttributeError as ae:
# I want here just the plane that caused the error
- geometries = [self.cutting_plane_a] if start_a is not None else [self.cutting_plane_b]
+ geometries = [self.cross_cutting_plane] if start_main is not None else [self.main_cutting_plane]
raise BeamJoiningError(self.elements, self, debug_info=str(ae), debug_geometries=geometries)
except Exception as ex:
raise BeamJoiningError(self.elements, self, debug_info=str(ex))
- self.beam_a.add_blank_extension(start_a, end_a, self.guid)
- self.beam_b.add_blank_extension(start_b, end_b, self.guid)
+ self.main_beam.add_blank_extension(start_main, end_main, self.main_beam_guid)
+ self.cross_beam.add_blank_extension(start_cross, end_cross, self.cross_beam_guid)
def add_features(self):
"""Adds the necessary features to the beams.
@@ -132,38 +79,39 @@ def add_features(self):
have been added via `Joint.add_extensions()`.
"""
- assert self.beam_a and self.beam_b
+ assert self.main_beam and self.cross_beam
if self.features:
- self.beam_a.remove_features(self.features)
- self.beam_b.remove_features(self.features)
+ self.main_beam.remove_features(self.features)
+ self.cross_beam.remove_features(self.features)
- frl_a = FrenchRidgeLap.from_beam_beam_and_plane(self.beam_a, self.beam_b, self.cutting_plane_b, self.drillhole_diam, self.beam_a_ref_side_index)
- frl_b = FrenchRidgeLap.from_beam_beam_and_plane(self.beam_b, self.beam_a, self.cutting_plane_a, self.drillhole_diam, self.beam_b_ref_side_index)
- self.beam_a.add_features(frl_a)
- self.beam_b.add_features(frl_b)
- self.features = [frl_a, frl_b]
+ main_frl_feature = FrenchRidgeLap.from_beam_beam_and_plane(self.main_beam, self.cross_beam, self.main_cutting_plane, self.drillhole_diam, self.main_ref_side_index)
+ cross_frl_feature = FrenchRidgeLap.from_beam_beam_and_plane(self.cross_beam, self.main_beam, self.cross_cutting_plane, self.drillhole_diam, self.cross_ref_side_index)
+ # store the features to the beams
+ self.main_beam.add_features(main_frl_feature)
+ self.cross_beam.add_features(cross_frl_feature)
+ # register the features in the joint
+ self.features = [main_frl_feature, cross_frl_feature]
def check_elements_compatibility(self):
"""Checks if the elements are compatible for the creation of the joint.
+ Compared to the LapJoint's `check_elements_compatibility` method, this one additionally checks if dimensions of the beams match.
+
Raises
------
BeamJoiningError
If the elements are not compatible for the creation of the joint.
-
"""
- # check if the beams are aligned
- cross_vect = self.beam_a.centerline.direction.cross(self.beam_b.centerline.direction)
- for beam in self.elements:
- beam_normal = beam.frame.normal.unitized()
- dot = abs(beam_normal.dot(cross_vect.unitized()))
- if not (TOL.is_zero(dot) or TOL.is_close(dot, 1)):
- raise BeamJoiningError(self.elements, self, debug_info="The two beams are not aligned to create a French Ridge Lap joint.")
-
+ if not are_beams_coplanar(*self.elements):
+ raise BeamJoiningError(
+ beams=self.elements,
+ joint=self,
+ debug_info="The two beams are not coplanar to create a Lap joint.",
+ )
# calculate widths and heights of the beams
dimensions = []
- ref_side_indices = [self.beam_a_ref_side_index, self.beam_b_ref_side_index]
+ ref_side_indices = [self.main_ref_side_index, self.cross_ref_side_index]
for i, beam in enumerate(self.elements):
width = beam.side_as_surface(ref_side_indices[i]).ysize
height = beam.height if ref_side_indices[i] % 2 == 0 else beam.width
diff --git a/src/compas_timber/connections/l_halflap.py b/src/compas_timber/connections/l_halflap.py
deleted file mode 100644
index f9cfead37..000000000
--- a/src/compas_timber/connections/l_halflap.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from compas.geometry import Frame
-
-from compas_timber.elements import CutFeature
-from compas_timber.elements import MillVolume
-from compas_timber.errors import BeamJoiningError
-
-from .lap_joint import LapJoint
-from .solver import JointTopology
-
-
-class LHalfLapJoint(LapJoint):
- """Represents a L-Lap type joint which joins the ends of two beams,
- trimming the main beam.
-
- This joint type is compatible with beams in L topology.
-
- Please use `LHalfLapJoint.create()` to properly create an instance of this class and associate it with a model.
-
- Parameters
- ----------
- main_beam : :class:`~compas_timber.parts.Beam`
- The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
- The cross beam to be joined.
- flip_lap_side : bool
- If True, the lap is flipped to the other side of the beams.
- cut_plane_bias : float
- Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
-
- Attributes
- ----------
- beams : list(:class:`~compas_timber.parts.Beam`)
- The beams joined by this joint.
- main_beam : :class:`~compas_timber.parts.Beam`
- The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
- The cross beam to be joined.
- main_beam_key : str
- The key of the main beam.
- cross_beam_key : str
- The key of the cross beam.
- features : list(:class:`~compas_timber.parts.Feature`)
- The features created by this joint.
- joint_type : str
- A string representation of this joint's type.
-
- """
-
- SUPPORTED_TOPOLOGY = JointTopology.TOPO_L
-
- def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
- super(LHalfLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias, **kwargs)
-
- def add_extensions(self):
- """Calculates and adds the necessary extensions to the beams.
-
- This method is automatically called when joint is created by the call to `Joint.create()`.
-
- Raises
- ------
- BeamJoiningError
- If the extension could not be calculated.
-
- """
- assert self.main_beam and self.cross_beam
- try:
- main_cutting_frame = self.get_main_cutting_frame()
- cross_cutting_frame = self.get_cross_cutting_frame()
- except Exception as ex:
- raise BeamJoiningError(beams=self.elements, joint=self, debug_info=str(ex))
-
- start_main, end_main = self.main_beam.extension_to_plane(main_cutting_frame)
- start_cross, end_cross = self.cross_beam.extension_to_plane(cross_cutting_frame)
-
- extension_tolerance = 0.01 # TODO: this should be proportional to the unit used
- self.main_beam.add_blank_extension(start_main + extension_tolerance, end_main + extension_tolerance, self.guid)
- self.cross_beam.add_blank_extension(start_cross + extension_tolerance, end_cross + extension_tolerance, self.guid)
-
- def add_features(self):
- assert self.main_beam and self.cross_beam
-
- try:
- main_cutting_frame = self.get_main_cutting_frame()
- cross_cutting_frame = self.get_cross_cutting_frame()
- negative_brep_main_beam, negative_brep_cross_beam = self._create_negative_volumes()
- except Exception as ex:
- raise BeamJoiningError(beams=self.elements, joint=self, debug_info=str(ex))
-
- main_volume = MillVolume(negative_brep_main_beam)
- cross_volume = MillVolume(negative_brep_cross_beam)
-
- self.main_beam.add_features(main_volume)
- self.cross_beam.add_features(cross_volume)
-
- f_cross = CutFeature(cross_cutting_frame)
- self.cross_beam.add_features(f_cross)
-
- trim_frame = Frame(main_cutting_frame.point, main_cutting_frame.xaxis, -main_cutting_frame.yaxis)
- f_main = CutFeature(trim_frame)
- self.main_beam.add_features(f_main)
-
- self.features = [main_volume, cross_volume, f_main, f_cross]
diff --git a/src/compas_timber/connections/l_lap.py b/src/compas_timber/connections/l_lap.py
new file mode 100644
index 000000000..60d0499a7
--- /dev/null
+++ b/src/compas_timber/connections/l_lap.py
@@ -0,0 +1,146 @@
+from compas.geometry import Frame
+
+from compas_timber.elements.features import CutFeature
+from compas_timber.elements.features import MillVolume
+from compas_timber.errors import BeamJoiningError
+from compas_timber.fabrication import JackRafterCut
+from compas_timber.fabrication import Lap
+
+from .lap_joint import LapJoint
+from .solver import JointTopology
+from .utilities import are_beams_coplanar
+
+
+class LLapJoint(LapJoint):
+ """Represents an L-Lap type joint which joins the ends of two beams with a lap.
+
+ This joint type is compatible with beams in L topology.
+
+ Please use `LLapJoint.create()` to properly create an instance of this class and associate it with a model.
+
+ Parameters
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The first beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The second beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+
+ Attributes
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The first beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The second beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+ """
+
+ SUPPORTED_TOPOLOGY = JointTopology.TOPO_L
+
+ def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
+ super(LLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias, **kwargs)
+
+ def add_extensions(self):
+ """Calculates and adds the necessary extensions to the beams.
+
+ This method is automatically called when joint is created by the call to `Joint.create()`.
+
+ Raises
+ ------
+ BeamJoiningError
+ If the extension could not be calculated.
+
+ """
+ assert self.main_beam and self.cross_beam
+
+ start_main, start_cross = None, None
+ try:
+ start_main, end_main = self.main_beam.extension_to_plane(self.main_cutting_plane)
+ start_cross, end_cross = self.cross_beam.extension_to_plane(self.cross_cutting_plane)
+ except AttributeError as ae:
+ # I want here just the plane that caused the error
+ geometries = [self.cross_cutting_plane] if start_main is not None else [self.main_cutting_plane]
+ raise BeamJoiningError(self.elements, self, debug_info=str(ae), debug_geometries=geometries)
+ except Exception as ex:
+ raise BeamJoiningError(self.elements, self, debug_info=str(ex))
+ self.main_beam.add_blank_extension(start_main, end_main, self.main_beam_guid)
+ self.cross_beam.add_blank_extension(start_cross, end_cross, self.cross_beam_guid)
+
+ def add_features(self):
+ """Adds the required joint features to both beams.
+
+ This method is automatically called when joint is created by the call to `Joint.create()`.
+
+ """
+ assert self.main_beam and self.cross_beam
+
+ if self.features:
+ self.main_beam.remove_features(self.features)
+ self.cross_beam.remove_features(self.features)
+
+ if are_beams_coplanar(*self.elements): # TODO: this is a temporal solution to allow the vizualization of non-coplanar lap joints.
+ # calculate the lap length and depth for each beam
+ main_lap_length, cross_lap_length = self._get_lap_lengths()
+ main_lap_depth, cross_lap_depth = self._get_lap_depths()
+
+ ## main_beam
+ # lap feature on main_beam
+ main_lap_feature = Lap.from_plane_and_beam(
+ self.main_cutting_plane,
+ self.main_beam,
+ main_lap_length,
+ main_lap_depth,
+ ref_side_index=self.main_ref_side_index,
+ )
+ # cutoff feature for main_beam
+ main_cut_feature = JackRafterCut.from_plane_and_beam(self.main_cutting_plane, self.main_beam)
+ main_features = [main_cut_feature, main_lap_feature]
+ self.main_beam.add_features(main_features)
+ self.features.extend(main_features)
+
+ ## cross_beam
+ # lap feature on cross_beam
+ cross_lap_feature = Lap.from_plane_and_beam(
+ self.cross_cutting_plane,
+ self.cross_beam,
+ cross_lap_length,
+ cross_lap_depth,
+ ref_side_index=self.cross_ref_side_index,
+ )
+ # cutoff feature for beam_b
+ cross_cut_feature = JackRafterCut.from_plane_and_beam(self.cross_cutting_plane, self.cross_beam)
+ cross_features = [cross_cut_feature, cross_lap_feature]
+ self.cross_beam.add_features(cross_features)
+ self.features.extend(cross_features)
+
+ else:
+ # TODO: this is a temporal solution to avoid the error if beams are not coplanar and allow the visualization of the joint.
+ # TODO: this solution does not generate machining features and therefore will be ignored in the fabrication process.
+ # TODO: once the Lap BTLx processing implimentation allows for non-coplanar beams, this should be removed.
+ try:
+ main_cutting_frame = self.get_main_cutting_frame()
+ cross_cutting_frame = self.get_cross_cutting_frame()
+ negative_brep_main_beam, negative_brep_cross_beam = self._create_negative_volumes()
+ except Exception as ex:
+ raise BeamJoiningError(beams=self.elements, joint=self, debug_info=str(ex))
+
+ main_volume = MillVolume(negative_brep_main_beam)
+ cross_volume = MillVolume(negative_brep_cross_beam)
+
+ self.main_beam.add_features(main_volume)
+ self.cross_beam.add_features(cross_volume)
+
+ f_cross = CutFeature(cross_cutting_frame)
+ self.cross_beam.add_features(f_cross)
+
+ trim_frame = Frame(main_cutting_frame.point, main_cutting_frame.xaxis, -main_cutting_frame.yaxis)
+ f_main = CutFeature(trim_frame)
+ self.main_beam.add_features(f_main)
+
+ self.features = [main_volume, cross_volume, f_main, f_cross]
diff --git a/src/compas_timber/connections/lap_joint.py b/src/compas_timber/connections/lap_joint.py
index 8c57a53b8..f4b642b11 100644
--- a/src/compas_timber/connections/lap_joint.py
+++ b/src/compas_timber/connections/lap_joint.py
@@ -1,3 +1,5 @@
+import warnings
+
from compas.geometry import Frame
from compas.geometry import Line
from compas.geometry import Plane
@@ -8,7 +10,12 @@
from compas.geometry import intersection_line_plane
from compas.geometry import intersection_plane_plane_plane
+from compas_timber.errors import BeamJoiningError
+
from .joint import Joint
+from .utilities import are_beams_coplanar
+from .utilities import beam_ref_side_incidence
+from .utilities import beam_ref_side_incidence_with_vector
class LapJoint(Joint):
@@ -18,9 +25,9 @@ class LapJoint(Joint):
Parameters
----------
- main_beam : :class:`~compas_timber.parts.Beam`
+ main_beam : :class:`~compas_timber.elements.Beam`
The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
+ cross_beam : :class:`~compas_timber.elements.Beam`
The cross beam to be joined.
flip_lap_side : bool
If True, the lap is flipped to the other side of the beams.
@@ -29,9 +36,11 @@ class LapJoint(Joint):
Attributes
----------
- main_beam : :class:`~compas_timber.parts.Beam`
+ elements : list of :class:`~compas_timber.elements.Beam`
+ The beams to be joined.
+ main_beam : :class:`~compas_timber.elements.Beam`
The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
+ cross_beam : :class:`~compas_timber.elements.Beam`
The cross beam to be joined.
flip_lap_side : bool
If True, the lap is flipped to the other side of the beams.
@@ -49,24 +58,125 @@ def __data__(self):
data["cut_plane_bias"] = self.cut_plane_bias
return data
- def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5):
- super(LapJoint, self).__init__()
+ def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
+ super(LapJoint, self).__init__(**kwargs)
self.main_beam = main_beam
self.cross_beam = cross_beam
+ self.main_beam_guid = kwargs.get("main_beam_guid", None) or str(main_beam.guid)
+ self.cross_beam_guid = kwargs.get("cross_beam_guid", None) or str(cross_beam.guid)
+
self.flip_lap_side = flip_lap_side
self.cut_plane_bias = cut_plane_bias
- self.main_beam_guid = str(main_beam.guid) if main_beam else None
- self.cross_beam_guid = str(cross_beam.guid) if cross_beam else None
self.features = []
+ self._main_ref_side_index = None
+ self._cross_ref_side_index = None
+ self._main_cutting_plane = None
+ self._cross_cutting_plane = None
+
@property
def elements(self):
return [self.main_beam, self.cross_beam]
+ @property
+ def main_ref_side_index(self):
+ """The reference side index of the main beam."""
+ if self._main_ref_side_index is None:
+ self._main_ref_side_index = self._get_beam_ref_side_index(self.main_beam, self.cross_beam, self.flip_lap_side)
+ return self._main_ref_side_index
+
+ @property
+ def cross_ref_side_index(self):
+ """The reference side index of the cross beam."""
+ if self._cross_ref_side_index is None:
+ self._cross_ref_side_index = self._get_beam_ref_side_index(self.cross_beam, self.main_beam, self.flip_lap_side)
+ return self._cross_ref_side_index
+
+ @property
+ def main_cutting_plane(self):
+ """The face of the cross beam that cuts the main beam as a plane."""
+ if self._main_cutting_plane is None:
+ self._main_cutting_plane = self._get_cutting_plane(self.cross_beam, self.main_beam)
+ return self._main_cutting_plane
+
+ @property
+ def cross_cutting_plane(self):
+ """The face of the main beam that cuts the cross beam as a plane."""
+ if self._cross_cutting_plane is None:
+ self._cross_cutting_plane = self._get_cutting_plane(self.main_beam, self.cross_beam)
+ return self._cross_cutting_plane
+
+ @staticmethod
+ def _get_beam_ref_side_index(beam_a, beam_b, flip):
+ """Returns the reference side index of beam_a with respect to beam_b."""
+ cross_vector = beam_a.centerline.direction.cross(beam_b.centerline.direction)
+ ref_side_dict = beam_ref_side_incidence_with_vector(beam_a, cross_vector, ignore_ends=True)
+ if flip:
+ return max(ref_side_dict, key=ref_side_dict.get)
+ return min(ref_side_dict, key=ref_side_dict.get)
+
+ @staticmethod
+ def _get_cutting_plane(beam_a, beam_b):
+ """Returns the plane from beam_b that cuts beam_a."""
+ ref_side_dict = beam_ref_side_incidence(beam_b, beam_a, ignore_ends=True)
+ ref_side_index = max(ref_side_dict, key=ref_side_dict.get)
+ return Plane.from_frame(beam_a.ref_sides[ref_side_index])
+
+ def check_elements_compatibility(self):
+ """Checks if the elements are compatible for the creation of the joint.
+
+ Raises
+ ------
+ UserWarning
+ If the elements are not compatible for the creation of the joint.
+ """
+ # TODO: This warning should be providing more information to the caller in regards to the affected beams and joints.
+ if not are_beams_coplanar(*self.elements):
+ warnings.warn("The beams are not coplanar, therefore BTLxProcessings will not be generated for this joint", UserWarning)
+
def restore_beams_from_keys(self, model):
"""After de-serialization, restores references to the main and cross beams saved in the model."""
- self.main_beam = model.beam_by_guid(self.main_beam_guid)
- self.cross_beam = model.beam_by_guid(self.cross_beam_guid)
+ self.main_beam = model.element_by_guid(self.main_beam_guid)
+ self.cross_beam = model.element_by_guid(self.cross_beam_guid)
+
+ def _get_lap_lengths(self):
+ lap_length_main = self.cross_beam.side_as_surface(self.cross_ref_side_index).ysize
+ lap_length_cross = self.main_beam.side_as_surface(self.main_ref_side_index).ysize
+ return lap_length_main, lap_length_cross
+
+ def _get_lap_depths(self):
+ """Returns the lap depths from the distance between the two lap faces and the bias value."""
+ main_frame = self.main_beam.ref_sides[self.main_ref_side_index]
+ cross_frame = self.cross_beam.ref_sides[self.cross_ref_side_index]
+
+ vect = main_frame.point - cross_frame.point
+ cross_vect = self.main_beam.centerline.direction.cross(self.cross_beam.centerline.direction)
+ cross_vect.unitize()
+ lap_depth = abs(cross_vect.dot(vect))
+
+ main_lap_depth = lap_depth * self.cut_plane_bias
+ cross_lap_depth = lap_depth * (1 - self.cut_plane_bias)
+
+ main_height = self.main_beam.get_dimensions_relative_to_side(self.main_ref_side_index)[1]
+ cross_height = self.cross_beam.get_dimensions_relative_to_side(self.cross_ref_side_index)[1]
+
+ if main_lap_depth >= main_height or cross_lap_depth >= cross_height: # TODO: should we instead bypass the bias and use the max. possible depth?
+ raise BeamJoiningError(beams=self.elements, joint=self, debug_info="Lap depth is bigger than the beam's height. Consider revising the bias.")
+ return main_lap_depth, cross_lap_depth
+
+ def get_main_cutting_frame(self):
+ assert self.elements
+ beam_a, beam_b = self.elements
+
+ _, cfr = self.get_face_most_towards_beam(beam_a, beam_b)
+ cfr = Frame(cfr.point, cfr.yaxis, cfr.xaxis) # flip normal towards the inside of main beam
+ return cfr
+
+ def get_cross_cutting_frame(self):
+ assert self.elements
+ beam_a, beam_b = self.elements
+ _, cfr = self.get_face_most_towards_beam(beam_b, beam_a)
+ return cfr
@staticmethod
def _sort_beam_planes(beam, cutplane_vector):
@@ -111,20 +221,6 @@ def _create_polyhedron(plane_a, lines, bias): # Hexahedron from 2 Planes and 4
],
)
- def get_main_cutting_frame(self):
- assert self.elements
- beam_a, beam_b = self.elements
-
- _, cfr = self.get_face_most_towards_beam(beam_a, beam_b)
- cfr = Frame(cfr.point, cfr.yaxis, cfr.xaxis) # flip normal towards the inside of main beam
- return cfr
-
- def get_cross_cutting_frame(self):
- assert self.elements
- beam_a, beam_b = self.elements
- _, cfr = self.get_face_most_towards_beam(beam_b, beam_a)
- return cfr
-
def _create_negative_volumes(self):
assert self.elements
beam_a, beam_b = self.elements
diff --git a/src/compas_timber/connections/t_butt.py b/src/compas_timber/connections/t_butt.py
index 2d2ef8835..d42180e99 100644
--- a/src/compas_timber/connections/t_butt.py
+++ b/src/compas_timber/connections/t_butt.py
@@ -155,13 +155,15 @@ def add_features(self):
# apply the pocket on the cross beam
if self.mill_depth:
cross_cutting_plane = self.main_beam.ref_sides[self.main_beam_ref_side_index]
- lap_width = self.main_beam.height if self.main_beam_ref_side_index % 2 == 0 else self.main_beam.width
+ lap_length = self.main_beam.get_dimensions_relative_to_side(self.main_beam_ref_side_index)[1]
+
cross_feature = Lap.from_plane_and_beam(
cross_cutting_plane,
self.cross_beam,
- lap_width,
+ lap_length,
self.mill_depth,
- self.cross_beam_ref_side_index,
+ is_pocket=True,
+ ref_side_index=self.cross_beam_ref_side_index,
)
self.cross_beam.add_features(cross_feature)
self.features.append(cross_feature)
diff --git a/src/compas_timber/connections/t_halflap.py b/src/compas_timber/connections/t_halflap.py
deleted file mode 100644
index d21110bc0..000000000
--- a/src/compas_timber/connections/t_halflap.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from compas.geometry import Frame
-
-from compas_timber.connections.lap_joint import LapJoint
-from compas_timber.elements import CutFeature
-from compas_timber.elements import MillVolume
-from compas_timber.errors import BeamJoiningError
-
-from .solver import JointTopology
-
-
-class THalfLapJoint(LapJoint):
- """Represents a T-Lap type joint which joins the end of a beam along the length of another beam,
- trimming the main beam.
-
- This joint type is compatible with beams in T topology.
-
- Please use `THalfLapJoint.create()` to properly create an instance of this class and associate it with a model.
-
- Parameters
- ----------
- main_beam : :class:`~compas_timber.parts.Beam`
- The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
- The cross beam to be joined.
- flip_lap_side : bool
- If True, the lap is flipped to the other side of the beams.
- cut_plane_bias : float
- Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
-
- """
-
- SUPPORTED_TOPOLOGY = JointTopology.TOPO_T
-
- def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5):
- super(THalfLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias)
-
- def add_extensions(self):
- """Calculates and adds the necessary extensions to the beams.
-
- This method is automatically called when joint is created by the call to `Joint.create()`.
-
- Raises
- ------
- BeamJoiningError
- If the extension could not be calculated.
-
- """
- assert self.main_beam and self.cross_beam # should never happen
-
- main_cutting_frame = None
- try:
- main_cutting_frame = self.get_main_cutting_frame()
- start_main, end_main = self.main_beam.extension_to_plane(main_cutting_frame)
- except AttributeError as ae:
- raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ae), debug_geometries=[main_cutting_frame])
- except Exception as ex:
- raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ex))
-
- extension_tolerance = 0.01 # TODO: this should be proportional to the unit used
- self.main_beam.add_blank_extension(start_main + extension_tolerance, end_main + extension_tolerance, self.guid)
-
- def add_features(self):
- assert self.main_beam and self.cross_beam # should never happen
-
- if self.features:
- self.main_beam.remove_features(self.features)
-
- main_cutting_frame = None
- try:
- main_cutting_frame = self.get_main_cutting_frame()
- negative_brep_main_beam, negative_brep_cross_beam = self._create_negative_volumes()
- except AttributeError as ae:
- raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ae), debug_geometries=[main_cutting_frame])
- except Exception as ex:
- raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ex))
-
- main_volume = MillVolume(negative_brep_main_beam)
- cross_volume = MillVolume(negative_brep_cross_beam)
- self.main_beam.add_features(main_volume)
- self.cross_beam.add_features(cross_volume)
-
- trim_frame = Frame(main_cutting_frame.point, main_cutting_frame.xaxis, -main_cutting_frame.yaxis)
- f_main = CutFeature(trim_frame)
- self.main_beam.add_features(f_main)
-
- self.features = [main_volume, cross_volume, f_main]
diff --git a/src/compas_timber/connections/t_lap.py b/src/compas_timber/connections/t_lap.py
new file mode 100644
index 000000000..105c3a999
--- /dev/null
+++ b/src/compas_timber/connections/t_lap.py
@@ -0,0 +1,148 @@
+from compas.geometry import Frame
+
+from compas_timber.elements.features import CutFeature
+from compas_timber.elements.features import MillVolume
+from compas_timber.errors import BeamJoiningError
+from compas_timber.fabrication import JackRafterCut
+from compas_timber.fabrication import Lap
+
+from .lap_joint import LapJoint
+from .solver import JointTopology
+from .utilities import are_beams_coplanar
+
+
+class TLapJoint(LapJoint):
+ """Represents a T-Lap type joint which joins the end of a beam along the length of another beam with a lap.
+
+ This joint type is compatible with beams in T topology.
+
+ Please use `TLapJoint.create()` to properly create an instance of this class and associate it with a model.
+
+ Parameters
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The main beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The cross beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+
+ Attributes
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The main beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The cross beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+
+ """
+
+ SUPPORTED_TOPOLOGY = JointTopology.TOPO_T
+
+ def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
+ super(TLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias, **kwargs)
+
+ def add_extensions(self):
+ """Calculates and adds the necessary extensions to the beams.
+
+ This method is automatically called when joint is created by the call to `Joint.create()`.
+
+ Raises
+ ------
+ BeamJoiningError
+ If the extension could not be calculated.
+
+ """
+ assert self.main_beam and self.cross_beam
+ try:
+ start_main, end_main = self.main_beam.extension_to_plane(self.main_cutting_plane)
+ except AttributeError as ae:
+ raise BeamJoiningError(
+ beams=self.elements,
+ joint=self,
+ debug_info=str(ae),
+ debug_geometries=[self.main_cutting_plane],
+ )
+ except Exception as ex:
+ raise BeamJoiningError(beams=self.elements, joint=self, debug_info=str(ex))
+ extension_tolerance = 0.01 # TODO: this should be proportional to the unit used
+ self.main_beam.add_blank_extension(
+ start_main + extension_tolerance,
+ end_main + extension_tolerance,
+ self.guid,
+ )
+
+ def add_features(self):
+ """Adds the required extension and trimming features to both beams.
+
+ This method is automatically called when joint is created by the call to `Joint.create()`.
+
+ """
+ assert self.main_beam and self.cross_beam
+
+ if self.features:
+ self.main_beam.remove_features(self.features)
+ self.cross_beam.remove_features(self.features)
+
+ if are_beams_coplanar(*self.elements): # TODO: this is a temporal solution to allow the vizualization of non-coplanar lap joints.
+ # calculate the lap length and depth for each beam
+ main_lap_length, cross_lap_length = self._get_lap_lengths()
+ main_lap_depth, cross_lap_depth = self._get_lap_depths()
+
+ ## cross_beam features
+ # cross Lap feature
+ cross_lap_feature = Lap.from_plane_and_beam(
+ self.cross_cutting_plane,
+ self.cross_beam,
+ cross_lap_length,
+ cross_lap_depth,
+ ref_side_index=self.cross_ref_side_index,
+ )
+ # register features to the joint
+ self.cross_beam.add_features(cross_lap_feature)
+ self.features.append(cross_lap_feature)
+
+ ## main_beam features
+ # main Lap feature
+ main_lap_feature = Lap.from_plane_and_beam(
+ self.main_cutting_plane,
+ self.main_beam,
+ main_lap_length,
+ main_lap_depth,
+ ref_side_index=self.main_ref_side_index,
+ )
+ # cutoff feature for main beam
+ main_cut_feature = JackRafterCut.from_plane_and_beam(self.main_cutting_plane, self.main_beam)
+ # register features to the joint
+ main_features = [main_lap_feature, main_cut_feature]
+ self.main_beam.add_features(main_features)
+ self.features.extend(main_features)
+
+ else:
+ # TODO: this is a temporal solution to avoid the error if beams are not coplanar and allow the visualization of the joint.
+ # TODO: this solution does not generate machining features and therefore will be ignored in the fabrication process.
+ # TODO: once the Lap BTLx processing implimentation allows for non-coplanar beams, this should be removed.
+ main_cutting_frame = None
+ try:
+ main_cutting_frame = self.get_main_cutting_frame()
+ negative_brep_main_beam, negative_brep_cross_beam = self._create_negative_volumes()
+ except AttributeError as ae:
+ raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ae), debug_geometries=[main_cutting_frame])
+ except Exception as ex:
+ raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ex))
+
+ main_volume = MillVolume(negative_brep_main_beam)
+ cross_volume = MillVolume(negative_brep_cross_beam)
+ self.main_beam.add_features(main_volume)
+ self.cross_beam.add_features(cross_volume)
+
+ trim_frame = Frame(main_cutting_frame.point, main_cutting_frame.xaxis, -main_cutting_frame.yaxis)
+ f_main = CutFeature(trim_frame)
+ self.main_beam.add_features(f_main)
+
+ self.features = [main_volume, cross_volume, f_main]
diff --git a/src/compas_timber/connections/t_step_joint.py b/src/compas_timber/connections/t_step_joint.py
index ef299e78c..9bbed6c17 100644
--- a/src/compas_timber/connections/t_step_joint.py
+++ b/src/compas_timber/connections/t_step_joint.py
@@ -97,7 +97,7 @@ def __init__(
main_width = self.main_beam.width if swap_main_dimensions else self.main_beam.height
main_height = self.main_beam.height if swap_main_dimensions else self.main_beam.width
# For the cross beam, use width or height based on the alignment
- cross_width = self.cross_beam.width if self.cross_beam_ref_side_index % 2 == 0 else self.cross_beam.height
+ cross_width = self.cross_beam.get_dimensions_relative_to_side(self.cross_beam_ref_side_index)[0]
self.start_y = (cross_width - main_width) / 2 if cross_width > main_width else 0.0
self.notch_limited = False
diff --git a/src/compas_timber/connections/t_tenon_mortise.py b/src/compas_timber/connections/t_tenon_mortise.py
index 23ea3a003..5f33d1e63 100644
--- a/src/compas_timber/connections/t_tenon_mortise.py
+++ b/src/compas_timber/connections/t_tenon_mortise.py
@@ -157,8 +157,7 @@ def tenon_shape(self):
def set_default_values(self):
"""Sets default values for attributes if they are not provided."""
- width = self.main_beam.width if self.main_beam_ref_side_index % 2 == 0 else self.main_beam.height
- height = self.main_beam.height if self.main_beam_ref_side_index % 2 == 0 else self.main_beam.width
+ width, height = self.main_beam.get_dimensions_relative_to_side(self.main_beam_ref_side_index)
# assign default values
self.start_y = self.start_y or 0.0
self.start_depth = self.start_depth or 0.0
diff --git a/src/compas_timber/connections/utilities.py b/src/compas_timber/connections/utilities.py
index d7edef055..61a147e25 100644
--- a/src/compas_timber/connections/utilities.py
+++ b/src/compas_timber/connections/utilities.py
@@ -168,16 +168,13 @@ def are_beams_coplanar(beam_a, beam_b, tol=TOL):
cross_vector = beam_a.centerline.direction.cross(beam_b.centerline.direction)
cross_vector.unitize()
- # Check dot products of the cross product with the normals of both beams' frames
- dot_with_beam_b_normal = abs(cross_vector.dot(beam_b.frame.normal))
- dot_with_beam_a_normal = abs(cross_vector.dot(beam_a.frame.normal))
-
- # Check if both dot products are close to 0 or 1 (indicating coplanarity)
- is_beam_a_normal_coplanar = tol.is_close(dot_with_beam_a_normal, 1.0) or tol.is_zero(dot_with_beam_a_normal)
- is_beam_b_normal_coplanar = tol.is_close(dot_with_beam_b_normal, 1.0) or tol.is_zero(dot_with_beam_b_normal)
-
- # Return True if both beams are coplanar
- return is_beam_a_normal_coplanar and is_beam_b_normal_coplanar
+ for beam in [beam_a, beam_b]:
+ # Check if the cross product is parallel to the normal of the beam's frame
+ dot_with_beam_normal = abs(cross_vector.dot(beam.frame.normal))
+ is_beam_normal_coplanar = tol.is_close(dot_with_beam_normal, 1.0) or tol.is_zero(dot_with_beam_normal)
+ if not is_beam_normal_coplanar:
+ return False
+ return True
def point_centerline_towards_joint(beam_a, beam_b):
diff --git a/src/compas_timber/connections/x_halflap.py b/src/compas_timber/connections/x_halflap.py
deleted file mode 100644
index 8f5a5ae7b..000000000
--- a/src/compas_timber/connections/x_halflap.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from compas_timber.elements import MillVolume
-from compas_timber.errors import BeamJoiningError
-
-from .lap_joint import LapJoint
-from .solver import JointTopology
-
-
-class XHalfLapJoint(LapJoint):
- """Represents a X-Lap type joint which joins the end of a beam along the length of another beam,
- trimming the main beam.
-
- This joint type is compatible with beams in T topology.
-
- Please use `XHalfLapJoint.create()` to properly create an instance of this class and associate it with a model.
-
- Parameters
- ----------
- main_beam : :class:`~compas_timber.parts.Beam`
- The main beam to be joined.
- cross_beam : :class:`~compas_timber.parts.Beam`
- The cross beam to be joined.
- flip_lap_side : bool
- If True, the lap is flipped to the other side of the beams.
- cut_plane_bias : float
- Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
-
- """
-
- SUPPORTED_TOPOLOGY = JointTopology.TOPO_X
-
- def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
- super(XHalfLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias, **kwargs)
-
- def add_features(self):
- assert self.main_beam and self.cross_beam # should never happen
-
- try:
- negative_brep_beam_a, negative_brep_beam_b = self._create_negative_volumes()
- except Exception as ex:
- raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ex))
- volume_a = MillVolume(negative_brep_beam_a)
- volume_b = MillVolume(negative_brep_beam_b)
- self.main_beam.add_features(volume_a)
- self.cross_beam.add_features(volume_b)
- self.features = [volume_a, volume_b]
diff --git a/src/compas_timber/connections/x_lap.py b/src/compas_timber/connections/x_lap.py
new file mode 100644
index 000000000..b3ae9f2d6
--- /dev/null
+++ b/src/compas_timber/connections/x_lap.py
@@ -0,0 +1,101 @@
+from compas_timber.elements.features import MillVolume
+from compas_timber.errors import BeamJoiningError
+from compas_timber.fabrication import Lap
+
+from .lap_joint import LapJoint
+from .solver import JointTopology
+from .utilities import are_beams_coplanar
+
+
+class XLapJoint(LapJoint):
+ """Represents an X-Lap type joint which joins the two beams somewhere along their length with a lap.
+
+ This joint type is compatible with beams in X topology.
+
+ Please use `XLapJoint.create()` to properly create an instance of this class and associate it with a model.
+
+ Parameters
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The first beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The second beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+
+ Attributes
+ ----------
+ main_beam : :class:`~compas_timber.parts.Beam`
+ The first beam to be joined.
+ cross_beam : :class:`~compas_timber.parts.Beam`
+ The second beam to be joined.
+ flip_lap_side : bool
+ If True, the lap is flipped to the other side of the beams.
+ cut_plane_bias : float
+ Allows lap to be shifted deeper into one beam or the other. Value should be between 0 and 1.0 without completely cutting through either beam. Default is 0.5.
+ """
+
+ SUPPORTED_TOPOLOGY = JointTopology.TOPO_X
+
+ def __init__(self, main_beam=None, cross_beam=None, flip_lap_side=False, cut_plane_bias=0.5, **kwargs):
+ super(XLapJoint, self).__init__(main_beam, cross_beam, flip_lap_side, cut_plane_bias, **kwargs)
+
+ def add_features(self):
+ """Adds the required extension and trimming features to both beams.
+
+ This method is automatically called when joint is created by the call to `Joint.create()`.
+
+ """
+ assert self.main_beam and self.cross_beam
+
+ if self.features:
+ self.main_beam.remove_features(self.features)
+ self.cross_beam.remove_features(self.features)
+
+ if are_beams_coplanar(*self.elements): # TODO: this is a temporal solution to allow the vizualization of non-coplanar lap joints.
+ # calculate the lap length and depth for each beam
+ main_lap_length, cross_lap_length = self._get_lap_lengths()
+ main_lap_depth, cross_lap_depth = self._get_lap_depths()
+
+ ## main_beam features
+ # main Lap feature
+ main_lap_feature = Lap.from_plane_and_beam(
+ self.main_cutting_plane,
+ self.main_beam,
+ main_lap_length,
+ main_lap_depth,
+ ref_side_index=self.main_ref_side_index,
+ )
+
+ ## cross_beam features
+ # cross Lap feature
+ cross_lap_feature = Lap.from_plane_and_beam(
+ self.cross_cutting_plane,
+ self.cross_beam,
+ cross_lap_length,
+ cross_lap_depth,
+ ref_side_index=self.cross_ref_side_index,
+ )
+
+ # add features to the beams
+ self.main_beam.add_features(main_lap_feature)
+ self.cross_beam.add_features(cross_lap_feature)
+
+ # register features to the joint
+ self.features.extend([main_lap_feature, cross_lap_feature])
+
+ else:
+ # TODO: this is a temporal solution to avoid the error if beams are not coplanar and allow the visualization of the joint.
+ # TODO: this solution does not generate machining features and therefore will be ignored in the fabrication process.
+ # TODO: once the Lap BTLx processing implimentation allows for non-coplanar beams, this should be removed.
+ try:
+ negative_brep_beam_a, negative_brep_beam_b = self._create_negative_volumes()
+ except Exception as ex:
+ raise BeamJoiningError(beams=self.beams, joint=self, debug_info=str(ex))
+ volume_a = MillVolume(negative_brep_beam_a)
+ volume_b = MillVolume(negative_brep_beam_b)
+ self.main_beam.add_features(volume_a)
+ self.cross_beam.add_features(volume_b)
+ self.features = [volume_a, volume_b]
diff --git a/src/compas_timber/design/workflow.py b/src/compas_timber/design/workflow.py
index 59b7a8b20..e7b86f3b0 100644
--- a/src/compas_timber/design/workflow.py
+++ b/src/compas_timber/design/workflow.py
@@ -2,7 +2,7 @@
from compas_timber.connections import JointTopology
from compas_timber.connections import LMiterJoint
from compas_timber.connections import TButtJoint
-from compas_timber.connections import XHalfLapJoint
+from compas_timber.connections import XLapJoint
from compas_timber.utils import intersection_line_line_param
@@ -52,7 +52,7 @@ def get_topology_rules(rules, use_defaults=False):
topo_rules = {
JointTopology.TOPO_L: TopologyRule(JointTopology.TOPO_L, LMiterJoint),
JointTopology.TOPO_T: TopologyRule(JointTopology.TOPO_T, TButtJoint),
- JointTopology.TOPO_X: TopologyRule(JointTopology.TOPO_X, XHalfLapJoint),
+ JointTopology.TOPO_X: TopologyRule(JointTopology.TOPO_X, XLapJoint),
}
for rule in rules: # separate category and topo and direct joint rules
if rule.__class__.__name__ == "TopologyRule":
diff --git a/src/compas_timber/elements/beam.py b/src/compas_timber/elements/beam.py
index 292239745..23bd87306 100644
--- a/src/compas_timber/elements/beam.py
+++ b/src/compas_timber/elements/beam.py
@@ -629,3 +629,22 @@ def endpoint_closest_to_point(self, point):
return "start", ps
else:
return "end", pe
+
+ def get_dimensions_relative_to_side(self, ref_side_index):
+ """Returns the perpendicular and parallel dimensions of the beam to the given reference side.
+
+ Parameters
+ ----------
+ ref_side_index : int
+ The index of the reference side to which the dimensions should be calculated.
+
+ Returns
+ -------
+ tuple(float, float)
+ The perpendicular and parallel dimensions of the beam to the reference side.
+ - Perpendicular dimension: The measurement at a right angle to the reference side.
+ - Parallel dimension: The measurement along the same direction as the reference side.
+ """
+ if ref_side_index in [1, 3]:
+ return self.height, self.width
+ return self.width, self.height
diff --git a/src/compas_timber/fabrication/btlx.py b/src/compas_timber/fabrication/btlx.py
index d02e8a446..4e0bd2d07 100644
--- a/src/compas_timber/fabrication/btlx.py
+++ b/src/compas_timber/fabrication/btlx.py
@@ -607,16 +607,29 @@ class MachiningLimits(object):
Limit the front face.
face_limited_back : bool
Limit the back face.
+ face_limited_top : bool
+ Limit the top face.
+ face_limited_bottom : bool
+ Limit the bottom face.
"""
- EXPECTED_KEYS = ["FaceLimitedStart", "FaceLimitedEnd", "FaceLimitedFront", "FaceLimitedBack"]
+ EXPECTED_KEYS = [
+ "FaceLimitedStart",
+ "FaceLimitedEnd",
+ "FaceLimitedFront",
+ "FaceLimitedBack",
+ "FaceLimitedTop",
+ "FaceLimitedBottom",
+ ]
def __init__(self):
self.face_limited_start = True
self.face_limited_end = True
self.face_limited_front = True
self.face_limited_back = True
+ self.face_limited_top = True
+ self.face_limited_bottom = True
@property
def limits(self):
@@ -626,6 +639,8 @@ def limits(self):
"FaceLimitedEnd": self.face_limited_end,
"FaceLimitedFront": self.face_limited_front,
"FaceLimitedBack": self.face_limited_back,
+ "FaceLimitedTop": self.face_limited_top,
+ "FaceLimitedBottom": self.face_limited_bottom,
}
diff --git a/src/compas_timber/fabrication/french_ridge_lap.py b/src/compas_timber/fabrication/french_ridge_lap.py
index 8200b0ae5..7118ffeaa 100644
--- a/src/compas_timber/fabrication/french_ridge_lap.py
+++ b/src/compas_timber/fabrication/french_ridge_lap.py
@@ -373,7 +373,7 @@ def lap_volume_from_params_and_beam(self, beam):
opp_edge = ref_edge.translated(ref_side.yaxis * ref_side.ysize)
# get the height of the beam and the edge length of the lap
- height = beam.height if self.ref_side_index % 2 == 0 else beam.width
+ height = beam.get_dimensions_relative_to_side(self.ref_side_index)[1]
edge_length = ref_side.ysize / math.sin(math.radians(self.angle))
if self.orientation == OrientationType.END:
edge_length = -edge_length
diff --git a/src/compas_timber/fabrication/lap.py b/src/compas_timber/fabrication/lap.py
index 8220c8483..53245ce7b 100644
--- a/src/compas_timber/fabrication/lap.py
+++ b/src/compas_timber/fabrication/lap.py
@@ -1,17 +1,20 @@
import math
-from compas.geometry import Box
+from compas.datastructures import Mesh
from compas.geometry import Brep
from compas.geometry import Frame
from compas.geometry import Line
from compas.geometry import Plane
from compas.geometry import Point
+from compas.geometry import Rotation
from compas.geometry import Vector
from compas.geometry import angle_vectors_signed
from compas.geometry import distance_point_point
from compas.geometry import intersection_line_plane
+from compas.geometry import intersection_plane_plane_plane
from compas.geometry import is_point_behind_plane
from compas.tolerance import TOL
+from compas.tolerance import Tolerance
from compas_timber.errors import FeatureApplicationError
@@ -287,31 +290,9 @@ def machining_limits(self, machining_limits):
########################################################################
@classmethod
- def from_beam_and_beam(cls, main_beam, cross_beam, depth):
- """Create a Lap instance from a main beam and a cross beam. The main beam is cut by the cross beam.
-
- Parameters
- ----------
- main_beam : :class:`~compas_timber.elements.Beam`
- The main beam to be cut.
- cross_beam : :class:`~compas_timber.elements.Beam`
- The cross beam that cuts the main beam.
- depth : float
- The depth of the lap.
-
- Returns
- -------
- :class:`~compas_timber.fabrication.Lap`
-
- """
- # type: (Beam, Beam, float) -> Lap
-
- raise NotImplementedError
-
-
- @classmethod
- def from_plane_and_beam(cls, plane, beam, width, depth, ref_side_index=0):
- """Create a Lap instance from a plane and a beam. The lap is defined by the plane given and a plane parallel to that at a distance defined by the width.
+ def from_plane_and_beam(cls, plane, beam, length, depth, is_pocket=False, ref_side_index=0):
+ """Create a Lap instance from a plane and a beam. The lap is defined by the plane given and a plane parallel to that at a distance defined by the length and a given depth.
+ This method is used to create pocket cuts.
Parameters
----------
@@ -319,10 +300,12 @@ def from_plane_and_beam(cls, plane, beam, width, depth, ref_side_index=0):
The plane that defines the lap.
beam : :class:`~compas_timber.elements.Beam`
The beam that the Lap instance is applied to.
- width : float
- The width of the lap.
+ length : float
+ The length of the lap.
depth : float
The depth of the lap.
+ is_pocket : bool, optional
+ If True, the lap is a pocket cut. Default is False
ref_side_index : int, optional
The reference side index of the main_beam to be cut. Default is 0 (i.e. RS1).
@@ -336,7 +319,7 @@ def from_plane_and_beam(cls, plane, beam, width, depth, ref_side_index=0):
plane = Plane.from_frame(plane)
# create an offset plane at the depth of the lap
- offset_plane = Plane(plane.point - plane.normal * width, -plane.normal)
+ offset_plane = Plane(plane.point - plane.normal * length, -plane.normal)
planes = [plane, offset_plane]
# get ref_side, and ref_edge from the beam
@@ -348,28 +331,37 @@ def from_plane_and_beam(cls, plane, beam, width, depth, ref_side_index=0):
planes.sort(key=lambda plane: abs(angle_vectors_signed(ref_side.normal, plane.normal, ref_side.yaxis, deg=True)))
# calculate the orientation of the lap
- orientation = cls._calculate_orientation(ref_side, planes)
+ orientation = cls._calculate_orientation(ref_side, planes[0])
# calculate the start_x of the lap
- start_x = cls._calculate_start_x(ref_side, ref_edge, planes, orientation, depth)
-
- # calculate the width of the lap
- width = ref_side_surface.ysize
+ start_x = cls._calculate_start_x(ref_side, ref_edge, planes, orientation, depth, is_pocket)
# calculate the angle of the lap
- angle = cls._calculate_angle(ref_side, planes)
+ angle = cls._calculate_angle(ref_side, planes[0])
+
+ # calculate the inclination of the lap
+ inclination = cls._calculate_inclination(ref_side, planes[0], is_pocket)
+
+ # calculate the slope of the lap
+ slope = 0.0 # TODO: implement slope calculation
# calculate length
length = cls._calculate_length(planes, ref_side, ref_edge, depth, angle)
+ # define the width of the lap
+ width = ref_side_surface.ysize
+
# define machining limits
machining_limits = MachiningLimits()
+ machining_limits.face_limited_top = False
machining_limits.face_limited_back = False
machining_limits.face_limited_front = False
return cls(orientation=orientation,
start_x=start_x,
angle=angle,
+ inclination=inclination,
+ slope=slope,
length=length,
width=width,
depth=depth,
@@ -377,37 +369,44 @@ def from_plane_and_beam(cls, plane, beam, width, depth, ref_side_index=0):
ref_side_index=ref_side_index)
@staticmethod
- def _calculate_orientation(ref_side, planes):
+ def _calculate_orientation(ref_side, plane):
# orientation is START if cutting plane normal points towards the start of the beam and END otherwise
- if is_point_behind_plane(ref_side.point, planes[0]):
+ if is_point_behind_plane(ref_side.point, plane):
return OrientationType.END
else:
return OrientationType.START
@staticmethod
- def _calculate_start_x(ref_side, ref_edge, planes, orientation, depth):
- # offset the reference edge by the depth of the lap
- offseted_ref_edge = ref_edge.translated(-ref_side.normal*depth)
- ref_edges = [offseted_ref_edge, ref_edge]
- # find the intersection points of the planes with the reference edges and calculate the distances from the start of the beam
+ def _calculate_start_x(ref_side, ref_edge, planes, orientation, depth, is_pocket):
+ # calculate the start x distance based on intersections of planes with reference edges.
+ # if the lap is meant for a pocket one must consider the offseted reference edge
+ if is_pocket:
+ offseted_ref_edge = ref_edge.translated(-ref_side.normal * depth)
+ ref_edges = [offseted_ref_edge, ref_edge]
+ else:
+ ref_edges = [ref_edge]
x_distances = []
- for plane, edge in zip(planes, ref_edges):
+ for edge, plane in zip(ref_edges, planes):
if intersection_line_plane(edge, plane) is None:
raise ValueError("One of the planes does not intersect with the beam.")
-
intersection_point = Point(*intersection_line_plane(edge, plane))
x_distances.append(distance_point_point(edge.start, intersection_point))
- if orientation == OrientationType.END:
- return max(x_distances)
- else:
- return min(x_distances)
+ return max(x_distances) if orientation == OrientationType.END else min(x_distances)
@staticmethod
- def _calculate_angle(ref_side, planes):
+ def _calculate_angle(ref_side, plane):
# vector rotation direction of the plane's normal in the vertical direction
- angle_vector = Vector.cross(ref_side.normal, planes[0].normal)
+ angle_vector = Vector.cross(ref_side.normal, plane.normal)
return abs(angle_vectors_signed(ref_side.xaxis, angle_vector, ref_side.normal, deg=True))
+ @staticmethod
+ def _calculate_inclination(ref_side, plane, is_pocket):
+ # vector rotation direction of the plane's normal in the vertical direction
+ if is_pocket:
+ return 90.0
+ else:
+ return abs(angle_vectors_signed(ref_side.normal, plane.normal, ref_side.normal, deg=True))
+
@staticmethod
def _calculate_length(planes, ref_side, ref_edge, depth, angle):
# calculate the length of the lap based on the intersection points of the planes with the reference edge
@@ -449,8 +448,19 @@ def apply(self, geometry, beam):
"""
# type: (Brep, Beam) -> Brep
- box = self.volume_from_params_and_beam(beam)
- lap_volume = Brep.from_box(box)
+ lap_volume = self.volume_from_params_and_beam(beam)
+
+ # convert mesh to brep
+ try:
+ lap_volume = Brep.from_mesh(lap_volume)
+ except Exception:
+ raise FeatureApplicationError(
+ lap_volume,
+ geometry,
+ "Could not convert the lap volume to a Brep.",
+ )
+
+ # subtract the lap volume from the beam geometry
try:
return geometry - lap_volume
except IndexError:
@@ -460,8 +470,8 @@ def apply(self, geometry, beam):
"The lap volume does not intersect with the beam geometry.",
)
- def volume_from_params_and_beam(self, beam):
- """Calculates the volume of the cut from the machining parameters in this instance and the given beam
+ def planes_from_params_and_beam(self, beam):
+ """Calculates the planes that create the lap from the machining parameters in this instance and the given beam
Parameters
----------
@@ -470,29 +480,143 @@ def volume_from_params_and_beam(self, beam):
Returns
-------
- :class:`compas.geometry.Box`
- The boxvolume of the cut as a box.
+ list of :class:`compas.geometry.Plane`
+ The planes of the cut as a list.
"""
- # type: (Beam) -> Brep
-
- ref_side = beam.side_as_surface(self.ref_side_index)
- p_origin = ref_side.point_at(self.start_x, self.start_y)
+ # type: (Beam) -> List[Plane]
+ assert self.orientation is not None
+ assert self.start_x is not None
+ assert self.start_y is not None
+ assert self.angle is not None
+ assert self.inclination is not None
+ assert self.slope is not None
+ assert self.length is not None
+ assert self.width is not None
+ assert self.depth is not None
+ assert self.machining_limits is not None
+
+ ref_surface = beam.side_as_surface(self.ref_side_index)
+ p_origin = ref_surface.point_at(self.start_x, self.start_y)
+ start_frame = Frame(p_origin, ref_surface.frame.xaxis, ref_surface.frame.yaxis)
+
+ # define angle rotation matrix
+ angle_angle = self.angle if self.orientation == OrientationType.END else 180-self.angle
+ angle_rot = Rotation.from_axis_and_angle(start_frame.zaxis, math.radians(angle_angle), point=p_origin)
+ # define inclination rotation matrix
+ inclination_axis = start_frame.xaxis if self.orientation == OrientationType.END else -start_frame.xaxis
+ inclination_rot = Rotation.from_axis_and_angle(inclination_axis, math.radians(self.inclination), point=p_origin)
+ # define slope rotation matrix
+ slope_axis = -start_frame.zaxis
+ slope_rot = Rotation.from_axis_and_angle(slope_axis, math.radians(self.slope), point=p_origin)
+
+ # get start_face and opp_face (two sides of the cut)
+ start_frame.transform(angle_rot*inclination_rot*slope_rot)
+ end_frame = start_frame.translated(-start_frame.zaxis * self.length)
+ end_frame.yaxis = -end_frame.yaxis
+
+ # get top_face and bottom_face
+ lead_inclination_axis = -start_frame.xaxis if self.orientation == OrientationType.END else start_frame.xaxis
+ lead_inclination_rot = Rotation.from_axis_and_angle(lead_inclination_axis, math.radians(self.lead_inclination), point=p_origin)
+ top_frame = start_frame.transformed(lead_inclination_rot)
+ bottom_frame = top_frame.translated(-top_frame.zaxis * self.depth)
+ bottom_frame.xaxis = -bottom_frame.xaxis
+
+ # get front_face and back_face #TODO: is this correct?
+ front_frame = start_frame.rotated(math.radians(self.lead_angle), start_frame.xaxis, point=start_frame.point)
+ back_frame = front_frame.translated(-front_frame.zaxis * self.width)
+ back_frame.xaxis = -back_frame.xaxis
+
+ frames = [start_frame, end_frame, top_frame, bottom_frame, front_frame, back_frame]
+
+ # replace frames according to machining limits
+ frames = self._update_frames_based_on_machining_limits(frames, beam)
+
+ return [Plane.from_frame(frame) for frame in frames]
- box_frame = Frame(p_origin, ref_side.frame.xaxis, -ref_side.frame.yaxis)
- rot_angle = 90-self.angle if self.orientation == OrientationType.END else self.angle-90
- box_frame.rotate(math.radians(rot_angle), box_frame.normal, point=box_frame.point)
+ def volume_from_params_and_beam(self, beam):
+ """
+ Calculates the trimming volume from the machining parameters in this instance and the given beam,
+ ensuring correct face orientation.
- box = Box(xsize=self.length, ysize=self.width, zsize=self.depth, frame=box_frame)
- box.translate(box_frame.xaxis * (self.length / 2) + box_frame.yaxis * (-self.width / 2) + box_frame.zaxis * (self.depth / 2))
+ Parameters
+ ----------
+ beam : :class:`compas_timber.elements.Beam`
+ The beam that is cut by this instance.
+ Returns
+ -------
+ :class:`compas.geometry.Mesh`
+ The correctly oriented trimming volume of the cut.
+ """
+ # Get cutting frames
+
+ start_plane, end_plane, top_plane, bottom_plane, front_plane, back_plane = self.planes_from_params_and_beam(beam)
+
+ # Calculate vertices using plane-plane-plane intersection
+ vertices = [
+ Point(*intersection_plane_plane_plane(start_plane, bottom_plane, front_plane)), # v0
+ Point(*intersection_plane_plane_plane(start_plane, bottom_plane, back_plane)), # v1
+ Point(*intersection_plane_plane_plane(end_plane, bottom_plane, back_plane)), # v2
+ Point(*intersection_plane_plane_plane(end_plane, bottom_plane, front_plane)), # v3
+ Point(*intersection_plane_plane_plane(start_plane, top_plane, front_plane)), # v4
+ Point(*intersection_plane_plane_plane(start_plane, top_plane, back_plane)), # v5
+ Point(*intersection_plane_plane_plane(end_plane, top_plane, back_plane)), # v6
+ Point(*intersection_plane_plane_plane(end_plane, top_plane, front_plane)), # v7
+ ]
+ # define faces of the trimming volume
+ # ensure vertices are defined in counter-clockwise order when viewed from the outside
+ faces = [
+ [3, 2, 1, 0], # Bottom face
+ [7, 6, 5, 4], # Top face
+ [0, 1, 5, 4], # Side face 1
+ [1, 2, 6, 5], # Side face 2
+ [2, 3, 7, 6], # Side face 3
+ [3, 0, 4, 7], # Side face 4
+ ]
+ # ensure proper vertex order based on orientation
if self.orientation == OrientationType.END:
- box.translate(box_frame.xaxis * -self.length)
+ faces = [face[::-1] for face in faces]
- if not self.machining_limits["FaceLimitedFront"] or not self.machining_limits["FaceLimitedBack"]:
- box.ysize = box.ysize*10 # make the box large enough to cut through the beam
+ return Mesh.from_vertices_and_faces(vertices, faces)
- return box
+ def _update_frames_based_on_machining_limits(self, frames, beam):
+ """Updates the frames based on the machining limits.
+
+ Parameters
+ ----------
+ frames : list of :class:`compas.geometry.Frame`
+ The frames of the cut.
+ beam : :class:`compas_timber.elements.Beam`
+ The beam that is cut by this instance.
+
+ Returns
+ -------
+ list of :class:`compas.geometry.Frame`
+ The updated frames of the cut.
+ """
+ tol = Tolerance()
+ tol.absolute=1e-3
+
+ if not self.machining_limits["FaceLimitedStart"]:
+ frames[0] = beam.ref_sides[4]
+ if not self.machining_limits["FaceLimitedEnd"]:
+ frames[1] = beam.ref_sides[5]
+ if not self.machining_limits["FaceLimitedTop"]:
+ frames[2] = beam.ref_sides[self.ref_side_index]
+ frames[2].translate(frames[2].zaxis * tol.absolute) # offset the top frame to avoid tolerance issues
+ if not self.machining_limits["FaceLimitedBottom"]:
+ opp_ref_side_index = beam.opposing_side_index(self.ref_side_index)
+ frames[3] = beam.ref_sides[opp_ref_side_index]
+ frames[3].translate(frames[3].zaxis * tol.absolute) # offset the bottom frame to avoid tolerance issues
+ if not self.machining_limits["FaceLimitedFront"]:
+ frames[4] = beam.ref_sides[(self.ref_side_index-1)%4]
+ frames[4].translate(frames[4].zaxis * tol.absolute) # offset the front frame to avoid tolerance issues
+ if not self.machining_limits["FaceLimitedBack"]:
+ frames[5] = beam.ref_sides[(self.ref_side_index+1)%4]
+ frames[5].translate(frames[5].zaxis * tol.absolute) # offset the back frame to avoid tolerance issues
+
+ return frames
class LapParams(BTLxProcessingParams):
diff --git a/src/compas_timber/fabrication/tenon.py b/src/compas_timber/fabrication/tenon.py
index 30d6a6cb8..c0da8bcc1 100644
--- a/src/compas_timber/fabrication/tenon.py
+++ b/src/compas_timber/fabrication/tenon.py
@@ -388,7 +388,7 @@ def from_plane_and_beam(
if not length_limited_top:
start_depth = 0.0
if not length_limited_bottom:
- beam_height = beam.height if ref_side_index % 2 == 0 else beam.width
+ beam_height = beam.get_dimensions_relative_to_side(ref_side_index)[1]
length = beam_height / math.sin(math.radians(inclination)) - start_depth
# calculate start_x
@@ -454,14 +454,14 @@ def _calculate_start_y(beam, orientation, start_y, ref_side_index):
# calculate the start_y of the cut based on the beam, orientation, start_y and ref_side_index
if orientation == OrientationType.END:
start_y = -start_y
- beam_width = beam.width if ref_side_index % 2 == 0 else beam.height
+ beam_width= beam.get_dimensions_relative_to_side(ref_side_index)[0]
return start_y + beam_width / 2
@staticmethod
def _calculate_start_depth(beam, inclination, length, ref_side_index):
# calculate the start_depth of the tenon from height of the beam and the projected length of the tenon
proj_length = (length * math.sin(math.radians(inclination)))
- beam_height = beam.height if ref_side_index % 2 == 0 else beam.width
+ beam_height = beam.get_dimensions_relative_to_side(ref_side_index)[1]
return (beam_height - proj_length)/2
@staticmethod
diff --git a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology/code.py b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology/code.py
index b11987eb9..6f9b2bd12 100644
--- a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology/code.py
+++ b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology/code.py
@@ -3,7 +3,7 @@
from compas_timber.connections import JointTopology
from compas_timber.connections import LMiterJoint
from compas_timber.connections import TButtJoint
-from compas_timber.connections import XHalfLapJoint
+from compas_timber.connections import XLapJoint
from compas_timber.design import TopologyRule
@@ -12,6 +12,6 @@ def RunScript(self):
topoRules = []
topoRules.append(TopologyRule(JointTopology.TOPO_L, LMiterJoint))
topoRules.append(TopologyRule(JointTopology.TOPO_T, TButtJoint))
- topoRules.append(TopologyRule(JointTopology.TOPO_X, XHalfLapJoint))
+ topoRules.append(TopologyRule(JointTopology.TOPO_X, XLapJoint))
return topoRules
diff --git a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/code.py b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/code.py
index ef2204b51..8f321a9b6 100644
--- a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/code.py
+++ b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/code.py
@@ -5,7 +5,7 @@
from compas_timber.connections import Joint
from compas_timber.connections import JointTopology
-from compas_timber.connections import XHalfLapJoint
+from compas_timber.connections import XLapJoint
from compas_timber.design import TopologyRule
from compas_timber.ghpython.ghcomponent_helpers import get_leaf_subclasses
from compas_timber.ghpython.ghcomponent_helpers import manage_dynamic_params
@@ -23,7 +23,7 @@ def __init__(self):
if JointTopology.TOPO_X in supported_topo:
self.classes[cls.__name__] = cls
if ghenv.Component.Params.Output[0].NickName == "Rule":
- self.joint_type = XHalfLapJoint
+ self.joint_type = XLapJoint
self.clicked = False
else:
self.joint_type = self.classes.get(ghenv.Component.Params.Output[0].NickName, None)
@@ -31,9 +31,9 @@ def __init__(self):
def RunScript(self, *args):
if not self.clicked:
- ghenv.Component.Message = "Default: XHalfLapJoint"
- self.AddRuntimeMessage(Warning, "XHalfLapJoint is default, change in context menu (right click)")
- return TopologyRule(JointTopology.TOPO_X, XHalfLapJoint)
+ ghenv.Component.Message = "Default: XLapJoint"
+ self.AddRuntimeMessage(Warning, "XLapJoint is default, change in context menu (right click)")
+ return TopologyRule(JointTopology.TOPO_X, XLapJoint)
else:
ghenv.Component.Message = self.joint_type.__name__
kwargs = {}
diff --git a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/metadata.json b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/metadata.json
index a6bc36f9a..bb0574bd3 100644
--- a/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/metadata.json
+++ b/src/compas_timber/ghpython/components/CT_Joint_Rule_Topology_X/metadata.json
@@ -3,7 +3,7 @@
"nickname": "X_Topo_Joint",
"category": "COMPAS Timber",
"subcategory": "Joint Rules",
- "description": "makes rule to apply joint type to X topology. Defualts to XHalfLapJoint",
+ "description": "makes rule to apply joint type to X topology. Defualts to XLapJoint",
"exposure": 4,
"ghpython": {
"isAdvancedMode": true,
diff --git a/src/compas_timber/ghpython/components/CT_Model/code.py b/src/compas_timber/ghpython/components/CT_Model/code.py
index 9999b11cc..28e3c154f 100644
--- a/src/compas_timber/ghpython/components/CT_Model/code.py
+++ b/src/compas_timber/ghpython/components/CT_Model/code.py
@@ -6,7 +6,7 @@
from compas_timber.connections import JointTopology
from compas_timber.connections import LMiterJoint
from compas_timber.connections import TButtJoint
-from compas_timber.connections import XHalfLapJoint
+from compas_timber.connections import XLapJoint
from compas_timber.design import DebugInfomation
from compas_timber.design import JointRule
from compas_timber.elements import Beam
@@ -14,7 +14,7 @@
from compas_timber.model import TimberModel
JOINT_DEFAULTS = {
- JointTopology.TOPO_X: XHalfLapJoint,
+ JointTopology.TOPO_X: XLapJoint,
JointTopology.TOPO_T: TButtJoint,
JointTopology.TOPO_L: LMiterJoint,
}
diff --git a/tests/compas_timber/test_joint.py b/tests/compas_timber/test_joint.py
index 8a0d31015..ad13a508d 100644
--- a/tests/compas_timber/test_joint.py
+++ b/tests/compas_timber/test_joint.py
@@ -9,10 +9,10 @@
from compas.geometry import Vector
from compas_timber.connections import LButtJoint
-from compas_timber.connections import LHalfLapJoint
+from compas_timber.connections import LLapJoint
from compas_timber.connections import TButtJoint
-from compas_timber.connections import THalfLapJoint
-from compas_timber.connections import XHalfLapJoint
+from compas_timber.connections import TLapJoint
+from compas_timber.connections import XLapJoint
from compas_timber.connections import find_neighboring_beams
from compas_timber.elements import Beam
from compas_timber.model import TimberModel
@@ -118,15 +118,15 @@ def test_joint_create_l_butt(l_topo_beams):
assert joint.elements
-def test_joint_create_x_half_lap(x_topo_beams):
+def test_joint_create_x_lap(x_topo_beams):
model = TimberModel()
- beam_a, beam_b = x_topo_beams
- model.add_element(beam_a)
- model.add_element(beam_b)
- joint = XHalfLapJoint.create(model, beam_a, beam_b)
+ main_beam, cross_beam = x_topo_beams
+ model.add_element(main_beam)
+ model.add_element(cross_beam)
+ joint = XLapJoint.create(model, main_beam, cross_beam)
- assert joint.main_beam is beam_a
- assert joint.cross_beam is beam_b
+ assert joint.main_beam is main_beam
+ assert joint.cross_beam is cross_beam
assert joint.elements
@@ -135,7 +135,7 @@ def test_joint_create_t_lap(t_topo_beams):
main_beam, cross_beam = t_topo_beams
model.add_element(main_beam)
model.add_element(cross_beam)
- joint = THalfLapJoint.create(model, main_beam, cross_beam)
+ joint = TLapJoint.create(model, main_beam, cross_beam)
assert joint.main_beam is main_beam
assert joint.cross_beam is cross_beam
@@ -144,13 +144,13 @@ def test_joint_create_t_lap(t_topo_beams):
def test_joint_create_l_lap(l_topo_beams):
model = TimberModel()
- beam_a, beam_b = l_topo_beams
- model.add_element(beam_a)
- model.add_element(beam_b)
- joint = LHalfLapJoint.create(model, beam_a, beam_b)
+ main_beam, cross_beam = l_topo_beams
+ model.add_element(main_beam)
+ model.add_element(cross_beam)
+ joint = LLapJoint.create(model, main_beam, cross_beam)
- assert joint.main_beam is beam_a
- assert joint.cross_beam is beam_b
+ assert joint.main_beam is main_beam
+ assert joint.cross_beam is cross_beam
assert joint.elements
@@ -196,7 +196,7 @@ def test_joint_create_kwargs_passthrough_xhalflap():
model.add_element(beam_a)
model.add_element(beam_b)
- joint = XHalfLapJoint.create(model, beam_a, beam_b, cut_plane_bias=0.4)
+ joint = XLapJoint.create(model, beam_a, beam_b, cut_plane_bias=0.4)
assert joint.cut_plane_bias == 0.4
diff --git a/tests/compas_timber/test_lap.py b/tests/compas_timber/test_lap.py
index e7f296b01..c3663666d 100644
--- a/tests/compas_timber/test_lap.py
+++ b/tests/compas_timber/test_lap.py
@@ -1,9 +1,10 @@
import pytest
-from collections import OrderedDict
+from compas.data import json_dumps
+from compas.data import json_loads
-from compas.geometry import Box
from compas.geometry import Point
+from compas.geometry import Plane
from compas.geometry import Frame
from compas.geometry import Line
from compas.geometry import Vector
@@ -15,344 +16,263 @@
@pytest.fixture
-def cross_beam():
- width = 100
- height = 120
+def tol():
+ return Tolerance(unit="MM", absolute=1e-3, relative=1e-3)
+
+def test_lap_for_pocket_from_frame(tol):
centerline = Line(
Point(x=30396.1444398, y=-3257.66821289, z=73.5839565671),
Point(x=34824.6096086, y=-3257.66821289, z=73.5839565671),
)
+ cross_section = [60, 120]
+ beam = Beam.from_centerline(centerline, cross_section[0], cross_section[1])
- return Beam.from_centerline(centerline, width, height)
+ cutting_frame = Frame(
+ point=Point(x=31108.527, y=-2416.770, z=123.584),
+ xaxis=Vector(x=0.708, y=-0.706, z=0.000),
+ yaxis=Vector(x=0.000, y=-0.000, z=-1.000),
+ )
+ lap_length = 80.0
+ lap_depth = 20.0
+ ref_side_index = 1
+ # Lap instance
+ instance = Lap.from_plane_and_beam(
+ cutting_frame,
+ beam,
+ lap_length,
+ lap_depth,
+ is_pocket=True,
+ ref_side_index=ref_side_index,
+ )
-@pytest.fixture
-def main_beams():
- width = 80
- height = 100
-
- centerlines = [
- Line(
- Point(x=32079.4104241, y=-3257.66821289, z=73.5839565671),
- Point(x=33474.4091319, y=-4826.83669239, z=1616.81118820),
- ),
- Line(Point(x=33636.4572343, y=-2124.34355562, z=0.0), Point(x=32465.6288321, y=-3257.66821289, z=73.5839565671)),
- Line(
- Point(x=34148.4484099, y=-3257.66821289, z=1333.76053347),
- Point(x=33438.9952150, y=-3257.66821289, z=73.5839565671),
- ),
- Line(
- Point(x=34328.9231602, y=-3257.66821289, z=73.5839565671),
- Point(x=35365.5736795, y=-2579.64848379, z=-2050.47736312),
- ),
+ # attribute assertions
+ assert instance.orientation == "end"
+ assert tol.is_close(instance.start_x, 1545.323)
+ assert tol.is_close(instance.angle, 90.0)
+ assert tol.is_close(instance.inclination, 90.0)
+ assert tol.is_close(instance.slope, 0.0)
+ assert tol.is_close(instance.length, 133.325)
+ assert tol.is_close(instance.width, 120.0)
+ assert tol.is_close(instance.depth, 20.0)
+ assert tol.is_close(instance.lead_angle, 90.0)
+ assert tol.is_close(instance.lead_inclination, 90.0)
+ assert instance.machining_limits == {
+ "FaceLimitedBack": False,
+ "FaceLimitedStart": True,
+ "FaceLimitedBottom": True,
+ "FaceLimitedTop": False,
+ "FaceLimitedEnd": True,
+ "FaceLimitedFront": False,
+ }
+ assert instance.ref_side_index == ref_side_index
+
+ # volume from Lap instance
+ mesh_volume = instance.volume_from_params_and_beam(beam)
+ mesh_vertices, mesh_faces = mesh_volume.to_vertices_and_faces()
+
+ # expected vertices and faces
+ expected_vertices = [
+ [31941.467663941679, -3247.6682128906177, 13.58295656705431],
+ [31941.467663941679, -3247.6682128906177, 133.58495656705432],
+ [31808.142267843792, -3247.6682128906177, 133.58495656705432],
+ [31808.142267843792, -3247.6682128906177, 13.58295656705431],
+ [31941.467663941679, -3227.667212890618, 13.58295656705431],
+ [31941.467663941679, -3227.667212890618, 133.58495656705432],
+ [31808.142267843792, -3227.667212890618, 133.58495656705432],
+ [31808.142267843792, -3227.667212890618, 13.58295656705431],
]
+ expected_faces = [
+ [0, 1, 2, 3],
+ [4, 5, 6, 7],
+ [4, 5, 1, 0],
+ [5, 6, 2, 1],
+ [6, 7, 3, 2],
+ [7, 4, 0, 3],
+ ]
+
+ # assert vertices
+ assert len(mesh_vertices) == len(expected_vertices)
+ for vertex, expected_vertex in zip(mesh_vertices, expected_vertices):
+ for coord, expected_coord in zip(vertex, expected_vertex):
+ assert tol.is_close(coord, expected_coord)
+ # assert faces
+ assert len(mesh_faces) == len(expected_faces)
+ for face, expected_face in zip(mesh_faces, expected_faces):
+ assert face == expected_face
- return [Beam.from_centerline(centerline, width, height) for centerline in centerlines]
-
-
-EXPECTED_LAP_PARAMS = [
- OrderedDict(
- [
- ("Name", "Lap"),
- ("Priority", "0"),
- ("Process", "yes"),
- ("ProcessID", "0"),
- ("ReferencePlaneID", "4"),
- ("Orientation", "start"),
- ("StartX", "1665.305"),
- ("StartY", "0.000"),
- ("Angle", "90.000"),
- ("Inclination", "90.000"),
- ("Slope", "0.000"),
- ("Length", "115.933"),
- ("Width", "120.000"),
- ("Depth", "10.000"),
- ("LeadAngleParallel", "yes"),
- ("LeadAngle", "90.000"),
- ("LeadInclinationParallel", "yes"),
- ("LeadInclination", "90.000"),
- (
- "MachiningLimits",
- OrderedDict(
- [
- ("FaceLimitedBack", "no"),
- ("FaceLimitedEnd", "yes"),
- ("FaceLimitedFront", "no"),
- ("FaceLimitedStart", "yes"),
- ]
- ),
- ),
- ]
- ),
- OrderedDict(
- [
- ("Name", "Lap"),
- ("Priority", "0"),
- ("Process", "yes"),
- ("ProcessID", "0"),
- ("ReferencePlaneID", "2"),
- ("Orientation", "start"),
- ("StartX", "2053.296"),
- ("StartY", "0.000"),
- ("Angle", "90.000"),
- ("Inclination", "90.000"),
- ("Slope", "0.000"),
- ("Length", "125.355"),
- ("Width", "120.000"),
- ("Depth", "10.000"),
- ("LeadAngleParallel", "yes"),
- ("LeadAngle", "90.000"),
- ("LeadInclinationParallel", "yes"),
- ("LeadInclination", "90.000"),
- (
- "MachiningLimits",
- OrderedDict(
- [
- ("FaceLimitedBack", "no"),
- ("FaceLimitedEnd", "yes"),
- ("FaceLimitedFront", "no"),
- ("FaceLimitedStart", "yes"),
- ]
- ),
- ),
- ]
- ),
- OrderedDict(
- [
- ("Name", "Lap"),
- ("Priority", "0"),
- ("Process", "yes"),
- ("ProcessID", "0"),
- ("ReferencePlaneID", "3"),
- ("Orientation", "start"),
- ("StartX", "3013.621"),
- ("StartY", "0.000"),
- ("Angle", "90.000"),
- ("Inclination", "90.000"),
- ("Slope", "0.000"),
- ("Length", "120.388"),
- ("Width", "100.000"),
- ("Depth", "10.000"),
- ("LeadAngleParallel", "yes"),
- ("LeadAngle", "90.000"),
- ("LeadInclinationParallel", "yes"),
- ("LeadInclination", "90.000"),
- (
- "MachiningLimits",
- OrderedDict(
- [
- ("FaceLimitedBack", "no"),
- ("FaceLimitedEnd", "yes"),
- ("FaceLimitedFront", "no"),
- ("FaceLimitedStart", "yes"),
- ]
- ),
- ),
- ]
- ),
- OrderedDict(
- [
- ("Name", "Lap"),
- ("Priority", "0"),
- ("Process", "yes"),
- ("ProcessID", "0"),
- ("ReferencePlaneID", "1"),
- ("Orientation", "start"),
- ("StartX", "3865.756"),
- ("StartY", "0.000"),
- ("Angle", "123.187"),
- ("Inclination", "90.000"),
- ("Slope", "0.000"),
- ("Length", "121.594"),
- ("Width", "100.000"),
- ("Depth", "10.000"),
- ("LeadAngleParallel", "yes"),
- ("LeadAngle", "90.000"),
- ("LeadInclinationParallel", "yes"),
- ("LeadInclination", "90.000"),
- (
- "MachiningLimits",
- OrderedDict(
- [
- ("FaceLimitedBack", "no"),
- ("FaceLimitedEnd", "yes"),
- ("FaceLimitedFront", "no"),
- ("FaceLimitedStart", "yes"),
- ]
- ),
- ),
- ]
- ),
-]
-
-EXPECTED_CUTTING_FRAMES = [
- Frame(
- point=Point(x=32089.6304179, y=-3208.96062535, z=113.871952827),
- xaxis=Vector(x=0.535356833682, y=-0.602197739702, z=0.59224230086),
- yaxis=Vector(x=0.393493090239, y=-0.442621882493, z=-0.805759925209),
- ),
- Frame(
- point=Point(x=33665.8981258, y=-2151.51562805, z=49.9490979824),
- xaxis=Vector(x=-0.717789410535, y=-0.6947973214, z=0.0451114652743),
- yaxis=Vector(x=-0.0324135303495, y=-0.0313752665244, z=-0.998981959647),
- ),
- Frame(
- point=Point(x=34104.8785573, y=-3217.66821289, z=1358.28945402),
- xaxis=Vector(x=-0.490578411027, y=0.0, z=-0.871397052229),
- yaxis=Vector(x=-0.0, y=-1.0, z=0.0),
- ),
- Frame(
- point=Point(x=34270.8814019, y=-3247.83444818, z=48.3956383955),
- xaxis=Vector(x=0.421598058227, y=0.275745582418, z=-0.863839945288),
- yaxis=Vector(x=0.547367991266, y=-0.836892037325, z=-2.77555756156e-17),
- ),
-]
-
-EXPECTED_BOX = [
- Box(
- xsize=115.932621049,
- ysize=1200.0,
- zsize=10.0,
- frame=Frame(
- point=Point(x=32119.4156515, y=-3302.66821289, z=73.5839565671),
- xaxis=Vector(x=1.0, y=-2.05374699157e-16, z=0.0),
- yaxis=Vector(x=1.25751580751e-32, y=6.12303176911e-17, z=-1.0),
- ),
- ),
- Box(
- xsize=125.355190923,
- ysize=1200.0,
- zsize=10.0,
- frame=Frame(
- point=Point(x=32512.1179628, y=-3212.66821289, z=73.5839565671),
- xaxis=Vector(x=1.0, y=-2.05374699157e-16, z=0.0),
- yaxis=Vector(x=1.25751580751e-32, y=6.12303176911e-17, z=1.0),
- ),
- ),
- Box(
- xsize=120.388041068,
- ysize=1000.0,
- zsize=10.0,
- frame=Frame(
- point=Point(x=33469.9590708, y=-3257.66821289, z=128.583956567),
- xaxis=Vector(x=1.0, y=4.26515051461e-17, z=0.0),
- yaxis=Vector(x=4.26515051461e-17, y=-1.0, z=1.22460635382e-16),
- ),
- ),
- Box(
- xsize=121.593895035,
- ysize=1000.0,
- zsize=10.0,
- frame=Frame(
- point=Point(x=34340.1491250, y=-3216.23451172, z=18.5839565671),
- xaxis=Vector(x=0.836892037325, y=0.547367991266, z=0.0),
- yaxis=Vector(x=-0.547367991266, y=0.836892037325, z=0.0),
- ),
- ),
-]
-
-
-@pytest.mark.parametrize(
- "expected_lap_params, expected_cutting_frames, width, depth, ref_side_index",
- [
- (EXPECTED_LAP_PARAMS[0], EXPECTED_CUTTING_FRAMES[0], 80, 10, 3), # main_beam_a
- (EXPECTED_LAP_PARAMS[1], EXPECTED_CUTTING_FRAMES[1], 80, 10, 1), # main_beam_b
- (EXPECTED_LAP_PARAMS[2], EXPECTED_CUTTING_FRAMES[2], 100, 10, 2), # main_beam_c
- (EXPECTED_LAP_PARAMS[3], EXPECTED_CUTTING_FRAMES[3], 100, 10, 0), # main_beam_d
- ],
-)
-def test_lap_params(
- cross_beam,
- expected_lap_params,
- expected_cutting_frames,
- width,
- depth,
- ref_side_index,
-):
- # Create the Lap object
- lap = Lap.from_plane_and_beam(expected_cutting_frames, cross_beam, width, depth, ref_side_index)
-
- # Validate generated parameters
- generated_params = lap.params_dict
- for key, value in expected_lap_params.items():
- assert generated_params[key] == value
-
-
-@pytest.mark.parametrize(
- "expected_lap_params, expected_box",
- [
- (EXPECTED_LAP_PARAMS[0], EXPECTED_BOX[0]),
- (EXPECTED_LAP_PARAMS[1], EXPECTED_BOX[1]),
- (EXPECTED_LAP_PARAMS[2], EXPECTED_BOX[2]),
- (EXPECTED_LAP_PARAMS[3], EXPECTED_BOX[3]),
- ],
-)
-def test_lap_box_from_params(
- cross_beam,
- expected_lap_params,
- expected_box,
-):
- # convert string values to the appropriate types (float, bool, etc.)
- def convert_ordered_dict(params):
- def convert_value(value):
- if isinstance(value, str):
- # Convert to float if the string represents a number
- try:
- return float(value)
- except ValueError:
- pass
- # Convert specific strings to booleans
- if value.lower() == "yes":
- return True
- elif value.lower() == "no":
- return False
- # If the value is an OrderedDict, recursively convert its items
- elif isinstance(value, OrderedDict):
- return OrderedDict((key, convert_value(val)) for key, val in value.items())
- return value
-
- # Retain the original casing of keys in the OrderedDict
- return OrderedDict((key, convert_value(value)) for key, value in params.items())
-
- # convert the OrderedDict values to the expected types
- params = convert_ordered_dict(expected_lap_params)
-
- # instantiate Lap with unpacked parameters from the OrderedDict
- lap = Lap(
- orientation=params["Orientation"],
- start_x=params["StartX"],
- start_y=params["StartY"],
- angle=params["Angle"],
- inclination=params["Inclination"],
- slope=params["Slope"],
- length=params["Length"],
- width=params["Width"],
- depth=params["Depth"],
- lead_angle_parallel=params["LeadAngleParallel"],
- lead_angle=params["LeadAngle"],
- lead_inclination_parallel=params["LeadInclinationParallel"],
- lead_inclination=params["LeadInclination"],
- machining_limits=params["MachiningLimits"],
- ref_side_index=int(params["ReferencePlaneID"] - 1),
+
+def test_lap_for_halflaps_from_plane(tol):
+ centerline = Line(
+ Point(x=32087.5161016, y=-4629.03501941, z=73.5839565671),
+ Point(x=33194.4503091, y=-1833.71037612, z=73.5839565671),
)
+ cross_cection = [80, 100]
+ beam = Beam.from_centerline(centerline, cross_cection[0], cross_cection[1])
+
+ cutting_plane = Plane(point=Point(x=30396.144, y=-3287.668, z=13.584), normal=Vector(x=-0.000, y=-1.000, z=0.000))
+ lap_length = 60.0
+ lap_depth = 88.0
+ ref_side_index = 2
+
+ # Lap instance
+ instance = Lap.from_plane_and_beam(cutting_plane, beam, lap_length, lap_depth, is_pocket=False, ref_side_index=ref_side_index)
+
+ # attribute assertions
+ assert instance.orientation == "start"
+ assert tol.is_close(instance.start_x, 1458.549)
+ assert tol.is_close(instance.angle, 68.397)
+ assert tol.is_close(instance.inclination, 90.0)
+ assert tol.is_close(instance.slope, 0.0)
+ assert tol.is_close(instance.length, 60.0)
+ assert tol.is_close(instance.width, 80.0)
+ assert tol.is_close(instance.depth, 88.0)
+ assert tol.is_close(instance.lead_angle, 90.0)
+ assert tol.is_close(instance.lead_inclination, 90.0)
+ assert instance.machining_limits == {
+ "FaceLimitedBack": False,
+ "FaceLimitedStart": True,
+ "FaceLimitedBottom": True,
+ "FaceLimitedTop": False,
+ "FaceLimitedEnd": True,
+ "FaceLimitedFront": False,
+ }
+ assert instance.ref_side_index == ref_side_index
+
+ # volume from Lap instance
+ mesh_volume = instance.volume_from_params_and_beam(beam)
+ mesh_vertices, mesh_faces = mesh_volume.to_vertices_and_faces()
+
+ # expected vertices and faces
+ expected_vertices = [
+ [32575.667318015712, -3287.6682128900229, 35.583956567057157],
+ [32661.713622767158, -3287.6682128900229, 35.583956567057157],
+ [32685.473314726958, -3227.6682128899961, 35.583956567057157],
+ [32599.427009975512, -3227.6682128899961, 35.583956567057157],
+ [32575.667318015712, -3287.6682128900229, 123.58495656705432],
+ [32661.713622767158, -3287.6682128900229, 123.58495656705432],
+ [32685.473314726958, -3227.6682128899961, 123.58495656705433],
+ [32599.427009975512, -3227.6682128899961, 123.58495656705433],
+ ]
+ expected_faces = [
+ [3, 2, 1, 0],
+ [7, 6, 5, 4],
+ [0, 1, 5, 4],
+ [1, 2, 6, 5],
+ [2, 3, 7, 6],
+ [3, 0, 4, 7],
+ ]
+
+ # assert vertices
+ assert len(mesh_vertices) == len(expected_vertices)
+ for vertex, expected_vertex in zip(mesh_vertices, expected_vertices):
+ for coord, expected_coord in zip(vertex, expected_vertex):
+ assert tol.is_close(coord, expected_coord)
+ # assert faces
+ assert len(mesh_faces) == len(expected_faces)
+ for face, expected_face in zip(mesh_faces, expected_faces):
+ assert face == expected_face
+
+
+def test_lap_data():
+ machining_limits = {
+ "FaceLimitedBack": False,
+ "FaceLimitedStart": True,
+ "FaceLimitedBottom": True,
+ "FaceLimitedTop": False,
+ "FaceLimitedEnd": True,
+ "FaceLimitedFront": False,
+ }
+
+ instance = Lap(
+ "end",
+ 2289.328,
+ 0.0,
+ 111.603,
+ 90.0,
+ 0.0,
+ 80.0,
+ 60.0,
+ 22.0,
+ True,
+ 90.0,
+ True,
+ 90.0,
+ machining_limits,
+ ref_side_index=0,
+ )
+ copied_instance = json_loads(json_dumps(instance))
+
+ assert copied_instance.orientation == instance.orientation
+ assert copied_instance.start_x == instance.start_x
+ assert copied_instance.start_y == instance.start_y
+ assert copied_instance.angle == instance.angle
+ assert copied_instance.inclination == instance.inclination
+ assert copied_instance.slope == instance.slope
+ assert copied_instance.length == instance.length
+ assert copied_instance.width == instance.width
+ assert copied_instance.depth == instance.depth
+ assert copied_instance.lead_angle == instance.lead_angle
+ assert copied_instance.lead_inclination == instance.lead_inclination
+ assert copied_instance.machining_limits == instance.machining_limits
+ assert copied_instance.ref_side_index == instance.ref_side_index
+
+
+def test_lap_params_obj():
+ machining_limits = {
+ "FaceLimitedBack": False,
+ "FaceLimitedStart": True,
+ "FaceLimitedBottom": True,
+ "FaceLimitedTop": False,
+ "FaceLimitedEnd": True,
+ "FaceLimitedFront": False,
+ }
+
+ instance = Lap(
+ "end",
+ 2289.328,
+ 0.0,
+ 111.603,
+ 90.0,
+ 0.0,
+ 80.0,
+ 60.0,
+ 22.0,
+ True,
+ 90.0,
+ True,
+ 90.0,
+ machining_limits,
+ ref_side_index=0,
+ )
+
+ params = instance.params_dict
+
+ assert params["Name"] == "Lap"
+ assert params["Process"] == "yes"
+ assert params["Priority"] == "0"
+ assert params["ProcessID"] == "0"
+ assert params["ReferencePlaneID"] == "1"
- # generate frame from the parameters
- generated_box = lap.volume_from_params_and_beam(cross_beam)
- print(generated_box)
-
- # set the tolerance for comparing the generated and expected boxes
- tolerance = Tolerance()
- tolerance.absolute = 1e-3
-
- def approx_point(p1, p2, tolerance):
- return tolerance.is_close(p1.x, p2.x) and tolerance.is_close(p1.y, p2.y) and tolerance.is_close(p1.z, p2.z)
-
- # compare generated planes to expected planes using `approx`
- assert tolerance.is_close(generated_box.xsize, expected_box.xsize)
- assert tolerance.is_close(generated_box.ysize, expected_box.ysize)
- assert tolerance.is_close(generated_box.zsize, expected_box.zsize)
- assert approx_point(generated_box.frame.point, expected_box.frame.point, tolerance)
- assert approx_point(generated_box.frame.xaxis, expected_box.frame.xaxis, tolerance)
- assert approx_point(generated_box.frame.yaxis, expected_box.frame.yaxis, tolerance)
- assert approx_point(generated_box.frame.zaxis, expected_box.frame.zaxis, tolerance)
+ assert params["Orientation"] == "end"
+ assert params["StartX"] == "2289.328"
+ assert params["StartY"] == "0.000"
+ assert params["Angle"] == "111.603"
+ assert params["Inclination"] == "90.000"
+ assert params["Slope"] == "0.000"
+ assert params["Length"] == "80.000"
+ assert params["Width"] == "60.000"
+ assert params["Depth"] == "22.000"
+ assert params["LeadAngleParallel"] == "yes"
+ assert params["LeadAngle"] == "90.000"
+ assert params["LeadInclinationParallel"] == "yes"
+ assert params["LeadInclination"] == "90.000"
+ assert params["MachiningLimits"] == {
+ "FaceLimitedBack": "no",
+ "FaceLimitedStart": "yes",
+ "FaceLimitedBottom": "yes",
+ "FaceLimitedTop": "no",
+ "FaceLimitedEnd": "yes",
+ "FaceLimitedFront": "no",
+ }