├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── benchmark ├── regularCurveSampling.js ├── regularSurfaceDerivatives.js └── regularSurfaceSampling.js ├── build ├── .gitignore └── js │ ├── verb.es.js │ ├── verb.js │ ├── verb.min.js │ └── verbHaxe.js ├── buildcpp.hxml ├── buildcs.hxml ├── buildjs.hxml ├── buildphp.hxml ├── buildpython.hxml ├── docs ├── .gitignore ├── README.md ├── custom_theme │ ├── base.html │ └── css │ │ └── base.css ├── docs │ ├── .gitignore │ └── index.md ├── gen.js ├── mkdocs.yml ├── package.json ├── parse.js └── template.md ├── examples ├── conics.html ├── css │ ├── codemirror.css │ ├── colorforth.css │ └── example.css ├── curveByPointInterpolation.html ├── curveClosestPoint.html ├── curveDivide.html ├── curveIntersection.html ├── curveReverse.html ├── curveSplit.html ├── cylindricalSurface.html ├── extrudedSurface.html ├── js │ ├── OrbitControls.js │ ├── codemirror.js │ ├── example.js │ ├── javascript.js │ ├── three.min.js │ ├── threeBasic.js │ ├── verb.min.js │ └── verbToThreeConversion.js ├── loftedSurface.html ├── meshIntersection.html ├── meshSlicing.html ├── revolvedSurface.html ├── surface.html ├── surfaceAdaptiveTessellation.html ├── surfaceBoundaries.html ├── surfaceClosestPoint.html ├── surfaceIntersection.html ├── surfaceIsocurves.html ├── surfaceSplit.html └── sweptSurface.html ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── site ├── css │ └── style.css ├── index.html └── pictures │ ├── conics.png │ ├── curveByPointInterpolation.png │ ├── curveClosestPoint.png │ ├── curveDivide.png │ ├── curveIntersection.png │ ├── curveSplit.png │ ├── cylindricalSurface.png │ ├── extrudedSurface.png │ ├── loftedSurface.png │ ├── meshIntersection.png │ ├── meshSlicing.png │ ├── revolvedSurface.png │ ├── surface.png │ ├── surfaceAdaptiveTessellation.png │ ├── surfaceBoundaries.png │ ├── surfaceClosestPoint.png │ ├── surfaceIntersection.png │ ├── surfaceIsocurves.png │ ├── surfaceSplit.png │ └── sweptSurface.png ├── src ├── support │ ├── footer.js │ └── header.js └── verb │ ├── Verb.hx │ ├── core │ ├── ArrayExtensions.hx │ ├── Binomial.hx │ ├── BoundingBox.hx │ ├── Constants.hx │ ├── Data.hx │ ├── Intersections.hx │ ├── KdTree.hx │ ├── LazyCurveBoundingBoxTree.hx │ ├── LazyMeshBoundingBoxTree.hx │ ├── LazyPolylineBoundingBoxTree.hx │ ├── LazySurfaceBoundingBoxTree.hx │ ├── Mat.hx │ ├── Mesh.hx │ ├── MeshBoundingBoxTree.hx │ ├── Minimizer.hx │ ├── Serialization.hx │ ├── SurfaceBoundingBoxTree.hx │ ├── Trig.hx │ └── Vec.hx │ ├── eval │ ├── Analyze.hx │ ├── Check.hx │ ├── Divide.hx │ ├── Eval.hx │ ├── Intersect.hx │ ├── Make.hx │ ├── Modify.hx │ └── Tess.hx │ ├── exe │ ├── Dispatcher.hx │ ├── ThreadPool.hx │ └── WorkerPool.hx │ └── geom │ ├── Arc.hx │ ├── BezierCurve.hx │ ├── Circle.hx │ ├── ConicalSurface.hx │ ├── CylindricalSurface.hx │ ├── Ellipse.hx │ ├── EllipseArc.hx │ ├── ExtrudedSurface.hx │ ├── ICurve.hx │ ├── ISurface.hx │ ├── Intersect.hx │ ├── Line.hx │ ├── NurbsCurve.hx │ ├── NurbsSurface.hx │ ├── RevolvedSurface.hx │ ├── SphericalSurface.hx │ └── SweptSurface.hx ├── test ├── testCore.js ├── testEval.js └── testGeom.js ├── todo └── verb.iml /.gitignore: -------------------------------------------------------------------------------- 1 | coverage.html 2 | .DS_Store 3 | out/ 4 | .idea/ 5 | build/cs/ 6 | lib-cov 7 | *.seed 8 | *.log 9 | *.csv 10 | *.dat 11 | *.out 12 | *.pid 13 | *.swp 14 | *.swo 15 | node_modules/ 16 | *.VC.db 17 | *.VC.db-wal 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | before_install: 5 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 6 | - sudo apt-get update -qq # run update before installing anything 7 | - sudo apt-get install python-software-properties -y # for the next command 8 | - sudo add-apt-repository ppa:haxe/releases -y # add the ubuntu ppa that contains haxe 9 | - sudo apt-get update # pull info from ppa 10 | - sudo apt-get install haxe -y # install haxe (and neko) 11 | - npm install -g grunt-cli 12 | - mkdir ~/haxelib # create a folder for installing haxelib 13 | - haxelib setup ~/haxelib 14 | - haxelib install promhx 15 | - haxelib install nodejs 16 | install: 17 | - sudo apt-get install -qq g++-4.8 18 | - export CXX="g++-4.8" 19 | - npm install 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Peter Boyer 2014-6 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # verb 2 | 3 | [![NPM version](https://badge.fury.io/js/verb-nurbs.svg)](https://badge.fury.io/js/verb-nurbs) 4 | [![NPM downloads](https://img.shields.io/npm/dw/verb-nurbs)](https://www.npmjs.com/package/verb-nurbs) 5 | [![Stability](https://img.shields.io/badge/stability-stable-success)](https://github.com/emersion/stability-badges#stable) 6 | [![License](https://img.shields.io/github/license/pboyer/verb)](https://github.com/pboyer/verb/blob/master/LICENSE) 7 | 8 | ### Open-source, cross-platform NURBS 9 | 10 | verb is a library for creating and manipulating NURBS surfaces and curves in many languages including JavaScript. 11 | 12 | verb provides advanced tools like derivative evaluation, adaptive tessellation, and intersection. Verb provides a concurrent execution runtime via WebWorkers in modern browsers and thread pools on other platforms and is suitable for use in a datacenter or in the browser. 13 | 14 | ### Platforms 15 | 16 | Using haxe, verb compiles for: 17 | 18 | * JavaScript 19 | * C# 20 | * C++ 21 | * Python 22 | * PHP 23 | 24 | ### Documentation 25 | 26 | For information on building and using verb, **go to the [docs](http://verbnurbs.com/docs)** 27 | 28 | ### JavaScript Quick Start 29 | 30 | You can install verb with 31 | 32 | npm install verb-nurbs 33 | 34 | Pre-compiled JavaScript can be found in [build/js](https://github.com/pboyer/verb/blob/master/build/js). 35 | 36 | In addition, this package is hosted at UNPKG, and can be imported directly into webpages. 37 | ``` 38 | 39 | ``` 40 | 41 | ### Examples 42 | 43 | You'll find many usage examples in the [examples directory](https://github.com/pboyer/verb/blob/master/examples) and amongst the [tests](https://github.com/pboyer/verb/tree/master/test). 44 | 45 | -------------------------------------------------------------------------------- /benchmark/regularCurveSampling.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require('benchmark') 2 | , verb = require('../build/js/verb.js'); 3 | 4 | 5 | var crv = verb.eval.Make.rationalBezierCurve( [[0,0,0], [1,1,1], [2,1,1], [3,1,0]] ); 6 | 7 | module.exports = { 8 | name: 'Regular curve sampling', 9 | tests: { 10 | 'curveRegularSamplePoints (100)': function() { 11 | var p = verb.eval.Eval.curveRegularSamplePoints( crv, 100 ); 12 | }, 13 | 'direct evaluation (100)': function() { 14 | var p = []; 15 | var sp = 1 / 100; 16 | 17 | for (var i = 0; i < 101; i++){ 18 | p.push( verb.eval.Eval.rationalCurvePoint( crv, i*sp ) ) 19 | } 20 | } 21 | } 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /benchmark/regularSurfaceDerivatives.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require('benchmark') 2 | , verb = require('../build/js/verb.js'); 3 | 4 | function getComplexSurface(){ 5 | 6 | var degree = 3 7 | , knots = [0, 0, 0, 0, 0.333, 0.666, 1, 1, 1, 1] 8 | , pts = [ [ [0, 0, -10], [10, 0, 0], [20, 0, 0], [30, 0, 0] , [40, 0, 0], [50, 0, 0] ], 9 | [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] , [40, -10, 0], [50, -10, 0] ], 10 | [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] , [40, -20, -2], [50, -20, 0] ], 11 | [ [0, -30, 0], [10, -30, 0], [20, -30, -23], [30, -30, 0] , [40, -30, 0], [50, -30, 0] ], 12 | [ [0, -40, 0], [10, -40, 0], [20, -40, 0], [30, -40, 4] , [40, -40, -20], [50, -40, 0] ], 13 | [ [0, -50, 12], [10, -50, 0], [20, -50, 0], [30, -50, 0] , [50, -50, 0], [50, -50, -15] ], ] 14 | , wts = [ [ 1, 1, 1, 1, 1, 1], 15 | [ 1, 1, 1, 1, 1, 1], 16 | [ 1, 1, 1, 1, 1, 1], 17 | [ 1, 1, 1, 1, 1, 1], 18 | [ 1, 1, 1, 1, 1, 1], 19 | [ 1, 1, 1, 1, 1, 1] ]; 20 | 21 | pts = verb.eval.Eval.homogenize2d(pts, wts); 22 | 23 | var srfObj = { 24 | degreeU : degree, 25 | degreeV : degree, 26 | knotsU : knots, 27 | knotsV : knots, 28 | controlPoints : pts 29 | }; 30 | 31 | return srfObj; 32 | } 33 | 34 | var complexSurface = getComplexSurface(); 35 | 36 | module.exports = { 37 | name: 'Regular surface derivatives', 38 | tests: { 39 | 'rationalSurfaceRegularSampleDerivatives (80 x 80)': function() { 40 | var ar = verb.eval.Eval.rationalSurfaceRegularSampleDerivatives( complexSurface, 80, 80, 1 ); 41 | }, 42 | 'direct evaluation (80 x 80)': function() { 43 | var ar = []; 44 | var sp = 1 / 80; 45 | 46 | for (var i = 0; i < 81; i++){ 47 | var ari = []; 48 | ar.push(ari); 49 | for (var j = 0; j < 81; j++){ 50 | ari.push( verb.eval.Eval.rationalSurfaceDerivatives( complexSurface, i*sp, j*sp, 1 ) ) 51 | } 52 | } 53 | } 54 | } 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /benchmark/regularSurfaceSampling.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require('benchmark') 2 | , verb = require('../build/js/verb.js'); 3 | 4 | function getComplexSurface(){ 5 | 6 | var degree = 3 7 | , knots = [0, 0, 0, 0, 0.333, 0.666, 1, 1, 1, 1] 8 | , pts = [ [ [0, 0, -10], [10, 0, 0], [20, 0, 0], [30, 0, 0] , [40, 0, 0], [50, 0, 0] ], 9 | [ [0, -10, 0], [10, -10, 10], [20, -10, 10], [30, -10, 0] , [40, -10, 0], [50, -10, 0] ], 10 | [ [0, -20, 0], [10, -20, 10], [20, -20, 10], [30, -20, 0] , [40, -20, -2], [50, -20, 0] ], 11 | [ [0, -30, 0], [10, -30, 0], [20, -30, -23], [30, -30, 0] , [40, -30, 0], [50, -30, 0] ], 12 | [ [0, -40, 0], [10, -40, 0], [20, -40, 0], [30, -40, 4] , [40, -40, -20], [50, -40, 0] ], 13 | [ [0, -50, 12], [10, -50, 0], [20, -50, 0], [30, -50, 0] , [50, -50, 0], [50, -50, -15] ], ] 14 | , wts = [ [ 1, 1, 1, 1, 1, 1], 15 | [ 1, 1, 1, 1, 1, 1], 16 | [ 1, 1, 1, 1, 1, 1], 17 | [ 1, 1, 1, 1, 1, 1], 18 | [ 1, 1, 1, 1, 1, 1], 19 | [ 1, 1, 1, 1, 1, 1] ]; 20 | 21 | pts = verb.eval.Eval.homogenize2d(pts, wts); 22 | 23 | var srfObj = { 24 | degreeU : degree, 25 | degreeV : degree, 26 | knotsU : knots, 27 | knotsV : knots, 28 | controlPoints : pts 29 | }; 30 | 31 | return srfObj; 32 | } 33 | 34 | var complexSurface = getComplexSurface(); 35 | 36 | module.exports = { 37 | name: 'Regular surface sampling', 38 | tests: { 39 | 'surfaceRegularSample (80 x 80)': function() { 40 | verb.eval.Eval.surfaceRegularSamplePoints( complexSurface, 80, 80 ); 41 | }, 42 | 'direct evaluation (80 x 80)': function() { 43 | var ar = []; 44 | var sp = 1 / 80; 45 | 46 | for (var i = 0; i < 81; i++){ 47 | var ari = []; 48 | ar.push(ari); 49 | for (var j = 0; j < 81; j++){ 50 | ari.push( verb.eval.Eval.surfacePoint( complexSurface, i*sp, j*sp ) ) 51 | } 52 | } 53 | } 54 | } 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | cpp 2 | cs 3 | python.py 4 | php 5 | java 6 | -------------------------------------------------------------------------------- /build/js/verb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Index / Entry point for the VERB-NURBS package. 3 | */ 4 | 5 | // provide window as required by the promhx library 6 | if (typeof window === "undefined") { 7 | var window = global; 8 | } 9 | 10 | // setup the promise handling for async method invocations 11 | const lookup = function(className, methodName){ 12 | var obj = { verb: verb }; 13 | 14 | className.split(".").forEach(function(x){ 15 | if (obj) obj = obj[ x ]; 16 | }); 17 | 18 | if (!obj) return null; 19 | 20 | return obj[ methodName ]; 21 | } 22 | 23 | const onmessage = function( e ) { 24 | if (!e.data || !e.data.className || !e.data.methodName) return; 25 | 26 | const method = lookup( e.data.className, e.data.methodName ); 27 | 28 | if (!method){ 29 | return console.error("could not find " + e.data.className + "." + e.data.methodName) 30 | } 31 | 32 | postMessage( { result: method.apply( null, e.data.args ), id: e.data.id } ); 33 | } 34 | 35 | 36 | // provide the Worker class for async library functions 37 | if (typeof Worker === "undefined") { 38 | var Worker = require("web-worker") 39 | } 40 | 41 | // import and re-export the Javascript library 42 | const verb = require('./verbHaxe.js'); 43 | 44 | module.exports = verb 45 | -------------------------------------------------------------------------------- /buildcpp.hxml: -------------------------------------------------------------------------------- 1 | -lib promhx 2 | -main verb.Verb 3 | -cp src 4 | -cpp build/cpp 5 | -dce std 6 | -D dll 7 | -------------------------------------------------------------------------------- /buildcs.hxml: -------------------------------------------------------------------------------- 1 | -lib promhx 2 | -main verb.Verb 3 | -cp src 4 | -cs build/cs 5 | -dce std 6 | -D dll -------------------------------------------------------------------------------- /buildjs.hxml: -------------------------------------------------------------------------------- 1 | -lib promhx 2 | -main verb.Verb 3 | -cp src 4 | -js build/js/verbHaxe.js 5 | -dce std -------------------------------------------------------------------------------- /buildphp.hxml: -------------------------------------------------------------------------------- 1 | -lib promhx 2 | -main verb.Verb 3 | -cp src 4 | -php build/php 5 | -dce std 6 | -D dll 7 | -------------------------------------------------------------------------------- /buildpython.hxml: -------------------------------------------------------------------------------- 1 | -lib promhx 2 | -main verb.Verb 3 | -cp src 4 | -python build/verb.py 5 | -dce std 6 | -D dll 7 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | site 3 | node_modules 4 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Dependencies 2 | 3 | Install [mkdocs](http://www.mkdocs.org/#installation) and [node.js](https://nodejs.org/en/download/) 4 | 5 | Install node dependencies: 6 | 7 | ``` 8 | > npm install 9 | ``` 10 | 11 | ## Building 12 | 13 | You'll need to generate all of the md files, which are generated from the .hx files and transported into the docs directory. 14 | 15 | ``` 16 | > node gen.js 17 | ``` 18 | 19 | Use mkdocs to generate the static html: 20 | 21 | ``` 22 | > mkdocs build 23 | ``` 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/custom_theme/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | verb docs - {{ current_page.title }} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 54 | 55 |
56 |
57 | 65 |
66 | 67 |
68 | {{ content }} 69 |
70 |
71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/custom_theme/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: 'Source Sans Pro', sans-serif; 4 | font-size: 18px; 5 | } 6 | 7 | .row, .col { overflow: hidden; position: absolute; } 8 | .row { left: 0; right: 0; } 9 | .col { top: 0; bottom: 0; } 10 | .scroll-x { overflow-x: auto; } 11 | .scroll-y { overflow-y: auto; } 12 | 13 | #header { 14 | overflow: visible; 15 | height: 65px; 16 | background: #444; 17 | z-index: 2; 18 | box-shadow: 2px 0px 10px rgba(0,0,0,0.5); 19 | } 20 | 21 | #search { 22 | float: right; 23 | position: relative; 24 | z-index: 0; 25 | } 26 | 27 | #title { 28 | position: absolute; 29 | top: 10px; 30 | left: 380px; 31 | right: 300px; 32 | font-weight: 300; 33 | text-align: center; 34 | font-size: 30px; 35 | color: #ccc; 36 | pointer-events: none; 37 | } 38 | 39 | #title span { 40 | font-weight: 400; 41 | } 42 | 43 | #site { 44 | float: right; 45 | padding-top: 20px; 46 | } 47 | 48 | #site a { 49 | text-decoration: none; 50 | color: #aaa; 51 | padding: 10px 22px; 52 | } 53 | 54 | #site a:hover { 55 | color: white; 56 | } 57 | 58 | #search input { 59 | padding: 15px; 60 | font-size: 18px; 61 | width: 200px; 62 | margin: 0; 63 | border: none; 64 | box-size: border-box; 65 | height: 65px; 66 | font-family: 'Source Sans Pro', sans-serif; 67 | outline-width: 0; 68 | background: #eee; 69 | } 70 | 71 | #search input:focus { 72 | background: white; 73 | } 74 | 75 | #search input:focus + div { 76 | display: block; 77 | } 78 | 79 | #mkdocs-search-results:hover { 80 | display: block; 81 | } 82 | 83 | #mkdocs-search-results { 84 | z-index: 1; 85 | display: none; 86 | box-size: border-box; 87 | position: absolute; 88 | top: 65px; 89 | left: -200px; 90 | width: 400px; 91 | background: #333; 92 | color: white; 93 | font-size: 18px; 94 | max-height: 600px; 95 | overflow-y: auto; 96 | box-shadow: -2px 5px 10px rgba(0,0,0,0.5); 97 | } 98 | 99 | #mkdocs-search-results article { 100 | padding: 5px; 101 | } 102 | 103 | #mkdocs-search-results article:hover { 104 | background: #444; 105 | } 106 | 107 | #mkdocs-search-results p { 108 | padding: 5px; 109 | } 110 | 111 | #mkdocs-search-results a { 112 | color: white; 113 | } 114 | 115 | #mkdocs-search-results article h3 { 116 | padding: 5px; 117 | margin: 0; 118 | } 119 | 120 | #mkdocs-search-results article p { 121 | padding: 0; 122 | color: #888; 123 | } 124 | 125 | #nav { 126 | display: inline-block; 127 | } 128 | 129 | #nav .nav-list { 130 | padding: 0; 131 | margin: 0; 132 | list-style-type: none; 133 | border: 0; 134 | font-size: 0; 135 | } 136 | 137 | .nav-major a { 138 | color: white; 139 | text-decoration: none; 140 | } 141 | 142 | .nav-major:nth-of-type(1) { 143 | background: #444; 144 | } 145 | 146 | .nav-major:nth-of-type(2) { 147 | background: #DD5500; 148 | } 149 | 150 | .nav-major:nth-of-type(3) { 151 | background: #B10064; 152 | } 153 | 154 | .nav-major:nth-of-type(4) { 155 | background: #008E6E; 156 | } 157 | 158 | .nav-major:nth-of-type(5) { 159 | background: #89CE00; 160 | } 161 | 162 | #nav .nav-major { 163 | text-shadow: 0px 1px #000000; 164 | color: white; 165 | box-sizing: border-box; 166 | height: 65px; 167 | text-transform: lowercase; 168 | font-size: 24px; 169 | border: 0; 170 | padding: 15px 15px; 171 | margin: 0; 172 | display: inline-block; 173 | list-style-type: none; 174 | position: relative; 175 | } 176 | 177 | #nav .nav-major:hover { 178 | background: #333; 179 | } 180 | 181 | #nav .nav-major:hover .nav-minor { 182 | display: inline-block; 183 | } 184 | 185 | #nav .nav-minor { 186 | box-shadow: 2px 2px 10px rgba(0,0,0,0.5); 187 | text-transform: none; 188 | position: absolute; 189 | z-index: 1; 190 | top: 65px; 191 | left: 0; 192 | border: 0; 193 | padding: 0; 194 | margin: 0; 195 | display: none; 196 | list-style-type: none; 197 | } 198 | 199 | #nav .nav-minor-minor { 200 | padding: 0; 201 | margin: 0; 202 | list-style-type: none; 203 | } 204 | 205 | #nav .nav-minor-minor a { 206 | box-sizing: border-box; 207 | background: #333; 208 | padding: 10px; 209 | display: block; 210 | width: 100%; 211 | margin: 0; 212 | font-size: 18px; 213 | } 214 | 215 | #nav .nav-minor-minor a:hover { 216 | background: #444; 217 | } 218 | 219 | #body { 220 | top: 65px; 221 | bottom: 0; 222 | } 223 | 224 | #toc { 225 | width: 300px; 226 | color: #222; 227 | text-shadow: 0px 1px white; 228 | background: #fafafa; 229 | } 230 | 231 | #toc h3 { 232 | text-transform: uppercase; 233 | padding: 5px 10px; 234 | color: #333; 235 | } 236 | 237 | #toc .toc-major { 238 | background: #ddd; 239 | } 240 | 241 | #toc ul { 242 | background: #e8e8e8; 243 | padding: 0; 244 | margin: 0; 245 | list-style-type: none; 246 | } 247 | 248 | #toc li { 249 | padding: 0; 250 | margin: 0; 251 | } 252 | 253 | #toc li a { 254 | display: block; 255 | width: 300px; 256 | padding: 15px; 257 | border-bottom: 1px solid #ddd; 258 | } 259 | 260 | #toc a:hover { 261 | background: #fafafa; 262 | } 263 | 264 | #toc a { 265 | text-decoration: none; 266 | color: #222; 267 | } 268 | 269 | #content { 270 | left: 300px; 271 | right: 0; 272 | padding: 20px; 273 | font-size: 20px; 274 | color: #555; 275 | box-shadow: 0px -2px 10px rgba(0,0,0,0.5); 276 | } 277 | 278 | #content a { 279 | color: #444; 280 | } 281 | 282 | #content a:hover { 283 | color: #aaa; 284 | } 285 | 286 | #content h1 { 287 | text-shadow: 0px 1px #000000; 288 | font-size: 60px; 289 | padding-bottom: 0px; 290 | padding-top: 20px; 291 | } 292 | 293 | #content h1:nth-of-type(1){ 294 | padding-top: 0; 295 | margin-top: 0; 296 | } 297 | 298 | #content h2 { 299 | padding-top: 15px; 300 | font-size: 40px; 301 | padding-bottom: 0px; 302 | margin-bottom: 10px; 303 | } 304 | 305 | code { 306 | color: #888; 307 | } -------------------------------------------------------------------------------- /docs/docs/.gitignore: -------------------------------------------------------------------------------- 1 | core 2 | geom 3 | topo 4 | eval 5 | exe 6 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | # verb 2 | 3 | ## What is verb? 4 | 5 | verb is a library for creating and manipulating NURBS surfaces and curves in many languages including JavaScript. 6 | 7 | verb provides advanced tools like derivative evaluation, adaptive tessellation, and intersection. Verb provides a 8 | concurrent execution runtime via WebWorkers in modern browsers and thread pools on other platforms and is suitable 9 | for use in a datacenter or in the browser. 10 | 11 | verb is not a graphics library. It nicely compliments web graphics libraries like three.js, but is happy to live without 12 | one. 13 | 14 | ## How is verb organized? 15 | 16 | The library is split into four parts: 17 | 18 | * **verb.geom** - Geometric types that make it easy to work with NURBS. Includes many commonly used curve and surface types. 19 | * **verb.core** - Mathematical and geometric utilities that are shared throughout the library. 20 | * **verb.eval** - Fundamental NURBS algorithms for evaluation, intersection, analysis, and more. 21 | * **verb.exe** - Tools for managing multi-core evaluation on different platforms. 22 | 23 | **Most people should just use verb.geom.** The other parts provide more powerful features and more direct access to 24 | core parts of the library. 25 | 26 | ## Platforms 27 | 28 | verb compiles for: 29 | 30 | * JavaScript 31 | * C# 32 | * C++ 33 | * Python 34 | * PHP 35 | 36 | ## Getting started 37 | 38 | Install haxe and node.js 39 | 40 | You should have installed **haxe** and **node.js**. 41 | 42 | haxe --version 43 | 44 | node --version 45 | 46 | Install node.js dependencies: 47 | 48 | npm install 49 | 50 | Install haxe dependencies: 51 | 52 | haxelib install promhx 53 | 54 | ## Using with JavaScript 55 | 56 | You can install verb with npm 57 | 58 | npm install verb-nurbs 59 | 60 | And immediately use the pre-compiled JavaScript packages found in [build/js](https://github.com/pboyer/verb/blob/master/build/js). 61 | 62 | You can use verb with require.js (AMD) or browserify (commonjs) in the browser or in node.js. 63 | 64 | You'll find many usage examples in the [examples directory](https://github.com/pboyer/verb/blob/master/examples). 65 | 66 | ## Compiling for JavaScript 67 | 68 | Compile to javascript: 69 | 70 | npm run build 71 | 72 | Create both ES and UMD versions of the verb package: 73 | 74 | npm run package 75 | 76 | Run all unit tests: 77 | 78 | npm run test 79 | 80 | ## Compiling for C# 81 | 82 | If you're using OS X, ensure that you have installed XCode 7. 83 | 84 | Install [mono](http://www.mono-project.com/docs/getting-started/install/) 85 | 86 | Install hxcs: 87 | 88 | haxelib install hxcs 89 | 90 | Build: 91 | 92 | haxe buildcs.hxml 93 | 94 | Output goes to build/cs. 95 | 96 | ## Compiling for C++ 97 | 98 | If you're using OS X, ensure that you have installed XCode 7. 99 | 100 | Install hxcpp: 101 | 102 | haxelib install hxcpp 103 | 104 | Build: 105 | 106 | haxe buildcpp.hxml 107 | 108 | Output goes to build/cpp. 109 | 110 | ## Compiling for Python 111 | 112 | Make sure python is installed. 113 | 114 | Build: 115 | 116 | haxe buildpython.hxml 117 | 118 | Output goes to build/verb.py 119 | 120 | ## Compiling for PHP 121 | 122 | Build: 123 | 124 | haxe buildphp.hxml 125 | 126 | Output goes to build/php 127 | 128 | ## Compiling for other platforms 129 | 130 | Haxe compiles for other platforms, including Flash and Neko. I have not tried to compile for these platforms - [would you like to contribute?](http://github.com/pboyer/verb) 131 | 132 | ### Compiling for Java 133 | 134 | verb does not currently compile for Java. It probably wouldn't be hard to do - [would you like to contribute?](http://github.com/pboyer/verb) 135 | 136 | ## Using verb with three.js 137 | 138 | You can find good examples of using verb with three in the examples directory. There's an example code showing how 139 | to convert verb geometric types in to three.js Meshes and Lines [in the verb examples](https://github.com/pboyer/verb/blob/master/examples/js/verbToThreeConversion.js). 140 | -------------------------------------------------------------------------------- /docs/gen.js: -------------------------------------------------------------------------------- 1 | var glob = require('glob') 2 | , mkdirp = require('mkdirp') 3 | , fs = require('fs') 4 | , path = require('path') 5 | , rimraf = require('rimraf') 6 | , _ = require('underscore') 7 | , parseHaxe = require('./parse').parse; 8 | 9 | // 10 | // CONSTANTS 11 | // 12 | 13 | var REPO_URL = "https://github.com/pboyer/verb/"; 14 | var TARGET_DIR = __dirname + "/docs"; 15 | var SRC = path.resolve(__dirname, "..", "src/verb"); 16 | var HX_GLOB = SRC + "/*/*.hx"; 17 | var HX_EXT = ".hx"; 18 | var MD_EXT = ".md"; 19 | var TEMPLATE_SRC = fs.readFileSync(path.join(__dirname, "template.md"), "utf8"); 20 | var TEMPLATE = _.template( TEMPLATE_SRC ); 21 | 22 | // 23 | // THE ACTUAL SCRIPT 24 | // 25 | 26 | // Remove the existing docs directory, if present 27 | 28 | if (fs.existsSync(TARGET_DIR)) { 29 | console.log("REMOVING TARGET DIRS: ", TARGET_DIR); 30 | 31 | getDirectories(TARGET_DIR) 32 | .map((d) => path.join(TARGET_DIR, d)) 33 | .forEach( rmdir ); 34 | } 35 | 36 | // Get all of the hx files, and generate their md files 37 | 38 | glob(HX_GLOB, (er, files) => { 39 | 40 | // for each file, generate the corresponding doc file 41 | files.forEach((srcfn) => { 42 | // remove the trailing file name 43 | var cleanfn = srcfn.slice( SRC.length ).slice(0, -HX_EXT.length); 44 | 45 | // build the name of the output file 46 | var outputfn = TARGET_DIR + cleanfn + MD_EXT; 47 | 48 | // make the doc file 49 | gen( srcfn, outputfn ); 50 | }); 51 | }); 52 | 53 | // 54 | // HELPERS 55 | // 56 | 57 | function gen(srcfn, outputfn) { 58 | console.log("GEN: ", outputfn); 59 | 60 | // make the directory (doing nothing if it already exists) 61 | mkdirp.sync( path.dirname( outputfn ) ); 62 | 63 | // compile the doc file 64 | compile(srcfn, outputfn); 65 | }; 66 | 67 | function compile(srcfn, outputfn){ 68 | 69 | var parsed = parseHaxe(srcfn); 70 | if (parsed.length === 0) return; 71 | 72 | var stream = fs.createWriteStream(outputfn); 73 | 74 | // build the url of the file on github 75 | var index0 = srcfn.indexOf("src/verb"); 76 | var fn = srcfn.slice( index0 ); 77 | var loc = REPO_URL + "blob/master/" + fn + "/"; // verb/Verb.hx/ 78 | 79 | var sfn = fn.slice(9, -3); 80 | var index1 = sfn.lastIndexOf("/"); 81 | var name = sfn.slice(0, index1); 82 | var index2 = name.lastIndexOf("/"); 83 | var namespace = name.slice(0, index1); 84 | 85 | writeln(stream,TEMPLATE({ types: parsed, sourceFile: loc, fn : fn, namespace : namespace })); 86 | stream.end(); 87 | } 88 | 89 | function writeln(stream, text){ 90 | return stream.write(text+ "\n"); 91 | } 92 | 93 | function rmdir(dirpath){ 94 | console.log("REMOVING DIRECTORY: ", dirpath); 95 | rimraf.sync(dirpath); 96 | } 97 | 98 | function getDirectories(srcpath) { 99 | return fs.readdirSync(srcpath).filter((file) => { 100 | return fs.statSync(path.join(srcpath, file)).isDirectory(); 101 | }); 102 | } 103 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Verb 2 | theme_dir: custom_theme 3 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gen.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "glob": "^5.0.14", 16 | "mkdirp": "^0.5.1", 17 | "rimraf": "^2.4.3", 18 | "underscore": "^1.8.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/template.md: -------------------------------------------------------------------------------- 1 | <% _.each(types, function(x) { %> 2 | 3 | # <%= x.name %> 4 | `verb.<%= namespace + "." + x.name %>` 5 | 6 | **<%= x.$type.toUpperCase() %>** 7 | 8 | [Source code](<%= sourceFile + "#L" + x.line %>) 9 | 10 | <%= x.$type.toUpperCase() === "TYPEDEF" && x.alias ? "`" + x.name + " = " + x.alias + "`" : "" %> 11 | 12 | <%= x.description %> 13 | 14 | <% if (x.$type === "Class" || x.$type === "Interface") { %> 15 | 16 | <% if (x.parentClass) { %> 17 | **Extends:** [<%= x.parentClass %>]( <%= x.parentClass %>) 18 | <% } %> 19 | 20 | <% if (x.interfaces && x.interfaces.length) { %> 21 | **Implements:** <%= _.map(x.interfaces, function(x){ return "[`" + x + "`](" + x + ")"}).join(", ") %> 22 | <% } %> 23 | 24 | <% _.each(x.methods, function(y) { %> 25 | ## <%= y.name === "new" ? "constructor" : y.name %> 26 | **<%= y.isStatic ? "STATIC " : "" %>METHOD** 27 | 28 | [Source code](<%= sourceFile + "#L" + y.line %>) 29 | 30 | `<%= y.name === "new" ? y.name + " " + x.name : y.name %>(<%= _.map(y.args,function(x){ return x.name + " : " + x.type }).join(", ") %>) <% if (y.returnType) { %>: <%= y.returnType %><% } %>` 31 | 32 | <%= y.description %> 33 | <% }) %> 34 | 35 | <% _.each(x.properties, function(y) { %> 36 | ## <%= y.name %> 37 | **<%= y.isStatic ? "STATIC " : "" %>PROPERTY** 38 | 39 | [Source code](<%= sourceFile + "#L" + y.line %>) 40 | 41 | `<%= y.name %> <% if (y.type) { %>: <%=y.type %><% } %><% if (y.defaultValue) { %> = <%=y.defaultValue %><% } %>` 42 | 43 | <%= (y.description || "").replace(/\n\+/g,"\n*") %> 44 | <% }) %> 45 | 46 | <% } %> 47 | 48 | <% }) %> 49 | -------------------------------------------------------------------------------- /examples/conics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Conics 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/css/colorforth.css: -------------------------------------------------------------------------------- 1 | .cm-s-colorforth.CodeMirror { background: none; color: #f8f8f8; } 2 | .cm-s-colorforth .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } 3 | .cm-s-colorforth .CodeMirror-guttermarker { color: #FFBD40; } 4 | .cm-s-colorforth .CodeMirror-guttermarker-subtle { color: #78846f; } 5 | .cm-s-colorforth .CodeMirror-linenumber { color: #bababa; } 6 | .cm-s-colorforth .CodeMirror-cursor { border-left: 1px solid white; } 7 | 8 | .cm-s-colorforth span.cm-comment { color: #ededed; } 9 | .cm-s-colorforth span.cm-def { color: #ff1c1c; font-weight:bold; } 10 | .cm-s-colorforth span.cm-keyword { color: #ffd900; } 11 | .cm-s-colorforth span.cm-builtin { color: #00d95a; } 12 | .cm-s-colorforth span.cm-variable { color: #73ff00; } 13 | .cm-s-colorforth span.cm-string { color: #007bff; } 14 | .cm-s-colorforth span.cm-number { color: #00c4ff; } 15 | .cm-s-colorforth span.cm-atom { color: #606060; } 16 | 17 | .cm-s-colorforth span.cm-variable-2 { color: #EEE; } 18 | .cm-s-colorforth span.cm-variable-3 { color: #DDD; } 19 | .cm-s-colorforth span.cm-property {} 20 | .cm-s-colorforth span.cm-operator {} 21 | 22 | .cm-s-colorforth span.cm-meta { color: yellow; } 23 | .cm-s-colorforth span.cm-qualifier { color: #FFF700; } 24 | .cm-s-colorforth span.cm-bracket { color: #cc7; } 25 | .cm-s-colorforth span.cm-tag { color: #FFBD40; } 26 | .cm-s-colorforth span.cm-attribute { color: #FFF700; } 27 | .cm-s-colorforth span.cm-error { color: #f00; } 28 | 29 | .cm-s-colorforth div.CodeMirror-selected { background: #333d53; } 30 | 31 | .cm-s-colorforth span.cm-compilation { background: rgba(255, 255, 255, 0.12); } 32 | 33 | .cm-s-colorforth .CodeMirror-activeline-background { background: #253540; } 34 | -------------------------------------------------------------------------------- /examples/css/example.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: 'Source Sans Pro', sans-serif; 4 | color: white; 5 | } 6 | 7 | .row, .col { overflow: hidden; position: absolute; } 8 | .row { left: 0; right: 0; } 9 | .col { top: 0; bottom: 0; } 10 | .scroll-x { overflow-x: auto; } 11 | .scroll-y { overflow-y: auto; } 12 | 13 | #viewer { 14 | left: 0; 15 | right: 0; 16 | } 17 | 18 | #button { 19 | font-size: 15px; 20 | color: white; 21 | position: absolute; 22 | background: green; 23 | bottom: 20px; 24 | padding: 10px; 25 | left: 20px; 26 | z-index: 2; 27 | } 28 | 29 | #button:hover{ 30 | background: grey; 31 | } 32 | 33 | #title { 34 | font-size: 40px; 35 | position: absolute; 36 | top: 20px; 37 | left: 20px; 38 | text-shadow: 0px 2px black; 39 | } 40 | 41 | #code-container { 42 | padding: 20px; 43 | background: rgba(0,0,0,0.75); 44 | right: 0; 45 | left: 0; 46 | } 47 | 48 | #code { 49 | font-size: 10px; 50 | padding: 10px; 51 | margin: 10px; 52 | top: 80px; 53 | bottom: 0; 54 | resize: none; 55 | width: 650px; 56 | border: 0; 57 | } 58 | 59 | .CodeMirror { 60 | height: auto; 61 | } -------------------------------------------------------------------------------- /examples/curveByPointInterpolation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Curve by Point Interpolation 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/curveClosestPoint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Curve Closest Point 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/curveDivide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Divide A Curve By Equal Arc Length 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/curveIntersection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Curve-Curve Intersection 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/curveReverse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Curve Reversal 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/curveSplit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Splitting a Curve 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/cylindricalSurface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cylindrical Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/extrudedSurface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extruded Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/js/example.js: -------------------------------------------------------------------------------- 1 | 2 | var codecontainer = document.getElementById("code-container"); 3 | codecontainer.innerHTML += ''; 4 | 5 | var title = document.getElementById("title"); 6 | title.innerHTML += document.title; 7 | 8 | var code = document.getElementById("code"); 9 | code.innerHTML = document.getElementById("script").innerHTML; 10 | 11 | var codemirror = CodeMirror.fromTextArea(code, { 12 | mode : "javascript", 13 | readOnly : true, 14 | "theme" : "colorforth" 15 | }); 16 | 17 | codecontainer.style.display='none'; 18 | 19 | var button = document.getElementById("button"); 20 | var isHidden = true; 21 | button.addEventListener("click", function(){ 22 | if (isHidden){ 23 | codecontainer.style.display='block'; 24 | isHidden = false; 25 | } else { 26 | codecontainer.style.display='none'; 27 | isHidden = true; 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /examples/js/threeBasic.js: -------------------------------------------------------------------------------- 1 | // Some helper methods for the examples 2 | 3 | var scene, camera, renderer; 4 | 5 | var raycaster = new THREE.Raycaster(); 6 | var mouse = new THREE.Vector2(); 7 | 8 | raycaster.lineprecision = 0.00001; 9 | raycaster.precision = 0.001; 10 | 11 | function onMouseMove( event ) { 12 | 13 | // calculate mouse position in normalized device coordinates 14 | // (-1 to +1) for both components 15 | 16 | mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; 17 | mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; 18 | 19 | } 20 | 21 | window.addEventListener( 'mousemove', onMouseMove, false ); 22 | 23 | function setupScene(doUseRaycaster){ 24 | useRaycaster = doUseRaycaster; 25 | 26 | scene = new THREE.Scene(); 27 | 28 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); 29 | camera.lookAt(20,-20,0); 30 | camera.position.x = 30; 31 | camera.position.y = -30; 32 | camera.position.z = 10; 33 | 34 | camera.up.set( 0, 0, 1 ); 35 | 36 | renderer = new THREE.WebGLRenderer({ antialias: true }); 37 | renderer.setSize( window.innerWidth, window.innerHeight ); 38 | 39 | var viewerele = document.getElementById("viewer"); 40 | 41 | viewerele.appendChild( renderer.domElement ); 42 | 43 | var ambientLight = new THREE.AmbientLight( 0xbbbbbb ); 44 | scene.add( ambientLight ); 45 | 46 | var lights = []; 47 | lights[0] = new THREE.PointLight( 0xececec, 0.25, 0 ); 48 | lights[1] = new THREE.PointLight( 0xececec, 0.25, 0 ); 49 | lights[2] = new THREE.PointLight( 0xececec, 0.25, 0 ); 50 | 51 | lights[0].position.set( 0, 100, 0 ); 52 | lights[1].position.set( 100, 200, 100 ); 53 | lights[2].position.set( -100, -200, -100 ); 54 | 55 | scene.add( lights[0] ); 56 | scene.add( lights[1] ); 57 | scene.add( lights[2] ); 58 | } 59 | 60 | var intersects = []; 61 | 62 | function renderScene(){ 63 | var controls = new THREE.OrbitControls(camera, renderer.domElement); 64 | 65 | function render() { 66 | if (useRaycaster){ 67 | 68 | // update the picking ray with the camera and mouse position 69 | raycaster.setFromCamera( mouse, camera ); 70 | 71 | // clear color 72 | for ( var i = 0; i < intersects.length; i++ ) { 73 | if (!intersects[ i ].object.material.color) continue; 74 | intersects[ i ].object.material.color.set( 0xffffff ); 75 | } 76 | 77 | // calculate objects intersecting the picking ray 78 | intersects = raycaster.intersectObjects( scene.children ); 79 | 80 | for ( var i = 0; i < intersects.length; i++ ) { 81 | if (!intersects[ i ].object.material.color) continue; 82 | intersects[ i ].object.material.color.set( 0xff0000 ); 83 | } 84 | } 85 | 86 | requestAnimationFrame( render ); 87 | renderer.render( scene, camera ); 88 | } 89 | render(); 90 | } 91 | 92 | function addCurveToScene(geom, material){ 93 | material = material || new THREE.LineBasicMaterial({ linewidth: 2, color: 0xdcdcdc}); 94 | scene.add( new THREE.Line( geom, material ) ); 95 | } 96 | 97 | function addLineToScene(pts, mat){ 98 | addCurveToScene(asGeometry(asVector3(pts)), mat); 99 | } 100 | 101 | function addMeshToScene(mesh, material, wireframe ){ 102 | material = material || new THREE.MeshNormalMaterial( { side: THREE.DoubleSide, wireframe: false, shading: THREE.SmoothShading, transparent: true, opacity: 0.4 } ) 103 | 104 | // new THREE.MeshPhongMaterial({ 105 | // specular: '#ffffff', 106 | // color: '#8e8e8e', 107 | // side: THREE.DoubleSide, 108 | // ambient: '#ffffff', 109 | // emissive: '#111111', 110 | // shininess: 40 111 | // }); 112 | 113 | scene.add( new THREE.Mesh( mesh, material ) ); 114 | 115 | if (wireframe){ 116 | var material2 = new THREE.MeshBasicMaterial( { color: 0x000000, side: THREE.DoubleSide, wireframe: true } ); 117 | var mesh2 = new THREE.Mesh( mesh, material2 ); 118 | scene.add( mesh2 ); 119 | } 120 | } 121 | 122 | function asVector3(pts){ 123 | return pts.map(function(x){ 124 | return new THREE.Vector3(x[0],x[1],x[2]); 125 | }); 126 | } 127 | 128 | function asGeometry(threePts){ 129 | var geometry = new THREE.Geometry(); 130 | geometry.vertices.push.apply( geometry.vertices, threePts ); 131 | return geometry; 132 | } 133 | 134 | function benchmark(func, runs){ 135 | var d1 = Date.now(); 136 | for (var i = 0 ; i < runs; i++) 137 | res = func(); 138 | var d2 = Date.now(); 139 | return { result : res, elapsed : d2-d1, each : (d2-d1)/runs }; 140 | } 141 | 142 | function pointsAsGeometry(pts){ 143 | return asGeometry( asVector3(pts) ) 144 | } 145 | 146 | function addPointsToScene(pts){ 147 | 148 | var geom = asGeometry( asVector3( pts ) ); 149 | var cloudMat2 = new THREE.PointCloudMaterial({ size: 6.5, sizeAttenuation: false, color: 0xffffff }); 150 | var cloud2 = new THREE.PointCloud( geom, cloudMat2 ); 151 | 152 | scene.add( cloud2 ); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /examples/js/verbToThreeConversion.js: -------------------------------------------------------------------------------- 1 | // Utility methods to support converting verb.geom types to THREE.Geometry 2 | 3 | (function(verb){ 4 | 5 | verb.geom.NurbsCurve.prototype.toThreeGeometry = function(){ 6 | return tessellateCurve( this ); 7 | } 8 | 9 | verb.geom.NurbsSurface.prototype.toThreeGeometry = function(){ 10 | return tessellateSurface( this ); 11 | } 12 | 13 | function asVector3(pts){ 14 | return pts.map(function(x){ 15 | return new THREE.Vector3(x[0],x[1],x[2]); 16 | }); 17 | } 18 | 19 | function tessellateCurve( curve ){ 20 | var geometry = new THREE.Geometry(); 21 | geometry.vertices = asVector3( curve.tessellate() ); 22 | return geometry; 23 | } 24 | 25 | function tessellateSurface(srf) { 26 | 27 | var tess = srf.tessellate(); 28 | 29 | var geometry = new THREE.Geometry(); 30 | 31 | geometry.vertices = asVector3( tess.points ); 32 | 33 | geometry.faces = tess.faces.map(function(faceIndices){ 34 | var normals = faceIndices.map(function(x){ 35 | var vn = tess.normals[x]; 36 | return new THREE.Vector3( vn[0], vn[1], vn[2] ); 37 | }); 38 | 39 | return new THREE.Face3(faceIndices[0],faceIndices[1],faceIndices[2], normals); 40 | }); 41 | 42 | return geometry; 43 | } 44 | 45 | })(verb); -------------------------------------------------------------------------------- /examples/loftedSurface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Surface Lofting 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 46 | 47 | -------------------------------------------------------------------------------- /examples/meshIntersection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mesh-Mesh Intersection 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /examples/meshSlicing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mesh Slicing 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/revolvedSurface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Revolved Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/surface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NURBS Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/surfaceAdaptiveTessellation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Adaptive surface tessellation 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/surfaceBoundaries.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Surface Boundary Curves 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /examples/surfaceClosestPoint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Closest Point to a Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/surfaceIntersection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Surface-Surface Intersection 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /examples/surfaceIsocurves.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Surface Isocurve Extraction 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/surfaceSplit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Splitting a Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/sweptSurface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swept Surface 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Show/Hide Code
23 |
24 |
25 |
26 |
27 | 28 | 29 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verb-nurbs", 3 | "version": "3.0.3", 4 | "author": "pboyer ", 5 | "description": "A library for creating and manipulating NURBS surfaces and curves in many languages", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/pboyer/verb.git" 9 | }, 10 | "main": "build/js/verb.js", 11 | "unpkg": "build/js/verb.min.js", 12 | "module": "build/js//verb.es.js", 13 | "scripts": { 14 | "build": "haxe buildjs.hxml", 15 | "package": "rollup --config", 16 | "test": "mocha" 17 | }, 18 | "keywords": [ 19 | "NURBS", 20 | "geometry", 21 | "cad", 22 | "surface", 23 | "curve", 24 | "spline", 25 | "mesh" 26 | ], 27 | "files": [ 28 | "LICENSE", 29 | "README.md", 30 | "build", 31 | "buildcpp.hxml", 32 | "buildcs.hxml", 33 | "buildjs.hxml", 34 | "buildphp.hxml", 35 | "buildpython.hxml", 36 | "examples", 37 | "package-lock.json", 38 | "package.json", 39 | "rollup.config.mjs", 40 | "src", 41 | "test", 42 | "verb.iml" 43 | ], 44 | "dependencies": { 45 | "web-worker": "^1.3.0" 46 | }, 47 | "devDependencies": { 48 | "@rollup/plugin-commonjs": "^26.0.1", 49 | "@rollup/plugin-node-resolve": "^15.2.3", 50 | "@rollup/plugin-terser": "^0.4.4", 51 | "mocha": "^10.6.0", 52 | "rollup": "^4.18.0", 53 | "rollup-plugin-polyfill-node": "^0.13.0", 54 | "should": "^13.2.3" 55 | }, 56 | "license": "MIT" 57 | } 58 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs' 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import terser from '@rollup/plugin-terser' 4 | import { nodeResolve } from '@rollup/plugin-node-resolve' 5 | 6 | export default { 7 | external: 'web-worker', 8 | input: 'build/js/verb.js', 9 | output: [ 10 | { 11 | file: 'build/js/verb.min.js', 12 | format: 'umd', 13 | name: 'verb', 14 | }, 15 | { 16 | file: 'build/js/verb.es.js', 17 | format: 'es', 18 | } 19 | ], 20 | plugins: [ 21 | nodeResolve(), 22 | commonjs(), 23 | nodePolyfills( /* options */ ) 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /site/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Source Sans Pro', sans-serif; 3 | font-size: 14px; 4 | margin: 0 auto; 5 | background: black; 6 | } 7 | 8 | #title-box { 9 | background: #DD5500; 10 | text-align: right; 11 | } 12 | 13 | #title-box .inner-box { 14 | padding-top: 0px; 15 | } 16 | 17 | #title-box h1 { 18 | padding: none; 19 | margin: none; 20 | font-size: 60px; 21 | } 22 | 23 | #source-box { 24 | background: #B10064; 25 | } 26 | 27 | #docs-box { 28 | background: #008E6E; 29 | } 30 | 31 | #download-box { 32 | background: #89CE00; 33 | } 34 | 35 | .box { 36 | position: relative; 37 | flex-grow: 1; 38 | overflow: hidden; 39 | color: white; 40 | height: 300px; 41 | width: 300px; 42 | text-shadow: 0px 1px #000000; 43 | } 44 | 45 | .box h1 { 46 | font-size: 40px; 47 | } 48 | 49 | .box a { 50 | color: white; 51 | text-decoration: none; 52 | } 53 | 54 | .inner-box { 55 | padding: 30px; 56 | height: 100%; 57 | width: 100%; 58 | box-sizing: border-box; 59 | } 60 | 61 | .link-box:hover { 62 | background: #222 !important; 63 | } 64 | 65 | .examples { 66 | background-color: black; 67 | display: flex; 68 | align-content: flex-start; 69 | flex-wrap: wrap; 70 | } 71 | 72 | .box img { 73 | position: absolute; 74 | width: 100%; 75 | } 76 | 77 | .picture-box { 78 | font-size: 28px; 79 | z-index: 2; 80 | position: absolute; 81 | padding: 30px; 82 | height: 100%; 83 | width: 100%; 84 | box-sizing: border-box; 85 | background: rgba(20,20,20,0.8); 86 | opacity: 0; 87 | } 88 | 89 | .picture-box:hover { 90 | opacity: 1.0; 91 | } -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | verb - Open-source, cross-platform NURBS 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |

verb2.1.0

16 |

Open-source, cross-platform NURBS

17 |
18 |
19 |
20 | 21 |
Conics
22 |
23 | 24 |
25 | 31 | 38 | 44 | 48 | 55 |
56 |
Curve-curve intersection
57 | 58 |
59 |
60 |
Split a curve
61 | 62 |
63 |
64 |
Cylindrical surface
65 | 66 |
67 |
68 |
Extruded surface
69 | 70 |
71 | 78 |
79 |
Revolved surface
80 | 81 |
82 |
83 |
Lofted surface
84 | 85 |
86 | 90 |
91 |
Mesh-mesh intersection
92 | 93 |
94 |
95 |
Mesh slicing
96 | 97 |
98 |
99 |
NURBS Surface
100 | 101 |
102 |
103 |
Surface boundary curves
104 | 105 |
106 |
107 |
Closest point to a surface
108 | 109 |
110 |
111 |
Surface Isocurves
112 | 113 |
114 |
115 |
Surface-surface intersection
116 | 117 |
118 |
119 |
Splitting a surface
120 | 121 |
122 |
123 |
Swept surface
124 | 125 |
126 | 127 |
128 | 129 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /site/pictures/conics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/conics.png -------------------------------------------------------------------------------- /site/pictures/curveByPointInterpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/curveByPointInterpolation.png -------------------------------------------------------------------------------- /site/pictures/curveClosestPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/curveClosestPoint.png -------------------------------------------------------------------------------- /site/pictures/curveDivide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/curveDivide.png -------------------------------------------------------------------------------- /site/pictures/curveIntersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/curveIntersection.png -------------------------------------------------------------------------------- /site/pictures/curveSplit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/curveSplit.png -------------------------------------------------------------------------------- /site/pictures/cylindricalSurface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/cylindricalSurface.png -------------------------------------------------------------------------------- /site/pictures/extrudedSurface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/extrudedSurface.png -------------------------------------------------------------------------------- /site/pictures/loftedSurface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/loftedSurface.png -------------------------------------------------------------------------------- /site/pictures/meshIntersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/meshIntersection.png -------------------------------------------------------------------------------- /site/pictures/meshSlicing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/meshSlicing.png -------------------------------------------------------------------------------- /site/pictures/revolvedSurface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/revolvedSurface.png -------------------------------------------------------------------------------- /site/pictures/surface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surface.png -------------------------------------------------------------------------------- /site/pictures/surfaceAdaptiveTessellation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceAdaptiveTessellation.png -------------------------------------------------------------------------------- /site/pictures/surfaceBoundaries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceBoundaries.png -------------------------------------------------------------------------------- /site/pictures/surfaceClosestPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceClosestPoint.png -------------------------------------------------------------------------------- /site/pictures/surfaceIntersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceIntersection.png -------------------------------------------------------------------------------- /site/pictures/surfaceIsocurves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceIsocurves.png -------------------------------------------------------------------------------- /site/pictures/surfaceSplit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/surfaceSplit.png -------------------------------------------------------------------------------- /site/pictures/sweptSurface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pboyer/verb/f3ba23cdccbab77102b686021460cf9779df240f/site/pictures/sweptSurface.png -------------------------------------------------------------------------------- /src/support/footer.js: -------------------------------------------------------------------------------- 1 | 2 | return verb; 3 | 4 | }); -------------------------------------------------------------------------------- /src/support/header.js: -------------------------------------------------------------------------------- 1 | // Header for verb for JavaScript 2 | // Borrowed from browserify, this header supports AMD (define) and common js (require) style modules 3 | 4 | (function(f){ 5 | if(typeof exports==="object"&&typeof module!=="undefined"){ 6 | module.exports=f() 7 | } else if(typeof define==="function"&&define.amd){ 8 | define([],f) 9 | } else { 10 | var g; 11 | if(typeof window!=="undefined"){ 12 | g=window 13 | } else if(typeof global!=="undefined"){ 14 | g=global 15 | } else if(typeof self!=="undefined"){ 16 | g=self 17 | } else{ 18 | g=this 19 | } 20 | 21 | g.verb = f() 22 | } 23 | })(function(){ 24 | var verb = {}; 25 | 26 | var global = this; 27 | 28 | var isBrowser = new Function("try {return this===window;}catch(e){ return false;}"); 29 | var isNode=new Function("try {return this===global;}catch(e){return false;}"); 30 | var isWebworker=new Function("try {return typeof importScripts === 'function';}catch(e){return false;}"); 31 | 32 | // node.js context, but not WebWorker 33 | if ( isNode() && !isWebworker() ){ 34 | Worker = require('webworker-threads').Worker; 35 | } 36 | 37 | // WebWorker or node.js context 38 | if ( isNode() || isWebworker() ){ 39 | 40 | var window = global; // required for promhx 41 | 42 | // WebWorker 43 | if ( isWebworker() ){ 44 | 45 | var lookup = function(className, methodName){ 46 | 47 | var obj = global; 48 | 49 | className.split(".").forEach(function(x){ 50 | if (obj) obj = obj[ x ]; 51 | }); 52 | 53 | if (!obj) return null; 54 | 55 | return obj[ methodName ]; 56 | } 57 | 58 | onmessage = function( e ){ 59 | 60 | if (!e.data.className || !e.data.methodName) return; 61 | 62 | var method = lookup( e.data.className, e.data.methodName ); 63 | 64 | if (!method){ 65 | return console.error("could not find " + e.data.className + "." + e.data.methodName) 66 | } 67 | 68 | postMessage( { result: method.apply( null, e.data.args ), id: e.data.id } ); 69 | 70 | }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/verb/Verb.hx: -------------------------------------------------------------------------------- 1 | package verb; 2 | 3 | // constants 4 | import verb.core.Constants; 5 | 6 | // geom 7 | 8 | import verb.geom.NurbsCurve; 9 | import verb.geom.Arc; 10 | import verb.geom.Line; 11 | import verb.geom.BezierCurve; 12 | import verb.geom.Circle; 13 | import verb.geom.Ellipse; 14 | import verb.geom.EllipseArc; 15 | import verb.geom.NurbsSurface; 16 | import verb.geom.SphericalSurface; 17 | import verb.geom.RevolvedSurface; 18 | import verb.geom.ExtrudedSurface; 19 | import verb.geom.CylindricalSurface; 20 | import verb.geom.ConicalSurface; 21 | import verb.geom.SweptSurface; 22 | import verb.geom.Intersect; 23 | 24 | // core 25 | 26 | import verb.core.Serialization; 27 | import verb.core.BoundingBox; 28 | import verb.core.Constants; 29 | import verb.core.Trig; 30 | import verb.core.Minimizer; 31 | import verb.core.ArrayExtensions; 32 | import verb.core.Mesh; 33 | import verb.core.KdTree; 34 | 35 | // eval 36 | 37 | import verb.eval.Eval; 38 | import verb.eval.Intersect; 39 | import verb.eval.Tess; 40 | import verb.eval.Make; 41 | import verb.eval.Modify; 42 | import verb.eval.Analyze; 43 | import verb.eval.Divide; 44 | import verb.eval.Check; 45 | 46 | class Verb { 47 | public static function main():Void { 48 | trace(Constants.VERSION); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/verb/core/ArrayExtensions.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | class ArrayExtensions { 4 | 5 | //Fill an array with n copies of null by mutation 6 | // 7 | //**params** 8 | // 9 | //* the array to fill 10 | //* the number of null's to add to the array 11 | // 12 | //**returns** 13 | // 14 | //* nothing, just mutates the given array 15 | 16 | public static function alloc( a : Array, n : Int ){ 17 | if (n < 0) return; 18 | while (a.length < n) { 19 | a.push(null); 20 | } 21 | } 22 | 23 | //Obtain a reversed copy of an array 24 | // 25 | //**params** 26 | // 27 | //* array of stuff 28 | // 29 | //**returns** 30 | // 31 | //* a reversed copy of the array 32 | 33 | public static function reversed(a : Array) : Array { 34 | var ac = a.copy(); 35 | ac.reverse(); 36 | return ac; 37 | } 38 | 39 | //Get the last element of an array 40 | // 41 | //**params** 42 | // 43 | //* array of stuff 44 | // 45 | //**returns** 46 | // 47 | //* the last element of the array 48 | 49 | public static function last(a : Array) : T { 50 | return a[a.length-1]; 51 | } 52 | 53 | //Get the first element of an array 54 | // 55 | //**params** 56 | // 57 | //* array of stuff 58 | // 59 | //**returns** 60 | // 61 | //* the last element of the array 62 | 63 | public static function first(a : Array) : T { 64 | return a[0]; 65 | } 66 | 67 | public static function spliceAndInsert(a : Array, start : Int, end : Int, ele : T) : Void { 68 | a.splice(start, end); 69 | a.insert(start, ele); 70 | } 71 | 72 | //Get the first half of an array including the pivot 73 | // 74 | //**params** 75 | // 76 | //* array of stuff 77 | // 78 | //**returns** 79 | // 80 | //* the left half 81 | 82 | public static function left(arr : Array) : Array{ 83 | if (arr.length == 0) return []; 84 | var len = Math.ceil( arr.length / 2 ); 85 | return arr.slice( 0, len ); 86 | } 87 | 88 | //Get the second half of an array, not including the pivot 89 | // 90 | //**params** 91 | // 92 | //* array of stuff 93 | // 94 | //**returns** 95 | // 96 | //* the right half 97 | 98 | public static function right(arr : Array) : Array{ 99 | if (arr.length == 0) return []; 100 | var len = Math.ceil( arr.length / 2 ); 101 | return arr.slice( len ); 102 | } 103 | 104 | //Get the second half of an array including the pivot 105 | // 106 | //**params** 107 | // 108 | //* array of stuff 109 | // 110 | //**returns** 111 | // 112 | //* the right half 113 | 114 | public static function rightWithPivot(arr : Array) : Array{ 115 | if (arr.length == 0) return []; 116 | var len = Math.ceil( arr.length / 2 ); 117 | return arr.slice( len-1 ); 118 | } 119 | 120 | //Obtain the unique set of elements in an array 121 | // 122 | //**params** 123 | // 124 | //* array of stuff 125 | //* *Function*, a function that receives two arguments (two objects from the array). Returning true indicates 126 | //the objects are equal. 127 | // 128 | //**returns** 129 | // 130 | //* array of unique elements 131 | 132 | public static function unique( arr : Array, comp : T -> T -> Bool ){ 133 | 134 | if (arr.length == 0) return []; 135 | 136 | var uniques = [ arr.pop() ]; 137 | 138 | while (arr.length > 0){ 139 | 140 | var ele = arr.pop(); 141 | var isUnique = true; 142 | 143 | for (unique in uniques){ 144 | if ( comp( ele, unique ) ){ 145 | isUnique = false; 146 | break; 147 | } 148 | } 149 | 150 | if ( isUnique ){ 151 | uniques.push( ele ); 152 | } 153 | } 154 | 155 | return uniques; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/verb/core/Binomial.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import haxe.ds.IntMap; 4 | 5 | class Binomial { 6 | 7 | static var memo = new IntMap>(); 8 | 9 | public static function get(n : Int, k : Int) : Float { 10 | if (k == 0.0) { 11 | return 1.0; 12 | } 13 | 14 | if (n == 0 || k > n) { 15 | return 0.0; 16 | } 17 | 18 | if (k > n - k) { 19 | k = n - k; 20 | } 21 | 22 | if ( memo_exists(n,k) ) { 23 | return get_memo(n,k); 24 | } 25 | 26 | var r : Float = 1, 27 | n_o = n; 28 | 29 | for (d in 1...k+1){ 30 | 31 | if ( memo_exists(n_o, d) ) { 32 | n--; 33 | r = get_memo(n_o, d); 34 | continue; 35 | } 36 | 37 | r *= n--; 38 | r /= d; 39 | 40 | memoize(n_o, d, r); 41 | 42 | } 43 | 44 | return r; 45 | } 46 | 47 | public static function get_no_memo(n : Int, k : Int) : Float { 48 | if (k == 0) { 49 | return 1; 50 | } 51 | 52 | if (n == 0 || k > n) { 53 | return 0; 54 | } 55 | 56 | if (k > n - k) { 57 | k = n - k; 58 | } 59 | 60 | var r : Float = 1, 61 | n_o = n; 62 | 63 | for (d in 1...k+1){ 64 | r *= n--; 65 | r /= d; 66 | } 67 | 68 | return r; 69 | } 70 | 71 | private static function memo_exists(n : Int, k : Int) : Bool { 72 | return ( memo.exists(n) && memo.get(n).exists(k) ); 73 | } 74 | 75 | private static function get_memo(n : Int, k : Int) : Float { 76 | return memo.get(n).get(k); 77 | } 78 | 79 | private static function memoize(n, k, val) { 80 | if ( !memo.exists(n) ) { 81 | memo.set( n, new IntMap() ); 82 | } 83 | 84 | memo.get( n ).set( k, val ); 85 | } 86 | } -------------------------------------------------------------------------------- /src/verb/core/BoundingBox.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Data; 4 | 5 | using Lambda; 6 | 7 | // `BoundingBox` is an n-dimensional bounding box implementation. It is used by many of verb's intersection algorithms. 8 | // 9 | // The first point added to the `BoundingBox` using `BoundingBox.add` will be used to define the dimensionality of the 10 | // bounding box. 11 | 12 | @:expose("core.BoundingBox") 13 | class BoundingBox { 14 | 15 | var initialized : Bool = false; 16 | var dim : Int = 3; 17 | 18 | //BoundingBox Constructor 19 | // 20 | //**params** 21 | // 22 | //* Points to add, if desired. Otherwise, will not be initialized until add is called. 23 | 24 | public function new( pts : Array = null ) { 25 | if ( pts != null ) { 26 | this.addRange( pts ); 27 | } 28 | } 29 | 30 | // The minimum point of the BoundingBox - the coordinates of this point are always <= max. 31 | public var min : Point = null; 32 | 33 | // The maximum point of the BoundingBox. The coordinates of this point are always >= min. 34 | public var max : Point = null; 35 | 36 | //Create a bounding box initialized with a single element 37 | // 38 | //**params** 39 | // 40 | //* A array of numbers 41 | // 42 | //**returns** 43 | // 44 | //* This BoundingBox for chaining 45 | 46 | public function fromPoint( pt ){ 47 | return new BoundingBox( [ pt ] ); 48 | } 49 | 50 | //Adds a point to the bounding box, expanding the bounding box if the point is outside of it. 51 | //If the bounding box is not initialized, this method has that side effect. 52 | // 53 | //**params** 54 | // 55 | //* A length-n array of numbers 56 | // 57 | //**returns** 58 | // 59 | //* This BoundingBox for chaining 60 | 61 | public function add( point : Point ) : BoundingBox 62 | { 63 | if ( !this.initialized ) 64 | { 65 | this.dim = point.length; 66 | this.min = point.slice(0); 67 | this.max = point.slice(0); 68 | this.initialized = true; 69 | 70 | return this; 71 | } 72 | 73 | for (i in 0...this.dim){ 74 | if (point[i] > this.max[i] ) this.max[i] = point[i]; 75 | if (point[i] < this.min[i] ) this.min[i] = point[i]; 76 | } 77 | 78 | return this; 79 | 80 | } 81 | 82 | //Asynchronously add an array of points to the bounding box 83 | // 84 | //**params** 85 | // 86 | //* An array of length-n array of numbers 87 | // 88 | //**returns** 89 | // 90 | //* this BoundingBox for chaining 91 | 92 | public function addRange( points : Array ) : BoundingBox 93 | { 94 | var l = points.length; 95 | 96 | for (i in 0...l){ 97 | this.add(points[i]); 98 | } 99 | 100 | return this; 101 | } 102 | 103 | //Determines if point is contained in the bounding box 104 | // 105 | //**params** 106 | // 107 | //* the point 108 | //* the tolerance 109 | // 110 | //**returns** 111 | // 112 | //* true if the two intervals overlap, otherwise false 113 | 114 | public function contains(point : Point, tol : Float = -1) : Bool { 115 | 116 | if ( !this.initialized ) 117 | { 118 | return false; 119 | } 120 | 121 | return this.intersects( new BoundingBox([point]), tol ); 122 | } 123 | 124 | //Determines if two intervals on the real number line intersect 125 | // 126 | //**params** 127 | // 128 | //* Beginning of first interval 129 | //* End of first interval 130 | //* Beginning of second interval 131 | //* End of second interval 132 | // 133 | //**returns** 134 | // 135 | //* true if the two intervals overlap, otherwise false 136 | 137 | public static function intervalsOverlap( a1 : Float, a2: Float, b1: Float, b2: Float, tol : Float = -1 ) : Bool { 138 | 139 | var tol = tol < -0.5 ? Constants.TOLERANCE : tol 140 | , x1 = Math.min(a1, a2) - tol 141 | , x2 = Math.max(a1, a2) + tol 142 | , y1 = Math.min(b1, b2) - tol 143 | , y2 = Math.max(b1, b2) + tol; 144 | 145 | return (x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2) ; 146 | } 147 | 148 | //Determines if this bounding box intersects with another 149 | // 150 | //**params** 151 | // 152 | //* BoundingBox to check for intersection with this one 153 | // 154 | //**returns** 155 | // 156 | //* true if the two bounding boxes intersect, otherwise false 157 | 158 | public function intersects( bb : BoundingBox, tol : Float = -1 ) : Bool { 159 | 160 | if ( !this.initialized || !bb.initialized ) return false; 161 | 162 | var a1 = min 163 | , a2 = max 164 | , b1 = bb.min 165 | , b2 = bb.max; 166 | 167 | for (i in 0...dim){ 168 | if (!intervalsOverlap(a1[i], a2[i], b1[i], b2[i], tol )) return false; 169 | } 170 | 171 | return true; 172 | } 173 | 174 | //Clear the bounding box, leaving it in an uninitialized state. Call add, addRange in order to 175 | //initialize 176 | // 177 | //**returns** 178 | // 179 | //* this BoundingBox for chaining 180 | 181 | public function clear() : BoundingBox { 182 | this.initialized = false; 183 | return this; 184 | } 185 | 186 | //Get longest axis of bounding box 187 | // 188 | //**returns** 189 | // 190 | //* Index of longest axis 191 | 192 | public function getLongestAxis() : Int { 193 | 194 | var max = 0.0; 195 | var id = 0; 196 | 197 | for ( i in 0...dim ){ 198 | var l = this.getAxisLength(i); 199 | if (l > max) { 200 | max = l; 201 | id = i; 202 | } 203 | } 204 | 205 | return id; 206 | } 207 | 208 | //Get length of given axis. 209 | // 210 | //**params** 211 | // 212 | //* Index of axis to inspect (between 0 and 2) 213 | // 214 | //**returns** 215 | // 216 | //* Length of the given axis. If axis is out of bounds, returns 0. 217 | 218 | public function getAxisLength( i : Int ) : Float { 219 | if (i < 0 || i > this.dim-1) return 0.0; 220 | return Math.abs( this.min[i] - this.max[i] ); 221 | } 222 | 223 | //Compute the boolean intersection of this with another axis-aligned bounding box. If the two 224 | //bounding boxes do not intersect, returns null. 225 | // 226 | //**params** 227 | // 228 | //* BoundingBox to intersect with 229 | // 230 | //**returns** 231 | // 232 | //* The bounding box formed by the intersection or null if there is no intersection. 233 | 234 | public function intersect( bb : BoundingBox, tol : Float ) : BoundingBox { 235 | 236 | if ( !this.initialized ) return null; 237 | 238 | var a1 = min 239 | , a2 = max 240 | , b1 = bb.min 241 | , b2 = bb.max; 242 | 243 | if ( !this.intersects( bb, tol ) ) return null; 244 | 245 | var maxbb = [] 246 | , minbb = []; 247 | 248 | for (i in 0...dim){ 249 | maxbb.push( Math.min( a2[i], b2[i] ) ); 250 | minbb.push( Math.max( a1[i], b1[i] ) ); 251 | } 252 | 253 | return new BoundingBox([minbb, maxbb]); 254 | } 255 | 256 | } -------------------------------------------------------------------------------- /src/verb/core/Constants.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | // `Constants` contains a collection of default constants used throughout the library. These can be set to adjust verb's 4 | // defaults. 5 | 6 | @:expose("core.Constants") 7 | class Constants { 8 | //The default euclidean distance that identifies whether two points are coincident 9 | @:expose("TOLERANCE") 10 | public static var TOLERANCE : Float = 1e-6; 11 | 12 | //The minimum value to determine whether two floating point numbers are the same 13 | @:expose("EPSILON") 14 | public static var EPSILON : Float = 1e-10; 15 | 16 | //The current version of verb 17 | @:expose("VERSION") 18 | public static var VERSION : String = "3.0.3"; 19 | } 20 | -------------------------------------------------------------------------------- /src/verb/core/Data.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Serialization; 4 | 5 | // A `Point` in verb is represented simply by an array of floating point numbers. 6 | // 7 | // So, in JavaScript, one would write simply `[0,0,0]` to create a Point at the origin. 8 | 9 | typedef Point = Array; 10 | 11 | // Like a `Point`, a `Vector` is simply an array of floating point numbers 12 | // 13 | // So, in JavaScript, one would write simply `[1,0,0]` to create the a unit vector in the x direction 14 | 15 | typedef Vector = Array 16 | 17 | // `Matrix` is represented by a nested array of floating point number arrays 18 | // 19 | // So, in JavaScript, one would write simply `[[1,0],[0,1]]` to create a 2x2 identity matrix 20 | 21 | typedef Matrix = Array> 22 | 23 | // A `KnotArray` is a non-decreasing sequence of floating point . Use the methods in `Check` to validate `KnotArray`'s 24 | 25 | typedef KnotArray = Array; 26 | 27 | // A `Plane` is simply an origin point and normal 28 | 29 | @:expose("core.Plane") 30 | class Plane extends SerializableBase { 31 | 32 | public var normal : Vector; 33 | public var origin : Point; 34 | 35 | public function new(origin, normal){ 36 | this.origin = origin; 37 | this.normal = normal; 38 | } 39 | } 40 | 41 | // A `Ray` is simply an origin point and a direction 42 | 43 | @:expose("core.Ray") 44 | class Ray extends SerializableBase { 45 | 46 | public var dir : Vector; 47 | public var origin : Point; 48 | 49 | public function new(origin, dir){ 50 | this.origin = origin; 51 | this.dir = dir; 52 | } 53 | } 54 | 55 | // A simple data structure representing a NURBS curve. `NurbsCurveData` does no checks for legality. You can use 56 | // `verb.eval.Check` for that. 57 | 58 | @:expose("core.NurbsCurveData") 59 | class NurbsCurveData extends SerializableBase { 60 | 61 | public function new(degree, knots, controlPoints){ 62 | this.degree = degree; 63 | this.controlPoints = controlPoints; 64 | this.knots = knots; 65 | } 66 | 67 | //integer degree of curve 68 | public var degree : Int; 69 | 70 | // 2d array of control points, where each control point is an array of length (dim) 71 | public var controlPoints : Array; 72 | 73 | //array of nondecreasing knot values 74 | public var knots : Array; 75 | 76 | } 77 | 78 | // A simple data structure representing a NURBS surface. `NurbsSurfaceData` does no checks for legality. You can use 79 | // `verb.eval.Check` for that. 80 | 81 | @:expose("core.NurbsSurfaceData") 82 | class NurbsSurfaceData extends SerializableBase { 83 | 84 | public function new(degreeU, degreeV, knotsU, knotsV, controlPoints){ 85 | this.degreeU = degreeU; 86 | this.degreeV = degreeV; 87 | this.knotsU = knotsU; 88 | this.knotsV = knotsV; 89 | this.controlPoints = controlPoints; 90 | } 91 | 92 | //integer degree of surface in u direction 93 | public var degreeU : Int; 94 | 95 | //integer degree of surface in v direction 96 | public var degreeV : Int; 97 | 98 | //array of nondecreasing knot values in u direction 99 | public var knotsU : KnotArray; 100 | 101 | //array of nondecreasing knot values in v direction 102 | public var knotsV : KnotArray; 103 | 104 | // 2d array of control points, the vertical direction (u) increases from top to bottom, the v direction from left to right, 105 | //and where each control point is an array of length (dim) 106 | public var controlPoints : Array>; 107 | 108 | } 109 | 110 | // A triangular face of a mesh 111 | 112 | typedef Tri = Array; 113 | 114 | // A `UV` is simply an array of floating point numbers. 115 | // 116 | // So, in JavaScript, one would write simply `[1,0]` to create a UV 117 | 118 | typedef UV = Array; 119 | 120 | // A simple data structure representing a mesh. `MeshData` does not check for legality. 121 | 122 | @:expose("core.MeshData") 123 | class MeshData extends SerializableBase { 124 | 125 | public var faces : Array; 126 | public var points : Array; 127 | public var normals : Array; 128 | public var uvs : Array; 129 | 130 | public function new(faces : Array, points : Array, normals : Array, uvs : Array ) { 131 | this.faces = faces; 132 | this.points = points; 133 | this.normals = normals; 134 | this.uvs = uvs; 135 | } 136 | 137 | public static function empty() : MeshData { 138 | return new MeshData([],[],[],[]); 139 | } 140 | } 141 | 142 | // A simple data structure representing a polyline. `PolylineData` is useful, for example, as the result of a curve tessellation. 143 | 144 | @:expose("core.PolylineData") 145 | class PolylineData extends SerializableBase { 146 | 147 | // The points in the polyline 148 | public var points : Array; 149 | 150 | // The parameters of the individual points 151 | public var params : Array; 152 | 153 | public function new(points, params){ 154 | this.points = points; 155 | this.params = params; 156 | } 157 | } 158 | 159 | // A simple data structure representing a NURBS volume. This data structure is largely experimental in intent. Like CurveData 160 | // and SurfaceData, this data structure does no legality checks. 161 | 162 | @:expose("core.VolumeData") 163 | class VolumeData extends SerializableBase { 164 | 165 | public function new(degreeU, degreeV, degreeW, knotsU, knotsV, knotsW, controlPoints){ 166 | this.degreeU = degreeU; 167 | this.degreeV = degreeV; 168 | this.degreeW = degreeW; 169 | this.knotsU = knotsU; 170 | this.knotsV = knotsV; 171 | this.knotsW = knotsW; 172 | this.controlPoints = controlPoints; 173 | } 174 | 175 | //integer degree in u direction 176 | public var degreeU : Int; 177 | 178 | //integer degree in v direction 179 | public var degreeV : Int; 180 | 181 | //integer degree in w direction 182 | public var degreeW : Int; 183 | 184 | //array of nondecreasing knot values in u direction 185 | public var knotsU : KnotArray; 186 | 187 | //array of nondecreasing knot values in v direction 188 | public var knotsV : KnotArray; 189 | 190 | //array of nondecreasing knot values in w direction 191 | public var knotsW : KnotArray; 192 | 193 | // 3d array of control points, where rows are the u dir, and columns run along the positive v direction, 194 | //and where each control point is an array of length (dim) 195 | public var controlPoints : Array>>; 196 | 197 | } 198 | 199 | // A simple parametric data type representing a pair of two objects 200 | 201 | @:expose("core.Pair") 202 | class Pair { 203 | public var item0 : T1; 204 | public var item1 : T2; 205 | 206 | public function new(item1 : T1, item2 : T2) { 207 | this.item0 = item1; 208 | this.item1 = item2; 209 | } 210 | } 211 | 212 | // A simple parametric data type representing an "interval" between two numbers. This data structure does no legality checks. 213 | 214 | @:expose("core.Interval") 215 | class Interval { 216 | public var min : T; 217 | public var max : T; 218 | 219 | public function new(min, max){ 220 | this.min = min; 221 | this.max = max; 222 | } 223 | } -------------------------------------------------------------------------------- /src/verb/core/Intersections.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Data; 4 | 5 | @:expose("core.CurveCurveIntersection") 6 | class CurveCurveIntersection { 7 | 8 | //where the intersection took place 9 | public var point0 : Point; 10 | 11 | //where the intersection took place on the second curve 12 | public var point1 : Point; 13 | 14 | //the parameter on the first curve 15 | public var u0 : Float; 16 | 17 | //the parameter on the second curve 18 | public var u1 : Float; 19 | 20 | public function new(point0, point1, u0, u1){ 21 | this.point0 = point0; 22 | this.point1 = point1; 23 | this.u0 = u0; 24 | this.u1 = u1; 25 | } 26 | } 27 | 28 | @:expose("core.CurveSurfaceIntersection") 29 | class CurveSurfaceIntersection { 30 | 31 | public var u : Float; 32 | public var uv : UV; 33 | public var curvePoint : Point; 34 | public var surfacePoint : Point; 35 | 36 | public function new( u, uv, curvePoint, surfacePoint ){ 37 | this.u = u; 38 | this.uv = uv; 39 | this.curvePoint = curvePoint; 40 | this.surfacePoint = surfacePoint; 41 | } 42 | } 43 | 44 | @:expose("core.MeshIntersectionPoint") 45 | class MeshIntersectionPoint { 46 | 47 | public var uv0 : UV; 48 | public var uv1 : UV; 49 | public var point : Point; 50 | 51 | public var faceIndex0 : Int; 52 | public var faceIndex1 : Int; 53 | 54 | //tags to navigate a segment structure 55 | public var opp : MeshIntersectionPoint = null; 56 | public var adj : MeshIntersectionPoint = null; 57 | public var visited : Bool = false; 58 | 59 | public function new(uv0, uv1, point, faceIndex0, faceIndex1){ 60 | this.uv0 = uv0; 61 | this.uv1 = uv1; 62 | this.point = point; 63 | this.faceIndex0; 64 | this.faceIndex1; 65 | } 66 | } 67 | 68 | @:expose("core.PolylineMeshIntersection") 69 | class PolylineMeshIntersection { 70 | 71 | public var point : Point; 72 | public var u : Float; 73 | public var uv : UV; 74 | public var polylineIndex : Int; 75 | public var faceIndex : Int; 76 | 77 | public function new(point, u, uv, polylineIndex, faceIndex){ 78 | this.point = point; 79 | this.u = u; 80 | this.uv = uv; 81 | this.polylineIndex = polylineIndex; 82 | this.faceIndex = faceIndex; 83 | } 84 | } 85 | 86 | @:expose("core.SurfaceSurfaceIntersectionPoint") 87 | class SurfaceSurfaceIntersectionPoint { 88 | 89 | public var uv0 : UV; 90 | public var uv1 : UV; 91 | public var point : Point; 92 | public var dist : Float; 93 | 94 | public function new( uv0, uv1, point, dist ){ 95 | this.uv0 = uv0; 96 | this.uv1 = uv1; 97 | this.point = point; 98 | this.dist = dist; 99 | } 100 | } 101 | 102 | @:expose("core.TriSegmentIntersection") 103 | class TriSegmentIntersection { 104 | 105 | //where the intersection took place 106 | public var point : Point; 107 | 108 | //the u param where u is the axis from v0 to v1 109 | public var s : Float; 110 | 111 | //the v param where v is the axis from v0 to v2 112 | public var t : Float; 113 | 114 | //the parameter along the segment 115 | public var p : Float; 116 | 117 | public function new(point, s, t, r){ 118 | this.point = point; 119 | this.s = s; 120 | this.t = t; 121 | this.p = r; 122 | } 123 | } 124 | 125 | @:expose("core.CurveTriPoint") 126 | class CurveTriPoint { 127 | public var u : Float; 128 | public var uv : UV; 129 | public var point : Point; 130 | 131 | public function new(u : Float, point : Point, uv : UV){ 132 | this.u = u; 133 | this.point = point; 134 | this.uv = uv; 135 | } 136 | } 137 | 138 | class SurfacePoint { 139 | 140 | public var uv : UV; 141 | public var point : Point; 142 | public var normal : Point; 143 | public var id : Int; 144 | public var degen : Bool; 145 | 146 | public function new(point : Point, normal : Point, uv : UV, id : Int = -1, degen : Bool = false) { 147 | this.uv = uv; 148 | this.point = point; 149 | this.normal = normal; 150 | this.id = id; 151 | this.degen = degen; 152 | } 153 | 154 | public static function fromUv(u,v){ 155 | return new SurfacePoint(null, null, [u,v] ); 156 | } 157 | } 158 | 159 | @:expose("core.CurvePoint") 160 | class CurvePoint { 161 | public var u : Float; 162 | public var pt : Point; 163 | 164 | public function new(u, pt) { 165 | this.u = u; 166 | this.pt = pt; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/verb/core/LazyCurveBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | using verb.core.ArrayExtensions; 4 | using verb.core.Vec; 5 | import verb.core.Data; 6 | 7 | import verb.eval.Eval; 8 | import verb.eval.Divide; 9 | import verb.eval.Intersect; 10 | 11 | class LazyCurveBoundingBoxTree implements IBoundingBoxTree { 12 | 13 | var _curve : NurbsCurveData; 14 | var _boundingBox : BoundingBox = null; 15 | var _knotTol : Float; 16 | 17 | public function new(curve, knotTol : Float = null){ 18 | _curve = curve; 19 | if (knotTol == null){ 20 | knotTol = _curve.knots.domain() / 64; 21 | } 22 | _knotTol = knotTol; 23 | } 24 | 25 | public function split() : Pair, IBoundingBoxTree> { 26 | var min = _curve.knots.first(); 27 | var max = _curve.knots.last(); 28 | var dom = max - min; 29 | 30 | var crvs = Divide.curveSplit( _curve, (max + min) / 2.0 + dom * 0.1 * Math.random()); 31 | 32 | return new Pair, IBoundingBoxTree>( 33 | new LazyCurveBoundingBoxTree( crvs[0], _knotTol ), 34 | new LazyCurveBoundingBoxTree( crvs[1], _knotTol )); 35 | } 36 | 37 | public function boundingBox(){ 38 | if (_boundingBox == null){ 39 | _boundingBox = new BoundingBox( Eval.dehomogenize1d(_curve.controlPoints) ); 40 | } 41 | return _boundingBox; 42 | } 43 | 44 | public function yield(){ 45 | return _curve; 46 | } 47 | 48 | public function indivisible( tolerance : Float ){ 49 | return _curve.knots.domain() < _knotTol; 50 | } 51 | 52 | public function empty(){ 53 | return false; 54 | } 55 | } -------------------------------------------------------------------------------- /src/verb/core/LazyMeshBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Data; 4 | import verb.eval.Intersect; 5 | 6 | using verb.core.ArrayExtensions; 7 | 8 | class LazyMeshBoundingBoxTree implements IBoundingBoxTree { 9 | var _mesh : MeshData; 10 | var _faceIndices : Array; 11 | var _boundingBox : BoundingBox = null; 12 | 13 | public function new(mesh, faceIndices = null){ 14 | _mesh = mesh; 15 | if (faceIndices == null) { 16 | faceIndices = [ for (i in 0...mesh.faces.length) i ]; 17 | } 18 | _faceIndices = faceIndices; 19 | } 20 | 21 | public function split() : Pair, IBoundingBoxTree> { 22 | var as = Mesh.sortTrianglesOnLongestAxis( boundingBox(), _mesh, _faceIndices ) 23 | , l = as.left() 24 | , r = as.right(); 25 | 26 | return new Pair, IBoundingBoxTree>( 27 | new LazyMeshBoundingBoxTree( _mesh, l), 28 | new LazyMeshBoundingBoxTree( _mesh, r )); 29 | } 30 | 31 | public function boundingBox(){ 32 | if (_boundingBox == null){ 33 | _boundingBox = Mesh.makeMeshAabb( _mesh, _faceIndices ); 34 | } 35 | return _boundingBox; 36 | } 37 | 38 | public function yield(){ 39 | return _faceIndices[0]; 40 | } 41 | 42 | public function indivisible( tolerance : Float ){ 43 | return _faceIndices.length == 1; 44 | } 45 | 46 | public function empty(){ 47 | return _faceIndices.length == 0; 48 | } 49 | } -------------------------------------------------------------------------------- /src/verb/core/LazyPolylineBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Data; 4 | import verb.eval.Intersect; 5 | 6 | class LazyPolylineBoundingBoxTree implements IBoundingBoxTree { 7 | 8 | var _interval : Interval; 9 | var _polyline : PolylineData; 10 | var _boundingBox : BoundingBox = null; 11 | 12 | public function new(polyline, interval = null){ 13 | _polyline = polyline; 14 | 15 | if (interval == null) { 16 | interval = new Interval(0, polyline.points.length != 0 ? polyline.points.length-1 : 0); 17 | } 18 | _interval = interval; 19 | } 20 | 21 | public function split() : Pair, IBoundingBoxTree> { 22 | var min = _interval.min; 23 | var max = _interval.max; 24 | 25 | var pivot = min + Math.ceil( (max-min) / 2 ); 26 | 27 | var l = new Interval( min, pivot ) 28 | , r = new Interval( pivot, max ); 29 | 30 | return new Pair, IBoundingBoxTree>( 31 | new LazyPolylineBoundingBoxTree( _polyline, l ), 32 | new LazyPolylineBoundingBoxTree( _polyline, r )); 33 | 34 | } 35 | 36 | public function boundingBox(){ 37 | if (_boundingBox == null){ 38 | _boundingBox = new BoundingBox( _polyline.points ); 39 | } 40 | 41 | return _boundingBox; 42 | } 43 | 44 | public function yield(){ 45 | return _interval.min; 46 | } 47 | 48 | public function indivisible( tolerance : Float ){ 49 | return _interval.max - _interval.min == 1; 50 | } 51 | 52 | public function empty(){ 53 | return _interval.max - _interval.min == 0; 54 | } 55 | } -------------------------------------------------------------------------------- /src/verb/core/LazySurfaceBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | using verb.core.ArrayExtensions; 4 | using verb.core.Vec; 5 | 6 | import verb.eval.Eval; 7 | import verb.eval.Divide; 8 | import verb.core.Data; 9 | import verb.eval.Intersect; 10 | 11 | class LazySurfaceBoundingBoxTree implements IBoundingBoxTree { 12 | 13 | var _surface : NurbsSurfaceData; 14 | var _boundingBox : BoundingBox = null; 15 | var _splitV : Bool; 16 | var _knotTolU : Float; 17 | var _knotTolV : Float; 18 | 19 | public function new(surface, splitV = false, knotTolU = null, knotTolV = null){ 20 | _surface = surface; 21 | _splitV = splitV; 22 | 23 | if (knotTolU == null){ 24 | knotTolU = (surface.knotsU.domain()) / 16; 25 | } 26 | 27 | if (knotTolV == null){ 28 | knotTolV = (surface.knotsV.domain()) / 16; 29 | } 30 | 31 | _knotTolU = knotTolU; 32 | _knotTolV = knotTolV; 33 | } 34 | 35 | public function split() : Pair, IBoundingBoxTree> { 36 | var min : Float; 37 | var max : Float; 38 | 39 | if (_splitV){ 40 | min = _surface.knotsV.first(); 41 | max = _surface.knotsV.last(); 42 | } else { 43 | min = _surface.knotsU.first(); 44 | max = _surface.knotsU.last(); 45 | } 46 | 47 | var dom = max - min; 48 | var pivot = (min + max) / 2.0; //* dom * 0.01 * Math.random(); 49 | 50 | var srfs = Divide.surfaceSplit( _surface, pivot, _splitV ); 51 | 52 | return new Pair, IBoundingBoxTree>( 53 | new LazySurfaceBoundingBoxTree( srfs[0], !_splitV, _knotTolU, _knotTolV ), 54 | new LazySurfaceBoundingBoxTree( srfs[1], !_splitV, _knotTolU, _knotTolV )); 55 | } 56 | 57 | public function boundingBox(){ 58 | if (_boundingBox == null){ 59 | _boundingBox = new BoundingBox(); 60 | for (row in _surface.controlPoints){ 61 | _boundingBox.addRange( Eval.dehomogenize1d(row) ); 62 | } 63 | } 64 | return _boundingBox; 65 | } 66 | 67 | public function yield(){ 68 | return _surface; 69 | } 70 | 71 | public function indivisible( tolerance : Float ){ 72 | return _surface.knotsV.domain() < _knotTolV && _surface.knotsU.domain() < _knotTolU; 73 | } 74 | 75 | public function empty(){ 76 | return false; 77 | } 78 | } -------------------------------------------------------------------------------- /src/verb/core/Mat.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Vec; 4 | import verb.core.Data; 5 | 6 | // Tools for working with matrices 7 | 8 | @:expose("core.Mat") 9 | class Mat { 10 | 11 | // Multiply a `Matrix` by a constant 12 | 13 | public static function mul(a : Float, b : Matrix ) : Matrix { 14 | return [ for (i in 0...b.length) Vec.mul(a, b[i]) ]; 15 | } 16 | 17 | // Multiply two matrices assuming they are of compatible dimensions. 18 | // 19 | // Based on the numeric.js routine - `numeric.dotMMsmall` 20 | 21 | public static function mult(x : Matrix, y : Matrix) : Matrix { 22 | 23 | var p,q,r,ret,foo,bar,woo,i0,k0,p0,r0; 24 | 25 | p = x.length; q = y.length; r = y[0].length; 26 | ret = new Matrix(); 27 | 28 | var i = p-1; 29 | var j = 0; 30 | var k = 0; 31 | 32 | while (i>=0){ 33 | foo = new Vector(); 34 | bar = x[i]; 35 | 36 | k = r-1; 37 | while( k >= 0 ){ 38 | woo = bar[q-1]*y[q-1][k]; 39 | 40 | j = q-2; 41 | while ( j >= 1 ){ 42 | i0 = j-1; 43 | woo += bar[j]*y[j][k] + bar[i0]*y[i0][k]; 44 | j -= 2; 45 | } 46 | if(j==0) { woo += bar[0]*y[0][k]; } 47 | foo[k] = woo; 48 | k--; 49 | } 50 | ret[i] = foo; 51 | i--; 52 | } 53 | return ret; 54 | } 55 | 56 | // Add two matrices 57 | 58 | public static function add(a : Matrix, b : Matrix ) : Matrix { 59 | return [ for (i in 0...a.length) Vec.add(a[i], b[i]) ]; 60 | } 61 | 62 | // Divide each of entry of a Matrix by a constant 63 | 64 | public static function div(a : Matrix, b : Float ) : Matrix { 65 | return [ for (i in 0...a.length) Vec.div(a[i], b) ]; 66 | } 67 | 68 | // Subtract two matrices 69 | 70 | public static function sub(a : Matrix, b : Matrix ) : Matrix { 71 | return [ for (i in 0...a.length) Vec.sub(a[i], b[i]) ]; 72 | } 73 | 74 | // Multiply a `Matrix` by a `Vector` 75 | 76 | public static function dot(a : Matrix, b : Vector ) : Vector { 77 | return [ for (i in 0...a.length) Vec.dot(a[i], b) ]; 78 | } 79 | 80 | // Build an identity matrix of a given size 81 | 82 | public static function identity(n : Int) : Matrix { 83 | var zeros = Vec.zeros2d(n, n); 84 | for (i in 0...n){ zeros[i][i] = 1.0; } 85 | return zeros; 86 | } 87 | 88 | // Transpose a matrix 89 | 90 | public static function transpose(a : Array>) : Array> { 91 | if (a.length == 0) return []; 92 | return [ for (i in 0...a[0].length) [for (j in 0...a.length) a[j][i] ] ]; 93 | } 94 | 95 | // Solve a system of equations 96 | 97 | public static function solve(A : Matrix, b : Vector) : Vector { 98 | return LUsolve( LU(A), b ); 99 | } 100 | 101 | // Based on methods from numeric.js 102 | 103 | private static function LUsolve(LUP : LUDecomp, b : Vector) : Vector { 104 | var i, j; 105 | var LU = LUP.LU; 106 | var n = LU.length; 107 | var x = b.copy(); 108 | var P = LUP.P; 109 | var Pi, LUi, LUii, tmp; 110 | 111 | i = n-1; 112 | while ( i != -1 ){ 113 | x[i] = b[i]; 114 | --i; 115 | } 116 | 117 | i = 0; 118 | while (i < n){ 119 | Pi = P[i]; 120 | if (P[i] != i) { 121 | tmp = x[i]; 122 | x[i] = x[Pi]; 123 | x[Pi] = tmp; 124 | } 125 | 126 | LUi = LU[i]; 127 | j = 0; 128 | while (j < i){ 129 | x[i] -= x[j] * LUi[j]; 130 | ++j; 131 | } 132 | ++i; 133 | } 134 | 135 | i = n-1; 136 | while (i >= 0){ 137 | LUi = LU[i]; 138 | j = i+1; 139 | while (j < n){ 140 | x[i] -= x[j] * LUi[j]; 141 | ++j; 142 | } 143 | 144 | x[i] /= LUi[i]; 145 | --i; 146 | } 147 | 148 | return x; 149 | } 150 | 151 | // Based on methods from numeric.js 152 | 153 | private static function LU( A : Matrix ) : LUDecomp { 154 | 155 | var abs = Math.abs; 156 | var i, j, k, absAjk, Akk, Ak, Pk, Ai; 157 | var max; 158 | //copy A 159 | A = [ for (i in 0...A.length) A[i].copy() ]; 160 | var n = A.length, n1 = n-1; 161 | var P = new Array(); //new Array(n); 162 | 163 | k = 0; 164 | while (k < n){ 165 | Pk = k; 166 | Ak = A[k]; 167 | max = Math.abs(Ak[k]); 168 | 169 | j = k+1; 170 | while (j < n){ 171 | absAjk = Math.abs(A[j][k]); 172 | if (max < absAjk) { 173 | max = absAjk; 174 | Pk = j; 175 | } 176 | ++j; 177 | } 178 | P[k] = Pk; 179 | 180 | if (Pk != k) { 181 | A[k] = A[Pk]; 182 | A[Pk] = Ak; 183 | Ak = A[k]; 184 | } 185 | 186 | Akk = Ak[k]; 187 | 188 | i = k+1; 189 | while (i < n){ 190 | A[i][k] /= Akk; 191 | ++i; 192 | } 193 | 194 | i = k+1; 195 | while (i < n){ 196 | Ai = A[i]; 197 | j = k+1; 198 | while (j < n1){ 199 | Ai[j] -= Ai[k] * Ak[j]; 200 | ++j; 201 | Ai[j] -= Ai[k] * Ak[j]; 202 | ++j; 203 | } 204 | if(j==n1) Ai[j] -= Ai[k] * Ak[j]; 205 | ++i; 206 | } 207 | 208 | ++k; 209 | } 210 | 211 | return new LUDecomp(A, P); 212 | } 213 | 214 | } 215 | 216 | // Based on methods from numeric.js 217 | 218 | private class LUDecomp { 219 | 220 | public var LU : Matrix; 221 | public var P : Array; 222 | public function new( lu : Matrix, p : Array ){ 223 | this.LU = lu; 224 | this.P = p; 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /src/verb/core/Mesh.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.BoundingBox; 4 | import verb.core.Data; 5 | 6 | using verb.core.ArrayExtensions; 7 | 8 | // `Mesh` provides various convenience methods for working with meshes. 9 | 10 | @:expose("core.Mesh") 11 | class Mesh { 12 | 13 | //Get triangle normal 14 | // 15 | //**params** 16 | // 17 | //* array of length 3 arrays of numbers representing the points 18 | //* length 3 array of point indices for the triangle 19 | // 20 | //**returns** 21 | // 22 | //* a normal vector represented by an array of length 3 23 | // 24 | 25 | public static function getTriangleNorm( points : Array, tri : Tri ) : Point { 26 | 27 | var v0 = points[ tri[0] ] 28 | , v1 = points[ tri[1] ] 29 | , v2 = points[ tri[2] ] 30 | , u = Vec.sub( v1, v0 ) 31 | , v = Vec.sub( v2, v0 ) 32 | , n = Vec.cross( u, v ); 33 | 34 | return Vec.mul( 1 / Vec.norm( n ), n ); 35 | 36 | } 37 | 38 | //Form axis-aligned bounding box from triangles of mesh 39 | // 40 | //**params** 41 | // 42 | //* a mesh 43 | //* face indices of the mesh to include in the bounding box 44 | // 45 | //**returns** 46 | // 47 | //* a BoundingBox containing the mesh 48 | // 49 | 50 | public static function makeMeshAabb( mesh : MeshData, faceIndices : Array ) : BoundingBox { 51 | 52 | var bb = new verb.core.BoundingBox(); 53 | 54 | for ( x in faceIndices ){ 55 | bb.add( mesh.points[ mesh.faces[ x ][0] ] ); 56 | bb.add( mesh.points[ mesh.faces[ x ][1] ] ); 57 | bb.add( mesh.points[ mesh.faces[ x ][2] ] ); 58 | } 59 | 60 | return bb; 61 | } 62 | 63 | //Sort particular faces of a mesh on the longest axis 64 | // 65 | //**params** 66 | // 67 | //* bounding box containing the faces 68 | //* the mesh it self 69 | //* the indices of the mesh faces to inspect 70 | // 71 | //**returns** 72 | // 73 | //* a point represented by an array of length (dim) 74 | // 75 | 76 | public static function sortTrianglesOnLongestAxis( bb : BoundingBox, mesh : MeshData, faceIndices : Array ) : Array { 77 | 78 | var longAxis = bb.getLongestAxis(); 79 | 80 | var minCoordFaceMap = new Array>(); 81 | for ( faceIndex in faceIndices){ 82 | var tri_min = getMinCoordOnAxis( mesh.points, mesh.faces[ faceIndex ], longAxis ); 83 | minCoordFaceMap.push( new Pair(tri_min, faceIndex) ); 84 | } 85 | 86 | minCoordFaceMap.sort(function(a : Pair, b : Pair):Int { 87 | var a0 = a.item0; 88 | var b0 = b.item0; 89 | 90 | if (a0 == b0) return 0 else if (a0 > b0) return 1 else return -1; 91 | }); 92 | 93 | var sortedFaceIndices = new Array(); 94 | for ( i in 0...minCoordFaceMap.length){ 95 | sortedFaceIndices.push( minCoordFaceMap[i].item1 ); 96 | } 97 | 98 | return sortedFaceIndices; 99 | } 100 | 101 | //Get min coordinate on an axis 102 | // 103 | //**params** 104 | // 105 | //* array of length 3 arrays of numbers representing the points 106 | //* length 3 array of point indices for the triangle 107 | //* index of the axis to test - 0 for x, 1 for y, 2 for z 108 | // 109 | //**returns** 110 | // 111 | //* the minimum coordinate 112 | 113 | private static function getMinCoordOnAxis( points : Array, tri : Tri, axis : Int ) : Float { 114 | 115 | var min = Math.POSITIVE_INFINITY; 116 | 117 | for (i in 0...3){ 118 | var coord = points[ tri[i] ][ axis ]; 119 | if (coord < min) min = coord; 120 | } 121 | 122 | return min; 123 | } 124 | 125 | //Get triangle centroid 126 | // 127 | //**params** 128 | // 129 | //* array of length 3 arrays of numbers representing the points 130 | //* length 3 array of point indices for the triangle 131 | // 132 | //**returns** 133 | // 134 | //* a point represented by an array of length 3 135 | // 136 | 137 | public static function getTriangleCentroid( points : Array, tri : Tri ) : Point { 138 | 139 | var centroid = [0.0,0.0,0.0]; 140 | 141 | for (i in 0...3){ 142 | for (j in 0...3){ 143 | centroid[j] += points[ tri[i] ][j]; 144 | } 145 | } 146 | 147 | for (i in 0...3){ 148 | centroid[i] /= 3; 149 | } 150 | 151 | return centroid; 152 | 153 | } 154 | 155 | //Given a point on a mesh triangle, obtain the UV on the triangle 156 | // 157 | //**params** 158 | // 159 | //* the mesh 160 | //* index of the face to test 161 | // 162 | //**returns** 163 | // 164 | //* the UV on the face 165 | 166 | public static function triangleUVFromPoint( mesh : MeshData, faceIndex : Int, f : Point ) : UV { 167 | 168 | var tri = mesh.faces[faceIndex]; 169 | 170 | var p1 = mesh.points[ tri[0] ]; 171 | var p2 = mesh.points[ tri[1] ]; 172 | var p3 = mesh.points[ tri[2] ]; 173 | 174 | var uv1 = mesh.uvs[ tri[0] ]; 175 | var uv2 = mesh.uvs[ tri[1] ]; 176 | var uv3 = mesh.uvs[ tri[2] ]; 177 | 178 | var f1 = Vec.sub(p1, f); 179 | var f2 = Vec.sub(p2, f); 180 | var f3 = Vec.sub(p3, f); 181 | 182 | //calculate the areas and factors (order of parameters doesn't matter): 183 | var a = Vec.norm( Vec.cross( Vec.sub(p1, p2), Vec.sub(p1, p3) ) ); //main triangle area a 184 | var a1 = Vec.norm( Vec.cross(f2, f3) ) / a; //p1's triangle area / a 185 | var a2 = Vec.norm( Vec.cross(f3, f1) ) / a; //p2's triangle area / a 186 | var a3 = Vec.norm( Vec.cross(f1, f2) ) / a; //p3's triangle area / a 187 | 188 | //find the uv corresponding to point f (uv1/uv2/uv3 are associated to p1/p2/p3): 189 | return Vec.add( Vec.mul( a1, uv1), Vec.add( Vec.mul( a2, uv2), Vec.mul( a3, uv3))); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/verb/core/MeshBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | using verb.core.ArrayExtensions; 4 | 5 | import verb.core.Data; 6 | import verb.eval.Intersect; 7 | 8 | class MeshBoundingBoxTree implements IBoundingBoxTree { 9 | 10 | var _children : Pair, IBoundingBoxTree>; 11 | var _boundingBox : BoundingBox; 12 | var _face : Int = -1; 13 | var _empty : Bool = false; 14 | 15 | public function new(mesh : MeshData, faceIndices : Array = null){ 16 | 17 | if (faceIndices == null) { 18 | faceIndices = [ for (i in 0...mesh.faces.length) i ]; 19 | } 20 | 21 | _boundingBox = Mesh.makeMeshAabb( mesh, faceIndices ); 22 | 23 | if (faceIndices.length < 1) { 24 | _empty = true; 25 | return; 26 | } else if (faceIndices.length < 2){ 27 | _face = faceIndices[0]; 28 | return; 29 | } 30 | 31 | var as = Mesh.sortTrianglesOnLongestAxis( _boundingBox, mesh, faceIndices ); 32 | var l = as.left(); 33 | var r = as.right(); 34 | 35 | _children = new Pair, IBoundingBoxTree>( 36 | new MeshBoundingBoxTree(mesh, l), 37 | new MeshBoundingBoxTree(mesh, r) 38 | ); 39 | } 40 | 41 | public function split() : Pair, IBoundingBoxTree> { 42 | return _children; 43 | } 44 | 45 | public function boundingBox(){ 46 | return _boundingBox; 47 | } 48 | 49 | public function yield(){ 50 | return _face; 51 | } 52 | 53 | public function indivisible( tolerance : Float ){ 54 | return _children == null; 55 | } 56 | 57 | public function empty(){ 58 | return _empty; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/verb/core/Minimizer.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | import verb.core.Mat; 3 | 4 | import verb.core.Vec; 5 | import verb.core.Data; 6 | 7 | @:expose("core.Minimizer") 8 | class Minimizer { 9 | 10 | public static function uncmin(f : Vector -> Float, x0 : Vector, tol : Float = null, gradient : Vector -> Vector= null, maxit : Int = null) : MinimizationResult { 11 | 12 | if(tol == null) { tol = 1e-8; } 13 | if(gradient == null) { gradient = function(x) { return numericalGradient(f,x); }; } 14 | if(maxit == null) maxit = 1000; 15 | 16 | x0 = x0.slice(0); 17 | var n = x0.length; 18 | var f0 = f(x0),f1 = f0, df0; 19 | 20 | if(Math.isNaN(f0)) throw 'uncmin: f(x0) is a NaN!'; 21 | 22 | tol = Math.max(tol, Constants.EPSILON); 23 | var step,g0,g1,H1 = Mat.identity(n); 24 | var it=0,i,s =[],x1,y,Hy,Hs,ys,i0,t,nstep,t1,t2; 25 | var msg = ""; 26 | g0 = gradient( x0 ); 27 | 28 | while(it < maxit) { 29 | 30 | if(!Vec.all(Vec.finite(g0))) { msg = "Gradient has Infinity or NaN"; break; } 31 | step = Vec.neg(Mat.dot(H1,g0)); 32 | 33 | if(!Vec.all(Vec.finite(step))) { msg = "Search direction has Infinity or NaN"; break; } 34 | 35 | nstep = Vec.norm(step); 36 | if(nstep < tol) { msg= "Newton step smaller than tol"; break; } 37 | 38 | t = 1.0; 39 | df0 = Vec.dot(g0,step); 40 | 41 | //line search 42 | x1 = x0; 43 | while(it < maxit) { 44 | if(t*nstep < tol) { break; } 45 | s = Vec.mul(t, step); 46 | x1 = Vec.add(x0,s); 47 | f1 = f(x1); 48 | if(f1-f0 >= 0.1*t*df0 || Math.isNaN(f1)) { 49 | t *= 0.5; 50 | ++it; 51 | continue; 52 | } 53 | break; 54 | } 55 | 56 | if(t*nstep < tol) { msg = "Line search step size smaller than tol"; break; } 57 | if(it == maxit) { msg = "maxit reached during line search"; break; } 58 | 59 | g1 = gradient(x1); 60 | y = Vec.sub(g1,g0); 61 | ys = Vec.dot(y,s); 62 | Hy = Mat.dot(H1,y); 63 | H1 = Mat.sub( 64 | Mat.add(H1, Mat.mul( (ys+Vec.dot(y,Hy))/(ys*ys), tensor(s,s) )), 65 | Mat.div(Mat.add(tensor(Hy,s),tensor(s,Hy)),ys)); 66 | x0 = x1; 67 | f0 = f1; 68 | g0 = g1; 69 | ++it; 70 | } 71 | 72 | return new MinimizationResult( x0, f0, g0, H1, it, msg); 73 | } 74 | 75 | private static function numericalGradient(f : Vector -> Float, x : Vector) : Vector { 76 | 77 | var n = x.length; 78 | var f0 = f(x); 79 | 80 | if(f0 == Math.NaN) throw 'gradient: f(x) is a NaN!'; 81 | 82 | var i, x0 = x.slice(0),f1,f2, J = []; 83 | 84 | var errest,roundoff,eps = 1e-3; 85 | var t0,t1,t2,it=0,d1,d2,N; 86 | 87 | for (i in 0...n){ 88 | 89 | var h = Math.max(1e-6 * f0, 1e-8); 90 | 91 | while(true) { 92 | ++it; 93 | if( it>20 ) { throw "Numerical gradient fails"; } 94 | x0[i] = x[i]+h; 95 | f1 = f(x0); 96 | x0[i] = x[i]-h; 97 | f2 = f(x0); 98 | x0[i] = x[i]; 99 | 100 | if(Math.isNaN(f1) || Math.isNaN(f2)) { h/=16; continue; } 101 | 102 | J[i] = (f1-f2)/(2*h); 103 | t0 = x[i]-h; 104 | t1 = x[i]; 105 | t2 = x[i]+h; 106 | d1 = (f1-f0)/h; 107 | d2 = (f0-f2)/h; 108 | N = Vec.max([ Math.abs(J[i]), Math.abs(f0), Math.abs(f1), Math.abs(f2), Math.abs(t0), Math.abs(t1), Math.abs(t2), 1e-8 ]); 109 | 110 | errest = Math.min( Vec.max([Math.abs(d1-J[i]), Math.abs(d2-J[i]), Math.abs(d1-d2)])/N, h/N); 111 | 112 | if(errest>eps) { h/=16; } else break; 113 | } 114 | } 115 | 116 | return J; 117 | } 118 | 119 | private static function tensor(x : Vector, y : Vector) : Matrix { 120 | 121 | var m = x.length, n = y.length, A = [], Ai, xi; 122 | 123 | var i = m-1; 124 | while (i >= 0){ 125 | Ai = []; 126 | xi = x[i]; 127 | var j = n-1; 128 | while(j >= 3){ 129 | Ai[j] = xi * y[j]; 130 | --j; 131 | Ai[j] = xi * y[j]; 132 | --j; 133 | Ai[j] = xi * y[j]; 134 | --j; 135 | Ai[j] = xi * y[j]; 136 | --j; 137 | } 138 | while(j>=0) { Ai[j] = xi * y[j]; --j; } 139 | A[i] = Ai; 140 | i--; 141 | } 142 | return A; 143 | } 144 | 145 | } 146 | 147 | class MinimizationResult { 148 | 149 | public var solution : Vector; 150 | public var value : Float; 151 | public var gradient : Vector; 152 | public var invHessian : Matrix; 153 | public var iterations : Int; 154 | public var message : String; 155 | 156 | public function new(solution, value, gradient, invHessian, iterations, message){ 157 | this.solution = solution; 158 | this.value = value; 159 | this.gradient = gradient; 160 | this.invHessian = invHessian; 161 | this.iterations = iterations; 162 | this.message = message; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/verb/core/Serialization.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import haxe.Serializer; 4 | import haxe.Unserializer; 5 | 6 | // An interface describing a type that can be serialized as a 7 | // string. Use verb.core.Deserializer to construct an instance of the 8 | // the type from the resultant string. The string is the serialized representation of a haxe 9 | // object and is strongly typed. For details, see 10 | // [http://haxe.org/manual/std-serialization.html](http://haxe.org/manual/std-serialization.html) for details. 11 | 12 | interface ISerializable { 13 | function serialize() : String; 14 | } 15 | 16 | // Forms a base class for serializable data types 17 | 18 | @:expose("core.SerializableBase") 19 | class SerializableBase { 20 | public function serialize() : String { 21 | var serializer = new Serializer(); 22 | serializer.serialize(this); 23 | return serializer.toString(); 24 | } 25 | } 26 | 27 | // Deserializes strings for types implementing ISerializable 28 | 29 | @:expose("core.Deserializer") 30 | class Deserializer { 31 | 32 | //Construct an ISerializable from its string representation, given a parameter T. You can 33 | //use this to deserialize almost any type in verb.geom or verb.core.*Data types. 34 | // 35 | //**params** 36 | // 37 | //* A string representing something implementing ISerializable 38 | // 39 | //**returns** 40 | // 41 | //* A new T from the string 42 | 43 | public static function deserialize(s : String) : T { 44 | var unserializer = new Unserializer(s); 45 | var r : T = unserializer.unserialize(); 46 | return r; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/verb/core/SurfaceBoundingBoxTree.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Data; 4 | using verb.core.ArrayExtensions; 5 | using verb.core.Vec; 6 | import verb.eval.Divide; 7 | import verb.eval.Eval; 8 | import verb.eval.Intersect; 9 | 10 | class SurfaceBoundingBoxTree implements IBoundingBoxTree { 11 | 12 | var _children : Pair, IBoundingBoxTree>; 13 | var _surface : NurbsSurfaceData; 14 | var _boundingBox : BoundingBox = null; 15 | 16 | public function new(surface, splitV = false, knotTolU = null, knotTolV = null){ 17 | _surface = surface; 18 | 19 | if (knotTolU == null){ 20 | knotTolU = (surface.knotsU.domain()) / 16; 21 | } 22 | 23 | if (knotTolV == null){ 24 | knotTolV = (surface.knotsV.domain()) / 16; 25 | } 26 | 27 | var divisible = false; 28 | 29 | if (splitV){ 30 | divisible = _surface.knotsV.domain() > knotTolV; 31 | } else { 32 | divisible = _surface.knotsU.domain() > knotTolU; 33 | } 34 | 35 | if ( !divisible ) return; 36 | 37 | var min : Float; 38 | var max : Float; 39 | 40 | if (splitV){ 41 | min = _surface.knotsV.first(); 42 | max = _surface.knotsV.last(); 43 | } else { 44 | min = _surface.knotsU.first(); 45 | max = _surface.knotsU.last(); 46 | } 47 | 48 | var dom = max - min; 49 | var pivot = (min + max) / 2.0 + dom * 0.1 * Math.random(); 50 | 51 | var srfs = Divide.surfaceSplit( _surface, pivot, splitV ); 52 | 53 | _children = new Pair, IBoundingBoxTree>( 54 | new SurfaceBoundingBoxTree( srfs[0], !splitV, knotTolU, knotTolV ), 55 | new SurfaceBoundingBoxTree( srfs[1], !splitV, knotTolU, knotTolV )); 56 | 57 | } 58 | 59 | public function split() : Pair, IBoundingBoxTree> { 60 | return _children; 61 | } 62 | 63 | public function boundingBox(){ 64 | if (_boundingBox == null){ 65 | _boundingBox = new BoundingBox(); 66 | for (row in _surface.controlPoints){ 67 | _boundingBox.addRange( Eval.dehomogenize1d(row) ); 68 | } 69 | } 70 | return _boundingBox; 71 | } 72 | 73 | public function yield(){ 74 | return _surface; 75 | } 76 | 77 | public function indivisible( tolerance : Float ){ 78 | return _children == null; 79 | } 80 | 81 | public function empty(){ 82 | return false; 83 | } 84 | } -------------------------------------------------------------------------------- /src/verb/core/Trig.hx: -------------------------------------------------------------------------------- 1 | package verb.core; 2 | 3 | import verb.core.Vec; 4 | import verb.core.Data; 5 | import verb.core.Intersections; 6 | using verb.core.Vec; 7 | 8 | // `Trig` provides various convenient methods for trigonometry 9 | 10 | @:expose("core.Trig") 11 | class Trig { 12 | 13 | public static function isPointInPlane( pt : Point, p : Plane, tol : Float ) : Bool{ 14 | return Math.abs( pt.sub( p.origin ).dot( p.normal ) ) < tol; 15 | } 16 | 17 | public static function distToSegment(a : Point, b : Point, c : Point){ 18 | var res = segmentClosestPoint( b, a, c, 0.0, 1.0 ); 19 | return Vec.dist( b, res.pt ); 20 | } 21 | 22 | //Find the closest point on a ray 23 | // 24 | //**params** 25 | // 26 | //* point to project 27 | //* origin for ray 28 | //* direction of ray 1, assumed normalized 29 | // 30 | //**returns** 31 | // 32 | //* pt 33 | 34 | public static function rayClosestPoint( pt, o, r ) { 35 | var o2pt = Vec.sub(pt,o) 36 | , do2ptr = Vec.dot(o2pt, r) 37 | , proj = Vec.add(o, Vec.mul(do2ptr, r)); 38 | 39 | return proj; 40 | } 41 | 42 | //Find the distance of a point to a ray 43 | // 44 | //**params** 45 | // 46 | //* point to project 47 | //* origin for ray 48 | //* direction of ray 1, assumed normalized 49 | // 50 | //**returns** 51 | // 52 | //* the distance 53 | 54 | public static function distToRay( pt, o, r ) { 55 | var d = rayClosestPoint( pt, o, r ); 56 | var dif = Vec.sub( d, pt ); 57 | 58 | return Vec.norm( dif ); 59 | 60 | } 61 | 62 | //Determine if three points form a straight line within a given tolerance for their 2 * squared area 63 | // 64 | // * p2 65 | // / \ 66 | // / \ 67 | // / \ 68 | // / \ 69 | // * p1 ---- * p3 70 | // 71 | //The area metric is 2 * the squared norm of the cross product of two edges, requiring no square roots and no divisions 72 | // 73 | //**params** 74 | // 75 | //* p1 76 | //* p2 77 | //* p3 78 | //* The tolerance 79 | // 80 | //**returns** 81 | // 82 | //* Whether the triangle passes the test 83 | 84 | public static function threePointsAreFlat( p1, p2, p3, tol ) { 85 | 86 | //find the area of the triangle without using a square root 87 | var p2mp1 = Vec.sub( p2, p1 ) 88 | , p3mp1 = Vec.sub( p3, p1 ) 89 | , norm = Vec.cross( p2mp1, p3mp1 ) 90 | , area = Vec.dot( norm, norm ); 91 | 92 | return area < tol; 93 | 94 | } 95 | 96 | //Find the closest point on a segment 97 | // 98 | //**params** 99 | // 100 | //* point to project 101 | //* first point of segment 102 | //* second point of segment 103 | //* first param of segment 104 | //* second param of segment 105 | // 106 | //**returns** 107 | // 108 | //* *Object* with u and pt properties 109 | 110 | public static function segmentClosestPoint( pt : Point, segpt0 : Point, segpt1 : Point, u0 : Float, u1 : Float ) { 111 | 112 | var dif = Vec.sub( segpt1, segpt0 ) 113 | , l = Vec.norm( dif ); 114 | 115 | if (l < Constants.EPSILON ) { 116 | return { u: u0, pt : segpt0 }; 117 | } 118 | 119 | var o = segpt0 120 | , r = Vec.mul( 1 / l, dif ) 121 | , o2pt = Vec.sub(pt, o) 122 | , do2ptr = Vec.dot(o2pt, r); 123 | 124 | if (do2ptr < 0){ 125 | return { u: u0, pt : segpt0 }; 126 | } else if (do2ptr > l){ 127 | return { u: u1, pt : segpt1 }; 128 | } 129 | 130 | return { u: u0 + (u1 - u0) * do2ptr / l, pt : Vec.add(o, Vec.mul( do2ptr, r ) ) }; 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/verb/eval/Check.hx: -------------------------------------------------------------------------------- 1 | package verb.eval; 2 | 3 | import verb.core.Data; 4 | 5 | import verb.core.Constants; 6 | 7 | import verb.core.ArrayExtensions; 8 | using verb.core.ArrayExtensions; 9 | 10 | // `Check` includes various tools for checking the validity of various NURBS data structures. This is important because it is 11 | // very easy to construct such data structures with incorrect structure. This class contains static, immutable functions for 12 | // doing those checks. 13 | // 14 | // **Note that the classes in verb.eval are very tolerant of incorrect NURBS data structures for performance reasons.** You should 15 | // perform these checks before using these classes. 16 | 17 | @:expose("eval.Check") 18 | class Check { 19 | 20 | //Check whether a given array is a valid NURBS knot vector. This also checks the validity of the end points. 21 | //More specifically, this method checks if the knot vector is of the following structure: 22 | // 23 | // The knot vector must be non-decreasing and of length (degree + 1) * 2 or greater 24 | // 25 | // [ (degree + 1 copies of the first knot), internal non-decreasing knots, (degree + 1 copies of the last knot) ] 26 | // 27 | //**params** 28 | // 29 | //* The knot vector to test 30 | //* The degree 31 | // 32 | //**returns** 33 | // 34 | //* Whether the array is a valid knot vector or knot 35 | 36 | public static function isValidKnotVector(vec : Array, degree : Int) : Bool { 37 | 38 | if (vec.length == 0) return false; 39 | if (vec.length < (degree + 1) * 2 ) return false; 40 | 41 | var rep = vec.first(); 42 | 43 | for (i in 0...degree+1){ 44 | if (Math.abs(vec[i]-rep) > Constants.EPSILON) return false; 45 | } 46 | 47 | rep = vec.last(); 48 | 49 | for (i in vec.length-degree-1...vec.length){ 50 | if (Math.abs(vec[i]-rep) > Constants.EPSILON) return false; 51 | } 52 | 53 | return isNonDecreasing( vec ); 54 | } 55 | 56 | //Check if an array of floating point numbers is non-decreasing, although there may be repeats. This is an important 57 | //validation step for NURBS knot vectors. 58 | // 59 | //**params** 60 | // 61 | //* The data object 62 | // 63 | //**returns** 64 | // 65 | //* Whether the array is non-decreasing 66 | 67 | public static function isNonDecreasing(vec : Array){ 68 | var rep = vec.first(); 69 | for ( i in 0...vec.length ){ 70 | if (vec[i] < rep - Constants.EPSILON ) return false; 71 | rep = vec[i]; 72 | } 73 | return true; 74 | } 75 | 76 | //Validate a NurbsCurveData object 77 | // 78 | //**params** 79 | // 80 | //* The data object 81 | // 82 | //**returns** 83 | // 84 | //* The original, unmodified data 85 | 86 | public static function isValidNurbsCurveData( data : NurbsCurveData ) : NurbsCurveData { 87 | if ( data.controlPoints == null ) throw "Control points array cannot be null!"; 88 | #if (!cpp && !cs && !java) 89 | if ( data.degree == null ) throw "Degree cannot be null!"; 90 | #end 91 | if ( data.degree < 1 ) throw "Degree must be greater than 1!"; 92 | if ( data.knots == null ) throw "Knots cannot be null!"; 93 | 94 | if ( data.knots.length != data.controlPoints.length + data.degree + 1 ){ 95 | throw "controlPoints.length + degree + 1 must equal knots.length!"; 96 | } 97 | 98 | if (!Check.isValidKnotVector( data.knots, data.degree )){ 99 | throw "Invalid knot vector format! Should begin with degree + 1 repeats and end with degree + 1 repeats!"; 100 | } 101 | 102 | return data; 103 | } 104 | 105 | //Validate a NurbsSurfaceData object 106 | // 107 | //**params** 108 | // 109 | //* The data object 110 | // 111 | //**returns** 112 | // 113 | //* The original, unmodified data 114 | 115 | public static function isValidNurbsSurfaceData( data : NurbsSurfaceData ) : NurbsSurfaceData { 116 | if ( data.controlPoints == null ) throw "Control points array cannot be null!"; 117 | #if (!cpp && !cs && !java) 118 | if ( data.degreeU == null ) throw "DegreeU cannot be null!"; 119 | if ( data.degreeV == null ) throw "DegreeV cannot be null!"; 120 | #end 121 | if ( data.degreeU < 1 ) throw "DegreeU must be greater than 1!"; 122 | if ( data.degreeV < 1 ) throw "DegreeV must be greater than 1!"; 123 | if ( data.knotsU == null ) throw "KnotsU cannot be null!"; 124 | if ( data.knotsV == null ) throw "KnotsV cannot be null!"; 125 | 126 | if ( data.knotsU.length != data.controlPoints.length + data.degreeU + 1 ){ 127 | throw "controlPointsU.length + degreeU + 1 must equal knotsU.length!"; 128 | } 129 | 130 | if ( data.knotsV.length != data.controlPoints[0].length + data.degreeV + 1 ){ 131 | throw "controlPointsV.length + degreeV + 1 must equal knotsV.length!"; 132 | } 133 | 134 | if (!Check.isValidKnotVector( data.knotsU, data.degreeU ) || !Check.isValidKnotVector( data.knotsV, data.degreeV )){ 135 | throw "Invalid knot vector format! Should begin with degree + 1 repeats and end with degree + 1 repeats!"; 136 | } 137 | 138 | return data; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/verb/eval/Divide.hx: -------------------------------------------------------------------------------- 1 | package verb.eval; 2 | 3 | import verb.core.Mat; 4 | import verb.core.Data; 5 | import verb.core.Vec; 6 | import verb.core.Constants; 7 | 8 | using Lambda; 9 | 10 | // Divide provides various tools for dividing and splitting NURBS geometry. 11 | 12 | @:expose("eval.Divide") 13 | class Divide { 14 | 15 | //Split a NURBS surface in two at a given parameter 16 | // 17 | //**params** 18 | // 19 | //* The surface to split 20 | //* The parameter at which to split the surface 21 | //* Whether to split in the U direction or V direction of the surface 22 | // 23 | //**returns** 24 | // 25 | //* A length two array of new surfaces 26 | 27 | public static function surfaceSplit( surface : NurbsSurfaceData, u : Float, useV : Bool = false) : Array { 28 | 29 | var knots 30 | , degree 31 | , controlPoints; 32 | 33 | if (!useV) { 34 | controlPoints = Mat.transpose( surface.controlPoints ); 35 | knots = surface.knotsU; 36 | degree = surface.degreeU; 37 | } else { 38 | controlPoints = surface.controlPoints; 39 | knots = surface.knotsV; 40 | degree = surface.degreeV; 41 | } 42 | 43 | var knots_to_insert = [ for (i in 0...degree+1) u ]; 44 | 45 | var newpts0 = new Array>() 46 | , newpts1 = new Array>(); 47 | 48 | var s = Eval.knotSpan( degree, u, knots ); 49 | var res : NurbsCurveData = null; 50 | 51 | for (cps in controlPoints){ 52 | res = Modify.curveKnotRefine( new NurbsCurveData(degree, knots, cps), knots_to_insert ); 53 | 54 | newpts0.push( res.controlPoints.slice( 0, s + 1 ) ); 55 | newpts1.push( res.controlPoints.slice( s + 1 ) ); 56 | } 57 | 58 | var knots0 = res.knots.slice(0, s + degree + 2); 59 | var knots1 = res.knots.slice( s + 1 ); 60 | 61 | if (!useV){ 62 | newpts0 = Mat.transpose( newpts0 ); 63 | newpts1 = Mat.transpose( newpts1 ); 64 | 65 | return [ new NurbsSurfaceData(degree, surface.degreeV, knots0, surface.knotsV.copy(), newpts0 ), 66 | new NurbsSurfaceData(degree, surface.degreeV, knots1, surface.knotsV.copy(), newpts1 ) ]; 67 | } 68 | 69 | //v dir 70 | return [ new NurbsSurfaceData(surface.degreeU, degree, surface.knotsU.copy(), knots0, newpts0 ), 71 | new NurbsSurfaceData(surface.degreeU, degree, surface.knotsU.copy(), knots1, newpts1 ) ]; 72 | } 73 | 74 | //Split a NURBS curve into two parts at a given parameter 75 | // 76 | //**params** 77 | // 78 | //* NurbsCurveData object representing the curve 79 | //* location to split the curve 80 | // 81 | //**returns** 82 | // 83 | //* *Array* two new curves, defined by degree, knots, and control points 84 | 85 | public static function curveSplit( curve : NurbsCurveData, u : Float ) : Array { 86 | 87 | var degree = curve.degree 88 | , controlPoints = curve.controlPoints 89 | , knots = curve.knots; 90 | 91 | var knots_to_insert = [for (i in 0...degree+1) u]; 92 | var res = Modify.curveKnotRefine( curve, knots_to_insert ); 93 | 94 | var s = Eval.knotSpan( degree, u, knots ); 95 | 96 | var knots0 = res.knots.slice(0, s + degree + 2); 97 | var knots1 = res.knots.slice( s + 1 ); 98 | 99 | var cpts0 = res.controlPoints.slice( 0, s + 1 ); 100 | var cpts1 = res.controlPoints.slice( s + 1 ); 101 | 102 | return [ 103 | new NurbsCurveData( degree, knots0, cpts0 ), 104 | new NurbsCurveData( degree, knots1, cpts1 ) 105 | ]; 106 | 107 | } 108 | 109 | //Divide a NURBS curve given a given number of times, including the end points. The result is not split curves 110 | //but a collection of `CurveLengthSample` objects that can be used for splitting. As with all arc length methods, 111 | //the result is an approximation. 112 | // 113 | //**params** 114 | // 115 | //* NurbsCurveData object representing the curve 116 | //* The number of parts to split the curve into 117 | // 118 | //**returns** 119 | // 120 | //* An array of `CurveLengthSample` objects 121 | 122 | public static function rationalCurveByEqualArcLength(curve : NurbsCurveData, num : Int) : Array { 123 | 124 | var tlen = Analyze.rationalCurveArcLength( curve ); 125 | var inc = tlen / num; 126 | 127 | return Divide.rationalCurveByArcLength(curve, inc); 128 | 129 | } 130 | 131 | //Divide a NURBS curve given a given number of times, including the end points. 132 | // 133 | //**params** 134 | // 135 | //* NurbsCurveData object representing the curve 136 | //* The arc length separating the resultant samples 137 | // 138 | //**returns** 139 | // 140 | //* A sequence of `CurveLengthSample` objects 141 | 142 | public static function rationalCurveByArcLength(curve : NurbsCurveData, l : Float) : Array { 143 | 144 | var crvs = Modify.decomposeCurveIntoBeziers( curve ) 145 | , crvlens = crvs.map(function(x){ return Analyze.rationalBezierCurveArcLength(x); }) 146 | , totlen = Vec.sum(crvlens) 147 | , pts = [ new CurveLengthSample( curve.knots[0], 0.0 ) ]; 148 | 149 | if (l > totlen) return pts; 150 | 151 | var inc = l 152 | , i = 0 153 | , lc = inc 154 | , runsum = 0.0 155 | , runsum1 = 0.0 156 | , u; 157 | 158 | while ( i < crvs.length ){ 159 | 160 | runsum += crvlens[i]; 161 | 162 | while ( lc < runsum + Constants.EPSILON ){ 163 | 164 | u = Analyze.rationalBezierCurveParamAtArcLength( crvs[i], lc - runsum1, Constants.TOLERANCE, crvlens[i] ); 165 | 166 | pts.push( new CurveLengthSample( u, lc ) ); 167 | lc += inc; 168 | 169 | } 170 | 171 | runsum1 += crvlens[i]; 172 | 173 | i++; 174 | 175 | } 176 | 177 | return pts; 178 | 179 | } 180 | 181 | } 182 | 183 | 184 | @:expose("eval.CurveLengthSample") 185 | class CurveLengthSample { 186 | public var u : Float; 187 | public var len : Float; 188 | 189 | public function new(u, len) { 190 | this.u = u; 191 | this.len = len; 192 | } 193 | } 194 | 195 | -------------------------------------------------------------------------------- /src/verb/exe/Dispatcher.hx: -------------------------------------------------------------------------------- 1 | package verb.exe; 2 | 3 | #if js 4 | import verb.exe.WorkerPool; 5 | #else 6 | import verb.exe.ThreadPool; 7 | #end 8 | 9 | import promhx.Deferred; 10 | import promhx.Promise; 11 | 12 | @:expose("exe.Dispatcher") 13 | class Dispatcher { 14 | 15 | public static var THREADS : Int = 1; 16 | 17 | #if js 18 | private static var _workerPool : WorkerPool; 19 | #else 20 | private static var _threadPool : ThreadPool; 21 | #end 22 | 23 | private static var _init : Bool = false; 24 | 25 | private static function init() : Void { 26 | 27 | if (_init) return; 28 | 29 | #if js 30 | _workerPool = new WorkerPool( THREADS ); 31 | #else 32 | _threadPool = new ThreadPool( THREADS ); 33 | #end 34 | 35 | _init = true; 36 | } 37 | 38 | public static function dispatchMethod( classType : Class, methodName : String, args : Array ) : Promise { 39 | 40 | init(); 41 | 42 | var def = new Deferred(); 43 | 44 | var callback = function(x){ 45 | def.resolve( x ); 46 | }; 47 | 48 | #if js 49 | _workerPool.addWork( Type.getClassName( classType ), methodName, args, callback ); 50 | #else 51 | _threadPool.addTask(function(_ : Dynamic){ var r : Dynamic = Reflect.callMethod(classType, Reflect.field(classType, methodName), args ); return r; }, null, callback); 52 | #end 53 | 54 | return new Promise( def ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/verb/exe/ThreadPool.hx: -------------------------------------------------------------------------------- 1 | package verb.exe; 2 | 3 | #if neko 4 | import neko.vm.Thread; 5 | import neko.vm.Mutex; 6 | #elseif cpp 7 | import cpp.vm.Thread; 8 | import cpp.vm.Mutex; 9 | #end 10 | 11 | #if (neko || cpp) 12 | private class PoolThread 13 | { 14 | private var thread:Thread; 15 | private var task:Dynamic->Dynamic; 16 | private var mutex:Mutex; 17 | public var started:Bool; 18 | private var _done:Bool; 19 | public var done(get, never):Bool; 20 | private function get_done():Bool 21 | { 22 | mutex.acquire(); 23 | var d:Bool = _done; 24 | mutex.release(); 25 | return d; 26 | } 27 | private var _result:Dynamic; 28 | public var result(get, never):Dynamic; 29 | private function get_result():Dynamic 30 | { 31 | mutex.acquire(); 32 | var r:Dynamic = _result; 33 | mutex.release(); 34 | return r; 35 | } 36 | 37 | public function new() 38 | { 39 | mutex = new Mutex(); 40 | } 41 | 42 | public function start(task:Dynamic->Dynamic, arg:Dynamic):Void 43 | { 44 | this.task = task; 45 | started = true; 46 | _done = false; 47 | thread = Thread.create(doWork); 48 | thread.sendMessage(arg); 49 | } 50 | 51 | private function doWork():Void 52 | { 53 | var arg:Dynamic = Thread.readMessage(true); 54 | var ret:Dynamic = task(arg); 55 | mutex.acquire(); 56 | _result = ret; 57 | _done = true; 58 | mutex.release(); 59 | } 60 | } 61 | #end 62 | 63 | private typedef Task = 64 | { 65 | var id:Int; 66 | var task:Dynamic->Dynamic; 67 | var done:Bool; 68 | var arg:Dynamic; 69 | #if (neko || cpp) 70 | var thread:PoolThread; 71 | #end 72 | var onFinish:Dynamic->Void; 73 | } 74 | 75 | /** 76 | * ... 77 | * @author Kenton Hamaluik 78 | */ 79 | class ThreadPool 80 | { 81 | #if (neko || cpp) 82 | private var numThreads:Int = 1; 83 | private var threads:Array; 84 | #end 85 | private var tasks:Array; 86 | private var nextID:Int = 0; 87 | 88 | public function new(numThreads:Int) 89 | { 90 | tasks = new Array (); 91 | #if (neko || cpp) 92 | this.numThreads = numThreads; 93 | threads = new Array(); 94 | for (i in 0...this.numThreads) 95 | { 96 | threads.push(new PoolThread()); 97 | } 98 | #end 99 | } 100 | 101 | public function addTask(task:Dynamic->Dynamic, arg:Dynamic, onFinish:Dynamic->Void):Void 102 | { 103 | tasks.push( { id: nextID, task: task, done: false, arg: arg, #if (neko || cpp) thread: null, #end onFinish: onFinish } ); 104 | nextID++; 105 | } 106 | 107 | #if (neko || cpp) 108 | private function allTasksAreDone():Bool 109 | { 110 | for (task in tasks) 111 | if (!task.done) 112 | return false; 113 | return true; 114 | } 115 | 116 | private function getNextFreeThread():PoolThread 117 | { 118 | for (thread in threads) 119 | if (!thread.started) 120 | return thread; 121 | return null; 122 | } 123 | #end 124 | 125 | public function blockRunAllTasks():Void 126 | { 127 | #if (neko || cpp) 128 | while (!allTasksAreDone()) 129 | { 130 | //get a free thread 131 | var thread:PoolThread = getNextFreeThread(); 132 | //but if it doesn't exist, try again 133 | if (thread == null) 134 | continue; 135 | 136 | for (task in tasks) 137 | { 138 | //skip any tasks that are done 139 | if (task.done) 140 | continue; 141 | 142 | //if this task is currently being run, see if it's done yet 143 | if (task.thread != null && task.thread.started) 144 | { 145 | if (task.thread.done) 146 | { 147 | //yay, it finished! 148 | task.done = true; 149 | //reset the thread 150 | task.thread.started = false; 151 | //call the on finish function 152 | if (task.onFinish != null) 153 | task.onFinish(task.thread.result); 154 | } 155 | continue; 156 | } 157 | 158 | //ok, we have a task that needs running 159 | //and a thread to run it 160 | //combine forces! 161 | task.thread = thread; 162 | thread.start(task.task, task.arg); 163 | 164 | //break to try to assign the next thread 165 | break; 166 | } 167 | } 168 | #else 169 | for (task in tasks) 170 | { 171 | if (task.onFinish != null) 172 | task.onFinish(task.task(task.arg)); 173 | } 174 | #end 175 | 176 | //clear the old tasks 177 | tasks = new Array(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/verb/exe/WorkerPool.hx: -------------------------------------------------------------------------------- 1 | package verb.exe; 2 | 3 | #if js 4 | 5 | import haxe.ds.IntMap; 6 | 7 | import js.html.Worker; 8 | 9 | // `WorkerPool` provides a pool of WebWorker objects for concurrent evaluation in JavaScript 10 | 11 | @:expose("exe.WorkerPool") 12 | class WorkerPool { 13 | 14 | private var _queue : Array = []; 15 | private var _pool : Array = []; 16 | private var _working = new IntMap(); 17 | private var _callbacks = new IntMap(); 18 | 19 | // Create a new `WorkerPool` 20 | // 21 | //**params** 22 | // 23 | //* the number of `Worker` threads to form 24 | //* the filename of verb's javascript file - defaults to "verb.js". The final path is formed by concatenating `WorkerPool.basePath` and this. 25 | 26 | public function new( numThreads : Int = 1, fileName : String = "verb.js" ) { 27 | 28 | for (i in 0...numThreads){ 29 | var w : Worker; 30 | try { 31 | w = new Worker( basePath + fileName ); 32 | } catch (e : Dynamic ) { 33 | w = new Worker( basePath + fileName.substring(0,-3) + ".min.js" ); 34 | } 35 | 36 | _pool.push( w ); 37 | } 38 | } 39 | 40 | // The base path to look for verb's source code 41 | 42 | public static var basePath = ""; 43 | 44 | // Add work to perform to the queue 45 | 46 | public function addWork( className : String, 47 | methodName : String, 48 | args : Array, 49 | callback : Dynamic ) : Void { 50 | 51 | var work = new Work( className, methodName, args ); 52 | _callbacks.set(work.id, callback); 53 | _queue.push( work ); 54 | 55 | processQueue(); 56 | } 57 | 58 | private function processQueue() { 59 | 60 | while (_queue.length > 0 && _pool.length > 0) { 61 | 62 | var work = _queue.shift(); 63 | var workId = work.id; 64 | 65 | var worker = _pool.shift(); 66 | 67 | _working.set(workId, worker); 68 | 69 | //upon completing your task... 70 | worker.onmessage = function( e ){ 71 | 72 | _working.remove( workId ); 73 | _pool.push( worker ); 74 | 75 | try { 76 | if ( _callbacks.exists( workId ) ) 77 | { 78 | _callbacks.get( workId )( e.data.result ); 79 | _callbacks.remove( workId ); 80 | } 81 | } catch(error : Dynamic) { 82 | trace( error ); 83 | } 84 | 85 | processQueue(); 86 | }; 87 | 88 | worker.postMessage( work ); 89 | } 90 | } 91 | 92 | } 93 | 94 | private class Work { 95 | 96 | private static var uuid : Int = 0; 97 | 98 | public var className : String; 99 | public var methodName : String; 100 | public var args : Array; 101 | public var id : Int; 102 | 103 | public function new(className, methodName, args){ 104 | this.className = className; 105 | this.methodName = methodName; 106 | this.args = args; 107 | this.id = uuid++; 108 | } 109 | } 110 | 111 | #end 112 | 113 | -------------------------------------------------------------------------------- /src/verb/geom/Arc.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Eval; 4 | import verb.eval.Make; 5 | import verb.core.Vec; 6 | import verb.core.Data; 7 | 8 | import haxe.Serializer; 9 | import haxe.Unserializer; 10 | 11 | // An Arc is a three dimensional curve representing a subset of a full Circle 12 | 13 | @:expose("geom.Arc") 14 | class Arc extends NurbsCurve { 15 | 16 | //Constructor for Arc 17 | // 18 | //**params** 19 | // 20 | //* Length 3 array representing the center of the arc 21 | //* Length 3 array representing the xaxis 22 | //* Length 3 array representing the perpendicular yaxis 23 | //* Radius of the arc arc 24 | //* Start angle in radians 25 | //* End angle in radians 26 | 27 | public function new( center : Point, 28 | xaxis : Vector, 29 | yaxis : Vector, 30 | radius : Float, 31 | minAngle : Float, 32 | maxAngle : Float ) { 33 | super( Make.arc(center, xaxis, yaxis, radius, minAngle, maxAngle ) ); 34 | 35 | _center = center; 36 | _xaxis = xaxis; 37 | _yaxis = yaxis; 38 | _radius = radius; 39 | _minAngle = minAngle; 40 | _maxAngle = maxAngle; 41 | } 42 | 43 | private var _center : Point; 44 | private var _xaxis : Vector; 45 | private var _yaxis : Vector; 46 | private var _radius : Float; 47 | private var _minAngle : Float; 48 | private var _maxAngle : Float; 49 | 50 | //Length 3 array representing the center of the arc 51 | public function center() : Point { return _center; } 52 | 53 | //Length 3 array representing the xaxis 54 | public function xaxis() : Vector { return _xaxis; } 55 | 56 | //Length 3 array representing the perpendicular yaxis 57 | public function yaxis() : Vector { return _yaxis; } 58 | 59 | //Radius of the arc 60 | public function radius() : Float { return _radius; } 61 | 62 | //Start angle in radians 63 | public function minAngle() : Float { return _minAngle; } 64 | 65 | //End angle in radians 66 | public function maxAngle() : Float { return _maxAngle; } 67 | 68 | } -------------------------------------------------------------------------------- /src/verb/geom/BezierCurve.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import haxe.io.Error; 4 | import verb.eval.Eval; 5 | import verb.eval.Make; 6 | import verb.core.Vec; 7 | import verb.core.Data; 8 | 9 | //A Bezier curve is a common spline curve 10 | @:expose("geom.BezierCurve") 11 | class BezierCurve extends NurbsCurve { 12 | 13 | //Create a bezier curve 14 | // 15 | //**params** 16 | // 17 | //* Array of control points 18 | //* Array of control point weights (optional) 19 | 20 | public function new( points : Array, weights : Array = null ) { 21 | super( Make.rationalBezierCurve( points, weights ) ); 22 | } 23 | } -------------------------------------------------------------------------------- /src/verb/geom/Circle.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.core.Data; 4 | import verb.core.Vec; 5 | 6 | import verb.eval.Make; 7 | 8 | // A Circle is a three dimensional curve representing the points that are equidistant from a point in a particular plane 9 | 10 | @:expose("geom.Circle") 11 | class Circle extends Arc { 12 | 13 | //Create a circle 14 | // 15 | //**params** 16 | // 17 | //* Length 3 array representing the center of the circle 18 | //* Length 3 array representing the xaxis 19 | //* Length 3 array representing the perpendicular yaxis 20 | //* Radius of the circle 21 | 22 | public function new( center : Point, 23 | xaxis : Vector, 24 | yaxis : Vector, 25 | radius : Float ) { 26 | super( center, xaxis, yaxis, radius, 0, Math.PI * 2 ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/verb/geom/ConicalSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Make; 4 | import verb.core.Data; 5 | import verb.core.Vec; 6 | 7 | // A ConicalSurface is a surface making up the curve surface of a cone 8 | 9 | @:expose("geom.ConicalSurface") 10 | class ConicalSurface extends NurbsSurface { 11 | 12 | //Make a conical surface 13 | // 14 | //**params** 15 | // 16 | //* Length 3 array representing the axis of the cone 17 | //* Length 3 array representing the x axis, perpendicular to the axis 18 | //* Length 3 array representing the base of the cone 19 | //* Height of the cone 20 | //* Radius of the cone 21 | 22 | public function new(axis : Vector, xaxis : Vector, base : Point, height : Float, radius : Float ) { 23 | super( Make.conicalSurface(axis, xaxis, base, height, radius )); 24 | 25 | _axis = axis; 26 | _xaxis = xaxis; 27 | _base = base; 28 | _height = height; 29 | _radius = radius; 30 | } 31 | 32 | private var _axis : Vector; 33 | private var _xaxis : Vector; 34 | private var _base : Point; 35 | private var _height : Float; 36 | private var _radius : Float; 37 | 38 | //Length 3 array representing the axis of the cone 39 | public function axis(){ return _axis; } 40 | 41 | //Length 3 array representing the x axis, perpendicular to the axis 42 | public function xaxis(){ return _xaxis; } 43 | 44 | //Length 3 array representing the base of the cone 45 | public function base(){ return _base; } 46 | 47 | //Height of the cone 48 | public function height(){ return _height; } 49 | 50 | //Radius of the cone 51 | public function radius(){ return _radius; } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/verb/geom/CylindricalSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | 4 | import verb.eval.Make; 5 | import verb.core.Mat; 6 | import verb.core.Vec; 7 | import verb.core.Data; 8 | 9 | // A CylindricalSurface is a surface making up the curve surface of a cylinder 10 | 11 | @:expose("geom.CylindricalSurface") 12 | class CylindricalSurface extends NurbsSurface { 13 | 14 | //Constructor for Cylinder 15 | // 16 | //**params** 17 | // 18 | //* Length 3 array representing the axis of the cylinder 19 | //* Length 3 array representing the x axis, perpendicular to the axis 20 | //* Length 3 array representing the base of the cylinder 21 | //* Height of the cylinder 22 | //* Radius of the cylinder 23 | 24 | public function new(axis : Vector, xaxis : Vector, base : Point, height : Float, radius : Float) { 25 | super(Make.cylindricalSurface(axis, xaxis, base, height, radius)); 26 | 27 | _axis = axis; 28 | _xaxis = xaxis; 29 | _base = base; 30 | _height = height; 31 | _radius = radius; 32 | } 33 | 34 | private var _axis : Vector; 35 | private var _xaxis : Vector; 36 | private var _base : Point; 37 | private var _height : Float; 38 | private var _radius : Float; 39 | 40 | //Length 3 array representing the axis of the cylinder 41 | public function axis(){ return _axis; } 42 | 43 | //Length 3 array representing the x axis, perpendicular to the axis 44 | public function xaxis(){ return _xaxis; } 45 | 46 | //Length 3 array representing the base of the cylinder 47 | public function base(){ return _base; } 48 | 49 | //Height of the cylinder 50 | public function height(){ return _height; } 51 | 52 | //Radius of the cylinder 53 | public function radius(){ return _radius; } 54 | } 55 | -------------------------------------------------------------------------------- /src/verb/geom/Ellipse.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.core.Vec; 4 | import verb.core.Data; 5 | 6 | // A CylindricalSurface is a surface making up part of a cylinder. 7 | 8 | @:expose("geom.Ellipse") 9 | class Ellipse extends EllipseArc { 10 | 11 | //Create an ellipse 12 | // 13 | //**params** 14 | // 15 | // 16 | //* Length 3 array representing the center of the circle 17 | //* Length 3 array representing the xaxis 18 | //* Length 3 array representing the perpendicular yaxis 19 | 20 | public function new( center : Point, 21 | xaxis : Vector, 22 | yaxis : Vector ) { 23 | super( center, xaxis, yaxis, 0, Math.PI * 2 ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/verb/geom/EllipseArc.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Eval; 4 | import verb.eval.Make; 5 | import verb.core.Vec; 6 | import verb.core.Data; 7 | 8 | // An EllipseArc is a subset of an Ellipse 9 | 10 | @:expose("geom.EllipseArc") 11 | class EllipseArc extends NurbsCurve { 12 | 13 | //Create an EllipseArc 14 | // 15 | //**params** 16 | // 17 | //* Length 3 array representing the center of the arc 18 | //* Length 3 array representing the xaxis 19 | //* Length 3 array representing the perpendicular yaxis 20 | //* Minimum angle of the EllipseArc 21 | //* Maximum angle of the EllipseArc 22 | 23 | public function new( center : Point, 24 | xaxis : Vector, 25 | yaxis : Vector, 26 | minAngle : Float, 27 | maxAngle : Float ) { 28 | super( Make.ellipseArc(center, xaxis, yaxis, minAngle, maxAngle) ); 29 | 30 | _center = center; 31 | _xaxis = xaxis; 32 | _yaxis = yaxis; 33 | _minAngle = minAngle; 34 | _maxAngle = maxAngle; 35 | } 36 | 37 | private var _center : Point; 38 | private var _xaxis : Vector; 39 | private var _yaxis : Vector; 40 | private var _minAngle : Float; 41 | private var _maxAngle : Float; 42 | 43 | //Length 3 array representing the center of the arc 44 | public function center(){ return _center; } 45 | 46 | //Length 3 array representing the xaxis 47 | public function xaxis(){ return _xaxis; } 48 | 49 | //Length 3 array representing the perpendicular yaxis 50 | public function yaxis(){ return _yaxis; } 51 | 52 | //Minimum angle of the EllipseArc 53 | public function minAngle(){ return _minAngle; } 54 | 55 | //Maximum angle of the EllipseArc 56 | public function maxAngle(){ return _maxAngle; } 57 | 58 | 59 | } -------------------------------------------------------------------------------- /src/verb/geom/ExtrudedSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | import verb.eval.Make; 3 | import verb.core.Vec; 4 | import verb.core.Data; 5 | 6 | // Form a Surface by extruding a curve along a vector 7 | 8 | @:expose("geom.ExtrudedSurface") 9 | class ExtrudedSurface extends NurbsSurface { 10 | 11 | //Construct a Surface by extruding a curve 12 | // 13 | //**params** 14 | // 15 | //* The profile curve 16 | //* The direction and magnitude of the extrusion 17 | 18 | public function new( profile : ICurve, direction : Vector ) { 19 | super( Make.extrudedSurface( Vec.normalized( direction ), Vec.norm( direction ), profile.asNurbs() )); 20 | 21 | _profile = profile; 22 | _direction = direction; 23 | } 24 | 25 | private var _profile : ICurve; 26 | private var _direction : Vector; 27 | 28 | //The profile curve 29 | 30 | public function profile() : ICurve { return _profile; } 31 | 32 | //The direction and magnitude of the extrusion 33 | 34 | public function direction() : Vector { return _direction; } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/verb/geom/ICurve.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.core.Data; 4 | import verb.core.Vec; 5 | import verb.core.Mat; 6 | import verb.core.Serialization; 7 | 8 | //An interface representing a Curve 9 | 10 | interface ICurve extends ISerializable { 11 | 12 | //Provide the NURBS representation of the curve 13 | // 14 | //**returns** 15 | // 16 | //* A NurbsCurveData object representing the curve 17 | 18 | function asNurbs() : NurbsCurveData; 19 | 20 | //Obtain the parametric domain of the curve 21 | // 22 | //**returns** 23 | // 24 | //* An Interval object containing the min and max of the domain 25 | 26 | function domain() : Interval; 27 | 28 | //Evaluate a point on the curve 29 | // 30 | //**params** 31 | // 32 | //* The parameter on the curve 33 | // 34 | //**returns** 35 | // 36 | //* The evaluated point 37 | 38 | function point(u : Float) : Point; 39 | 40 | //Evaluate the derivatives at a point on a curve 41 | // 42 | //**params** 43 | // 44 | //* The parameter on the curve 45 | //* The number of derivatives to evaluate on the curve 46 | // 47 | //**returns** 48 | // 49 | //* An array of derivative vectors 50 | 51 | function derivatives(u : Float, numDerivs : Int = 1) : Array; 52 | } 53 | -------------------------------------------------------------------------------- /src/verb/geom/ISurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.core.Data; 4 | import verb.core.Vec; 5 | import verb.core.Serialization; 6 | 7 | import verb.core.Mat; 8 | 9 | // An interface representing a Surface 10 | 11 | interface ISurface extends ISerializable { 12 | 13 | //Provide the NURBS representation of the curve 14 | // 15 | //**returns** 16 | // 17 | //* A NurbsCurveData object representing the curve 18 | 19 | function asNurbs() : NurbsSurfaceData; 20 | 21 | //Provide the domain of the surface in the U direction 22 | // 23 | //**returns** 24 | // 25 | //* An interval object with min and max properties 26 | 27 | function domainU() : Interval; 28 | 29 | //Provide the domain of the surface in the V direction 30 | // 31 | //**returns** 32 | // 33 | //* An interval object with min and max properties 34 | 35 | function domainV() : Interval; 36 | 37 | //Obtain a point on the surface at the given parameter 38 | // 39 | //**params** 40 | // 41 | //* The u parameter 42 | //* The v parameter 43 | // 44 | //**returns** 45 | // 46 | //* A point on the surface 47 | 48 | function point(u : Float, v : Float) : Point; 49 | 50 | //Obtain the derivatives of the NurbsSurface. Returns a two dimensional array 51 | //containing the derivative vectors. Increasing U partial derivatives are increasing 52 | //row-wise. Increasing V partial derivatives are increasing column-wise. Therefore, 53 | //the [0][0] position is a point on the surface, [n][0] is the nth V partial derivative, 54 | //the [1][1] position is twist vector or mixed partial derivative Puv. 55 | // 56 | //**params** 57 | // 58 | //* The u parameter 59 | //* The v parameter 60 | //* Number of derivatives to evaluate 61 | // 62 | //**returns** 63 | // 64 | //* A two dimensional array of vectors 65 | 66 | function derivatives(u : Float, v : Float, numDerivs : Int = 1) : Array>; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/verb/geom/Intersect.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import promhx.Promise; 4 | 5 | import verb.core.Vec; 6 | import verb.core.Data; 7 | import verb.exe.Dispatcher; 8 | import verb.core.Intersections; 9 | 10 | using verb.core.Vec; 11 | 12 | // A class providing simplified access to verb's intersection tools. Intersect contains only static methods. 13 | // 14 | // Similar to `NurbsCurve` and `NurbsSurface`, `Intersect` provides asynchronous versions of all of its methods. 15 | 16 | @:expose("geom.Intersect") 17 | class Intersect { 18 | 19 | //Determine the intersection of two curves 20 | // 21 | //**params** 22 | // 23 | //* ICurve object 24 | //* ICurve object 25 | //* tolerance for the intersection 26 | // 27 | //**returns** 28 | // 29 | //* a possibly empty array of CurveCurveIntersection objects 30 | 31 | public static function curves( first : ICurve, second : ICurve, tol : Float = 1e-3 ) : Array { 32 | return verb.eval.Intersect.curves( first.asNurbs(), second.asNurbs(), tol ); 33 | } 34 | 35 | // The async version of `curves` 36 | 37 | public static function curvesAsync( first : ICurve, second : ICurve, tol : Float = 1e-3 ) : Promise> { 38 | return Dispatcher.dispatchMethod( verb.eval.Intersect, "curves", [first.asNurbs(), second.asNurbs(), tol ]); 39 | } 40 | 41 | //Determine the intersection of a curve and a surface 42 | // 43 | //**params** 44 | // 45 | //* ICurve 46 | //* ISurface 47 | //* tolerance for the curve intersection 48 | // 49 | //**returns** 50 | // 51 | //* array of CurveSurfaceIntersection objects 52 | 53 | public static function curveAndSurface( curve : ICurve, surface : ISurface, tol : Float = 1e-3 ) : Array { 54 | return verb.eval.Intersect.curveAndSurface( curve.asNurbs(), surface.asNurbs(), tol); 55 | } 56 | 57 | // The async version of `curveAndSurface` 58 | 59 | public static function curveAndSurfaceAsync( curve : ICurve, surface : ISurface, tol : Float = 1e-3 ) : Promise> { 60 | return Dispatcher.dispatchMethod( verb.eval.Intersect, "curveAndSurface", [curve.asNurbs(), surface.asNurbs(), tol ]); 61 | } 62 | 63 | //Determine the intersection of two surfaces 64 | // 65 | //**params** 66 | // 67 | //* ISurface 68 | //* ISurface 69 | // 70 | //**returns** 71 | // 72 | //* array of NurbsCurveData objects 73 | 74 | public static function surfaces( first : ISurface, second : ISurface, tol : Float = 1e-3 ) : Array { 75 | return verb.eval.Intersect.surfaces( first.asNurbs(), second.asNurbs(), tol ) 76 | .map(function(cd){ return new NurbsCurve(cd); }); 77 | } 78 | 79 | // The async version of `surfaces` 80 | 81 | public static function surfacesAsync( first : ISurface, second : ISurface, tol : Float = 1e-3 ) : Promise> { 82 | return Dispatcher.dispatchMethod( verb.eval.Intersect, "surfaces", [first.asNurbs(), second.asNurbs(), tol]) 83 | .then(function(cds){ 84 | return cds.map(function(cd){ return new NurbsCurve(cd); }); 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/verb/geom/Line.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Eval; 4 | import verb.eval.Make; 5 | import verb.core.Vec; 6 | import verb.core.Data; 7 | 8 | 9 | // A curve representing a straight line 10 | @:expose("geom.Line") 11 | class Line extends NurbsCurve { 12 | 13 | //Create a line 14 | // 15 | //**params** 16 | // 17 | //* Length 3 array representing the start point 18 | //* Length 3 array representing the end point 19 | 20 | public function new( start : Point, end : Point ) { 21 | super( Make.polyline( [ start, end ] ) ); 22 | 23 | _start = start; 24 | _end = end; 25 | } 26 | 27 | private var _start : Point; 28 | private var _end : Point; 29 | 30 | //Length 3 array representing the start point 31 | public function start(){ return _start; } 32 | 33 | //Length 3 array representing the end point 34 | public function end(){ return _end; } 35 | 36 | } -------------------------------------------------------------------------------- /src/verb/geom/RevolvedSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.core.Data; 4 | import verb.core.Vec; 5 | import verb.eval.Make; 6 | 7 | // A surface formed by revolving a profile curve around a given axis line 8 | 9 | @:expose("geom.RevolvedSurface") 10 | class RevolvedSurface extends NurbsSurface { 11 | 12 | //Construct a revolved surface 13 | // 14 | //**params** 15 | // 16 | //* The profile curve 17 | //* A point on the axis of revolution 18 | //* The direction of the axis of revolution 19 | //* The angle to revolve around. 2 * Math.PI corresponds to a complete revolution 20 | 21 | public function new( profile : NurbsCurve, center : Point, axis : Vector, angle : Float ) { 22 | super( Make.revolvedSurface( profile.asNurbs(), center, axis, angle ) ); 23 | 24 | _profile = profile; 25 | _center = center; 26 | _axis = axis; 27 | _angle = angle; 28 | } 29 | 30 | private var _profile : ICurve; 31 | private var _center : Point; 32 | private var _axis : Vector; 33 | private var _angle : Float; 34 | 35 | //The profile curve 36 | 37 | public function profile() : ICurve { return _profile; } 38 | 39 | //A point on the axis of revolution 40 | 41 | public function center() : Point { return _center; } 42 | 43 | //The direction of the axis of revolution 44 | 45 | public function axis() : Vector { return _center; } 46 | 47 | //The angle to revolve around. 2 * Math.PI corresponds to a complete revolution 48 | 49 | public function angle() : Float { return _angle; } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/verb/geom/SphericalSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Make; 4 | import verb.core.Vec; 5 | import verb.core.Data; 6 | import verb.core.Mat; 7 | 8 | // A surface representing the doubly curved surface of a sphere 9 | 10 | @:expose("geom.SphericalSurface") 11 | class SphericalSurface extends NurbsSurface { 12 | 13 | //Create a spherical surface 14 | // 15 | //**params** 16 | // 17 | //* Length 3 array representing the center of the circle 18 | //* Radius of the circle 19 | 20 | public function new( center : Point, 21 | radius : Float ) { 22 | super( Make.sphericalSurface( center, [0,0,1], [1,0,0], radius )); 23 | 24 | _center = center; 25 | _radius = radius; 26 | } 27 | 28 | private var _center : Point; 29 | private var _radius : Float; 30 | 31 | //Length 3 array representing the center of the circle 32 | 33 | public function center() : Point{ return _center; } 34 | 35 | //Radius of the circle 36 | 37 | public function radius() : Float{ return _radius; } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/verb/geom/SweptSurface.hx: -------------------------------------------------------------------------------- 1 | package verb.geom; 2 | 3 | import verb.eval.Make; 4 | import verb.core.Vec; 5 | import verb.core.Data; 6 | 7 | // A SweptSurface uses a profile curve and a guide rail to form a surface. The profile curve is "swept" along the guide 8 | // rail by a lofting operation. 9 | 10 | @:expose("geom.SweptSurface") 11 | class SweptSurface extends NurbsSurface { 12 | 13 | //Construct a Surface by translating along a rail curve 14 | // 15 | //**params** 16 | // 17 | //* The profile curve 18 | //* The rail curve 19 | 20 | public function new( profile : ICurve, rail : ICurve ) { 21 | super( Make.rationalTranslationalSurface( profile.asNurbs(), rail.asNurbs() )); 22 | 23 | _profile = profile; 24 | _rail = rail; 25 | } 26 | 27 | private var _profile : ICurve; 28 | private var _rail : ICurve; 29 | 30 | //The profile curve 31 | 32 | public function profile() : ICurve { return _profile; } 33 | 34 | //The rail curve 35 | 36 | public function rail() : ICurve { return _rail; } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | Documentation generation - docco no longer works, always sucked anyways 2 | Update website 3 | 4 | Split 5 | Holes are not recognized 6 | 7 | SweptSurface 8 | More efficient representation for surface tessellation, don't use 2d arrays 9 | More exact srf-srf intersection 10 | Marching surface-Surface intersection 11 | SurfaceClosestPoint occasionally fails when point is far from surface - need to check for wild derivatives 12 | Curve interpolation with sparse matrices 13 | Curve approximation 14 | verb.eval.Make.interpCurve with tangents is numerically unstable, implementation likely wrong -------------------------------------------------------------------------------- /verb.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------