yapCAD 0.2.5__py2.py3-none-any.whl → 0.3.1__py2.py3-none-any.whl

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.
yapcad/triangulator.py ADDED
@@ -0,0 +1,103 @@
1
+ """Triangulation helpers for yapCAD surfaces.
2
+
3
+ We delegate to ``mapbox-earcut`` (the fast ear clipping implementation
4
+ used by Mapbox GL) to keep the logic compact and reliable. The helper
5
+ routine in this file simply normalises yapCAD polygon inputs into the
6
+ format expected by earcut and converts the resulting indices back into
7
+ triangle vertex tuples.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Iterable, List, Sequence, Tuple
13
+
14
+ import numpy as np
15
+
16
+ try:
17
+ import mapbox_earcut as _earcut
18
+ except ImportError as exc: # pragma: no cover - import guard
19
+ raise ImportError(
20
+ "mapbox-earcut must be installed to triangulate polygons with holes"
21
+ ) from exc
22
+
23
+ from yapcad.geom import epsilon
24
+
25
+ Point2D = Tuple[float, float]
26
+
27
+
28
+ def triangulate_polygon(outer: Sequence[Sequence[float]],
29
+ holes: Iterable[Sequence[Sequence[float]]] | None = None
30
+ ) -> List[List[Point2D]]:
31
+ """Return triangles covering ``outer`` minus any ``holes``.
32
+
33
+ ``outer`` and each entry in ``holes`` is expected to be a sequence of
34
+ XY-like points. Degenerate loops (fewer than three distinct points)
35
+ are ignored. The returned triangles are lists of three ``(x, y)``
36
+ pairs. Downstream code is responsible for lifting the coordinates
37
+ into 3D and enforcing winding to match the target surface normal.
38
+ """
39
+
40
+ if holes is None:
41
+ holes = []
42
+
43
+ outer_loop = _prepare_loop(outer, want_ccw=True)
44
+ if len(outer_loop) < 3:
45
+ return []
46
+
47
+ point_map: List[Point2D] = []
48
+
49
+ ring_ends: List[int] = []
50
+
51
+ def _append(loop: Sequence[Point2D]) -> None:
52
+ for x, y in loop:
53
+ point_map.append((x, y))
54
+ ring_ends.append(len(point_map))
55
+
56
+ _append(outer_loop)
57
+
58
+ for hole in holes:
59
+ loop = _prepare_loop(hole, want_ccw=False)
60
+ if len(loop) < 3:
61
+ continue
62
+ _append(loop)
63
+
64
+ vertices = np.asarray(point_map, dtype=np.float32)
65
+ ring_array = np.asarray(ring_ends, dtype=np.uint32)
66
+ indices = _earcut.triangulate_float32(vertices, ring_array)
67
+ triangles: List[List[Point2D]] = []
68
+ for i in range(0, len(indices), 3):
69
+ triangles.append([point_map[indices[i]],
70
+ point_map[indices[i + 1]],
71
+ point_map[indices[i + 2]]])
72
+ return triangles
73
+
74
+
75
+ def _prepare_loop(points: Sequence[Sequence[float]], *, want_ccw: bool) -> List[Point2D]:
76
+ loop: List[Point2D] = []
77
+ for pt in points:
78
+ x, y = float(pt[0]), float(pt[1])
79
+ if loop and _near(loop[-1], (x, y)):
80
+ continue
81
+ loop.append((x, y))
82
+ if loop and _near(loop[0], loop[-1]):
83
+ loop.pop()
84
+ if len(loop) < 3:
85
+ return loop
86
+ area = _signed_area(loop)
87
+ if want_ccw and area < 0:
88
+ loop.reverse()
89
+ elif not want_ccw and area > 0:
90
+ loop.reverse()
91
+ return loop
92
+
93
+
94
+ def _near(p1: Point2D, p2: Point2D) -> bool:
95
+ return abs(p1[0] - p2[0]) <= epsilon and abs(p1[1] - p2[1]) <= epsilon
96
+
97
+
98
+ def _signed_area(loop: Sequence[Point2D]) -> float:
99
+ total = 0.0
100
+ for i, (x0, y0) in enumerate(loop):
101
+ x1, y1 = loop[(i + 1) % len(loop)]
102
+ total += x0 * y1 - x1 * y0
103
+ return total / 2.0
yapcad/xform.py CHANGED
@@ -44,7 +44,6 @@ import yapcad.geom as geom
44
44
 
45
45
  ## FIXME: only one class is defined for now
46
46
 
47
-
48
47
  class Matrix:
49
48
  """4x4 transformation matrix class for transforming homogemenous 3D coordinates"""
50
49
 
@@ -165,11 +164,17 @@ class Matrix:
165
164
  self.m[j] = x
166
165
 
167
166
 
168
- # matrix multiply. If x is a matrix, compute MX. If X is a
167
+ # matrix multiply. If x is a matrix, compute MX. If x is a
169
168
  # vector, compute Mx. If x is a scalar, compute xM. If x isn't any
170
- # of these, return False. Respects transpose flag.
171
-
169
+ # of these, a ValueError is raised. Respects transpose flag.
170
+
172
171
  def mul(self,x):
172
+ """Return the product of this matrix and ``x``.
173
+
174
+ ``x`` may be another :class:`Matrix`, a 4D vector, or a scalar. In
175
+ each case the appropriate matrix multiplication is performed. If
176
+ ``x`` is none of these types, a :class:`ValueError` is raised.
177
+ """
173
178
  if isinstance(x,Matrix):
174
179
  result = Matrix()
175
180
  for i in range(4):
@@ -232,7 +237,7 @@ def Rotation(axis,angle,inverse=False):
232
237
 
233
238
  def Translation(delta,inverse=False):
234
239
  if inverse:
235
- delta = mul(delta,-1.0)
240
+ delta = geom.scale3(delta, -1.0)
236
241
  dx = delta[0]
237
242
  dy = delta[1]
238
243
  dz = delta[2]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: yapCAD
3
- Version: 0.2.5
3
+ Version: 0.3.1
4
4
  Summary: yet another procedural CAD and computational geometry system
5
5
  Author-email: Richard DeVaul <richard.devaul@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/rdevaul/yapCAD/
@@ -8,31 +8,32 @@ Project-URL: Documentation, https://yapcad.readthedocs.io/en/latest/
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.7
11
+ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/x-rst
13
13
  License-File: LICENSE
14
14
  License-File: LICENSE.txt
15
15
  License-File: AUTHORS.rst
16
- Requires-Dist: ezdxf
17
- Requires-Dist: pyglet (<2)
18
- Requires-Dist: mpmath
19
- Provides-Extra: testing
20
- Requires-Dist: pytest ; extra == 'testing'
21
- Requires-Dist: pytest-cov ; extra == 'testing'
16
+ Requires-Dist: ezdxf>=1.1
17
+ Requires-Dist: pyglet<2,>=1.5
18
+ Requires-Dist: mpmath>=1.2
19
+ Requires-Dist: pyobjc-core; platform_system == "Darwin"
20
+ Requires-Dist: pyobjc-framework-Cocoa; platform_system == "Darwin"
21
+ Requires-Dist: pyobjc-framework-Quartz; platform_system == "Darwin"
22
22
  Provides-Extra: tests
23
- Requires-Dist: pytest ; extra == 'tests'
24
- Requires-Dist: pytest-cov ; extra == 'tests'
23
+ Requires-Dist: pytest; extra == "tests"
24
+ Requires-Dist: pytest-cov; extra == "tests"
25
+ Dynamic: license-file
25
26
 
26
27
  **yapCAD**
27
28
  ==========
28
29
 
29
30
  yet another procedural CAD and computational geometry system written in
30
- python 3
31
+ python 3, now with a growing focus on 3D generative design and STL export
31
32
 
32
- .. figure:: images/yapCadSplash.png
33
- :alt: **yapCAD** image
33
+ .. figure:: images/RocketDemoScreenshot.png
34
+ :alt: **yapCAD** rocket example
34
35
 
35
- **yapCAD** image
36
+ **yapCAD** rocket example
36
37
 
37
38
  what’s **yapCAD** for?
38
39
  ----------------------
@@ -41,8 +42,10 @@ First and foremost, **yapCAD** is a framework for creating
41
42
  `parametric <https://en.wikipedia.org/wiki/Parametric_design>`__,
42
43
  procedural, and
43
44
  `generative <https://en.wikipedia.org/wiki/Parametric_design>`__ design
44
- systems. You can also use **yapCAD** for other CAD, CAM, and
45
- computational geometry purposes.
45
+ systems. Starting with the 0.5 release, the emphasis has shifted toward
46
+ 3D solid workflows, including STL export for downstream slicing and
47
+ simulation, while retaining support for DXF generation and computational
48
+ geometry experiments.
46
49
 
47
50
  software status
48
51
  ---------------
@@ -83,28 +86,23 @@ clone the github repository as shown above, and make sure that your
83
86
  PYTHONPATH includes the cloned top-level ``yapCAD`` directory. You will
84
87
  find the examples in the ``yapCAD/examples`` directory.
85
88
 
86
- For a fully worked parametric design system, see the ``boxcut`` example.
89
+ For a fully worked 2D parametric design system, see the ``boxcut`` example.
90
+ For a 3D generative example that builds a multi-stage rocket, visualises
91
+ it, and exports STL, see ``examples/rocket_demo.py``.
87
92
 
88
93
  documentation
89
94
  ~~~~~~~~~~~~~
90
95
 
91
96
  Online **yapCAD** documentation can be found here:
92
- https://yapcad.readthedocs.io/en/latest/ — for some reason
93
- ``readthedocs.io`` isn’t generating the full module documentation, so
94
- you might want to build a local copy, as described below.
97
+ https://yapcad.readthedocs.io/en/latest/ — some module references lag
98
+ behind the latest 3D-focused APIs, so you may want to build a local copy
99
+ as described below to explore ``geometry_utils``, ``geometry_checks``,
100
+ ``metadata``, and ``io.stl``.
95
101
 
96
- To build the HTML **yapCAD** documentation locally, first make sure you
97
- have the sphinx package installed:
98
-
99
- ::
100
-
101
- pip install sphinx --user
102
-
103
- Then clone the github repository as shown above, ``cd`` to the
104
- ``yapCAD`` directory, and type
105
-
106
- ::
102
+ To build the HTML **yapCAD** documentation locally, install the
103
+ documentation dependencies and run Sphinx from the project root::
107
104
 
105
+ python3 -m pip install -r docs/requirements.txt
108
106
  make -C docs html
109
107
 
110
108
  This will build the HTML documents in the ``build/sphinx/html``
@@ -113,16 +111,80 @@ supported by Sphinx. See the `Sphinx
113
111
  documentation <https://www.sphinx-doc.org/en/master/>`__ for more
114
112
  information.
115
113
 
114
+ running tests
115
+ ~~~~~~~~~~~~~
116
+
117
+ The repository includes a comprehensive pytest suite that exercises both core
118
+ geometry primitives and visual rendering capabilities. First, set up the
119
+ testing environment::
120
+
121
+ # Create and activate virtual environment
122
+ pyenv local 3.12 # or use python3.12 directly
123
+ python3 -m venv v_312
124
+ source v_312/bin/activate
125
+
126
+ # Install dependencies
127
+ pip install -r requirements.txt
128
+ pip install pytest pytest-cov
129
+
130
+ Non-Visual Tests (Automated/CI-friendly)
131
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
132
+
133
+ Run the core computational geometry tests (including triangle, metadata,
134
+ validation, and STL exporter checks) without interactive displays::
135
+
136
+ # Run all non-visual tests
137
+ PYTHONPATH=./src pytest tests/ -m "not visual"
138
+
139
+ # With coverage reporting (default)
140
+ PYTHONPATH=./src pytest tests/ -m "not visual" --cov=src
141
+
142
+ # Skip coverage for faster execution
143
+ PYTHONPATH=./src pytest tests/ -m "not visual" --override-ini addopts=
144
+
145
+ Visual Tests (Interactive)
146
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
147
+
148
+ yapCAD includes visual tests that create interactive 3D renderings to verify
149
+ geometry generation and display functionality (for example,
150
+ ``tests/test_mesh_view.py::test_mesh_view_visual_normals``). These require a
151
+ display and user interaction::
152
+
153
+ # Run all visual tests (opens interactive windows)
154
+ ./run_visual_tests_venv.sh
155
+
156
+ # Run specific visual tests by pattern
157
+ ./run_visual_tests_venv.sh test_geom # Only test_geom* visual tests
158
+ ./run_visual_tests_venv.sh surface # Tests matching "surface"
159
+ ./run_visual_tests_venv.sh Face # Face-related tests
160
+
161
+ # Alternative: Manual pytest execution
162
+ VISUALTEST=true PYTHONPATH=./src pytest tests/ -m visual
163
+
164
+ # Or run individual visual tests
165
+ VISUALTEST=true PYTHONPATH=./src pytest tests/test_geom3d.py::TestSurface::test_surface -s
166
+
167
+ **Note:** Visual tests require closing each interactive window to proceed to the next test. Use the dedicated ``run_visual_tests_venv.sh`` script for the best experience, as it runs each test in an isolated subprocess to prevent early termination.
168
+
116
169
  **yapCAD** goals
117
170
  ----------------
118
171
 
119
172
  The purpose of **yapCAD** is to support 2D and 3D computational geometry
120
173
  and parametric, procedural, and generative design projects in python3.
121
- **yapCAD** is designed to support multiple rendering back-ends, such
122
- that a relatively small amount of code is necessary to add support for a
123
- 2D or 3D cad or drawing file format. At present, **yapCAD** supports the
124
- AutoCad DXF file format for creating two-dimensional drawings and OpenGL
125
- for creating interactive 2D and 3D renderings.
174
+ **yapCAD** is designed to support multiple rendering back-ends, such that
175
+ only a small amount of code is necessary to add support for a CAD or
176
+ drawing file format. At present, **yapCAD** supports:
177
+
178
+ * AutoCAD DXF output for two-dimensional drawings (via
179
+ `ezdxf <https://github.com/mozman/ezdxf>`__).
180
+ * STL export for 3D solids (via the new ``yapcad.io.stl`` module).
181
+ * OpenGL visualisation for 2D/3D geometries using
182
+ `pyglet <https://github.com/pyglet/pyglet>`__.
183
+
184
+ The 0.5.0 release lays the shared foundations (triangle utilities,
185
+ metadata, validation checks, and STL export) that pave the way toward STEP
186
+ support and a packaged, provenance-aware project model targeted for the
187
+ forthcoming 1.0 release.
126
188
 
127
189
  The foundations of **yapCAD** are grounded in decades of the author’s
128
190
  experience with graphics system programming, 3D CAD and simulation.
@@ -141,8 +203,8 @@ package, and interactive OpenGL visualization using the amazing
141
203
 
142
204
  (for a more complete list, see the `examples folder <./examples/>`__)
143
205
 
144
- It’s pretty easy to make a DXF drawing with **yapCAD**. Here is an
145
- example:
206
+ It’s pretty easy to make a DXF drawing or a 3D model with **yapCAD**. Here
207
+ is a DXF example:
146
208
 
147
209
  ::
148
210
 
@@ -177,6 +239,15 @@ example:
177
239
  # write out the geometry as example1-out.dxf
178
240
  dd.display()
179
241
 
242
+ For a 3D example that generates a complete rocket assembly and exports
243
+ STL::
244
+
245
+ from pathlib import Path
246
+ from examples.rocket_demo import build_rocket, export_stl
247
+
248
+ components, assembly = build_rocket()
249
+ export_stl(assembly, Path("rocket_demo.stl"))
250
+
180
251
  The **yapCAD** system isn’t just about rendering, of course, it’s about
181
252
  computational geometry. For example, if you want to calculate the
182
253
  intersection of lines and arcs in a plane, we have you covered:
@@ -0,0 +1,27 @@
1
+ yapcad/__init__.py,sha256=CUEd7EOL-ne31ggDprsi4lwLWMV1SRV5KYqkuDTdxrQ,400
2
+ yapcad/combine.py,sha256=WhI3D9lSZFzu1HD68HGUTbNNrJsT4SeCl1MOcjxsa1k,2932
3
+ yapcad/drawable.py,sha256=kPYbIhOD5MesmRSeXTiFJoE1UwVQWekoeu19athfH7Y,15843
4
+ yapcad/ezdxf_drawable.py,sha256=o6Rei044ROkaniHZJwyBGtahaD3BU1927-AkFpPzqwE,5966
5
+ yapcad/geom.py,sha256=bg_ayg1aovT17HwgFwoJ7HbvI2InEjii9Bvr8PoZ04g,105604
6
+ yapcad/geom3d.py,sha256=c6MleS-AiCWdju_mtPzUd7vhzkoT5nlJvoz7yimwdwU,33415
7
+ yapcad/geom3d_util.py,sha256=O-LUVqk2-BmFb-g-2ex44vHwKK_U4xCfqMY5Cevbvi8,15663
8
+ yapcad/geom_util.py,sha256=GMfgCyXuv9J4mdOfkqxNZT7EZZFEMV_ZpVTfuol8ve8,26370
9
+ yapcad/geometry.py,sha256=FcIF94a_usw04C0RDJDRDCCi44eya7lyahlU1Vw9wXM,16145
10
+ yapcad/geometry_checks.py,sha256=ABI0t5kls2HAidyyPp_EdoVqPxJAEtCWysPKvCeSK3s,2975
11
+ yapcad/geometry_utils.py,sha256=N_073tEV08AKjJRTeuNJMPjPgonFPUeiuh_jY7Xx7mA,3438
12
+ yapcad/mesh.py,sha256=QmPMeqdToyLztPupY8vSHQfrUz3UEBH1p8Pa9XFVPCg,1329
13
+ yapcad/metadata.py,sha256=6foSa_4N9-PO4kL8YchbXboXZZFjEcUSG6F1spwGCVE,3096
14
+ yapcad/octtree.py,sha256=MxyNMgozeLUBK5cC5_IC_9fK4BqSg4VeoKP389AUVmM,19793
15
+ yapcad/poly.py,sha256=y5cSiCbA-pCbkO5zIa9kVk2_00w7FkWxY380nK14wt4,16549
16
+ yapcad/pyglet_drawable.py,sha256=etgul99FOywq3rXf9uNu6cd59cECe4OMYX_ht5BcyMw,38424
17
+ yapcad/triangulator.py,sha256=hwxoxb564wZRYPEYbGiD-SDPu_x_SueYha0Kjmnu3Bo,3210
18
+ yapcad/xform.py,sha256=Qy_KYqbTcmspmKZfo-OGU_1Z3XRpL7p5buwc5uAzlvo,9537
19
+ yapcad/io/__init__.py,sha256=EDwSGx-0na9fZabaK57zrq3GwEtkwN5Vo_pTS-ZtuXQ,85
20
+ yapcad/io/stl.py,sha256=HNICfPz_CXYIbTP7d4spdSkMdmrt3d825MNzYA9ah9w,2596
21
+ yapcad-0.3.1.dist-info/licenses/AUTHORS.rst,sha256=JzvJA3p1aSIOTaaB3aCtB-ZrUQrVm869gdROUV5bRh8,84
22
+ yapcad-0.3.1.dist-info/licenses/LICENSE,sha256=NkGvciCD5MEHmCtnivAi2PqcEhBYAkSAarP-bWAoknM,1071
23
+ yapcad-0.3.1.dist-info/licenses/LICENSE.txt,sha256=FyT_Hxn5USuJ1Mu3-4Ou4T2x-Dzfiy06ZOnWkfdJ33o,1081
24
+ yapcad-0.3.1.dist-info/METADATA,sha256=HBc4tviyXQPUCGdA3-nueViX7QVaBaNHM7YGbVEqOWU,21588
25
+ yapcad-0.3.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
26
+ yapcad-0.3.1.dist-info/top_level.txt,sha256=NAtnfsyeALrvhI63FGS3_TEUh26OKRvnm1_lCOOgjKA,7
27
+ yapcad-0.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.4)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,17 +0,0 @@
1
- yapcad/__init__.py,sha256=Wi3rOCWkiWUKzXbHatCZ7t6RHrGZXCoxpvTjyv4o1jc,363
2
- yapcad/combine.py,sha256=N7NPfvKHm00ejWlUFJITzAJ6D4zcFb1ihwKvh5s-EeU,13925
3
- yapcad/drawable.py,sha256=RKG8emMC0E6__-L89zrBVxAB1JiFUPA51cAweCu7zP8,14605
4
- yapcad/ezdxf_drawable.py,sha256=pZGLS9Dw108GG6ItahszSkkRopYLtgP9CDVWxHCrMrs,5948
5
- yapcad/geom.py,sha256=nzCPYIsse6Z66AIYxWVoD7IddtrCNcwGWZDX06Mh-_w,108801
6
- yapcad/geom3d.py,sha256=W6gmo8UifgFwy3HHQvgvvgp96l2ahMqTVyTmbXqNEd8,2865
7
- yapcad/geometry.py,sha256=hCVnJvZXX6R4c8bWTwsWGq0tzTUJtzB974Q7s8vO28U,2549
8
- yapcad/poly.py,sha256=Fku0ynBzYUSb5UZA6WY0yLPmDTc_Eg6BVE3DUKqiJqo,21447
9
- yapcad/pyglet_drawable.py,sha256=dV9dcwzZRlPwhx18Dv0CX7wQOCxnxwk4Si_FL9heNs8,18475
10
- yapcad/xform.py,sha256=kL2xAsTlbhZplPwpWERbNp6Izkr2l90sRUf_fSfL3uw,9235
11
- yapCAD-0.2.5.dist-info/AUTHORS.rst,sha256=JzvJA3p1aSIOTaaB3aCtB-ZrUQrVm869gdROUV5bRh8,84
12
- yapCAD-0.2.5.dist-info/LICENSE,sha256=NkGvciCD5MEHmCtnivAi2PqcEhBYAkSAarP-bWAoknM,1071
13
- yapCAD-0.2.5.dist-info/LICENSE.txt,sha256=FyT_Hxn5USuJ1Mu3-4Ou4T2x-Dzfiy06ZOnWkfdJ33o,1081
14
- yapCAD-0.2.5.dist-info/METADATA,sha256=yLDVyexjBquyJ8Tx1EEEday9Dnwt7iOCnx6wYNGX8X0,18374
15
- yapCAD-0.2.5.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
16
- yapCAD-0.2.5.dist-info/top_level.txt,sha256=NAtnfsyeALrvhI63FGS3_TEUh26OKRvnm1_lCOOgjKA,7
17
- yapCAD-0.2.5.dist-info/RECORD,,