├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── _layouts └── default.html ├── box_with_circular_holes ├── box_with_circular_holes.py ├── frame_box.py └── sketch_190918a.gif ├── box_with_rectangular_holes ├── box_with_rectangular_holes.py ├── frame_box.py └── sketch_190522a.gif ├── extruded_simple_poly ├── extruded_simple_poly.py ├── parts.py └── sketch_190917b.gif ├── paraboloid_box_v0 ├── debug.py ├── draw_2D.py ├── draw_3D.py ├── paraboloid_box_v0.png └── paraboloid_box_v0.py ├── simple_2D_unfolded_box ├── simple_2D_unfolded_box.png └── simple_2D_unfolded_box.py ├── unfold_pyramid ├── sketch_190502a.gif └── unfold_pyramid.py └── unfold_pyramidal_solid_py5 ├── geometry.py ├── sketch_190509a.gif └── unfold_pyramidal_solid.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: villares 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | custom: ['https://gumroad.com/villares#aunif', 'https://www.paypal.com/donate/?hosted_button_id=5B4MZ78C9J724'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.class 3 | *.DS_Store 4 | *.properties 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 Alexandre B A Villares 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paper objects with Processing and Python 2 | 3 |

4 | 5 | Studies in digital fabrication [from this GitHub repository](https://github.com/villares/Paper-objects-with-Processing-and-Python/) using the [py5](https://py5coding.org) library that combines the Processing drawing infrastructure with modern Python. More about [installing py5 and using the imported mode style](https://abav.lugaralgum.com/como-instalar-py5/index-EN.html). 6 | 7 | For the earlier [Processing Python Mode](https://abav.lugaralgum.com/como-instalar-o-processing-modo-python/index-EN.html) version of this project, visit the ["archived branch"](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/Processing-Python-mode). 8 | 9 | 12 | 13 | 14 | ## Simple 2D unfolded box 15 | 16 | ![simple_2D_unfolded_box](simple_2D_unfolded_box/simple_2D_unfolded_box.png) 17 | 18 | [Code for the Simple 2D unfolded box](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/simple_2D_unfolded_box/) 19 | 20 | ## Box with rectangular holes 21 | 22 | ![box_with_rectangular_holes](box_with_rectangular_holes/sketch_190522a.gif) 23 | 24 | [Code for Box with rectangular holes](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/box_with_rectangular_holes/) 25 | 26 | ## Box with cricular holes 27 | 28 | ![box_with_circular_holes](/box_with_circular_holes/sketch_190918a.gif) 29 | 30 | [Code for Box with circular holes](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/box_with_circular_holes/) 31 | 32 | ## Paraboloid box 33 | 34 | ![paraboloid_box_v0](paraboloid_box_v0/paraboloid_box_v0.png) 35 | 36 | [Code for Paraboloid box v0](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/paraboloid_box_v0/) 37 | 38 | ## Unfold Pyramid 39 | 40 | ![unfold_pyramid](unfold_pyramid/sketch_190502a.gif) 41 | 42 | [Code for Unfold pyramid](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/unfold_pyramid/) 43 | 44 | ## Unfold Pyramidal Solid 45 | 46 | ![unfold_pyramidal_solid](unfold_pyramidal_solid_py5/sketch_190509a.gif) 47 | 48 | [Code for Unfold pyramidal solid](https://github.com/villares/Paper-objects-with-Processing-and-Python/tree/main/unfold_pyramidal_solid_py5/) 49 | 50 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Alexandre Villares 8 | 9 | 10 | 11 | 12 |

Alexandre B A Villares

13 |
14 |
15 |
16 | {{ content }} 17 |
18 |
19 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /box_with_circular_holes/box_with_circular_holes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day 3 | 4 | Box with circular holes 5 | 6 | based on https://github.com/villares/sketch-a-day/tree/master/2019/sketch_190918a) 7 | """ 8 | 9 | from frame_box import frame_box, unfolded_frame_box 10 | 11 | DIMENSION_KEYS = (('a', 'd'), 12 | ('w', 's'), 13 | (LEFT, RIGHT), 14 | (UP, DOWN)) 15 | dimensions = [250, 150, 100, 30] 16 | modes = [-1, 0, 1] # click mouse to switch modes 17 | 18 | def setup(): 19 | size(600, 600, P3D) 20 | 21 | def draw(): 22 | background(200) 23 | translate(300, 300) 24 | w, h, d, thick = dimensions 25 | if modes[0] >= 0: 26 | fill(255) 27 | stroke(0) 28 | push_matrix() 29 | translate(0, 0, 200) 30 | rotate_x(HALF_PI / 2) 31 | frame_box(w, h, d, thick) 32 | pop_matrix() 33 | if modes[0] <= 0: 34 | unfolded_frame_box(w, h, d, thick) 35 | 36 | def mouse_pressed(): 37 | modes[:] = modes[1:] + [modes[0]] 38 | 39 | def key_pressed(): 40 | if key == 'p': 41 | save_frame("a###.png") 42 | if key == ' ': 43 | dimensions[:] = [250, 150, 100, 30] 44 | 45 | k = key_code if key == CODED else key 46 | for i, (plus, minus) in enumerate(DIMENSION_KEYS): 47 | if k == plus: 48 | dimensions[i] += 1 49 | elif k == minus: 50 | dimensions[i] -= 1 51 | -------------------------------------------------------------------------------- /box_with_circular_holes/frame_box.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | CUT_STROKE, FOLD_STROKE = py5.color(255, 0, 0), py5.color(0, 0, 255) 4 | 5 | def frame_box(w, h, d, thick=0): 6 | """ draw the 3D version of the box with rectangular holes """ 7 | mw, mh, md = w / 2., h / 2., d / 2. 8 | py5.translate(0, 0, -md) # base 9 | face(0, 0, w, h, thick) 10 | py5.translate(0, 0, d) # top 11 | face(0, 0, w, h, thick) 12 | py5.translate(0, 0, -md) # back to 0 13 | py5.rotate_y(py5.HALF_PI) 14 | py5.translate(0, 0, -mw) # left side 15 | face(0, 0, d, h, thick) 16 | py5.translate(0, 0, w) # right side 17 | face(0, 0, d, h, thick) 18 | py5.translate(0, 0, -mw) # back to middle 19 | py5.rotate_y(-py5.HALF_PI) # back to 0 rotation 20 | py5.rotate_x(py5.HALF_PI) 21 | py5.translate(0, 0, -mh) # lateral e 22 | face(0, 0, w, d, thick) 23 | py5.translate(0, 0, h) # lateral d 24 | face(0, 0, w, d, thick) 25 | py5.translate(0, 0, -mw) # reset translate 26 | py5.rotate_x(-py5.HALF_PI) # reset rotate 27 | 28 | 29 | def face(x, y, w, h, e): 30 | mw, mh = w / 2., h / 2. 31 | py5.push_matrix() 32 | py5.translate(x, y) 33 | py5.begin_shape() 34 | py5.vertex(-mw, -mh) 35 | py5.vertex(+mw, -mh) 36 | py5.vertex(+mw, +mh) 37 | py5.vertex(-mw, +mh) 38 | hole(mw, mh, e) 39 | py5.end_shape(py5.CLOSE) 40 | py5.pop_matrix() 41 | 42 | 43 | def hole(mw, mh, e): 44 | if e > 0 and mw - e > 0 and mh - e > 0: 45 | py5.begin_contour() 46 | np = 24 47 | for i in range(np): 48 | ang = py5.TWO_PI / np * i 49 | x = py5.sin(ang) * e 50 | y = py5.cos(ang) * e 51 | py5.vertex(x, y) 52 | py5.end_contour() 53 | 54 | 55 | def unfolded_frame_box(w, h, d, thick=0, draw_main=True): 56 | mw, mh, md = w / 2., h / 2., d / 2. 57 | unfolded_face(0, -h - md, w, d, "aaan", thick, draw_main) 58 | unfolded_face(0, -mh, w, h, "vvvv", thick, draw_main) 59 | unfolded_face(0, -mh + mh + md, w, d, "cncv", thick, draw_main) 60 | unfolded_face(0, +mh + d, w, h, "cncc", thick, draw_main) 61 | unfolded_face(-mw - md, -mh, d, h, "acna", thick, draw_main) 62 | unfolded_face(mw + md, -mh, d, h, "ncaa", thick, draw_main) 63 | 64 | 65 | def unfolded_face(x, y, w, h, edge_types, thick=0, draw_main=True): 66 | e0, e1, e2, e3 = edge_types 67 | mw, mh = w / 2., h / 2. 68 | py5.push_matrix() 69 | py5.translate(x, y) 70 | if draw_main: 71 | edge(-mw, +mh, -mw, -mh, e0) 72 | edge(-mw, -mh, +mw, -mh, e1) 73 | edge(+mw, -mh, +mw, +mh, e2) 74 | edge(+mw, +mh, -mw, +mh, e3) 75 | if thick > 0 and mw - thick > 0 and mh - thick > 0: 76 | py5.stroke(CUT_STROKE) 77 | py5.circle(0, 0, thick * 2) 78 | py5.pop_matrix() 79 | 80 | 81 | def edge(x0, y0, x1, y1, edge_type): 82 | if edge_type == "n": # no edge is drawn 83 | return 84 | elif edge_type == "c": # cut stroke selected 85 | py5.stroke(CUT_STROKE) 86 | else: 87 | py5.stroke(FOLD_STROKE) # fold stroke selected for "v" and "a" 88 | py5.line(x0, y0, x1, y1) # line drawn here 89 | if edge_type == "a": # tab (note a fold-stroke line was already drawn) 90 | py5.stroke(CUT_STROKE) 91 | py5.no_fill() 92 | glue_tab((x0, y0), (x1, y1), 10) 93 | 94 | 95 | def glue_tab(p1, p2, tab_w, cut_ang=py5.QUARTER_PI / 3): 96 | """ 97 | draws a trapezoidal or triangular glue tab along edge defined by p1 and p2, 98 | with width tab_w and cut angle a 99 | """ 100 | al = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) 101 | a1 = al + cut_ang + py5.PI 102 | a2 = al - cut_ang 103 | # calculate cut_len to get the right tab width 104 | cut_len = tab_w / py5.sin(cut_ang) 105 | f1 = (p1[0] + cut_len * py5.sin(a1), 106 | p1[1] + cut_len * py5.cos(a1)) 107 | f2 = (p2[0] + cut_len * py5.sin(a2), 108 | p2[1] + cut_len * py5.cos(a2)) 109 | edge_len = py5.dist(p1[0], p1[1], p2[0], p2[1]) 110 | 111 | if edge_len > 2 * cut_len * py5.cos(cut_ang): # 'normal' trapezoidal tab 112 | py5.begin_shape() 113 | py5.vertex(*p1) # vertex(p1[0], p1[1]) 114 | py5.vertex(*f1) 115 | py5.vertex(*f2) 116 | py5.vertex(*p2) 117 | py5.end_shape() 118 | else: # short triangular tab 119 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 120 | py5.begin_shape() 121 | py5.vertex(*p1) 122 | py5.vertex(*fm) # middle way of f1 and f2 123 | py5.vertex(*p2) 124 | py5.end_shape() 125 | 126 | 127 | -------------------------------------------------------------------------------- /box_with_circular_holes/sketch_190918a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/box_with_circular_holes/sketch_190918a.gif -------------------------------------------------------------------------------- /box_with_rectangular_holes/box_with_rectangular_holes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day 3 | 4 | Box with rectangular holes 5 | """ 6 | 7 | from frame_box import frame_box, unfolded_frame_box 8 | 9 | DIMENSION_KEYS = (('a', 'd'), 10 | ('w', 's'), 11 | (LEFT, RIGHT), 12 | (UP, DOWN)) 13 | modes = [-1, 0, 1] # click mouse to switch modes 14 | dimensions = [250, 150, 100, 30] # initial dimensions, a list to be mutated 15 | 16 | def setup(): 17 | size(600, 600, P3D) 18 | 19 | def draw(): 20 | background(200) 21 | translate(300, 300) 22 | w, h, d, thick = dimensions 23 | if modes[0] >= 0: 24 | fill(255) 25 | stroke(0) 26 | push_matrix() 27 | translate(0, 0, 200) 28 | rotate_x(HALF_PI / 2) 29 | frame_box(w, h, d, thick) 30 | pop_matrix() 31 | 32 | if modes[0] <= 0: 33 | unfolded_frame_box(w, h, d, thick) 34 | 35 | if is_key_pressed: 36 | k = key_code if key == CODED else key 37 | for i, (plus, minus) in enumerate(DIMENSION_KEYS): 38 | if k == plus: 39 | dimensions[i] += 1 40 | elif k == minus: 41 | dimensions[i] -= 1 42 | 43 | def mouse_pressed(): 44 | modes[:] = modes[1:] + [modes[0]] 45 | 46 | def key_pressed(): 47 | if key == 's': 48 | save_frame("a###.png") 49 | if key == ' ': 50 | dimensions[:] = [250, 150, 100, 30] 51 | 52 | -------------------------------------------------------------------------------- /box_with_rectangular_holes/frame_box.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | CUT_STROKE, FOLD_STROKE = py5.color(255, 0, 0), py5.color(0, 0, 255) 4 | 5 | def frame_box(w, h, d, thick=0): 6 | """ draw the 3D version of the box with rectangular holes """ 7 | mw, mh, md = w / 2., h / 2., d / 2. 8 | py5.translate(0, 0, -md) # base 9 | face(0, 0, w, h, thick) 10 | py5.translate(0, 0, d) # top 11 | face(0, 0, w, h, thick) 12 | py5.translate(0, 0, -md) # back to 0 13 | py5.rotate_y(py5.HALF_PI) 14 | py5.translate(0, 0, -mw) # left side 15 | face(0, 0, d, h, thick) 16 | py5.translate(0, 0, w) # right side 17 | face(0, 0, d, h, thick) 18 | py5.translate(0, 0, -mw) # back to middle 19 | py5.rotate_y(-py5.HALF_PI) # back to 0 rotation 20 | py5.rotate_x(py5.HALF_PI) 21 | py5.translate(0, 0, -mh) # lateral e 22 | face(0, 0, w, d, thick) 23 | py5.translate(0, 0, h) # lateral d 24 | face(0, 0, w, d, thick) 25 | py5.translate(0, 0, -mw) # reset translate 26 | py5.rotate_x(-py5.HALF_PI) # reset rotate 27 | 28 | 29 | def face(x, y, w, h, thick): 30 | mw, mh = w / 2., h / 2. 31 | py5.push_matrix() 32 | py5.translate(x, y) 33 | py5.begin_shape() 34 | py5.vertex(-mw, -mh) 35 | py5.vertex(+mw, -mh) 36 | py5.vertex(+mw, +mh) 37 | py5.vertex(-mw, +mh) 38 | if thick > 0 and mw - thick > 0 and mh - thick > 0: 39 | mw -= thick 40 | mh -= thick 41 | py5.begin_contour() # counterclockwise hole 42 | py5.vertex(-mw, -mh) 43 | py5.vertex(-mw, +mh) 44 | py5.vertex(+mw, +mh) 45 | py5.vertex(+mw, -mh) 46 | py5.end_contour() 47 | py5.end_shape(py5.CLOSE) 48 | py5.pop_matrix() 49 | 50 | 51 | def unfolded_frame_box(w, h, d, thick=0, draw_main=True): 52 | mw, mh, md = w / 2., h / 2., d / 2. 53 | unfolded_face(0, -h - md, w, d, "aaan", thick, draw_main) 54 | unfolded_face(0, -mh, w, h, "vvvv", thick, draw_main) 55 | unfolded_face(0, -mh + mh + md, w, d, "cncv", thick, draw_main) 56 | unfolded_face(0, +mh + d, w, h, "cncc", thick, draw_main) 57 | unfolded_face(-mw - md, -mh, d, h, "acna", thick, draw_main) 58 | unfolded_face(mw + md, -mh, d, h, "ncaa", thick, draw_main) 59 | 60 | 61 | def unfolded_face(x, y, w, h, edge_types, thick=0, draw_main=True): 62 | e0, e1, e2, e3 = edge_types 63 | mw, mh = w / 2., h / 2. 64 | py5.push_matrix() 65 | py5.translate(x, y) 66 | if draw_main: 67 | edge(-mw, +mh, -mw, -mh, e0) 68 | edge(-mw, -mh, +mw, -mh, e1) 69 | edge(+mw, -mh, +mw, +mh, e2) 70 | edge(+mw, +mh, -mw, +mh, e3) 71 | if thick > 0 and mw - thick > 0 and mh - thick > 0: 72 | unfolded_face(0, 0, w - thick * 2, h - thick * 2, "cccc") 73 | py5.pop_matrix() 74 | 75 | 76 | def edge(x0, y0, x1, y1, edge_type): 77 | if edge_type == "n": # no edge is drawn 78 | return 79 | elif edge_type == "c": # cut stroke selected 80 | py5.stroke(CUT_STROKE) 81 | else: 82 | py5.stroke(FOLD_STROKE) # fold stroke selected for "v" and "a" 83 | py5.line(x0, y0, x1, y1) # line drawn here 84 | if edge_type == "a": # tab (note a fold-stroke line was already drawn) 85 | py5.stroke(CUT_STROKE) 86 | py5.no_fill() 87 | glue_tab((x0, y0), (x1, y1), 10) 88 | 89 | 90 | def glue_tab(p1, p2, tab_w, cut_ang=py5.QUARTER_PI / 3): 91 | """ 92 | draws a trapezoidal or triangular glue tab along edge defined by p1 and p2, 93 | with width tab_w and cut angle a 94 | """ 95 | al = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) 96 | a1 = al + cut_ang + py5.PI 97 | a2 = al - cut_ang 98 | # calculate cut_len to get the right tab width 99 | cut_len = tab_w / py5.sin(cut_ang) 100 | f1 = (p1[0] + cut_len * py5.sin(a1), 101 | p1[1] + cut_len * py5.cos(a1)) 102 | f2 = (p2[0] + cut_len * py5.sin(a2), 103 | p2[1] + cut_len * py5.cos(a2)) 104 | edge_len = py5.dist(p1[0], p1[1], p2[0], p2[1]) 105 | 106 | if edge_len > 2 * cut_len * py5.cos(cut_ang): # 'normal' trapezoidal tab 107 | py5.begin_shape() 108 | py5.vertex(*p1) # vertex(p1[0], p1[1]) 109 | py5.vertex(*f1) 110 | py5.vertex(*f2) 111 | py5.vertex(*p2) 112 | py5.end_shape() 113 | else: # short triangular tab 114 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 115 | py5.begin_shape() 116 | py5.vertex(*p1) 117 | py5.vertex(*fm) # middle way of f1 and f2 118 | py5.vertex(*p2) 119 | py5.end_shape() 120 | -------------------------------------------------------------------------------- /box_with_rectangular_holes/sketch_190522a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/box_with_rectangular_holes/sketch_190522a.gif -------------------------------------------------------------------------------- /extruded_simple_poly/extruded_simple_poly.py: -------------------------------------------------------------------------------- 1 | # Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day 2 | """ 3 | Unfold simple extrusion (no holes) 4 | """ 5 | 6 | from parts import Face, glue_tab 7 | 8 | faces = [] 9 | THICK = 20 10 | 11 | def setup(): 12 | size(740, 480, P3D) 13 | zig = [(2.5, 3.5), (5.5, 3.5), (2.5, 5.5), 14 | # (5.5, 5.5), (2.5, 7.5), (5.5, 7.5), 15 | # (3.5, 9.5), (8.5, 6.5), (5.5, 6.5), 16 | (8.5, 4.5), (5.5, 4.5), (8.5, 2.5), 17 | # (5.5, 2.5), (7.5, 0.5) 18 | ] 19 | faces.append(Face(zig, THICK)) 20 | 21 | 22 | def draw(): 23 | background(200, 210, 220) 24 | for f in faces: 25 | fill(255) 26 | stroke(0) 27 | f.draw_3D(-QUARTER_PI) 28 | no_fill() 29 | stroke(255, 0, 0) 30 | translate(200, 0) 31 | f.draw_2D() 32 | translate(200, 0) 33 | f.draw_2D() 34 | x, y = 25, 350 35 | translate(-400, 0) 36 | for p1, p2 in f.edges(): 37 | d = dist(p1[0], p1[1], p2[0], p2[1]) * 35 38 | glue_tab((x, y), (x + d, y)) 39 | glue_tab((x + d, y + THICK),( x, y + THICK)) 40 | stroke(0, 0, 255) 41 | rect(x, y , d, THICK) 42 | x += d 43 | stroke(255, 0, 0) 44 | if x > width - d: 45 | glue_tab((x, y), (x, y + THICK)) 46 | x = 25 47 | y += THICK * 2.2 48 | else: # a for else... 49 | glue_tab((x, y), (x, y + THICK)) 50 | -------------------------------------------------------------------------------- /extruded_simple_poly/parts.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | 4 | class Face: 5 | 6 | def __init__(self, pts, thickness, orientation=(0, 0, 0)): 7 | self.points = pts 8 | self.thickness = thickness 9 | self.o = orientation 10 | 11 | def draw_2D(self): 12 | draw_poly(self.points) 13 | 14 | def draw_3D(self, rot): 15 | S = 35.28 16 | 17 | t = self.thickness 18 | pts = self.points 19 | with py5.push_matrix(): 20 | py5.translate(0, py5.height() / 2) 21 | py5.rotate_x(self.o[0] * 0 + rot) 22 | py5.translate(0, -py5.height() / 2) 23 | py5.translate(0, 0, -t/2) 24 | py5.fill(230) 25 | draw_poly(pts) 26 | py5.translate(0, 0, t) 27 | py5.fill(170) 28 | draw_poly(pts) 29 | py5.fill(250) 30 | for p1, p2 in pairwise(tuple(pts) + (pts[0],)): 31 | # print((p1, p2)) 32 | py5.begin_shape(py5.QUAD_STRIP) 33 | py5.vertex(p1[0]*S, p1[1]*S, 0) 34 | py5.vertex(p1[0]*S, p1[1]*S, -t) 35 | py5.vertex(p2[0]*S, p2[1]*S, 0) 36 | py5.vertex(p2[0]*S, p2[1]*S, -t) 37 | py5.end_shape() 38 | 39 | def edges(self): 40 | return pairwise(tuple(self.points) + (self.points[0],)) 41 | 42 | 43 | def draw_poly(pts, closed=True): 44 | S = 35.28 45 | py5.begin_shape() 46 | for p in pts: 47 | py5.vertex(p[0]*S, p[1]*S, 0) 48 | if closed: 49 | py5.end_shape(py5.CLOSE) 50 | else: 51 | py5.end_shape() 52 | 53 | 54 | def pairwise(iterable): 55 | import itertools 56 | "s -> (s0,s1), (s1,s2), (s2, s3), ..." 57 | a, b = itertools.tee(iterable) 58 | next(b, None) 59 | return zip(a, b) 60 | 61 | 62 | def glue_tab(p1, p2, tab_w=10, cut_ang=py5.QUARTER_PI/2): 63 | """ 64 | draws a trapezoidal or triangular glue tab 65 | along edge defined by p1 and p2, with provided 66 | width (tab_w) and cut angle (cut_ang) 67 | """ 68 | a1 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) + cut_ang + py5.PI 69 | a2 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) - cut_ang 70 | # calculate cut_len to get the right tab width 71 | cut_len = tab_w / py5.sin(cut_ang) 72 | f1 = (p1[0] + cut_len * py5.sin(a1), 73 | p1[1] + cut_len * py5.cos(a1)) 74 | f2 = (p2[0] + cut_len * py5.sin(a2), 75 | p2[1] + cut_len * py5.cos(a2)) 76 | edge_len = py5.dist(p1[0], p1[1], p2[0], p2[1]) 77 | 78 | if edge_len > 2 * cut_len * py5.cos(cut_ang): # 'normal' trapezoidal tab 79 | with py5.begin_shape(): 80 | py5.vertex(*p1) # vertex(p1[0], p1[1]) 81 | py5.vertex(*f1) 82 | py5.vertex(*f2) 83 | py5.vertex(*p2) 84 | else: # short triangular tab 85 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 86 | with py5.begin_shape(): 87 | py5.vertex(*p1) 88 | py5.vertex(*fm) # middle way of f1 and f2 89 | py5.vertex(*p2) 90 | 91 | 92 | -------------------------------------------------------------------------------- /extruded_simple_poly/sketch_190917b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/extruded_simple_poly/sketch_190917b.gif -------------------------------------------------------------------------------- /paraboloid_box_v0/debug.py: -------------------------------------------------------------------------------- 1 | DEBUG = False 2 | 3 | def debug_text(name, points, enum=False): 4 | if DEBUG: 5 | for i, p in enumerate(points): 6 | with push(): 7 | 8 | fill(255, 0, 0) 9 | if enum: 10 | translate(0, -5, 10) 11 | text(name + "-" + str(i), *p) 12 | else: 13 | translate(10, 10, 10) 14 | text(name[i], *p) 15 | -------------------------------------------------------------------------------- /paraboloid_box_v0/draw_2D.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | from draw_3D import poly_draw 4 | from debug import debug_text 5 | 6 | CUT_COLOR = py5.color(255, 0, 0) # Color to mark outline cut 7 | ENG_COLOR = py5.color(0, 0, 255) # Color to mark folding/engraving 8 | TAB_W = 10 # glue tab width 9 | TAB_A = py5.radians(30) # glue tab angle 10 | 11 | 12 | def draw_unfolded(box_w, box_d, ab_l, cd_l, face_data): 13 | """ 14 | main 2D drawing procedure 15 | takes 2 box dimentions, 2 top point height lists, 16 | and a collection of 3D points (face_data) from the 3D procedure 17 | then draws the unfolded version of the volume with glue tabs 18 | """ 19 | ah, bh, ch, dh = ab_l[0], ab_l[-1], cd_l[0], cd_l[-1] 20 | ah_2d, a0_2d = (box_w * 2 + box_d, -ah), (box_w * 2 + box_d, 0) 21 | bh_2d, b0_2d = (0, -bh), (0, 0) 22 | ch_2d, c0_2d = (box_w, -ch), (box_w, 0) 23 | dh_2d, d0_2d = (box_w + box_d, -dh), (box_w + box_d, 0) 24 | 25 | py5.no_fill() 26 | # Marked for folding 27 | py5.stroke(ENG_COLOR) 28 | # verticals 29 | line_draw(b0_2d, bh_2d) 30 | line_draw(c0_2d, ch_2d) 31 | line_draw(d0_2d, dh_2d) 32 | line_draw(a0_2d, ah_2d) 33 | debug_text("BCDA", (bh_2d, ch_2d, dh_2d, ah_2d)) 34 | 35 | # divided top face - also draws some CUT_COLOR glue tabs! 36 | start_1, start_2 = bh_2d, ch_2d 37 | for a_3d, b_3d, c_3d, d_3d in face_data: 38 | start_1, start_2 = unfold_tri_face((start_1, start_2), 39 | (a_3d, b_3d, c_3d, d_3d)) 40 | 41 | # top tab 42 | line_draw(start_1, start_2, tab=True) 43 | # floor face 44 | py5.rect(0, 0, box_w, box_d) 45 | 46 | # Marked for cutting 47 | py5.stroke(CUT_COLOR) 48 | # middle tab 49 | glue_tab(b0_2d, bh_2d, TAB_W, TAB_A) 50 | # floor tabs 51 | glue_tab((0, box_d), b0_2d, TAB_W, TAB_A) 52 | glue_tab((box_w, box_d), (0, box_d), TAB_W, TAB_A) 53 | glue_tab((box_w, 0), (box_w, box_d), TAB_W, TAB_A) 54 | # main outline cut 55 | num_pts = len(cd_l) 56 | cd_2_dpts = [(box_w + box_d * i / (num_pts - 1), -cd_l[i]) 57 | for i in range(num_pts)] 58 | ab_2_dpts = [(box_w * 2 + box_d + box_d * i / (num_pts - 1), -ab_l[i]) 59 | for i in range(num_pts)] 60 | main_outline = cd_2_dpts + ab_2_dpts + [(box_w * 2 + box_d * 2, 0), c0_2d] 61 | poly_draw(main_outline, closed=False) 62 | 63 | 64 | def line_draw(p1, p2, tab=False): 65 | """ 66 | sugar for drawing lines from 2 "points" (tuples or PVectors) 67 | may also draw a glue tab suitably marked for cutting. 68 | """ 69 | py5.line(p1[0], p1[1], p2[0], p2[1]) 70 | if tab: 71 | with py5.push_style(): 72 | py5.stroke(CUT_COLOR) 73 | glue_tab(p1, p2, TAB_W, TAB_A) 74 | 75 | 76 | def glue_tab(p1, p2, tab_w=10, cut_ang=py5.QUARTER_PI): 77 | """ 78 | draws a trapezoidal or triangular glue tab 79 | along edge defined by p1 and p2, with provided 80 | width (tab_w) and cut angle (cut_ang) 81 | """ 82 | a1 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) + cut_ang + py5.PI 83 | a2 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) - cut_ang 84 | # calculate cut_len to get the right tab width 85 | cut_len = tab_w / py5.sin(cut_ang) 86 | f1 = py5.Py5Vector(p1[0] + cut_len * py5.sin(a1), 87 | p1[1] + cut_len * py5.cos(a1)) 88 | f2 = py5.Py5Vector(p2[0] + cut_len * py5.sin(a2), 89 | p2[1] + cut_len * py5.cos(a2)) 90 | edge_len = py5.dist(p1[0], p1[1], p2[0], p2[1]) 91 | 92 | if edge_len > 2 * cut_len * py5.cos(cut_ang): # 'normal' trapezoidal tab 93 | line_draw(p1, f1) 94 | line_draw(f1, f2) 95 | line_draw(f2, p2) 96 | else: # short triangular tab 97 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 98 | line_draw(p1, fm) 99 | line_draw(fm, p2) 100 | 101 | 102 | def unfold_tri_face(pts_2_d, pts_3_d): 103 | """ 104 | gets a collection of 2 (B, D) starting 2D points (PVectors or tuples) 105 | Gets a collection of 4 (A, B, C, D) 3D points (PVectors or tuples) 106 | Draws the unfolded face a returns (A, C) 2D positions. 107 | """ 108 | b2_d, c2_d = pts_2_d 109 | a3_d, b3_d, c3_d, d3_d = pts_3_d 110 | bd_len = py5.dist(b3_d[0], b3_d[1], b3_d[2], d3_d[0], d3_d[1], d3_d[2]) 111 | cd_len = py5.dist(c3_d[0], c3_d[1], c3_d[2], d3_d[0], d3_d[1], d3_d[2]) 112 | # lower triangle 113 | d2_d = third_point(b2_d, c2_d, bd_len, cd_len)[ 114 | 0] # gets the first solution 115 | line_draw(b2_d, c2_d) 116 | line_draw(b2_d, d2_d) 117 | line_draw(d2_d, c2_d, tab=True) 118 | # upper triangle (fixed from 190408a) 119 | ab_len = py5.dist(b3_d[0], b3_d[1], b3_d[2], a3_d[0], a3_d[1], a3_d[2]) 120 | ad_len = py5.dist(a3_d[0], a3_d[1], a3_d[2], d3_d[0], d3_d[1], d3_d[2]) 121 | # gets the 1st solution too! 122 | a2_d = third_point(b2_d, d2_d, ab_len, ad_len)[0] 123 | line_draw(b2_d, a2_d, tab=True) 124 | line_draw(d2_d, a2_d) 125 | return (a2_d, d2_d) 126 | 127 | 128 | def third_point(a, b, ac_len, bc_len): 129 | """ 130 | Adapted from code by Monkut https://stackoverflow.com/users/24718/monkut 131 | at https://stackoverflow.com/questions/4001948/drawing-a-triangle-in-a-coordinate-plane-given-its-three-sides 132 | 133 | Returns two point c options given: 134 | point a, point b, ac length, bc length 135 | """ 136 | class NoTrianglePossible(BaseException): 137 | pass 138 | 139 | # To allow use of tuples, creates or recreates PVectors 140 | a, b = py5.Py5Vector(*a), py5.Py5Vector(*b) 141 | # check if a triangle is possible 142 | ab_len = a.dist(b) 143 | if ab_len > (ac_len + bc_len) or ab_len < abs(ac_len - bc_len): 144 | raise NoTrianglePossible("The sides do not form a triangle") 145 | 146 | # get the length to the vertex of the right triangle formed, 147 | # by the intersection formed by circles a and b 148 | ad_len = (ab_len ** 2 + ac_len ** 2 - bc_len ** 2) / (2.0 * ab_len) 149 | # get the height of the line at a right angle from a_len 150 | h = py5.sqrt(abs(ac_len ** 2 - ad_len ** 2)) 151 | 152 | # Calculate the mid point d, needed to calculate point c(1|2) 153 | d = py5.Py5Vector(a.x + ad_len * (b.x - a.x) / ab_len, 154 | a.y + ad_len * (b.y - a.y) / ab_len) 155 | # get point c locations 156 | c1 = py5.Py5Vector(d.x + h * (b.y - a.y) / ab_len, 157 | d.y - h * (b.x - a.x) / ab_len) 158 | c2 = py5.Py5Vector(d.y + h * (b.x - a.x) / ab_len, 159 | d.x - h * (b.y - a.y) / ab_len) 160 | return c1, c2 161 | 162 | -------------------------------------------------------------------------------- /paraboloid_box_v0/draw_3D.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | from debug import debug_text 4 | 5 | def draw_3D(box_w, box_d, ab_l, cd_l): 6 | """ 7 | main 3D drawing procedure, this also calculates some 3D point positions 8 | from 2 lists of heights (ab_l and cd_l) that will then be returned 9 | and used by the 2D procedure 10 | """ 11 | # calculates upper 3D points from heights 12 | num_pts = len(cd_l) 13 | cd_3_dpts = tuple([(box_w, 14 | box_d - box_d * i / (num_pts - 1), 15 | cd_l[::-1][i]) for i in range(num_pts)]) 16 | ab_3_dpts = tuple([(0, box_d * i / (num_pts - 1), ab_l[::-1][i]) 17 | for i in range(num_pts)]) 18 | # draw faces 19 | py5.stroke(0) 20 | py5.fill(255, 200) 21 | # floor face 22 | poly_draw(((0, 0, 0), 23 | (box_w, 0, 0), 24 | (box_w, box_d, 0), 25 | (0, box_d, 0))) 26 | # face 0 27 | poly_draw(((0, 0, ab_l[-1]), 28 | (box_w, 0, cd_l[0]), 29 | (box_w, 0, 0), 30 | (0, 0, 0))) 31 | # face 1 32 | poly_draw(cd_3_dpts + ( 33 | (box_w, 0, 0), 34 | (box_w, box_d, 0))) 35 | # face 2 36 | poly_draw(((box_w, box_d, cd_l[-1]), 37 | (0, box_d, ab_l[0]), 38 | (0, box_d, 0), 39 | (box_w, box_d, 0))) 40 | # face 3 41 | poly_draw(ab_3_dpts + ( 42 | (0, box_d, 0), 43 | (0, 0, 0))) 44 | # top faces - using calculated the 3D points 45 | face_data = [] 46 | for i in range(1, len(ab_3_dpts)): 47 | p = i - 1 48 | a = py5.Py5Vector(*ab_3_dpts[i]) 49 | b = py5.Py5Vector(*ab_3_dpts[p]) 50 | c = py5.Py5Vector(*cd_3_dpts[::-1][p]) 51 | d = py5.Py5Vector(*cd_3_dpts[::-1][i]) 52 | triangulated_face(a, b, c, d) 53 | face_data.append((a, b, c, d)) 54 | # debug text 55 | debug_text("cd", cd_3_dpts[::-1], enum=True) 56 | debug_text("ab", ab_3_dpts[::-1], enum=True) 57 | debug_text("DAad", ((box_w, box_d, cd_l[-1]), 58 | (0, box_d, ab_l[0]), 59 | (0, box_d, 0), 60 | (box_w, box_d, 0))) 61 | return face_data # returns to be used by the 2D procedure 62 | 63 | 64 | def poly_draw(pts, closed=True): 65 | """ sugar for face drawing """ 66 | py5.begin_shape() 67 | for p in pts: 68 | py5.vertex(*p) 69 | if closed: 70 | py5.end_shape(py5.CLOSE) 71 | else: 72 | py5.end_shape() 73 | 74 | 75 | def triangulated_face(a, b, c, d): 76 | # two triangles - could be with a diferent diagonal! 77 | # TODO: let one choose diagonal orientation 78 | poly_draw((a, b, d)) 79 | poly_draw((b, d, c)) 80 | 81 | -------------------------------------------------------------------------------- /paraboloid_box_v0/paraboloid_box_v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/paraboloid_box_v0/paraboloid_box_v0.png -------------------------------------------------------------------------------- /paraboloid_box_v0/paraboloid_box_v0.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day 3 | 4 | A paraboloid test 5 | """ 6 | # add_library('peasycam') 7 | 8 | from draw_2D import draw_unfolded 9 | from draw_3D import draw_3D 10 | 11 | # initial box dimensions 12 | box_d, box_w, box_h = 100, 100, 100 13 | # height of points between d and c 14 | edge_num = 8 # number of points on top lateral edges 15 | cd_l = [box_h - 10 * i for i in range(edge_num)] 16 | # height of points between a and b 17 | ab_l = [box_h - 10 * i for i in range(edge_num)] 18 | assert len(cd_l) == len(ab_l) # equal number of points only 19 | 20 | 21 | def setup(): 22 | size(1000, 720, P3D) 23 | global cam, export 24 | #cam = PeasyCam(this, -450, 0, 0, 300) 25 | # hint(ENABLE_DEPTH_SORT) 26 | smooth(16) 27 | stroke_weight(2) 28 | export = False 29 | 30 | 31 | def draw(): 32 | global export 33 | background(200) 34 | # Draw 3D 35 | with push_matrix(): 36 | # Comment out if using with PeasyCam 37 | translate(width / 2, height / 2) 38 | translate(50, 0) 39 | rotate_x(QUARTER_PI) 40 | translate(-400, -50, -50) 41 | # rotateZ(0) 42 | face_3D_data = draw_3D(box_w, box_d, ab_l, cd_l) 43 | 44 | # Draw 2D unfolded 45 | if export: 46 | begin_record(PDF, SKETCH_NAME + ".pdf") 47 | # cam.begin_hud() # for use with PeasyCam 48 | with push_matrix(): 49 | translate(width * 0.66, 450) 50 | rotate(-HALF_PI) 51 | if export: 52 | scale(10, 10) 53 | draw_unfolded(box_w, box_d, ab_l, cd_l, face_3D_data) 54 | # cam.end_hud() 55 | if export: 56 | end_record() 57 | export = False 58 | 59 | 60 | def key_pressed(): 61 | global box_w, box_d, box_h 62 | global export 63 | 64 | ah, bh, ch, dh = ab_l[0], ab_l[-1], cd_l[0], cd_l[-1] 65 | if key == "q": 66 | ah += 5 67 | if key == "a" and ah > 5: 68 | ah -= 5 69 | if key == "w": 70 | bh += 5 71 | if key == "s" and bh > 5: 72 | bh -= 5 73 | if key == "e": 74 | ch += 5 75 | if key == "d" and ch > 5: 76 | ch -= 5 77 | if key == "r": 78 | dh += 5 79 | if key == "f" and dh > 5: 80 | dh -= 5 81 | if key_code == UP and box_d + box_w < 220: 82 | box_d += 5 83 | if key_code == DOWN and box_d > 5: 84 | box_d -= 5 85 | if key_code == RIGHT and box_w + box_d < 220: 86 | box_w += 5 87 | if key_code == LEFT and box_w > 5: 88 | box_w -= 5 89 | 90 | ab_l[0], ab_l[-1], cd_l[0], cd_l[-1] = ah, bh, ch, dh 91 | 92 | if key == "S": 93 | print("file exported") 94 | export = True 95 | if key == "p": 96 | save_frame("####.png") 97 | elif key in ("+", "="): 98 | box_h += 5 99 | cd_l[:] = [cd_l[i] + 5 for i in range(edge_num)] 100 | ab_l[:] = [ab_l[i] + 5 for i in range(edge_num)] 101 | elif (key == "-" and box_h > 5 and ah > 5 and bh > 5 and ch > 5 and dh > 5): 102 | box_h -= 5 103 | cd_l[:] = [cd_l[i] - 5 for i in range(edge_num)] 104 | ab_l[:] = [ab_l[i] - 5 for i in range(edge_num)] 105 | elif key == " ": 106 | slowly_reset_values() 107 | 108 | 109 | def slowly_reset_values(): 110 | global box_w, box_d, box_h, ah, bh, ch, dh 111 | 112 | box_w += (100 - box_w) / 2. 113 | box_d += (100 - box_d) / 2. 114 | delta_h = (100 - box_h) / 2. 115 | box_h += delta_h 116 | cd_l[:] = [cd_l[i] + delta_h for i in range(edge_num)] 117 | ab_l[:] = [ab_l[i] + delta_h for i in range(edge_num)] 118 | 119 | -------------------------------------------------------------------------------- /simple_2D_unfolded_box/simple_2D_unfolded_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/simple_2D_unfolded_box/simple_2D_unfolded_box.png -------------------------------------------------------------------------------- /simple_2D_unfolded_box/simple_2D_unfolded_box.py: -------------------------------------------------------------------------------- 1 | CUT_STROKE, FOLD_STROKE = color(255, 0, 0), color(0, 0, 255) 2 | 3 | 4 | def setup(): 5 | size(600, 600, P3D) 6 | 7 | 8 | def draw(): 9 | background(200) 10 | translate(300, 300) 11 | unfolded_box(250, 150, 100) 12 | 13 | 14 | def unfolded_box(w, h, d): 15 | mw, mh, md = w/2., h/2., d/2. 16 | face_2_d(0, -h - md, w, d, "aaan") 17 | face_2_d(0, -mh, w, h, "vvvv") 18 | face_2_d(0, -mh + mh + md, w, d, "cncv") 19 | face_2_d(0, +mh + d, w, h, "cncc") 20 | face_2_d(-mw - md, -mh, d, h, "acna") 21 | face_2_d(mw + md, -mh, d, h, "ncaa") 22 | 23 | 24 | def face_2_d(x, y, w, h, lados): 25 | """ 26 | O string lados: 27 | a: aba (tab) 28 | c: corte (cut) 29 | v: vinco (fold) 30 | n: não (no line) 31 | """ 32 | l0, l1, l2, l3 = lados 33 | mw, mh = w/2., h/2. 34 | push_matrix() 35 | translate(x, y) 36 | my_line(-mw, +mh, -mw, -mh, l0) 37 | my_line(-mw, -mh, +mw, -mh, l1) 38 | my_line(+mw, -mh, +mw, +mh, l2) 39 | my_line(+mw, +mh, -mw, +mh, l3) 40 | pop_matrix() 41 | 42 | 43 | def my_line(x0, y0, x1, y1, variation): 44 | if variation == "n": 45 | return 46 | elif variation == "c": 47 | stroke(CUT_STROKE) 48 | else: 49 | stroke(FOLD_STROKE) 50 | line(x0, y0, x1, y1) 51 | if variation == "a": 52 | stroke(CUT_STROKE) 53 | no_fill() 54 | glue_tab((x0, y0), (x1, y1), 10) 55 | 56 | 57 | def glue_tab(p1, p2, tab_w, cut_ang=QUARTER_PI/3): 58 | """ 59 | draws a trapezoidal or triangular glue tab along edge defined by p1 and p2, 60 | with width tab_w and cut angle a 61 | """ 62 | al = atan2(p1[0] - p2[0], p1[1] - p2[1]) 63 | a1 = al + cut_ang + PI 64 | a2 = al - cut_ang 65 | # calculate cut_len to get the right tab width 66 | cut_len = tab_w / sin(cut_ang) 67 | f1 = (p1[0] + cut_len * sin(a1), 68 | p1[1] + cut_len * cos(a1)) 69 | f2 = (p2[0] + cut_len * sin(a2), 70 | p2[1] + cut_len * cos(a2)) 71 | edge_len = dist(p1[0], p1[1], p2[0], p2[1]) 72 | 73 | if edge_len > 2 * cut_len * cos(cut_ang): # 'normal' trapezoidal tab 74 | begin_shape() 75 | vertex(*p1) # vertex(p1[0], p1[1]) 76 | vertex(*f1) 77 | vertex(*f2) 78 | vertex(*p2) 79 | end_shape() 80 | else: # short triangular tab 81 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 82 | begin_shape() 83 | vertex(*p1) 84 | vertex(*fm) # middle way of f1 and f2 85 | vertex(*p2) 86 | end_shape() 87 | -------------------------------------------------------------------------------- /unfold_pyramid/sketch_190502a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/unfold_pyramid/sketch_190502a.gif -------------------------------------------------------------------------------- /unfold_pyramid/unfold_pyramid.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alexandre B A Villares - https://abav.lugaralgum.com/sketch-a-day 3 | 4 | - Unfolding pyramids 5 | """ 6 | 7 | CUT_STROKE = color(255, 0, 0) 8 | FOLD_STROKE = color(0, 0, 255) 9 | 10 | outer_radius, inner_radius = 100, 50 11 | sides = 5 12 | 13 | 14 | def setup(): 15 | size(400, 600, P3D) 16 | hint(ENABLE_DEPTH_TEST) 17 | hint(ENABLE_DEPTH_SORT) 18 | 19 | 20 | def draw(): 21 | background(240) 22 | push_matrix() 23 | translate(width / 2, height / 4 + 50) 24 | rotate_x(radians(45)) 25 | rotate_z(radians(frame_count / 3.)) 26 | fill(255, 200) 27 | stroke(0) 28 | stroke_weight(2) 29 | # draw 3D pyramid and get pts 30 | pts = pyramid_3_d(sides, outer_radius, inner_radius) 31 | pop_matrix() 32 | # draw unfolded 2D 33 | translate(width / 2, height * 3 / 4 - 50) 34 | pyramid_2_d(pts) 35 | 36 | 37 | def pyramid_3_d(np, ext_r, base_r): 38 | # calculando os pts 39 | pts = [] 40 | n = np * 2 41 | for i in range(n): 42 | ang = radians(i * 360. / n) 43 | if i % 2 == 0: 44 | r = base_r 45 | else: 46 | r = ext_r 47 | x = sin(ang) * r 48 | y = cos(ang) * r 49 | pts.append((x, y)) 50 | # edges da base 51 | base_pts = pts[::2] 52 | o_base_pts = base_pts[1:] + [base_pts[0]] 53 | base_edges = zip(base_pts, o_base_pts) 54 | # calculo da altura 55 | (p0x, p0y), (p1x, p1y) = pts[0], pts[1] 56 | side = dist(p0x, p0y, p1x, p1y) 57 | h_squared = side * side - base_r * base_r 58 | if h_squared > 0: # se a altura viavel 59 | h = sqrt(h_squared) 60 | for edge in base_edges: 61 | p1, p2 = edge 62 | begin_shape() 63 | vertex(*p1) 64 | vertex(*p2) 65 | vertex(0, 0, h) 66 | end_shape(CLOSE) 67 | # always draws base 68 | begin_shape() 69 | for pt in base_pts: 70 | vertex(*pt) 71 | end_shape(CLOSE) 72 | # return pts for 2D! 73 | return pts 74 | 75 | 76 | def pyramid_2_d(pts): 77 | no_fill() 78 | # base fold lines 79 | stroke(FOLD_STROKE) 80 | begin_shape() 81 | for pt in pts[::2]: 82 | vertex(*pt) 83 | end_shape(CLOSE) 84 | # lateral edges 85 | o_pts = pts[1:] + [pts[0]] 86 | edges = zip(pts, o_pts) 87 | for i, edge in enumerate(edges): 88 | p1, p2 = edge 89 | stroke(CUT_STROKE) 90 | if i % 2 == 0: 91 | # abas de cola 92 | glue_tab(p2, p1, 10, ) 93 | # FOLD_STROKE 94 | stroke(FOLD_STROKE) 95 | line(p2[0], p2[1], p1[0], p1[1]) 96 | else: 97 | # outra edge cortada 98 | line(p1[0], p1[1], p2[0], p2[1]) 99 | 100 | 101 | def glue_tab(p1, p2, tab_w, cut_ang=QUARTER_PI / 3): 102 | """ 103 | draws a trapezoidal or triangular glue tab along edge defined by p1 and p2, 104 | with width tab_w and cut angle a 105 | """ 106 | al = atan2(p1[0] - p2[0], p1[1] - p2[1]) 107 | a1 = al + cut_ang + PI 108 | a2 = al - cut_ang 109 | # calculate cut_len to get the base_rght tab width 110 | cut_len = tab_w / sin(cut_ang) 111 | f1 = (p1[0] + cut_len * sin(a1), 112 | p1[1] + cut_len * cos(a1)) 113 | f2 = (p2[0] + cut_len * sin(a2), 114 | p2[1] + cut_len * cos(a2)) 115 | edge_len = dist(p1[0], p1[1], p2[0], p2[1]) 116 | 117 | if edge_len > 2 * cut_len * cos(cut_ang): # 'normal' trapezoidal tab 118 | begin_shape() 119 | vertex(*p1) # vertex(p1[0], p1[1]) 120 | vertex(*f1) 121 | vertex(*f2) 122 | vertex(*p2) 123 | end_shape() 124 | else: # short triangular tab 125 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 126 | begin_shape() 127 | vertex(*p1) 128 | vertex(*fm) # middle way of f1 and f2 129 | vertex(*p2) 130 | end_shape() 131 | 132 | 133 | def key_pressed(): 134 | global inner_radius, outer_radius, sides 135 | if key_code == UP: 136 | outer_radius += 5 137 | if key_code == DOWN: 138 | outer_radius -= 5 139 | if key_code == LEFT: 140 | inner_radius += 5 141 | if key_code == RIGHT: 142 | inner_radius -= 5 143 | if key == "+" or key == "=": 144 | sides += 1 145 | if key == "-" and sides > 3: 146 | sides -= 1 147 | -------------------------------------------------------------------------------- /unfold_pyramidal_solid_py5/geometry.py: -------------------------------------------------------------------------------- 1 | import py5 2 | 3 | CUT_STROKE = py5.color(255, 0, 0) 4 | 5 | 6 | def unfold_tri_face(pts_2D, pts_3D): 7 | """ 8 | gets a collection of 2 (B, C) starting 2D points (Py5Vectors or tuples) 9 | gets a collection of 4 (A, B, C, D) 3D points (p_vectors or tuples) 10 | draws the unfolded face and returns (A, D) 2D positions. 11 | """ 12 | b2D, c2D = pts_2D 13 | a3D, b3D, c3D, d3D = pts_3D 14 | bd_len = py5.dist(b3D[0], b3D[1], b3D[2], d3D[0], d3D[1], d3D[2]) 15 | cd_len = py5.dist(c3D[0], c3D[1], c3D[2], d3D[0], d3D[1], d3D[2]) 16 | # lower triangle 17 | d2D = third_point(b2D, c2D, bd_len, cd_len)[ 18 | 0] # gets the first solution 19 | line_draw(b2D, c2D) 20 | line_draw(d2D, c2D, tab=True) 21 | # upper triangle (fixed from 190408a) 22 | ab_len = py5.dist(b3D[0], b3D[1], b3D[2], a3D[0], a3D[1], a3D[2]) 23 | ad_len = py5.dist(a3D[0], a3D[1], a3D[2], d3D[0], d3D[1], d3D[2]) 24 | # gets the 1st solution too! 25 | a2D = third_point(b2D, d2D, ab_len, ad_len)[0] 26 | line_draw(b2D, a2D, tab=True) 27 | # line_draw(d2D, a2D) 28 | return (a2D, d2D) 29 | 30 | 31 | def third_point(a, b, ac_len, bc_len): 32 | """ 33 | Adapted from code by monkut https://stackoverflow.com/users/24718/monkut 34 | at https://stackoverflow.com/questions/4001948/drawing-a-triangle-in-a-coordinate-plane-given-its-three-sides 35 | for use with processing python mode - using p_vectors 36 | 37 | returns two point c options given: 38 | point a, point b, ac length, bc length 39 | """ 40 | class NoTrianglePossible(BaseException): 41 | pass 42 | 43 | # To allow use of tuples, creates or recreates PVectors 44 | a, b = py5.Py5Vector(*a), py5.Py5Vector(*b) 45 | # check if a triangle is possible 46 | ab_len = a.dist(b) 47 | if ab_len > (ac_len + bc_len) or ab_len < abs(ac_len - bc_len): 48 | raise no_triangle_possible("The sides do not form a triangle") 49 | 50 | # get the length to the vertex of the right triangle formed, 51 | # by the intersection formed by circles a and b 52 | ad_len = (ab_len ** 2 + ac_len ** 2 - bc_len ** 2) / (2.0 * ab_len) 53 | # get the height of the line at a right angle from a_len 54 | h = py5.sqrt(abs(ac_len ** 2 - ad_len ** 2)) 55 | 56 | # Calculate the mid point d, needed to calculate point c(1|2) 57 | d = py5.Py5Vector(a.x + ad_len * (b.x - a.x) / ab_len, 58 | a.y + ad_len * (b.y - a.y) / ab_len) 59 | # get point c locations 60 | c1 = py5.Py5Vector(d.x + h * (b.y - a.y) / ab_len, 61 | d.y - h * (b.x - a.x) / ab_len) 62 | c2 = py5.Py5Vector(d.y + h * (b.x - a.x) / ab_len, 63 | d.x - h * (b.y - a.y) / ab_len) 64 | return c1, c2 65 | 66 | 67 | def line_draw(p1, p2, tab=False): 68 | """ 69 | sugar for drawing lines from 2 "points" (tuples or p_vectors) 70 | may also draw a glue tab suitably marked for cutting. 71 | """ 72 | py5.line(p1[0], p1[1], p2[0], p2[1]) 73 | if tab: 74 | with py5.push_style(): 75 | py5.stroke(CUT_STROKE) 76 | glue_tab(p1, p2) 77 | 78 | 79 | def glue_tab(p1, p2, tab_w=10, cut_ang=py5.QUARTER_PI): 80 | """ 81 | draws a trapezoidal or triangular glue tab 82 | along edge defined by p1 and p2, with provided 83 | width (tab_w) and cut angle (cut_ang) 84 | """ 85 | a1 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) + cut_ang + py5.PI 86 | a2 = py5.atan2(p1[0] - p2[0], p1[1] - p2[1]) - cut_ang 87 | # calculate cut_len to get the right tab width 88 | cut_len = tab_w / py5.sin(cut_ang) 89 | f1 = (p1[0] + cut_len * py5.sin(a1), 90 | p1[1] + cut_len * py5.cos(a1)) 91 | f2 = (p2[0] + cut_len * py5.sin(a2), 92 | p2[1] + cut_len * py5.cos(a2)) 93 | edge_len = py5.dist(p1[0], p1[1], p2[0], p2[1]) 94 | 95 | if edge_len > 2 * cut_len * py5.cos(cut_ang): # 'normal' trapezoidal tab 96 | line_draw(p1, f1) 97 | line_draw(f1, f2) 98 | line_draw(f2, p2) 99 | else: # short triangular tab 100 | fm = ((f1[0] + f2[0]) / 2, (f1[1] + f2[1]) / 2) 101 | line_draw(p1, fm) 102 | line_draw(fm, p2) 103 | 104 | DEBUG = True 105 | 106 | def debug_text(name, pts, enum=False): 107 | if DEBUG: 108 | for i, p in enumerate(pts): 109 | with py5.push(): 110 | 111 | py5.fill(255, 0, 0) 112 | if enum: 113 | py5.translate(0, -5, 10) 114 | py5.text(name + "-" + str(i), *p) 115 | else: 116 | py5.translate(10, 10, 10) 117 | py5.text(name[i], *p) 118 | 119 | 120 | def poly_draw(pts, force_z=None, closed=True): 121 | """ sugar for face drawing """ 122 | py5.begin_shape() 123 | for p in pts: 124 | if force_z is None: 125 | py5.vertex(*p) 126 | else: 127 | py5.vertex(p[0], p[1], force_z) 128 | if closed: 129 | py5.end_shape(py5.CLOSE) 130 | else: 131 | py5.end_shape() 132 | 133 | 134 | def triangulated_face(*args): 135 | if len(args) == 4: 136 | a, b, c, d = args 137 | print("face") 138 | else: 139 | a, b, c, d = args[0] 140 | # two triangles - could be with a diferent diagonal! 141 | # TODO: let one choose diagonal orientation 142 | py5.stroke(0) 143 | poly_draw((a, b, d)) 144 | poly_draw((b, d, c)) 145 | 146 | 147 | def test(): 148 | #size(600, 400, P3D) 149 | p3D = [(50, 100, 0), (200, 100, 0), (200, 200, 0), (100, 300, -100)] 150 | debug_text("ABCD", p3D) 151 | py5.begin_shape() 152 | for p in p3D: 153 | py5.vertex(*p) 154 | py5.end_shape(py5.CLOSE) 155 | x0, y0, z0 = p3D[1] 156 | x2, y2, z2 = p3D[3] 157 | py5.line(x0, y0, z0, x2, y2, z2) 158 | print(py5.dist(x0, y0, z0, x2, y2, z2)) 159 | 160 | p2D = [(250, 100), (250, 200)] 161 | bx, by = p2D[0] 162 | debug_text("BC", p2D) 163 | for i in range(1): 164 | p2D = unfold_tri_face(p2D, p3D) 165 | print(p2D) 166 | debug_text("AD", p2D) 167 | dx, dy, _ = p2D[1] 168 | print(py5.dist(bx, by, dx, dy)) 169 | 170 | -------------------------------------------------------------------------------- /unfold_pyramidal_solid_py5/sketch_190509a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villares/Paper-objects-with-Processing-and-Python/f7b9bbd0bb4e330c8c95fcd1356c1dbdbc5483ba/unfold_pyramidal_solid_py5/sketch_190509a.gif -------------------------------------------------------------------------------- /unfold_pyramidal_solid_py5/unfold_pyramidal_solid.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alexandre B A Aillares - https://abav.lugaralgum.com/ 3 | 4 | Unfolding a prism or pyramideal solid 5 | 6 | https://github.com/villares/paper-objects-with-processing-and-python 7 | Now for py5 (py5coding.org) imported mode 8 | """ 9 | 10 | from geometry import poly_draw, line_draw, unfold_tri_face, glue_tab 11 | 12 | CUT_STROKE = color(255, 0, 0) 13 | FOLD_STROKE = color(0, 0, 255) 14 | 15 | p_height = 100 16 | base_radius, top_radius = 50, 50 17 | sides = 5 18 | 19 | def setup(): 20 | size(600, 600, P3D) 21 | hint(ENABLE_DEPTH_TEST) 22 | hint(ENABLE_DEPTH_SORT) 23 | 24 | def draw(): 25 | background(240) 26 | push_matrix() 27 | translate(width / 2, height / 4 + 50) 28 | rotate_x(radians(45)) 29 | rotate_z(radians(frame_count / 3.)) 30 | fill(255, 200) 31 | stroke(0) 32 | stroke_weight(2) 33 | # draw 3D piramid and get points 34 | base, top, face = prism_3D(sides, p_height, base_radius, top_radius) 35 | pop_matrix() 36 | # draw unfolded 2D 37 | translate(width / 2, height * 3 / 4 - 50) 38 | prism_2D(base, top, face) 39 | 40 | 41 | def prism_3D(np, h, base_r, top_r): 42 | # calculando os points 43 | base_points = [] 44 | for i in range(np): 45 | ang = radians(i * 360. / np) 46 | x = sin(ang) * base_r 47 | y = cos(ang) * base_r 48 | base_points.append((x, y, 0)) 49 | # edges da base 50 | o_base_points = base_points[1:] + [base_points[0]] 51 | base_edges = list(zip(base_points, o_base_points)) 52 | top_points = [] 53 | for i in range(np): 54 | ang = radians(i * 360. / np) 55 | x = sin(ang) * top_r 56 | y = cos(ang) * top_r 57 | top_points.append((x, y, h)) 58 | # edges da base 59 | o_top_points = top_points[1:] + [top_points[0]] 60 | top_edges = list(zip(top_points, o_top_points)) 61 | # edges 62 | for base_edge, top_edge in zip(base_edges, top_edges): 63 | (p1x, p1y, p1z), (p2x, p2y, p2z) = base_edge 64 | (p1tx, p1ty, p1tz), (p2tx, p2ty, p2tz) = top_edge 65 | begin_shape() 66 | vertex(p1x, p1y, p1z) 67 | vertex(p1tx, p1ty, p1tz) 68 | vertex(p2tx, p2ty, p2tz) 69 | vertex(p2x, p2y, p2z) 70 | end_shape(CLOSE) 71 | # one face 72 | (p1x, p1y, p1z), (p2x, p2y, p2z) = base_edges[0] 73 | (p1tx, p1ty, p1tz), (p2tx, p2ty, p2tz) = top_edges[0] 74 | face = [(p2x, p2y, p2z), 75 | (p1x, p1y, p1z), 76 | (p1tx, p1ty, p1tz), 77 | (p2tx, p2ty, p2tz), 78 | ] 79 | # draw base and top 80 | poly_draw(top_points) 81 | poly_draw(base_points) 82 | 83 | return base_points, top_points, face 84 | 85 | 86 | def prism_2D(base, top, face): 87 | with push_matrix(): 88 | translate(150, -300) 89 | poly_draw(top, force_z=0) 90 | with push_matrix(): 91 | translate(-150, -300) 92 | poly_draw(base, force_z=0) 93 | x0, y0, z0 = face[1] 94 | x2, y2, z2 = face[2] 95 | d = dist(x0, y0, z0, x2, y2, z2) 96 | side = ((150, d - 150), (150, -150)) 97 | for i in range(sides): 98 | side = unfold_tri_face(side, face[::-1]) 99 | stroke(CUT_STROKE) 100 | line_draw(side[0], side[1]) 101 | glue_tab((150, -150), (150, d - 150)) 102 | 103 | 104 | def key_pressed(): 105 | global base_radius, top_radius, p_height, sides 106 | if key_code == UP: 107 | p_height += 5 108 | if key_code == DOWN: 109 | p_height -= 5 110 | if key_code == LEFT: 111 | base_radius += 5 112 | if key_code == RIGHT: 113 | base_radius -= 5 114 | if key == "w": 115 | sides += 1 116 | if key == "s" and sides > 3: 117 | sides -= 1 118 | if key == "a" and top_radius > 0: 119 | top_radius -= 5 120 | if key == "d": 121 | top_radius += 5 122 | if key == "p": 123 | save_frame(SKETCH_NAME + ".png") 124 | --------------------------------------------------------------------------------