From 253e63c60bff83fd939addcbb030dc76683edf20 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 13 Sep 2023 17:21:14 +0200 Subject: [PATCH 1/5] WIP serialization with new data interface --- src/compas_timber/assembly/assembly.py | 4 ++++ src/compas_timber/connections/joint.py | 18 ++++++---------- src/compas_timber/connections/l_butt.py | 22 ++++++++++---------- src/compas_timber/connections/l_miter.py | 25 ++++++++++++----------- src/compas_timber/connections/t_butt.py | 26 ++++++++++++------------ 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/compas_timber/assembly/assembly.py b/src/compas_timber/assembly/assembly.py index 4f1956926..980bc7f31 100644 --- a/src/compas_timber/assembly/assembly.py +++ b/src/compas_timber/assembly/assembly.py @@ -49,6 +49,10 @@ def from_data(cls, data): if isinstance(part, Joint): assembly._joints.append(part) part.restore_beams_from_keys(assembly) + + # TODO: this is needed when serializing but messes up deep copying. + # for joint in assembly._joints: + # joint.add_features() return assembly @property diff --git a/src/compas_timber/connections/joint.py b/src/compas_timber/connections/joint.py index b80e3d857..d43b93fba 100644 --- a/src/compas_timber/connections/joint.py +++ b/src/compas_timber/connections/joint.py @@ -1,4 +1,5 @@ from compas.data import Data +from compas.geometry import Frame from compas.geometry import Point from compas.geometry import angle_vectors from compas.geometry import intersection_line_line @@ -60,21 +61,14 @@ class Joint(Data): SUPPORTED_TOPOLOGY = JointTopology.TOPO_UNKNOWN - def __init__(self, *args, **kwargs): + def __init__(self, frame=None, key=None): super(Joint, self).__init__() - # will be needed as coordinate system for structural calculations for the forces at the joint - # TODO: CK: who's supposed to sets these? - self.frame = None - self.key = None + self.frame = frame or Frame.worldXY() + self.key = key @property def data(self): - return {"frame": self.frame, "key": self.key} - - @data.setter - def data(self, value): - self.frame = value["frame"] - self.key = value["key"] + return {"frame": self.frame.data, "key": self.key} @property def beams(self): @@ -129,7 +123,7 @@ def create(cls, assembly, *beams): if len(beams) < 2: raise ValueError("Expected at least 2 beams. Got instead: {}".format(len(beams))) - joint = cls(assembly, *beams) + joint = cls(*beams) assembly.add_joint(joint, beams) joint.add_features() return joint diff --git a/src/compas_timber/connections/l_butt.py b/src/compas_timber/connections/l_butt.py index 2e19db854..3d119f1a2 100644 --- a/src/compas_timber/connections/l_butt.py +++ b/src/compas_timber/connections/l_butt.py @@ -40,13 +40,13 @@ class LButtJoint(Joint): SUPPORTED_TOPOLOGY = JointTopology.TOPO_L - def __init__(self, assembly=None, main_beam=None, cross_beam=None): - super(LButtJoint, self).__init__(assembly, [main_beam, cross_beam]) - self.main_beam_key = main_beam.key - self.cross_beam_key = cross_beam.key + def __init__(self, main_beam=None, cross_beam=None, gap=0.0, frame=None, key=None): + super(LButtJoint, self).__init__(frame=frame, key=key) self.main_beam = main_beam self.cross_beam = cross_beam - self.gap = 0.0 # float, additional gap, e.g. for glue + self.main_beam_key = main_beam.key if main_beam else None + self.cross_beam_key = cross_beam.key if cross_beam else None + self.gap = gap # float, additional gap, e.g. for glue self.features = [] @property @@ -59,12 +59,12 @@ def data(self): data_dict.update(super(LButtJoint, self).data) return data_dict - @data.setter - def data(self, value): - Joint.data.fset(self, value) - self.main_beam_key = value["main_beam_key"] - self.cross_beam_key = value["cross_beam_key"] - self.gap = value["gap"] + @classmethod + def from_data(cls, value): + instance = cls(frame=Frame.from_data(value["frame"]), key=value["key"], gap=value["gap"]) + instance.main_beam_key = value["main_beam_key"] + instance.cross_beam_key = value["cross_beam_key"] + return instance @property def beams(self): diff --git a/src/compas_timber/connections/l_miter.py b/src/compas_timber/connections/l_miter.py index 524543c51..e2edff187 100644 --- a/src/compas_timber/connections/l_miter.py +++ b/src/compas_timber/connections/l_miter.py @@ -42,31 +42,32 @@ class LMiterJoint(Joint): SUPPORTED_TOPOLOGY = JointTopology.TOPO_L - def __init__(self, assembly, beam_a, beam_b, cutoff=None): - super(LMiterJoint, self).__init__(assembly, [beam_a, beam_b]) + def __init__(self, beam_a=None, beam_b=None, cutoff=None, frame=None, key=None): + super(LMiterJoint, self).__init__(frame, key) self.beam_a = beam_a self.beam_b = beam_b - self.beam_a_key = None - self.beam_b_key = None + self.beam_a_key = beam_a.key if beam_a else None + self.beam_b_key = beam_b.key if beam_b else None self.cutoff = cutoff # for very acute angles, limit the extension of the tip/beak of the joint self.features = [] @property def data(self): data_dict = { - "beam_a": self.beam_a.key, - "beam_b": self.beam_b.key, + "beam_a": self.beam_a_key, + "beam_b": self.beam_b_key, "cutoff": self.cutoff, } data_dict.update(Joint.data.fget(self)) return data_dict - @data.setter - def data(self, value): - Joint.data.fset(self, value) - self.beam_a_key = value["beam_a"] - self.beam_b_key = value["beam_b"] - self.cutoff = value["cutoff"] + @classmethod + def from_data(cls, value): + instance = cls(frame=Frame.from_data(value["frame"]), key=value["key"], cutoff=value["cutoff"]) + instance.beam_a_key = value["beam_a"] + instance.beam_b_key = value["beam_b"] + instance.cutoff = value["cutoff"] + return instance @property def joint_type(self): diff --git a/src/compas_timber/connections/t_butt.py b/src/compas_timber/connections/t_butt.py index 1240cd05e..8f96b46d7 100644 --- a/src/compas_timber/connections/t_butt.py +++ b/src/compas_timber/connections/t_butt.py @@ -41,31 +41,31 @@ class TButtJoint(Joint): SUPPORTED_TOPOLOGY = JointTopology.TOPO_T - def __init__(self, assembly=None, main_beam=None, cross_beam=None): - super(TButtJoint, self).__init__(assembly, [main_beam, cross_beam]) - self.main_beam_key = None - self.cross_beam_key = None + def __init__(self, main_beam=None, cross_beam=None, gap=None, frame=None, key=None): + super(TButtJoint, self).__init__(frame, key) + self.main_beam_key = main_beam.key if main_beam else None + self.cross_beam_key = cross_beam.key if cross_beam else None self.main_beam = main_beam self.cross_beam = cross_beam - self.gap = None + self.gap = gap self.features = [] @property def data(self): data_dict = { - "main_beam_key": self.main_beam.key, - "cross_beam_key": self.cross_beam.key, + "main_beam_key": self.main_beam_key, + "cross_beam_key": self.cross_beam_key, "gap": self.gap, } data_dict.update(Joint.data.fget(self)) return data_dict - @data.setter - def data(self, value): - Joint.data.fset(self, value) - self.main_beam_key = value["main_beam_key"] - self.cross_beam_key = value["cross_beam_key"] - self.gap = value["gap"] + @classmethod + def from_data(cls, value): + instance = cls(frame=Frame.from_data(value["frame"]), key=value["key"], gap=value["gap"]) + instance.main_beam_key = value["main_beam_key"] + instance.cross_beam_key = value["cross_beam_key"] + return instance @property def beams(self): From 0d36a177703651834d9873c0030b719bb7408ec2 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 13 Sep 2023 21:45:56 +0200 Subject: [PATCH 2/5] fixed unittests --- tests/compas_timber/test_TButtJoint.py | 2 +- tests/compas_timber/test_joint.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/compas_timber/test_TButtJoint.py b/tests/compas_timber/test_TButtJoint.py index a412c1264..613a3f27d 100644 --- a/tests/compas_timber/test_TButtJoint.py +++ b/tests/compas_timber/test_TButtJoint.py @@ -28,4 +28,4 @@ def test_create(): A = TimberAssembly() A.add_beam(B1) A.add_beam(B2) - _ = TButtJoint(A, B1, B2) + TButtJoint.create(A, B1, B2) diff --git a/tests/compas_timber/test_joint.py b/tests/compas_timber/test_joint.py index 6973ad8d1..21ef40f1b 100644 --- a/tests/compas_timber/test_joint.py +++ b/tests/compas_timber/test_joint.py @@ -9,7 +9,7 @@ from compas.geometry import Vector from compas_timber.assembly import TimberAssembly -from compas_timber.connections import Joint +from compas_timber.connections import TButtJoint from compas_timber.connections import find_neighboring_beams from compas_timber.parts import Beam @@ -38,7 +38,7 @@ def test_create(mocker): B2 = Beam(Frame.worldYZ(), length=1.0, width=0.1, height=0.1, geometry_type="mesh") A.add_beam(B1) A.add_beam(B2) - J = Joint.create(A, B1, B2) + J = TButtJoint.create(A, B1, B2) assert len(list(A.graph.nodes())) == 3 assert len(list(A.graph.edges())) == 2 @@ -54,7 +54,7 @@ def test_joint_override_protection(mocker): A.add_beam(B1) A.add_beam(B2) A.add_beam(B3) - J = Joint.create(A, B1, B2) + J = TButtJoint.create(A, B1, B2) assert A.are_parts_joined([B1, B2]) assert A.are_parts_joined([B1, B3]) is False @@ -74,7 +74,7 @@ def test_deepcopy(mocker): B2 = Beam.from_endpoints(Point(1, 0, 0), Point(1, 1, 0), 0.1, 0.2, z_vector=Vector(0, 0, 1), geometry_type="mesh") A.add_beam(B1) A.add_beam(B2) - J = Joint(A, B1, B2) + J = TButtJoint.create(A, B1, B2) J_copy = deepcopy(J) assert J_copy is not J From b62c7595785687d1cde0b5789a32b2fe6374e449 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Wed, 13 Sep 2023 21:47:04 +0200 Subject: [PATCH 3/5] restore features in from_data --- src/compas_timber/assembly/assembly.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/compas_timber/assembly/assembly.py b/src/compas_timber/assembly/assembly.py index 980bc7f31..0a23e6cb2 100644 --- a/src/compas_timber/assembly/assembly.py +++ b/src/compas_timber/assembly/assembly.py @@ -49,10 +49,8 @@ def from_data(cls, data): if isinstance(part, Joint): assembly._joints.append(part) part.restore_beams_from_keys(assembly) - - # TODO: this is needed when serializing but messes up deep copying. - # for joint in assembly._joints: - # joint.add_features() + for joint in assembly._joints: + joint.add_features() return assembly @property From 889c5be554bb855f71923296225799b10d48f85e Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 14 Sep 2023 09:00:43 +0200 Subject: [PATCH 4/5] drawing timber assembly does not modify it --- src/compas_timber/ghpython/components/CT_ShowAssembly/code.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compas_timber/ghpython/components/CT_ShowAssembly/code.py b/src/compas_timber/ghpython/components/CT_ShowAssembly/code.py index be367ea73..662b5c543 100644 --- a/src/compas_timber/ghpython/components/CT_ShowAssembly/code.py +++ b/src/compas_timber/ghpython/components/CT_ShowAssembly/code.py @@ -8,7 +8,6 @@ def RunScript(self, Assembly): if not Assembly: self.AddRuntimeMessage(Warning, "Input parameter Assembly failed to collect data") return - Assembly = Assembly.copy() # we're gonna be making changes to upstream objects Brep = [] for beam in Assembly.beams: From bd92e87f16755852b19df5179d8528e38adf9cd2 Mon Sep 17 00:00:00 2001 From: Chen Kasirer Date: Thu, 14 Sep 2023 09:45:45 +0200 Subject: [PATCH 5/5] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4eb4de1d..6145cd137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Beam transformed geometry with features is available using property `geometry`. * Adapted the `Data` interface of `Beam` and `Assembly` according to the changes in COMPAS core. * Beam geometry is created on demand. +* Adapted the `Data` interface of `Joint` and its implementations according to the changes in COMPAS core. ### Removed