yapCAD 0.3.0__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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yapCAD
3
- Version: 0.3.0
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/
@@ -28,12 +28,12 @@ Dynamic: license-file
28
28
  ==========
29
29
 
30
30
  yet another procedural CAD and computational geometry system written in
31
- python 3
31
+ python 3, now with a growing focus on 3D generative design and STL export
32
32
 
33
- .. figure:: images/yapCadSplash.png
34
- :alt: **yapCAD** image
33
+ .. figure:: images/RocketDemoScreenshot.png
34
+ :alt: **yapCAD** rocket example
35
35
 
36
- **yapCAD** image
36
+ **yapCAD** rocket example
37
37
 
38
38
  what’s **yapCAD** for?
39
39
  ----------------------
@@ -42,8 +42,10 @@ First and foremost, **yapCAD** is a framework for creating
42
42
  `parametric <https://en.wikipedia.org/wiki/Parametric_design>`__,
43
43
  procedural, and
44
44
  `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.
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.
47
49
 
48
50
  software status
49
51
  ---------------
@@ -84,28 +86,23 @@ clone the github repository as shown above, and make sure that your
84
86
  PYTHONPATH includes the cloned top-level ``yapCAD`` directory. You will
85
87
  find the examples in the ``yapCAD/examples`` directory.
86
88
 
87
- 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``.
88
92
 
89
93
  documentation
90
94
  ~~~~~~~~~~~~~
91
95
 
92
96
  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.
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``.
96
101
 
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
- ::
102
+ To build the HTML **yapCAD** documentation locally, install the
103
+ documentation dependencies and run Sphinx from the project root::
108
104
 
105
+ python3 -m pip install -r docs/requirements.txt
109
106
  make -C docs html
110
107
 
111
108
  This will build the HTML documents in the ``build/sphinx/html``
@@ -117,29 +114,77 @@ information.
117
114
  running tests
118
115
  ~~~~~~~~~~~~~
119
116
 
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``::
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::
123
120
 
124
- python3 -m pip install pytest pytest-cov
125
- PYTHONPATH=./src python3 -m pytest
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
126
125
 
127
- The default configuration enables coverage reporting via
128
- ``pytest-cov``. If you prefer to skip coverage, you can override the
129
- options::
126
+ # Install dependencies
127
+ pip install -r requirements.txt
128
+ pip install pytest pytest-cov
130
129
 
131
- PYTHONPATH=./src python3 -m pytest --override-ini addopts=
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.
132
168
 
133
169
  **yapCAD** goals
134
170
  ----------------
135
171
 
136
172
  The purpose of **yapCAD** is to support 2D and 3D computational geometry
137
173
  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.
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.
143
188
 
144
189
  The foundations of **yapCAD** are grounded in decades of the author’s
145
190
  experience with graphics system programming, 3D CAD and simulation.
@@ -158,8 +203,8 @@ package, and interactive OpenGL visualization using the amazing
158
203
 
159
204
  (for a more complete list, see the `examples folder <./examples/>`__)
160
205
 
161
- It’s pretty easy to make a DXF drawing with **yapCAD**. Here is an
162
- example:
206
+ It’s pretty easy to make a DXF drawing or a 3D model with **yapCAD**. Here
207
+ is a DXF example:
163
208
 
164
209
  ::
165
210
 
@@ -194,6 +239,15 @@ example:
194
239
  # write out the geometry as example1-out.dxf
195
240
  dd.display()
196
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
+
197
251
  The **yapCAD** system isn’t just about rendering, of course, it’s about
198
252
  computational geometry. For example, if you want to calculate the
199
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,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