piecad 1.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 (118) hide show
  1. piecad-1.0.0/.gitignore +2 -0
  2. piecad-1.0.0/.gitmodules +0 -0
  3. piecad-1.0.0/LICENSE +30 -0
  4. piecad-1.0.0/Makefile +22 -0
  5. piecad-1.0.0/PKG-INFO +12 -0
  6. piecad-1.0.0/README.md +23 -0
  7. piecad-1.0.0/doc_examples/cone.example +1 -0
  8. piecad-1.0.0/doc_examples/cube.example +1 -0
  9. piecad-1.0.0/doc_examples/cuboid.example +1 -0
  10. piecad-1.0.0/doc_examples/difference2d.example +3 -0
  11. piecad-1.0.0/doc_examples/difference3d.example +3 -0
  12. piecad-1.0.0/doc_examples/ellipsoid.example +1 -0
  13. piecad-1.0.0/doc_examples/elliptical_cylinder.example +1 -0
  14. piecad-1.0.0/doc_examples/extrude.example +1 -0
  15. piecad-1.0.0/doc_examples/extrude_chaining.example +19 -0
  16. piecad-1.0.0/doc_examples/extrude_transforming.example +4 -0
  17. piecad-1.0.0/doc_examples/geodesic_sphere.example +1 -0
  18. piecad-1.0.0/doc_examples/hull2d.example +4 -0
  19. piecad-1.0.0/doc_examples/hull3d.example +4 -0
  20. piecad-1.0.0/doc_examples/intersect2d.example +3 -0
  21. piecad-1.0.0/doc_examples/intersect3d.example +3 -0
  22. piecad-1.0.0/doc_examples/mk_doc_examples +26 -0
  23. piecad-1.0.0/doc_examples/polygon.example +6 -0
  24. piecad-1.0.0/doc_examples/process_example.py +82 -0
  25. piecad-1.0.0/doc_examples/pyramid.example +1 -0
  26. piecad-1.0.0/doc_examples/revolve.example +1 -0
  27. piecad-1.0.0/doc_examples/rounded_cuboid.example +1 -0
  28. piecad-1.0.0/doc_examples/rounded_cylinder.example +1 -0
  29. piecad-1.0.0/doc_examples/sphere.example +1 -0
  30. piecad-1.0.0/doc_examples/torus.example +1 -0
  31. piecad-1.0.0/doc_examples/union2d.example +3 -0
  32. piecad-1.0.0/doc_examples/union3d.example +3 -0
  33. piecad-1.0.0/docs/bulk_ops.html +294 -0
  34. piecad-1.0.0/docs/examples/cone.html +51 -0
  35. piecad-1.0.0/docs/examples/cube.html +51 -0
  36. piecad-1.0.0/docs/examples/cuboid.html +51 -0
  37. piecad-1.0.0/docs/examples/difference2d.html +53 -0
  38. piecad-1.0.0/docs/examples/difference3d.html +53 -0
  39. piecad-1.0.0/docs/examples/ellipsoid.html +51 -0
  40. piecad-1.0.0/docs/examples/elliptical_cylinder.html +51 -0
  41. piecad-1.0.0/docs/examples/extrude.html +51 -0
  42. piecad-1.0.0/docs/examples/extrude_chaining.html +69 -0
  43. piecad-1.0.0/docs/examples/extrude_transforming.html +54 -0
  44. piecad-1.0.0/docs/examples/geodesic_sphere.html +51 -0
  45. piecad-1.0.0/docs/examples/hull2d.html +54 -0
  46. piecad-1.0.0/docs/examples/hull3d.html +54 -0
  47. piecad-1.0.0/docs/examples/intersect2d.html +53 -0
  48. piecad-1.0.0/docs/examples/intersect3d.html +53 -0
  49. piecad-1.0.0/docs/examples/polygon.html +56 -0
  50. piecad-1.0.0/docs/examples/pyramid.html +51 -0
  51. piecad-1.0.0/docs/examples/revolve.html +51 -0
  52. piecad-1.0.0/docs/examples/rounded_cuboid.html +51 -0
  53. piecad-1.0.0/docs/examples/rounded_cylinder.html +51 -0
  54. piecad-1.0.0/docs/examples/sphere.html +51 -0
  55. piecad-1.0.0/docs/examples/torus.html +51 -0
  56. piecad-1.0.0/docs/examples/union2d.html +53 -0
  57. piecad-1.0.0/docs/examples/union3d.html +53 -0
  58. piecad-1.0.0/docs/index.html +1740 -0
  59. piecad-1.0.0/docs/models/cone.glb +0 -0
  60. piecad-1.0.0/docs/models/cube.glb +0 -0
  61. piecad-1.0.0/docs/models/cuboid.glb +0 -0
  62. piecad-1.0.0/docs/models/difference2d.glb +0 -0
  63. piecad-1.0.0/docs/models/difference3d.glb +0 -0
  64. piecad-1.0.0/docs/models/ellipsoid.glb +0 -0
  65. piecad-1.0.0/docs/models/elliptical_cylinder.glb +0 -0
  66. piecad-1.0.0/docs/models/extrude.glb +0 -0
  67. piecad-1.0.0/docs/models/extrude_chaining.glb +0 -0
  68. piecad-1.0.0/docs/models/extrude_transforming.glb +0 -0
  69. piecad-1.0.0/docs/models/geodesic_sphere.glb +0 -0
  70. piecad-1.0.0/docs/models/hull2d.glb +0 -0
  71. piecad-1.0.0/docs/models/hull3d.glb +0 -0
  72. piecad-1.0.0/docs/models/intersect2d.glb +0 -0
  73. piecad-1.0.0/docs/models/intersect3d.glb +0 -0
  74. piecad-1.0.0/docs/models/polygon.glb +0 -0
  75. piecad-1.0.0/docs/models/pyramid.glb +0 -0
  76. piecad-1.0.0/docs/models/revolve.glb +0 -0
  77. piecad-1.0.0/docs/models/rounded_cuboid.glb +0 -0
  78. piecad-1.0.0/docs/models/rounded_cylinder.glb +0 -0
  79. piecad-1.0.0/docs/models/sphere.glb +0 -0
  80. piecad-1.0.0/docs/models/torus.glb +0 -0
  81. piecad-1.0.0/docs/models/union2d.glb +0 -0
  82. piecad-1.0.0/docs/models/union3d.glb +0 -0
  83. piecad-1.0.0/docs/primitives_2d.html +493 -0
  84. piecad-1.0.0/docs/primitives_3d.html +902 -0
  85. piecad-1.0.0/docs/trigonometry.html +310 -0
  86. piecad-1.0.0/docs/utilities.html +371 -0
  87. piecad-1.0.0/examples/NeilMedDrainStandv1.py +71 -0
  88. piecad-1.0.0/examples/NeilMedDrainStandv2.py +91 -0
  89. piecad-1.0.0/examples/kid_n_b.py +124 -0
  90. piecad-1.0.0/examples/kid_nail.py +48 -0
  91. piecad-1.0.0/examples/name_plate.py +39 -0
  92. piecad-1.0.0/examples/piecad +1 -0
  93. piecad-1.0.0/piecad/__init__.py +665 -0
  94. piecad-1.0.0/piecad/_color.py +202 -0
  95. piecad-1.0.0/piecad/_poly_point_isect.py +1391 -0
  96. piecad-1.0.0/piecad/_text.py +114 -0
  97. piecad-1.0.0/piecad/bulk_ops.py +159 -0
  98. piecad-1.0.0/piecad/fonts/Hack-Regular-License.txt +45 -0
  99. piecad-1.0.0/piecad/fonts/Hack-Regular.ttf +0 -0
  100. piecad-1.0.0/piecad/fonts/Roboto-Regular-License.txt +93 -0
  101. piecad-1.0.0/piecad/fonts/Roboto-Regular.ttf +0 -0
  102. piecad-1.0.0/piecad/primitives_2d.py +306 -0
  103. piecad-1.0.0/piecad/primitives_3d.py +590 -0
  104. piecad-1.0.0/piecad/trigonometry.py +152 -0
  105. piecad-1.0.0/piecad/utilities.py +272 -0
  106. piecad-1.0.0/pyproject.toml +28 -0
  107. piecad-1.0.0/serve_docs +4 -0
  108. piecad-1.0.0/setup +5 -0
  109. piecad-1.0.0/tests/piecad +1 -0
  110. piecad-1.0.0/tests/summary.py +53 -0
  111. piecad-1.0.0/tests/test_2d.py +294 -0
  112. piecad-1.0.0/tests/test_3d.py +245 -0
  113. piecad-1.0.0/tests/test_bulk_ops.py +94 -0
  114. piecad-1.0.0/tests/test_color.py +55 -0
  115. piecad-1.0.0/tests/test_m_direct.py +220 -0
  116. piecad-1.0.0/tests/test_methods.py +185 -0
  117. piecad-1.0.0/tests/test_text.py +48 -0
  118. piecad-1.0.0/tests/test_trig.py +159 -0
@@ -0,0 +1,2 @@
1
+ # Created by venv; see https://docs.python.org/3/library/venv.html
2
+ *
File without changes
piecad-1.0.0/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ The bulk of the code is covered by this license:
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2025 Brian Sturgill
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ ----
25
+
26
+ The file `piecad/_poly_point_isect.py` has it's own license (located at the top of the file).
27
+
28
+ ----
29
+
30
+ Fonts are covered by a separate license, see the License files in piecad/fonts for details.
piecad-1.0.0/Makefile ADDED
@@ -0,0 +1,22 @@
1
+
2
+ all: test docs
3
+
4
+ docs:
5
+ black piecad/*.py tests/*.py doc_examples/*.example examples/*.py
6
+ rm -rf docs/*
7
+ pdoc3 --html piecad --force
8
+ (cd doc_examples; ./mk_doc_examples)
9
+ mv html/piecad docs
10
+ rmdir html
11
+
12
+ test:
13
+ pytest tests --benchmark-disable
14
+
15
+ benchmark:
16
+ pytest tests --benchmark-only
17
+
18
+ savebenchmark:
19
+ (read -p "Benchmark Name: " -r NAME ; pytest tests --benchmark-only --benchmark-save=`date +%F`"$${NAME}" --benchmark-name=short)
20
+
21
+ clean:
22
+ rm -f examples/*.obj
piecad-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.1
2
+ Name: piecad
3
+ Version: 1.0.0
4
+ Summary: "Easy as Pie" CAD (Piecad)
5
+ Author-email: Brian Sturgill <codersnext@gmail.com>
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Requires-Dist: trimesh >= 1.26.4
8
+ Requires-Dist: fontPens >= 0.2.4
9
+ Requires-Dist: fontTools >= 4.57.0
10
+ Requires-Dist: manifold3d >= 3.0.1
11
+ Requires-Dist: skia-pathops >= 0.8.0
12
+ Project-URL: Home, https://github.com/briansturgill/Piecad
piecad-1.0.0/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # "Easy as Pie" CAD (Piecad)
2
+
3
+ For many years I used [OpenSCAD](https://www.openscad.org),
4
+ but the functional language it uses was often a hinderance and its speed
5
+ was poor. **Piecad** is my opinionted view of what a good, simple CAD API should look like.
6
+ Its primary focus is the creation of models for 3D printing.
7
+
8
+ [Documentation](https://briansturgill.github.io/Piecad)
9
+
10
+ INSTALLATION IS STILL IN PROGRESS -- FINISHED SOON
11
+
12
+
13
+ ## CREDITS
14
+
15
+ Piecad is based on [Manifold](https://github.com/elalish/manifold), a 3D CAD package written in C++.
16
+ Manifold incorporates [Clipper2](https://github.com/AngusJohnson/Clipper2) for 2D objects.
17
+ It also uses [`quickhull`](https://github.com/akuukka/quickhull) for 3d convex hulls.
18
+ You can see Manifold's web site for other packages that are used.
19
+
20
+ Piecad also uses [isect_segments-bentley_ottmann](https://github.com/ideasman42/isect_segments-bentley_ottmann)
21
+ to check for polygon self intersections. Also, [fontTools](https://github.com/fonttools/fonttools) and [fontPens](https://github.com/robotools/fontPens) are used to support text.
22
+
23
+ We include two fonts: `Hack-Regular.tts` and `Roboto-Regular.tts`, see `piecad/fonts` for the licenses.
@@ -0,0 +1 @@
1
+ out = cone(10, 5, 0, 100)
@@ -0,0 +1 @@
1
+ out = cube(10)
@@ -0,0 +1 @@
1
+ out = cube([10, 5, 8])
@@ -0,0 +1,3 @@
1
+ c1 = circle(6)
2
+ c2 = circle(4)
3
+ out = difference(c1, c2)
@@ -0,0 +1,3 @@
1
+ c1 = cylinder(10, 6)
2
+ c2 = cylinder(10, 4)
3
+ out = difference(c1, c2)
@@ -0,0 +1 @@
1
+ out = ellipsoid((10, 5, 8), 100)
@@ -0,0 +1 @@
1
+ out = elliptical_cylinder(18, (8, 5), 100)
@@ -0,0 +1 @@
1
+ out = extrude(star(5, 4), 10)
@@ -0,0 +1,19 @@
1
+ def sphere_from_chaining(radius, segs):
2
+ deg_per_seg = 180.0 / segs
3
+ hs = (3.141592653589793 * radius) / segs
4
+ l = []
5
+ l.append((-radius, circle(0.3, segs)))
6
+ h_sum = -radius
7
+ for i in range(1, segs - 1):
8
+ factor = sin(i * deg_per_seg)
9
+ r = radius * factor
10
+ h = hs * factor
11
+ h_sum += h
12
+ l.append((h_sum, circle(r, segs)))
13
+ l.append((radius, circle(0.3, segs)))
14
+
15
+ s = extrude_chaining(l, is_convex=True)
16
+ return s
17
+
18
+
19
+ out = sphere_from_chaining(10, 60)
@@ -0,0 +1,4 @@
1
+ o2 = difference(
2
+ rounded_rectangle([10, 9], 2), rounded_rectangle([8, 7], 1).translate([1, 1])
3
+ ).translate((-5, -4))
4
+ out = extrude_transforming(o2, 16, num_twist_divisions=100, twist=180, scale=(2, 2))
@@ -0,0 +1 @@
1
+ out = geodesic_sphere(17)
@@ -0,0 +1,4 @@
1
+ c1 = circle(3)
2
+ c2 = circle(3).translate((7, 0))
3
+ c3 = circle(3).translate((0, 7))
4
+ out = hull(c1, c2, c3)
@@ -0,0 +1,4 @@
1
+ c1 = cylinder(10, 4)
2
+ c2 = cylinder(10, 4).translate((5, 0, 0))
3
+ c3 = cylinder(10, 4).translate((0, 5, 3))
4
+ out = hull(c1, c2, c3)
@@ -0,0 +1,3 @@
1
+ c1 = circle(6)
2
+ c2 = circle(4).translate((6, 0))
3
+ out = intersect(c1, c2)
@@ -0,0 +1,3 @@
1
+ c1 = cylinder(10, 6)
2
+ c2 = cylinder(10, 4).translate((6, 0, 0))
3
+ out = intersect(c1, c2)
@@ -0,0 +1,26 @@
1
+ #!/bin/sh
2
+ models="../html/piecad/models"
3
+ examples="../html/piecad/examples"
4
+ if [ ! -d $models ]; then
5
+ mkdir $models
6
+ fi
7
+ if [ ! -d $examples ]; then
8
+ mkdir $examples
9
+ fi
10
+ if [ ! -d tmp ]; then
11
+ mkdir tmp
12
+ fi
13
+ if [ ! -L tmp/piecad ]; then
14
+ ln -s ../../piecad tmp/piecad
15
+ fi
16
+ for f in *.example; do
17
+ echo $f
18
+ python process_example.py $f
19
+ if [ $? -ne 0 ]; then
20
+ exit $?
21
+ fi
22
+ done
23
+ mv tmp/*.glb $models
24
+ mv tmp/*.html $examples
25
+ rm tmp/*.py
26
+ rm tmp/piecad
@@ -0,0 +1,6 @@
1
+ paths = [
2
+ [(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)],
3
+ [(2.0, 2.0), (8.0, 2.0), (8.0, 8.0), (2.0, 8.0)],
4
+ [(4.0, 4.0), (6.0, 4.0), (6.0, 6.0), (4.0, 6.0)],
5
+ ]
6
+ out = polygon(paths).extrude(10)
@@ -0,0 +1,82 @@
1
+ import sys
2
+ import os
3
+
4
+ if len(sys.argv) != 2:
5
+ print("Should have 1 argument.")
6
+ sys.exit(1)
7
+
8
+
9
+ idx = sys.argv[1].rindex(".example")
10
+ example_name=sys.argv[1][:idx]
11
+
12
+ with open(sys.argv[1], "r") as f:
13
+ example_code = f.readlines()
14
+
15
+ example_model = f"tmp/{example_name}.glb"
16
+ example_creator = f"tmp/{example_name}.py"
17
+ example_html = f"tmp/{example_name}.html"
18
+
19
+ template = f"""
20
+ <html>
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ <head>
23
+ <style>
24
+ body {{
25
+ background-color: lightblue;
26
+ color: black;
27
+ }}
28
+ pre {{
29
+ margin: 0;
30
+ text-align: left;
31
+ }}
32
+ .cd {{
33
+ padding-top: 0px;
34
+ }}
35
+ .mv {{
36
+ display: block;
37
+ margin-left: auto;
38
+ margin-right: auto;
39
+ width: 300px;
40
+ }}
41
+ </style>
42
+ </head>
43
+ <body>
44
+ <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
45
+ <!--link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">-->
46
+ <!--script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>-->
47
+ <!--script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>-->
48
+ <!--script>hljs.highlightAll();</script>-->
49
+ <table width="100%">
50
+ <tr>
51
+ <td class="cd">
52
+ <code>
53
+ <pre>
54
+ {"".join(example_code)}
55
+ </pre>
56
+ </code>
57
+ </td>
58
+ </tr>
59
+ <tr>
60
+ <td>
61
+ <div class="mv">
62
+ <model-viewer src="../models/{example_name}.glb" ar ar-scale="fixed" interaction-prompt="none" camera-controls touch-action="pan-y" disable-zoom alt="Output of code example." shadow-intensity="2" camera-orbit="0deg 180deg 0" xr-environment></model-viewer>
63
+ </div>
64
+ </td>
65
+ </tr>
66
+ </table>
67
+ </body>
68
+ </html>
69
+ """
70
+
71
+ with open(example_html, "w") as f:
72
+ f.write(template)
73
+
74
+ with open(example_creator, "w") as f:
75
+ f.write("from piecad import *\n")
76
+ f.write("".join(example_code))
77
+ f.write("if type(out) == Obj2d: out = out.extrude(0.2)\n")
78
+ f.write("out = out.color(\"khaki\")\n")
79
+ f.write(f"save(\"{example_model}\", out)\n")
80
+
81
+ ret = os.system(f"python {example_creator}")
82
+ sys.exit(0 if ret == 0 else 1)
@@ -0,0 +1 @@
1
+ out = pyramid(height=15, num_sides=4, radius=10)
@@ -0,0 +1 @@
1
+ out = star(10, 5).translate([10, 0]).revolve(segments=100)
@@ -0,0 +1 @@
1
+ out = rounded_cuboid((20, 16, 18), rounding_radius=3.0, segments=100)
@@ -0,0 +1 @@
1
+ out = rounded_cylinder(18, 8, rounding_radius=3.0, segments=100)
@@ -0,0 +1 @@
1
+ out = sphere(17, 100)
@@ -0,0 +1 @@
1
+ out = torus(outer_radius=10, inner_radius=6, segments=100)
@@ -0,0 +1,3 @@
1
+ c1 = circle(6)
2
+ c2 = circle(4).translate((6, 0))
3
+ out = union(c1, c2)
@@ -0,0 +1,3 @@
1
+ c1 = cylinder(10, 6)
2
+ c2 = cylinder(10, 4).translate((6, 0, 0))
3
+ out = union(c1, c2)
@@ -0,0 +1,294 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
6
+ <meta name="generator" content="pdoc3 0.11.6">
7
+ <title>piecad.bulk_ops API documentation</title>
8
+ <meta name="description" content="Miscellaneous bulk operations that work on multiple objects.">
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/typography.min.css" integrity="sha512-Y1DYSb995BAfxobCkKepB1BqJJTPrOp3zPL74AWFugHHmmdcvO+C48WLrUOlhGMc0QG7AE3f7gmvvcrmX2fDoA==" crossorigin>
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" crossorigin>
12
+ <style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:1.5em;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:2em 0 .50em 0}h3{font-size:1.4em;margin:1.6em 0 .7em 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .2s ease-in-out}a:visited{color:#503}a:hover{color:#b62}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900;font-weight:bold}pre code{font-size:.8em;line-height:1.4em;padding:1em;display:block}code{background:#f3f3f3;font-family:"DejaVu Sans Mono",monospace;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source > summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible;min-width:max-content}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em 1em;margin:1em 0}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
13
+ <style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul ul{padding-left:1em}.toc > ul > li{margin-top:.5em}}</style>
14
+ <style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
15
+ <script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin></script>
16
+ <script>window.addEventListener('DOMContentLoaded', () => {
17
+ hljs.configure({languages: ['bash', 'css', 'diff', 'graphql', 'ini', 'javascript', 'json', 'plaintext', 'python', 'python-repl', 'rust', 'shell', 'sql', 'typescript', 'xml', 'yaml']});
18
+ hljs.highlightAll();
19
+ /* Collapse source docstrings */
20
+ setTimeout(() => {
21
+ [...document.querySelectorAll('.hljs.language-python > .hljs-string')]
22
+ .filter(el => el.innerHTML.length > 200 && ['"""', "'''"].includes(el.innerHTML.substring(0, 3)))
23
+ .forEach(el => {
24
+ let d = document.createElement('details');
25
+ d.classList.add('hljs-string');
26
+ d.innerHTML = '<summary>"""</summary>' + el.innerHTML.substring(3);
27
+ el.replaceWith(d);
28
+ });
29
+ }, 100);
30
+ })</script>
31
+ </head>
32
+ <body>
33
+ <main>
34
+ <article id="content">
35
+ <header>
36
+ <h1 class="title">Module <code>piecad.bulk_ops</code></h1>
37
+ </header>
38
+ <section id="section-intro">
39
+ <h2 id="miscellaneous-bulk-operations-that-work-on-multiple-objects">Miscellaneous bulk operations that work on multiple objects.</h2>
40
+ </section>
41
+ <section>
42
+ </section>
43
+ <section>
44
+ </section>
45
+ <section>
46
+ <h2 class="section-title" id="header-functions">Functions</h2>
47
+ <dl>
48
+ <dt id="piecad.bulk_ops.compose"><code class="name flex">
49
+ <span>def <span class="ident">compose</span></span>(<span>*objs: <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a>) ‑> <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a></span>
50
+ </code></dt>
51
+ <dd>
52
+ <details class="source">
53
+ <summary>
54
+ <span>Expand source code</span>
55
+ </summary>
56
+ <pre><code class="python">def compose(*objs: Obj2d | Obj3d) -&gt; Obj2d | Obj3d:
57
+ &#34;&#34;&#34;
58
+ Returns a single object containing all the disjoint objects.
59
+
60
+ It is important that the objects not overlap.
61
+ This is for making a scene of a group of objects that can be saved as a single model.
62
+
63
+ If you want to combine two models into one model, see `union` below.
64
+
65
+ See the `decompose` method on `Obj2d` and `Obj3d` for the inverse operation.
66
+
67
+ &#34;&#34;&#34;
68
+ ty = type(objs[0])
69
+ _chkGOTY(&#34;objs&#34;, ty)
70
+ if len(objs) == 0:
71
+ return Obj2d(_m.CrossSection()) if ty == Obj2d else Obj3d(_m.Manifold())
72
+ for o in objs:
73
+ if type(o) != ty:
74
+ raise ValidationError(&#34;Mixed types in parameter: objs.&#34;)
75
+ l = []
76
+ for o in objs:
77
+ l.append(o.mo)
78
+
79
+ if ty == Obj2d:
80
+ return Obj2d(_m.CrossSection.compose(l))
81
+ else:
82
+ return Obj3d(_m.Manifold.compose(l))</code></pre>
83
+ </details>
84
+ <div class="desc"><p>Returns a single object containing all the disjoint objects.</p>
85
+ <p>It is important that the objects not overlap.
86
+ This is for making a scene of a group of objects that can be saved as a single model.</p>
87
+ <p>If you want to combine two models into one model, see <code><a title="piecad.bulk_ops.union" href="#piecad.bulk_ops.union">union()</a></code> below.</p>
88
+ <p>See the <code>decompose</code> method on <code>Obj2d</code> and <code>Obj3d</code> for the inverse operation.</p></div>
89
+ </dd>
90
+ <dt id="piecad.bulk_ops.difference"><code class="name flex">
91
+ <span>def <span class="ident">difference</span></span>(<span>*objs: <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a>) ‑> <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a></span>
92
+ </code></dt>
93
+ <dd>
94
+ <details class="source">
95
+ <summary>
96
+ <span>Expand source code</span>
97
+ </summary>
98
+ <pre><code class="python">def difference(*objs: Obj2d | Obj3d) -&gt; Obj2d | Obj3d:
99
+ &#34;&#34;&#34;
100
+ Returns the object removing the second through the last objects from the first object.
101
+
102
+ In some packages this function might be called subtract
103
+
104
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/difference2d.html&#34;&gt;&lt;/iframe&gt;
105
+
106
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/difference3d.html&#34;&gt;&lt;/iframe&gt;
107
+ &#34;&#34;&#34;
108
+ ty = type(objs[0])
109
+ _chkGOTY(&#34;objs&#34;, ty)
110
+ if len(objs) == 0:
111
+ return Obj2d(_m.CrossSection()) if ty == Obj2d else Obj3d(_m.Manifold())
112
+ for o in objs:
113
+ if type(o) != ty:
114
+ raise ValidationError(&#34;Mixed types in parameter: objs.&#34;)
115
+ if ty == Obj2d:
116
+ l = []
117
+ for o in objs:
118
+ l.append(o.mo)
119
+ return Obj2d(_m.CrossSection.batch_boolean(l, _m.OpType.Subtract))
120
+ elif ty == Obj3d:
121
+ l = []
122
+ for o in objs:
123
+ l.append(o.mo)
124
+ return Obj3d(_m.Manifold.batch_boolean(l, _m.OpType.Subtract))
125
+ else:
126
+ raise ValidationError(&#34;All objects must be of one type, Obj2d or Obj3d&#34;)</code></pre>
127
+ </details>
128
+ <div class="desc"><p>Returns the object removing the second through the last objects from the first object.</p>
129
+ <p>In some packages this function might be called subtract</p>
130
+ <iframe width="100%" height="250" src="examples/difference2d.html"></iframe>
131
+ <iframe width="100%" height="250" src="examples/difference3d.html"></iframe></div>
132
+ </dd>
133
+ <dt id="piecad.bulk_ops.hull"><code class="name flex">
134
+ <span>def <span class="ident">hull</span></span>(<span>*objs: <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a>) ‑> <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a></span>
135
+ </code></dt>
136
+ <dd>
137
+ <details class="source">
138
+ <summary>
139
+ <span>Expand source code</span>
140
+ </summary>
141
+ <pre><code class="python">def hull(*objs: Obj2d | Obj3d) -&gt; Obj2d | Obj3d:
142
+ &#34;&#34;&#34;
143
+ Return a convex hull of the given objects.
144
+
145
+ All objects must be of the same type (Obj2d or Obj3d).
146
+
147
+ The corresponding hull that is returned will be of the same type
148
+ as the input objects.
149
+
150
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/hull2d.html&#34;&gt;&lt;/iframe&gt;
151
+
152
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/hull3d.html&#34;&gt;&lt;/iframe&gt;
153
+ &#34;&#34;&#34;
154
+ ty = type(objs[0])
155
+ _chkGOTY(&#34;objs&#34;, ty)
156
+ if len(objs) == 0:
157
+ return Obj2d(_m.CrossSection()) if ty == Obj2d else Obj3d(_m.Manifold())
158
+ for o in objs:
159
+ if type(o) != ty:
160
+ raise ValidationError(&#34;Mixed types in parameter: objs.&#34;)
161
+ if ty == Obj2d:
162
+ l = []
163
+ for o in objs:
164
+ l.append(o.mo)
165
+ return Obj2d(_m.CrossSection.batch_hull(l))
166
+ elif ty == Obj3d:
167
+ l = []
168
+ for o in objs:
169
+ l.append(o.mo)
170
+ return Obj3d(_m.Manifold.batch_hull(l))
171
+ else:
172
+ raise ValidationError(&#34;All objects must be of one type, Obj2d or Obj3d&#34;)</code></pre>
173
+ </details>
174
+ <div class="desc"><p>Return a convex hull of the given objects.</p>
175
+ <p>All objects must be of the same type (Obj2d or Obj3d).</p>
176
+ <p>The corresponding hull that is returned will be of the same type
177
+ as the input objects.</p>
178
+ <iframe width="100%" height="250" src="examples/hull2d.html"></iframe>
179
+ <iframe width="100%" height="250" src="examples/hull3d.html"></iframe></div>
180
+ </dd>
181
+ <dt id="piecad.bulk_ops.intersect"><code class="name flex">
182
+ <span>def <span class="ident">intersect</span></span>(<span>*objs: <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a>) ‑> <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a></span>
183
+ </code></dt>
184
+ <dd>
185
+ <details class="source">
186
+ <summary>
187
+ <span>Expand source code</span>
188
+ </summary>
189
+ <pre><code class="python">def intersect(*objs: Obj2d | Obj3d) -&gt; Obj2d | Obj3d:
190
+ &#34;&#34;&#34;
191
+ Returns the object made by adding those portions that occur only in all `objs` together.
192
+
193
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/intersect2d.html&#34;&gt;&lt;/iframe&gt;
194
+
195
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/intersect3d.html&#34;&gt;&lt;/iframe&gt;
196
+ &#34;&#34;&#34;
197
+ ty = type(objs[0])
198
+ _chkGOTY(&#34;objs&#34;, ty)
199
+ if len(objs) == 0:
200
+ return Obj2d(_m.CrossSection()) if ty == Obj2d else Obj3d(_m.Manifold())
201
+ for o in objs:
202
+ if type(o) != ty:
203
+ raise ValidationError(&#34;Mixed types in parameter: objs.&#34;)
204
+ if ty == Obj2d:
205
+ l = []
206
+ for o in objs:
207
+ l.append(o.mo)
208
+ return Obj2d(_m.CrossSection.batch_boolean(l, _m.OpType.Intersect))
209
+ elif ty == Obj3d:
210
+ l = []
211
+ for o in objs:
212
+ l.append(o.mo)
213
+ return Obj3d(_m.Manifold.batch_boolean(l, _m.OpType.Intersect))
214
+ else:
215
+ raise ValidationError(&#34;All objects must be of one type, Obj2d or Obj3d&#34;)</code></pre>
216
+ </details>
217
+ <div class="desc"><p>Returns the object made by adding those portions that occur only in all <code>objs</code> together.</p>
218
+ <iframe width="100%" height="250" src="examples/intersect2d.html"></iframe>
219
+ <iframe width="100%" height="250" src="examples/intersect3d.html"></iframe></div>
220
+ </dd>
221
+ <dt id="piecad.bulk_ops.union"><code class="name flex">
222
+ <span>def <span class="ident">union</span></span>(<span>*objs: <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a>) ‑> <a title="piecad.Obj2d" href="index.html#piecad.Obj2d">Obj2d</a> | <a title="piecad.Obj3d" href="index.html#piecad.Obj3d">Obj3d</a></span>
223
+ </code></dt>
224
+ <dd>
225
+ <details class="source">
226
+ <summary>
227
+ <span>Expand source code</span>
228
+ </summary>
229
+ <pre><code class="python">def union(*objs: Obj2d | Obj3d) -&gt; Obj2d | Obj3d:
230
+ &#34;&#34;&#34;
231
+ Returns the object made by adding all the `objs` together.
232
+
233
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/union2d.html&#34;&gt;&lt;/iframe&gt;
234
+
235
+ &lt;iframe width=&#34;100%&#34; height=&#34;250&#34; src=&#34;examples/union3d.html&#34;&gt;&lt;/iframe&gt;
236
+ &#34;&#34;&#34;
237
+ ty = type(objs[0])
238
+ _chkGOTY(&#34;objs&#34;, ty)
239
+ if len(objs) == 0:
240
+ return Obj2d(_m.CrossSection()) if ty == Obj2d else Obj3d(_m.Manifold())
241
+ for o in objs:
242
+ if type(o) != ty:
243
+ raise ValidationError(&#34;Mixed types in parameter: objs.&#34;)
244
+ if ty == Obj2d:
245
+ l = []
246
+ for o in objs:
247
+ l.append(o.mo)
248
+ return Obj2d(_m.CrossSection.batch_boolean(l, _m.OpType.Add))
249
+ elif ty == Obj3d:
250
+ l = []
251
+ for o in objs:
252
+ l.append(o.mo)
253
+ return Obj3d(_m.Manifold.batch_boolean(l, _m.OpType.Add))
254
+ else:
255
+ raise ValidationError(&#34;All objects must be of one type, Obj2d or Obj3d&#34;)</code></pre>
256
+ </details>
257
+ <div class="desc"><p>Returns the object made by adding all the <code>objs</code> together.</p>
258
+ <iframe width="100%" height="250" src="examples/union2d.html"></iframe>
259
+ <iframe width="100%" height="250" src="examples/union3d.html"></iframe></div>
260
+ </dd>
261
+ </dl>
262
+ </section>
263
+ <section>
264
+ </section>
265
+ </article>
266
+ <nav id="sidebar">
267
+ <div class="toc">
268
+ <ul>
269
+ <li><a href="#miscellaneous-bulk-operations-that-work-on-multiple-objects">Miscellaneous bulk operations that work on multiple objects.</a></li>
270
+ </ul>
271
+ </div>
272
+ <ul id="index">
273
+ <li><h3>Super-module</h3>
274
+ <ul>
275
+ <li><code><a title="piecad" href="index.html">piecad</a></code></li>
276
+ </ul>
277
+ </li>
278
+ <li><h3><a href="#header-functions">Functions</a></h3>
279
+ <ul class="">
280
+ <li><code><a title="piecad.bulk_ops.compose" href="#piecad.bulk_ops.compose">compose</a></code></li>
281
+ <li><code><a title="piecad.bulk_ops.difference" href="#piecad.bulk_ops.difference">difference</a></code></li>
282
+ <li><code><a title="piecad.bulk_ops.hull" href="#piecad.bulk_ops.hull">hull</a></code></li>
283
+ <li><code><a title="piecad.bulk_ops.intersect" href="#piecad.bulk_ops.intersect">intersect</a></code></li>
284
+ <li><code><a title="piecad.bulk_ops.union" href="#piecad.bulk_ops.union">union</a></code></li>
285
+ </ul>
286
+ </li>
287
+ </ul>
288
+ </nav>
289
+ </main>
290
+ <footer id="footer">
291
+ <p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.6</a>.</p>
292
+ </footer>
293
+ </body>
294
+ </html>