compas-timber 2.2.0__tar.gz → 3.0.0__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 (117) hide show
  1. {compas_timber-2.2.0/src/compas_timber.egg-info → compas_timber-3.0.0}/PKG-INFO +40 -12
  2. {compas_timber-2.2.0 → compas_timber-3.0.0}/README.md +27 -10
  3. {compas_timber-2.2.0 → compas_timber-3.0.0}/pyproject.toml +48 -40
  4. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/__init__.py +1 -1
  5. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/base.py +52 -54
  6. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/__init__.py +8 -2
  7. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/ball_node.py +3 -1
  8. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/butt_joint.py +37 -48
  9. compas_timber-3.0.0/src/compas_timber/connections/candidate_dispatch.py +74 -0
  10. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/cluster.py +24 -16
  11. compas_timber-3.0.0/src/compas_timber/connections/composite_joint.py +109 -0
  12. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/i_simple_scarf.py +0 -7
  13. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/joint.py +59 -11
  14. compas_timber-3.0.0/src/compas_timber/connections/joint_candidate.py +213 -0
  15. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/l_butt.py +6 -6
  16. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/l_french_ridge_lap.py +3 -8
  17. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/l_lap.py +0 -4
  18. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/l_miter.py +7 -93
  19. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/l_tenon_mortise.py +2 -4
  20. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/lap_joint.py +3 -9
  21. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/mortise_tenon.py +0 -8
  22. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/oligina.py +8 -6
  23. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/panel_joint.py +1 -3
  24. compas_timber-3.0.0/src/compas_timber/connections/panel_layer_butt_joint.py +66 -0
  25. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/plate_joint.py +16 -1
  26. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/plate_miter_joint.py +11 -1
  27. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/solver.py +10 -5
  28. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_birdsmouth.py +2 -8
  29. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_butt.py +5 -2
  30. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_dovetail.py +2 -8
  31. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_lap.py +2 -6
  32. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_step_joint.py +5 -9
  33. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/t_tenon_mortise.py +2 -4
  34. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/utilities.py +1 -95
  35. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/x_lap.py +1 -5
  36. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/x_notch.py +3 -8
  37. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/y_butt.py +3 -8
  38. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/__init__.py +8 -8
  39. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/beam.py +8 -8
  40. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/fastener.py +0 -8
  41. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/fasteners/ball_node_fastener.py +6 -4
  42. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/fasteners/plate_fastener.py +6 -4
  43. compas_timber-3.0.0/src/compas_timber/elements/layer.py +522 -0
  44. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/panel.py +113 -29
  45. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/plate.py +22 -22
  46. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/plate_geometry.py +92 -93
  47. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/errors.py +19 -5
  48. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/__init__.py +2 -0
  49. compas_timber-3.0.0/src/compas_timber/fabrication/basic_characters.zip +0 -0
  50. compas_timber-3.0.0/src/compas_timber/fabrication/birds_mouth.py +697 -0
  51. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/btlx.py +12 -19
  52. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/double_cut.py +5 -4
  53. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/dovetail_mortise.py +10 -6
  54. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/dovetail_tenon.py +13 -7
  55. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/drilling.py +8 -7
  56. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/free_contour.py +4 -2
  57. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/french_ridge_lap.py +16 -14
  58. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/jack_cut.py +12 -6
  59. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/lap.py +14 -14
  60. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/longitudinal_cut.py +19 -6
  61. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/mortise.py +10 -6
  62. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/pocket.py +30 -14
  63. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/simple_scarf.py +21 -27
  64. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/slot.py +5 -4
  65. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/step_joint.py +32 -15
  66. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/step_joint_notch.py +19 -16
  67. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/tenon.py +14 -8
  68. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/fabrication/text.py +61 -15
  69. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/geometry.py +141 -2
  70. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/model.py +187 -85
  71. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/panel_features/opening.py +4 -4
  72. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/panel_features/panel_features.py +1 -1
  73. compas_timber-3.0.0/src/compas_timber/proto/__init__.py +0 -0
  74. compas_timber-3.0.0/src/compas_timber/proto/common_pb2.py +48 -0
  75. compas_timber-3.0.0/src/compas_timber/proto/common_pb2.pyi +67 -0
  76. compas_timber-3.0.0/src/compas_timber/proto/connections_pb2.py +109 -0
  77. compas_timber-3.0.0/src/compas_timber/proto/connections_pb2.pyi +757 -0
  78. compas_timber-3.0.0/src/compas_timber/proto/conversions.py +1001 -0
  79. compas_timber-3.0.0/src/compas_timber/proto/elements_pb2.py +85 -0
  80. compas_timber-3.0.0/src/compas_timber/proto/elements_pb2.pyi +284 -0
  81. compas_timber-3.0.0/src/compas_timber/proto/fabrication_pb2.py +97 -0
  82. compas_timber-3.0.0/src/compas_timber/proto/fabrication_pb2.pyi +786 -0
  83. compas_timber-3.0.0/src/compas_timber/proto/model_pb2.py +71 -0
  84. compas_timber-3.0.0/src/compas_timber/proto/model_pb2.pyi +172 -0
  85. compas_timber-3.0.0/src/compas_timber/proto/panel_features_pb2.py +42 -0
  86. compas_timber-3.0.0/src/compas_timber/proto/panel_features_pb2.pyi +57 -0
  87. compas_timber-3.0.0/src/compas_timber/proto/planning_pb2.py +59 -0
  88. compas_timber-3.0.0/src/compas_timber/proto/planning_pb2.pyi +176 -0
  89. compas_timber-3.0.0/src/compas_timber/proto/structural_pb2.py +43 -0
  90. compas_timber-3.0.0/src/compas_timber/proto/structural_pb2.pyi +35 -0
  91. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/rhino/__init__.py +1 -1
  92. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/structural.py +6 -2
  93. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/utils/r_tree.py +1 -1
  94. {compas_timber-2.2.0 → compas_timber-3.0.0/src/compas_timber.egg-info}/PKG-INFO +40 -12
  95. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber.egg-info/SOURCES.txt +25 -1
  96. compas_timber-3.0.0/src/compas_timber.egg-info/entry_points.txt +2 -0
  97. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber.egg-info/requires.txt +9 -0
  98. compas_timber-2.2.0/src/compas_timber/connections/joint_candidate.py +0 -66
  99. compas_timber-2.2.0/src/compas_timber/elements/features.py +0 -238
  100. {compas_timber-2.2.0 → compas_timber-3.0.0}/LICENSE +0 -0
  101. {compas_timber-2.2.0 → compas_timber-3.0.0}/setup.cfg +0 -0
  102. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/btlx/__init__.py +0 -0
  103. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/btlx/reader.py +0 -0
  104. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/panel_butt_joint.py +0 -0
  105. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/panel_miter_joint.py +0 -0
  106. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/connections/plate_butt_joint.py +0 -0
  107. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/elements/fasteners/__init__.py +0 -0
  108. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/panel_features/__init__.py +0 -0
  109. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/panel_features/panel_connection_interface.py +0 -0
  110. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/planning/__init__.py +0 -0
  111. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/planning/nesting.py +0 -0
  112. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/planning/sequencer.py +0 -0
  113. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/rhino/install.py +0 -0
  114. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber/utils/__init__.py +0 -0
  115. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber.egg-info/dependency_links.txt +0 -0
  116. {compas_timber-2.2.0 → compas_timber-3.0.0}/src/compas_timber.egg-info/not-zip-safe +0 -0
  117. {compas_timber-2.2.0 → compas_timber-3.0.0}/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.2.0
3
+ Version: 3.0.0
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>, Nicolas Benjamin Boscoboinik <boscoboinik@arch.ethz.ch>
5
+ Author: Aleksandra Anna Apolinarska, Chen Kasirer, Gonzalo Casas, Jonas Haldemann, Oliver Appling Bucklin, Aurèle L. Gheyselinck, Panayiotis Papacharalambous, Anastasiia Stryzhevska, Jelle Feringa, Joseph Kenny, Beverly Lytle, Eric Gozzi, Rodrigo Arca Zimmermann, Nicolas Benjamin Boscoboinik, Paul Jaeggi, Matthias Helmreich, Daniel Nunes Locatelli
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
@@ -13,6 +13,9 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
16
19
  Requires-Python: >=3.9
17
20
  Description-Content-Type: text/markdown
18
21
  License-File: LICENSE
@@ -20,9 +23,12 @@ Requires-Dist: rtree
20
23
  Requires-Dist: compas<3.0,>=2.15.1
21
24
  Requires-Dist: compas_model==0.9.2
22
25
  Requires-Dist: scipy>=1.1
26
+ Requires-Dist: compas_brep>=0.3.1
27
+ Requires-Dist: compas_pb<2.0,>=1.2.0
23
28
  Provides-Extra: dev
24
29
  Requires-Dist: bump-my-version; extra == "dev"
25
30
  Requires-Dist: compas_invocations2[mkdocs]>=1.0.2; extra == "dev"
31
+ Requires-Dist: mkdocs-panzoom-plugin; extra == "dev"
26
32
  Requires-Dist: invoke>=0.14; extra == "dev"
27
33
  Requires-Dist: ruff; extra == "dev"
28
34
  Requires-Dist: twine; extra == "dev"
@@ -34,15 +40,19 @@ Requires-Dist: tomlkit; extra == "dev"
34
40
  Requires-Dist: pytest-cov; extra == "dev"
35
41
  Requires-Dist: pytest-randomly; extra == "dev"
36
42
  Requires-Dist: pre-commit; extra == "dev"
43
+ Requires-Dist: pyyaml; extra == "dev"
44
+ Requires-Dist: compas_brep[occ]; extra == "dev"
37
45
  Provides-Extra: gh
38
46
  Requires-Dist: compas_invocations2; extra == "gh"
39
47
  Requires-Dist: tomlkit; extra == "gh"
40
48
  Requires-Dist: pythonnet; extra == "gh"
49
+ Provides-Extra: viz
50
+ Requires-Dist: compas_brep[occ]; extra == "viz"
51
+ Requires-Dist: compas_viewer; extra == "viz"
41
52
  Dynamic: license-file
42
53
 
43
54
  <h1>
44
- <img src="docs\_logo\PNG_tranparent-background.png" alt="Logo" width="70" height="70" style="vertical-align:middle">
45
- COMPAS Timber
55
+ <img src="docs/_logo/PNG_tranparent-background.png" alt="COMPAS Timber logo" width="70" height="70" align="absmiddle"> COMPAS Timber
46
56
  </h1>
47
57
 
48
58
 
@@ -52,26 +62,44 @@ Dynamic: license-file
52
62
  [![pip downloads](https://img.shields.io/pypi/dm/compas_timber)](https://pypi.python.org/project/compas_timber)
53
63
  [![PyPI Package latest release](https://img.shields.io/pypi/v/compas_timber.svg)](https://pypi.python.org/pypi/compas_timber)
54
64
  [![Supported implementations](https://img.shields.io/pypi/implementation/compas_timber.svg)](https://pypi.python.org/pypi/compas_timber)
55
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7934267.svg)](https://doi.org/10.5281/zenodo.7934267)
65
+ [![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.7934266-blue)](https://doi.org/10.5281/zenodo.7934266)
56
66
  [![Twitter Follow](https://img.shields.io/twitter/follow/compas_dev?style=social)](https://twitter.com/compas_dev)
57
67
  [![Made with COMPAS](https://compas.dev/badge.svg)](https://compas.dev/#/)
58
68
  ![COMPAS Timber](https://raw.githubusercontent.com/gramaziokohler/compas_timber/main/docs/_images/compas_timber.jpg)
59
69
 
60
- `compas_timber` is a user-friendly open-source software toolkit to streamline the design of timber frame structures. Despite its advances in digitalization compared to other building techniques, timber construction is often perceived as a challenging field, involving intricate processes in design, planning, coordination, and fabrication. We aim to increase the use of timber in architecture by lowering the threshold of creating versatile and resource-aware designs.
70
+ `compas_timber` is a user-friendly open-source software toolkit that streamlines the design-to-fabrication workflow of timber frame structures. A project is designed and refined in a single digital model that also produces the manufacturing data, so it can go from first concept to fabrication without re-modeling or back-and-forth rework along the way. By lowering the threshold for creating versatile and resource-aware timber designs, we aim to increase the use of timber in architecture.
61
71
 
62
72
  ## Installation
63
73
 
64
- > It is recomended you install `compas_timber` inside a virtual environment.
74
+ We recommend managing environments and dependencies with [uv](https://docs.astral.sh/uv/), but a plain `pip install compas_timber` works just as well.
75
+
76
+ Add `compas_timber` to your project:
77
+
78
+ ```bash
79
+ uv add compas_timber
80
+ ```
81
+
82
+ To visualize models outside of Rhino, install with the `viz` extra, which adds [compas_viewer](https://github.com/compas-dev/compas_viewer) and `compas_brep[occ]`:
83
+
84
+ ```bash
85
+ uv add "compas_timber[viz]"
86
+ ```
87
+
88
+ For a development setup, clone the repository and sync the environment:
65
89
 
66
90
  ```bash
67
- pip install compas_timber
91
+ git clone https://github.com/gramaziokohler/compas_timber.git
92
+ cd compas_timber
93
+ uv sync --extra dev
94
+ uv run pytest
68
95
  ```
69
96
 
70
97
  ## First Steps
71
98
 
72
99
  * [Documentation](https://gramaziokohler.github.io/compas_timber/)
73
- * [COMPAS TIMBER Grasshopper Tutorial](https://gramaziokohler.github.io/compas_timber/latest/tutorials.html)
74
- * [COMPAS TIMBER API Reference](https://gramaziokohler.github.io/compas_timber/latest/api.html)
100
+ * [COMPAS TIMBER Grasshopper Plugin (timber_design)](https://github.com/gramaziokohler/timber_design)
101
+ * [COMPAS TIMBER Grasshopper Tutorial](https://gramaziokohler.github.io/timber_design/)
102
+ * [COMPAS TIMBER API Reference](https://gramaziokohler.github.io/compas_timber/latest/api/compas_timber.model/)
75
103
 
76
104
  ## Questions and feedback
77
105
 
@@ -86,9 +114,9 @@ If you found an issue or have a suggestion for a dandy new feature, please file
86
114
 
87
115
  We love contributions!
88
116
 
89
- Check the [Contributor's Guide](https://github.com/gramaziokohler/compas_timber/blob/main/CONTRIBUTING.md)
117
+ Check the [Contributor's Guide](CONTRIBUTING.md)
90
118
  for more details.
91
119
 
92
120
  ## Credits
93
121
 
94
- `compas_timber` is currently developed by Gramazio Kohler Research. See the [list of authors](https://github.com/gramaziokohler/compas_timber/blob/main/AUTHORS.md) for a complete overview.
122
+ `compas_timber` is currently developed by Gramazio Kohler Research. See [`CITATION.cff`](CITATION.cff) for a complete list of authors.
@@ -1,6 +1,5 @@
1
1
  <h1>
2
- <img src="docs\_logo\PNG_tranparent-background.png" alt="Logo" width="70" height="70" style="vertical-align:middle">
3
- COMPAS Timber
2
+ <img src="docs/_logo/PNG_tranparent-background.png" alt="COMPAS Timber logo" width="70" height="70" align="absmiddle"> COMPAS Timber
4
3
  </h1>
5
4
 
6
5
 
@@ -10,26 +9,44 @@
10
9
  [![pip downloads](https://img.shields.io/pypi/dm/compas_timber)](https://pypi.python.org/project/compas_timber)
11
10
  [![PyPI Package latest release](https://img.shields.io/pypi/v/compas_timber.svg)](https://pypi.python.org/pypi/compas_timber)
12
11
  [![Supported implementations](https://img.shields.io/pypi/implementation/compas_timber.svg)](https://pypi.python.org/pypi/compas_timber)
13
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7934267.svg)](https://doi.org/10.5281/zenodo.7934267)
12
+ [![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.7934266-blue)](https://doi.org/10.5281/zenodo.7934266)
14
13
  [![Twitter Follow](https://img.shields.io/twitter/follow/compas_dev?style=social)](https://twitter.com/compas_dev)
15
14
  [![Made with COMPAS](https://compas.dev/badge.svg)](https://compas.dev/#/)
16
15
  ![COMPAS Timber](https://raw.githubusercontent.com/gramaziokohler/compas_timber/main/docs/_images/compas_timber.jpg)
17
16
 
18
- `compas_timber` is a user-friendly open-source software toolkit to streamline the design of timber frame structures. Despite its advances in digitalization compared to other building techniques, timber construction is often perceived as a challenging field, involving intricate processes in design, planning, coordination, and fabrication. We aim to increase the use of timber in architecture by lowering the threshold of creating versatile and resource-aware designs.
17
+ `compas_timber` is a user-friendly open-source software toolkit that streamlines the design-to-fabrication workflow of timber frame structures. A project is designed and refined in a single digital model that also produces the manufacturing data, so it can go from first concept to fabrication without re-modeling or back-and-forth rework along the way. By lowering the threshold for creating versatile and resource-aware timber designs, we aim to increase the use of timber in architecture.
19
18
 
20
19
  ## Installation
21
20
 
22
- > It is recomended you install `compas_timber` inside a virtual environment.
21
+ We recommend managing environments and dependencies with [uv](https://docs.astral.sh/uv/), but a plain `pip install compas_timber` works just as well.
22
+
23
+ Add `compas_timber` to your project:
24
+
25
+ ```bash
26
+ uv add compas_timber
27
+ ```
28
+
29
+ To visualize models outside of Rhino, install with the `viz` extra, which adds [compas_viewer](https://github.com/compas-dev/compas_viewer) and `compas_brep[occ]`:
30
+
31
+ ```bash
32
+ uv add "compas_timber[viz]"
33
+ ```
34
+
35
+ For a development setup, clone the repository and sync the environment:
23
36
 
24
37
  ```bash
25
- pip install compas_timber
38
+ git clone https://github.com/gramaziokohler/compas_timber.git
39
+ cd compas_timber
40
+ uv sync --extra dev
41
+ uv run pytest
26
42
  ```
27
43
 
28
44
  ## First Steps
29
45
 
30
46
  * [Documentation](https://gramaziokohler.github.io/compas_timber/)
31
- * [COMPAS TIMBER Grasshopper Tutorial](https://gramaziokohler.github.io/compas_timber/latest/tutorials.html)
32
- * [COMPAS TIMBER API Reference](https://gramaziokohler.github.io/compas_timber/latest/api.html)
47
+ * [COMPAS TIMBER Grasshopper Plugin (timber_design)](https://github.com/gramaziokohler/timber_design)
48
+ * [COMPAS TIMBER Grasshopper Tutorial](https://gramaziokohler.github.io/timber_design/)
49
+ * [COMPAS TIMBER API Reference](https://gramaziokohler.github.io/compas_timber/latest/api/compas_timber.model/)
33
50
 
34
51
  ## Questions and feedback
35
52
 
@@ -44,9 +61,9 @@ If you found an issue or have a suggestion for a dandy new feature, please file
44
61
 
45
62
  We love contributions!
46
63
 
47
- Check the [Contributor's Guide](https://github.com/gramaziokohler/compas_timber/blob/main/CONTRIBUTING.md)
64
+ Check the [Contributor's Guide](CONTRIBUTING.md)
48
65
  for more details.
49
66
 
50
67
  ## Credits
51
68
 
52
- `compas_timber` is currently developed by Gramazio Kohler Research. See the [list of authors](https://github.com/gramaziokohler/compas_timber/blob/main/AUTHORS.md) for a complete overview.
69
+ `compas_timber` is currently developed by Gramazio Kohler Research. See [`CITATION.cff`](CITATION.cff) for a complete list of authors.
@@ -6,20 +6,23 @@
6
6
  name = "compas_timber"
7
7
  description = "COMPAS package for modeling, designing and fabricating timber assemblies."
8
8
  authors = [
9
- { name = "Aleksandra Anna Apolinarska", email = "apolinarska@arch.ethz.ch" },
10
- { name = "Chen Kasirer", email = "kasirer@arch.ethz.ch" },
11
- { name = "Gonzalo Casas", email = "casas@arch.ethz.ch" },
12
- { name = "Jonas Haldemann", email = "haldemann@arch.ethz.ch" },
13
- { name = "Oliver Appling Bucklin", email = "bucklin@arch.ethz.ch" },
14
- { name = "Aurèle L. Gheyselinck", email = "gheyselinck@arch.ethz.ch" },
15
- { name = "Panayiotis Papacharalambous", email = "papacharalambous@arch.ethz.ch" },
16
- { name = "Anastasiia Stryzhevska", email = "astryzhevska@arch.ethz.ch" },
17
- { name = "Jelle Feringa", email = "jelleferinga@gmail.com" },
18
- { name = "Joseph Kenny", email = "jk6372@princeton.edu" },
19
- { name = "Beverly Lytle", email = "lytle@arch.ethz.ch" },
20
- { name = "Eric Gozzi", email = "eric.gozzi@arch.ethz.ch" },
21
- { name = "Rodrigo Arca Zimmermann", email = "rodrigo.arca@gmail.com" },
22
- { name = "Nicolas Benjamin Boscoboinik", email = "boscoboinik@arch.ethz.ch" }
9
+ {name = "Aleksandra Anna Apolinarska"},
10
+ {name = "Chen Kasirer"},
11
+ {name = "Gonzalo Casas"},
12
+ {name = "Jonas Haldemann"},
13
+ {name = "Oliver Appling Bucklin"},
14
+ {name = "Aurèle L. Gheyselinck"},
15
+ {name = "Panayiotis Papacharalambous"},
16
+ {name = "Anastasiia Stryzhevska"},
17
+ {name = "Jelle Feringa"},
18
+ {name = "Joseph Kenny"},
19
+ {name = "Beverly Lytle"},
20
+ {name = "Eric Gozzi"},
21
+ {name = "Rodrigo Arca Zimmermann"},
22
+ {name = "Nicolas Benjamin Boscoboinik"},
23
+ {name = "Paul Jaeggi"},
24
+ {name = "Matthias Helmreich"},
25
+ {name = "Daniel Nunes Locatelli"},
23
26
  ]
24
27
  license = "MIT"
25
28
  readme = "README.md"
@@ -33,19 +36,25 @@ classifiers = [
33
36
  "Programming Language :: Python :: 3.9",
34
37
  "Programming Language :: Python :: 3.10",
35
38
  "Programming Language :: Python :: 3.11",
39
+ "Programming Language :: Python :: 3.12",
40
+ "Programming Language :: Python :: 3.13",
41
+ "Programming Language :: Python :: 3.14",
36
42
  ]
37
43
 
38
44
  dependencies = [
39
45
  "rtree",
40
46
  "compas >=2.15.1, <3.0",
41
47
  "compas_model == 0.9.2", # pin until first release
42
- "scipy >= 1.1"
48
+ "scipy >= 1.1",
49
+ "compas_brep >= 0.3.1",
50
+ "compas_pb >= 1.2.0, < 2.0",
43
51
  ]
44
52
 
45
53
  [project.optional-dependencies]
46
54
  dev = [
47
55
  "bump-my-version",
48
56
  "compas_invocations2[mkdocs]>=1.0.2",
57
+ "mkdocs-panzoom-plugin",
49
58
  "invoke >=0.14",
50
59
  "ruff",
51
60
  "twine",
@@ -57,6 +66,8 @@ dev = [
57
66
  "pytest-cov",
58
67
  "pytest-randomly",
59
68
  "pre-commit",
69
+ "pyyaml",
70
+ "compas_brep[occ]"
60
71
  ]
61
72
 
62
73
  gh = [
@@ -65,15 +76,23 @@ gh = [
65
76
  "pythonnet",
66
77
  ]
67
78
 
79
+ viz = [
80
+ "compas_brep[occ]",
81
+ "compas_viewer"
82
+ ]
83
+
68
84
  [project.urls]
69
85
  Homepage = "https://gramaziokohler.github.io/compas_timber/latest/"
70
86
  Repository = "https://github.com/gramaziokohler/compas_timber"
71
87
 
88
+ [project.entry-points.'compas_pb.plugins']
89
+ serializers = 'compas_timber.proto.conversions'
90
+
72
91
  # ============================================================================
73
92
  # Setup and build
74
93
  # ============================================================================
75
94
  [build-system]
76
- requires = ["setuptools>=68", "wheel"]
95
+ requires = ["setuptools>=68", "wheel", "compas_pb >= 1.2.0, < 2.0", "grpcio-tools"]
77
96
  build-backend = "setuptools.build_meta"
78
97
 
79
98
  [tool.setuptools]
@@ -83,42 +102,25 @@ zip-safe = false
83
102
 
84
103
  [tool.setuptools.dynamic]
85
104
  version = { attr = "compas_timber.__version__" }
86
- dependencies = { file = "requirements.txt" }
87
- optional-dependencies = { dev = { file = "requirements-dev.txt" } }
88
105
 
89
106
  [tool.setuptools.packages.find]
90
107
  where = ["src"]
91
108
 
92
109
  [tool.setuptools.package-data]
110
+ "compas_timber.fabrication" = ["basic_characters.zip"]
93
111
 
94
112
  # ============================================================================
95
113
  # Release
96
114
  # ============================================================================
97
115
 
116
+ # `release-pr` bumps the version and `release-check` validates it; both support
117
+ # stable semantic versions only, and both drive the commit/tag through the
118
+ # release pull request rather than through bump-my-version.
98
119
  [tool.bumpversion]
99
- current_version = "2.2.0"
120
+ current_version = "3.0.0"
100
121
  message = "Bump version to {new_version}"
101
- commit = true
102
- tag = true
103
- parse = """(?x)
104
- (?P<major>0|[1-9]\\d*)\\.
105
- (?P<minor>0|[1-9]\\d*)\\.
106
- (?P<patch>0|[1-9]\\d*)
107
- (?:
108
- - # dash separator for pre-release section
109
- (?P<pre_l>[a-zA-Z-]+) # pre-release label
110
- (?P<pre_n>0|[1-9]\\d*) # pre-release version number
111
- )? # pre-release section is optional
112
- """
113
- serialize = [
114
- "{major}.{minor}.{patch}-{pre_l}{pre_n}",
115
- "{major}.{minor}.{patch}",
116
- ]
117
-
118
- [tool.bumpversion.parts.pre_l]
119
- values = ["dev", "rc", "final"]
120
- optional_value = "final"
121
- first_value = "final"
122
+ commit = false
123
+ tag = false
122
124
 
123
125
  [[tool.bumpversion.files]]
124
126
  filename = "src/compas_timber/__init__.py"
@@ -153,6 +155,9 @@ forced_separate = "test_compas_timber"
153
155
  line-length = 179
154
156
  indent-width = 4
155
157
  target-version = "py39"
158
+ # protoc-generated modules are not authored by hand and are regenerated by
159
+ # `invoke pre_build`; linting them makes `invoke lint` fail in CI.
160
+ extend-exclude = ["*_pb2.py", "*_pb2.pyi"]
156
161
 
157
162
  [tool.ruff.lint]
158
163
  select = ["E", "F", "I"]
@@ -192,3 +197,6 @@ python_files = [
192
197
  addopts = "-ra --strict --doctest-modules --doctest-glob=*.rst --tb=short"
193
198
  doctest_optionflags= "NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL ALLOW_UNICODE ALLOW_BYTES NUMBER"
194
199
  filterwarnings = "ignore::DeprecationWarning"
200
+ markers = [
201
+ "requires_occ: marks tests that need the OCC Brep backend, which is not available on Python 3.9",
202
+ ]
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import compas
3
3
 
4
- __version__ = "2.2.0"
4
+ __version__ = "3.0.0"
5
5
 
6
6
  HERE = os.path.dirname(__file__)
7
7
  HOME = os.path.abspath(os.path.join(HERE, "..", ".."))
@@ -1,5 +1,4 @@
1
1
  import abc
2
- from functools import wraps
3
2
 
4
3
  from compas.geometry import Frame
5
4
  from compas.geometry import Line
@@ -7,21 +6,42 @@ from compas.geometry import PlanarSurface
7
6
  from compas.geometry import Transformation
8
7
  from compas.geometry import Vector
9
8
  from compas_model.elements import Element
10
- from compas_model.elements import reset_computed
11
9
 
10
+ # Cached attributes, in two tiers. Element classes extend these with their own in class attributes of the same name.
11
+ # Element tier: derived from the element's own data (its geometry, features, transformation).
12
+ ELEMENT_CACHE = ("_elementgeometry",)
13
+ # Model tier: derived from the element's placement in the model, i.e. also from its ancestors' transformations.
14
+ MODEL_CACHE = ("_modeltransformation", "_modelgeometry", "_aabb", "_obb", "_collision_mesh", "_point", "_surface_mesh", "_volumetric_mesh")
12
15
 
13
- def reset_timber_attrs(f):
14
- """Decorator to reset cached timber-specific attributes."""
15
16
 
16
- @wraps(f)
17
- def wrapper(*args, **kwargs):
18
- self: TimberElement = args[0]
19
- self._blank = None
20
- self._ref_frame = None
21
- self._geometry = None # from Element
22
- return f(*args, **kwargs)
17
+ def clear_element_cache(element):
18
+ """Clear the cached data of `element` after its own data changed (geometry, features, transformation).
19
+
20
+ Its model-space data is derived from it, so this also calls :func:`clear_model_cache`.
21
+
22
+ Parameters
23
+ ----------
24
+ element : :class:`compas_model.elements.Element`
25
+
26
+ """
27
+ for name in getattr(element, "ELEMENT_CACHE", ELEMENT_CACHE):
28
+ setattr(element, name, None)
29
+ clear_model_cache(element)
30
+
31
+
32
+ def clear_model_cache(element):
33
+ """Clear the cached model-space data of `element` and of all its descendants, after its placement changed.
34
+
35
+ The descendants' placement is composed from `element`'s, so their model-space data is cleared too.
36
+
37
+ Parameters
38
+ ----------
39
+ element : :class:`compas_model.elements.Element`
23
40
 
24
- return wrapper
41
+ """
42
+ for e in [node.element for node in element.treenode.traverse()] if element.treenode else [element]:
43
+ for name in getattr(e, "MODEL_CACHE", MODEL_CACHE):
44
+ setattr(e, name, None)
25
45
 
26
46
 
27
47
  class TimberElement(Element, abc.ABC):
@@ -63,10 +83,16 @@ class TimberElement(Element, abc.ABC):
63
83
 
64
84
  """
65
85
 
86
+ MODEL_CACHE = MODEL_CACHE + ("_blank", "_ref_frame", "_geometry")
87
+
66
88
  @property
67
89
  def __data__(self):
68
90
  data = {}
69
- data["frame"] = self.frame
91
+ # not `self.frame`: that is `Frame.from_transformation(self.modeltransformation)`, the world-composed
92
+ # frame with the whole ancestor chain baked in. The constructor treats `frame` as `self.transformation`,
93
+ # the *local*, parent-relative placement - so for a nested element (a child of another element in the
94
+ # model tree), storing the world frame would double-apply the ancestor's transformation on deserialization.
95
+ data["frame"] = Frame.from_transformation(self.transformation)
70
96
  data["length"] = self.length
71
97
  data["width"] = self.width
72
98
  data["height"] = self.height
@@ -85,31 +111,6 @@ class TimberElement(Element, abc.ABC):
85
111
  self._ref_frame = None
86
112
  self.debug_info = []
87
113
 
88
- @reset_computed
89
- @reset_timber_attrs
90
- def _reset_computed_dummy(self):
91
- """Dummy method to trigger reset_computed decorator."""
92
- pass
93
-
94
- def reset_computed_properties(self):
95
- """Reset all computed/cached properties."""
96
- self._reset_computed_dummy()
97
-
98
- def clear_model_dependent_cache(self):
99
- """Clear cached attributes that depend on the element's position in the model hierarchy.
100
-
101
- Preserves model-independent caches such as ``_elementgeometry``, features,
102
- and blank extensions.
103
- """
104
- self._modeltransformation = None
105
- self._modelgeometry = None
106
- self._aabb = None
107
- self._obb = None
108
- self._collision_mesh = None
109
- self._blank = None
110
- self._ref_frame = None
111
- self._geometry = None
112
-
113
114
  @property
114
115
  def is_beam(self):
115
116
  return False
@@ -129,14 +130,15 @@ class TimberElement(Element, abc.ABC):
129
130
  return self._features
130
131
 
131
132
  @features.setter
132
- @reset_computed
133
133
  def features(self, features):
134
134
  self._features = features
135
+ clear_element_cache(self)
135
136
 
136
- @reset_timber_attrs
137
- def transform(self, transformation):
138
- # override to reset timber-specific cached attributes
139
- super().transform(transformation)
137
+ @Element.transformation.setter
138
+ def transformation(self, transformation):
139
+ # `Element.transform()` goes through here too
140
+ self._transformation = transformation
141
+ clear_element_cache(self)
140
142
 
141
143
  @property
142
144
  def geometry(self):
@@ -178,25 +180,21 @@ class TimberElement(Element, abc.ABC):
178
180
  self.remove_blank_extension()
179
181
  self.debug_info = []
180
182
 
181
- @reset_computed
182
- @reset_timber_attrs
183
183
  def add_feature(self, feature):
184
184
  # type: (BTLxProcessing) -> None
185
- """Adds one or more features to the beam.
185
+ """Adds a feature to the element.
186
186
 
187
187
  Parameters
188
188
  ----------
189
- feature : :class:`~compas_timber.fabrication.BTLxProcessing`)
189
+ feature : :class:`~compas_timber.fabrication.BTLxProcessing`
190
190
  The feature to be added.
191
191
 
192
192
  """
193
- self._features.append(feature) # type: ignore
193
+ self.add_features(feature)
194
194
 
195
- @reset_computed
196
- @reset_timber_attrs
197
195
  def add_features(self, features):
198
196
  # type: (BTLxProcessing | list[BTLxProcessing]) -> None
199
- """Adds one or more features to the beam.
197
+ """Adds one or more features to the element.
200
198
 
201
199
  Parameters
202
200
  ----------
@@ -204,15 +202,14 @@ class TimberElement(Element, abc.ABC):
204
202
  The feature or features to be added described as a BTLxProcessing or a list of BTLxProcessings.
205
203
 
206
204
  """
205
+ clear_element_cache(self)
207
206
  if not isinstance(features, list):
208
207
  features = [features]
209
208
  self._features.extend(features) # type: ignore
210
209
 
211
- @reset_computed
212
- @reset_timber_attrs
213
210
  def remove_features(self, features=None):
214
211
  # type: (None | BTLxProcessing | list[BTLxProcessing]) -> None
215
- """Removes a feature from the beam.
212
+ """Removes one or more features from the element.
216
213
 
217
214
  Parameters
218
215
  ----------
@@ -221,6 +218,7 @@ class TimberElement(Element, abc.ABC):
221
218
  If None, all features will be removed.
222
219
 
223
220
  """
221
+ clear_element_cache(self)
224
222
  if features is None:
225
223
  self._features = []
226
224
  else:
@@ -4,7 +4,6 @@ from .i_simple_scarf import ISimpleScarf
4
4
  from .l_butt import LButtJoint
5
5
  from .l_lap import LLapJoint
6
6
  from .l_miter import LMiterJoint
7
- from .l_miter import MiterPlaneSpec
8
7
  from .l_french_ridge_lap import LFrenchRidgeLapJoint
9
8
  from .lap_joint import LapJoint
10
9
  from .joint_candidate import JointCandidate
@@ -13,6 +12,7 @@ from .solver import ConnectionSolver
13
12
  from .solver import PlateConnectionSolver
14
13
  from .solver import JointTopology
15
14
  from .solver import find_neighboring_elements
15
+ from .candidate_dispatch import get_connection_candidate
16
16
  from .t_butt import TButtJoint
17
17
  from .t_step_joint import TStepJoint
18
18
  from .t_birdsmouth import TBirdsmouthJoint
@@ -40,8 +40,11 @@ from .plate_miter_joint import PlateMiterJoint
40
40
  from .panel_butt_joint import PanelLButtJoint
41
41
  from .panel_butt_joint import PanelTButtJoint
42
42
  from .panel_miter_joint import PanelMiterJoint
43
+ from .panel_layer_butt_joint import PanelLLayerButtJoint
43
44
  from .cluster import Cluster
44
45
  from .cluster import get_clusters_from_joint_candidates
46
+ from .cluster import get_topology_from_joints
47
+ from .composite_joint import CompositeJoint
45
48
 
46
49
 
47
50
  __all__ = [
@@ -66,6 +69,7 @@ __all__ = [
66
69
  "ConnectionSolver",
67
70
  "PlateConnectionSolver",
68
71
  "find_neighboring_elements",
72
+ "get_connection_candidate",
69
73
  "TDovetailJoint",
70
74
  "MortiseTenonJoint",
71
75
  "BallNodeJoint",
@@ -74,7 +78,6 @@ __all__ = [
74
78
  "YButtJoint",
75
79
  "TOliGinaJoint",
76
80
  "CutPlaneSpec",
77
- "MiterPlaneSpec",
78
81
  "beam_ref_side_incidence",
79
82
  "beam_ref_side_incidence_with_vector",
80
83
  "point_centerline_towards_joint",
@@ -89,6 +92,9 @@ __all__ = [
89
92
  "PanelLButtJoint",
90
93
  "PanelTButtJoint",
91
94
  "PanelMiterJoint",
95
+ "PanelLLayerButtJoint",
92
96
  "Cluster",
93
97
  "get_clusters_from_joint_candidates",
98
+ "get_topology_from_joints",
99
+ "CompositeJoint",
94
100
  ]
@@ -131,4 +131,6 @@ class BallNodeJoint(Joint):
131
131
  pt = beam.centerline.closest_point(self._node_point)
132
132
  interface.frame = Frame(pt, Vector.from_start_end(pt, beam.centerline.midpoint), beam.frame.zaxis)
133
133
  self.fastener.interfaces.append(interface)
134
- beam.add_features(interface.get_features(beam))
134
+ features = interface.get_features(beam)
135
+ beam.add_features(features)
136
+ self.features.extend(features)