yapCAD 0.3.0__py2.py3-none-any.whl → 0.5.0__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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yapCAD
3
- Version: 0.3.0
3
+ Version: 0.5.0
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/
@@ -16,6 +16,8 @@ License-File: AUTHORS.rst
16
16
  Requires-Dist: ezdxf>=1.1
17
17
  Requires-Dist: pyglet<2,>=1.5
18
18
  Requires-Dist: mpmath>=1.2
19
+ Requires-Dist: numpy>=1.22
20
+ Requires-Dist: mapbox-earcut>=1.0.3
19
21
  Requires-Dist: pyobjc-core; platform_system == "Darwin"
20
22
  Requires-Dist: pyobjc-framework-Cocoa; platform_system == "Darwin"
21
23
  Requires-Dist: pyobjc-framework-Quartz; platform_system == "Darwin"
@@ -28,12 +30,12 @@ Dynamic: license-file
28
30
  ==========
29
31
 
30
32
  yet another procedural CAD and computational geometry system written in
31
- python 3
33
+ python 3, now with a growing focus on 3D generative design and STL export
32
34
 
33
- .. figure:: images/yapCadSplash.png
34
- :alt: **yapCAD** image
35
+ .. figure:: images/RocketDemoScreenshot.png
36
+ :alt: **yapCAD** rocket example
35
37
 
36
- **yapCAD** image
38
+ **yapCAD** rocket example
37
39
 
38
40
  what’s **yapCAD** for?
39
41
  ----------------------
@@ -42,8 +44,10 @@ First and foremost, **yapCAD** is a framework for creating
42
44
  `parametric <https://en.wikipedia.org/wiki/Parametric_design>`__,
43
45
  procedural, and
44
46
  `generative <https://en.wikipedia.org/wiki/Parametric_design>`__ design
45
- systems. You can also use **yapCAD** for other CAD, CAM, and
46
- computational geometry purposes.
47
+ systems. Starting with the 0.5 release, the emphasis has shifted toward
48
+ 3D solid workflows, including STL export for downstream slicing and
49
+ simulation, while retaining support for DXF generation and computational
50
+ geometry experiments.
47
51
 
48
52
  software status
49
53
  ---------------
@@ -84,28 +88,23 @@ clone the github repository as shown above, and make sure that your
84
88
  PYTHONPATH includes the cloned top-level ``yapCAD`` directory. You will
85
89
  find the examples in the ``yapCAD/examples`` directory.
86
90
 
87
- For a fully worked parametric design system, see the ``boxcut`` example.
91
+ For a fully worked 2D parametric design system, see the ``boxcut`` example.
92
+ For a 3D generative example that builds a multi-stage rocket, visualises
93
+ it, and exports STL, see ``examples/rocket_demo.py``.
88
94
 
89
95
  documentation
90
96
  ~~~~~~~~~~~~~
91
97
 
92
98
  Online **yapCAD** documentation can be found here:
93
- https://yapcad.readthedocs.io/en/latest/ — for some reason
94
- ``readthedocs.io`` isn’t generating the full module documentation, so
95
- you might want to build a local copy, as described below.
99
+ https://yapcad.readthedocs.io/en/latest/ — some module references lag
100
+ behind the latest 3D-focused APIs, so you may want to build a local copy
101
+ as described below to explore ``geometry_utils``, ``geometry_checks``,
102
+ ``metadata``, and ``io.stl``.
96
103
 
97
- To build the HTML **yapCAD** documentation locally, first make sure you
98
- have the sphinx package installed:
99
-
100
- ::
101
-
102
- pip install sphinx --user
103
-
104
- Then clone the github repository as shown above, ``cd`` to the
105
- ``yapCAD`` directory, and type
106
-
107
- ::
104
+ To build the HTML **yapCAD** documentation locally, install the
105
+ documentation dependencies and run Sphinx from the project root::
108
106
 
107
+ python3 -m pip install -r docs/requirements.txt
109
108
  make -C docs html
110
109
 
111
110
  This will build the HTML documents in the ``build/sphinx/html``
@@ -117,29 +116,77 @@ information.
117
116
  running tests
118
117
  ~~~~~~~~~~~~~
119
118
 
120
- The repository includes a small pytest suite that exercises the core
121
- geometry primitives. Install the testing dependencies and run pytest
122
- from the project root with the source tree on ``PYTHONPATH``::
119
+ The repository includes a comprehensive pytest suite that exercises both core
120
+ geometry primitives and visual rendering capabilities. First, set up the
121
+ testing environment::
123
122
 
124
- python3 -m pip install pytest pytest-cov
125
- PYTHONPATH=./src python3 -m pytest
123
+ # Create and activate virtual environment
124
+ pyenv local 3.12 # or use python3.12 directly
125
+ python3 -m venv v_312
126
+ source v_312/bin/activate
126
127
 
127
- The default configuration enables coverage reporting via
128
- ``pytest-cov``. If you prefer to skip coverage, you can override the
129
- options::
128
+ # Install dependencies
129
+ pip install -r requirements.txt
130
+ pip install pytest pytest-cov
130
131
 
131
- PYTHONPATH=./src python3 -m pytest --override-ini addopts=
132
+ Non-Visual Tests (Automated/CI-friendly)
133
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
134
+
135
+ Run the core computational geometry tests (including triangle, metadata,
136
+ validation, and STL exporter checks) without interactive displays::
137
+
138
+ # Run all non-visual tests
139
+ PYTHONPATH=./src pytest tests/ -m "not visual"
140
+
141
+ # With coverage reporting (default)
142
+ PYTHONPATH=./src pytest tests/ -m "not visual" --cov=src
143
+
144
+ # Skip coverage for faster execution
145
+ PYTHONPATH=./src pytest tests/ -m "not visual" --override-ini addopts=
146
+
147
+ Visual Tests (Interactive)
148
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
149
+
150
+ yapCAD includes visual tests that create interactive 3D renderings to verify
151
+ geometry generation and display functionality (for example,
152
+ ``tests/test_mesh_view.py::test_mesh_view_visual_normals``). These require a
153
+ display and user interaction::
154
+
155
+ # Run all visual tests (opens interactive windows)
156
+ ./run_visual_tests_venv.sh
157
+
158
+ # Run specific visual tests by pattern
159
+ ./run_visual_tests_venv.sh test_geom # Only test_geom* visual tests
160
+ ./run_visual_tests_venv.sh surface # Tests matching "surface"
161
+ ./run_visual_tests_venv.sh Face # Face-related tests
162
+
163
+ # Alternative: Manual pytest execution
164
+ VISUALTEST=true PYTHONPATH=./src pytest tests/ -m visual
165
+
166
+ # Or run individual visual tests
167
+ VISUALTEST=true PYTHONPATH=./src pytest tests/test_geom3d.py::TestSurface::test_surface -s
168
+
169
+ **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.
132
170
 
133
171
  **yapCAD** goals
134
172
  ----------------
135
173
 
136
174
  The purpose of **yapCAD** is to support 2D and 3D computational geometry
137
175
  and parametric, procedural, and generative design projects in python3.
138
- **yapCAD** is designed to support multiple rendering back-ends, such
139
- that a relatively small amount of code is necessary to add support for a
140
- 2D or 3D cad or drawing file format. At present, **yapCAD** supports the
141
- AutoCad DXF file format for creating two-dimensional drawings and OpenGL
142
- for creating interactive 2D and 3D renderings.
176
+ **yapCAD** is designed to support multiple rendering back-ends, such that
177
+ only a small amount of code is necessary to add support for a CAD or
178
+ drawing file format. At present, **yapCAD** supports:
179
+
180
+ * AutoCAD DXF output for two-dimensional drawings (via
181
+ `ezdxf <https://github.com/mozman/ezdxf>`__).
182
+ * STL export for 3D solids (via the new ``yapcad.io.stl`` module).
183
+ * OpenGL visualisation for 2D/3D geometries using
184
+ `pyglet <https://github.com/pyglet/pyglet>`__.
185
+
186
+ The 0.5.0 release lays the shared foundations (triangle utilities,
187
+ metadata, validation checks, and STL export) that pave the way toward STEP
188
+ support and a packaged, provenance-aware project model targeted for the
189
+ forthcoming 1.0 release.
143
190
 
144
191
  The foundations of **yapCAD** are grounded in decades of the author’s
145
192
  experience with graphics system programming, 3D CAD and simulation.
@@ -158,8 +205,8 @@ package, and interactive OpenGL visualization using the amazing
158
205
 
159
206
  (for a more complete list, see the `examples folder <./examples/>`__)
160
207
 
161
- It’s pretty easy to make a DXF drawing with **yapCAD**. Here is an
162
- example:
208
+ It’s pretty easy to make a DXF drawing or a 3D model with **yapCAD**. Here
209
+ is a DXF example:
163
210
 
164
211
  ::
165
212
 
@@ -194,6 +241,15 @@ example:
194
241
  # write out the geometry as example1-out.dxf
195
242
  dd.display()
196
243
 
244
+ For a 3D example that generates a complete rocket assembly and exports
245
+ STL::
246
+
247
+ from pathlib import Path
248
+ from examples.rocket_demo import build_rocket, export_stl
249
+
250
+ components, assembly = build_rocket()
251
+ export_stl(assembly, Path("rocket_demo.stl"))
252
+
197
253
  The **yapCAD** system isn’t just about rendering, of course, it’s about
198
254
  computational geometry. For example, if you want to calculate the
199
255
  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.5.0.dist-info/licenses/AUTHORS.rst,sha256=JzvJA3p1aSIOTaaB3aCtB-ZrUQrVm869gdROUV5bRh8,84
22
+ yapcad-0.5.0.dist-info/licenses/LICENSE,sha256=NkGvciCD5MEHmCtnivAi2PqcEhBYAkSAarP-bWAoknM,1071
23
+ yapcad-0.5.0.dist-info/licenses/LICENSE.txt,sha256=FyT_Hxn5USuJ1Mu3-4Ou4T2x-Dzfiy06ZOnWkfdJ33o,1081
24
+ yapcad-0.5.0.dist-info/METADATA,sha256=mWZ0DsMbSzQc7KiStxZIsGzsyEVCr9dor9KnrDtH2m0,21651
25
+ yapcad-0.5.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
26
+ yapcad-0.5.0.dist-info/top_level.txt,sha256=NAtnfsyeALrvhI63FGS3_TEUh26OKRvnm1_lCOOgjKA,7
27
+ yapcad-0.5.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- yapcad/__init__.py,sha256=CUEd7EOL-ne31ggDprsi4lwLWMV1SRV5KYqkuDTdxrQ,400
2
- yapcad/combine.py,sha256=uC8QsFRGa198rIkKhNIOm-GhuDvezlGcX3nhEY2N2YE,13993
3
- yapcad/drawable.py,sha256=RKG8emMC0E6__-L89zrBVxAB1JiFUPA51cAweCu7zP8,14605
4
- yapcad/ezdxf_drawable.py,sha256=pZGLS9Dw108GG6ItahszSkkRopYLtgP9CDVWxHCrMrs,5948
5
- yapcad/geom.py,sha256=YSqG-C7Ti9VCkSyIC3VggJImx_tAlyxtN38SNUdGM5Q,108843
6
- yapcad/geom3d.py,sha256=W6gmo8UifgFwy3HHQvgvvgp96l2ahMqTVyTmbXqNEd8,2865
7
- yapcad/geometry.py,sha256=H-ahJG2nsXvV__6TqWf_ReRWVFZdL4bBPPPTYEnPvi0,2564
8
- yapcad/poly.py,sha256=Fku0ynBzYUSb5UZA6WY0yLPmDTc_Eg6BVE3DUKqiJqo,21447
9
- yapcad/pyglet_drawable.py,sha256=dV9dcwzZRlPwhx18Dv0CX7wQOCxnxwk4Si_FL9heNs8,18475
10
- yapcad/xform.py,sha256=It95Ql9JpMFqk95X8MvAmqYpCQu0VKp-JShmRTC0Ztk,9538
11
- yapcad-0.3.0.dist-info/licenses/AUTHORS.rst,sha256=JzvJA3p1aSIOTaaB3aCtB-ZrUQrVm869gdROUV5bRh8,84
12
- yapcad-0.3.0.dist-info/licenses/LICENSE,sha256=NkGvciCD5MEHmCtnivAi2PqcEhBYAkSAarP-bWAoknM,1071
13
- yapcad-0.3.0.dist-info/licenses/LICENSE.txt,sha256=FyT_Hxn5USuJ1Mu3-4Ou4T2x-Dzfiy06ZOnWkfdJ33o,1081
14
- yapcad-0.3.0.dist-info/METADATA,sha256=zfvxqcMOeJJsH2yp5Mw4TJkGeuyIAbeQ507n4f7PTdY,18999
15
- yapcad-0.3.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
16
- yapcad-0.3.0.dist-info/top_level.txt,sha256=NAtnfsyeALrvhI63FGS3_TEUh26OKRvnm1_lCOOgjKA,7
17
- yapcad-0.3.0.dist-info/RECORD,,
File without changes