├── .gitignore ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── main ├── opengeometry-babylon │ └── package.json ├── opengeometry-three │ ├── .DS_Store │ ├── index.ts │ ├── package.json │ └── src │ │ ├── base-types.ts │ │ ├── markup │ │ ├── baseMarker.ts │ │ └── spotMarker.ts │ │ ├── pencil.ts │ │ ├── primitives │ │ ├── circle.ts │ │ ├── index.ts │ │ ├── poly-line.ts │ │ ├── rectangle.ts │ │ └── simple-line.ts │ │ ├── shapes │ │ ├── cylinder.ts │ │ ├── index.ts │ │ └── polygon.ts │ │ ├── snapper.ts │ │ └── utils │ │ ├── event.ts │ │ ├── randomizer.ts │ │ └── store.ts ├── opengeometry-webgl │ └── package.json └── opengeometry │ ├── Cargo.toml │ ├── cad.md │ └── src │ ├── brep │ ├── edge.rs │ ├── face.rs │ └── vertex.rs │ ├── elements │ └── wall.rs │ ├── geometry │ ├── baseflatmesh.rs │ ├── basegeometry.rs │ ├── basegroup.rs │ ├── basemesh.rs │ ├── basepolygon.rs │ ├── geometry.md │ └── triangle.rs │ ├── lib.rs │ ├── operations │ ├── degenerate-test.rs │ ├── extrude.rs │ ├── graph.rs │ ├── triangulate.rs │ └── windingsort.rs │ ├── primitives │ ├── arc.rs │ ├── circle.rs │ ├── cubiod.rs │ ├── curve.rs │ ├── cylinder.rs │ ├── poly_line.rs │ ├── polygon.rs │ ├── rectangle.rs │ ├── simple_line.rs │ └── sphere.rs │ └── utility │ └── openmath.rs ├── package-lock.json ├── package.json ├── rollup.config.js ├── test └── index.html ├── tsconfig.json └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ 22 | 23 | # Web 24 | node_modules/ 25 | 26 | dist 27 | .DS_Store 28 | .vscode/ 29 | settings.json -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./main/opengeometry/Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🚧 Under Development 🚧 2 | 3 | **This project is currently a work in progress!** 4 | Expect frequent updates, breaking changes, and exciting new features. 5 | 6 | # OpenGeometry 7 | CAD Kernel for Web, built from scratch! 8 | 9 | Documentation is available at [OpenGeometry Documentation](https://docs.opengeometry.io) 10 | 11 | #### Basic Example 12 | - A basic example is available at [Quick Start](https://github.com/OpenGeometry-io/quickstart-js) 13 | 14 | #### Advanced and Additional Examples 15 | - Extensive Examples(Source Code) have a separate repo - https://github.com/OpenGeometry-io/OpenGeometry-examples 16 | - The live demo is available at [Kernel Examples](https://demos.opengeometry.io/src/kernel/index.html) -------------------------------------------------------------------------------- /main/opengeometry-babylon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opengeometry/kernel-webgl", 3 | "version": "1.0.0", 4 | "description": "OpenGeometry Kernel for Babylon.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "opengeometry", 11 | "three", 12 | "geometry" 13 | ], 14 | "author": "Vishwajeet Mane", 15 | "license": "MIT", 16 | "dependencies": {} 17 | } -------------------------------------------------------------------------------- /main/opengeometry-three/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry-three/.DS_Store -------------------------------------------------------------------------------- /main/opengeometry-three/index.ts: -------------------------------------------------------------------------------- 1 | import init, { 2 | Vector3D, 3 | BasePolygon, 4 | BaseFlatMesh, 5 | CircleArc, 6 | OGSimpleLine, 7 | OGPolyLine, 8 | OGRectangle, 9 | OGPolygon, 10 | } from "../opengeometry/pkg/opengeometry"; 11 | import * as THREE from "three"; 12 | import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js"; 13 | import { getUUID } from "./src/utils/randomizer"; 14 | import { Pencil } from "./src/pencil"; 15 | import { SpotLabel } from "./src/markup/spotMarker"; 16 | import { OPEN_GEOMETRY_THREE_VERSION, OpenGeometryOptions } from "./src/base-types"; 17 | import { BaseCircle } from "./src/primitives/circle"; 18 | import { Rectangle } from "./src/primitives/rectangle"; 19 | 20 | export type OUTLINE_TYPE = "front" | "side" | "top"; 21 | 22 | export class OpenGeometry { 23 | static version = OPEN_GEOMETRY_THREE_VERSION; 24 | 25 | protected scene: THREE.Scene | undefined; 26 | protected container: HTMLElement | undefined; 27 | private _pencil: Pencil | undefined; 28 | private _labelRenderer: CSS2DRenderer | undefined; 29 | 30 | constructor(container:HTMLElement, threeScene: THREE.Scene, private camera: THREE.Camera) { 31 | this.scene = threeScene; 32 | this.container = container; 33 | } 34 | 35 | // Why Generic Types are used sometimes 36 | // verifyOptions(options: OpenGeometryOptions) { 37 | // for (const key in options) { 38 | // if (options[key as keyof OpenGeometryOptions] === undefined) { 39 | // throw new Error(`Missing required option: ${key}`); 40 | // } 41 | // } 42 | // } 43 | 44 | static async create(options: OpenGeometryOptions) { 45 | const { container, scene, camera } = options; 46 | if (!container || !scene || !camera) { 47 | throw new Error("Missing required options"); 48 | } 49 | const openGeometry = new OpenGeometry(container, scene, camera); 50 | await openGeometry.setup(options.wasmURL); 51 | return openGeometry; 52 | } 53 | 54 | private async setup(wasmURL: string) { 55 | await init(wasmURL); 56 | this.setuplabelRenderer(); 57 | if (!this.container || !this.scene) return; 58 | this._pencil = new Pencil(this.container, this.scene, this.camera); 59 | this.setupEvent(); 60 | } 61 | 62 | get pencil() { 63 | return this._pencil; 64 | } 65 | 66 | get labelRenderer() { 67 | return this._labelRenderer; 68 | } 69 | 70 | setuplabelRenderer() { 71 | if (!this.container || !this.scene) { 72 | throw new Error("Container or Scene is not defined"); 73 | } 74 | 75 | const labelRenderer = new CSS2DRenderer(); 76 | labelRenderer.setSize(this.container.clientWidth, this.container.clientHeight); 77 | labelRenderer.domElement.style.position = "absolute"; 78 | labelRenderer.domElement.style.top = "0"; 79 | this.container.appendChild(labelRenderer.domElement); 80 | this._labelRenderer = labelRenderer; 81 | } 82 | 83 | setupEvent() { 84 | window.addEventListener("resize", () => { 85 | if (!this.container) return; 86 | this._labelRenderer?.setSize(this.container?.clientWidth, this.container?.clientHeight); 87 | }); 88 | } 89 | 90 | update(scene: THREE.Scene, camera: THREE.Camera) { 91 | this._labelRenderer?.render(scene, camera); 92 | } 93 | } 94 | 95 | export class BasePoly extends THREE.Mesh { 96 | ogid: string; 97 | layerVertices: Vector3D[] = []; 98 | layerBackVertices: Vector3D[] = []; 99 | 100 | polygon: BasePolygon | null = null; 101 | isTriangulated: boolean = false; 102 | 103 | constructor(vertices?: Vector3D[]) { 104 | super(); 105 | this.ogid = getUUID(); 106 | this.polygon = new BasePolygon(this.ogid); 107 | 108 | if (vertices) { 109 | this.polygon.add_vertices(vertices); 110 | 111 | // Triangulate the polygon 112 | this.polygon?.triangulate(); 113 | 114 | const bufFlush = this.polygon?.get_buffer_flush(); 115 | this.addFlushBufferToScene(bufFlush); 116 | } 117 | } 118 | 119 | addVertices(vertices: Vector3D[]) { 120 | if (!this.polygon) return; 121 | this.polygon.add_vertices(vertices); 122 | this.polygon?.triangulate(); 123 | const bufFlush = this.polygon?.get_buffer_flush(); 124 | this.addFlushBufferToScene(bufFlush); 125 | } 126 | 127 | resetVertices() { 128 | if (!this.polygon) return; 129 | this.layerVertices = []; 130 | this.geometry.dispose(); 131 | this.polygon?.reset_polygon(); 132 | this.isTriangulated = false; 133 | } 134 | 135 | addVertex(threeVertex: Vector3D) { 136 | if (this.isTriangulated) { 137 | this.layerVertices = []; 138 | this.geometry.dispose(); 139 | this.polygon?.reset_polygon(); 140 | this.isTriangulated = false; 141 | 142 | for (const vertex of this.layerBackVertices) { 143 | this.layerVertices.push(vertex.clone()); 144 | } 145 | 146 | }; 147 | 148 | const backupVertex = new Vector3D( 149 | parseFloat(threeVertex.x.toFixed(2)), 150 | 0, 151 | parseFloat(threeVertex.z.toFixed(2)) 152 | ); 153 | this.layerBackVertices.push(backupVertex); 154 | 155 | const vertex = new Vector3D( 156 | parseFloat(threeVertex.x.toFixed(2)), 157 | // when doing the parse operation getting -0 instead of 0 158 | 0, 159 | parseFloat(threeVertex.z.toFixed(2)) 160 | ); 161 | this.layerVertices.push(vertex); 162 | 163 | if (this.layerVertices.length > 3) { 164 | this.polygon?.add_vertices(this.layerVertices); 165 | const bufFlush = this.polygon?.triangulate(); 166 | 167 | if (!bufFlush) { 168 | return; 169 | } 170 | this.addFlushBufferToScene(bufFlush); 171 | 172 | this.isTriangulated = true; 173 | } 174 | } 175 | 176 | addHole(holeVertices: Vector3D[]) { 177 | if (!this.polygon) return; 178 | this.polygon.add_holes(holeVertices); 179 | const triResult = JSON.parse(this.polygon.new_triangulate()); 180 | const newBufferFlush = triResult.new_buffer; 181 | const geometry = new THREE.BufferGeometry(); 182 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(newBufferFlush), 3)); 183 | this.geometry = geometry; 184 | 185 | // const bufFlush = this.polygon.get_buffer_flush(); 186 | // this.addFlushBufferToScene(bufFlush); 187 | } 188 | 189 | addFlushBufferToScene(flush: string) { 190 | const flushBuffer = JSON.parse(flush); 191 | const geometry = new THREE.BufferGeometry(); 192 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 193 | // geometry.computeVertexNormals(); 194 | 195 | const material = new THREE.MeshStandardMaterial({ 196 | color: 0x00ff00, 197 | // side: THREE.DoubleSide, 198 | transparent: true, 199 | opacity: 0.5, 200 | // wireframe: true 201 | }); 202 | 203 | this.geometry = geometry; 204 | this.material = material; 205 | } 206 | 207 | extrude(height: number) { 208 | if (!this.polygon) return; 209 | const extruded_buff = this.polygon.extrude_by_height(height); 210 | this.generateExtrudedGeometry(extruded_buff); 211 | } 212 | 213 | generateExtrudedGeometry(extruded_buff: string) { 214 | // THIS WORKS 215 | const flushBuffer = JSON.parse(extruded_buff); 216 | console.log(flushBuffer); 217 | 218 | // const geometry = new THREE.BufferGeometry(); 219 | // geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 220 | // // geometry.computeVertexNormals(); 221 | 222 | // // const material = new THREE.MeshPhongMaterial({ 223 | // // color: 0x3a86ff, 224 | // // }); 225 | // // material.side = THREE.DoubleSide; 226 | 227 | // this.geometry = geometry; 228 | // this.material = material; 229 | } 230 | } 231 | 232 | interface IBaseCircleOptions { 233 | radius: number; 234 | segments: number; 235 | position: Vector3D; 236 | startAngle: number; 237 | endAngle: number; 238 | } 239 | 240 | export class CirclePoly extends THREE.Mesh { 241 | ogid: string; 242 | polygon: OGPolygon | null = null; 243 | baseCircle: BaseCircle; 244 | isExtruded: boolean = false; 245 | 246 | constructor(baseCircle: BaseCircle) { 247 | super(); 248 | this.ogid = getUUID(); 249 | 250 | if (!baseCircle.circleArc) { 251 | throw new Error("CircleArc is not defined"); 252 | } 253 | // baseCircle.nodeChild = this; 254 | baseCircle.nodeOperation = "polygon"; 255 | this.baseCircle = baseCircle; 256 | 257 | this.generateGeometry(); 258 | this.addFlushBufferToScene(); 259 | } 260 | 261 | update() { 262 | this.geometry.dispose(); 263 | 264 | this.polygon?.clear_vertices(); 265 | this.polygon?.add_vertices(this.baseCircle.circleArc.get_raw_points()); 266 | 267 | this.generateGeometry(); 268 | this.addFlushBufferToScene(); 269 | } 270 | 271 | generateGeometry() { 272 | if (!this.baseCircle.circleArc) return; 273 | this.polygon = OGPolygon.new_with_circle(this.baseCircle.circleArc.clone()); 274 | } 275 | 276 | addFlushBufferToScene() { 277 | if (!this.polygon) return; 278 | const bufFlush = this.polygon.get_buffer_flush(); 279 | const flushBuffer = JSON.parse(bufFlush); 280 | const geometry = new THREE.BufferGeometry(); 281 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 282 | 283 | // TODO: Do this using a set method, poly.visualizeTriangles = true 284 | // different colors for each triangle in the polygon dont interolate 285 | // const colors = new Float32Array(flushBuffer.length); 286 | // for (let i = 0; i < colors.length; i += 9) { 287 | // const r = Math.random(); 288 | // const g = Math.random(); 289 | // const b = Math.random(); 290 | // colors[i] = r; 291 | // colors[i + 1] = g; 292 | // colors[i + 2] = b; 293 | // colors[i + 3] = r; 294 | // colors[i + 4] = g; 295 | // colors[i + 5] = b; 296 | // colors[i + 6] = r; 297 | // colors[i + 7] = g; 298 | // colors[i + 8] = b; 299 | // } 300 | 301 | // geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); 302 | 303 | const material = new THREE.MeshStandardMaterial({ 304 | color: 0x00ff00, 305 | // side: THREE.DoubleSide, 306 | transparent: true, 307 | opacity: 0.5, 308 | // wireframe: true 309 | }); 310 | 311 | this.geometry = geometry; 312 | this.material = material; 313 | } 314 | 315 | clearGeometry() { 316 | this.geometry.dispose(); 317 | } 318 | 319 | extrude(height: number) { 320 | if (!this.polygon) return; 321 | const extruded_buff = this.polygon.extrude_by_height(height); 322 | console.log(JSON.parse(extruded_buff)); 323 | this.isExtruded = true; 324 | 325 | this.generateExtrudedGeometry(extruded_buff); 326 | } 327 | 328 | getBrepData() { 329 | if (!this.polygon) return; 330 | const brepData = this.polygon.get_brep_data(); 331 | const parsedData = JSON.parse(brepData); 332 | console.log(parsedData); 333 | } 334 | 335 | getOutline(type: OUTLINE_TYPE) { 336 | if (!this.polygon) return; 337 | const outlines = this.polygon.get_outlines(); 338 | const outlineBuffer = JSON.parse(outlines); 339 | 340 | // TODO: move this logic to Kernel 341 | const faces = []; 342 | for (const data of outlineBuffer) { 343 | const vertices = []; 344 | for (const vertex of data) { 345 | const x_float = type === "side" ? 0 : parseFloat(vertex.x.toFixed(5)); 346 | const y_float = type === "top" ? 0 : parseFloat(vertex.y.toFixed(5)); 347 | const z_float = type === "front" ? 0 : parseFloat(vertex.z.toFixed(5)); 348 | vertices.push(new THREE.Vector3(x_float, y_float, z_float)); 349 | } 350 | faces.push(vertices); 351 | } 352 | 353 | const clonedFaces = faces.map((face) => { 354 | return face.map((vertex) => { 355 | return new THREE.Vector3(vertex.x, vertex.y, vertex.z); 356 | }); 357 | } 358 | ); 359 | 360 | // remove duplicates inside the faces 361 | const uniqueFaces = clonedFaces.map((face) => { 362 | return face.filter((vertex, index, self) => 363 | index === self.findIndex((v) => ( 364 | v.x === vertex.x && v.y === vertex.y && v.z === vertex.z 365 | )) 366 | ); 367 | }); 368 | 369 | // Picking unique vertices from all faces 370 | const uniqueVertices = []; 371 | const vertexSet = new Set(); 372 | for (const face of uniqueFaces) { 373 | for (const vertex of face) { 374 | const key = `${vertex.x},${vertex.y},${vertex.z}`; 375 | if (!vertexSet.has(key)) { 376 | vertexSet.add(key); 377 | uniqueVertices.push(vertex); 378 | } 379 | } 380 | } 381 | 382 | // arrange the vertices in a clockwise manner 383 | const center = new THREE.Vector3(); 384 | for (const vertex of uniqueVertices) { 385 | center.add(vertex); 386 | } 387 | center.divideScalar(uniqueVertices.length); 388 | uniqueVertices.sort((a, b) => { 389 | if (type === "side") { 390 | const angleA = Math.atan2(a.y - center.y, a.z - center.z); 391 | const angleB = Math.atan2(b.y - center.y, b.z - center.z); 392 | return angleA - angleB; 393 | } else if (type === "top") { 394 | const angleA = Math.atan2(a.x - center.x, a.z - center.z); 395 | const angleB = Math.atan2(b.x - center.x, b.z - center.z); 396 | return angleA - angleB; 397 | } 398 | const angleA = Math.atan2(a.x - center.x, a.y - center.y); 399 | const angleB = Math.atan2(b.x - center.x, b.y - center.y); 400 | return angleA - angleB; 401 | } 402 | ); 403 | 404 | // merge collinear vertices 405 | const mergedVertices = []; 406 | for (let i = 0; i < uniqueVertices.length; i++) { 407 | const current = uniqueVertices[i]; 408 | const next = uniqueVertices[(i + 1) % uniqueVertices.length]; 409 | const prev = uniqueVertices[(i - 1 + uniqueVertices.length) % uniqueVertices.length]; 410 | 411 | const v1 = new THREE.Vector3().subVectors(current, prev); 412 | const v2 = new THREE.Vector3().subVectors(next, current); 413 | 414 | if (v1.angleTo(v2) > 0.01) { 415 | mergedVertices.push(current); 416 | } 417 | } 418 | 419 | mergedVertices.push(mergedVertices[0]); 420 | // TODO: move logic until here to Kernel 421 | 422 | // Create a new geometry with the merged vertices 423 | const mergedGeometry = new THREE.BufferGeometry().setFromPoints(mergedVertices); 424 | const mergedMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide }); 425 | const mergedMesh = new THREE.Line(mergedGeometry, mergedMaterial); 426 | return mergedMesh; 427 | } 428 | 429 | generateExtrudedGeometry(extruded_buff: string) { 430 | // THIS WORKS 431 | const flushBuffer = JSON.parse(extruded_buff); 432 | const geometry = new THREE.BufferGeometry(); 433 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 434 | 435 | // To Test If Triangulation is working 436 | // const colors = new Float32Array(flushBuffer.length); 437 | // for (let i = 0; i < colors.length; i += 9) { 438 | // const r = Math.random(); 439 | // const g = Math.random(); 440 | // const b = Math.random(); 441 | // colors[i] = r; 442 | // colors[i + 1] = g; 443 | // colors[i + 2] = b; 444 | // colors[i + 3] = r; 445 | // colors[i + 4] = g; 446 | // colors[i + 5] = b; 447 | // colors[i + 6] = r; 448 | // colors[i + 7] = g; 449 | // colors[i + 8] = b; 450 | // } 451 | 452 | // geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); 453 | 454 | // const material = new THREE.MeshPhongMaterial( { 455 | // color: 0xffffff, 456 | // flatShading: true, 457 | // vertexColors: true, 458 | // shininess: 0, 459 | // side: THREE.DoubleSide 460 | // }); 461 | 462 | // const material = new THREE.MeshPhongMaterial({ 463 | // color: 0x3a86ff, 464 | // }); 465 | // material.side = THREE.DoubleSide; 466 | 467 | this.geometry = geometry; 468 | // this.material = material; 469 | } 470 | } 471 | 472 | export class RectanglePoly extends THREE.Mesh { 473 | ogid: string; 474 | polygon: BasePolygon | null = null; 475 | baseRectangle: Rectangle; 476 | isExtruded: boolean = false; 477 | constructor(baseRectangle: Rectangle) { 478 | super(); 479 | this.ogid = getUUID(); 480 | 481 | if (!baseRectangle.polyLineRectangle) { 482 | throw new Error("BaseRectangle is not defined"); 483 | } 484 | // baseRectangle.nodeChild = this; 485 | baseRectangle.nodeOperation = "polygon"; 486 | this.baseRectangle = baseRectangle; 487 | 488 | this.generateGeometry(); 489 | this.addFlushBufferToScene(); 490 | } 491 | 492 | update() { 493 | this.geometry.dispose(); 494 | this.polygon?.clear_vertices(); 495 | this.polygon?.add_vertices(this.baseRectangle.polyLineRectangle.get_raw_points()); 496 | this.generateGeometry(); 497 | this.addFlushBufferToScene(); 498 | } 499 | generateGeometry() { 500 | if (!this.baseRectangle.polyLineRectangle) return; 501 | this.polygon = BasePolygon.new_with_rectangle(this.baseRectangle.polyLineRectangle.clone()); 502 | } 503 | 504 | addFlushBufferToScene() { 505 | if (!this.polygon) return; 506 | const bufFlush = this.polygon.get_buffer_flush(); 507 | const flushBuffer = JSON.parse(bufFlush); 508 | const geometry = new THREE.BufferGeometry(); 509 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 510 | const material = new THREE.MeshStandardMaterial({ color: 0x3a86ff, transparent: true, opacity: 0.5 }); 511 | this.geometry = geometry; 512 | this.material = material; 513 | } 514 | 515 | clearGeometry() { 516 | this.geometry.dispose(); 517 | } 518 | 519 | extrude(height: number) { 520 | if (!this.polygon) return; 521 | const extruded_buff = this.polygon.extrude_by_height(height); 522 | this.isExtruded = true; 523 | this.generateExtrudedGeometry(extruded_buff); 524 | } 525 | 526 | generateExtrudedGeometry(extruded_buff: string) { 527 | // THIS WORKS 528 | const flushBuffer = JSON.parse(extruded_buff); 529 | const geometry = new THREE.BufferGeometry(); 530 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 531 | geometry.computeVertexNormals(); 532 | 533 | // const colors = new Float32Array(flushBuffer.length); 534 | // for (let i = 0; i < colors.length; i += 9) { 535 | // const r = Math.random(); 536 | // const g = Math.random(); 537 | // const b = Math.random(); 538 | // colors[i] = r; 539 | // colors[i + 1] = g; 540 | // colors[i + 2] = b; 541 | // colors[i + 3] = r; 542 | // colors[i + 4] = g; 543 | // colors[i + 5] = b; 544 | // colors[i + 6] = r; 545 | // colors[i + 7] = g; 546 | // colors[i + 8] = b; 547 | // } 548 | // geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); 549 | // const material = new THREE.MeshPhongMaterial( { 550 | // color: 0xffffff, 551 | // flatShading: true, 552 | // vertexColors: true, 553 | // shininess: 0, 554 | // side: THREE.DoubleSide 555 | // }); 556 | 557 | const material = new THREE.MeshPhongMaterial({ 558 | color: 0x3a86ff, 559 | }); 560 | material.side = THREE.DoubleSide; 561 | 562 | this.geometry = geometry; 563 | this.material = material; 564 | } 565 | 566 | getOutline(type: OUTLINE_TYPE) { 567 | if (!this.polygon) return; 568 | const outlines = this.polygon.get_outlines(); 569 | const outlineBuffer = JSON.parse(outlines); 570 | 571 | // TODO: move this logic to Kernel 572 | const faces = []; 573 | for (const data of outlineBuffer) { 574 | const vertices = []; 575 | for (const vertex of data) { 576 | const x_float = type === "side" ? 0 : parseFloat(vertex.x.toFixed(5)); 577 | const y_float = type === "top" ? 0 : parseFloat(vertex.y.toFixed(5)); 578 | const z_float = type === "front" ? 0 : parseFloat(vertex.z.toFixed(5)); 579 | vertices.push(new THREE.Vector3(x_float, y_float, z_float)); 580 | } 581 | faces.push(vertices); 582 | } 583 | 584 | const clonedFaces = faces.map((face) => { 585 | return face.map((vertex) => { 586 | return new THREE.Vector3(vertex.x, vertex.y, vertex.z); 587 | }); 588 | } 589 | ); 590 | 591 | // remove duplicates inside the faces 592 | const uniqueFaces = clonedFaces.map((face) => { 593 | return face.filter((vertex, index, self) => 594 | index === self.findIndex((v) => ( 595 | v.x === vertex.x && v.y === vertex.y && v.z === vertex.z 596 | )) 597 | ); 598 | }); 599 | 600 | // Picking unique vertices from all faces 601 | const uniqueVertices = []; 602 | const vertexSet = new Set(); 603 | for (const face of uniqueFaces) { 604 | for (const vertex of face) { 605 | const key = `${vertex.x},${vertex.y},${vertex.z}`; 606 | if (!vertexSet.has(key)) { 607 | vertexSet.add(key); 608 | uniqueVertices.push(vertex); 609 | } 610 | } 611 | } 612 | 613 | // arrange the vertices in a clockwise manner 614 | const center = new THREE.Vector3(); 615 | for (const vertex of uniqueVertices) { 616 | center.add(vertex); 617 | } 618 | center.divideScalar(uniqueVertices.length); 619 | uniqueVertices.sort((a, b) => { 620 | if (type === "side") { 621 | const angleA = Math.atan2(a.y - center.y, a.z - center.z); 622 | const angleB = Math.atan2(b.y - center.y, b.z - center.z); 623 | return angleA - angleB; 624 | } else if (type === "top") { 625 | const angleA = Math.atan2(a.x - center.x, a.z - center.z); 626 | const angleB = Math.atan2(b.x - center.x, b.z - center.z); 627 | return angleA - angleB; 628 | } 629 | const angleA = Math.atan2(a.x - center.x, a.y - center.y); 630 | const angleB = Math.atan2(b.x - center.x, b.y - center.y); 631 | return angleA - angleB; 632 | } 633 | ); 634 | 635 | // merge collinear vertices 636 | const mergedVertices = []; 637 | for (let i = 0; i < uniqueVertices.length; i++) { 638 | const current = uniqueVertices[i]; 639 | const next = uniqueVertices[(i + 1) % uniqueVertices.length]; 640 | const prev = uniqueVertices[(i - 1 + uniqueVertices.length) % uniqueVertices.length]; 641 | 642 | const v1 = new THREE.Vector3().subVectors(current, prev); 643 | const v2 = new THREE.Vector3().subVectors(next, current); 644 | 645 | if (v1.angleTo(v2) > 0.01) { 646 | mergedVertices.push(current); 647 | } 648 | } 649 | 650 | mergedVertices.push(mergedVertices[0]); 651 | // TODO: move logic until here to Kernel 652 | 653 | // Create a new geometry with the merged vertices 654 | const mergedGeometry = new THREE.BufferGeometry().setFromPoints(mergedVertices); 655 | const mergedMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide }); 656 | const mergedMesh = new THREE.Line(mergedGeometry, mergedMaterial); 657 | return mergedMesh; 658 | } 659 | } 660 | 661 | /** 662 | * Base Flat Mesh 663 | */ 664 | export class FlatMesh extends THREE.Mesh { 665 | constructor(vertices: Vector3D[]) { 666 | super(); 667 | const baseMesh = new BaseFlatMesh(getUUID()); 668 | baseMesh.add_vertices(vertices); 669 | const bufFlush = baseMesh.triangulate(); 670 | const flushBuffer = JSON.parse(bufFlush); 671 | const geometry = new THREE.BufferGeometry(); 672 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 673 | const material = new THREE.MeshStandardMaterial({ color: 0xff0000, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); 674 | this.geometry = geometry; 675 | this.material = material; 676 | } 677 | } 678 | 679 | 680 | export { 681 | Vector3D, 682 | SpotLabel, 683 | } 684 | 685 | export * from './src/primitives/'; 686 | export * from './src/shapes/'; 687 | -------------------------------------------------------------------------------- /main/opengeometry-three/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opengeometry/kernel-three", 3 | "version": "1.0.0", 4 | "description": "OpenGeometry Kernel for Three.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "opengeometry", 11 | "three", 12 | "geometry" 13 | ], 14 | "author": "Vishwajeet Mane", 15 | "license": "MIT", 16 | "dependencies": {} 17 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/base-types.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | import { OGSimpleLine, Vector3D } from "./../../opengeometry/pkg/opengeometry"; 3 | 4 | export const OPEN_GEOMETRY_THREE_VERSION = '0.0.1'; 5 | 6 | export interface OpenGeometryOptions { 7 | container: HTMLElement; 8 | scene: THREE.Scene; 9 | camera: THREE.Camera; 10 | wasmURL: string; 11 | } 12 | 13 | export interface IBaseCircleOptions { 14 | radius: number; 15 | segments: number; 16 | position: Vector3D; 17 | startAngle: number; 18 | endAngle: number; 19 | } 20 | 21 | export type RectangeOptions = { 22 | width: number; 23 | breadth: number; 24 | center: Vector3D 25 | } 26 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/markup/baseMarker.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry-three/src/markup/baseMarker.ts -------------------------------------------------------------------------------- /main/opengeometry-three/src/markup/spotMarker.ts: -------------------------------------------------------------------------------- 1 | import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js"; 2 | 3 | function spotLabelElement() { 4 | const spotLabelElement = document.createElement("div"); 5 | spotLabelElement.style.position = "absolute"; 6 | spotLabelElement.style.width = "3px"; 7 | spotLabelElement.style.height = "3px"; 8 | spotLabelElement.style.backgroundColor = "blue"; 9 | spotLabelElement.style.pointerEvents = "none"; 10 | // spotLabelElement.style.borderRadius = "50%"; 11 | spotLabelElement.style.border = "1px solid black"; 12 | return spotLabelElement; 13 | } 14 | 15 | export class SpotLabel extends CSS2DObject { 16 | constructor() { 17 | const spotLabel = spotLabelElement(); 18 | super(spotLabel); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/pencil.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js"; 3 | import { Event } from "./utils/event"; 4 | 5 | /** 6 | * Whenever you want something to work with pencil you should add it to pencil object 7 | */ 8 | 9 | export type PencilMode = "draw" | "erase" | "select" | "cursor" | "view"; 10 | 11 | export class Pencil { 12 | private container: HTMLElement; 13 | private scene: THREE.Scene; 14 | private raycaster: THREE.Raycaster = new THREE.Raycaster(); 15 | pencilMeshes: THREE.Mesh[] = []; 16 | 17 | cursor: CSS2DObject | undefined; 18 | onCursorDown: Event = new Event(); 19 | onCursorMove: Event = new Event(); 20 | 21 | onElementHover: Event = new Event(); 22 | onElementSelected: Event = new Event(); 23 | 24 | pencilMode: PencilMode = "cursor"; 25 | 26 | // dummy plane can be ignored when we have at least one object in the scene 27 | private dummyPlane: THREE.Mesh | undefined; 28 | 29 | constructor(container: HTMLElement, scene: THREE.Scene, private camera: THREE.Camera) { 30 | this.container = container; 31 | this.scene = scene; 32 | this.setup(); 33 | } 34 | 35 | set mode(mode: PencilMode) { 36 | this.pencilMode = mode; 37 | 38 | if (mode === "select" || mode === "view") { 39 | this.container.style.cursor = "default"; 40 | } else { 41 | this.container.style.cursor = "none"; 42 | } 43 | } 44 | 45 | get drawingCanvas() { 46 | return this.dummyPlane; 47 | } 48 | 49 | setup() { 50 | this.setupCursor(); 51 | this.setupCursorEvent(); 52 | 53 | // Raycaster 54 | this.raycaster.params.Line.threshold = 0.1 55 | 56 | // A Dummy Ground Plane 57 | const geometry = new THREE.PlaneGeometry(100, 100); 58 | const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide, transparent: true, opacity: 0 }); 59 | const plane = new THREE.Mesh(geometry, material); 60 | plane.name = "pencil-ground"; 61 | plane.rotation.x = Math.PI / 2; 62 | this.scene.add(plane); 63 | plane.visible = false; 64 | this.dummyPlane = plane; 65 | } 66 | 67 | groundVisible(visible: boolean) { 68 | if (this.dummyPlane) { 69 | this.dummyPlane.visible = visible; 70 | } 71 | } 72 | 73 | setupCursor() { 74 | const cursorElement = document.createElement("div"); 75 | cursorElement.style.position = "absolute"; 76 | cursorElement.style.width = "1px"; 77 | cursorElement.style.height = "60px"; 78 | cursorElement.style.backgroundColor = "red"; 79 | cursorElement.style.pointerEvents = "none"; 80 | 81 | const horizontalLine = document.createElement("div"); 82 | horizontalLine.style.position = "absolute"; 83 | horizontalLine.style.width = "60px"; 84 | horizontalLine.style.height = "1px"; 85 | horizontalLine.style.left = "-30px"; 86 | horizontalLine.style.top = "30px"; 87 | horizontalLine.style.backgroundColor = "red"; 88 | horizontalLine.style.pointerEvents = "none"; 89 | cursorElement.appendChild(horizontalLine); 90 | 91 | this.container.style.cursor = "none"; 92 | 93 | const cursorMesh = new CSS2DObject(cursorElement); 94 | cursorMesh.name = "cursor"; 95 | cursorMesh.position.set(0, 0, 0); 96 | this.scene.add(cursorMesh); 97 | this.cursor = cursorMesh; 98 | } 99 | 100 | setupCursorEvent() { 101 | this.container.addEventListener("mousemove", (event) => { 102 | const rect = this.container.getBoundingClientRect(); 103 | const x = ((event.clientX - rect.left) / rect.width) * 2 - 1; 104 | const y = -((event.clientY - rect.top) / rect.height) * 2 + 1; 105 | 106 | this.raycaster.setFromCamera(new THREE.Vector2(x, y), this.camera); 107 | 108 | if (this.pencilMode === "cursor") { 109 | const intersects = this.raycaster.intersectObjects([this.dummyPlane!]); 110 | const intersect = intersects[0]; 111 | const point = intersect.point; 112 | this.cursor?.position.set(point.x, point.y, point.z); 113 | this.onCursorMove.trigger(point); 114 | } else if (this.pencilMode === "select") { 115 | const intersects = this.raycaster.intersectObjects(this.pencilMeshes); 116 | if (intersects.length === 0) { 117 | return; 118 | } 119 | const intersect = intersects[0]; 120 | this.onElementHover.trigger(intersect.object as THREE.Mesh); 121 | } 122 | }); 123 | 124 | this.container.addEventListener("mousedown", (event) => { 125 | const rect = this.container.getBoundingClientRect(); 126 | const x = ((event.clientX - rect.left) / rect.width) * 2 - 1; 127 | const y = -((event.clientY - rect.top) / rect.height) * 2 + 1; 128 | 129 | this.raycaster.setFromCamera(new THREE.Vector2(x, y), this.camera); 130 | 131 | if (this.pencilMode === "cursor") { 132 | const intersects = this.raycaster.intersectObjects([this.dummyPlane!]); 133 | const intersect = intersects[0]; 134 | const point = intersect.point; 135 | this.onCursorDown.trigger(point); 136 | } else if (this.pencilMode === "select") { 137 | const intersects = this.raycaster.intersectObjects(this.pencilMeshes); 138 | const intersect = intersects[0]; 139 | this.onElementSelected.trigger(intersect.object as THREE.Mesh); 140 | } 141 | }); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/primitives/circle.ts: -------------------------------------------------------------------------------- 1 | import { CircleArc } from "./../../../opengeometry/pkg/opengeometry"; 2 | import * as THREE from "three"; 3 | import { getUUID } from "../utils/randomizer"; 4 | import { IBaseCircleOptions } from "../base-types"; 5 | 6 | export class BaseCircle extends THREE.Line { 7 | ogid: string; 8 | circleArc: CircleArc; 9 | options: IBaseCircleOptions; 10 | // nodeChild: CirclePoly | null = null; 11 | nodeOperation: String = "none"; 12 | 13 | set color(color: number) { 14 | if (this.material instanceof THREE.LineBasicMaterial) { 15 | this.material.color.set(color); 16 | } 17 | } 18 | 19 | constructor(options: IBaseCircleOptions) { 20 | super(); 21 | this.ogid = getUUID(); 22 | this.options = options; 23 | this.circleArc = new CircleArc(this.ogid); 24 | 25 | this.setConfig(); 26 | this.generateGeometry(); 27 | } 28 | 29 | setConfig() { 30 | const { radius, segments, position, startAngle, endAngle } = this.options; 31 | this.circleArc.set_config( 32 | position, 33 | radius, 34 | startAngle, 35 | endAngle, 36 | segments 37 | ); 38 | } 39 | 40 | generateGeometry() { 41 | this.circleArc.generate_points(); 42 | const bufRaw = this.circleArc.get_points(); 43 | const bufFlush = JSON.parse(bufRaw); 44 | console.log(bufFlush); 45 | const line = new THREE.BufferGeometry().setFromPoints(bufFlush); 46 | const material = new THREE.LineBasicMaterial({ color: 0x000000 }); 47 | this.geometry = line; 48 | this.material = material; 49 | } 50 | 51 | discardGeoemtry() { 52 | this.geometry.dispose(); 53 | } 54 | 55 | set radius(radius: number) { 56 | this.options.radius = radius; 57 | this.circleArc.update_radius(radius); 58 | 59 | this.generateGeometry(); 60 | // if (this.nodeChild) { 61 | // this.nodeChild.update(); 62 | // } 63 | } 64 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/primitives/index.ts: -------------------------------------------------------------------------------- 1 | export * from './simple-line'; 2 | export * from './poly-line'; 3 | export * from './circle'; 4 | export * from './rectangle'; 5 | // export * from './simple-curve'; -------------------------------------------------------------------------------- /main/opengeometry-three/src/primitives/poly-line.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | import { OGPolyLine, Vector3D } from '../../../opengeometry/pkg/opengeometry'; 3 | import { getUUID } from "../utils/randomizer"; 4 | 5 | /** 6 | * PolyLine defined by multiple points 7 | */ 8 | export class PolyLine extends THREE.Line { 9 | ogid: string; 10 | points: Vector3D[] = []; 11 | isClosed: boolean = false; 12 | 13 | private polyline: OGPolyLine; 14 | 15 | set color(color: number) { 16 | if (this.material instanceof THREE.LineBasicMaterial) { 17 | this.material.color.set(color); 18 | } 19 | } 20 | 21 | constructor(points: Vector3D[] = []) { 22 | super(); 23 | this.ogid = getUUID(); 24 | this.points = points; 25 | this.polyline = new OGPolyLine(this.ogid); 26 | 27 | this.setConfig(points); 28 | this.generateGeometry(); 29 | } 30 | 31 | setConfig(points: Vector3D[]) { 32 | if (this.points.length < 2) return; 33 | this.polyline.set_config(points); 34 | } 35 | 36 | addPoint(point: Vector3D) { 37 | this.points.push(point); 38 | this.polyline.add_point(point); 39 | 40 | if (this.points.length < 2) return; 41 | this.generateGeometry(); 42 | } 43 | 44 | private clearGeometry() { 45 | this.geometry.dispose(); 46 | } 47 | 48 | private generateGeometry() { 49 | this.clearGeometry(); 50 | const buf = this.polyline.get_points(); 51 | const bufFlush = JSON.parse(buf); 52 | const line = new THREE.BufferGeometry().setFromPoints(bufFlush); 53 | const material = new THREE.LineBasicMaterial({ color: 0xff0000 }); 54 | this.geometry = line; 55 | this.material = material; 56 | 57 | this.isClosed = this.polyline.is_closed(); 58 | } 59 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/primitives/rectangle.ts: -------------------------------------------------------------------------------- 1 | import { OGSimpleLine, Vector3D, CircleArc, OGRectangle } from "./../../../opengeometry/pkg/opengeometry"; 2 | import * as THREE from "three"; 3 | import { getUUID } from "../utils/randomizer"; 4 | import { RectangeOptions } from "../base-types"; 5 | 6 | /** 7 | * Rectangle 8 | */ 9 | export class Rectangle extends THREE.Line { 10 | ogid: string; 11 | polyLineRectangle: OGRectangle; 12 | options: RectangeOptions; 13 | // nodeChild: RectanglePoly | null = null; 14 | nodeOperation: String = "none"; 15 | #color: number = 0x000000; 16 | 17 | set width(width: number) { 18 | this.options.width = width; 19 | this.polyLineRectangle.update_width(width); 20 | this.generateGeometry(); 21 | } 22 | 23 | set breadth(breadth: number) { 24 | this.options.breadth = breadth; 25 | this.polyLineRectangle.update_breadth(breadth); 26 | this.generateGeometry(); 27 | } 28 | 29 | set color(color: number) { 30 | if (this.material instanceof THREE.LineBasicMaterial) { 31 | this.material.color.set(color); 32 | this.#color = color; 33 | } 34 | } 35 | 36 | constructor(options: RectangeOptions) { 37 | super(); 38 | this.ogid = getUUID(); 39 | this.options = options; 40 | this.polyLineRectangle = new OGRectangle(this.ogid); 41 | 42 | this.setConfig(); 43 | this.generateGeometry(); 44 | } 45 | 46 | setConfig() { 47 | const { breadth, width, center } = this.options; 48 | this.polyLineRectangle.set_config( 49 | center, 50 | width, 51 | breadth 52 | ); 53 | } 54 | 55 | generateGeometry() { 56 | this.polyLineRectangle.generate_points(); 57 | const bufRaw = this.polyLineRectangle.get_points(); 58 | const bufFlush = JSON.parse(bufRaw); 59 | const line = new THREE.BufferGeometry().setFromPoints(bufFlush); 60 | const material = new THREE.LineBasicMaterial({ color: this.#color }); 61 | this.geometry = line; 62 | this.material = material; 63 | } 64 | 65 | discardGeometry() { 66 | this.geometry.dispose(); 67 | } 68 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/primitives/simple-line.ts: -------------------------------------------------------------------------------- 1 | import { OGSimpleLine, Vector3D } from "./../../../opengeometry/pkg/opengeometry"; 2 | import * as THREE from "three"; 3 | import { getUUID } from "../utils/randomizer"; 4 | 5 | /** 6 | * Simple Line defined by Two Points 7 | */ 8 | export class SimpleLine extends THREE.Line { 9 | ogid: string; 10 | points: Vector3D[] = []; 11 | 12 | set color(color: number) { 13 | if (this.material instanceof THREE.LineBasicMaterial) { 14 | this.material.color.set(color); 15 | } 16 | } 17 | 18 | constructor( 19 | start: Vector3D = new Vector3D(1, 0, 0), 20 | end: Vector3D = new Vector3D(-1, 0, 0) 21 | ) { 22 | super(); 23 | this.ogid = getUUID(); 24 | this.points.push(start); 25 | this.points.push(end); 26 | 27 | this.generateGeometry(); 28 | } 29 | 30 | addPoint(point: Vector3D) { 31 | this.points.push(point); 32 | if (this.points.length > 2) { 33 | throw new Error("Simple Line can only have two points, clear points or use PolyLine"); 34 | } 35 | 36 | if (this.points.length < 2) return; 37 | this.generateGeometry(); 38 | } 39 | 40 | private generateGeometry() { 41 | const ogLine = new OGSimpleLine(this.ogid); 42 | ogLine.set_config(this.points[0], this.points[1]); 43 | const buf = ogLine.get_points(); 44 | const bufFlush = JSON.parse(buf); 45 | const line = new THREE.BufferGeometry().setFromPoints(bufFlush); 46 | const material = new THREE.LineBasicMaterial({ color: 0xff0000 }); 47 | this.geometry = line; 48 | this.material = material; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/shapes/cylinder.ts: -------------------------------------------------------------------------------- 1 | import { OGCylinder, Vector3D } from "./../../../opengeometry/pkg/opengeometry"; 2 | import * as THREE from "three"; 3 | import { getUUID } from "../utils/randomizer"; 4 | 5 | interface ICylinderOptions { 6 | radius: number; 7 | height: number; 8 | segments: number; 9 | angle: number; 10 | center?: Vector3D; 11 | } 12 | 13 | export class Cylinder extends THREE.Mesh { 14 | ogid: string; 15 | options: ICylinderOptions; 16 | cylinder: OGCylinder; 17 | 18 | #outlineMesh: THREE.Line | null = null; 19 | 20 | // Store local center offset to align outlines 21 | // TODO: Can this be moved to Engine? It can increase performance | Needs to be used in other shapes too 22 | private _geometryCenterOffset = new THREE.Vector3(); 23 | 24 | constructor(options: ICylinderOptions) { 25 | super(); 26 | this.ogid = getUUID(); 27 | this.options = options; 28 | 29 | this.cylinder = new OGCylinder(this.ogid); 30 | this.setConfig(); 31 | this.generateGeometry(); 32 | } 33 | 34 | validateOptions() { 35 | if (!this.options) { 36 | throw new Error("Options are not defined for Cylinder"); 37 | } 38 | } 39 | 40 | setConfig() { 41 | this.validateOptions(); 42 | 43 | const { radius, height, segments, angle, center } = this.options; 44 | this.cylinder.set_config( 45 | center?.clone() || new Vector3D(0, 0, 0), 46 | radius, 47 | height, 48 | angle, 49 | segments 50 | ); 51 | } 52 | 53 | generateGeometry() { 54 | this.cylinder.generate_geometry(); 55 | const geometryData = this.cylinder.get_geometry(); 56 | const bufferData = JSON.parse(geometryData); 57 | console.log(bufferData); 58 | 59 | const geometry = new THREE.BufferGeometry(); 60 | geometry.setAttribute( 61 | "position", 62 | new THREE.Float32BufferAttribute(bufferData, 3) 63 | ); 64 | 65 | const material = new THREE.MeshStandardMaterial({ 66 | color: 0x00ff00, 67 | transparent: true, 68 | opacity: 0.6, 69 | }); 70 | 71 | geometry.computeVertexNormals(); 72 | geometry.computeBoundingBox(); 73 | 74 | const boundingBox = geometry.boundingBox; 75 | if (boundingBox) { 76 | boundingBox.getCenter(this._geometryCenterOffset); 77 | geometry.translate( 78 | -this._geometryCenterOffset.x, 79 | -this._geometryCenterOffset.y, 80 | -this._geometryCenterOffset.z 81 | ); 82 | } 83 | 84 | this.geometry = geometry; 85 | this.material = material; 86 | 87 | this.position.set( 88 | this.options.center?.x || 0, 89 | this.options.center?.y || 0, 90 | this.options.center?.z || 0 91 | ); 92 | } 93 | 94 | set outline(enable: boolean) { 95 | if (enable && !this.#outlineMesh) { 96 | const outline_buff = this.cylinder.outline_edges(); 97 | const outline_buf = JSON.parse(outline_buff); 98 | 99 | const outlineGeometry = new THREE.BufferGeometry(); 100 | outlineGeometry.setAttribute( 101 | "position", 102 | new THREE.Float32BufferAttribute(outline_buf, 3) 103 | ); 104 | 105 | outlineGeometry.translate( 106 | -this._geometryCenterOffset.x, 107 | -this._geometryCenterOffset.y, 108 | -this._geometryCenterOffset.z 109 | ); 110 | 111 | const outlineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 }); 112 | this.#outlineMesh = new THREE.LineSegments( 113 | outlineGeometry, 114 | outlineMaterial 115 | ); 116 | 117 | this.add(this.#outlineMesh); 118 | } 119 | 120 | if (!enable && this.#outlineMesh) { 121 | this.remove(this.#outlineMesh); 122 | this.#outlineMesh.geometry.dispose(); 123 | this.#outlineMesh = null; 124 | } 125 | } 126 | 127 | getBrepData() { 128 | const brepData = this.cylinder.get_brep_dump(); 129 | const brepDataParsed = JSON.parse(brepData); 130 | console.log(brepDataParsed); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/shapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cylinder'; 2 | export * from './polygon'; -------------------------------------------------------------------------------- /main/opengeometry-three/src/shapes/polygon.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import { OGPolygon, Vector3D } from "../../../opengeometry/pkg/opengeometry"; 3 | import { getUUID } from "../utils/randomizer"; 4 | 5 | export class Polygon extends THREE.Mesh { 6 | ogid: string; 7 | layerVertices: Vector3D[] = []; 8 | layerBackVertices: Vector3D[] = []; 9 | 10 | polygon: OGPolygon | null = null; 11 | isTriangulated: boolean = false; 12 | 13 | #outlineMesh: THREE.Line | null = null; 14 | private _geometryCenterOffset = new THREE.Vector3(); 15 | 16 | constructor(vertices?: Vector3D[]) { 17 | super(); 18 | this.ogid = getUUID(); 19 | this.polygon = new OGPolygon(this.ogid); 20 | 21 | if (vertices) { 22 | this.polygon.add_vertices(vertices); 23 | 24 | // Triangulate the polygon - WORKS 25 | this.polygon?.triangulate(); 26 | const bufFlush = this.polygon?.get_buffer_flush(); 27 | this.addFlushBufferToScene(bufFlush); 28 | 29 | // Testing New Triangulation - FAILING 30 | // const triResult = JSON.parse(this.polygon.new_triangulate()); 31 | // console.log(triResult); 32 | } 33 | 34 | // THIS MIGHT HELP WITH SHARING THE POSITION with KERNEL when something is changed 35 | // const originalSet = this.position.set.bind(this.position); 36 | // this.position.set = (x: number, y: number, z: number) => { 37 | // console.log(`Position set to (${x}, ${y}, ${z})`); 38 | // // your custom logic here (e.g., notify OpenGeometry) 39 | // return originalSet(x, y, z); 40 | // }; 41 | 42 | // // Optional: Override copy if you're using .copy() too 43 | // const originalCopy = this.position.copy.bind(this.position); 44 | // this.position.copy = (v: THREE.Vector3) => { 45 | // console.log(`Position copied from ${v.x}, ${v.y}, ${v.z}`); 46 | // return originalCopy(v); 47 | // }; 48 | } 49 | 50 | addVertices(vertices: Vector3D[]) { 51 | if (!this.polygon) return; 52 | this.polygon.add_vertices(vertices); 53 | this.polygon?.triangulate(); 54 | const bufFlush = this.polygon?.get_buffer_flush(); 55 | this.addFlushBufferToScene(bufFlush); 56 | } 57 | 58 | resetVertices() { 59 | if (!this.polygon) return; 60 | this.layerVertices = []; 61 | this.geometry.dispose(); 62 | this.polygon?.reset_polygon(); 63 | this.isTriangulated = false; 64 | } 65 | 66 | addVertex(threeVertex: Vector3D) { 67 | if (this.isTriangulated) { 68 | this.layerVertices = []; 69 | this.geometry.dispose(); 70 | this.polygon?.reset_polygon(); 71 | this.isTriangulated = false; 72 | 73 | for (const vertex of this.layerBackVertices) { 74 | this.layerVertices.push(vertex.clone()); 75 | } 76 | 77 | }; 78 | 79 | const backupVertex = new Vector3D( 80 | parseFloat(threeVertex.x.toFixed(2)), 81 | 0, 82 | parseFloat(threeVertex.z.toFixed(2)) 83 | ); 84 | this.layerBackVertices.push(backupVertex); 85 | 86 | const vertex = new Vector3D( 87 | parseFloat(threeVertex.x.toFixed(2)), 88 | // when doing the parse operation getting -0 instead of 0 89 | 0, 90 | parseFloat(threeVertex.z.toFixed(2)) 91 | ); 92 | this.layerVertices.push(vertex); 93 | 94 | if (this.layerVertices.length > 3) { 95 | this.polygon?.add_vertices(this.layerVertices); 96 | const bufFlush = this.polygon?.triangulate(); 97 | 98 | if (!bufFlush) { 99 | return; 100 | } 101 | this.addFlushBufferToScene(bufFlush); 102 | 103 | this.isTriangulated = true; 104 | } 105 | } 106 | 107 | addHole(holeVertices: Vector3D[]) { 108 | if (!this.polygon) return; 109 | this.polygon.add_holes(holeVertices); 110 | const triResult = JSON.parse(this.polygon.triangulate_with_holes()); 111 | console.log(triResult); 112 | const newBufferFlush = triResult.new_buffer; 113 | const geometry = new THREE.BufferGeometry(); 114 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(newBufferFlush), 3)); 115 | this.geometry = geometry; 116 | 117 | // const bufFlush = this.polygon.get_buffer_flush(); 118 | // this.addFlushBufferToScene(bufFlush); 119 | } 120 | 121 | addFlushBufferToScene(flush: string) { 122 | const flushBuffer = JSON.parse(flush); 123 | const geometry = new THREE.BufferGeometry(); 124 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 125 | geometry.computeVertexNormals(); 126 | 127 | const material = new THREE.MeshStandardMaterial({ 128 | color: 0x00ff00, 129 | // side: THREE.DoubleSide, 130 | transparent: true, 131 | opacity: 0.5, 132 | // wireframe: true 133 | }); 134 | 135 | this.geometry = geometry; 136 | this.material = material; 137 | } 138 | 139 | extrude(height: number) { 140 | if (!this.polygon) return; 141 | const extruded_buff = this.polygon.extrude_by_height(height); 142 | console.log(extruded_buff); 143 | this.generateExtrudedGeometry(extruded_buff); 144 | } 145 | 146 | generateExtrudedGeometry(extruded_buff: string) { 147 | // THIS WORKS 148 | const flushBuffer = JSON.parse(extruded_buff); 149 | console.log(flushBuffer); 150 | 151 | const geometry = new THREE.BufferGeometry(); 152 | geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(flushBuffer), 3)); 153 | geometry.computeVertexNormals(); 154 | this.geometry = geometry; 155 | 156 | // const material = new THREE.MeshStandardMaterial({ 157 | // color: 0x3a86ff, 158 | // }); 159 | // // material.side = THREE.DoubleSide; 160 | // this.material = material; 161 | } 162 | 163 | getBrepData() { 164 | if (!this.polygon) return; 165 | const brepData = this.polygon.get_brep_data(); 166 | const parsedData = JSON.parse(brepData); 167 | console.log(parsedData); 168 | } 169 | 170 | set outline(enable: boolean) { 171 | if (enable && !this.#outlineMesh && this.polygon) { 172 | const outline_buff = this.polygon.outline_edges(); 173 | const outline_buf = JSON.parse(outline_buff); 174 | 175 | const outlineGeometry = new THREE.BufferGeometry(); 176 | outlineGeometry.setAttribute( 177 | "position", 178 | new THREE.Float32BufferAttribute(outline_buf, 3) 179 | ); 180 | 181 | // TODO: Fix the outline position 182 | // outlineGeometry.translate( 183 | // -this._geometryCenterOffset.x, 184 | // -this._geometryCenterOffset.y, 185 | // -this._geometryCenterOffset.z 186 | // ); 187 | 188 | const outlineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 }); 189 | this.#outlineMesh = new THREE.LineSegments( 190 | outlineGeometry, 191 | outlineMaterial 192 | ); 193 | 194 | this.add(this.#outlineMesh); 195 | } 196 | 197 | if (!enable && this.#outlineMesh) { 198 | this.remove(this.#outlineMesh); 199 | this.#outlineMesh.geometry.dispose(); 200 | this.#outlineMesh = null; 201 | } 202 | } 203 | 204 | get outline() { 205 | if (this.#outlineMesh) { 206 | return true; 207 | } 208 | return false; 209 | } 210 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/snapper.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry-three/src/snapper.ts -------------------------------------------------------------------------------- /main/opengeometry-three/src/utils/event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Original source: Simple event handler by [Jason Kleban](https://gist.github.com/JasonKleban/50cee44960c225ac1993c922563aa540). Keep in mind that if you want to remove it later, you might want to declare the callback as an object. If you want to maintain the reference to `this`, you will need to declare the callback as an arrow function. 3 | * Adapted to TypeScript by ThatOpen Company 4 | */ 5 | 6 | 7 | export class Event { 8 | /** 9 | * Add a callback to this event instance. 10 | * @param handler - the callback to be added to this event. 11 | */ 12 | add(handler: T extends void ? { (): void } : { (data: T): void }): void { 13 | this.handlers.push(handler); 14 | } 15 | 16 | /** 17 | * Removes a callback from this event instance. 18 | * @param handler - the callback to be removed from this event. 19 | */ 20 | remove(handler: T extends void ? { (): void } : { (data: T): void }): void { 21 | this.handlers = this.handlers.filter((h) => h !== handler); 22 | } 23 | 24 | /** Triggers all the callbacks assigned to this event. */ 25 | trigger = (data?: T) => { 26 | const handlers = this.handlers.slice(0); 27 | for (const handler of handlers) { 28 | handler(data as any); 29 | } 30 | }; 31 | 32 | /** Gets rid of all the suscribed events. */ 33 | reset() { 34 | this.handlers.length = 0; 35 | } 36 | 37 | private handlers: (T extends void ? { (): void } : { (data: T): void })[] = 38 | []; 39 | } -------------------------------------------------------------------------------- /main/opengeometry-three/src/utils/randomizer.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | export function getUUID() { 4 | const uuid = uuidv4(); 5 | return uuid; 6 | } 7 | -------------------------------------------------------------------------------- /main/opengeometry-three/src/utils/store.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry-three/src/utils/store.ts -------------------------------------------------------------------------------- /main/opengeometry-webgl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@opengeometry/kernel-webgl", 3 | "version": "1.0.0", 4 | "description": "OpenGeometry Kernel for WebGL", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "opengeometry", 11 | "three", 12 | "geometry" 13 | ], 14 | "author": "Vishwajeet Mane", 15 | "license": "MIT", 16 | "dependencies": {} 17 | } -------------------------------------------------------------------------------- /main/opengeometry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "opengeometry" 3 | version = "0.1.1" 4 | authors = ["Vishwajeet Mane "] 5 | description = "Graphics Kernel for Next Gen" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/OpenGeometry-io/OpenGeometry" 8 | edition = "2021" 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [dependencies] 14 | wasm-bindgen = "0.2" 15 | serde = { version = "1.0", features = ["derive"] } 16 | serde-wasm-bindgen = "0.4" 17 | serde_json = "1.0.127" 18 | -------------------------------------------------------------------------------- /main/opengeometry/cad.md: -------------------------------------------------------------------------------- 1 | ### Creation of Elements and Primitives 2 | 3 | #### Cyclinder 4 | There are two ways to create a Cyclinder 5 | 1. Create a Circle Primitive, then create Circle Poly Face and Then Extrude The Polygon by given height 6 | 2. Create a Cylinder Primitive and provide height and radius -------------------------------------------------------------------------------- /main/opengeometry/src/brep/edge.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/brep/edge.rs -------------------------------------------------------------------------------- /main/opengeometry/src/brep/face.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/brep/face.rs -------------------------------------------------------------------------------- /main/opengeometry/src/brep/vertex.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/brep/vertex.rs -------------------------------------------------------------------------------- /main/opengeometry/src/elements/wall.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/elements/wall.rs -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/baseflatmesh.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * As of now we can consider this as a 2D Mesh 3 | * It's y value is always 0 4 | * Potientally, it will be used for Creation of Walls and Other 2D Meshes 5 | */ 6 | 7 | use crate::{operations::{triangulate::triangulate_polygon_buffer_geometry, windingsort}, utility::openmath}; 8 | use wasm_bindgen::prelude::*; 9 | use serde::{Serialize, Deserialize}; 10 | use super::basegeometry; 11 | 12 | #[wasm_bindgen] 13 | #[derive(Clone, Serialize, Deserialize)] 14 | pub struct BaseFlatMesh { 15 | id: String, 16 | geometry: basegeometry::BaseGeometry, 17 | pub extruded: bool, 18 | pub is_mesh: bool, 19 | pub position: openmath::Vector3D, 20 | pub rotation: openmath::Vector3D, 21 | pub scale: openmath::Vector3D, 22 | buffer: Vec 23 | } 24 | 25 | #[wasm_bindgen] 26 | impl BaseFlatMesh { 27 | // Why Getter and Setter - https://github.com/rustwasm/wasm-bindgen/issues/1775 28 | #[wasm_bindgen(setter)] 29 | pub fn set_id(&mut self, id: String) { 30 | self.id = id; 31 | } 32 | 33 | #[wasm_bindgen(getter)] 34 | pub fn id(&self) -> String { 35 | self.id.clone() 36 | } 37 | 38 | #[wasm_bindgen(constructor)] 39 | pub fn new(id: String) -> BaseFlatMesh { 40 | let geometry_id = id.clone(); 41 | BaseFlatMesh { 42 | id, 43 | geometry : basegeometry::BaseGeometry::new(geometry_id.clone()), 44 | extruded : false, 45 | is_mesh : false, 46 | position : openmath::Vector3D::create(0.0, 0.0, 0.0), 47 | rotation : openmath::Vector3D::create(0.0, 0.0, 0.0), 48 | scale : openmath::Vector3D::create(1.0, 1.0, 1.0), 49 | buffer : Vec::new() 50 | } 51 | } 52 | 53 | #[wasm_bindgen] 54 | pub fn add_vertices(&mut self, vertices: Vec) { 55 | self.geometry.add_vertices(vertices); 56 | } 57 | 58 | #[wasm_bindgen] 59 | pub fn add_vertex(&mut self, vertex: openmath::Vector3D) { 60 | self.geometry.add_vertex(vertex); 61 | 62 | if self.geometry.get_vertices().len() > 2 { 63 | self.is_mesh = true; 64 | } 65 | } 66 | 67 | #[wasm_bindgen] 68 | pub fn triangulate(&mut self) -> String { 69 | self.is_mesh = true; 70 | 71 | let merged_vertices = self.geometry.get_vertices(); 72 | let indices = triangulate_polygon_buffer_geometry(self.geometry.clone()); 73 | 74 | // let ccw_vertices = windingsort::ccw_test(self.geometry.get_vertices()); 75 | 76 | // for index in indices { 77 | // for i in index { 78 | // let vertex = ccw_vertices[i as usize]; 79 | // self.buffer.push(vertex.x); 80 | // self.buffer.push(vertex.y); 81 | // self.buffer.push(vertex.z); 82 | // } 83 | // } 84 | 85 | // serde_json::to_string(&self.buffer).unwrap() 86 | 87 | serde_json::to_string(&merged_vertices).unwrap() 88 | } 89 | 90 | #[wasm_bindgen] 91 | pub fn get_buffer_flush(&self) -> String { 92 | serde_json::to_string(&self.buffer).unwrap() 93 | } 94 | 95 | #[wasm_bindgen] 96 | pub fn reset_mesh(&mut self) { 97 | self.is_mesh = false; 98 | self.geometry.reset_geometry(); 99 | self.buffer.clear(); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/basegeometry.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Base Geometry Module 3 | * 4 | * The Base Geomtry is a base trait that all geometry types should implement. 5 | * 6 | * Instead of Serde bindgen can be directly used for getting values from struct - https://github.com/rustwasm/wasm-bindgen/issues/439 7 | */ 8 | 9 | use crate::utility::openmath; 10 | use wasm_bindgen::prelude::*; 11 | use serde::{Serialize, Deserialize}; 12 | 13 | 14 | #[derive(Clone, Serialize, Deserialize)] 15 | pub struct BaseGeometry { 16 | id: String, 17 | vertices: Vec, 18 | indices: Vec, 19 | normals: Vec, 20 | treated: bool, 21 | buffer: Vec, 22 | holes: Vec>, 23 | flat_vertices: Vec, 24 | pub ccw: bool, 25 | } 26 | 27 | impl BaseGeometry { 28 | // Why Getter and Setter - https://github.com/rustwasm/wasm-bindgen/issues/1775 29 | pub fn set_id(&mut self, id: String) { 30 | self.id = id; 31 | } 32 | 33 | pub fn id(&self) -> String { 34 | self.id.clone() 35 | } 36 | 37 | pub fn new(id: String) -> BaseGeometry { 38 | BaseGeometry { 39 | id, 40 | vertices: Vec::new(), 41 | indices: Vec::new(), 42 | normals: Vec::new(), 43 | treated: false, 44 | buffer: Vec::new(), 45 | holes: Vec::new(), 46 | flat_vertices: Vec::new(), 47 | ccw: false, 48 | } 49 | } 50 | 51 | pub fn set_ccw(&mut self, ccw: bool) { 52 | self.ccw = ccw; 53 | } 54 | 55 | pub fn add_vertices(&mut self, vertices: Vec) { 56 | for vertex in vertices { 57 | self.vertices.push(vertex.clone()); 58 | 59 | self.flat_vertices.push(vertex.x); 60 | self.flat_vertices.push(vertex.y); 61 | self.flat_vertices.push(vertex.z); 62 | } 63 | } 64 | 65 | pub fn add_indices(&mut self, indices: Vec) { 66 | for index in indices { 67 | self.indices.push(index); 68 | } 69 | } 70 | 71 | pub fn add_holes(&mut self, holes: Vec) { 72 | self.holes.push(holes.clone()); 73 | } 74 | 75 | 76 | pub fn get_holes(&mut self) -> Vec> { 77 | self.holes.clone() 78 | } 79 | 80 | 81 | pub fn add_vertex(&mut self, vertex: openmath::Vector3D) { 82 | self.vertices.push(vertex.clone()); 83 | } 84 | 85 | 86 | pub fn add_index(&mut self, index: u32) { 87 | self.indices.push(index); 88 | } 89 | 90 | 91 | pub fn add_normal(&mut self, normal: f32) { 92 | self.normals.push(normal); 93 | } 94 | 95 | 96 | pub fn clone_geometry(&self) -> BaseGeometry { 97 | self.clone() 98 | } 99 | 100 | 101 | pub fn get_vertices(&self) -> Vec { 102 | self.vertices.clone() 103 | } 104 | 105 | 106 | pub fn get_geometry(&self) -> String { 107 | let geometry = serde_json::to_string(&self).unwrap(); 108 | geometry 109 | } 110 | 111 | // pub fn get_dimension_for_vertex (&self) -> u32 { 112 | // self.vertices[0]. 113 | // } 114 | 115 | pub fn reset_geometry(&mut self) { 116 | self.vertices.clear(); 117 | self.indices.clear(); 118 | self.normals.clear(); 119 | self.holes.clear(); 120 | self.treated = false; 121 | self.buffer.clear(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/basegroup.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/geometry/basegroup.rs -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/basemesh.rs: -------------------------------------------------------------------------------- 1 | use crate::utility::openmath::{self, Geometry}; 2 | use wasm_bindgen::prelude::*; 3 | use serde::{Serialize, Deserialize}; 4 | use super::basegeometry; 5 | 6 | #[wasm_bindgen] 7 | #[derive(Clone, Serialize, Deserialize)] 8 | pub struct BaseMesh { 9 | id: String, 10 | geometry: basegeometry::BaseGeometry, 11 | brep: Geometry, 12 | pub is_from_extruded: bool, 13 | pub is_from_polygon: bool, 14 | pub position: openmath::Vector3D, 15 | pub rotation: openmath::Vector3D, 16 | pub scale: openmath::Vector3D, 17 | buffer: Vec 18 | } 19 | 20 | #[wasm_bindgen] 21 | impl BaseMesh { 22 | #[wasm_bindgen(setter)] 23 | pub fn set_id(&mut self, id: String) { 24 | self.id = id; 25 | } 26 | 27 | #[wasm_bindgen(getter)] 28 | pub fn id(&self) -> String { 29 | self.id.clone() 30 | } 31 | 32 | #[wasm_bindgen(constructor)] 33 | pub fn new(id: String) -> BaseMesh { 34 | let geometry_id = id.clone(); 35 | BaseMesh { 36 | id, 37 | geometry : basegeometry::BaseGeometry::new(geometry_id.clone()), 38 | brep : Geometry { 39 | vertices: Vec::new(), 40 | edges: Vec::new(), 41 | faces: Vec::new(), 42 | }, 43 | is_from_extruded : false, 44 | is_from_polygon : false, 45 | position : openmath::Vector3D::create(0.0, 0.0, 0.0), 46 | rotation : openmath::Vector3D::create(0.0, 0.0, 0.0), 47 | scale : openmath::Vector3D::create(1.0, 1.0, 1.0), 48 | buffer : Vec::new() 49 | } 50 | } 51 | 52 | /** 53 | * Side view as of now 54 | */ 55 | pub fn outline(&self) -> String { 56 | let mut outline_data = Vec::new(); 57 | 58 | for face in self.brep.faces.clone() { 59 | for index in face { 60 | let vertex_start = self.brep.vertices[index as usize].clone(); 61 | 62 | let mut vertex_end_index = 0; 63 | // Check if the next index is within bounds 64 | if vertex_end_index < self.brep.vertices.len() { 65 | vertex_end_index += 1; 66 | } else { 67 | vertex_end_index = 0; 68 | } 69 | 70 | let vertex_end = self.brep.vertices[vertex_end_index as usize].clone(); 71 | let edge = { 72 | vec![vertex_start, vertex_end] 73 | }; 74 | outline_data.push(edge); 75 | } 76 | } 77 | 78 | serde_json::to_string(&outline_data).unwrap() 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/basepolygon.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::operations::extrude::extrude_polygon_by_buffer_geometry; 4 | use crate::operations::triangulate::triangulate_polygon_buffer_geometry; 5 | use crate::operations::windingsort; 6 | use crate::{geometry, primitives}; 7 | use crate::utility::openmath::Vector3D; 8 | use crate::{operations::triangulate, utility::openmath}; 9 | use crate::geometry::basegeometry; 10 | use serde_json::ser; 11 | use wasm_bindgen::prelude::*; 12 | use serde::{Serialize, Deserialize}; 13 | 14 | #[wasm_bindgen] 15 | #[derive(Clone, Serialize, Deserialize)] 16 | pub struct BasePolygon { 17 | id: String, 18 | geometry: basegeometry::BaseGeometry, 19 | pub extruded: bool, 20 | pub extruded_height: f64, 21 | pub is_polygon: bool, 22 | pub position: openmath::Vector3D, 23 | pub rotation: openmath::Vector3D, 24 | pub scale: openmath::Vector3D, 25 | buffer: Vec 26 | } 27 | 28 | /** 29 | * A Polygon created with a `id` will have a BaseGeometry with same `id`. Feels like a good decision as of now. 30 | */ 31 | 32 | #[wasm_bindgen] 33 | impl BasePolygon { 34 | // Why Getter and Setter - https://github.com/rustwasm/wasm-bindgen/issues/1775 35 | #[wasm_bindgen(setter)] 36 | pub fn set_id(&mut self, id: String) { 37 | self.id = id; 38 | } 39 | 40 | #[wasm_bindgen(getter)] 41 | pub fn id(&self) -> String { 42 | self.id.clone() 43 | } 44 | 45 | // Add the ability to create polygon with list of verticies passed in constructor itself 46 | // as of now use add_vertices method to push all vertices at once 47 | #[wasm_bindgen(constructor)] 48 | pub fn new(id: String) -> BasePolygon { 49 | BasePolygon { 50 | id: id.clone(), 51 | geometry : basegeometry::BaseGeometry::new(id.clone()), 52 | extruded : false, 53 | extruded_height : 0.0, 54 | is_polygon : false, 55 | position : openmath::Vector3D::create(0.0, 0.0, 0.0), 56 | rotation : openmath::Vector3D::create(0.0, 0.0, 0.0), 57 | scale : openmath::Vector3D::create(1.0, 1.0, 1.0), 58 | buffer : Vec::new() 59 | } 60 | } 61 | 62 | #[wasm_bindgen] 63 | pub fn new_with_circle(circle_arc: primitives::circle::CircleArc) -> BasePolygon { 64 | let mut polygon = BasePolygon::new(circle_arc.id()); 65 | // discard the last point as it is same as the first point 66 | let mut circle_arc_points = circle_arc.get_raw_points(); 67 | circle_arc_points.pop(); 68 | polygon.add_vertices(circle_arc_points); 69 | polygon.triangulate(); 70 | polygon 71 | } 72 | 73 | #[wasm_bindgen] 74 | pub fn new_with_rectangle(rectangle: primitives::rectangle::OGRectangle) -> BasePolygon { 75 | let mut polygon = BasePolygon::new(rectangle.id()); 76 | // discard the last point as it is same as the first point 77 | let mut rectangle_points = rectangle.get_raw_points(); 78 | rectangle_points.pop(); 79 | polygon.add_vertices(rectangle_points); 80 | polygon.triangulate(); 81 | polygon 82 | } 83 | 84 | #[wasm_bindgen] 85 | pub fn add_vertices(&mut self, vertices: Vec) { 86 | self.geometry.add_vertices(vertices); 87 | } 88 | 89 | #[wasm_bindgen] 90 | pub fn add_vertex(&mut self, vertex: openmath::Vector3D) { 91 | self.geometry.add_vertex(vertex); 92 | 93 | // If more than 3 vertices are added, then the polygon is created 94 | if self.geometry.get_vertices().len() > 2 { 95 | self.is_polygon = true; 96 | } 97 | } 98 | 99 | #[wasm_bindgen] 100 | pub fn add_holes(&mut self, holes: Vec) { 101 | self.geometry.add_holes(holes); 102 | } 103 | 104 | #[wasm_bindgen] 105 | pub fn triangulate(&mut self) -> String { 106 | self.is_polygon = true; 107 | 108 | let indices = triangulate_polygon_buffer_geometry(self.geometry.clone()); 109 | 110 | let ccw_vertices = windingsort::ccw_test(self.geometry.get_vertices()); 111 | // let ccw_vertices = windingsort::ccw_test(merged_vertices.clone()); 112 | 113 | for index in indices { 114 | for i in index { 115 | let vertex = ccw_vertices[i as usize]; 116 | self.buffer.push(vertex.x); 117 | self.buffer.push(vertex.y); 118 | self.buffer.push(vertex.z); 119 | } 120 | } 121 | 122 | serde_json::to_string(&self.buffer).unwrap() 123 | 124 | // serde_json::to_string(&indices).unwrap() 125 | 126 | // serde_json::to_string(&merged_vertices).unwrap() 127 | } 128 | 129 | pub fn new_triangulate(&mut self) -> String { 130 | // Step 1 - Flatten the geometry - Works 131 | let flat_data = triangulate::flatten_buffer_geometry(self.geometry.clone()); 132 | let vertices = flat_data.vertices.clone(); 133 | let holes = flat_data.holes; 134 | let dimension = flat_data.dimension; 135 | 136 | // Step 2 - Find the left most point in the first hole 137 | let start_index_in_vertices = holes[0] * 3; 138 | let mut end_index_in_vertices = 0; 139 | 140 | if holes.len() > 1 { 141 | end_index_in_vertices = holes[1] * 3; 142 | } else { 143 | // if only one hole is present 144 | end_index_in_vertices = vertices.len() as u32; 145 | } 146 | 147 | let right_most_index = triangulate::find_right_most_point_index(vertices.clone(), start_index_in_vertices, end_index_in_vertices); 148 | 149 | // Step 3 - Find Ray Casting with the outer edges 150 | let right_point = Vector3D::create( 151 | vertices[right_most_index as usize], 152 | vertices[right_most_index as usize + 1], 153 | vertices[right_most_index as usize + 2] 154 | ); 155 | 156 | // Step 4 is inside 157 | let ray_edge = triangulate::check_vertex_collision_with_flat_vertices( 158 | vertices.clone(), 159 | right_point, 160 | 0, 161 | start_index_in_vertices + 1 162 | ); 163 | 164 | let mut new_vertices_processed: Vec = Vec::new(); 165 | // Step 5 - Create Bridge 166 | let bridge_start_index = ray_edge[0][0]; 167 | let bridge_end_index = right_most_index; 168 | let bridge_start = Vector3D::create( 169 | vertices[bridge_start_index as usize], 170 | vertices[bridge_start_index as usize + 1], 171 | vertices[bridge_start_index as usize + 2] 172 | ); 173 | let bridge_end = Vector3D::create( 174 | vertices[bridge_end_index as usize], 175 | vertices[bridge_end_index as usize + 1], 176 | vertices[bridge_end_index as usize + 2] 177 | ); 178 | 179 | let hole_one = self.geometry.get_holes()[0].clone(); 180 | let hole_vertex_nodes = triangulate::create_vertex_nodes(hole_one.clone(), start_index_in_vertices, end_index_in_vertices - 1, true); 181 | 182 | // Before Bridge Vertices 183 | for i in 0..bridge_start_index+3 { 184 | let vertex = vertices[i as usize]; 185 | new_vertices_processed.push(vertex); 186 | } 187 | 188 | // Insert Bridge Vertices 189 | let mut vertex_nodes_data: Vec = Vec::new(); 190 | let mut vertex_next_nodes_data: Vec = Vec::new(); 191 | for node in hole_vertex_nodes { 192 | vertex_nodes_data.push(node.vertex.x); 193 | vertex_nodes_data.push(node.vertex.y); 194 | vertex_nodes_data.push(node.vertex.z); 195 | 196 | vertex_next_nodes_data.push(node.next_index as f64); 197 | } 198 | for i in vertex_next_nodes_data.iter() { 199 | let index = *i as usize; 200 | let x = vertices[index]; 201 | let y = vertices[index + 1]; 202 | let z = vertices[index + 2]; 203 | 204 | new_vertices_processed.push(x); 205 | new_vertices_processed.push(y); 206 | new_vertices_processed.push(z); 207 | } 208 | 209 | // Start Index Of Hole-Bridge again to complete the loop, i.e. vertex_next_nodes 210 | let start_index_of_bridge_to_hole = vertex_next_nodes_data[0] as usize; 211 | let bridge_to_hole_x = vertices[start_index_of_bridge_to_hole]; 212 | let bridge_to_hole_y = vertices[start_index_of_bridge_to_hole + 1]; 213 | let bridge_to_hole_z = vertices[start_index_of_bridge_to_hole + 2]; 214 | new_vertices_processed.push(bridge_to_hole_x); 215 | new_vertices_processed.push(bridge_to_hole_y); 216 | new_vertices_processed.push(bridge_to_hole_z); 217 | 218 | // // Back To Bridge 219 | let bridge_x = vertices[bridge_start_index as usize]; 220 | let bridge_y = vertices[bridge_start_index as usize + 1]; 221 | let bridge_z = vertices[bridge_start_index as usize + 2]; 222 | new_vertices_processed.push(bridge_x); 223 | new_vertices_processed.push(bridge_y); 224 | new_vertices_processed.push(bridge_z); 225 | 226 | // After Bridge Vertices 227 | let before_hole_start_index = holes[0] * 3; 228 | for i in bridge_start_index..before_hole_start_index as u32 { 229 | let vertex = vertices[i as usize]; 230 | new_vertices_processed.push(vertex); 231 | } 232 | 233 | 234 | let mut new_buffergeometry = basegeometry::BaseGeometry::new("new_buffergeometry".to_string()); 235 | let og_vertices: Vec = new_vertices_processed.chunks(3) 236 | .map(|chunk| Vector3D::create(chunk[0], chunk[1], chunk[2])) 237 | .collect(); 238 | new_buffergeometry.add_vertices(og_vertices); 239 | let new_tricut = triangulate_polygon_buffer_geometry(new_buffergeometry.clone()); 240 | let ccw_vertices = windingsort::ccw_test(new_buffergeometry.get_vertices()); 241 | let mut new_buffer: Vec = Vec::new(); 242 | 243 | for index in new_tricut { 244 | for i in index { 245 | let vertex = ccw_vertices[i as usize]; 246 | new_buffer.push(vertex.x); 247 | new_buffer.push(vertex.y); 248 | new_buffer.push(vertex.z); 249 | } 250 | } 251 | 252 | 253 | let mut data = HashMap::new(); 254 | data.insert("vertices", vertices); 255 | data.insert("holes", holes.into_iter().map(|x| x as f64).collect()); 256 | data.insert("dimension", vec![dimension as f64]); 257 | data.insert("start_index_in_vertices", vec![start_index_in_vertices as f64]); 258 | data.insert("end_index_in_vertices", vec![end_index_in_vertices as f64]); 259 | data.insert("right_most_index", vec![right_most_index as f64]); 260 | data.insert("right_point", vec![right_point.x, right_point.y, right_point.z]); 261 | data.insert("bridge_start", vec![bridge_start.x, bridge_start.y, bridge_start.z]); 262 | data.insert("bridge_end", vec![bridge_end.x, bridge_end.y, bridge_end.z]); 263 | 264 | data.insert("new_vertices_processed", new_vertices_processed); 265 | data.insert("vertex_nodes", vertex_nodes_data); 266 | data.insert("vertex_next_nodes", vertex_next_nodes_data); 267 | 268 | data.insert("new_buffer", new_buffer); 269 | 270 | let mut edge_data: Vec = Vec::new(); 271 | for edge in ray_edge { 272 | for i in edge { 273 | edge_data.push(i as f64); 274 | } 275 | } 276 | data.insert("ray_edge", edge_data); 277 | 278 | serde_json::to_string(&data).unwrap() 279 | } 280 | 281 | #[wasm_bindgen] 282 | pub fn get_buffer_flush(&self) -> String { 283 | serde_json::to_string(&self.buffer).unwrap() 284 | } 285 | 286 | #[wasm_bindgen] 287 | pub fn clear_vertices(&mut self) { 288 | self.geometry.reset_geometry(); 289 | } 290 | 291 | #[wasm_bindgen] 292 | pub fn reset_polygon(&mut self) { 293 | // Reset the geometry 294 | } 295 | 296 | #[wasm_bindgen] 297 | pub fn extrude_by_height(&mut self, height: f64) -> String { 298 | self.extruded = true; 299 | self.extruded_height = height; 300 | 301 | let extrude_data = extrude_polygon_by_buffer_geometry(self.geometry.clone(), height); 302 | 303 | let mut local_geometry = Vec::new(); 304 | 305 | // let face = extrude_data.faces[0].clone(); 306 | for face in extrude_data.faces.clone() { 307 | let mut face_vertices: Vec = Vec::new(); 308 | for index in face.clone() { 309 | face_vertices.push(extrude_data.vertices[index as usize].clone()); 310 | } 311 | 312 | let triangulated_face = triangulate::triangulate_polygon_by_face(face_vertices.clone()); 313 | for index in triangulated_face { 314 | for i in index { 315 | let vertex = face_vertices[i as usize]; 316 | local_geometry.push(vertex.x); 317 | local_geometry.push(vertex.y); 318 | local_geometry.push(vertex.z); 319 | } 320 | } 321 | } 322 | 323 | // let face_data_string = serde_json::to_string(&face).unwrap(); // Serialize face_data 324 | // face_data_string 325 | 326 | // let extrude_data_string = serde_json::to_string(&extrude_data).unwrap(); // Serialize extrude_data 327 | // extrude_data_string 328 | 329 | let string_data = serde_json::to_string(&local_geometry).unwrap(); 330 | string_data 331 | 332 | 333 | // ABOVE LINE WORKING 334 | 335 | // // TESTING EDGES OUTLINE 336 | // let mut outline_data: Vec> = Vec::new(); 337 | 338 | // // for edge in extruded_raw.edges { 339 | // // let start = vertices[edge[0] as usize].clone(); 340 | // // let end = vertices[edge[1] as usize].clone(); 341 | 342 | // // let edge_vertices = vec![start, end]; 343 | // // outline_data.push(edge_vertices); 344 | // // } 345 | 346 | // for face in faces { 347 | // let mut face_vertices: Vec = Vec::new(); 348 | // for index in face { 349 | // let v_face = vertices[index as usize].clone(); 350 | // face_vertices.push(v_face); 351 | // } 352 | // outline_data.push(face_vertices); 353 | // } 354 | 355 | // serde_json::to_string(&outline_data).unwrap() 356 | } 357 | 358 | #[wasm_bindgen] 359 | pub fn get_outlines(&self) -> String { 360 | let height = self.extruded_height; 361 | if height == 0.0 { 362 | return "Please extrude the polygon first".to_string(); 363 | } 364 | 365 | let mut outline_data: Vec> = Vec::new(); 366 | let extruded_raw = extrude_polygon_by_buffer_geometry(self.geometry.clone(), height); 367 | let faces = extruded_raw.faces; 368 | let vertices = extruded_raw.vertices; 369 | 370 | for face in faces { 371 | let mut face_vertices: Vec = Vec::new(); 372 | for index in face { 373 | let v_face = vertices[index as usize].clone(); 374 | face_vertices.push(v_face); 375 | } 376 | outline_data.push(face_vertices); 377 | } 378 | serde_json::to_string(&outline_data).unwrap() 379 | } 380 | 381 | #[wasm_bindgen] 382 | pub fn get_geometry(&self) -> String { 383 | let geometry = self.geometry.get_geometry(); 384 | geometry 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/geometry.md: -------------------------------------------------------------------------------- 1 | What is Geometry? 2 | Primitive + Operation 3 | 4 | -------------------------------------------------------------------------------- /main/opengeometry/src/geometry/triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::utility::openmath::Vector3D; 2 | 3 | #[derive(Clone)] 4 | pub struct Triangle { 5 | pub a: Vector3D, 6 | pub b: Vector3D, 7 | pub c: Vector3D, 8 | } 9 | 10 | impl Triangle { 11 | pub fn new() -> Triangle { 12 | Triangle { 13 | a: Vector3D::create(0.0, 0.0, 0.0), 14 | b: Vector3D::create(0.0, 0.0, 0.0), 15 | c: Vector3D::create(0.0, 0.0, 0.0), 16 | } 17 | } 18 | 19 | pub fn set_vertices(&mut self, a: Vector3D, b: Vector3D, c: Vector3D) { 20 | self.a = a; 21 | self.b = b; 22 | self.c = c; 23 | } 24 | 25 | pub fn is_point_in_triangle(&self, p : Vector3D) -> bool { 26 | let ab = self.b.clone().subtract(&self.a); 27 | let bc = self.c.clone().subtract(&self.b); 28 | let ca = self.a.clone().subtract(&self.c); 29 | 30 | let ap = p.clone().subtract(&self.a); 31 | let bp = p.clone().subtract(&self.b); 32 | let cp = p.clone().subtract(&self.c); 33 | 34 | let cross_abp = ab.clone().cross(&ap); 35 | let cross_bcp = bc.clone().cross(&bp); 36 | let cross_cap = ca.clone().cross(&cp); 37 | 38 | if ( 39 | cross_abp.y > 0.0 && 40 | cross_bcp.y > 0.0 && 41 | cross_cap.y > 0.0 42 | ) || ( 43 | cross_abp.y < 0.0 && 44 | cross_bcp.y < 0.0 && 45 | cross_cap.y < 0.0 46 | ) { 47 | return true; 48 | } 49 | false 50 | } 51 | } -------------------------------------------------------------------------------- /main/opengeometry/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod geometry { 2 | pub mod basegeometry; 3 | pub mod basemesh; 4 | pub mod basepolygon; 5 | pub mod basegroup; 6 | pub mod triangle; 7 | pub mod baseflatmesh; 8 | } 9 | 10 | pub mod operations { 11 | pub mod triangulate; 12 | pub mod windingsort; 13 | pub mod extrude; 14 | } 15 | 16 | pub mod utility { 17 | pub mod openmath; 18 | } 19 | 20 | pub mod primitives { 21 | pub mod circle; 22 | pub mod simple_line; 23 | pub mod poly_line; 24 | pub mod rectangle; 25 | pub mod cylinder; 26 | pub mod polygon; 27 | } 28 | -------------------------------------------------------------------------------- /main/opengeometry/src/operations/degenerate-test.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * Test for Degenerate Cases in 3D Coords 3 | */ -------------------------------------------------------------------------------- /main/opengeometry/src/operations/extrude.rs: -------------------------------------------------------------------------------- 1 | use crate::{geometry::basegeometry::BaseGeometry, utility::openmath::{Geometry, Geometry_Holes, Vector3D}}; 2 | use super::{triangulate, windingsort}; 3 | 4 | pub fn extrude_polygon_by_buffer_geometry(geom_buf: BaseGeometry, height: f64) -> Geometry { 5 | if (geom_buf.get_vertices().len() < 3) { 6 | // return String::from("Polygon should have atleast 3 vertices"); 7 | } 8 | 9 | let ccw_vertices = windingsort::ccw_test(geom_buf.get_vertices()); 10 | 11 | let mut buf_vertices = ccw_vertices.clone(); 12 | let mut buf_edges: Vec> = Vec::new(); 13 | let mut buf_faces: Vec> = Vec::new(); 14 | 15 | let current_length = buf_vertices.len(); 16 | 17 | // Bottom Face 18 | for i in 0..current_length { 19 | let edge = { 20 | vec![i as u8, ((i + 1) % ccw_vertices.len()) as u8] 21 | }; 22 | buf_edges.push(edge); 23 | } 24 | 25 | let mut face: Vec = Vec::new(); 26 | for i in 0..ccw_vertices.len() { 27 | face.push(i as u8); 28 | } 29 | // face.reverse(); 30 | buf_faces.push(face); 31 | 32 | for index in 0..ccw_vertices.len() { 33 | let new_vertex = ccw_vertices[index].clone().add_extrude_in_up(height, Vector3D::create(0.0, 1.0, 0.0)); 34 | buf_vertices.push(new_vertex); 35 | 36 | let edge = { 37 | vec![index as u8, buf_vertices.len() as u8 - 1] 38 | }; 39 | 40 | buf_edges.push(edge); 41 | } 42 | 43 | for i in current_length..buf_vertices.len() { 44 | if i < buf_vertices.len() - 1 { 45 | let edge = { 46 | vec![i as u8, (i + 1) as u8] 47 | }; 48 | buf_edges.push(edge); 49 | } else { 50 | let edge = { 51 | vec![i as u8, (current_length) as u8] 52 | }; 53 | buf_edges.push(edge); 54 | } 55 | } 56 | 57 | // Side Faces 58 | for i in 0..current_length { 59 | let next = (i + 1) % current_length; 60 | let mut face: Vec = vec![ 61 | i as u8, 62 | next as u8, 63 | (next + current_length) as u8, 64 | i as u8 + current_length as u8, 65 | ]; 66 | face.reverse(); 67 | buf_faces.push(face); 68 | } 69 | 70 | // Top Face 71 | let mut face: Vec = Vec::new(); 72 | for i in 0..current_length { 73 | face.push(i as u8 + current_length as u8); 74 | } 75 | face.reverse(); 76 | buf_faces.push(face); 77 | 78 | let geometry = Geometry { 79 | vertices: buf_vertices, 80 | edges: buf_edges, 81 | faces: buf_faces, 82 | }; 83 | geometry 84 | } 85 | 86 | 87 | pub fn extrude_polygon_with_holes(mut geom_buf: BaseGeometry, height: f64) -> Geometry_Holes { 88 | if (geom_buf.get_vertices().len() < 3) { 89 | // return String::from("Polygon should have atleast 3 vertices"); 90 | } 91 | 92 | let flat_data = triangulate::flatten_buffer_geometry(geom_buf.clone()); 93 | let vertices = flat_data.vertices.clone(); 94 | let holes = flat_data.holes.clone(); 95 | 96 | let mut start_hole_index = 0; 97 | if holes.len() > 0 { 98 | start_hole_index = holes[0] * 3; 99 | } 100 | 101 | let mut temp_vertices: Vec = Vec::new(); 102 | let mut i: usize = 0; 103 | while i < start_hole_index as usize { 104 | let x = vertices[i]; 105 | let y = vertices[i + 1]; 106 | let z = vertices[i + 2]; 107 | let vertex = Vector3D::create(x, y, z); 108 | temp_vertices.push(vertex); 109 | i += 3; 110 | } 111 | 112 | let ccw_vertices = windingsort::ccw_test(temp_vertices); 113 | let mut buf_vertices = ccw_vertices.clone(); 114 | let mut buf_edges: Vec> = Vec::new(); 115 | let mut buf_faces: Vec> = Vec::new(); 116 | let mut buf_holes: Vec> = Vec::new(); 117 | let mut face_holes_map: std::collections::HashMap> = std::collections::HashMap::new(); 118 | 119 | let mut hole_index = 0; 120 | let mut face_index = 0; 121 | 122 | let mut current_length = buf_vertices.len(); 123 | 124 | // Bottom Face 125 | // TODO: Extra Edge for holes 126 | for i in 0..current_length { 127 | let edge = { 128 | vec![i as u8, ((i + 1) % ccw_vertices.len()) as u8] 129 | }; 130 | buf_edges.push(edge); 131 | } 132 | 133 | let mut face: Vec = Vec::new(); 134 | for i in 0..ccw_vertices.len() { 135 | face.push(i as u8); 136 | } 137 | face.reverse(); 138 | buf_faces.push(face); 139 | 140 | if geom_buf.get_holes().len() > 0 { 141 | let mut holes_for_face = Vec::new(); 142 | for hole in geom_buf.get_holes() { 143 | let mut clone_hole = hole.clone(); 144 | clone_hole.reverse(); 145 | buf_holes.push(clone_hole); 146 | // Bottom Face hence 0 147 | holes_for_face.push(hole_index); 148 | hole_index += 1; 149 | } 150 | face_holes_map.insert(face_index, holes_for_face); 151 | face_index += 1; 152 | } 153 | 154 | // Side Face Refined 155 | for index in 0..ccw_vertices.len() { 156 | let new_vertex = ccw_vertices[index].clone().add_extrude_in_up(height, Vector3D::create(0.0, 1.0, 0.0)); 157 | buf_vertices.push(new_vertex); 158 | 159 | let edge = { 160 | vec![index as u8, buf_vertices.len() as u8 - 1] 161 | }; 162 | 163 | buf_edges.push(edge); 164 | } 165 | 166 | // Extruded Top Face Edges 167 | for i in current_length..buf_vertices.len() { 168 | if i < buf_vertices.len() - 1 { 169 | let edge = { 170 | vec![i as u8, (i + 1) as u8] 171 | }; 172 | buf_edges.push(edge); 173 | } else { 174 | let edge = { 175 | vec![i as u8, (current_length) as u8] 176 | }; 177 | buf_edges.push(edge); 178 | } 179 | } 180 | 181 | // Bottom Holes - Holes can now be added to buf_vertices since we follow the order that holes come after normal vertices 182 | let mut hole_index_start = buf_vertices.len(); 183 | let mut i = start_hole_index as usize; 184 | while i < vertices.len() - 2 { 185 | let x = vertices[i]; 186 | let y = vertices[i + 1]; 187 | let z = vertices[i + 2]; 188 | let vertex = Vector3D::create(x, y, z); 189 | buf_vertices.push(vertex); 190 | i += 3; 191 | } 192 | // Create Edges for holes 193 | for hole in geom_buf.get_holes() { 194 | for i in 0..hole.len() { 195 | let edge = { 196 | vec![(hole_index_start + i) as u8, ((i + 1) % hole.len() + hole_index_start) as u8] 197 | }; 198 | buf_edges.push(edge); 199 | } 200 | } 201 | 202 | // Side Faces 203 | for i in 0..current_length { 204 | let next = (i + 1) % current_length; 205 | let mut face: Vec = vec![ 206 | i as u8, 207 | next as u8, 208 | (next + current_length) as u8, 209 | i as u8 + current_length as u8, 210 | ]; 211 | // face.reverse(); 212 | buf_faces.push(face); 213 | 214 | face_holes_map.insert(face_index, Vec::new()); 215 | face_index += 1; 216 | } 217 | 218 | // let mut hole_index_start_buf_vertices = buf_vertices.len(); 219 | 220 | // TODO: Add Side Faces Of Holes 221 | // Add Extrude for Holes 222 | if geom_buf.get_holes().len() > 0 { 223 | for hole in geom_buf.get_holes() { 224 | 225 | let hole_index_constant = hole_index_start; 226 | 227 | // Hole Side Face Edges Vertical 228 | for vertex in hole.clone() { 229 | let new_vertex = vertex.clone().add_extrude_in_up(height, Vector3D::create(0.0, 1.0, 0.0)); 230 | buf_vertices.push(new_vertex); 231 | 232 | let edge = { 233 | vec![hole_index_start as u8, buf_vertices.len() as u8 - 1] 234 | }; 235 | buf_edges.push(edge); 236 | hole_index_start += 1; 237 | } 238 | 239 | // Add Edges for extruded holes 240 | for i in 0..hole.len() { 241 | let edge = { 242 | vec![(hole_index_start + i) as u8, ((i + 1) % hole.len() + hole_index_start) as u8] 243 | }; 244 | buf_edges.push(edge); 245 | } 246 | 247 | // SOME PROBLEM WITH THIS 248 | // // Hole Side Face Actuals 249 | let hole_end_index = buf_vertices.len() - hole.len(); 250 | for i in 0..hole.len() { 251 | let next = (i + 1) % hole.len(); 252 | let mut face: Vec = vec![ 253 | hole_index_constant as u8 + i as u8, 254 | hole_index_constant as u8 + next as u8, 255 | hole_end_index as u8 + next as u8, 256 | hole_end_index as u8 + i as u8, 257 | ]; 258 | face.reverse(); 259 | buf_faces.push(face); 260 | 261 | // // Add to Face Holes Map 262 | face_holes_map.insert(face_index, Vec::new()); 263 | face_index += 1; 264 | } 265 | } 266 | } 267 | 268 | // Top Face 269 | let mut face: Vec = Vec::new(); 270 | for i in 0..current_length { 271 | face.push(i as u8 + current_length as u8); 272 | } 273 | face.reverse(); 274 | buf_faces.push(face); 275 | face_holes_map.insert(face_index, Vec::new()); 276 | 277 | if geom_buf.get_holes().len() > 0 { 278 | for hole in geom_buf.get_holes() { 279 | let mut extruded_hole: Vec = Vec::new(); 280 | for i in 0..hole.len() { 281 | let index = hole_index_start + i; 282 | extruded_hole.push(buf_vertices[index as usize].clone()); 283 | } 284 | extruded_hole.reverse(); 285 | buf_holes.push(extruded_hole.clone()); 286 | // Top Face hence 1 287 | let mut holes_for_face = Vec::new(); 288 | holes_for_face.push(hole_index); 289 | hole_index += 1; 290 | face_holes_map.insert(face_index, holes_for_face); 291 | face_index += 1; 292 | } 293 | } 294 | 295 | let brep_geom: Geometry_Holes = Geometry_Holes { 296 | vertices: buf_vertices.clone(), 297 | edges: buf_edges.clone(), 298 | faces: buf_faces.clone(), 299 | holes: buf_holes.clone(), 300 | face_holes_map: face_holes_map.clone(), 301 | is_ccw_last_face: false, 302 | face_length: 0 303 | }; 304 | brep_geom 305 | } -------------------------------------------------------------------------------- /main/opengeometry/src/operations/graph.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * Linked List of Primitives or Geometry Objects, where each node is a primitive or geometry object 3 | * and the edges are the operations between the nodes. 4 | */ 5 | 6 | use wasm_bindgen::prelude::*; 7 | use serde::{Serialize, Deserialize}; 8 | 9 | pub struct GraphScene { 10 | id: String, 11 | nodes: Vec, 12 | edges: Vec 13 | } 14 | 15 | #[wasm_bindgen] 16 | #[derive(Clone, Serialize, Deserialize)] 17 | pub struct GraphNode { 18 | id: String, 19 | primitive: Option, 20 | geometry: Option 21 | } 22 | 23 | #[wasm_bindgen] 24 | #[derive(Clone, Serialize, Deserialize)] 25 | pub struct GraphEdge { 26 | id: String, 27 | operation: Operation, 28 | from: String, 29 | to: String 30 | } 31 | 32 | #[wasm_bindgen] 33 | #[derive(Clone, Serialize, Deserialize)] 34 | pub enum Operation { 35 | Triangulate, 36 | Windingsort, 37 | Intersect 38 | } -------------------------------------------------------------------------------- /main/opengeometry/src/operations/triangulate.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use wasm_bindgen::prelude::wasm_bindgen; 3 | 4 | use crate::geometry::{self, basegeometry::BaseGeometry, triangle::Triangle}; 5 | use crate::operations::windingsort; 6 | use std::collections::HashMap; 7 | use crate::utility::openmath::Vector3D; 8 | 9 | pub fn ear_triangle_test( 10 | vertices: HashMap>, 11 | a_index: u32, 12 | b_index: u32, 13 | c_index: u32, 14 | ) -> bool { 15 | let point_a = Vector3D::create( 16 | vertices[&(a_index)][0], 17 | vertices[&(a_index)][1], 18 | vertices[&(a_index)][2] 19 | ); 20 | let point_b = Vector3D::create( 21 | vertices[&(b_index)][0], 22 | vertices[&(b_index)][1], 23 | vertices[&(b_index)][2] 24 | ); 25 | let point_c = Vector3D::create( 26 | vertices[&(c_index)][0], 27 | vertices[&(c_index)][1], 28 | vertices[&(c_index)][2] 29 | ); 30 | let ba = point_b.subtract(&point_a); 31 | let bc = point_b.subtract(&point_c); 32 | let cross_product = ba.cross(&bc); 33 | 34 | if cross_product.y < 0.0 { 35 | return false; 36 | } 37 | 38 | let mut triangle = Triangle::new(); 39 | triangle.set_vertices(point_a, point_b, point_c); 40 | 41 | for (i, vertex) in vertices.iter() { 42 | if *i != a_index && *i != b_index && *i != c_index { 43 | let p = Vector3D::create(vertex[0], vertex[1], vertex[2]); 44 | if triangle.is_point_in_triangle(p) { 45 | return false; 46 | } 47 | } 48 | } 49 | 50 | true 51 | } 52 | 53 | // Accepting Vertices in CCW order 54 | pub fn tricut(polygon_vertices: Vec) -> Vec> { 55 | let mut all_vertices: HashMap> = HashMap::new(); 56 | for (i, vertex) in polygon_vertices.iter().enumerate() { 57 | all_vertices.insert(i as u32, vec![vertex.x, vertex.y, vertex.z]); 58 | } 59 | 60 | let mut remaining_vertices: Vec = (0..all_vertices.len() as u32).collect(); 61 | let mut triangle_indices: Vec> = Vec::new(); 62 | 63 | while remaining_vertices.len() > 3 { 64 | let len = remaining_vertices.len(); 65 | for i in 0..len { 66 | let a = remaining_vertices[(i + len - 1) % len]; 67 | let b = remaining_vertices[i]; 68 | let c = remaining_vertices[(i + 1) % len]; 69 | 70 | if ear_triangle_test(all_vertices.clone(), a, b, c) { 71 | triangle_indices.push(vec![a, b, c]); // changed from vec![a, b, c] to vec![a, c, b] 72 | remaining_vertices.remove(i); 73 | break; 74 | } 75 | } 76 | } 77 | 78 | // Reverse the order for the last triangle as well 79 | triangle_indices.push(vec![ 80 | remaining_vertices[0], 81 | remaining_vertices[1], // changed from [0, 1, 2] to [0, 2, 1] 82 | remaining_vertices[2], 83 | ]); 84 | 85 | triangle_indices 86 | } 87 | 88 | 89 | pub fn triangulate_polygon_buffer_geometry(geom_buf: BaseGeometry) -> Vec> { 90 | 91 | let raw_vertices = geom_buf.get_vertices().clone(); 92 | 93 | let vertices; 94 | 95 | if (geom_buf.ccw) { 96 | vertices = raw_vertices; 97 | } else { 98 | vertices = windingsort::ccw_test(raw_vertices.clone()); 99 | } 100 | // let mut triangles_vertices: Vec = Vec::new(); 101 | let tri_indices = tricut(vertices); 102 | 103 | tri_indices 104 | } 105 | 106 | 107 | // 108 | // Triangule by faces and vertices 109 | // 110 | pub fn triangulate_polygon_by_face(face: Vec) -> Vec> { 111 | let raw_vertices = face.clone(); 112 | let ccw_vertices = windingsort::ccw_test(raw_vertices.clone()); 113 | 114 | // let mut triangles_vertices: Vec = Vec::new(); 115 | let tri_indices = tricut(ccw_vertices); 116 | 117 | tri_indices 118 | } 119 | 120 | 121 | pub struct FlattenData { 122 | pub vertices: Vec, 123 | pub holes: Vec, 124 | pub dimension: u32, 125 | } 126 | 127 | pub fn flatten_buffer_geometry(mut geom_buf: BaseGeometry) -> FlattenData { 128 | let mut vertices: Vec = Vec::new(); 129 | let mut holes: Vec = Vec::new(); 130 | 131 | let dimension: u32 = 3; 132 | 133 | let mut current_index = 0; 134 | 135 | for vertex in geom_buf.get_vertices() { 136 | vertices.push(vertex.x); 137 | vertices.push(vertex.y); 138 | vertices.push(vertex.z); 139 | 140 | current_index += 1; 141 | } 142 | 143 | // Do we check for clockwise or counterclockwise here? 144 | for hole in geom_buf.get_holes() { 145 | for vertex in &hole { 146 | vertices.push(vertex.x); 147 | vertices.push(vertex.y); 148 | vertices.push(vertex.z); 149 | } 150 | 151 | holes.push(current_index); 152 | 153 | current_index += hole.len() as u32; 154 | } 155 | 156 | FlattenData { 157 | vertices, 158 | holes, 159 | dimension, 160 | } 161 | } 162 | 163 | /** 164 | * Find Left Most Point in Given Polygon 165 | */ 166 | // pub fn find_left_most_point(flat_data_vertices: Vec, start: u32, end: u32) -> Vector3D { 167 | // let mut left_most_index = start; 168 | // let mut left_most_x = flat_data_vertices[start as usize * 3]; 169 | 170 | // for i in start..end { 171 | // let x = flat_data_vertices[i as usize * 3]; 172 | // if x < left_most_x { 173 | // left_most_x = x; 174 | // left_most_index = i; 175 | // } 176 | // } 177 | 178 | // Vector3D::create( 179 | // flat_data_vertices[left_most_index as usize * 3], 180 | // flat_data_vertices[left_most_index as usize * 3 + 1], 181 | // flat_data_vertices[left_most_index as usize * 3 + 2] 182 | // ) 183 | // } 184 | 185 | /** 186 | * Find Right Most Point in Given Polygon or Hole 187 | */ 188 | pub fn find_right_most_point_index(flat_data_vertices: Vec, start: u32, end: u32) -> u32 { 189 | let mut i = start; 190 | let mut right_most_index = start; 191 | 192 | let mut right_most_x = flat_data_vertices[start as usize]; 193 | 194 | while i < end { 195 | let x = flat_data_vertices[i as usize]; 196 | if x > right_most_x { 197 | right_most_x = x; 198 | right_most_index = i; 199 | } 200 | 201 | i += 3; 202 | } 203 | 204 | right_most_index 205 | } 206 | 207 | pub fn check_vertex_collision_with_flat_vertices( 208 | flat_data_vertices: Vec, 209 | right_max_point: Vector3D, 210 | start: u32, 211 | end: u32 212 | ) -> Vec> { 213 | let mut i = start; 214 | let mut potential_edge: Vec> = Vec::new(); 215 | 216 | // Traverse All The Edges 217 | while i < end { 218 | // This while loop will check from A to B, and B to C 219 | if (i + 6) < end { 220 | let x = flat_data_vertices[i as usize]; 221 | let y = flat_data_vertices[i as usize + 1]; 222 | let z = flat_data_vertices[i as usize + 2]; 223 | 224 | let x1 = flat_data_vertices[i as usize + 3]; 225 | let y1 = flat_data_vertices[i as usize + 4]; 226 | let z1 = flat_data_vertices[i as usize + 5]; 227 | 228 | // Check if z of A is more than z of right_max_point and z of B is less than z of right_max_point 229 | if (z <= right_max_point.z && z1 >= right_max_point.z) { 230 | let edge_index = vec![i, i + 3]; 231 | potential_edge.push(edge_index); 232 | } 233 | } else { 234 | // This is for last edge 235 | let x = flat_data_vertices[i as usize]; 236 | let y = flat_data_vertices[i as usize + 1]; 237 | let z = flat_data_vertices[i as usize + 2]; 238 | 239 | let x1 = flat_data_vertices[start as usize]; 240 | let y1 = flat_data_vertices[start as usize + 1]; 241 | let z1 = flat_data_vertices[start as usize + 2]; 242 | 243 | // Check if z of A is more than z of right_max_point and z of B is less than z of right_max_point 244 | if (z <= right_max_point.z && z1 >= right_max_point.z) { 245 | let edge_index = vec![i, start]; 246 | potential_edge.push(edge_index); 247 | } 248 | 249 | break; 250 | } 251 | 252 | i += 3; 253 | } 254 | 255 | // Step 4 - Cast the ray from the right_most_index to all potential edges and check if it intersects without obstacles 256 | let mut found_edge: Vec> = Vec::new(); 257 | for edge in potential_edge { 258 | let a_index = edge[0]; 259 | let b_index = edge[1]; 260 | 261 | let x = flat_data_vertices[a_index as usize]; 262 | let y = flat_data_vertices[a_index as usize + 1]; 263 | let z = flat_data_vertices[a_index as usize + 2]; 264 | let A = Vector3D::create(x, y, z); 265 | 266 | let x1 = flat_data_vertices[b_index as usize]; 267 | let y1 = flat_data_vertices[b_index as usize + 1]; 268 | let z1 = flat_data_vertices[b_index as usize + 2]; 269 | let B = Vector3D::create(x1, y1, z1); 270 | 271 | // Check if the ray intersects with the edge and no obstacles 272 | let right_ray_from_vertex = Vector3D::create(right_max_point.x + 1.0, right_max_point.y, right_max_point.z); 273 | let ray = right_ray_from_vertex.subtract(&right_max_point); 274 | let edge_vector = B.subtract(&A); 275 | let cross_product = ray.cross(&edge_vector); 276 | let cross_product_length = cross_product.dot(&cross_product); 277 | let edge_vector_length = edge_vector.dot(&edge_vector); 278 | let ray_length = ray.dot(&ray); 279 | let denominator = cross_product_length * edge_vector_length - ray_length * edge_vector_length; 280 | if denominator == 0.0 { 281 | continue; // Parallel lines 282 | } 283 | let t = (cross_product.dot(&edge_vector) * ray_length - cross_product.dot(&ray) * edge_vector_length) / denominator; 284 | let u = (cross_product.dot(&ray) * edge_vector_length - cross_product.dot(&edge_vector) * ray_length) / denominator; 285 | if t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0 { 286 | // The ray intersects the edge 287 | let f_edge = vec![a_index, b_index]; 288 | found_edge.push(f_edge); 289 | break; 290 | } 291 | } 292 | 293 | found_edge 294 | } 295 | 296 | 297 | /** 298 | * Vertex Node for Tricut 299 | * This is used to store the vertex information and its connections in the triangulation process 300 | * is_hole: true if the vertex is part of a hole 301 | * is_hole_treated: true if the hole has been treated 302 | * hole_index: index of the hole in the list of holes, -1 if not a hole 303 | * next_index: index of the next vertex in the list 304 | * prev_index: index of the previous vertex in the list 305 | * treated: true if the vertex has been treated 306 | * index: index of the vertex in the main list 307 | * vertex: the vertex itself 308 | */ 309 | #[derive(Clone)] 310 | pub struct VertexNodeTricut { 311 | pub vertex: Vector3D, 312 | pub index: u32, 313 | pub treated: bool, 314 | pub is_hole: bool, 315 | pub is_hole_treated: bool, 316 | pub hole_index: i32, 317 | pub next_index: u32, 318 | pub prev_index: u32 319 | } 320 | 321 | 322 | impl VertexNodeTricut { 323 | pub fn new(vertex: Vector3D, index: u32) -> Self { 324 | Self { 325 | vertex, 326 | index, 327 | treated: false, 328 | is_hole: false, 329 | is_hole_treated: false, 330 | hole_index: -1, 331 | next_index: 0, 332 | prev_index: 0 333 | } 334 | } 335 | } 336 | 337 | pub fn create_vertex_nodes(vertices: Vec, start: u32, end: u32, is_hole: bool) -> Vec { 338 | let mut vertex_nodes: Vec = Vec::new(); 339 | 340 | let mut index = start; 341 | let mut current_index = start; 342 | 343 | for vertex in vertices { 344 | let node = VertexNodeTricut::new( 345 | vertex, 346 | index 347 | ); 348 | vertex_nodes.push(node); 349 | 350 | index += 3; 351 | } 352 | 353 | // Set the next and previous indices 354 | for i in 0..vertex_nodes.len() { 355 | let next_index = current_index + 3; 356 | let prev_index = current_index - 3; 357 | vertex_nodes[i].next_index = next_index; 358 | vertex_nodes[i].prev_index = prev_index; 359 | current_index += 3; 360 | if current_index >= end { 361 | vertex_nodes[i].next_index = start; 362 | } 363 | if current_index <= start { 364 | vertex_nodes[i].prev_index = end; 365 | } 366 | } 367 | 368 | // Set the is_hole and hole_index properties 369 | // if (is_hole) { 370 | // for i in 0..vertex_nodes.len() { 371 | // vertex_nodes[i].is_hole = true; 372 | // vertex_nodes[i].hole_index = 0; 373 | // } 374 | // } else { 375 | // for i in 0..vertex_nodes.len() { 376 | // vertex_nodes[i].is_hole = false; 377 | // vertex_nodes[i].hole_index = -1; 378 | // } 379 | // } 380 | 381 | vertex_nodes 382 | } 383 | -------------------------------------------------------------------------------- /main/opengeometry/src/operations/windingsort.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * Code for sorting the winding order of a given vertices 3 | */ 4 | 5 | use crate::utility::openmath::Vector3D; 6 | 7 | fn compute_signed_area(points: &[Vector3D]) -> f64 { 8 | let n = points.len(); 9 | let mut sum = 0.0; 10 | for i in 0..n { 11 | let p1 = &points[i]; 12 | let p2 = &points[(i + 1) % n]; 13 | sum += p1.x * p2.z - p2.x * p1.z; 14 | } 15 | sum / 2.0 16 | } 17 | 18 | 19 | pub fn ccw_test(raw_points: Vec) -> Vec { 20 | let mut points = raw_points; 21 | let area = compute_signed_area(&points); 22 | if area < 0.0 { 23 | points.reverse(); 24 | } 25 | points 26 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/arc.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/primitives/arc.rs -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/circle.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Circle Curve 3 | * A circle is defined by a center point and a radius. 4 | * Created on XZ plane. 5 | */ 6 | 7 | // TODO: What if we create the Circle using the Formula for Angles. 8 | 9 | use crate::utility::openmath; 10 | use wasm_bindgen::prelude::*; 11 | use serde::{Serialize, Deserialize}; 12 | 13 | #[wasm_bindgen] 14 | #[derive(Clone, Serialize, Deserialize)] 15 | pub struct CircleArc { 16 | id: String, 17 | center: openmath::Vector3D, 18 | radius: f64, 19 | start_angle: f64, 20 | end_angle: f64, 21 | segments: u32, 22 | points: Vec 23 | } 24 | 25 | #[wasm_bindgen] 26 | impl CircleArc { 27 | #[wasm_bindgen(setter)] 28 | pub fn set_id(&mut self, id: String) { 29 | self.id = id; 30 | } 31 | 32 | #[wasm_bindgen(getter)] 33 | pub fn id(&self) -> String { 34 | self.id.clone() 35 | } 36 | 37 | #[wasm_bindgen(constructor)] 38 | pub fn new(id: String) -> CircleArc { 39 | CircleArc { 40 | id, 41 | center: openmath::Vector3D::create(0.0, 0.0, 0.0), 42 | radius: 1.0, 43 | start_angle: 0.0, 44 | end_angle: 2.0 * std::f64::consts::PI, 45 | segments: 32, 46 | points: Vec::new() 47 | } 48 | } 49 | 50 | #[wasm_bindgen] 51 | pub fn clone(&self) -> CircleArc { 52 | CircleArc { 53 | id: self.id.clone(), 54 | center: self.center.clone(), 55 | radius: self.radius, 56 | start_angle: self.start_angle, 57 | end_angle: self.end_angle, 58 | segments: self.segments, 59 | points: self.points.clone() 60 | } 61 | } 62 | 63 | #[wasm_bindgen] 64 | pub fn set_config(&mut self, center: openmath::Vector3D, radius: f64, start_angle: f64, end_angle: f64, segments: u32) { 65 | self.center = center; 66 | self.radius = radius; 67 | self.start_angle = start_angle; 68 | self.end_angle = end_angle; 69 | self.segments = segments; 70 | } 71 | 72 | #[wasm_bindgen] 73 | pub fn generate_points(&mut self) { 74 | let mut angle = self.start_angle; 75 | let angle_diff = (self.end_angle - self.start_angle) / self.segments as f64; 76 | for _ in 0..self.segments + 1 { 77 | let x = self.center.x + self.radius * angle.cos(); 78 | let y = self.center.y; 79 | let z = self.center.z + self.radius * angle.sin(); 80 | self.points.push(openmath::Vector3D::create(x, y, z)); 81 | angle += angle_diff; 82 | } 83 | } 84 | 85 | #[wasm_bindgen] 86 | pub fn update_radius(&mut self, radius: f64) { 87 | self.destroy(); 88 | self.radius = radius; 89 | } 90 | 91 | #[wasm_bindgen] 92 | pub fn update_center(&mut self, center: openmath::Vector3D) { 93 | self.destroy(); 94 | self.center = center; 95 | } 96 | 97 | // Dispose 98 | #[wasm_bindgen] 99 | pub fn dispose_points(&mut self) { 100 | self.points.clear(); 101 | } 102 | 103 | // TODO: Implement Destroy 104 | #[wasm_bindgen] 105 | pub fn destroy(&mut self) { 106 | self.points.clear(); 107 | } 108 | 109 | // Get Points for the Circle 110 | #[wasm_bindgen] 111 | pub fn get_points(&self) -> String { 112 | serde_json::to_string(&self.points).unwrap() 113 | } 114 | 115 | pub fn get_raw_points(&self) -> Vec { 116 | self.points.clone() 117 | } 118 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/cubiod.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/primitives/cubiod.rs -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/curve.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/primitives/curve.rs -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/cylinder.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | use std::clone; 3 | 4 | use crate::operations::extrude::{self, extrude_polygon_by_buffer_geometry}; 5 | use crate::operations::triangulate::triangulate_polygon_by_face; 6 | use crate::operations::windingsort; 7 | use crate::utility::openmath::{Geometry, Vector3D}; 8 | /** 9 | * Copyright (c) 2025, OpenGeometry. All rights reserved. 10 | * Cylinder primitive for OpenGeometry. 11 | * 12 | * Base created by default on XZ plane and etruded along Y axis. 13 | * 14 | * There are two ways to create a cylinder: 15 | * 1. By creating a cylinder with a circle arc, create a Circle Poly Face and then extrude by a given height 16 | * 2. By creating a cylinder primitive with a given radius and height 17 | * 18 | * This class is used to create a cylinder primitive(2) using radius and height. 19 | * */ 20 | 21 | use crate::{utility::openmath}; 22 | use crate::geometry::basegeometry; 23 | use wasm_bindgen::prelude::*; 24 | use serde::{Serialize, Deserialize}; 25 | 26 | #[wasm_bindgen] 27 | #[derive(Clone, Serialize, Deserialize)] 28 | pub struct OGCylinder { 29 | id: String, 30 | center: openmath::Vector3D, 31 | radius: f64, 32 | height: f64, 33 | angle: f64, 34 | segments: u32, 35 | geometry: basegeometry::BaseGeometry, 36 | buffer: Vec, 37 | brep: Geometry, 38 | } 39 | 40 | #[wasm_bindgen] 41 | impl OGCylinder { 42 | #[wasm_bindgen(setter)] 43 | pub fn set_id(&mut self, id: String) { 44 | self.id = id; 45 | } 46 | 47 | #[wasm_bindgen(getter)] 48 | pub fn id(&self) -> String { 49 | self.id.clone() 50 | } 51 | 52 | #[wasm_bindgen(constructor)] 53 | pub fn new(id: String) -> OGCylinder { 54 | OGCylinder { 55 | id: id.clone(), 56 | center: openmath::Vector3D::create(0.0, 0.0, 0.0), 57 | radius: 1.0, 58 | height: 1.0, 59 | angle: 2.0 * std::f64::consts::PI, 60 | segments: 32, 61 | geometry: basegeometry::BaseGeometry::new(id.clone()), 62 | buffer: Vec::new(), 63 | brep: Geometry::new(), 64 | } 65 | } 66 | 67 | #[wasm_bindgen] 68 | pub fn set_config(&mut self, center: openmath::Vector3D, radius: f64, height: f64, angle: f64, segments: u32) { 69 | self.center = center; 70 | self.radius = radius; 71 | self.height = height; 72 | self.angle = angle; 73 | self.segments = segments; 74 | } 75 | 76 | #[wasm_bindgen] 77 | pub fn generate_geometry(&mut self) { 78 | let mut points: Vec = Vec::new(); 79 | let mut normals: Vec = Vec::new(); 80 | // let mut uvs: Vec = Vec::new(); 81 | let mut indices: Vec = Vec::new(); 82 | 83 | 84 | let half_height = self.height / 2.0; 85 | let mut actual_segments: u32 = self.segments; 86 | 87 | // If the end angle makes a full circle then we don't need to add a center point 88 | if self.angle < 2.0 * std::f64::consts::PI { 89 | // Add center point 90 | points.push(openmath::Vector3D::create(self.center.x, self.center.y - half_height, self.center.z)); 91 | actual_segments += 1; 92 | } 93 | 94 | let mut start_angle: f64 = 0.0; 95 | let angle_step = self.angle / self.segments as f64; 96 | for _ in 0..actual_segments { 97 | let x = self.center.x + self.radius * start_angle.cos(); 98 | let y = self.center.y - half_height; 99 | let z = self.center.z + self.radius * start_angle.sin(); 100 | points.push(openmath::Vector3D::create(x, y, z)); 101 | 102 | // // Indices for the top circle 103 | // if i < self.segments - 1 { 104 | // indices.push(i); 105 | // indices.push(i + 1); 106 | // indices.push(self.segments); 107 | // } else { 108 | // indices.push(i); 109 | // indices.push(0); 110 | // indices.push(self.segments); 111 | // } 112 | 113 | start_angle += angle_step; 114 | } 115 | 116 | // Side Faces Indices 117 | 118 | // let ccw_points = windingsort::ccw_test(points.clone()); 119 | // self.geometry.add_vertices(ccw_points.clone()); 120 | let mut clonedpoints = points.clone(); 121 | clonedpoints.reverse(); 122 | self.geometry.add_vertices(clonedpoints); 123 | self.geometry.add_indices(indices); 124 | } 125 | 126 | fn generate_brep(&mut self) -> Geometry { 127 | let extrude_data = extrude_polygon_by_buffer_geometry(self.geometry.clone(), self.height); 128 | self.brep = extrude_data.clone(); 129 | extrude_data 130 | } 131 | 132 | #[wasm_bindgen] 133 | pub fn get_geometry(&mut self) -> String { 134 | let geometry = self.geometry.get_geometry(); 135 | // geometry 136 | let extrude_data = self.generate_brep(); 137 | 138 | let mut local_geometry = Vec::new(); 139 | 140 | // let face = extrude_data.faces[0].clone(); 141 | for face in extrude_data.faces.clone() { 142 | let mut face_vertices: Vec = Vec::new(); 143 | for index in face.clone() { 144 | face_vertices.push(extrude_data.vertices[index as usize].clone()); 145 | } 146 | 147 | let triangulated_face = triangulate_polygon_by_face(face_vertices.clone()); 148 | // let ccw_vertices = windingsort::ccw_test(face_vertices.clone()); 149 | for index in triangulated_face { 150 | for i in index { 151 | let vertex = face_vertices[i as usize].clone(); 152 | // let vertex = ccw_vertices[i as usize]; 153 | local_geometry.push(vertex.x); 154 | local_geometry.push(vertex.y); 155 | local_geometry.push(vertex.z); 156 | } 157 | } 158 | } 159 | 160 | // let face_data_string = serde_json::to_string(&face).unwrap(); // Serialize face_data 161 | // face_data_string 162 | 163 | // let extrude_data_string = serde_json::to_string(&extrude_data).unwrap(); // Serialize extrude_data 164 | // extrude_data_string 165 | 166 | let string_data = serde_json::to_string(&local_geometry).unwrap(); 167 | string_data 168 | } 169 | 170 | #[wasm_bindgen] 171 | pub fn discard_geometry(&mut self) { 172 | // self.geometry.discard_geometry(); 173 | } 174 | 175 | #[wasm_bindgen] 176 | pub fn outline_edges(&mut self) -> String { 177 | let mut outline_points: Vec = Vec::new(); 178 | 179 | for edge in self.brep.edges.clone() { 180 | let start_index = edge[0] as usize; 181 | let end_index = edge[1] as usize; 182 | 183 | let start_point = self.brep.vertices[start_index].clone(); 184 | let end_point = self.brep.vertices[end_index].clone(); 185 | 186 | outline_points.push(start_point.x); 187 | outline_points.push(start_point.y); 188 | outline_points.push(start_point.z); 189 | 190 | outline_points.push(end_point.x); 191 | outline_points.push(end_point.y); 192 | outline_points.push(end_point.z); 193 | } 194 | 195 | let outline_data_string = serde_json::to_string(&outline_points).unwrap(); 196 | outline_data_string 197 | } 198 | 199 | #[wasm_bindgen] 200 | pub fn get_brep_dump(&mut self) -> String { 201 | let brep_data_string = serde_json::to_string(&self.brep).unwrap(); 202 | brep_data_string 203 | } 204 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/poly_line.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Poly Line 3 | * Definition - A Polyline is a connected sequence of line segments created as a single object. 4 | */ 5 | 6 | use crate::utility::openmath; 7 | use wasm_bindgen::prelude::*; 8 | use serde::{Serialize, Deserialize}; 9 | 10 | #[wasm_bindgen] 11 | #[derive(Clone, Serialize, Deserialize)] 12 | pub struct OGPolyLine { 13 | id: String, 14 | points: Vec, 15 | is_closed: bool 16 | } 17 | 18 | #[wasm_bindgen] 19 | impl OGPolyLine { 20 | #[wasm_bindgen(setter)] 21 | pub fn set_id(&mut self, id: String) { 22 | self.id = id; 23 | } 24 | 25 | #[wasm_bindgen(getter)] 26 | pub fn id(&self) -> String { 27 | self.id.clone() 28 | } 29 | 30 | #[wasm_bindgen(constructor)] 31 | pub fn new(id: String) -> OGPolyLine { 32 | OGPolyLine { 33 | id, 34 | points: Vec::new(), 35 | is_closed: false 36 | } 37 | } 38 | 39 | #[wasm_bindgen] 40 | pub fn clone(&self) -> OGPolyLine { 41 | OGPolyLine { 42 | id: self.id.clone(), 43 | points: self.points.clone(), 44 | is_closed: self.is_closed 45 | } 46 | } 47 | 48 | #[wasm_bindgen] 49 | pub fn set_config(&mut self, points: Vec) { 50 | self.points.clear(); 51 | for point in points { 52 | self.points.push(point); 53 | } 54 | 55 | self.check_closed_test(); 56 | } 57 | 58 | #[wasm_bindgen] 59 | pub fn add_point(&mut self, point: openmath::Vector3D) { 60 | self.points.push(point); 61 | self.check_closed_test(); 62 | } 63 | 64 | // Dispose 65 | #[wasm_bindgen] 66 | pub fn dispose_points(&mut self) { 67 | self.points.clear(); 68 | } 69 | 70 | // TODO: Implement Destroy 71 | #[wasm_bindgen] 72 | pub fn destroy(&mut self) { 73 | self.points.clear(); 74 | } 75 | 76 | // Get Points for the Circle 77 | #[wasm_bindgen] 78 | pub fn get_points(&self) -> String { 79 | serde_json::to_string(&self.points).unwrap() 80 | } 81 | 82 | pub fn get_raw_points(&self) -> Vec { 83 | self.points.clone() 84 | } 85 | 86 | #[wasm_bindgen] 87 | pub fn is_closed(&self) -> bool { 88 | self.is_closed 89 | } 90 | 91 | // Simple Check to see if the Polyline is closed 92 | // This can be made better 93 | pub fn check_closed_test(&mut self) { 94 | if self.points.len() > 2 { 95 | if self.points[0].x == self.points[self.points.len() - 1].x && 96 | self.points[0].y == self.points[self.points.len() - 1].y && 97 | self.points[0].z == self.points[self.points.len() - 1].z { 98 | self.is_closed = true; 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/polygon.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::operations::extrude::{extrude_polygon_by_buffer_geometry, extrude_polygon_with_holes}; 4 | use crate::operations::triangulate::triangulate_polygon_buffer_geometry; 5 | use crate::operations::windingsort; 6 | use crate::{geometry, primitives}; 7 | use crate::utility::openmath::{Geometry, Vector3D}; 8 | use crate::{operations::triangulate, utility::openmath}; 9 | use crate::geometry::basegeometry::{self, BaseGeometry}; 10 | use serde_json::ser; 11 | use wasm_bindgen::prelude::*; 12 | use serde::{Serialize, Deserialize}; 13 | 14 | #[wasm_bindgen] 15 | #[derive(Clone, Serialize, Deserialize)] 16 | pub struct OGPolygon { 17 | id: String, 18 | geometry: basegeometry::BaseGeometry, 19 | pub extruded: bool, 20 | pub extruded_height: f64, 21 | pub is_polygon: bool, 22 | pub position: openmath::Vector3D, 23 | pub rotation: openmath::Vector3D, 24 | pub scale: openmath::Vector3D, 25 | buffer: Vec, 26 | variable_geometry: basegeometry::BaseGeometry, 27 | brep: Geometry, 28 | } 29 | 30 | /** 31 | * A Polygon created with a `id` will have a BaseGeometry with same `id`. Feels like a good decision as of now. 32 | */ 33 | 34 | #[wasm_bindgen] 35 | impl OGPolygon { 36 | // Why Getter and Setter - https://github.com/rustwasm/wasm-bindgen/issues/1775 37 | #[wasm_bindgen(setter)] 38 | pub fn set_id(&mut self, id: String) { 39 | self.id = id; 40 | } 41 | 42 | #[wasm_bindgen(getter)] 43 | pub fn id(&self) -> String { 44 | self.id.clone() 45 | } 46 | 47 | // Add the ability to create polygon with list of verticies passed in constructor itself 48 | // as of now use add_vertices method to push all vertices at once 49 | #[wasm_bindgen(constructor)] 50 | pub fn new(id: String) -> OGPolygon { 51 | OGPolygon { 52 | id: id.clone(), 53 | geometry : basegeometry::BaseGeometry::new(id.clone()), 54 | extruded : false, 55 | extruded_height : 0.0, 56 | is_polygon : false, 57 | position : openmath::Vector3D::create(0.0, 0.0, 0.0), 58 | rotation : openmath::Vector3D::create(0.0, 0.0, 0.0), 59 | scale : openmath::Vector3D::create(1.0, 1.0, 1.0), 60 | buffer : Vec::new(), 61 | variable_geometry: basegeometry::BaseGeometry::new(id.clone()), 62 | brep : Geometry::new(), 63 | } 64 | } 65 | 66 | #[wasm_bindgen] 67 | pub fn new_with_circle(circle_arc: primitives::circle::CircleArc) -> OGPolygon { 68 | let mut polygon = OGPolygon::new(circle_arc.id()); 69 | // discard the last point as it is same as the first point 70 | let mut circle_arc_points = circle_arc.get_raw_points(); 71 | circle_arc_points.pop(); 72 | circle_arc_points.reverse(); 73 | polygon.add_vertices(circle_arc_points); 74 | polygon.triangulate(); 75 | polygon 76 | } 77 | 78 | #[wasm_bindgen] 79 | pub fn new_with_rectangle(rectangle: primitives::rectangle::OGRectangle) -> OGPolygon { 80 | let mut polygon = OGPolygon::new(rectangle.id()); 81 | // discard the last point as it is same as the first point 82 | let mut rectangle_points = rectangle.get_raw_points(); 83 | rectangle_points.pop(); 84 | polygon.add_vertices(rectangle_points); 85 | polygon.triangulate(); 86 | polygon 87 | } 88 | 89 | #[wasm_bindgen] 90 | pub fn add_vertices(&mut self, vertices: Vec) { 91 | self.geometry.add_vertices(vertices); 92 | } 93 | 94 | #[wasm_bindgen] 95 | pub fn add_vertex(&mut self, vertex: openmath::Vector3D) { 96 | self.geometry.add_vertex(vertex); 97 | 98 | // If more than 3 vertices are added, then the polygon is created 99 | if self.geometry.get_vertices().len() > 2 { 100 | self.is_polygon = true; 101 | } 102 | } 103 | 104 | #[wasm_bindgen] 105 | pub fn add_holes(&mut self, holes: Vec) { 106 | self.geometry.add_holes(holes); 107 | } 108 | 109 | #[wasm_bindgen] 110 | pub fn triangulate(&mut self) -> String { 111 | self.is_polygon = true; 112 | 113 | let indices = triangulate_polygon_buffer_geometry(self.geometry.clone()); 114 | 115 | // This is important as the current vertices are not in the same order as the indices, Genius Vishwajeet 116 | let ccw_vertices = windingsort::ccw_test(self.geometry.get_vertices()); 117 | 118 | // Should we do this? Store the ccw vertices in the geometry or we CCW the vertices every time we need to use them? 119 | // self.geometry.add_vertices(ccw_vertices.clone()); 120 | 121 | for index in indices { 122 | for i in index { 123 | let vertex = ccw_vertices[i as usize]; 124 | // let vertex = self.geometry.get_vertices()[i as usize]; 125 | self.buffer.push(vertex.x); 126 | self.buffer.push(vertex.y); 127 | self.buffer.push(vertex.z); 128 | } 129 | } 130 | serde_json::to_string(&self.buffer).unwrap() 131 | } 132 | 133 | #[wasm_bindgen] 134 | pub fn triangulate_with_holes_variable_geometry(&mut self, is_ccw: bool) -> String { 135 | // Step 1 - Flatten the geometry - Works 136 | let flat_data = triangulate::flatten_buffer_geometry(self.variable_geometry.clone()); 137 | let vertices = flat_data.vertices.clone(); 138 | let holes = flat_data.holes; 139 | let dimension = flat_data.dimension; 140 | 141 | // Step 2 - Find the left most point in the first hole 142 | let start_index_in_vertices = holes[0] * 3; 143 | let mut end_index_in_vertices = 0; 144 | 145 | if holes.len() > 1 { 146 | end_index_in_vertices = holes[1] * 3; 147 | } else { 148 | // if only one hole is present 149 | end_index_in_vertices = vertices.len() as u32; 150 | } 151 | 152 | let right_most_index = triangulate::find_right_most_point_index(vertices.clone(), start_index_in_vertices, end_index_in_vertices); 153 | 154 | // Step 3 - Find Ray Casting with the outer edges 155 | let right_point = Vector3D::create( 156 | vertices[right_most_index as usize], 157 | vertices[right_most_index as usize + 1], 158 | vertices[right_most_index as usize + 2] 159 | ); 160 | 161 | // Step 4 is inside 162 | let ray_edge = triangulate::check_vertex_collision_with_flat_vertices( 163 | vertices.clone(), 164 | right_point, 165 | 0, 166 | start_index_in_vertices + 1 167 | ); 168 | 169 | let mut new_vertices_processed: Vec = Vec::new(); 170 | // Step 5 - Create Bridge 171 | let bridge_start_index = ray_edge[0][0]; 172 | let bridge_end_index = right_most_index; 173 | let bridge_start = Vector3D::create( 174 | vertices[bridge_start_index as usize], 175 | vertices[bridge_start_index as usize + 1], 176 | vertices[bridge_start_index as usize + 2] 177 | ); 178 | let bridge_end = Vector3D::create( 179 | vertices[bridge_end_index as usize], 180 | vertices[bridge_end_index as usize + 1], 181 | vertices[bridge_end_index as usize + 2] 182 | ); 183 | 184 | let hole_one = self.variable_geometry.get_holes()[0].clone(); 185 | let hole_vertex_nodes = triangulate::create_vertex_nodes(hole_one.clone(), start_index_in_vertices, end_index_in_vertices - 1, true); 186 | 187 | // Before Bridge Vertices 188 | for i in 0..bridge_start_index+3 { 189 | let vertex = vertices[i as usize]; 190 | new_vertices_processed.push(vertex); 191 | } 192 | // Insert Bridge Vertices 193 | let mut vertex_nodes_data: Vec = Vec::new(); 194 | let mut vertex_next_nodes_data: Vec = Vec::new(); 195 | for node in hole_vertex_nodes { 196 | vertex_nodes_data.push(node.vertex.x); 197 | vertex_nodes_data.push(node.vertex.y); 198 | vertex_nodes_data.push(node.vertex.z); 199 | 200 | vertex_next_nodes_data.push(node.next_index as f64); 201 | } 202 | for i in vertex_next_nodes_data.iter() { 203 | let index = *i as usize; 204 | let x = vertices[index]; 205 | let y = vertices[index + 1]; 206 | let z = vertices[index + 2]; 207 | 208 | new_vertices_processed.push(x); 209 | new_vertices_processed.push(y); 210 | new_vertices_processed.push(z); 211 | } 212 | // Start Index Of Hole-Bridge again to complete the loop, i.e. vertex_next_nodes 213 | let start_index_of_bridge_to_hole = vertex_next_nodes_data[0] as usize; 214 | let bridge_to_hole_x = vertices[start_index_of_bridge_to_hole]; 215 | let bridge_to_hole_y = vertices[start_index_of_bridge_to_hole + 1]; 216 | let bridge_to_hole_z = vertices[start_index_of_bridge_to_hole + 2]; 217 | new_vertices_processed.push(bridge_to_hole_x); 218 | new_vertices_processed.push(bridge_to_hole_y); 219 | new_vertices_processed.push(bridge_to_hole_z); 220 | // // Back To Bridge 221 | let bridge_x = vertices[bridge_start_index as usize]; 222 | let bridge_y = vertices[bridge_start_index as usize + 1]; 223 | let bridge_z = vertices[bridge_start_index as usize + 2]; 224 | new_vertices_processed.push(bridge_x); 225 | new_vertices_processed.push(bridge_y); 226 | new_vertices_processed.push(bridge_z); 227 | // After Bridge Vertices 228 | let before_hole_start_index = holes[0] * 3; 229 | for i in bridge_start_index..before_hole_start_index as u32 { 230 | let vertex = vertices[i as usize]; 231 | new_vertices_processed.push(vertex); 232 | } 233 | let mut new_buffergeometry = basegeometry::BaseGeometry::new("new_buffergeometry".to_string()); 234 | let og_vertices: Vec = new_vertices_processed.chunks(3) 235 | .map(|chunk| Vector3D::create(chunk[0], chunk[1], chunk[2])) 236 | .collect(); 237 | new_buffergeometry.add_vertices(og_vertices); 238 | let new_tricut = triangulate_polygon_buffer_geometry(new_buffergeometry.clone()); 239 | // let ccw_vertices = windingsort::ccw_test(new_buffergeometry.get_vertices()); 240 | let mut new_buffer: Vec = Vec::new(); 241 | for index in new_tricut { 242 | for i in index { 243 | // let vertex = ccw_vertices[i as usize]; 244 | let vertex = new_buffergeometry.get_vertices()[i as usize]; 245 | new_buffer.push(vertex.x); 246 | new_buffer.push(vertex.y); 247 | new_buffer.push(vertex.z); 248 | } 249 | } 250 | 251 | let mut reverse_triangles:Vec = Vec::new(); 252 | if is_ccw { 253 | // read the triangles in reverse order but in start from end - 3 to end then push them and 254 | let mut rev_buf = new_buffer.clone(); 255 | rev_buf.reverse(); 256 | let index = 3; 257 | let mut i = 0; 258 | 259 | while i < rev_buf.len() { 260 | let x = rev_buf[i + 2]; 261 | let y = rev_buf[i + 1]; 262 | let z = rev_buf[i]; 263 | reverse_triangles.push(x); 264 | reverse_triangles.push(y); 265 | reverse_triangles.push(z); 266 | 267 | i += index; 268 | } 269 | 270 | new_buffer = reverse_triangles; 271 | } 272 | 273 | let mut data = HashMap::new(); 274 | data.insert("vertices", vertices); 275 | data.insert("holes", holes.into_iter().map(|x| x as f64).collect()); 276 | data.insert("dimension", vec![dimension as f64]); 277 | data.insert("start_index_in_vertices", vec![start_index_in_vertices as f64]); 278 | data.insert("end_index_in_vertices", vec![end_index_in_vertices as f64]); 279 | data.insert("right_most_index", vec![right_most_index as f64]); 280 | data.insert("right_point", vec![right_point.x, right_point.y, right_point.z]); 281 | data.insert("bridge_start", vec![bridge_start.x, bridge_start.y, bridge_start.z]); 282 | data.insert("bridge_end", vec![bridge_end.x, bridge_end.y, bridge_end.z]); 283 | data.insert("new_vertices_processed", new_vertices_processed); 284 | data.insert("vertex_nodes", vertex_nodes_data); 285 | data.insert("vertex_next_nodes", vertex_next_nodes_data); 286 | data.insert("new_buffer", new_buffer); 287 | let mut edge_data: Vec = Vec::new(); 288 | for edge in ray_edge { 289 | for i in edge { 290 | edge_data.push(i as f64); 291 | } 292 | } 293 | data.insert("ray_edge", edge_data); 294 | let string_data = serde_json::to_string(&data).unwrap(); 295 | string_data 296 | } 297 | 298 | #[wasm_bindgen] 299 | pub fn triangulate_with_holes(&mut self) -> String { 300 | // Step 1 - Flatten the geometry - Works 301 | let flat_data = triangulate::flatten_buffer_geometry(self.geometry.clone()); 302 | let vertices = flat_data.vertices.clone(); 303 | let holes = flat_data.holes; 304 | let dimension = flat_data.dimension; 305 | 306 | // Step 2 - Find the left most point in the first hole 307 | let start_index_in_vertices = holes[0] * 3; 308 | let mut end_index_in_vertices = 0; 309 | 310 | if holes.len() > 1 { 311 | end_index_in_vertices = holes[1] * 3; 312 | } else { 313 | // if only one hole is present 314 | end_index_in_vertices = vertices.len() as u32; 315 | } 316 | 317 | let right_most_index = triangulate::find_right_most_point_index(vertices.clone(), start_index_in_vertices, end_index_in_vertices); 318 | 319 | // Step 3 - Find Ray Casting with the outer edges 320 | let right_point = Vector3D::create( 321 | vertices[right_most_index as usize], 322 | vertices[right_most_index as usize + 1], 323 | vertices[right_most_index as usize + 2] 324 | ); 325 | 326 | // Step 4 is inside 327 | let ray_edge = triangulate::check_vertex_collision_with_flat_vertices( 328 | vertices.clone(), 329 | right_point, 330 | 0, 331 | start_index_in_vertices + 1 332 | ); 333 | 334 | let mut new_vertices_processed: Vec = Vec::new(); 335 | // Step 5 - Create Bridge 336 | let bridge_start_index = ray_edge[0][0]; 337 | let bridge_end_index = right_most_index; 338 | let bridge_start = Vector3D::create( 339 | vertices[bridge_start_index as usize], 340 | vertices[bridge_start_index as usize + 1], 341 | vertices[bridge_start_index as usize + 2] 342 | ); 343 | let bridge_end = Vector3D::create( 344 | vertices[bridge_end_index as usize], 345 | vertices[bridge_end_index as usize + 1], 346 | vertices[bridge_end_index as usize + 2] 347 | ); 348 | 349 | let hole_one = self.geometry.get_holes()[0].clone(); 350 | let hole_vertex_nodes = triangulate::create_vertex_nodes(hole_one.clone(), start_index_in_vertices, end_index_in_vertices - 1, true); 351 | 352 | // Before Bridge Vertices 353 | for i in 0..bridge_start_index+3 { 354 | let vertex = vertices[i as usize]; 355 | new_vertices_processed.push(vertex); 356 | } 357 | 358 | // Insert Bridge Vertices 359 | let mut vertex_nodes_data: Vec = Vec::new(); 360 | let mut vertex_next_nodes_data: Vec = Vec::new(); 361 | for node in hole_vertex_nodes { 362 | vertex_nodes_data.push(node.vertex.x); 363 | vertex_nodes_data.push(node.vertex.y); 364 | vertex_nodes_data.push(node.vertex.z); 365 | 366 | vertex_next_nodes_data.push(node.next_index as f64); 367 | } 368 | for i in vertex_next_nodes_data.iter() { 369 | let index = *i as usize; 370 | let x = vertices[index]; 371 | let y = vertices[index + 1]; 372 | let z = vertices[index + 2]; 373 | 374 | new_vertices_processed.push(x); 375 | new_vertices_processed.push(y); 376 | new_vertices_processed.push(z); 377 | } 378 | 379 | // Start Index Of Hole-Bridge again to complete the loop, i.e. vertex_next_nodes 380 | let start_index_of_bridge_to_hole = vertex_next_nodes_data[0] as usize; 381 | let bridge_to_hole_x = vertices[start_index_of_bridge_to_hole]; 382 | let bridge_to_hole_y = vertices[start_index_of_bridge_to_hole + 1]; 383 | let bridge_to_hole_z = vertices[start_index_of_bridge_to_hole + 2]; 384 | new_vertices_processed.push(bridge_to_hole_x); 385 | new_vertices_processed.push(bridge_to_hole_y); 386 | new_vertices_processed.push(bridge_to_hole_z); 387 | 388 | // // Back To Bridge 389 | let bridge_x = vertices[bridge_start_index as usize]; 390 | let bridge_y = vertices[bridge_start_index as usize + 1]; 391 | let bridge_z = vertices[bridge_start_index as usize + 2]; 392 | new_vertices_processed.push(bridge_x); 393 | new_vertices_processed.push(bridge_y); 394 | new_vertices_processed.push(bridge_z); 395 | 396 | // After Bridge Vertices 397 | let before_hole_start_index = holes[0] * 3; 398 | for i in bridge_start_index..before_hole_start_index as u32 { 399 | let vertex = vertices[i as usize]; 400 | new_vertices_processed.push(vertex); 401 | } 402 | 403 | let mut new_buffergeometry = basegeometry::BaseGeometry::new("new_buffergeometry".to_string()); 404 | let og_vertices: Vec = new_vertices_processed.chunks(3) 405 | .map(|chunk| Vector3D::create(chunk[0], chunk[1], chunk[2])) 406 | .collect(); 407 | new_buffergeometry.add_vertices(og_vertices); 408 | let new_tricut = triangulate_polygon_buffer_geometry(new_buffergeometry.clone()); 409 | let ccw_vertices = windingsort::ccw_test(new_buffergeometry.get_vertices()); 410 | let mut new_buffer: Vec = Vec::new(); 411 | 412 | for index in new_tricut { 413 | for i in index { 414 | let vertex = ccw_vertices[i as usize]; 415 | new_buffer.push(vertex.x); 416 | new_buffer.push(vertex.y); 417 | new_buffer.push(vertex.z); 418 | } 419 | } 420 | 421 | let mut data = HashMap::new(); 422 | data.insert("vertices", vertices); 423 | data.insert("holes", holes.into_iter().map(|x| x as f64).collect()); 424 | data.insert("dimension", vec![dimension as f64]); 425 | data.insert("start_index_in_vertices", vec![start_index_in_vertices as f64]); 426 | data.insert("end_index_in_vertices", vec![end_index_in_vertices as f64]); 427 | data.insert("right_most_index", vec![right_most_index as f64]); 428 | data.insert("right_point", vec![right_point.x, right_point.y, right_point.z]); 429 | data.insert("bridge_start", vec![bridge_start.x, bridge_start.y, bridge_start.z]); 430 | data.insert("bridge_end", vec![bridge_end.x, bridge_end.y, bridge_end.z]); 431 | 432 | data.insert("new_vertices_processed", new_vertices_processed); 433 | data.insert("vertex_nodes", vertex_nodes_data); 434 | data.insert("vertex_next_nodes", vertex_next_nodes_data); 435 | 436 | data.insert("new_buffer", new_buffer); 437 | 438 | let mut edge_data: Vec = Vec::new(); 439 | for edge in ray_edge { 440 | for i in edge { 441 | edge_data.push(i as f64); 442 | } 443 | } 444 | data.insert("ray_edge", edge_data); 445 | 446 | serde_json::to_string(&data).unwrap() 447 | } 448 | 449 | #[wasm_bindgen] 450 | pub fn get_buffer_flush(&self) -> String { 451 | serde_json::to_string(&self.buffer).unwrap() 452 | } 453 | 454 | #[wasm_bindgen] 455 | pub fn clear_vertices(&mut self) { 456 | self.geometry.reset_geometry(); 457 | } 458 | 459 | #[wasm_bindgen] 460 | pub fn reset_polygon(&mut self) { 461 | // Reset the geometry 462 | } 463 | 464 | pub fn extrude_by_height_with_holes(&mut self, height: f64) -> String { 465 | // Create a new buffer geometry 466 | // Loop through the extruded faces with holes 467 | // Create Variable Geometry 468 | // Call Triangulate with Holes with Custom Geometry method 469 | // push to the buffer geometry 470 | // Return the buffer geometry 471 | 472 | self.extruded = true; 473 | self.extruded_height = height; 474 | 475 | let mut extrude_data = extrude_polygon_with_holes(self.geometry.clone(), height); 476 | 477 | let mut local_geometry: Vec = Vec::new(); 478 | let faces = extrude_data.faces.clone(); 479 | let all_vertices_raw = extrude_data.vertices.clone(); 480 | let holes = extrude_data.holes.clone(); 481 | let face_holes_map = extrude_data.face_holes_map.clone(); 482 | 483 | let face_length = face_holes_map.keys().len(); 484 | // extrude_data.face_length = face_length; 485 | let mut face_current_index: usize = 0; 486 | 487 | let mut face_map: Vec<_> = face_holes_map.keys().cloned().collect(); 488 | face_map.sort(); 489 | 490 | for face_index in face_map { 491 | let mut variable_geometry: BaseGeometry = BaseGeometry::new("variable_geometry".to_string()); 492 | let face = faces[face_index as usize].clone(); 493 | let mut face_vertices: Vec = Vec::new(); 494 | 495 | for index in face.clone() { 496 | let v_face = all_vertices_raw[index as usize].clone(); 497 | face_vertices.push(v_face); 498 | } 499 | face_vertices.reverse(); 500 | variable_geometry.add_vertices(face_vertices.clone()); 501 | 502 | let mut is_ccw = false; 503 | if (face_current_index > face_length - 2) { 504 | is_ccw = true; 505 | extrude_data.is_ccw_last_face = true; 506 | } 507 | 508 | // get holes for given face 509 | let holes_for_face = face_holes_map.get(&face_index).unwrap(); 510 | if holes_for_face.len() > 0 { 511 | for hole_index in holes_for_face { 512 | let hole = holes[*hole_index as usize].clone(); 513 | variable_geometry.add_holes(hole.clone()); 514 | } 515 | self.variable_geometry = variable_geometry.clone(); 516 | 517 | // last most processed face 518 | extrude_data.face_length = face_index as usize; 519 | 520 | let triangle_data = self.triangulate_with_holes_variable_geometry(is_ccw); 521 | let triangulated_data: HashMap> = serde_json::from_str(&triangle_data).unwrap(); 522 | let vertices = triangulated_data.get("new_buffer").unwrap(); 523 | for i in vertices { 524 | local_geometry.push(*i); 525 | } 526 | } 527 | else { 528 | // If no holes, then just triangulate the face 529 | let triangulated_face = triangulate::triangulate_polygon_by_face(face_vertices.clone()); 530 | for index in triangulated_face { 531 | for i in index { 532 | let vertex = face_vertices[i as usize].clone(); 533 | local_geometry.push(vertex.x); 534 | local_geometry.push(vertex.y); 535 | local_geometry.push(vertex.z); 536 | } 537 | } 538 | } 539 | 540 | // Destroy the variable geometry 541 | variable_geometry.reset_geometry(); 542 | 543 | face_current_index += 1; 544 | } 545 | 546 | // Add BREP 547 | let brep = Geometry { 548 | vertices: extrude_data.vertices.clone(), 549 | faces: extrude_data.faces.clone(), 550 | edges: extrude_data.edges.clone() 551 | }; 552 | self.brep = brep.clone(); 553 | 554 | // let extrude_data_string = serde_json::to_string(&extrude_data).unwrap(); 555 | // extrude_data_string 556 | 557 | serde_json::to_string(&local_geometry).unwrap() 558 | } 559 | 560 | #[wasm_bindgen] 561 | pub fn extrude_by_height(&mut self, height: f64) -> String { 562 | self.extruded = true; 563 | self.extruded_height = height; 564 | 565 | 566 | // If Polygon has holes, use other extrude method 567 | if (self.geometry.get_holes().len() > 0) { 568 | return self.extrude_by_height_with_holes(height); 569 | } 570 | 571 | let extrude_data = extrude_polygon_by_buffer_geometry(self.geometry.clone(), height); 572 | 573 | let mut local_geometry = Vec::new(); 574 | 575 | // let face = extrude_data.faces[0].clone(); 576 | for face in extrude_data.faces.clone() { 577 | let mut face_vertices: Vec = Vec::new(); 578 | for index in face.clone() { 579 | face_vertices.push(extrude_data.vertices[index as usize].clone()); 580 | } 581 | 582 | let triangulated_face = triangulate::triangulate_polygon_by_face(face_vertices.clone()); 583 | // let ccw_vertices = windingsort::ccw_test(face_vertices.clone()); 584 | for index in triangulated_face { 585 | for i in index { 586 | // let vertex = ccw_vertices[i as usize]; 587 | let vertex = face_vertices[i as usize].clone(); 588 | local_geometry.push(vertex.x); 589 | local_geometry.push(vertex.y); 590 | local_geometry.push(vertex.z); 591 | } 592 | } 593 | } 594 | 595 | // let face_data_string = serde_json::to_string(&face).unwrap(); // Serialize face_data 596 | // face_data_string 597 | 598 | // let extrude_data_string = serde_json::to_string(&extrude_data).unwrap(); // Serialize extrude_data 599 | // extrude_data_string 600 | 601 | let string_data = serde_json::to_string(&local_geometry).unwrap(); 602 | string_data 603 | 604 | // ABOVE LINE WORKING 605 | 606 | // // TESTING EDGES OUTLINE 607 | // let mut outline_data: Vec> = Vec::new(); 608 | 609 | // // for edge in extruded_raw.edges { 610 | // // let start = vertices[edge[0] as usize].clone(); 611 | // // let end = vertices[edge[1] as usize].clone(); 612 | 613 | // // let edge_vertices = vec![start, end]; 614 | // // outline_data.push(edge_vertices); 615 | // // } 616 | 617 | // for face in faces { 618 | // let mut face_vertices: Vec = Vec::new(); 619 | // for index in face { 620 | // let v_face = vertices[index as usize].clone(); 621 | // face_vertices.push(v_face); 622 | // } 623 | // outline_data.push(face_vertices); 624 | // } 625 | 626 | // serde_json::to_string(&outline_data).unwrap() 627 | } 628 | 629 | #[wasm_bindgen] 630 | pub fn get_outlines(&self) -> String { 631 | let height = self.extruded_height; 632 | if height == 0.0 { 633 | return "Please extrude the polygon first".to_string(); 634 | } 635 | 636 | let mut outline_data: Vec> = Vec::new(); 637 | let extruded_raw = extrude_polygon_by_buffer_geometry(self.geometry.clone(), height); 638 | let faces = extruded_raw.faces; 639 | let vertices = extruded_raw.vertices; 640 | 641 | for face in faces { 642 | let mut face_vertices: Vec = Vec::new(); 643 | for index in face { 644 | let v_face = vertices[index as usize].clone(); 645 | face_vertices.push(v_face); 646 | } 647 | outline_data.push(face_vertices); 648 | } 649 | serde_json::to_string(&outline_data).unwrap() 650 | } 651 | 652 | #[wasm_bindgen] 653 | pub fn get_geometry(&self) -> String { 654 | let geometry = self.geometry.get_geometry(); 655 | geometry 656 | } 657 | 658 | #[wasm_bindgen] 659 | pub fn get_brep_data(&self) -> String { 660 | let geometry = self.brep.get_geometry(); 661 | geometry 662 | } 663 | 664 | #[wasm_bindgen] 665 | pub fn outline_edges(&mut self) -> String { 666 | let mut outline_points: Vec = Vec::new(); 667 | 668 | for edge in self.brep.edges.clone() { 669 | let start_index = edge[0] as usize; 670 | let end_index = edge[1] as usize; 671 | 672 | let start_point = self.brep.vertices[start_index].clone(); 673 | let end_point = self.brep.vertices[end_index].clone(); 674 | 675 | outline_points.push(start_point.x); 676 | outline_points.push(start_point.y); 677 | outline_points.push(start_point.z); 678 | 679 | outline_points.push(end_point.x); 680 | outline_points.push(end_point.y); 681 | outline_points.push(end_point.z); 682 | } 683 | 684 | let outline_data_string = serde_json::to_string(&outline_points).unwrap(); 685 | outline_data_string 686 | } 687 | } 688 | -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/rectangle.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rectange 3 | * A Simple OGRectangle defined by width and breadth. 4 | * Created on XZ plane. 5 | */ 6 | 7 | use crate::utility::openmath; 8 | use wasm_bindgen::prelude::*; 9 | use serde::{Serialize, Deserialize}; 10 | 11 | #[wasm_bindgen] 12 | #[derive(Clone, Serialize, Deserialize)] 13 | pub struct OGRectangle { 14 | id: String, 15 | center: openmath::Vector3D, 16 | width: f64, 17 | breadth: f64, 18 | points: Vec 19 | } 20 | 21 | #[wasm_bindgen] 22 | impl OGRectangle { 23 | #[wasm_bindgen(setter)] 24 | pub fn set_id(&mut self, id: String) { 25 | self.id = id; 26 | } 27 | 28 | #[wasm_bindgen(getter)] 29 | pub fn id(&self) -> String { 30 | self.id.clone() 31 | } 32 | 33 | #[wasm_bindgen(constructor)] 34 | pub fn new(id: String) -> OGRectangle { 35 | OGRectangle { 36 | id, 37 | center: openmath::Vector3D::create(0.0, 0.0, 0.0), 38 | width: 1.0, 39 | breadth: 1.0, 40 | points: Vec::new() 41 | } 42 | } 43 | 44 | #[wasm_bindgen] 45 | pub fn clone(&self) -> OGRectangle { 46 | OGRectangle { 47 | id: self.id.clone(), 48 | center: self.center.clone(), 49 | width: self.width, 50 | breadth: self.breadth, 51 | points: self.points.clone() 52 | } 53 | } 54 | 55 | #[wasm_bindgen] 56 | pub fn set_config(&mut self, center: openmath::Vector3D, width: f64, breadth: f64) { 57 | self.center = center; 58 | self.width = width; 59 | self.breadth = breadth; 60 | } 61 | 62 | #[wasm_bindgen] 63 | pub fn generate_points(&mut self) { 64 | let half_width = self.width / 2.0; 65 | let half_breadth = self.breadth / 2.0; 66 | 67 | let p1 = openmath::Vector3D::create(self.center.x - half_width, self.center.y, self.center.z - half_breadth); 68 | let p2 = openmath::Vector3D::create(self.center.x + half_width, self.center.y, self.center.z - half_breadth); 69 | let p3 = openmath::Vector3D::create(self.center.x + half_width, self.center.y, self.center.z + half_breadth); 70 | let p4 = openmath::Vector3D::create(self.center.x - half_width, self.center.y, self.center.z + half_breadth); 71 | 72 | self.points.clear(); 73 | self.points.push(p1); 74 | self.points.push(p2); 75 | self.points.push(p3); 76 | self.points.push(p4); 77 | self.points.push(p1); // Close the loop 78 | } 79 | 80 | #[wasm_bindgen] 81 | pub fn update_width(&mut self, width: f64) { 82 | self.destroy(); 83 | self.width = width; 84 | } 85 | 86 | #[wasm_bindgen] 87 | pub fn update_breadth(&mut self, breadth: f64) { 88 | self.destroy(); 89 | self.breadth = breadth; 90 | } 91 | 92 | #[wasm_bindgen] 93 | pub fn update_center(&mut self, center: openmath::Vector3D) { 94 | self.destroy(); 95 | self.center = center; 96 | } 97 | 98 | #[wasm_bindgen] 99 | pub fn dispose_points(&mut self) { 100 | self.points.clear(); 101 | } 102 | 103 | #[wasm_bindgen] 104 | pub fn destroy(&mut self) { 105 | self.points.clear(); 106 | } 107 | 108 | // Get Points for the Circle 109 | #[wasm_bindgen] 110 | pub fn get_points(&self) -> String { 111 | serde_json::to_string(&self.points).unwrap() 112 | } 113 | 114 | pub fn get_raw_points(&self) -> Vec { 115 | self.points.clone() 116 | } 117 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/simple_line.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple Line 3 | * A Line is defined by two points. 4 | * Created on XZ plane. 5 | */ 6 | 7 | use crate::utility::openmath; 8 | use wasm_bindgen::prelude::*; 9 | use serde::{Serialize, Deserialize}; 10 | 11 | #[wasm_bindgen] 12 | #[derive(Clone, Serialize, Deserialize)] 13 | pub struct OGSimpleLine { 14 | id: String, 15 | points: Vec 16 | } 17 | 18 | #[wasm_bindgen] 19 | impl OGSimpleLine { 20 | #[wasm_bindgen(setter)] 21 | pub fn set_id(&mut self, id: String) { 22 | self.id = id; 23 | } 24 | 25 | #[wasm_bindgen(getter)] 26 | pub fn id(&self) -> String { 27 | self.id.clone() 28 | } 29 | 30 | #[wasm_bindgen(constructor)] 31 | pub fn new(id: String) -> OGSimpleLine { 32 | OGSimpleLine { 33 | id, 34 | // No more than 2 points for simple line, else it becomes a polyline 35 | points: Vec::with_capacity(2) 36 | } 37 | } 38 | 39 | #[wasm_bindgen] 40 | pub fn clone(&self) -> OGSimpleLine { 41 | OGSimpleLine { 42 | id: self.id.clone(), 43 | points: self.points.clone() 44 | } 45 | } 46 | 47 | #[wasm_bindgen] 48 | pub fn set_config(&mut self, start: openmath::Vector3D, end: openmath::Vector3D) { 49 | self.points.clear(); 50 | self.points.push(start); 51 | self.points.push(end); 52 | } 53 | 54 | // Dispose 55 | #[wasm_bindgen] 56 | pub fn dispose_points(&mut self) { 57 | self.points.clear(); 58 | } 59 | 60 | // TODO: Implement Destroy 61 | #[wasm_bindgen] 62 | pub fn destroy(&mut self) { 63 | self.points.clear(); 64 | } 65 | 66 | // Get Points for the Circle 67 | #[wasm_bindgen] 68 | pub fn get_points(&self) -> String { 69 | serde_json::to_string(&self.points).unwrap() 70 | } 71 | 72 | pub fn get_raw_points(&self) -> Vec { 73 | self.points.clone() 74 | } 75 | } -------------------------------------------------------------------------------- /main/opengeometry/src/primitives/sphere.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/63312f906bbf79b6015ce678f6d36751dddaaefc/main/opengeometry/src/primitives/sphere.rs -------------------------------------------------------------------------------- /main/opengeometry/src/utility/openmath.rs: -------------------------------------------------------------------------------- 1 | use core::num; 2 | use std::{collections::HashMap, hash::Hash}; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | #[wasm_bindgen] 8 | #[derive(Copy, Clone, Serialize, Deserialize)] 9 | pub struct Vector3D { 10 | pub x: f64, 11 | pub y: f64, 12 | pub z: f64, 13 | } 14 | 15 | #[wasm_bindgen] 16 | impl Vector3D { 17 | #[wasm_bindgen(constructor)] 18 | pub fn create(x: f64, y: f64, z: f64) -> Vector3D { 19 | Vector3D { x, y, z } 20 | } 21 | 22 | pub fn update(&mut self, x: f64, y: f64, z: f64) { 23 | self.x = x; 24 | self.y = y; 25 | self.z = z; 26 | } 27 | 28 | pub fn add(&self, other: &Vector3D) -> Vector3D { 29 | Vector3D { 30 | x: self.x + other.x, 31 | y: self.y + other.y, 32 | z: self.z + other.z 33 | } 34 | } 35 | 36 | pub fn subtract(&self, other: &Vector3D) -> Vector3D { 37 | Vector3D { 38 | x: self.x - other.x, 39 | y: self.y - other.y, 40 | z: self.z - other.z 41 | } 42 | } 43 | 44 | pub fn add_scalar(&self, scalar: f64) -> Vector3D { 45 | Vector3D { 46 | x: self.x + scalar, 47 | y: self.y + scalar, 48 | z: self.z + scalar 49 | } 50 | } 51 | 52 | pub fn subtract_scalar(&self, scalar: f64) -> Vector3D { 53 | Vector3D { 54 | x: self.x - scalar, 55 | y: self.y - scalar, 56 | z: self.z - scalar 57 | } 58 | } 59 | 60 | pub fn add_extrude_in_up(&self, height: f64, up_vector: Vector3D) -> Vector3D { 61 | Vector3D { 62 | x: self.x + up_vector.x * height, 63 | y: self.y + up_vector.y * height, 64 | z: self.z + up_vector.z * height 65 | } 66 | } 67 | 68 | pub fn cross(&self, other: &Vector3D) -> Vector3D { 69 | Vector3D { 70 | x: self.y * other.z - self.z * other.y, 71 | y: self.z * other.x - self.x * other.z, 72 | z: self.x * other.y - self.y * other.x 73 | } 74 | } 75 | 76 | pub fn dot(&self, other: &Vector3D) -> f64 { 77 | self.x * other.x + self.y * other.y + self.z * other.z 78 | } 79 | 80 | pub fn clone(&self) -> Vector3D { 81 | Vector3D { 82 | x: self.x, 83 | y: self.y, 84 | z: self.z 85 | } 86 | } 87 | } 88 | 89 | #[wasm_bindgen] 90 | #[derive(Copy, Clone, Serialize, Deserialize)] 91 | pub struct Matrix3D { 92 | pub m11: f64, pub m12: f64, pub m13: f64, 93 | pub m21: f64, pub m22: f64, pub m23: f64, 94 | pub m31: f64, pub m32: f64, pub m33: f64, 95 | } 96 | 97 | #[wasm_bindgen] 98 | impl Matrix3D { 99 | #[wasm_bindgen(constructor)] 100 | pub fn set( 101 | m11: f64, m12: f64, m13: f64, 102 | m21: f64, m22: f64, m23: f64, 103 | m31: f64, m32: f64, m33: f64, 104 | ) -> Matrix3D { 105 | Matrix3D { m11, m12, m13, m21, m22, m23, m31, m32, m33 } 106 | } 107 | 108 | pub fn add(&self, other: &Matrix3D) -> Matrix3D { 109 | Matrix3D { 110 | m11: self.m11 + other.m11, m12: self.m12 + other.m12, m13: self.m13 + other.m13, 111 | m21: self.m21 + other.m21, m22: self.m22 + other.m22, m23: self.m23 + other.m23, 112 | m31: self.m31 + other.m31, m32: self.m32 + other.m32, m33: self.m33 + other.m33, 113 | } 114 | } 115 | 116 | pub fn subtract(&self, other: &Matrix3D) -> Matrix3D { 117 | Matrix3D { 118 | m11: self.m11 - other.m11, m12: self.m12 - other.m12, m13: self.m13 - other.m13, 119 | m21: self.m21 - other.m21, m22: self.m22 - other.m22, m23: self.m23 - other.m23, 120 | m31: self.m31 - other.m31, m32: self.m32 - other.m32, m33: self.m33 - other.m33, 121 | } 122 | } 123 | } 124 | 125 | #[wasm_bindgen] 126 | #[derive(Copy, Clone, Serialize, Deserialize)] 127 | pub struct ColorRGB { 128 | pub r: u8, 129 | pub g: u8, 130 | pub b: u8, 131 | } 132 | 133 | #[wasm_bindgen] 134 | impl ColorRGB { 135 | #[wasm_bindgen(constructor)] 136 | pub fn new(r: u8, g: u8, b: u8) -> ColorRGB { 137 | ColorRGB { r, g, b } 138 | } 139 | 140 | pub fn to_hex(&self) -> String { 141 | format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b) 142 | } 143 | } 144 | 145 | #[wasm_bindgen] 146 | pub struct Color { 147 | hex: String 148 | } 149 | 150 | #[wasm_bindgen] 151 | impl Color { 152 | #[wasm_bindgen(constructor)] 153 | pub fn new(hex: String) -> Color { 154 | Color { hex } 155 | } 156 | 157 | #[wasm_bindgen] 158 | pub fn to_rgba(&self) -> Result { 159 | let hex = self.hex.trim_start_matches('#'); 160 | let len = hex.len(); 161 | 162 | if len != 6 && len != 8 { 163 | return Err("Hex string must be in the format #RRGGBB or #RRGGBBAA".to_string()); 164 | } 165 | 166 | let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| "Invalid red component")?; 167 | let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| "Invalid green component")?; 168 | let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| "Invalid blue component")?; 169 | 170 | Ok(ColorRGB { r, g, b }) 171 | } 172 | } 173 | 174 | #[derive(Clone, Serialize, Deserialize)] 175 | pub struct Geometry { 176 | pub vertices: Vec, 177 | pub edges: Vec>, 178 | pub faces: Vec>, 179 | } 180 | 181 | impl Geometry { 182 | pub fn new() -> Geometry { 183 | Geometry { 184 | vertices: Vec::new(), 185 | edges: Vec::new(), 186 | faces: Vec::new(), 187 | } 188 | } 189 | 190 | pub fn get_geometry(&self) -> String { 191 | // serialize geometry 192 | let serialized = serde_json::to_string(&self).unwrap(); 193 | serialized 194 | } 195 | } 196 | 197 | 198 | // Brep Geometry with Holes 199 | #[derive(Clone, Serialize, Deserialize)] 200 | pub struct Geometry_Holes { 201 | pub vertices: Vec, 202 | pub edges: Vec>, 203 | pub faces: Vec>, 204 | pub holes: Vec>, 205 | pub face_holes_map: HashMap>, 206 | pub is_ccw_last_face: bool, 207 | pub face_length: usize 208 | } 209 | 210 | impl Geometry_Holes { 211 | pub fn new() -> Geometry_Holes { 212 | Geometry_Holes { 213 | vertices: Vec::new(), 214 | edges: Vec::new(), 215 | faces: Vec::new(), 216 | holes: Vec::new(), 217 | face_holes_map: HashMap::new(), 218 | is_ccw_last_face: false, 219 | face_length: 0, 220 | } 221 | } 222 | 223 | pub fn get_geometry(&self) -> String { 224 | // serialize geometry 225 | let serialized = serde_json::to_string(&self).unwrap(); 226 | serialized 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opengeometry", 3 | "version": "0.0.3", 4 | "description": "Kernel For CAD and Graphics", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "dev": "vite", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "build-three": "rollup -c rollup.config.js", 11 | "build-core": "cd main/opengeometry && wasm-pack build --target web && cargo build --release", 12 | "cpy-wasm-dist": "cpy main/opengeometry/pkg/opengeometry_bg.wasm dist --flat", 13 | "cpy-wasm-dt-dist": "cpy main/opengeometry/pkg/opengeometry_bg.wasm.d.ts dist --flat", 14 | "build": "npm run build-core && npm run build-three && npm run cpy-wasm-dist && npm run cpy-wasm-dt-dist", 15 | "build-local": "npm run build && cpy dist/* ./../openplans/kernel/", 16 | "make-examples": "cpy . ./../../../OpenGeometry-Examples/core/ --cwd=dist" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/OpenGeometry-io/OpenGeometry.git" 21 | }, 22 | "keywords": [ 23 | "Graphics", 24 | "Kernel", 25 | "Geometry", 26 | "CAD", 27 | "web" 28 | ], 29 | "author": "Vishwajeet Vinayak Mane", 30 | "license": "ISC", 31 | "bugs": { 32 | "url": "https://github.com/OpenGeometry-io/OpenGeometry/issues" 33 | }, 34 | "homepage": "https://github.com/OpenGeometry-io/OpenGeometry#readme", 35 | "peerDependencies": { 36 | "three": "^0.168.0" 37 | }, 38 | "dependencies": { 39 | "tsc": "^2.0.4", 40 | "uuid": "^10.0.0" 41 | }, 42 | "devDependencies": { 43 | "@rollup/plugin-babel": "^6.0.4", 44 | "@rollup/plugin-commonjs": "^26.0.1", 45 | "@rollup/plugin-node-resolve": "^15.2.3", 46 | "@rollup/plugin-typescript": "^11.1.6", 47 | "@types/three": "^0.168.0", 48 | "@types/uuid": "^10.0.0", 49 | "rollup": "^4.21.3", 50 | "tslib": "^2.7.0", 51 | "vite": "^6.2.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import typescript from '@rollup/plugin-typescript'; 5 | 6 | export default { 7 | input: 'main/opengeometry-three/index.ts', 8 | output: { 9 | file: 'dist/index.js', 10 | format: 'esm', 11 | name: 'opengeometry', 12 | sourcemap: true, 13 | }, 14 | plugins: [ 15 | nodeResolve(), 16 | commonjs(), 17 | typescript({ 18 | tsconfig: './tsconfig.json', // Use your project's tsconfig 19 | declaration: true, 20 | declarationDir: 'dist/', // Outputs declarations in a specific folder 21 | }), 22 | babel({ 23 | babelHelpers: 'bundled', 24 | exclude: 'node_modules/**', 25 | }), 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenGeometry 5 | 6 | 7 | 12 | 13 | 14 |
17 | 18 | 126 | 127 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es2020", // Use "es2020" for ESM 5 | "outDir": "dist", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, // Add this line for ESM 9 | "moduleResolution": "node", // Change module resolution to "node" for ESM 10 | "baseUrl": ".", // Add this line for ESM 11 | "paths": { 12 | "*": ["main/*"] // Add this line for ESM 13 | } 14 | }, 15 | "include": [ 16 | "main/**/*.ts" 17 | ], 18 | "exclude": [ 19 | "node_modules" 20 | ] 21 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | root: 'test', 5 | server: { 6 | port: 7070 7 | } 8 | }); --------------------------------------------------------------------------------