compas-timber 2.0.0.dev0__tar.gz → 2.1.1.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {compas_timber-2.0.0.dev0/src/compas_timber.egg-info → compas_timber-2.1.1.dev0}/PKG-INFO +4 -6
  2. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/pyproject.toml +8 -6
  3. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/__init__.py +1 -1
  4. {compas_timber-2.0.0.dev0/src/compas_timber/connections → compas_timber-2.1.1.dev0/src/compas_timber}/analyzers.py +68 -19
  5. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/base.py +5 -5
  6. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/__init__.py +8 -18
  7. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/joint.py +44 -5
  8. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/joint_candidate.py +2 -2
  9. compas_timber-2.1.1.dev0/src/compas_timber/connections/l_miter.py +289 -0
  10. compas_timber-2.1.1.dev0/src/compas_timber/connections/l_tenon_mortise.py +143 -0
  11. compas_timber-2.0.0.dev0/src/compas_timber/connections/t_tenon_mortise.py → compas_timber-2.1.1.dev0/src/compas_timber/connections/mortise_tenon.py +51 -99
  12. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/oligina.py +40 -6
  13. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/plate_joint.py +13 -0
  14. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/solver.py +4 -6
  15. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/t_dovetail.py +47 -49
  16. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/t_step_joint.py +62 -36
  17. compas_timber-2.1.1.dev0/src/compas_timber/connections/t_tenon_mortise.py +109 -0
  18. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/y_butt.py +4 -2
  19. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/beam.py +29 -5
  20. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/fastener.py +5 -5
  21. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/fasteners/ball_node_fastener.py +9 -4
  22. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/fasteners/plate_fastener.py +14 -11
  23. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/features.py +2 -2
  24. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/panel.py +4 -13
  25. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/plate.py +3 -3
  26. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/plate_geometry.py +20 -10
  27. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/errors.py +1 -1
  28. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/btlx.py +118 -18
  29. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/double_cut.py +9 -40
  30. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/dovetail_mortise.py +18 -49
  31. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/dovetail_tenon.py +26 -50
  32. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/drilling.py +11 -25
  33. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/free_contour.py +67 -36
  34. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/french_ridge_lap.py +8 -38
  35. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/jack_cut.py +8 -38
  36. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/lap.py +16 -52
  37. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/longitudinal_cut.py +13 -56
  38. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/mortise.py +15 -50
  39. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/pocket.py +29 -65
  40. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/slot.py +15 -47
  41. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/step_joint.py +11 -42
  42. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/step_joint_notch.py +15 -46
  43. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/tenon.py +17 -48
  44. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/text.py +14 -27
  45. compas_timber-2.1.1.dev0/src/compas_timber/geometry.py +62 -0
  46. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/model.py +95 -44
  47. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/rhino/__init__.py +2 -2
  48. compas_timber-2.1.1.dev0/src/compas_timber/structural.py +267 -0
  49. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0/src/compas_timber.egg-info}/PKG-INFO +4 -6
  50. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber.egg-info/SOURCES.txt +4 -1
  51. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber.egg-info/requires.txt +2 -4
  52. compas_timber-2.0.0.dev0/src/compas_timber/connections/l_miter.py +0 -181
  53. compas_timber-2.0.0.dev0/src/compas_timber/structural.py +0 -115
  54. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/LICENSE +0 -0
  55. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/README.md +0 -0
  56. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/setup.cfg +0 -0
  57. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/ball_node.py +0 -0
  58. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/butt_joint.py +0 -0
  59. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/l_butt.py +0 -0
  60. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/l_french_ridge_lap.py +0 -0
  61. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/l_lap.py +0 -0
  62. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/lap_joint.py +0 -0
  63. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/panel_butt_joint.py +0 -0
  64. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/panel_joint.py +0 -0
  65. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/panel_miter_joint.py +0 -0
  66. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/plate_butt_joint.py +0 -0
  67. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/plate_miter_joint.py +0 -0
  68. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/t_birdsmouth.py +0 -0
  69. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/t_butt.py +0 -0
  70. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/t_lap.py +0 -0
  71. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/utilities.py +0 -0
  72. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/x_lap.py +0 -0
  73. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/connections/x_notch.py +0 -0
  74. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/__init__.py +0 -0
  75. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/elements/fasteners/__init__.py +0 -0
  76. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/fabrication/__init__.py +0 -0
  77. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/panel_features/__init__.py +0 -0
  78. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/panel_features/panel_connection_interface.py +0 -0
  79. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/panel_features/panel_features.py +0 -0
  80. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/planning/__init__.py +0 -0
  81. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/planning/nesting.py +0 -0
  82. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/planning/sequencer.py +0 -0
  83. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/rhino/install.py +0 -0
  84. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/utils/__init__.py +0 -0
  85. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber/utils/r_tree.py +0 -0
  86. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber.egg-info/dependency_links.txt +0 -0
  87. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber.egg-info/not-zip-safe +0 -0
  88. {compas_timber-2.0.0.dev0 → compas_timber-2.1.1.dev0}/src/compas_timber.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compas_timber
3
- Version: 2.0.0.dev0
3
+ Version: 2.1.1.dev0
4
4
  Summary: COMPAS package for modeling, designing and fabricating timber assemblies.
5
- Author-email: Aleksandra Anna Apolinarska <apolinarska@arch.ethz.ch>, Chen Kasirer <kasirer@arch.ethz.ch>, Gonzalo Casas <casas@arch.ethz.ch>, Jonas Haldemann <haldemann@arch.ethz.ch>, Oliver Appling Bucklin <bucklin@arch.ethz.ch>, "Aurèle L. Gheyselinck" <gheyselinck@arch.ethz.ch>, Panayiotis Papacharalambous <papacharalambous@arch.ethz.ch>, Anastasiia Stryzhevska <astryzhevska@arch.ethz.ch>, Jelle Feringa <jelleferinga@gmail.com>, Joseph Kenny <jk6372@princeton.edu>, Beverly Lytle <lytle@arch.ethz.ch>, Eric Gozzi <eric.gozzi@arch.ethz.ch>, Rodrigo Arca Zimmermann <rodrigo.arca@gmail.com>
5
+ Author-email: Aleksandra Anna Apolinarska <apolinarska@arch.ethz.ch>, Chen Kasirer <kasirer@arch.ethz.ch>, Gonzalo Casas <casas@arch.ethz.ch>, Jonas Haldemann <haldemann@arch.ethz.ch>, Oliver Appling Bucklin <bucklin@arch.ethz.ch>, "Aurèle L. Gheyselinck" <gheyselinck@arch.ethz.ch>, Panayiotis Papacharalambous <papacharalambous@arch.ethz.ch>, Anastasiia Stryzhevska <astryzhevska@arch.ethz.ch>, Jelle Feringa <jelleferinga@gmail.com>, Joseph Kenny <jk6372@princeton.edu>, Beverly Lytle <lytle@arch.ethz.ch>, Eric Gozzi <eric.gozzi@arch.ethz.ch>, Rodrigo Arca Zimmermann <rodrigo.arca@gmail.com>, Nicolas Benjamin Boscoboinik <boscoboinik@arch.ethz.ch>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://gramaziokohler.github.io/compas_timber/latest/
8
8
  Project-URL: Repository, https://github.com/gramaziokohler/compas_timber
@@ -19,20 +19,18 @@ License-File: LICENSE
19
19
  Requires-Dist: rtree
20
20
  Requires-Dist: compas<3.0,>=2.0
21
21
  Requires-Dist: compas_model==0.9.1
22
+ Requires-Dist: scipy>=1.1
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: bump-my-version; extra == "dev"
24
- Requires-Dist: compas_invocations2; extra == "dev"
25
+ Requires-Dist: compas_invocations2[mkdocs]>=1.0.2; extra == "dev"
25
26
  Requires-Dist: invoke>=0.14; extra == "dev"
26
27
  Requires-Dist: ruff; extra == "dev"
27
- Requires-Dist: sphinx_compas2_theme; extra == "dev"
28
- Requires-Dist: sphinxcontrib-mermaid; extra == "dev"
29
28
  Requires-Dist: twine; extra == "dev"
30
29
  Requires-Dist: wheel; extra == "dev"
31
30
  Requires-Dist: pytest-mock; extra == "dev"
32
31
  Requires-Dist: pytest>=8.0; extra == "dev"
33
32
  Requires-Dist: build; extra == "dev"
34
33
  Requires-Dist: tomlkit; extra == "dev"
35
- Requires-Dist: compas_viewer; extra == "dev"
36
34
  Requires-Dist: pytest-cov; extra == "dev"
37
35
  Requires-Dist: pytest-randomly; extra == "dev"
38
36
  Requires-Dist: pre-commit; extra == "dev"
@@ -18,7 +18,8 @@ authors = [
18
18
  { name = "Joseph Kenny", email = "jk6372@princeton.edu" },
19
19
  { name = "Beverly Lytle", email = "lytle@arch.ethz.ch" },
20
20
  { name = "Eric Gozzi", email = "eric.gozzi@arch.ethz.ch" },
21
- { name = "Rodrigo Arca Zimmermann", email = "rodrigo.arca@gmail.com" }
21
+ { name = "Rodrigo Arca Zimmermann", email = "rodrigo.arca@gmail.com" },
22
+ { name = "Nicolas Benjamin Boscoboinik", email = "boscoboinik@arch.ethz.ch" }
22
23
  ]
23
24
  license = "MIT"
24
25
  readme = "README.md"
@@ -38,23 +39,21 @@ dependencies = [
38
39
  "rtree",
39
40
  "compas >=2.0, <3.0",
40
41
  "compas_model == 0.9.1", # pin until first release
42
+ "scipy >= 1.1"
41
43
  ]
42
44
 
43
45
  [project.optional-dependencies]
44
46
  dev = [
45
47
  "bump-my-version",
46
- "compas_invocations2",
48
+ "compas_invocations2[mkdocs]>=1.0.2",
47
49
  "invoke >=0.14",
48
50
  "ruff",
49
- "sphinx_compas2_theme",
50
- "sphinxcontrib-mermaid",
51
51
  "twine",
52
52
  "wheel",
53
53
  "pytest-mock",
54
54
  "pytest>=8.0",
55
55
  "build",
56
56
  "tomlkit",
57
- "compas_viewer",
58
57
  "pytest-cov",
59
58
  "pytest-randomly",
60
59
  "pre-commit",
@@ -73,6 +72,9 @@ Repository = "https://github.com/gramaziokohler/compas_timber"
73
72
  # ============================================================================
74
73
  # Setup and build
75
74
  # ============================================================================
75
+ [build-system]
76
+ requires = ["setuptools>=68", "wheel"]
77
+ build-backend = "setuptools.build_meta"
76
78
 
77
79
  [tool.setuptools]
78
80
  package-dir = { "" = "src" }
@@ -94,7 +96,7 @@ where = ["src"]
94
96
  # ============================================================================
95
97
 
96
98
  [tool.bumpversion]
97
- current_version = "2.0.0-dev0"
99
+ current_version = "2.1.1-dev0"
98
100
  message = "Bump version to {new_version}"
99
101
  commit = true
100
102
  tag = true
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import compas
3
3
 
4
- __version__ = "2.0.0-dev0"
4
+ __version__ = "2.1.1-dev0"
5
5
 
6
6
  HERE = os.path.dirname(__file__)
7
7
  HOME = os.path.abspath(os.path.join(HERE, "..", ".."))
@@ -1,14 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  import math
4
+ from weakref import WeakKeyDictionary
2
5
 
3
- import compas.geometry
4
- import compas.tolerance # noqa: F401
5
- import compas_model.elements # noqa: F401
6
- from compas.geometry import KDTree
6
+ from compas.geometry import Point
7
7
  from compas.tolerance import TOL
8
+ from compas_model.elements import Element
8
9
 
9
- import compas_timber.connections # noqa: F401
10
- import compas_timber.elements # noqa: F401
11
10
  from compas_timber.connections import JointTopology
11
+ from compas_timber.geometry import KDTree
12
+ from compas_timber.model import TimberModel
12
13
 
13
14
 
14
15
  class Cluster(object):
@@ -40,8 +41,7 @@ class Cluster(object):
40
41
  return len(self.elements)
41
42
 
42
43
  @property
43
- def elements(self):
44
- # type: () -> set[compas_model.elements.Element]
44
+ def elements(self) -> set[Element]:
45
45
  if not self._elements:
46
46
  self._elements = set()
47
47
  for joint in self.joints:
@@ -49,8 +49,7 @@ class Cluster(object):
49
49
  return self._elements
50
50
 
51
51
  @property
52
- def location(self):
53
- # type: () -> compas.geometry.Point
52
+ def location(self) -> Point:
54
53
  return self.joints[0].location
55
54
 
56
55
  @property
@@ -81,6 +80,57 @@ class BeamGroupAnalyzer(object):
81
80
  raise NotImplementedError
82
81
 
83
82
 
83
+ class _CacheEntry(object):
84
+ def __init__(self, fingerprint: frozenset, joints: list, tree: KDTree) -> None:
85
+ self.fingerprint = fingerprint
86
+ self.joints = joints
87
+ self.tree = tree
88
+
89
+
90
+ class _KDTreeCache(object):
91
+ """Caches a :class:`KDTree` per model instance, rebuilding it when the joint set changes.
92
+
93
+ The cache is keyed by model identity (via a :class:`~weakref.WeakKeyDictionary`) so the
94
+ tree is automatically released when the model is garbage-collected. A fingerprint of
95
+ the current joint-candidate identities detects structural changes: if joints have been
96
+ added or removed since the tree was built, the tree is transparently rebuilt.
97
+
98
+ Crucially, the cache also stores the *canonical ordering* of joints used when the tree
99
+ was first built. Every caller receives that same list so that KDTree indices are always
100
+ consistent with ``self._joints``, regardless of the iteration order of the underlying set.
101
+ """
102
+
103
+ _store = WeakKeyDictionary() # type: WeakKeyDictionary # model -> _CacheEntry
104
+
105
+ @classmethod
106
+ def get(cls, model: TimberModel, joints: list) -> tuple[list, KDTree]:
107
+ """Return the canonical joints list and :class:`KDTree` for *model*.
108
+
109
+ If the joint set has changed since the last call (joints added or removed),
110
+ a new tree is built and the canonical list is updated.
111
+
112
+ Parameters
113
+ ----------
114
+ model : :class:`~compas_timber.model.TimberModel`
115
+ The model whose joint candidates the tree covers.
116
+ joints : list
117
+ The current joint candidates used only to compute the fingerprint and,
118
+ on a cache miss, to build the new tree.
119
+
120
+ Returns
121
+ -------
122
+ tuple[list, :class:`KDTree`]
123
+ ``(canonical_joints, tree)`` – the ordering and tree that all callers
124
+ for this model generation must share.
125
+ """
126
+ fingerprint = frozenset(id(j) for j in joints)
127
+ cached = cls._store.get(model)
128
+ if cached is None or cached.fingerprint != fingerprint:
129
+ cls._store[model] = _CacheEntry(fingerprint, joints, KDTree([j.location for j in joints]))
130
+ entry = cls._store[model]
131
+ return entry.joints, entry.tree
132
+
133
+
84
134
  class NBeamKDTreeAnalyzer(BeamGroupAnalyzer):
85
135
  """Finds clusters of N beams connected pairwise at the same point within a given max_distance.
86
136
 
@@ -96,14 +146,13 @@ class NBeamKDTreeAnalyzer(BeamGroupAnalyzer):
96
146
 
97
147
  def __init__(self, model, n=2, max_distance=None):
98
148
  super(NBeamKDTreeAnalyzer, self).__init__()
99
- self._joints = list(model.joint_candidates)
100
- if not self._joints:
149
+ joints = list(model.joint_candidates)
150
+ if not joints:
101
151
  raise ValueError("The model has no joint candidates to analyze. Forgot to call `model.connect_adjacent_beams()`?")
102
152
 
103
- self._kdtree = KDTree([joint.location for joint in self._joints])
153
+ self._joints, self._kdtree = _KDTreeCache.get(model, joints)
104
154
  self._n = n
105
155
  self.max_distance = max_distance or TOL.absolute
106
-
107
156
  # TODO: add parameter to specify groupwise clustering, i.e only look at joints of elements within the same group
108
157
 
109
158
  def find(self, exclude=None):
@@ -231,14 +280,14 @@ def MaxNCompositeAnalyzer(model, n, max_distance=None):
231
280
  The TimberModel to analyze.
232
281
  n : int
233
282
  The maximum cluster size.
234
- tolerance : :class:`~compas.tolerance.Tolerance` | None
235
- The tolerance to use for the analysis. If None, a default tolerance is used.
283
+ max_distance : float | None
284
+ The max distance to use for the analysis. If None, a default max distance is used.
236
285
 
237
286
  Returns
238
287
  -------
239
288
  CompositeAnalyzer
240
289
  An instance of CompositeAnalyzer that finds clusters of size n down to 2.
241
290
  """
242
- analyzers_cls = [lambda m, t, k=k: NBeamKDTreeAnalyzer(m, n=k, max_distance=t) for k in range(n, 1, -1)]
243
- # Use lambdas to capture k at each step
244
- return CompositeAnalyzer([cls(model, max_distance) for cls in analyzers_cls])
291
+ # All analyzers share the same KDTree automatically via the class-level cache keyed on model
292
+ max_cluster_size = max(n, 2)
293
+ return CompositeAnalyzer([NBeamKDTreeAnalyzer(model, n=k, max_distance=max_distance) for k in range(max_cluster_size, 1, -1)])
@@ -201,7 +201,7 @@ class TimberElement(Element, abc.ABC):
201
201
 
202
202
  Parameters
203
203
  ----------
204
- feature : :class:`~compas_timber.fabrication.BTLxProcessing` | list(:class:`~compas_timber.fabrication.BTLxProcessing`) | None
204
+ features : :class:`~compas_timber.fabrication.BTLxProcessing` | list(:class:`~compas_timber.fabrication.BTLxProcessing`) | None
205
205
  The feature or features to be removed described as a BTLxProcessing or a list of BTLxProcessings.
206
206
  If None, all features will be removed.
207
207
 
@@ -351,7 +351,7 @@ class TimberElement(Element, abc.ABC):
351
351
 
352
352
  def get_dimensions_relative_to_side(self, ref_side_index):
353
353
  # type: (int) -> tuple[float, float]
354
- """Returns the perpendicular and parallel dimensions of the beam to the given reference side.
354
+ """Returns the dimensions of a timber element with respect to the Y- axis and Normal of the given reference side.
355
355
 
356
356
  Parameters
357
357
  ----------
@@ -361,9 +361,9 @@ class TimberElement(Element, abc.ABC):
361
361
  Returns
362
362
  -------
363
363
  tuple(float, float)
364
- The perpendicular and parallel dimensions of the beam to the reference side.
365
- - Perpendicular dimension: The measurement normal to the reference side.
366
- - Parallel dimension: The measurement along y-axis of reference side.
364
+ Y-axis dimension, Normal direction dimension.
365
+ - Y-axis dimension: The element dimension along y-axis of reference side.
366
+ - Normal dimension: The element dimension normal to the reference side.
367
367
  """
368
368
  if ref_side_index in [1, 3]:
369
369
  return self.height, self.width
@@ -18,10 +18,12 @@ from .t_lap import TLapJoint
18
18
  from .x_lap import XLapJoint
19
19
  from .x_notch import XNotchJoint
20
20
  from .t_dovetail import TDovetailJoint
21
- from .t_tenon_mortise import TenonMortiseJoint
21
+ from .mortise_tenon import MortiseTenonJoint
22
+ from .t_tenon_mortise import TTenonMortiseJoint
23
+ from .l_tenon_mortise import LTenonMortiseJoint
22
24
  from .ball_node import BallNodeJoint
23
25
  from .y_butt import YButtJoint
24
- from .oligina import OliGinaJoint
26
+ from .oligina import TOliGinaJoint
25
27
  from .utilities import beam_ref_side_incidence
26
28
  from .utilities import beam_ref_side_incidence_with_vector
27
29
  from .utilities import point_centerline_towards_joint
@@ -34,13 +36,6 @@ from .plate_miter_joint import PlateMiterJoint
34
36
  from .panel_butt_joint import PanelLButtJoint
35
37
  from .panel_butt_joint import PanelTButtJoint
36
38
  from .panel_miter_joint import PanelMiterJoint
37
- from .analyzers import NBeamKDTreeAnalyzer
38
- from .analyzers import TripletAnalyzer
39
- from .analyzers import QuadAnalyzer
40
- from .analyzers import CompositeAnalyzer
41
- from .analyzers import Cluster
42
- from .analyzers import BeamGroupAnalyzer
43
- from .analyzers import MaxNCompositeAnalyzer
44
39
 
45
40
  __all__ = [
46
41
  "Joint",
@@ -64,10 +59,12 @@ __all__ = [
64
59
  "PlateConnectionSolver",
65
60
  "find_neighboring_elements",
66
61
  "TDovetailJoint",
62
+ "MortiseTenonJoint",
67
63
  "BallNodeJoint",
68
- "TenonMortiseJoint",
64
+ "TTenonMortiseJoint",
65
+ "LTenonMortiseJoint",
69
66
  "YButtJoint",
70
- "OliGinaJoint",
67
+ "TOliGinaJoint",
71
68
  "beam_ref_side_incidence",
72
69
  "beam_ref_side_incidence_with_vector",
73
70
  "point_centerline_towards_joint",
@@ -80,11 +77,4 @@ __all__ = [
80
77
  "PanelLButtJoint",
81
78
  "PanelTButtJoint",
82
79
  "PanelMiterJoint",
83
- "NBeamKDTreeAnalyzer",
84
- "TripletAnalyzer",
85
- "QuadAnalyzer",
86
- "CompositeAnalyzer",
87
- "Cluster",
88
- "BeamGroupAnalyzer",
89
- "MaxNCompositeAnalyzer",
90
80
  ]
@@ -3,12 +3,43 @@ from itertools import combinations
3
3
  from compas.data import Data
4
4
  from compas.geometry import Point
5
5
  from compas.geometry import distance_point_line
6
+ from compas.tolerance import TOL
6
7
 
7
8
  from compas_timber.errors import BeamJoiningError
9
+ from compas_timber.utils import distance_segment_segment_points
8
10
 
9
11
  from .solver import JointTopology
10
12
 
11
13
 
14
+ def location_from_centerlines(beams):
15
+ """Compute the approximate joint location from two beam centerlines.
16
+
17
+ Returns the closest point between the two centerlines, or the midpoint
18
+ between them when the beams are skew (non-intersecting).
19
+
20
+ Parameters
21
+ ----------
22
+ beams : list(:class:`~compas_timber.elements.Beam`)
23
+ A list of two beams for which to calculate the joint location.
24
+
25
+ Returns
26
+ -------
27
+ :class:`~compas.geometry.Point`
28
+
29
+ """
30
+ if len(beams) != 2:
31
+ raise ValueError(
32
+ "Automatic location calculation only works for joints connecting 2 elements. "
33
+ "Please set the location manually or implement a custom location calculation for your joint type."
34
+ )
35
+ beam_a, beam_b = beams
36
+ distance, point_a, point_b = distance_segment_segment_points(beam_a.centerline, beam_b.centerline)
37
+ point_a, point_b = Point(*point_a), Point(*point_b)
38
+ if not TOL.is_zero(distance):
39
+ return (point_a + point_b) / 2.0
40
+ return point_a
41
+
42
+
12
43
  class Joint(Data):
13
44
  """Base class for a joint connecting two beams.
14
45
 
@@ -48,13 +79,16 @@ class Joint(Data):
48
79
 
49
80
  def __init__(self, topology=None, location=None, name=None, **kwargs):
50
81
  super().__init__(name=name)
51
- self._topology = topology if topology is not None else JointTopology.TOPO_UNKNOWN
52
- self._location = location or Point(0, 0, 0)
82
+ self._topology = None
83
+ self._location = None
84
+ self.topology = topology
85
+ if location:
86
+ self.location = location
53
87
 
54
88
  @property
55
89
  def __data__(self):
56
90
  # type: () -> dict
57
- return {"name": self.name}
91
+ return {"name": self.name, "topology": self.topology, "location": self._location}
58
92
 
59
93
  def __repr__(self):
60
94
  return '{}(name="{}")'.format(self.__class__.__name__, self.name)
@@ -65,11 +99,16 @@ class Joint(Data):
65
99
 
66
100
  @topology.setter
67
101
  def topology(self, value):
68
- """Set the topology of the joint."""
69
- self._topology = value
102
+ self._topology = value or JointTopology.TOPO_UNKNOWN
70
103
 
71
104
  @property
72
105
  def location(self):
106
+ if self._location is None and all(self.elements):
107
+ self._location = location_from_centerlines(self.elements)
108
+
109
+ if self._location is None:
110
+ raise ValueError("Location of the joint could not be determined. Please set it manually.")
111
+
73
112
  return self._location
74
113
 
75
114
  @location.setter
@@ -5,7 +5,7 @@ from .plate_joint import PlateJoint
5
5
  class JointCandidate(Joint):
6
6
  """A JointCandidate is an information-only joint, which does not add any features to the elements it connects.
7
7
 
8
- It is used to create a first-pass joinery information which can be later used to perform analysis using :class:`~compas_timber.connections.analyzers.BeamGroupAnalyzer`.
8
+ It is used to create a first-pass joinery information which can be later used to perform analysis using :class:`~compas_timber.analyzers.BeamGroupAnalyzer`.
9
9
 
10
10
  Please use `JointCandidate.create()` to properly create an instance of this class and associate it with an model.
11
11
 
@@ -70,7 +70,7 @@ class JointCandidate(Joint):
70
70
  class PlateJointCandidate(PlateJoint, JointCandidate):
71
71
  """A PlateJointCandidate is an information-only joint for plate connections.
72
72
 
73
- It is used to create a first-pass joinery information which can be later used to perform analysis using :class:`~compas_timber.connections.analyzers.BeamGroupAnalyzer`.
73
+ It is used to create a first-pass joinery information which can be later used to perform analysis using :class:`~compas_timber.analyzers.BeamGroupAnalyzer`.
74
74
 
75
75
  Parameters
76
76
  ----------