├── .github
└── FUNDING.yml
├── .gitignore
├── README.md
├── assets
├── girl-pearl-earring-mini.png
├── girl-pearl-earring.png
├── lena-mini.png
├── lena.png
├── mona-lisa-mini.png
├── mona-lisa.png
├── starry-night-mini.png
└── starry-night.png
├── bin
├── html5
│ └── bin
│ │ ├── Demo.js
│ │ ├── favicon.png
│ │ ├── index.html
│ │ ├── lib
│ │ ├── FileSaver.min.js
│ │ ├── howler.min.js
│ │ └── pako.min.js
│ │ └── manifest
│ │ └── default.json
└── js
│ ├── index.html
│ └── js_demo.js
├── build.hxml
├── com
└── nodename
│ ├── delaunay
│ ├── ArrayHelper.hx
│ ├── Edge.hx
│ ├── EdgeList.hx
│ ├── EdgeReorderer.hx
│ ├── Halfedge.hx
│ ├── HalfedgePriorityQueue.hx
│ ├── ICoord.hx
│ ├── IDisposable.hx
│ ├── Kruskal.hx
│ ├── LR.hx
│ ├── SelectHelper.hx
│ ├── Site.hx
│ ├── SiteList.hx
│ ├── Triangle.hx
│ ├── Vertex.hx
│ └── Voronoi.hx
│ └── geom
│ ├── Circle.hx
│ ├── LineSegment.hx
│ ├── Point.hx
│ ├── Polygon.hx
│ ├── Rectangle.hx
│ └── Winding.hx
├── hxDelaunayTest.hxproj
├── project.xml
├── screenshots
├── delaunay.png
├── girl-pearl-earring-320.png
├── girl-pearl-earring-voronoi.png
├── lena-320.png
├── lena-voronoi.png
├── mona-lisa-320.png
├── mona-lisa-hollow-voronoi.png
├── screenshot.png
├── starry-night-320.png
└── starry-night-voronoi.png
└── src
├── Demo.hx
└── DemoJs.hx
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [azrafe7] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build and Release Folders
2 | bin/
3 | !bin/js
4 | bin-debug/
5 | bin-release/
6 |
7 | # Other files and folders
8 | .settings/
9 |
10 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
11 | # should NOT be excluded as they contain compiler settings and other important
12 | # information for Eclipse / Flash Builder.
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | hxDelaunay
2 | ==========
3 |
4 | Port to Haxe 3 of [sledorze/hxDelaunay](https://github.com/sledorze/hxDelaunay) (itself a port of the excellent [nodename/as3delaunay](https://github.com/nodename/as3delaunay)).
5 |
6 | [](https://rawgit.com/azrafe7/hxdelaunay/master/bin/html5/bin/index.html)
7 |
8 | _click the image above to try the [demo](https://rawgit.com/azrafe7/hxdelaunay/master/bin/html5/bin/index.html) in the browser_
9 |
10 | No external dependencies (demo still needs openfl). Tested on flash/js/cpp/neko.
11 |
12 | ### Features: ###
13 |
14 | - [Voronoi diagram](http://en.wikipedia.org/wiki/Voronoi)
15 | - [Delaunay triangulation](http://en.wikipedia.org/wiki/Delaunay_triangulation)
16 | - [Convex hull](http://en.wikipedia.org/wiki/Convex_hull)
17 | - [Minimum spanning tree](http://en.wikipedia.org/wiki/Euclidean_minimum_spanning_tree)
18 | - [Onion](http://cgm.cs.mcgill.ca/~orm/ontri.html)
19 |
20 | See original authors' links for details and licensing (MIT).
21 |
22 |
23 | ### Update:
24 |
25 | - Delaunay triangulation visualization ([see JS code example](src/DemoJs.hx)).
26 |
27 | [](https://rawgit.com/azrafe7/hxDelaunay/master/bin/js/index.html)
28 |
29 | _click on image to see the code in action_
30 |
31 | ## more screenshots
32 |
33 |    
34 |    
35 |
36 | (well... I've cheated a bit in some of these, but not much ;)
37 |
38 | # haxelib local use
39 |
40 | Currently there is no haxelib, but you can use this git repo as a development directory:
41 |
42 | ```
43 | haxelib dev hxdelaunay path/to/folder
44 | ```
45 |
46 | or use git directly:
47 |
48 | ```
49 | haxelib git hxdelaunay https://github.com/azrafe7/hxDelaunay.git
50 | ```
51 |
52 | don't forget to add it to your build file:
53 |
54 | ```
55 | -lib hxdelaunay
56 | ```
57 |
58 | or for **openfl**:
59 |
60 | ```
61 |
62 | ```
63 |
64 |
65 | Check out the [openfl example](src/Demo.hx) for more information.
66 |
67 |
68 | Or a simpler [js code example](src/DemoJs.hx). See it in action here: [JavaScript example](https://rawgit.com/azrafe7/hxDelaunay/master/bin/js/index.html).
69 |
70 | **Enjoy!**
71 |
72 |
--------------------------------------------------------------------------------
/assets/girl-pearl-earring-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/girl-pearl-earring-mini.png
--------------------------------------------------------------------------------
/assets/girl-pearl-earring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/girl-pearl-earring.png
--------------------------------------------------------------------------------
/assets/lena-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/lena-mini.png
--------------------------------------------------------------------------------
/assets/lena.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/lena.png
--------------------------------------------------------------------------------
/assets/mona-lisa-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/mona-lisa-mini.png
--------------------------------------------------------------------------------
/assets/mona-lisa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/mona-lisa.png
--------------------------------------------------------------------------------
/assets/starry-night-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/starry-night-mini.png
--------------------------------------------------------------------------------
/assets/starry-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/assets/starry-night.png
--------------------------------------------------------------------------------
/bin/html5/bin/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/bin/html5/bin/favicon.png
--------------------------------------------------------------------------------
/bin/html5/bin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
28 |
29 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/bin/html5/bin/lib/FileSaver.min.js:
--------------------------------------------------------------------------------
1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})}
3 |
--------------------------------------------------------------------------------
/bin/html5/bin/lib/howler.min.js:
--------------------------------------------------------------------------------
1 | !function(){"use strict";var e=function(){this.init()};e.prototype={init:function(){var e=this||t;return e._counter=1e3,e._codecs={},e._howls=[],e._muted=!1,e._volume=1,e._canPlayEvent="canplaythrough",e._navigator="undefined"!=typeof window&&window.navigator?window.navigator:null,e.masterGain=null,e.noAudio=!1,e.usingWebAudio=!0,e.autoSuspend=!0,e.ctx=null,e.mobileAutoEnable=!0,e._setup(),e},volume:function(e){var n=this||t;if(e=parseFloat(e),n.ctx||d(),void 0!==e&&e>=0&&e<=1){if(n._volume=e,n._muted)return n;n.usingWebAudio&&(n.masterGain.gain.value=e);for(var o=0;o=0;n--)e._howls[n].unload();return e.usingWebAudio&&e.ctx&&void 0!==e.ctx.close&&(e.ctx.close(),e.ctx=null,d()),e},codecs:function(e){return(this||t)._codecs[e.replace(/^x-/,"")]},_setup:function(){var e=this||t;if(e._initSuspended&&e.ctx&&"suspended"!=e.ctx.state&&(e._initSuspended=!1),e.state=e.ctx&&!e._initSuspended?e.ctx.state||"running":"running",e._autoSuspend(),!e.usingWebAudio)if("undefined"!=typeof Audio)try{void 0===(n=new Audio).oncanplaythrough&&(e._canPlayEvent="canplay")}catch(t){e.noAudio=!0}else e.noAudio=!0;try{var n=new Audio;n.muted&&(e.noAudio=!0)}catch(e){}return e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||t,n=null;try{n="undefined"!=typeof Audio?new Audio:null}catch(t){return e}if(!n||"function"!=typeof n.canPlayType)return e;var o=n.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator&&e._navigator.userAgent.match(/OPR\/([0-6].)/g),i=r&&parseInt(r[0].split("/")[1],10)<33;return e._codecs={mp3:!(i||!o&&!n.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!o,opus:!!n.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!n.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!n.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!n.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),aac:!!n.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!n.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(n.canPlayType("audio/x-m4a;")||n.canPlayType("audio/m4a;")||n.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(n.canPlayType("audio/x-mp4;")||n.canPlayType("audio/mp4;")||n.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!!n.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),webm:!!n.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),dolby:!!n.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,""),flac:!!(n.canPlayType("audio/x-flac;")||n.canPlayType("audio/flac;")).replace(/^no$/,"")},e},_enableMobileAudio:function(){var e=this||t,n=/iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi/i.test(e._navigator&&e._navigator.userAgent),o=!!("ontouchend"in window||e._navigator&&e._navigator.maxTouchPoints>0||e._navigator&&e._navigator.msMaxTouchPoints>0);if(!e._mobileEnabled&&e.ctx&&(n||o)){e._mobileEnabled=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var r=function(){t._autoResume();var n=e.ctx.createBufferSource();n.buffer=e._scratchBuffer,n.connect(e.ctx.destination),void 0===n.start?n.noteOn(0):n.start(0),"function"==typeof e.ctx.resume&&e.ctx.resume(),n.onended=function(){n.disconnect(0),e._mobileEnabled=!0,e.mobileAutoEnable=!1,document.removeEventListener("touchstart",r,!0),document.removeEventListener("touchend",r,!0)}};return document.addEventListener("touchstart",r,!0),document.addEventListener("touchend",r,!0),e}},_autoSuspend:function(){var e=this;if(e.autoSuspend&&e.ctx&&void 0!==e.ctx.suspend&&t.usingWebAudio){for(var n=0;n0?u._seek:o._sprite[e][0]/1e3),_=Math.max(0,(o._sprite[e][0]+o._sprite[e][1])/1e3-d),l=1e3*_/Math.abs(u._rate);u._paused=!1,u._ended=!1,u._sprite=e,u._seek=d,u._start=o._sprite[e][0]/1e3,u._stop=(o._sprite[e][0]+o._sprite[e][1])/1e3,u._loop=!(!u._loop&&!o._sprite[e][2]);var c=u._node;if(o._webAudio){var f=function(){o._refreshBuffer(u);var e=u._muted||o._muted?0:u._volume;c.gain.setValueAtTime(e,t.ctx.currentTime),u._playStart=t.ctx.currentTime,void 0===c.bufferSource.start?u._loop?c.bufferSource.noteGrainOn(0,d,86400):c.bufferSource.noteGrainOn(0,d,_):u._loop?c.bufferSource.start(0,d,86400):c.bufferSource.start(0,d,_),l!==1/0&&(o._endTimers[u._id]=setTimeout(o._ended.bind(o,u),l)),n||setTimeout(function(){o._emit("play",u._id)},0)};"running"===t.state?f():(o.once("resume",f),o._clearTimer(u._id))}else{var p=function(){c.currentTime=d,c.muted=u._muted||o._muted||t._muted||c.muted,c.volume=u._volume*t.volume(),c.playbackRate=u._rate;try{if(c.play(),c.paused)return void o._emit("playerror",u._id,"Playback was unable to start. This is most commonly an issue on mobile devices where playback was not within a user interaction.");l!==1/0&&(o._endTimers[u._id]=setTimeout(o._ended.bind(o,u),l)),n||o._emit("play",u._id)}catch(e){o._emit("playerror",u._id,e)}},h=window&&window.ejecta||!c.readyState&&t._navigator.isCocoonJS;if(4===c.readyState||h)p();else{var m=function(){p(),c.removeEventListener(t._canPlayEvent,m,!1)};c.addEventListener(t._canPlayEvent,m,!1),o._clearTimer(u._id)}}return u._id},pause:function(e){var t=this;if("loaded"!==t._state)return t._queue.push({event:"pause",action:function(){t.pause(e)}}),t;for(var n=t._getSoundIds(e),o=0;o=0?n=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),n=parseInt(r[1],10));var i;if(!(void 0!==e&&e>=0&&e<=1))return(i=n?o._soundById(n):o._sounds[0])?i._volume:0;if("loaded"!==o._state)return o._queue.push({event:"volume",action:function(){o.volume.apply(o,r)}}),o;void 0===n&&(o._volume=e),n=o._getSoundIds(n);for(var a=0;an?"out":"in",d=Math.abs(t-n)/.01,_=d>0?o/d:o;_<4&&(d=Math.ceil(d/(4/_)),_=4),e._interval=setInterval(function(){d>0&&(u+="in"===s?.01:-.01),u=Math.max(0,u),u=Math.min(1,u),u=Math.round(100*u)/100,a._webAudio?e._volume=u:a.volume(u,e._id,!0),i&&(a._volume=u),(nt&&u>=n)&&(clearInterval(e._interval),e._interval=null,a.volume(n,e._id),a._emit("fade",e._id))},_)},_stopFade:function(e){var n=this._soundById(e);return n&&n._interval&&(this._webAudio&&n._node.gain.cancelScheduledValues(t.ctx.currentTime),clearInterval(n._interval),n._interval=null,this._emit("fade",e)),this},loop:function(){var e,t,n,o=arguments;if(0===o.length)return this._loop;if(1===o.length){if("boolean"!=typeof o[0])return!!(n=this._soundById(parseInt(o[0],10)))&&n._loop;e=o[0],this._loop=e}else 2===o.length&&(e=o[0],t=parseInt(o[1],10));for(var r=this._getSoundIds(t),i=0;i=0?n=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),n=parseInt(r[1],10));var i;if("number"!=typeof e)return(i=o._soundById(n))?i._rate:o._rate;if("loaded"!==o._state)return o._queue.push({event:"rate",action:function(){o.rate.apply(o,r)}}),o;void 0===n&&(o._rate=e),n=o._getSoundIds(n);for(var a=0;a=0?n=parseInt(r[0],10):o._sounds.length&&(n=o._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),n=parseInt(r[1],10));if(void 0===n)return o;if("loaded"!==o._state)return o._queue.push({event:"seek",action:function(){o.seek.apply(o,r)}}),o;var i=o._soundById(n);if(i){if(!("number"==typeof e&&e>=0)){if(o._webAudio){var a=o.playing(n)?t.ctx.currentTime-i._playStart:0,u=i._rateSeek?i._rateSeek-i._seek:0;return i._seek+(u+a*Math.abs(i._rate))}return i._node.currentTime}var s=o.playing(n);s&&o.pause(n,!0),i._seek=e,i._ended=!1,o._clearTimer(n),s&&o.play(n,!0),!o._webAudio&&i._node&&(i._node.currentTime=e),o._emit("seek",n)}return o},playing:function(e){if("number"==typeof e){var t=this._soundById(e);return!!t&&!t._paused}for(var n=0;n=0&&t._howls.splice(i,1)}var a=!0;for(o=0;o=0;r--)o[r].id&&o[r].id!==t&&"load"!==e||(setTimeout(function(e){e.call(this,t,n)}.bind(this,o[r].fn),0),o[r].once&&this.off(e,o[r].fn,o[r].id));return this},_loadQueue:function(){var e=this;if(e._queue.length>0){var t=e._queue[0];e.once(t.event,function(){e._queue.shift(),e._loadQueue()}),t.action()}return e},_ended:function(e){var n=e._sprite;if(!this._webAudio&&e._node&&!e._node.paused&&!e._node.ended)return setTimeout(this._ended.bind(this,e),100),this;var o=!(!e._loop&&!this._sprite[n][2]);if(this._emit("end",e._id),!this._webAudio&&o&&this.stop(e._id,!0).play(e._id),this._webAudio&&o){this._emit("play",e._id),e._seek=e._start||0,e._rateSeek=0,e._playStart=t.ctx.currentTime;var r=1e3*(e._stop-e._start)/Math.abs(e._rate);this._endTimers[e._id]=setTimeout(this._ended.bind(this,e),r)}return this._webAudio&&!o&&(e._paused=!0,e._ended=!0,e._seek=e._start||0,e._rateSeek=0,this._clearTimer(e._id),this._cleanBuffer(e._node),t._autoSuspend()),this._webAudio||o||this.stop(e._id),this},_clearTimer:function(e){return this._endTimers[e]&&(clearTimeout(this._endTimers[e]),delete this._endTimers[e]),this},_soundById:function(e){for(var t=0;t=0;n--){if(t<=e)return;this._sounds[n]._ended&&(this._webAudio&&this._sounds[n]._node&&this._sounds[n]._node.disconnect(0),this._sounds.splice(n,1),t--)}}},_getSoundIds:function(e){if(void 0===e){for(var t=[],n=0;n0&&(r[n._src]=e,s(n,e))},function(){n._emit("loaderror",null,"Decoding audio data failed.")})},s=function(e,t){t&&!e._duration&&(e._duration=t.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue())},d=function(){try{"undefined"!=typeof AudioContext?t.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?t.ctx=new webkitAudioContext:t.usingWebAudio=!1}catch(e){t.usingWebAudio=!1}t._initSuspended=void 0!==t.ctx&&t.ctx&&null!=t.ctx.state&&"suspended"===t.ctx.state;var e=/iP(hone|od|ad)/.test(t._navigator&&t._navigator.platform),n=t._navigator&&t._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),o=n?parseInt(n[1],10):null;if(e&&o&&o<9){var r=/safari/.test(t._navigator&&t._navigator.userAgent.toLowerCase());(t._navigator&&t._navigator.standalone&&!r||t._navigator&&!t._navigator.standalone&&!r)&&(t.usingWebAudio=!1)}t.usingWebAudio&&(t.masterGain=void 0===t.ctx.createGain?t.ctx.createGainNode():t.ctx.createGain(),t.masterGain.gain.value=t._muted?0:1,t.masterGain.connect(t.ctx.destination)),t._setup()};"function"==typeof define&&define.amd&&define([],function(){return{Howler:t,Howl:n}}),"undefined"!=typeof exports&&(exports.Howler=t,exports.Howl=n),"undefined"!=typeof window?(window.HowlerGlobal=e,window.Howler=t,window.Howl=n,window.Sound=o):"undefined"!=typeof global&&(global.HowlerGlobal=e,global.Howler=t,global.Howl=n,global.Sound=o)}();
2 | !function(){"use strict";HowlerGlobal.prototype._pos=[0,0,0],HowlerGlobal.prototype._orientation=[0,0,-1,0,1,0],HowlerGlobal.prototype.stereo=function(n){var e=this;if(!e.ctx||!e.ctx.listener)return e;for(var t=e._howls.length-1;t>=0;t--)e._howls[t].stereo(n);return e},HowlerGlobal.prototype.pos=function(n,e,t){var o=this;return o.ctx&&o.ctx.listener?(e="number"!=typeof e?o._pos[1]:e,t="number"!=typeof t?o._pos[2]:t,"number"!=typeof n?o._pos:(o._pos=[n,e,t],o.ctx.listener.setPosition(o._pos[0],o._pos[1],o._pos[2]),o)):o},HowlerGlobal.prototype.orientation=function(n,e,t,o,r,i){if(!this.ctx||!this.ctx.listener)return this;var a=this._orientation;return e="number"!=typeof e?a[1]:e,t="number"!=typeof t?a[2]:t,o="number"!=typeof o?a[3]:o,r="number"!=typeof r?a[4]:r,i="number"!=typeof i?a[5]:i,"number"!=typeof n?a:(this._orientation=[n,e,t,o,r,i],this.ctx.listener.setOrientation(n,e,t,o,r,i),this)},Howl.prototype.init=function(n){return function(e){return this._orientation=e.orientation||[1,0,0],this._stereo=e.stereo||null,this._pos=e.pos||null,this._pannerAttr={coneInnerAngle:void 0!==e.coneInnerAngle?e.coneInnerAngle:360,coneOuterAngle:void 0!==e.coneOuterAngle?e.coneOuterAngle:360,coneOuterGain:void 0!==e.coneOuterGain?e.coneOuterGain:0,distanceModel:void 0!==e.distanceModel?e.distanceModel:"inverse",maxDistance:void 0!==e.maxDistance?e.maxDistance:1e4,panningModel:void 0!==e.panningModel?e.panningModel:"HRTF",refDistance:void 0!==e.refDistance?e.refDistance:1,rolloffFactor:void 0!==e.rolloffFactor?e.rolloffFactor:1},this._onstereo=e.onstereo?[{fn:e.onstereo}]:[],this._onpos=e.onpos?[{fn:e.onpos}]:[],this._onorientation=e.onorientation?[{fn:e.onorientation}]:[],n.call(this,e)}}(Howl.prototype.init),Howl.prototype.stereo=function(e,t){var o=this;if(!o._webAudio)return o;if("loaded"!==o._state)return o._queue.push({event:"stereo",action:function(){o.stereo(e,t)}}),o;var r=void 0===Howler.ctx.createStereoPanner?"spatial":"stereo";if(void 0===t){if("number"!=typeof e)return o._stereo;o._stereo=e,o._pos=[e,0,0]}for(var i=o._getSoundIds(t),a=0;a0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=o.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(a!==b)throw new Error(d[a]);if(e.header&&o.deflateSetHeader(this.strm,e.header),e.dictionary){var n;if(n="string"==typeof e.dictionary?h.string2buf(e.dictionary):"[object ArrayBuffer]"===_.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,a=o.deflateSetDictionary(this.strm,n),a!==b)throw new Error(d[a]);this._dict_set=!0}}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}function s(t,e){return e=e||{},e.gzip=!0,n(t,e)}var o=t("./zlib/deflate"),l=t("./utils/common"),h=t("./utils/strings"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=Object.prototype.toString,u=0,c=4,b=0,g=1,m=2,w=-1,p=0,v=8;i.prototype.push=function(t,e){var a,i,n=this.strm,r=this.options.chunkSize;if(this.ended)return!1;i=e===~~e?e:e===!0?c:u,"string"==typeof t?n.input=h.string2buf(t):"[object ArrayBuffer]"===_.call(t)?n.input=new Uint8Array(t):n.input=t,n.next_in=0,n.avail_in=n.input.length;do{if(0===n.avail_out&&(n.output=new l.Buf8(r),n.next_out=0,n.avail_out=r),a=o.deflate(n,i),a!==g&&a!==b)return this.onEnd(a),this.ended=!0,!1;0!==n.avail_out&&(0!==n.avail_in||i!==c&&i!==m)||("string"===this.options.to?this.onData(h.buf2binstring(l.shrinkBuf(n.output,n.next_out))):this.onData(l.shrinkBuf(n.output,n.next_out)))}while((n.avail_in>0||0===n.avail_out)&&a!==g);return i===c?(a=o.deflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===b):i!==m||(this.onEnd(b),n.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===b&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=l.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Deflate=i,a.deflate=n,a.deflateRaw=r,a.gzip=s},{"./utils/common":3,"./utils/strings":4,"./zlib/deflate":8,"./zlib/messages":13,"./zlib/zstream":15}],2:[function(t,e,a){"use strict";function i(t){if(!(this instanceof i))return new i(t);this.options=o.assign({chunkSize:16384,windowBits:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0===(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=s.inflateInit2(this.strm,e.windowBits);if(a!==h.Z_OK)throw new Error(d[a]);this.header=new _,s.inflateGetHeader(this.strm,this.header)}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}var s=t("./zlib/inflate"),o=t("./utils/common"),l=t("./utils/strings"),h=t("./zlib/constants"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=t("./zlib/gzheader"),u=Object.prototype.toString;i.prototype.push=function(t,e){var a,i,n,r,d,f,_=this.strm,c=this.options.chunkSize,b=this.options.dictionary,g=!1;if(this.ended)return!1;i=e===~~e?e:e===!0?h.Z_FINISH:h.Z_NO_FLUSH,"string"==typeof t?_.input=l.binstring2buf(t):"[object ArrayBuffer]"===u.call(t)?_.input=new Uint8Array(t):_.input=t,_.next_in=0,_.avail_in=_.input.length;do{if(0===_.avail_out&&(_.output=new o.Buf8(c),_.next_out=0,_.avail_out=c),a=s.inflate(_,h.Z_NO_FLUSH),a===h.Z_NEED_DICT&&b&&(f="string"==typeof b?l.string2buf(b):"[object ArrayBuffer]"===u.call(b)?new Uint8Array(b):b,a=s.inflateSetDictionary(this.strm,f)),a===h.Z_BUF_ERROR&&g===!0&&(a=h.Z_OK,g=!1),a!==h.Z_STREAM_END&&a!==h.Z_OK)return this.onEnd(a),this.ended=!0,!1;_.next_out&&(0!==_.avail_out&&a!==h.Z_STREAM_END&&(0!==_.avail_in||i!==h.Z_FINISH&&i!==h.Z_SYNC_FLUSH)||("string"===this.options.to?(n=l.utf8border(_.output,_.next_out),r=_.next_out-n,d=l.buf2string(_.output,n),_.next_out=r,_.avail_out=c-r,r&&o.arraySet(_.output,_.output,n,r,0),this.onData(d)):this.onData(o.shrinkBuf(_.output,_.next_out)))),0===_.avail_in&&0===_.avail_out&&(g=!0)}while((_.avail_in>0||0===_.avail_out)&&a!==h.Z_STREAM_END);return a===h.Z_STREAM_END&&(i=h.Z_FINISH),i===h.Z_FINISH?(a=s.inflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===h.Z_OK):i!==h.Z_SYNC_FLUSH||(this.onEnd(h.Z_OK),_.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===h.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Inflate=i,a.inflate=n,a.inflateRaw=r,a.ungzip=n},{"./utils/common":3,"./utils/strings":4,"./zlib/constants":6,"./zlib/gzheader":9,"./zlib/inflate":11,"./zlib/messages":13,"./zlib/zstream":15}],3:[function(t,e,a){"use strict";var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;a.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var a=e.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(var i in a)a.hasOwnProperty(i)&&(t[i]=a[i])}}return t},a.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var n={arraySet:function(t,e,a,i,n){if(e.subarray&&t.subarray)return void t.set(e.subarray(a,a+i),n);for(var r=0;r=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,a.string2buf=function(t){var e,a,i,r,s,o=t.length,l=0;for(r=0;r>>6,e[s++]=128|63&a):a<65536?(e[s++]=224|a>>>12,e[s++]=128|a>>>6&63,e[s++]=128|63&a):(e[s++]=240|a>>>18,e[s++]=128|a>>>12&63,e[s++]=128|a>>>6&63,e[s++]=128|63&a);return e},a.buf2binstring=function(t){return i(t,t.length)},a.binstring2buf=function(t){for(var e=new n.Buf8(t.length),a=0,i=e.length;a4)h[n++]=65533,a+=s-1;else{for(r&=2===s?31:3===s?15:7;s>1&&a1?h[n++]=65533:r<65536?h[n++]=r:(r-=65536,h[n++]=55296|r>>10&1023,h[n++]=56320|1023&r)}return i(h,n)},a.utf8border=function(t,e){var a;for(e=e||t.length,e>t.length&&(e=t.length),a=e-1;a>=0&&128===(192&t[a]);)a--;return a<0?e:0===a?e:a+o[t[a]]>e?a:e}},{"./common":3}],5:[function(t,e,a){"use strict";function i(t,e,a,i){for(var n=65535&t|0,r=t>>>16&65535|0,s=0;0!==a;){s=a>2e3?2e3:a,a-=s;do n=n+e[i++]|0,r=r+n|0;while(--s);n%=65521,r%=65521}return n|r<<16|0}e.exports=i},{}],6:[function(t,e,a){"use strict";e.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],7:[function(t,e,a){"use strict";function i(){for(var t,e=[],a=0;a<256;a++){t=a;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[a]=t}return e}function n(t,e,a,i){var n=r,s=i+a;t^=-1;for(var o=i;o>>8^n[255&(t^e[o])];return t^-1}var r=i();e.exports=n},{}],8:[function(t,e,a){"use strict";function i(t,e){return t.msg=D[e],e}function n(t){return(t<<1)-(t>4?9:0)}function r(t){for(var e=t.length;--e>=0;)t[e]=0}function s(t){var e=t.state,a=e.pending;a>t.avail_out&&(a=t.avail_out),0!==a&&(R.arraySet(t.output,e.pending_buf,e.pending_out,a,t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))}function o(t,e){C._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,s(t.strm)}function l(t,e){t.pending_buf[t.pending++]=e}function h(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function d(t,e,a,i){var n=t.avail_in;return n>i&&(n=i),0===n?0:(t.avail_in-=n,R.arraySet(e,t.input,t.next_in,n,a),1===t.state.wrap?t.adler=N(t.adler,e,n,a):2===t.state.wrap&&(t.adler=O(t.adler,e,n,a)),t.next_in+=n,t.total_in+=n,n)}function f(t,e){var a,i,n=t.max_chain_length,r=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-ft?t.strstart-(t.w_size-ft):0,h=t.window,d=t.w_mask,f=t.prev,_=t.strstart+dt,u=h[r+s-1],c=h[r+s];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do if(a=e,h[a+s]===c&&h[a+s-1]===u&&h[a]===h[r]&&h[++a]===h[r+1]){r+=2,a++;do;while(h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&r<_);if(i=dt-(_-r),r=_-dt,i>s){if(t.match_start=e,s=i,i>=o)break;u=h[r+s-1],c=h[r+s]}}while((e=f[e&d])>l&&0!==--n);return s<=t.lookahead?s:t.lookahead}function _(t){var e,a,i,n,r,s=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=s+(s-ft)){R.arraySet(t.window,t.window,s,s,0),t.match_start-=s,t.strstart-=s,t.block_start-=s,a=t.hash_size,e=a;do i=t.head[--e],t.head[e]=i>=s?i-s:0;while(--a);a=s,e=a;do i=t.prev[--e],t.prev[e]=i>=s?i-s:0;while(--a);n+=s}if(0===t.strm.avail_in)break;if(a=d(t.strm,t.window,t.strstart+t.lookahead,n),t.lookahead+=a,t.lookahead+t.insert>=ht)for(r=t.strstart-t.insert,t.ins_h=t.window[r],t.ins_h=(t.ins_h<t.pending_buf_size-5&&(a=t.pending_buf_size-5);;){if(t.lookahead<=1){if(_(t),0===t.lookahead&&e===I)return vt;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+a;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,o(t,!1),0===t.strm.avail_out))return vt;if(t.strstart-t.block_start>=t.w_size-ft&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.strstart>t.block_start&&(o(t,!1),0===t.strm.avail_out)?vt:vt}function c(t,e){for(var a,i;;){if(t.lookahead=ht&&(t.ins_h=(t.ins_h<=ht)if(i=C._tr_tally(t,t.strstart-t.match_start,t.match_length-ht),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=ht){t.match_length--;do t.strstart++,t.ins_h=(t.ins_h<=ht&&(t.ins_h=(t.ins_h<4096)&&(t.match_length=ht-1)),t.prev_length>=ht&&t.match_length<=t.prev_length){n=t.strstart+t.lookahead-ht,i=C._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-ht),t.lookahead-=t.prev_length-1,t.prev_length-=2;do++t.strstart<=n&&(t.ins_h=(t.ins_h<=ht&&t.strstart>0&&(n=t.strstart-1,i=s[n],i===s[++n]&&i===s[++n]&&i===s[++n])){r=t.strstart+dt;do;while(i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&nt.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=ht?(a=C._tr_tally(t,1,t.match_length-ht),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function m(t,e){for(var a;;){if(0===t.lookahead&&(_(t),0===t.lookahead)){if(e===I)return vt;break}if(t.match_length=0,a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function w(t,e,a,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=a,this.max_chain=i,this.func=n}function p(t){t.window_size=2*t.w_size,r(t.head),t.max_lazy_match=Z[t.level].max_lazy,t.good_match=Z[t.level].good_length,t.nice_match=Z[t.level].nice_length,t.max_chain_length=Z[t.level].max_chain,t.strstart=0,t.block_start=0,t.lookahead=0,t.insert=0,t.match_length=t.prev_length=ht-1,t.match_available=0,t.ins_h=0}function v(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=V,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new R.Buf16(2*ot),this.dyn_dtree=new R.Buf16(2*(2*rt+1)),this.bl_tree=new R.Buf16(2*(2*st+1)),r(this.dyn_ltree),r(this.dyn_dtree),r(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new R.Buf16(lt+1),this.heap=new R.Buf16(2*nt+1),r(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new R.Buf16(2*nt+1),r(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function k(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_type=Q,e=t.state,e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=e.wrap?ut:wt,t.adler=2===e.wrap?0:1,e.last_flush=I,C._tr_init(e),H):i(t,K)}function y(t){var e=k(t);return e===H&&p(t.state),e}function x(t,e){return t&&t.state?2!==t.state.wrap?K:(t.state.gzhead=e,H):K}function z(t,e,a,n,r,s){if(!t)return K;var o=1;if(e===Y&&(e=6),n<0?(o=0,n=-n):n>15&&(o=2,n-=16),r<1||r>$||a!==V||n<8||n>15||e<0||e>9||s<0||s>W)return i(t,K);8===n&&(n=9);var l=new v;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=n,l.w_size=1<L||e<0)return t?i(t,K):K;if(o=t.state,!t.output||!t.input&&0!==t.avail_in||o.status===pt&&e!==F)return i(t,0===t.avail_out?P:K);if(o.strm=t,a=o.last_flush,o.last_flush=e,o.status===ut)if(2===o.wrap)t.adler=0,l(o,31),l(o,139),l(o,8),o.gzhead?(l(o,(o.gzhead.text?1:0)+(o.gzhead.hcrc?2:0)+(o.gzhead.extra?4:0)+(o.gzhead.name?8:0)+(o.gzhead.comment?16:0)),l(o,255&o.gzhead.time),l(o,o.gzhead.time>>8&255),l(o,o.gzhead.time>>16&255),l(o,o.gzhead.time>>24&255),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,255&o.gzhead.os),o.gzhead.extra&&o.gzhead.extra.length&&(l(o,255&o.gzhead.extra.length),l(o,o.gzhead.extra.length>>8&255)),o.gzhead.hcrc&&(t.adler=O(t.adler,o.pending_buf,o.pending,0)),o.gzindex=0,o.status=ct):(l(o,0),l(o,0),l(o,0),l(o,0),l(o,0),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,zt),o.status=wt);else{var _=V+(o.w_bits-8<<4)<<8,u=-1;u=o.strategy>=G||o.level<2?0:o.level<6?1:6===o.level?2:3,_|=u<<6,0!==o.strstart&&(_|=_t),_+=31-_%31,o.status=wt,h(o,_),0!==o.strstart&&(h(o,t.adler>>>16),h(o,65535&t.adler)),t.adler=1}if(o.status===ct)if(o.gzhead.extra){for(d=o.pending;o.gzindex<(65535&o.gzhead.extra.length)&&(o.pending!==o.pending_buf_size||(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending!==o.pending_buf_size));)l(o,255&o.gzhead.extra[o.gzindex]),o.gzindex++;o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),o.gzindex===o.gzhead.extra.length&&(o.gzindex=0,o.status=bt)}else o.status=bt;if(o.status===bt)if(o.gzhead.name){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.gzindex=0,o.status=gt)}else o.status=gt;if(o.status===gt)if(o.gzhead.comment){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.status=mt)}else o.status=mt;if(o.status===mt&&(o.gzhead.hcrc?(o.pending+2>o.pending_buf_size&&s(t),o.pending+2<=o.pending_buf_size&&(l(o,255&t.adler),l(o,t.adler>>8&255),t.adler=0,o.status=wt)):o.status=wt),0!==o.pending){if(s(t),0===t.avail_out)return o.last_flush=-1,H}else if(0===t.avail_in&&n(e)<=n(a)&&e!==F)return i(t,P);if(o.status===pt&&0!==t.avail_in)return i(t,P);if(0!==t.avail_in||0!==o.lookahead||e!==I&&o.status!==pt){var c=o.strategy===G?m(o,e):o.strategy===X?g(o,e):Z[o.level].func(o,e);if(c!==yt&&c!==xt||(o.status=pt),c===vt||c===yt)return 0===t.avail_out&&(o.last_flush=-1),H;if(c===kt&&(e===U?C._tr_align(o):e!==L&&(C._tr_stored_block(o,0,0,!1),e===T&&(r(o.head),0===o.lookahead&&(o.strstart=0,o.block_start=0,o.insert=0))),s(t),0===t.avail_out))return o.last_flush=-1,H}return e!==F?H:o.wrap<=0?j:(2===o.wrap?(l(o,255&t.adler),l(o,t.adler>>8&255),l(o,t.adler>>16&255),l(o,t.adler>>24&255),l(o,255&t.total_in),l(o,t.total_in>>8&255),l(o,t.total_in>>16&255),l(o,t.total_in>>24&255)):(h(o,t.adler>>>16),h(o,65535&t.adler)),s(t),o.wrap>0&&(o.wrap=-o.wrap),0!==o.pending?H:j)}function E(t){var e;return t&&t.state?(e=t.state.status,e!==ut&&e!==ct&&e!==bt&&e!==gt&&e!==mt&&e!==wt&&e!==pt?i(t,K):(t.state=null,e===wt?i(t,M):H)):K}function A(t,e){var a,i,n,s,o,l,h,d,f=e.length;if(!t||!t.state)return K;if(a=t.state,s=a.wrap,2===s||1===s&&a.status!==ut||a.lookahead)return K;for(1===s&&(t.adler=N(t.adler,e,f,0)),a.wrap=0,f>=a.w_size&&(0===s&&(r(a.head),a.strstart=0,a.block_start=0,a.insert=0),d=new R.Buf8(a.w_size),R.arraySet(d,e,f-a.w_size,a.w_size,0),e=d,f=a.w_size),o=t.avail_in,l=t.next_in,h=t.input,t.avail_in=f,t.next_in=0,t.input=e,_(a);a.lookahead>=ht;){i=a.strstart,n=a.lookahead-(ht-1);do a.ins_h=(a.ins_h<>>24,b>>>=y,g-=y,y=k>>>16&255,0===y)A[o++]=65535&k;else{if(!(16&y)){if(0===(64&y)){k=m[(65535&k)+(b&(1<>>=y,g-=y),g<15&&(b+=E[r++]<>>24,b>>>=y,g-=y,y=k>>>16&255,!(16&y)){if(0===(64&y)){k=w[(65535&k)+(b&(1<d){t.msg="invalid distance too far back",a.mode=i;break t}if(b>>>=y,g-=y,y=o-l,z>y){if(y=z-y,y>_&&a.sane){t.msg="invalid distance too far back",a.mode=i;break t}if(B=0,S=c,0===u){if(B+=f-y,y2;)A[o++]=S[B++],A[o++]=S[B++],A[o++]=S[B++],x-=3;x&&(A[o++]=S[B++],x>1&&(A[o++]=S[B++]))}else{B=o-z;do A[o++]=A[B++],A[o++]=A[B++],A[o++]=A[B++],x-=3;while(x>2);x&&(A[o++]=A[B++],x>1&&(A[o++]=A[B++]))}break}}break}}while(r>3,r-=x,g-=x<<3,b&=(1<>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function n(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new w.Buf16(320),this.work=new w.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function r(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=T,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new w.Buf32(bt),e.distcode=e.distdyn=new w.Buf32(gt),e.sane=1,e.back=-1,Z):N}function s(t){var e;return t&&t.state?(e=t.state,e.wsize=0,e.whave=0,e.wnext=0,r(t)):N}function o(t,e){var a,i;return t&&t.state?(i=t.state,e<0?(a=0,e=-e):(a=(e>>4)+1,e<48&&(e&=15)),e&&(e<8||e>15)?N:(null!==i.window&&i.wbits!==e&&(i.window=null),i.wrap=a,i.wbits=e,s(t))):N}function l(t,e){var a,i;return t?(i=new n,t.state=i,i.window=null,a=o(t,e),a!==Z&&(t.state=null),a):N}function h(t){return l(t,wt)}function d(t){if(pt){var e;for(g=new w.Buf32(512),m=new w.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(y(z,t.lens,0,288,g,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;y(B,t.lens,0,32,m,0,t.work,{bits:5}),pt=!1}t.lencode=g,t.lenbits=9,t.distcode=m,t.distbits=5}function f(t,e,a,i){var n,r=t.state;return null===r.window&&(r.wsize=1<=r.wsize?(w.arraySet(r.window,e,a-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(n=r.wsize-r.wnext,n>i&&(n=i),w.arraySet(r.window,e,a-i,n,r.wnext),i-=n,i?(w.arraySet(r.window,e,a-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=n,r.wnext===r.wsize&&(r.wnext=0),r.whave>>8&255,a.check=v(a.check,Et,2,0),_=0,u=0,a.mode=F;break}if(a.flags=0,a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&_)<<8)+(_>>8))%31){t.msg="incorrect header check",a.mode=_t;break}if((15&_)!==U){t.msg="unknown compression method",a.mode=_t;break}if(_>>>=4,u-=4,yt=(15&_)+8,0===a.wbits)a.wbits=yt;else if(yt>a.wbits){t.msg="invalid window size",a.mode=_t;break}a.dmax=1<>8&1),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=L;case L:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>8&255,Et[2]=_>>>16&255,Et[3]=_>>>24&255,a.check=v(a.check,Et,4,0)),_=0,u=0,a.mode=H;case H:for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>8),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=j;case j:if(1024&a.flags){for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0}else a.head&&(a.head.extra=null);a.mode=K;case K:if(1024&a.flags&&(g=a.length,g>l&&(g=l),g&&(a.head&&(yt=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Array(a.head.extra_len)),w.arraySet(a.head.extra,n,s,g,yt)),512&a.flags&&(a.check=v(a.check,n,g,s)),l-=g,s+=g,a.length-=g),a.length))break t;a.length=0,a.mode=M;case M:if(2048&a.flags){if(0===l)break t;g=0;do yt=n[s+g++],a.head&&yt&&a.length<65536&&(a.head.name+=String.fromCharCode(yt));while(yt&&g>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=X;break;case q:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>=7&u,u-=7&u,a.mode=ht;break}for(;u<3;){if(0===l)break t;l--,_+=n[s++]<>>=1,u-=1,3&_){case 0:a.mode=J;break;case 1:if(d(a),a.mode=at,e===A){_>>>=2,u-=2;break t}break;case 2:a.mode=$;break;case 3:t.msg="invalid block type",a.mode=_t}_>>>=2,u-=2;break;case J:for(_>>>=7&u,u-=7&u;u<32;){if(0===l)break t;l--,_+=n[s++]<>>16^65535)){t.msg="invalid stored block lengths",a.mode=_t;break}if(a.length=65535&_,_=0,u=0,a.mode=Q,e===A)break t;case Q:a.mode=V;case V:if(g=a.length){if(g>l&&(g=l),g>h&&(g=h),0===g)break t;w.arraySet(r,n,s,g,o),l-=g,s+=g,h-=g,o+=g,a.length-=g;break}a.mode=X;break;case $:for(;u<14;){if(0===l)break t;
3 | l--,_+=n[s++]<>>=5,u-=5,a.ndist=(31&_)+1,_>>>=5,u-=5,a.ncode=(15&_)+4,_>>>=4,u-=4,a.nlen>286||a.ndist>30){t.msg="too many length or distance symbols",a.mode=_t;break}a.have=0,a.mode=tt;case tt:for(;a.have>>=3,u-=3}for(;a.have<19;)a.lens[At[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,zt={bits:a.lenbits},xt=y(x,a.lens,0,19,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid code lengths set",a.mode=_t;break}a.have=0,a.mode=et;case et:for(;a.have>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=gt,u-=gt,a.lens[a.have++]=wt;else{if(16===wt){for(Bt=gt+2;u>>=gt,u-=gt,0===a.have){t.msg="invalid bit length repeat",a.mode=_t;break}yt=a.lens[a.have-1],g=3+(3&_),_>>>=2,u-=2}else if(17===wt){for(Bt=gt+3;u>>=gt,u-=gt,yt=0,g=3+(7&_),_>>>=3,u-=3}else{for(Bt=gt+7;u>>=gt,u-=gt,yt=0,g=11+(127&_),_>>>=7,u-=7}if(a.have+g>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=_t;break}for(;g--;)a.lens[a.have++]=yt}}if(a.mode===_t)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=_t;break}if(a.lenbits=9,zt={bits:a.lenbits},xt=y(z,a.lens,0,a.nlen,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid literal/lengths set",a.mode=_t;break}if(a.distbits=6,a.distcode=a.distdyn,zt={bits:a.distbits},xt=y(B,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,zt),a.distbits=zt.bits,xt){t.msg="invalid distances set",a.mode=_t;break}if(a.mode=at,e===A)break t;case at:a.mode=it;case it:if(l>=6&&h>=258){t.next_out=o,t.avail_out=h,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=u,k(t,b),o=t.next_out,r=t.output,h=t.avail_out,s=t.next_in,n=t.input,l=t.avail_in,_=a.hold,u=a.bits,a.mode===X&&(a.back=-1);break}for(a.back=0;St=a.lencode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,a.length=wt,0===mt){a.mode=lt;break}if(32&mt){a.back=-1,a.mode=X;break}if(64&mt){t.msg="invalid literal/length code",a.mode=_t;break}a.extra=15&mt,a.mode=nt;case nt:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=rt;case rt:for(;St=a.distcode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,64&mt){t.msg="invalid distance code",a.mode=_t;break}a.offset=wt,a.extra=15&mt,a.mode=st;case st:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=_t;break}a.mode=ot;case ot:if(0===h)break t;if(g=b-h,a.offset>g){if(g=a.offset-g,g>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=_t;break}g>a.wnext?(g-=a.wnext,m=a.wsize-g):m=a.wnext-g,g>a.length&&(g=a.length),bt=a.window}else bt=r,m=o-a.offset,g=a.length;g>h&&(g=h),h-=g,a.length-=g;do r[o++]=bt[m++];while(--g);0===a.length&&(a.mode=it);break;case lt:if(0===h)break t;r[o++]=a.length,h--,a.mode=it;break;case ht:if(a.wrap){for(;u<32;){if(0===l)break t;l--,_|=n[s++]<=1&&0===j[N];N--);if(O>N&&(O=N),0===N)return b[g++]=20971520,b[g++]=20971520,w.bits=1,0;for(C=1;C0&&(t===o||1!==N))return-1;for(K[1]=0,Z=1;Zr||t===h&&T>s)return 1;for(var Y=0;;){Y++,B=Z-I,m[R]z?(S=M[P+m[R]],E=L[H+m[R]]):(S=96,E=0),p=1<>I)+v]=B<<24|S<<16|E|0;while(0!==v);for(p=1<>=1;if(0!==p?(F&=p-1,F+=p):F=0,R++,0===--j[Z]){if(Z===N)break;Z=e[a+m[R]]}if(Z>O&&(F&y)!==k){for(0===I&&(I=O),x+=C,D=Z-I,U=1<r||t===h&&T>s)return 1;k=F&y,b[k]=O<<24|D<<16|x-g|0}}return 0!==F&&(b[x+F]=Z-I<<24|64<<16|0),w.bits=O,0}},{"../utils/common":3}],13:[function(t,e,a){"use strict";e.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],14:[function(t,e,a){"use strict";function i(t){for(var e=t.length;--e>=0;)t[e]=0}function n(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}function r(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function s(t){return t<256?lt[t]:lt[256+(t>>>7)]}function o(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function l(t,e,a){t.bi_valid>W-a?(t.bi_buf|=e<>W-t.bi_valid,t.bi_valid+=a-W):(t.bi_buf|=e<>>=1,a<<=1;while(--e>0);return a>>>1}function f(t){16===t.bi_valid?(o(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}function _(t,e){var a,i,n,r,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,_=e.stat_desc.extra_bits,u=e.stat_desc.extra_base,c=e.stat_desc.max_length,b=0;for(r=0;r<=X;r++)t.bl_count[r]=0;for(l[2*t.heap[t.heap_max]+1]=0,a=t.heap_max+1;ac&&(r=c,b++),l[2*i+1]=r,i>h||(t.bl_count[r]++,s=0,i>=u&&(s=_[i-u]),o=l[2*i],t.opt_len+=o*(r+s),f&&(t.static_len+=o*(d[2*i+1]+s)));if(0!==b){do{for(r=c-1;0===t.bl_count[r];)r--;t.bl_count[r]--,t.bl_count[r+1]+=2,t.bl_count[c]--,b-=2}while(b>0);for(r=c;0!==r;r--)for(i=t.bl_count[r];0!==i;)n=t.heap[--a],n>h||(l[2*n+1]!==r&&(t.opt_len+=(r-l[2*n+1])*l[2*n],l[2*n+1]=r),i--)}}function u(t,e,a){var i,n,r=new Array(X+1),s=0;for(i=1;i<=X;i++)r[i]=s=s+a[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=d(r[o]++,o))}}function c(){var t,e,a,i,r,s=new Array(X+1);for(a=0,i=0;i>=7;i8?o(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function m(t,e,a,i){g(t),i&&(o(t,a),o(t,~a)),N.arraySet(t.pending_buf,t.window,e,a,t.pending),t.pending+=a}function w(t,e,a,i){var n=2*e,r=2*a;return t[n]>1;a>=1;a--)p(t,r,a);n=l;do a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],p(t,r,1),i=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=i,r[2*n]=r[2*a]+r[2*i],t.depth[n]=(t.depth[a]>=t.depth[i]?t.depth[a]:t.depth[i])+1,r[2*a+1]=r[2*i+1]=n,t.heap[1]=n++,p(t,r,1);while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],_(t,e),u(r,h,t.bl_count)}function y(t,e,a){var i,n,r=-1,s=e[1],o=0,l=7,h=4;for(0===s&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=s,s=e[2*(i+1)+1],++o=3&&0===t.bl_tree[2*nt[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}function B(t,e,a,i){var n;for(l(t,e-257,5),l(t,a-1,5),l(t,i-4,4),n=0;n>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return D;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return I;for(e=32;e0?(t.strm.data_type===U&&(t.strm.data_type=S(t)),k(t,t.l_desc),k(t,t.d_desc),s=z(t),n=t.opt_len+3+7>>>3,r=t.static_len+3+7>>>3,r<=n&&(n=r)):n=r=a+5,a+4<=n&&e!==-1?A(t,e,a,i):t.strategy===O||r===n?(l(t,(F<<1)+(i?1:0),3),v(t,st,ot)):(l(t,(L<<1)+(i?1:0),3),B(t,t.l_desc.max_code+1,t.d_desc.max_code+1,s+1),v(t,t.dyn_ltree,t.dyn_dtree)),b(t),i&&g(t)}function C(t,e,a){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&a,t.last_lit++,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(ht[a]+M+1)]++,t.dyn_dtree[2*s(e)]++),t.last_lit===t.lit_bufsize-1}var N=t("../utils/common"),O=4,D=0,I=1,U=2,T=0,F=1,L=2,H=3,j=258,K=29,M=256,P=M+1+K,Y=30,q=19,G=2*P+1,X=15,W=16,J=7,Q=256,V=16,$=17,tt=18,et=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],at=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],it=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],nt=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],rt=512,st=new Array(2*(P+2));i(st);var ot=new Array(2*Y);i(ot);var lt=new Array(rt);i(lt);var ht=new Array(j-H+1);i(ht);var dt=new Array(K);i(dt);var ft=new Array(Y);i(ft);var _t,ut,ct,bt=!1;a._tr_init=E,a._tr_stored_block=A,a._tr_flush_block=R,a._tr_tally=C,a._tr_align=Z},{"../utils/common":3}],15:[function(t,e,a){"use strict";function i(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}e.exports=i},{}],"/":[function(t,e,a){"use strict";var i=t("./lib/utils/common").assign,n=t("./lib/deflate"),r=t("./lib/inflate"),s=t("./lib/zlib/constants"),o={};i(o,n,r,s),e.exports=o},{"./lib/deflate":1,"./lib/inflate":2,"./lib/utils/common":3,"./lib/zlib/constants":6}]},{},[])("/")});
4 |
--------------------------------------------------------------------------------
/bin/html5/bin/manifest/default.json:
--------------------------------------------------------------------------------
1 | {"name":null,"assets":"ah","version":2,"libraryArgs":[],"libraryType":null}
--------------------------------------------------------------------------------
/bin/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Delaunay JS example
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/build.hxml:
--------------------------------------------------------------------------------
1 | -cp src
2 | -cp com
3 | -main DemoJs
4 | -js bin/js/js_demo.js
5 | # -D source-map-content
6 | # -debug
7 | -dce full
8 | # -xml bin/delaunay.xml
9 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/ArrayHelper.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 |
4 | /**
5 | * ...
6 | * @author sledorze
7 | * @author azrafe7
8 | */
9 | class ArrayHelper {
10 |
11 | /**
12 | * Empties an array of its' contents
13 | * @param array filled array
14 | */
15 | public static inline function clear(array:Array)
16 | {
17 | #if (cpp || php)
18 | array.splice(0, array.length);
19 | #else
20 | untyped array.length = 0;
21 | #end
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/com/nodename/delaunay/Edge.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.LineSegment;
4 | import com.nodename.geom.Point;
5 | import com.nodename.geom.Rectangle;
6 |
7 |
8 |
9 | /**
10 | * The line segment connecting the two Sites is part of the Delaunay triangulation;
11 | * the line segment connecting the two Vertices is part of the Voronoi diagram
12 | * @author ashaw
13 | *
14 | */
15 | class Edge
16 | {
17 | private static var _pool:Array = new Array();
18 |
19 | private static var _nedges:Int = 0;
20 |
21 | public static var DELETED:Edge = new Edge();
22 |
23 | /**
24 | * This is the only way to create a new Edge
25 | * @param site0
26 | * @param site1
27 | * @return
28 | *
29 | */
30 | public static function createBisectingEdge(site0:Site, site1:Site):Edge {
31 |
32 | var dx = site1.x - site0.x;
33 | var dy = site1.y - site0.y;
34 | var absdx = dx > 0 ? dx : -dx;
35 | var absdy = dy > 0 ? dy : -dy;
36 | var c = site0.x * dx + site0.y * dy + (dx * dx + dy * dy) * 0.5;
37 |
38 | var a:Float;
39 | var b:Float;
40 | if (absdx > absdy) {
41 | a = 1.0; b = dy/dx; c /= dx;
42 | } else {
43 | b = 1.0; a = dx/dy; c /= dy;
44 | }
45 |
46 | var edge = Edge.create();
47 |
48 | edge.leftSite = site0;
49 | edge.rightSite = site1;
50 |
51 | site0.addEdge(edge);
52 | site1.addEdge(edge);
53 |
54 | edge.leftVertex = null;
55 | edge.rightVertex = null;
56 |
57 | edge.a = a;
58 | edge.b = b;
59 | edge.c = c;
60 | //trace("createBisectingEdge: a ", edge.a, "b", edge.b, "c", edge.c);
61 |
62 | return edge;
63 | }
64 |
65 | private static function create():Edge
66 | {
67 | var edge:Edge;
68 | if (_pool.length > 0)
69 | {
70 | edge = _pool.pop();
71 | edge.init();
72 | }
73 | else
74 | {
75 | edge = new Edge();
76 | }
77 | return edge;
78 | }
79 |
80 | public function delaunayLine():LineSegment {
81 | // draw a line connecting the input Sites for which the edge is a bisector:
82 | return new LineSegment(leftSite.coord, rightSite.coord);
83 | }
84 |
85 |
86 | // the equation of the edge: ax + by = c
87 | public var a:Float = 0;
88 | public var b:Float = 0;
89 | public var c:Float = 0;
90 |
91 | // the two Voronoi vertices that the edge connects
92 | // (if one of them is null, the edge extends to infinity)
93 | public var leftVertex(get, null):Vertex = null;
94 | inline private function get_leftVertex():Vertex {
95 | return leftVertex;
96 | }
97 |
98 | public var rightVertex(get, null):Vertex = null;
99 | inline private function get_rightVertex():Vertex {
100 | return rightVertex;
101 | }
102 |
103 | inline public function vertex(leftRight:LR):Vertex {
104 | return (leftRight == LR.LEFT) ? leftVertex : rightVertex;
105 | }
106 |
107 | public function setVertex(leftRight:LR, v:Vertex):Void {
108 | if (leftRight == LR.LEFT) {
109 | leftVertex = v;
110 | } else {
111 | rightVertex = v;
112 | }
113 | }
114 |
115 | inline public function isPartOfConvexHull():Bool {
116 | return (leftVertex == null || rightVertex == null);
117 | }
118 |
119 | inline public function sitesDistance():Float {
120 | return Point.distance(leftSite.coord, rightSite.coord);
121 | }
122 |
123 | static public function compareSitesDistances_MAX(edge0:Edge, edge1:Edge):Int {
124 | var length0:Float = edge0.sitesDistance();
125 | var length1:Float = edge1.sitesDistance();
126 | if (length0 < length1) {
127 | return 1;
128 | } else if (length0 > length1) {
129 | return -1;
130 | } else {
131 | return 0;
132 | }
133 | }
134 |
135 | inline static public function compareSitesDistances(edge0:Edge, edge1:Edge):Int {
136 | return -compareSitesDistances_MAX(edge0, edge1);
137 | }
138 |
139 | private var __leftPoint : Point;
140 | private var __rightPoint : Point;
141 |
142 | public function clippedEnds(or : LR) : Point {
143 | return (or == LR.LEFT)?__leftPoint:__rightPoint;
144 | }
145 |
146 | public function setClippedEnds(or : LR, p : Point) : Void {
147 | if (or == LR.LEFT)
148 | __leftPoint = p;
149 | else
150 | __rightPoint = p;
151 | }
152 |
153 | // unless the entire Edge is outside the bounds.
154 | // In that case visible will be false:
155 | public var visible(get, null): Bool;
156 | inline private function get_visible():Bool {
157 | return __leftPoint != null && __rightPoint != null; // _clippedVertices != null;
158 | }
159 |
160 | // the two input Sites for which this Edge is a bisector:
161 | public var leftSite : Site;
162 | public var rightSite : Site;
163 |
164 | inline public function site(leftRight:LR):Site {
165 | return (leftRight == LR.LEFT) ? leftSite : rightSite;
166 | }
167 |
168 | public var _edgeIndex:Int = 0;
169 |
170 | public function dispose():Void {
171 | leftVertex = null;
172 | rightVertex = null;
173 | setClippedEnds(LR.LEFT, null);
174 | setClippedEnds(LR.RIGHT, null);
175 |
176 | rightSite = null;
177 | leftSite = null;
178 |
179 | leftSite = null;
180 | rightSite = null;
181 |
182 | //_sitesDic = null;
183 |
184 | _pool.push(this);
185 | }
186 |
187 | // Should be private
188 |
189 | public function new() {
190 | _edgeIndex = _nedges++;
191 | init();
192 | }
193 |
194 | private function init():Void
195 | {
196 | __leftPoint = null;
197 | __rightPoint = null;
198 |
199 | leftSite = null;
200 | rightSite = null;
201 | }
202 |
203 | public function toString():String
204 | {
205 | return "Edge " + _edgeIndex + "; sites " + site(LR.LEFT) + ", " + site(LR.RIGHT)
206 | + "; endVertices " + (leftVertex != null ? ""+leftVertex.vertexIndex : "null") + ", "
207 | + (rightVertex != null ? ""+rightVertex.vertexIndex : "null") + ((leftSite != null)?""+leftSite:"null") + ((rightSite != null)?""+rightSite:"null") + "::";
208 | }
209 |
210 | /**
211 | * Set _clippedVertices to contain the two ends of the portion of the Voronoi edge that is visible
212 | * within the bounds. If no part of the Edge falls within the bounds, leave _clippedVertices null.
213 | * @param bounds
214 | *
215 | */
216 | public function clipVertices(bounds:Rectangle):Void {
217 | var xmin:Float = bounds.x;
218 | var ymin:Float = bounds.y;
219 | var xmax:Float = bounds.right;
220 | var ymax:Float = bounds.bottom;
221 |
222 | var vertex0:Vertex, vertex1:Vertex;
223 | var x0:Float, x1:Float, y0:Float, y1:Float;
224 |
225 | if (a == 1.0 && b >= 0.0)
226 | {
227 | vertex0 = rightVertex;
228 | vertex1 = leftVertex;
229 | }
230 | else
231 | {
232 | vertex0 = leftVertex;
233 | vertex1 = rightVertex;
234 | }
235 |
236 | if (a == 1.0)
237 | {
238 | y0 = ymin;
239 | if (vertex0 != null && vertex0.y > ymin)
240 | {
241 | y0 = vertex0.y;
242 | }
243 | if (y0 > ymax)
244 | {
245 | return;
246 | }
247 | x0 = c - b * y0;
248 |
249 | y1 = ymax;
250 | if (vertex1 != null && vertex1.y < ymax)
251 | {
252 | y1 = vertex1.y;
253 | }
254 | if (y1 < ymin)
255 | {
256 | return;
257 | }
258 | x1 = c - b * y1;
259 |
260 | if ((x0 > xmax && x1 > xmax) || (x0 < xmin && x1 < xmin))
261 | {
262 | return;
263 | }
264 |
265 | if (x0 > xmax)
266 | {
267 | x0 = xmax; y0 = (c - x0)/b;
268 | }
269 | else if (x0 < xmin)
270 | {
271 | x0 = xmin; y0 = (c - x0)/b;
272 | }
273 |
274 | if (x1 > xmax)
275 | {
276 | x1 = xmax; y1 = (c - x1)/b;
277 | }
278 | else if (x1 < xmin)
279 | {
280 | x1 = xmin; y1 = (c - x1)/b;
281 | }
282 | }
283 | else
284 | {
285 | x0 = xmin;
286 | if (vertex0 != null && vertex0.x > xmin)
287 | {
288 | x0 = vertex0.x;
289 | }
290 | if (x0 > xmax)
291 | {
292 | return;
293 | }
294 | y0 = c - a * x0;
295 |
296 | x1 = xmax;
297 | if (vertex1 != null && vertex1.x < xmax)
298 | {
299 | x1 = vertex1.x;
300 | }
301 | if (x1 < xmin)
302 | {
303 | return;
304 | }
305 | y1 = c - a * x1;
306 |
307 | if ((y0 > ymax && y1 > ymax) || (y0 < ymin && y1 < ymin))
308 | {
309 | return;
310 | }
311 |
312 | if (y0 > ymax)
313 | {
314 | y0 = ymax; x0 = (c - y0)/a;
315 | }
316 | else if (y0 < ymin)
317 | {
318 | y0 = ymin; x0 = (c - y0)/a;
319 | }
320 |
321 | if (y1 > ymax)
322 | {
323 | y1 = ymax; x1 = (c - y1)/a;
324 | }
325 | else if (y1 < ymin)
326 | {
327 | y1 = ymin; x1 = (c - y1)/a;
328 | }
329 | }
330 |
331 | if (vertex0 == leftVertex)
332 | {
333 | setClippedEnds(LR.LEFT, new Point(x0, y0));
334 | setClippedEnds(LR.RIGHT, new Point(x1, y1));
335 | } else {
336 | setClippedEnds(LR.RIGHT, new Point(x0, y0));
337 | setClippedEnds(LR.LEFT, new Point(x1, y1));
338 | }
339 | }
340 |
341 | }
342 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/EdgeList.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.delaunay.IDisposable;
4 | import com.nodename.geom.Point;
5 |
6 |
7 | class EdgeList implements IDisposable {
8 |
9 | private var _deltax:Float = 0;
10 | private var _xmin:Float = 0;
11 |
12 | private var _hashsize:Int;
13 | private var _hash:Array;
14 |
15 | public var leftEnd(get, null):Halfedge;
16 | inline private function get_leftEnd():Halfedge {
17 | return leftEnd;
18 | }
19 |
20 | public var rightEnd(get, null):Halfedge;
21 | inline private function get_rightEnd():Halfedge {
22 | return rightEnd;
23 | }
24 |
25 | public function dispose():Void
26 | {
27 | var halfEdge:Halfedge = leftEnd;
28 | var prevHe:Halfedge;
29 | while (halfEdge != rightEnd)
30 | {
31 | prevHe = halfEdge;
32 | halfEdge = halfEdge.edgeListRightNeighbor;
33 | prevHe.dispose();
34 | }
35 | leftEnd = null;
36 | rightEnd.dispose();
37 | rightEnd = null;
38 |
39 | var i:Int;
40 | for (i in 0..._hashsize) {
41 | _hash[i] = null;
42 | }
43 | _hash = null;
44 | }
45 |
46 | public function new(xmin:Float, deltax:Float, sqrt_nsites:Int) {
47 | _xmin = xmin;
48 | _deltax = deltax;
49 | _hashsize = 2 * sqrt_nsites;
50 |
51 | _hash = new Array();
52 | _hash[_hashsize - 1] = null;
53 |
54 | // two dummy Halfedges:
55 | leftEnd = Halfedge.createDummy();
56 | rightEnd = Halfedge.createDummy();
57 | leftEnd.edgeListLeftNeighbor = null;
58 | leftEnd.edgeListRightNeighbor = rightEnd;
59 | rightEnd.edgeListLeftNeighbor = leftEnd;
60 | rightEnd.edgeListRightNeighbor = null;
61 | _hash[0] = leftEnd;
62 | _hash[_hashsize - 1] = rightEnd;
63 | }
64 |
65 | /**
66 | * Insert newHalfedge to the right of lb
67 | * @param lb
68 | * @param newHalfedge
69 | *
70 | */
71 | public function insert(lb:Halfedge, newHalfedge:Halfedge):Void
72 | {
73 | newHalfedge.edgeListLeftNeighbor = lb;
74 | newHalfedge.edgeListRightNeighbor = lb.edgeListRightNeighbor;
75 | lb.edgeListRightNeighbor.edgeListLeftNeighbor = newHalfedge;
76 | lb.edgeListRightNeighbor = newHalfedge;
77 | }
78 |
79 | /**
80 | * This function only removes the Halfedge from the left-right list.
81 | * We cannot dispose it yet because we are still using it.
82 | * @param halfEdge
83 | *
84 | */
85 |
86 | public function remove(halfEdge:Halfedge):Void
87 | {
88 | halfEdge.edgeListLeftNeighbor.edgeListRightNeighbor = halfEdge.edgeListRightNeighbor;
89 | halfEdge.edgeListRightNeighbor.edgeListLeftNeighbor = halfEdge.edgeListLeftNeighbor;
90 | //trace("RRRRRRREEEEEEEEEEEEEMMMMMMMMMMMOOOOOOOOOOVVVVVVVVVVVEEEEEEEEEEEDDDDDDDDDD");
91 | halfEdge.edge = Edge.DELETED;
92 | halfEdge.edgeListLeftNeighbor = halfEdge.edgeListRightNeighbor = null;
93 | }
94 |
95 | /**
96 | * Find the rightmost Halfedge that is still left of p
97 | * @param p
98 | * @return
99 | *
100 | */
101 | public function edgeListLeftNeighbor(p:Point):Halfedge {
102 |
103 | /* Use hash table to get close to desired halfedge */
104 | var bucket = Std.int(((p.x - _xmin)/_deltax) * _hashsize);
105 | if (bucket < 0) {
106 | bucket = 0;
107 | }
108 | if (bucket >= _hashsize) {
109 | bucket = _hashsize - 1;
110 | }
111 |
112 | var halfEdge = getHash(bucket);
113 | if (halfEdge == null) {
114 | var i = 1;
115 | while (true) {
116 | halfEdge = getHash(bucket - i);
117 | if (halfEdge != null) break;
118 | halfEdge = getHash(bucket + i);
119 | if (halfEdge != null) break;
120 | i++;
121 | }
122 | }
123 |
124 | /* Now search linear list of halfedges for the correct one */
125 | if (halfEdge == leftEnd || (halfEdge != rightEnd && halfEdge.isLeftOf(p)))
126 | {
127 | do
128 | {
129 | halfEdge = halfEdge.edgeListRightNeighbor;
130 | }
131 | while (halfEdge != rightEnd && halfEdge.isLeftOf(p));
132 | halfEdge = halfEdge.edgeListLeftNeighbor;
133 | }
134 | else
135 | {
136 | do
137 | {
138 | halfEdge = halfEdge.edgeListLeftNeighbor;
139 | }
140 | while (halfEdge != leftEnd && !halfEdge.isLeftOf(p));
141 | }
142 |
143 | /* Update hash table and reference counts */
144 | if (bucket > 0 && bucket <_hashsize - 1)
145 | {
146 | _hash[bucket] = halfEdge;
147 | }
148 | return halfEdge;
149 | }
150 |
151 | /* Get entry from hash table, pruning any deleted nodes */
152 | private function getHash(b:Int):Halfedge {
153 | var halfEdge:Halfedge = null;
154 |
155 | if (b >= 0 && b < _hashsize) {
156 | halfEdge = _hash[b];
157 | if (halfEdge != null && halfEdge.edge == Edge.DELETED)
158 | {
159 | /* Hash table points to deleted halfedge. Patch as necessary. */
160 | _hash[b] = null;
161 | // still can't dispose halfEdge yet!
162 | halfEdge = null;
163 | }
164 | }
165 | return halfEdge;
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/EdgeReorderer.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 |
4 | class EdgeReorderer {
5 |
6 | public var edges(get, null):Array;
7 | inline private function get_edges():Array {
8 | return edges;
9 | }
10 |
11 | public var edgeOrientations(get, null):Array;
12 | inline private function get_edgeOrientations():Array {
13 | return edgeOrientations;
14 | }
15 |
16 | inline public static function edgeToLeftVertex(ed : Edge) : ICoord { return ed.leftVertex;}
17 | inline public static function edgeToLeftSite(ed : Edge) : ICoord { return ed.leftSite;}
18 | inline public static function edgeToRightVertex(ed : Edge) : ICoord { return ed.rightVertex;}
19 | inline public static function edgeToRightSite(ed : Edge) : ICoord { return ed.rightSite;}
20 |
21 | // TODO: use a ADT to represent criterion.
22 | public function new(origEdges:Array, leftCoord: Edge -> ICoord, rightCoord: Edge -> ICoord) {
23 | edges = new Array();
24 | edgeOrientations = new Array();
25 | if (origEdges.length > 0)
26 | {
27 | edges = reorderEdges(origEdges, leftCoord, rightCoord);
28 | }
29 | }
30 |
31 | public function dispose():Void
32 | {
33 | edges = null;
34 | edgeOrientations = null;
35 | }
36 |
37 | private function reorderEdges(origEdges:Array, leftCoord: Edge -> ICoord, rightCoord: Edge -> ICoord):Array {
38 | var i:Int;
39 | var j:Int;
40 | var n:Int = origEdges.length;
41 | var edge:Edge;
42 | // we're going to reorder the edges in order of traversal
43 | var done:Array = new Array();
44 | done[n - 1] = false;
45 | var nDone:Int = 0;
46 | for (b in done) {
47 | b = false;
48 | }
49 | var newEdges:Array = new Array();
50 |
51 | i = 0;
52 | edge = origEdges[i];
53 | newEdges.push(edge);
54 | edgeOrientations.push(LR.LEFT);
55 | var firstPoint:ICoord = leftCoord(edge);
56 | var lastPoint:ICoord = rightCoord(edge);
57 |
58 | if (firstPoint == Vertex.VERTEX_AT_INFINITY || lastPoint == Vertex.VERTEX_AT_INFINITY)
59 | {
60 | return new Array();
61 | }
62 |
63 | done[i] = true;
64 | ++nDone;
65 |
66 | while (nDone < n)
67 | {
68 | for (i in 1...n)
69 | {
70 | if (done[i])
71 | {
72 | continue;
73 | }
74 | edge = origEdges[i];
75 | var leftPoint:ICoord = leftCoord(edge);
76 | var rightPoint:ICoord = rightCoord(edge);
77 |
78 | if (leftPoint == Vertex.VERTEX_AT_INFINITY || rightPoint == Vertex.VERTEX_AT_INFINITY)
79 | {
80 | return new Array();
81 | }
82 | if (leftPoint == lastPoint)
83 | {
84 | lastPoint = rightPoint;
85 | edgeOrientations.push(LR.LEFT);
86 | newEdges.push(edge);
87 | done[i] = true;
88 | }
89 | else if (rightPoint == firstPoint)
90 | {
91 | firstPoint = leftPoint;
92 | edgeOrientations.unshift(LR.LEFT);
93 | newEdges.unshift(edge);
94 | done[i] = true;
95 | }
96 | else if (leftPoint == firstPoint)
97 | {
98 | firstPoint = rightPoint;
99 | edgeOrientations.unshift(LR.RIGHT);
100 | newEdges.unshift(edge);
101 | done[i] = true;
102 | }
103 | else if (rightPoint == lastPoint)
104 | {
105 | lastPoint = leftPoint;
106 | edgeOrientations.push(LR.RIGHT);
107 | newEdges.push(edge);
108 | done[i] = true;
109 | }
110 | if (done[i])
111 | {
112 | ++nDone;
113 | }
114 | }
115 | }
116 |
117 | return newEdges;
118 | }
119 |
120 | }
--------------------------------------------------------------------------------
/com/nodename/delaunay/Halfedge.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 |
5 |
6 | class Halfedge {
7 | private static var _pool:Array = new Array();
8 | public static function create(edge:Edge, lr:LR):Halfedge {
9 | if (_pool.length > 0)
10 | {
11 | return _pool.pop().init(edge, lr);
12 | }
13 | else
14 | {
15 | return new Halfedge(edge, lr);
16 | }
17 | }
18 |
19 | public static function createDummy():Halfedge
20 | {
21 | return create(null, null);
22 | }
23 |
24 | public var edgeListLeftNeighbor:Halfedge;
25 | public var edgeListRightNeighbor:Halfedge;
26 | public var nextInPriorityQueue:Halfedge;
27 |
28 | public var edge:Edge;
29 | public var leftRight:LR;
30 | public var vertex:Vertex;
31 |
32 | // the vertex's y-coordinate in the transformed Voronoi space V*
33 | public var ystar:Float = 0;
34 |
35 | // Should be private
36 | public function new(edge:Edge = null, lr:LR = null)
37 | {
38 | init(edge, lr);
39 | }
40 |
41 | private function init(edge:Edge, lr:LR):Halfedge
42 | {
43 | this.edge = edge;
44 | leftRight = lr;
45 | nextInPriorityQueue = null;
46 | vertex = null;
47 | return this;
48 | }
49 |
50 | public function toString():String
51 | {
52 | return "Halfedge (leftRight: " + leftRight + "; vertex: " + vertex + ")";
53 | }
54 |
55 | public function dispose():Void
56 | {
57 | if (edgeListLeftNeighbor != null || edgeListRightNeighbor != null)
58 | {
59 | // still in EdgeList
60 | return;
61 | }
62 | if (nextInPriorityQueue != null)
63 | {
64 | // still in PriorityQueue
65 | return;
66 | }
67 | edge = null;
68 | leftRight = null;
69 | vertex = null;
70 | _pool.push(this);
71 | }
72 |
73 | public function reallyDispose():Void
74 | {
75 | edgeListLeftNeighbor = null;
76 | edgeListRightNeighbor = null;
77 | nextInPriorityQueue = null;
78 | edge = null;
79 | leftRight = null;
80 | vertex = null;
81 | _pool.push(this);
82 | }
83 |
84 | public function isLeftOf(p:Point):Bool {
85 | var above:Bool;
86 |
87 | var topSite = edge.rightSite;
88 |
89 | var rightOfSite = p.x > topSite.x;
90 | if (rightOfSite && this.leftRight == LR.LEFT) {
91 | return true;
92 | }
93 | if (!rightOfSite && this.leftRight == LR.RIGHT) {
94 | return false;
95 | }
96 |
97 | if (edge.a == 1.0) {
98 | var dyp = p.y - topSite.y;
99 | var dxp = p.x - topSite.x;
100 | var fast = false;
101 | if ((!rightOfSite && edge.b < 0.0) || (rightOfSite && edge.b >= 0.0) ) {
102 | above = dyp >= (edge.b * dxp);
103 | fast = above;
104 | } else {
105 | above = (p.x + p.y * edge.b) > edge.c;
106 | if (edge.b < 0.0) {
107 | above = !above;
108 | }
109 | fast = !above;
110 | }
111 | if (!fast) {
112 | var dxs = topSite.x - edge.leftSite.x;
113 | above = edge.b * (dxp * dxp - dyp * dyp) < (dxs * dyp * (1.0 + 2.0 * dxp/dxs + edge.b * edge.b));
114 | if (edge.b < 0.0) {
115 | above = !above;
116 | }
117 | }
118 | } else /* edge.b == 1.0 */ {
119 | var yl = edge.c - edge.a * p.x;
120 | var t1 = p.y - yl;
121 | var t2 = p.x - topSite.x;
122 | var t3 = yl - topSite.y;
123 | above = (t1 * t1) > (t2 * t2 + t3 * t3);
124 | }
125 |
126 | var xx = this.leftRight == LR.LEFT;
127 |
128 | return this.leftRight == LR.LEFT ? above : !above;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/HalfedgePriorityQueue.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 |
5 |
6 | class HalfedgePriorityQueue // also known as heap
7 | {
8 | private var _hash:Array;
9 | private var _count:Int;
10 | private var _minBucket:Int;
11 | private var _hashsize:Int;
12 |
13 | private var _ymin:Float = 0;
14 | private var _deltay:Float = 0;
15 |
16 | public function new(ymin:Float, deltay:Float, sqrt_nsites:Int)
17 | {
18 | _ymin = ymin;
19 | _deltay = deltay;
20 | _hashsize = 4 * sqrt_nsites;
21 | initialize();
22 | }
23 |
24 | public function dispose():Void
25 | {
26 | // get rid of dummies
27 | for (i in 0..._hashsize)
28 | {
29 | _hash[i].dispose();
30 | _hash[i] = null;
31 | }
32 | _hash = null;
33 | }
34 |
35 | private function initialize():Void
36 | {
37 | var i:Int;
38 |
39 | _count = 0;
40 | _minBucket = 0;
41 | _hash = new Array();
42 | _hash[_hashsize - 1] = null;
43 | // dummy Halfedge at the top of each hash
44 | for (i in 0..._hashsize)
45 | {
46 | _hash[i] = Halfedge.createDummy();
47 | _hash[i].nextInPriorityQueue = null;
48 | }
49 | }
50 |
51 | public function insert(halfEdge:Halfedge):Void
52 | {
53 | var previous:Halfedge, next:Halfedge;
54 | var insertionBucket:Int = bucket(halfEdge);
55 | if (insertionBucket < _minBucket)
56 | {
57 | _minBucket = insertionBucket;
58 | }
59 | previous = _hash[insertionBucket];
60 | while ((next = previous.nextInPriorityQueue) != null
61 | && (halfEdge.ystar > next.ystar || (halfEdge.ystar == next.ystar && halfEdge.vertex.x > next.vertex.x)))
62 | {
63 | previous = next;
64 | }
65 | halfEdge.nextInPriorityQueue = previous.nextInPriorityQueue;
66 | previous.nextInPriorityQueue = halfEdge;
67 | ++_count;
68 | }
69 |
70 | public function remove(halfEdge:Halfedge):Void
71 | {
72 | var removalBucket = bucket(halfEdge);
73 |
74 | if (halfEdge.vertex != null)
75 | {
76 | var previous = _hash[removalBucket];
77 | while (previous.nextInPriorityQueue != halfEdge)
78 | {
79 | previous = previous.nextInPriorityQueue;
80 | }
81 | previous.nextInPriorityQueue = halfEdge.nextInPriorityQueue;
82 | _count--;
83 | halfEdge.vertex = null;
84 | halfEdge.nextInPriorityQueue = null;
85 | halfEdge.dispose();
86 | }
87 | }
88 |
89 | private function bucket(halfEdge:Halfedge):Int
90 | {
91 | var theBucket:Int = Std.int((halfEdge.ystar - _ymin) / _deltay * _hashsize);
92 | if (theBucket < 0) theBucket = 0;
93 | if (theBucket >= _hashsize) theBucket = _hashsize - 1;
94 | return theBucket;
95 | }
96 |
97 | private function isEmpty(bucket:Int):Bool
98 | {
99 | return (_hash[bucket].nextInPriorityQueue == null);
100 | }
101 |
102 | /**
103 | * move _minBucket until it contains an actual Halfedge (not just the dummy at the top);
104 | *
105 | */
106 | private function adjustMinBucket():Void
107 | {
108 | while (_minBucket < _hashsize - 1 && isEmpty(_minBucket))
109 | {
110 | ++_minBucket;
111 | }
112 | }
113 |
114 | public function empty():Bool
115 | {
116 | return _count == 0;
117 | }
118 |
119 | /**
120 | * @return coordinates of the Halfedge's vertex in V*, the transformed Voronoi diagram
121 | *
122 | */
123 | public function min():Point
124 | {
125 | adjustMinBucket();
126 | var answer:Halfedge = _hash[_minBucket].nextInPriorityQueue;
127 | return new Point(answer.vertex.x, answer.ystar);
128 | }
129 |
130 | /**
131 | * remove and return the min Halfedge
132 | * @return
133 | *
134 | */
135 | public function extractMin():Halfedge
136 | {
137 | // get the first real Halfedge in _minBucket
138 | var answer = _hash[_minBucket].nextInPriorityQueue;
139 |
140 | _hash[_minBucket].nextInPriorityQueue = answer.nextInPriorityQueue;
141 | _count--;
142 | answer.nextInPriorityQueue = null;
143 |
144 | return answer;
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/ICoord.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 |
5 | interface ICoord {
6 | var coord(get, null):Point;
7 | }
8 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/IDisposable.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | interface IDisposable
4 | {
5 | function dispose():Void;
6 | }
7 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/Kruskal.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.LineSegment;
4 | import com.nodename.geom.Point;
5 |
6 |
7 | class Kruskal {
8 | /**
9 | * Kruskal's spanning tree algorithm with union-find
10 | * Skiena: The Algorithm Design Manual, p. 196ff
11 | * Note: the sites are implied: they consist of the end points of the line segments
12 | */
13 | public static function kruskal(lineSegments:Array, type:SortType = null):Array
14 | {
15 | if (type == null) type = SortType.MINIMUM;
16 |
17 | var nodes = new Map();
18 | var mst:Array = new Array();
19 | var nodePool:Array = Node.pool;
20 |
21 | switch (type) {
22 | // note that the compare functions are the reverse of what you'd expect
23 | // because (see below) we traverse the lineSegments in reverse order for speed
24 | case MAXIMUM:
25 | lineSegments.sort(LineSegment.compareLengths);
26 | case MINIMUM:
27 | lineSegments.sort(LineSegment.compareLengths_MAX);
28 | }
29 | var index = lineSegments.length - 1;
30 | while (index >= 0) {
31 | var lineSegment = lineSegments[index];
32 | index--;
33 |
34 | var node0:Node = nodes[lineSegment.p0];
35 | var rootOfSet0:Node;
36 | if (node0 == null)
37 | {
38 | node0 = nodePool.length > 0 ? nodePool.pop() : new Node();
39 | // intialize the node:
40 | rootOfSet0 = node0.parent = node0;
41 | node0.treeSize = 1;
42 |
43 | nodes[lineSegment.p0] = node0;
44 | }
45 | else
46 | {
47 | rootOfSet0 = find(node0);
48 | }
49 |
50 | var node1:Node = nodes[cast lineSegment.p1];
51 | var rootOfSet1:Node;
52 | if (node1 == null)
53 | {
54 | node1 = nodePool.length > 0 ? nodePool.pop() : new Node();
55 | // intialize the node:
56 | rootOfSet1 = node1.parent = node1;
57 | node1.treeSize = 1;
58 |
59 | nodes[lineSegment.p1] = node1;
60 | }
61 | else
62 | {
63 | rootOfSet1 = find(node1);
64 | }
65 |
66 | if (rootOfSet0 != rootOfSet1) // nodes not in same set
67 | {
68 | mst.push(lineSegment);
69 |
70 | // merge the two sets:
71 | var treeSize0:Int = rootOfSet0.treeSize;
72 | var treeSize1:Int = rootOfSet1.treeSize;
73 | if (treeSize0 >= treeSize1)
74 | {
75 | // set0 absorbs set1:
76 | rootOfSet1.parent = rootOfSet0;
77 | rootOfSet0.treeSize += treeSize1;
78 | }
79 | else
80 | {
81 | // set1 absorbs set0:
82 | rootOfSet0.parent = rootOfSet1;
83 | rootOfSet1.treeSize += treeSize0;
84 | }
85 | }
86 | }
87 |
88 | for (node in nodes) {
89 | nodePool.push(node);
90 | }
91 |
92 | return mst;
93 | }
94 |
95 |
96 | static function find(node:Node):Node {
97 | if (node.parent == node) {
98 | return node;
99 | } else {
100 | var root:Node = find(node.parent);
101 | // this line is just to speed up subsequent finds by keeping the tree depth low:
102 | node.parent = root;
103 | return root;
104 | }
105 | }
106 | }
107 |
108 | class Node {
109 | public static var pool:Array = new Array();
110 |
111 | public var parent:Node;
112 | public var treeSize:Int;
113 |
114 | public function new() {}
115 | }
116 |
117 | enum SortType {
118 | MINIMUM;
119 | MAXIMUM;
120 | }
--------------------------------------------------------------------------------
/com/nodename/delaunay/LR.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | class LR {
4 | public static var LEFT:LR = new LR("left");
5 | public static var RIGHT:LR = new LR("right");
6 |
7 |
8 |
9 | private var _name:String;
10 |
11 | // Should be private
12 | public function new(name:String) {
13 | _name = name;
14 | }
15 |
16 | public static function other(leftRight:LR):LR
17 | {
18 | return leftRight == LEFT ? RIGHT : LEFT;
19 | }
20 | public function toString():String
21 | {
22 | return _name;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/SelectHelper.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 | import com.nodename.geom.LineSegment;
5 |
6 |
7 | class SelectHelper {
8 |
9 |
10 | public static function visibleLineSegments(edges:Array):Array
11 | {
12 | var segments = new Array();
13 |
14 | for (edge in edges) {
15 | if (edge.visible) {
16 | var p1 = edge.clippedEnds(LR.LEFT);
17 | var p2 = edge.clippedEnds(LR.RIGHT);
18 | segments.push(new LineSegment(p1, p2));
19 | }
20 | }
21 |
22 | return segments;
23 | }
24 |
25 | public static function selectEdgesForSitePoint(coord:Point, edgesToTest:Array):Array
26 | {
27 | return edgesToTest.filter(
28 | function (edge:Edge)
29 | return ((edge.leftSite != null && edge.leftSite.coord == coord) || (edge.rightSite != null && edge.rightSite.coord == coord))
30 | );
31 | }
32 |
33 | public static function delaunayLinesForEdges(edges:Array):Array
34 | {
35 | var segments = new Array();
36 | for (edge in edges) {
37 | segments.push(edge.delaunayLine());
38 | }
39 | return segments;
40 | }
41 |
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/Site.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Polygon;
4 | import com.nodename.geom.Winding;
5 | import com.nodename.geom.Point;
6 | import com.nodename.geom.Rectangle;
7 |
8 | using com.nodename.delaunay.ArrayHelper;
9 |
10 |
11 | class Site implements ICoord
12 | {
13 | private static var _pool:Array = new Array();
14 | public static function create(p:Point, index:Int, weight:Float, color:Int):Site {
15 | if (_pool.length > 0)
16 | {
17 | return _pool.pop().init(p, index, weight, color);
18 | }
19 | else
20 | {
21 | return new Site(p, index, weight, color);
22 | }
23 | }
24 |
25 | public inline static function sortSites(sites:Array):Void {
26 | sites.sort(Site.compare);
27 | }
28 |
29 | /**
30 | * sort sites on y, then x, coord
31 | * also change each site's _siteIndex to match its new position in the list
32 | * so the _siteIndex can be used to identify the site for nearest-neighbor queries
33 | *
34 | * haha "also" - means more than one responsibility...
35 | *
36 | */
37 |
38 | private static function compare(s1:Site, s2:Site):Int {
39 | var returnValue:Int = Voronoi.compareByYThenX(s1, s2);
40 |
41 | // swap _siteIndex values if necessary to match new ordering:
42 | var tempIndex:Int;
43 | if (returnValue == -1)
44 | {
45 | if (s1._siteIndex > s2._siteIndex)
46 | {
47 | tempIndex = s1._siteIndex;
48 | s1._siteIndex = s2._siteIndex;
49 | s2._siteIndex = tempIndex;
50 | }
51 | }
52 | else if (returnValue == 1)
53 | {
54 | if (s2._siteIndex > s1._siteIndex)
55 | {
56 | tempIndex = s2._siteIndex;
57 | s2._siteIndex = s1._siteIndex;
58 | s1._siteIndex = tempIndex;
59 | }
60 |
61 | }
62 |
63 | return returnValue;
64 | }
65 |
66 |
67 |
68 | private static var EPSILON = .005;
69 | private static function closeEnough(p0:Point, p1:Point):Bool {
70 | var dx2 = (p0.x - p1.x) * (p0.x - p1.x);
71 | var dy2 = (p0.y - p1.y) * (p0.y - p1.y);
72 | return Math.sqrt(dx2 + dy2) < EPSILON;
73 | }
74 |
75 | public var coord(get, null) : Point;
76 | inline private function get_coord():Point {
77 | return coord;
78 | }
79 |
80 | public var color:Int;
81 | public var weight:Float = 0;
82 |
83 | private var _siteIndex:Int;
84 |
85 | // the edges that define this Site's Voronoi region:
86 | public var edges(get, null):Array;
87 | inline private function get_edges():Array
88 | {
89 | return edges;
90 | }
91 | // which end of each edge hooks up with the previous edge in _edges:
92 | private var _edgeOrientations:Array;
93 | // ordered list of points that define the region clipped to bounds:
94 | private var _region:Array;
95 |
96 | // use create instead.
97 | private function new(p:Point, index:Int, weight:Float, color:Int) {
98 | init(p, index, weight, color);
99 | }
100 |
101 | private function init(p:Point, index:Int, weight:Float, color:Int):Site
102 | {
103 | coord = p;
104 | _siteIndex = index;
105 | this.weight = weight;
106 | this.color = color;
107 | edges = new Array();
108 | _region = null;
109 | return this;
110 | }
111 |
112 | public function toString():String
113 | {
114 | return "Site " + _siteIndex + ": " + coord;
115 | }
116 |
117 | private function move(p:Point):Void
118 | {
119 | clear();
120 | coord = p;
121 | }
122 |
123 | public function dispose():Void
124 | {
125 | coord = null;
126 | clear();
127 | _pool.push(this);
128 | }
129 |
130 | private function clear():Void
131 | {
132 | if (edges != null)
133 | {
134 | edges.clear();
135 | edges = null;
136 | }
137 | if (_edgeOrientations != null)
138 | {
139 | _edgeOrientations.clear();
140 | _edgeOrientations = null;
141 | }
142 | if (_region != null)
143 | {
144 | _region.clear();
145 | _region = null;
146 | }
147 | }
148 |
149 | inline public function addEdge(edge:Edge):Void
150 | {
151 | edges.push(edge);
152 | }
153 |
154 | // TODO: Can be optimized.
155 | public function nearestEdge():Edge
156 | {
157 | edges.sort(Edge.compareSitesDistances);
158 | return edges[0];
159 | }
160 |
161 | public function neighborSites():Array
162 | {
163 | if (edges == null || edges.length == 0)
164 | {
165 | return new Array();
166 | }
167 | if (_edgeOrientations == null)
168 | {
169 | reorderEdges();
170 | }
171 | var list = new Array();
172 | for (edge in edges)
173 | {
174 | list.push(neighborSite(edge));
175 | }
176 | return list;
177 | }
178 |
179 | private function neighborSite(edge:Edge):Site
180 | {
181 | if (this == edge.leftSite)
182 | {
183 | return edge.rightSite;
184 | }
185 | if (this == edge.rightSite)
186 | {
187 | return edge.leftSite;
188 | }
189 | return null;
190 | }
191 |
192 | public function region(clippingBounds:Rectangle):Array {
193 | if (edges == null || edges.length == 0)
194 | {
195 | return new Array();
196 | }
197 | if (_edgeOrientations == null)
198 | {
199 | reorderEdges();
200 | _region = clipToBounds(clippingBounds);
201 | if ((new Polygon(_region)).winding() == Winding.CLOCKWISE)
202 | {
203 | _region.reverse();
204 | }
205 | }
206 | return _region;
207 | }
208 |
209 | private function reorderEdges():Void
210 | {
211 | //trace("edges:", edges);
212 | var reorderer = new EdgeReorderer(edges, EdgeReorderer.edgeToLeftVertex, EdgeReorderer.edgeToRightVertex);
213 | edges = reorderer.edges;
214 | //trace("reordered:", edges);
215 | _edgeOrientations = reorderer.edgeOrientations;
216 | reorderer.dispose();
217 | }
218 |
219 | private function clipToBounds(bounds:Rectangle):Array
220 | {
221 | var points:Array = new Array();
222 | var n:Int = edges.length;
223 | var i:Int = 0;
224 | var edge:Edge;
225 | while (i < n && (edges[i].visible == false))
226 | {
227 | ++i;
228 | }
229 |
230 | if (i == n)
231 | {
232 | // no edges visible
233 | return new Array();
234 | }
235 | edge = edges[i];
236 | var orientation:LR = _edgeOrientations[i];
237 | points.push(edge.clippedEnds(orientation));
238 | points.push(edge.clippedEnds(LR.other(orientation)));
239 |
240 | for (j in (i + 1)...n)
241 | {
242 | edge = edges[j];
243 | if (edge.visible == false)
244 | {
245 | continue;
246 | }
247 | connect(points, j, bounds);
248 | }
249 | // close up the polygon by adding another corner point of the bounds if needed:
250 | connect(points, i, bounds, true);
251 |
252 | return points;
253 | }
254 |
255 | private function connect(points:Array, j:Int, bounds:Rectangle, closingUp:Bool = false):Void
256 | {
257 | var rightPoint = points[points.length - 1];
258 | var newEdge = edges[j];
259 | var newOrientation:LR = _edgeOrientations[j];
260 | // the point that must be connected to rightPoint:
261 | var newPoint = newEdge.clippedEnds(newOrientation);
262 | if (!closeEnough(rightPoint, newPoint))
263 | {
264 | // The points do not coincide, so they must have been clipped at the bounds;
265 | // see if they are on the same border of the bounds:
266 | if (rightPoint.x != newPoint.x
267 | && rightPoint.y != newPoint.y)
268 | {
269 | // They are on different borders of the bounds;
270 | // insert one or two corners of bounds as needed to hook them up:
271 | // (NOTE this will not be correct if the region should take up more than
272 | // half of the bounds rect, for then we will have gone the wrong way
273 | // around the bounds and included the smaller part rather than the larger)
274 | var rightCheck:Int = BoundsCheck.check(rightPoint, bounds);
275 | var newCheck:Int = BoundsCheck.check(newPoint, bounds);
276 | var px;
277 | var py;
278 | if (rightCheck & BoundsCheck.RIGHT != 0)
279 | {
280 | px = bounds.right;
281 | if (newCheck & BoundsCheck.BOTTOM != 0)
282 | {
283 | py = bounds.bottom;
284 | points.push(new Point(px, py));
285 | }
286 | else if (newCheck & BoundsCheck.TOP != 0)
287 | {
288 | py = bounds.top;
289 | points.push(new Point(px, py));
290 | }
291 | else if (newCheck & BoundsCheck.LEFT != 0)
292 | {
293 | if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height)
294 | {
295 | py = bounds.top;
296 | }
297 | else
298 | {
299 | py = bounds.bottom;
300 | }
301 | points.push(new Point(px, py));
302 | points.push(new Point(bounds.left, py));
303 | }
304 | }
305 | else if (rightCheck & BoundsCheck.LEFT != 0)
306 | {
307 | px = bounds.left;
308 | if (newCheck & BoundsCheck.BOTTOM != 0)
309 | {
310 | py = bounds.bottom;
311 | points.push(new Point(px, py));
312 | }
313 | else if (newCheck & BoundsCheck.TOP != 0)
314 | {
315 | py = bounds.top;
316 | points.push(new Point(px, py));
317 | }
318 | else if (newCheck & BoundsCheck.RIGHT != 0)
319 | {
320 | if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height)
321 | {
322 | py = bounds.top;
323 | }
324 | else
325 | {
326 | py = bounds.bottom;
327 | }
328 | points.push(new Point(px, py));
329 | points.push(new Point(bounds.right, py));
330 | }
331 | }
332 | else if (rightCheck & BoundsCheck.TOP != 0)
333 | {
334 | py = bounds.top;
335 | if (newCheck & BoundsCheck.RIGHT != 0)
336 | {
337 | px = bounds.right;
338 | points.push(new Point(px, py));
339 | }
340 | else if (newCheck & BoundsCheck.LEFT != 0)
341 | {
342 | px = bounds.left;
343 | points.push(new Point(px, py));
344 | }
345 | else if (newCheck & BoundsCheck.BOTTOM != 0)
346 | {
347 | if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width)
348 | {
349 | px = bounds.left;
350 | }
351 | else
352 | {
353 | px = bounds.right;
354 | }
355 | points.push(new Point(px, py));
356 | points.push(new Point(px, bounds.bottom));
357 | }
358 | }
359 | else if (rightCheck & BoundsCheck.BOTTOM != 0)
360 | {
361 | py = bounds.bottom;
362 | if (newCheck & BoundsCheck.RIGHT != 0)
363 | {
364 | px = bounds.right;
365 | points.push(new Point(px, py));
366 | }
367 | else if (newCheck & BoundsCheck.LEFT != 0)
368 | {
369 | px = bounds.left;
370 | points.push(new Point(px, py));
371 | }
372 | else if (newCheck & BoundsCheck.TOP != 0)
373 | {
374 | if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width)
375 | {
376 | px = bounds.left;
377 | }
378 | else
379 | {
380 | px = bounds.right;
381 | }
382 | points.push(new Point(px, py));
383 | points.push(new Point(px, bounds.top));
384 | }
385 | }
386 | }
387 | if (closingUp)
388 | {
389 | // newEdge's ends have already been added
390 | return;
391 | }
392 | points.push(newPoint);
393 | }
394 | var newRightPoint = newEdge.clippedEnds(LR.other(newOrientation));
395 | if (!closeEnough(points[0], newRightPoint))
396 | {
397 | points.push(newRightPoint);
398 | }
399 | }
400 |
401 | public var x(get, null):Float;
402 | inline private function get_x() {
403 | return coord.x;
404 | }
405 |
406 | public var y(get, null):Float;
407 | inline private function get_y() {
408 | return coord.y;
409 | }
410 |
411 | public function dist(p:ICoord)
412 | {
413 | var dx2 = (p.coord.x - this.coord.x) * (p.coord.x - this.coord.x);
414 | var dy2 = (p.coord.y - this.coord.y) * (p.coord.y - this.coord.y);
415 | return Math.sqrt(dx2 + dy2);
416 | }
417 |
418 | }
419 |
420 |
421 | @:final class BoundsCheck
422 | {
423 | public static var TOP:Int = 1;
424 | public static var BOTTOM:Int = 2;
425 | public static var LEFT:Int = 4;
426 | public static var RIGHT:Int = 8;
427 |
428 | /**
429 | *
430 | * @param point
431 | * @param bounds
432 | * @return an Int with the appropriate bits set if the Point lies on the corresponding bounds lines
433 | *
434 | */
435 | public static function check(point:Point, bounds:Rectangle):Int
436 | {
437 | var value:Int = 0;
438 | if (point.x == bounds.left)
439 | {
440 | value |= LEFT;
441 | } else if (point.x == bounds.right) {
442 | value |= RIGHT;
443 | }
444 | if (point.y == bounds.top)
445 | {
446 | value |= TOP;
447 | } else if (point.y == bounds.bottom) {
448 | value |= BOTTOM;
449 | }
450 | return value;
451 | }
452 |
453 | private function new()
454 | {
455 | throw "BoundsCheck constructor unused";
456 | }
457 |
458 | }
--------------------------------------------------------------------------------
/com/nodename/delaunay/SiteList.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Circle;
4 | import com.nodename.delaunay.IDisposable;
5 | import com.nodename.geom.Point;
6 | import com.nodename.geom.Rectangle;
7 |
8 | using com.nodename.delaunay.ArrayHelper;
9 |
10 |
11 | class SiteList implements IDisposable {
12 | private var _sites:Array;
13 | private var _currentIndex:Int;
14 |
15 | private var _sorted:Bool;
16 |
17 | public function new()
18 | {
19 | _sites = new Array();
20 | _sorted = false;
21 | }
22 |
23 | public function dispose():Void
24 | {
25 | if (_sites != null)
26 | {
27 | for (site in _sites)
28 | {
29 | site.dispose();
30 | }
31 | _sites.clear();
32 | _sites = null;
33 | }
34 | }
35 |
36 | public function push(site:Site):Int
37 | {
38 | _sorted = false;
39 | return _sites.push(site);
40 | }
41 |
42 | public var length(get, null) : Int;
43 | inline private function get_length():Int {
44 | return _sites.length;
45 | }
46 |
47 | public function next():Site
48 | {
49 | if (_sorted == false)
50 | {
51 | throw "SiteList::next(): sites have not been sorted";
52 | }
53 | if (_currentIndex < _sites.length)
54 | {
55 | return _sites[_currentIndex++];
56 | }
57 | else
58 | {
59 | return null;
60 | }
61 | }
62 |
63 | public function getSitesBounds():Rectangle {
64 | if (_sorted == false)
65 | {
66 | Site.sortSites(_sites);
67 | _currentIndex = 0;
68 | _sorted = true;
69 | }
70 | var xmin:Float;
71 | var xmax:Float;
72 | var ymin:Float;
73 | var ymax:Float;
74 | if (_sites.length == 0)
75 | {
76 | return new Rectangle(0, 0, 0, 0);
77 | }
78 |
79 | xmin = Math.POSITIVE_INFINITY;
80 | xmax = Math.NEGATIVE_INFINITY;
81 | for (site in _sites)
82 | {
83 | if (site.x < xmin)
84 | {
85 | xmin = site.x;
86 | }
87 | if (site.x > xmax)
88 | {
89 | xmax = site.x;
90 | }
91 | }
92 | // here's where we assume that the sites have been sorted on y:
93 | ymin = _sites[0].y;
94 | ymax = _sites[_sites.length - 1].y;
95 |
96 | return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
97 | }
98 |
99 | public function siteColors():Array
100 | {
101 | var colors = new Array();
102 | for (site in _sites)
103 | {
104 | colors.push(site.color);
105 | }
106 | return colors;
107 | }
108 |
109 | public function siteCoords():Array
110 | {
111 | var coords:Array = new Array();
112 | for (site in _sites)
113 | {
114 | coords.push(site.coord);
115 | }
116 | return coords;
117 | }
118 |
119 | public function sites():Array {
120 | return _sites;
121 | }
122 |
123 | /**
124 | *
125 | * @return the largest circle centered at each site that fits in its region;
126 | * if the region is infinite, return a circle of radius 0.
127 | *
128 | */
129 | public function circles():Array
130 | {
131 | var circles = new Array();
132 | for (site in _sites) {
133 | var nearestEdge = site.nearestEdge();
134 |
135 | var radius = (!nearestEdge.isPartOfConvexHull())? (nearestEdge.sitesDistance() * 0.5): 0;
136 | circles.push(new Circle(site.x, site.y, radius));
137 | }
138 | return circles;
139 | }
140 |
141 | public function regions(plotBounds:Rectangle):Array> {
142 | var regions = new Array>();
143 | for (site in _sites)
144 | {
145 | regions.push(site.region(plotBounds));
146 | }
147 | return regions;
148 | }
149 |
150 | /**
151 | *
152 | * @param x
153 | * @param y
154 | * @return coordinates of nearest Site to (x, y)
155 | *
156 | */
157 | public function nearestSitePoint(x:Int, y:Int):Point
158 | {
159 | var res = null;
160 | var p = new Point(x, y);
161 | var minDistSqr = Math.POSITIVE_INFINITY;
162 |
163 | for (site in _sites) {
164 | var q = site.coord;
165 | var distSqr = Point.distanceSquared(p, q);
166 | if (distSqr < minDistSqr) {
167 | minDistSqr = distSqr;
168 | res = site;
169 | }
170 | }
171 |
172 | return res != null ? res.coord : null;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/Triangle.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 |
5 | using com.nodename.delaunay.ArrayHelper;
6 |
7 |
8 | class Triangle {
9 | public var sites(get, null) : Array;
10 | inline private function get_sites():Array {
11 | return sites;
12 | }
13 |
14 | public var points(get, null) : Array;
15 | inline private function get_points():Array {
16 | return points;
17 | }
18 |
19 | public function new(a:Site, b:Site, c:Site) {
20 | sites = [a, b, c];
21 | points = [a.coord, b.coord, c.coord];
22 | }
23 |
24 | public function dispose():Void {
25 | sites.clear();
26 | sites = null;
27 | points.clear();
28 | points = null;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/Vertex.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.delaunay;
2 |
3 | import com.nodename.geom.Point;
4 |
5 |
6 | class Vertex implements ICoord
7 | {
8 | public static var VERTEX_AT_INFINITY:Vertex = new Vertex(Math.NaN, Math.NaN);
9 |
10 | private static var _pool:Array = new Array();
11 | private static function create(x:Float, y:Float):Vertex
12 | {
13 | if (Math.isNaN(x) || Math.isNaN(y))
14 | {
15 | return VERTEX_AT_INFINITY;
16 | }
17 | if (_pool.length > 0)
18 | {
19 | return _pool.pop().init(x, y);
20 | }
21 | else
22 | {
23 | return new Vertex(x, y);
24 | }
25 | }
26 |
27 |
28 | private static var _nvertices:Int = 0;
29 |
30 | public var coord(get, null):Point;
31 | inline private function get_coord():Point {
32 | return coord;
33 | }
34 |
35 | public var vertexIndex(get, null):Int;
36 | inline private function get_vertexIndex():Int
37 | {
38 | return vertexIndex;
39 | }
40 |
41 | // Should be private
42 | public function new(x:Float, y:Float) {
43 | init(x, y);
44 | }
45 |
46 | private function init(x:Float, y:Float):Vertex {
47 | coord = new Point(x, y);
48 | return this;
49 | }
50 |
51 | public function dispose():Void {
52 | coord = null;
53 | _pool.push(this);
54 | }
55 |
56 | inline public function setIndex():Void {
57 | vertexIndex = _nvertices++;
58 | }
59 |
60 | public function toString():String
61 | {
62 | return "Vertex (" + vertexIndex + ")";
63 | }
64 |
65 | /**
66 | * This is the only way to make a Vertex
67 | *
68 | * @param halfedge0
69 | * @param halfedge1
70 | * @return
71 | *
72 | */
73 | public static function intersect(halfedge0:Halfedge, halfedge1:Halfedge):Vertex {
74 | var halfedge:Halfedge;
75 | var determinant:Float;
76 | var intersectionX:Float;
77 | var intersectionY:Float;
78 | var rightOfSite:Bool;
79 |
80 | var edge0 = halfedge0.edge;
81 | var edge1 = halfedge1.edge;
82 | if (edge0 == null || edge1 == null) {
83 | return null;
84 | }
85 | if (edge0.rightSite == edge1.rightSite) {
86 | return null;
87 | }
88 |
89 | determinant = edge0.a * edge1.b - edge0.b * edge1.a;
90 | if (-1.0e-10 < determinant && determinant < 1.0e-10)
91 | {
92 | // the edges are parallel
93 | return null;
94 | }
95 |
96 | var oneOverDet = 1 / determinant;
97 | intersectionX = (edge0.c * edge1.b - edge1.c * edge0.b)*oneOverDet;
98 | intersectionY = (edge1.c * edge0.a - edge0.c * edge1.a)*oneOverDet;
99 |
100 | var edge:Edge;
101 | if (Voronoi.isInfSite(edge0.rightSite, edge1.rightSite)) {
102 | halfedge = halfedge0; edge = edge0;
103 | } else {
104 | halfedge = halfedge1; edge = edge1;
105 | }
106 | rightOfSite = intersectionX >= edge.rightSite.x;
107 | if ((rightOfSite && halfedge.leftRight == LR.LEFT)
108 | || (!rightOfSite && halfedge.leftRight == LR.RIGHT))
109 | {
110 | return null;
111 | }
112 |
113 | return Vertex.create(intersectionX, intersectionY);
114 | }
115 | public var x(get, null):Float;
116 | public var y(get, null):Float;
117 | inline private function get_x():Float {
118 | return coord.x;
119 | }
120 | inline private function get_y():Float {
121 | return coord.y;
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/com/nodename/delaunay/Voronoi.hx:
--------------------------------------------------------------------------------
1 | /*
2 | * The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
3 | * Bell Laboratories.
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose without fee is hereby granted, provided that this entire notice
6 | * is included in all copies of any software which is or includes a copy
7 | * or modification of this software and in all copies of the supporting
8 | * documentation for such software.
9 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10 | * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
11 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12 | * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13 | */
14 |
15 |
16 | package com.nodename.delaunay;
17 |
18 | import com.nodename.delaunay.Kruskal.SortType;
19 | import com.nodename.geom.Circle;
20 | import com.nodename.geom.LineSegment;
21 | import com.nodename.geom.Point;
22 | import com.nodename.geom.Rectangle;
23 | import com.nodename.delaunay.SelectHelper;
24 |
25 | using com.nodename.delaunay.ArrayHelper;
26 |
27 | class Voronoi {
28 | private var _sites:SiteList;
29 | private var _sitesIndexedByLocation:Map;
30 | private var _triangles:Array;
31 | private var _edges:Array;
32 |
33 |
34 | // TODO generalize this so it doesn't have to be a rectangle;
35 | // then we can make the fractal voronois-within-voronois
36 | private var _plotBounds:Rectangle;
37 | public function getPlotBounds():Rectangle
38 | {
39 | return _plotBounds;
40 | }
41 |
42 | public function dispose(): Void {
43 | var i, n:Int;
44 | if (_sites != null)
45 | {
46 | _sites.dispose();
47 | _sites = null;
48 | }
49 | if (_triangles != null)
50 | {
51 | n = _triangles.length;
52 | for (i in 0...n)
53 | {
54 | _triangles[i].dispose();
55 | }
56 | _triangles.clear();
57 | _triangles = null;
58 | }
59 | if (_edges != null)
60 | {
61 | n = _edges.length;
62 | for (i in 0...n)
63 | {
64 | _edges[i].dispose();
65 | }
66 | _edges.clear();
67 | _edges = null;
68 | }
69 | _plotBounds = null;
70 | _sitesIndexedByLocation = null;
71 | }
72 |
73 | public function new(points:Array, colors:Array, plotBounds:Rectangle)
74 | {
75 | _sites = new SiteList();
76 | _sitesIndexedByLocation = new Map();
77 | addSites(points, colors);
78 | _plotBounds = plotBounds;
79 | _triangles = new Array();
80 | _edges = new Array();
81 | fortunesAlgorithm();
82 | }
83 |
84 | private function addSites(points:Array, colors:Array):Void
85 | {
86 | var length:Int = points.length;
87 | for (i in 0 ... length)
88 | {
89 | addSite(points[i], colors != null ? colors[i] : 0, i);
90 | }
91 | }
92 |
93 | private function addSite(p:Point, color:Int, index:Int):Void
94 | {
95 | var weight:Float = Math.random() * 100;
96 | var site:Site = Site.create(p, index, weight, color);
97 | _sites.push(site);
98 | _sitesIndexedByLocation[p] = site;
99 | }
100 |
101 | public function region(p:Point):Array
102 | {
103 | var site:Site = _sitesIndexedByLocation[p];
104 | if (site == null)
105 | {
106 | return new Array();
107 | }
108 | return site.region(_plotBounds);
109 | }
110 |
111 | public function neighborSitesForSite(coord:Point):Array
112 | {
113 | var points:Array = new Array();
114 | var site:Site = _sitesIndexedByLocation[coord];
115 | if (site == null)
116 | {
117 | return points;
118 | }
119 | var sites:Array = site.neighborSites();
120 | var neighbor:Site;
121 | for (neighbor in sites)
122 | {
123 | points.push(neighbor.coord);
124 | }
125 | return points;
126 | }
127 |
128 | public function circles():Array
129 | {
130 | return _sites.circles();
131 | }
132 |
133 | public function edges() : Array {
134 | return _edges;
135 | }
136 |
137 | public function sites() : SiteList {
138 | return _sites;
139 | }
140 |
141 | public function voronoiBoundaryForSite(coord:Point):Array
142 | {
143 | return SelectHelper.visibleLineSegments(SelectHelper.selectEdgesForSitePoint(coord, _edges));
144 | }
145 |
146 | public function delaunayLinesForSite(coord:Point):Array
147 | {
148 | return SelectHelper.delaunayLinesForEdges(SelectHelper.selectEdgesForSitePoint(coord, _edges));
149 | }
150 |
151 | public function voronoiDiagram():Array
152 | {
153 | return SelectHelper.visibleLineSegments(_edges);
154 | }
155 |
156 | public function delaunayTriangulation():Array
157 | {
158 | return SelectHelper.delaunayLinesForEdges(_edges);
159 | }
160 |
161 | public function hull():Array
162 | {
163 | return SelectHelper.delaunayLinesForEdges(hullEdges());
164 | }
165 |
166 | inline static function myTest(edge:Edge):Bool
167 | {
168 | return edge.isPartOfConvexHull();
169 | }
170 |
171 | private function hullEdges():Array {
172 | return _edges.filter(myTest);
173 | }
174 |
175 | public function hullPointsInOrder():Array
176 | {
177 | var hullEdges:Array = hullEdges();
178 |
179 | var points:Array = new Array();
180 | if (hullEdges.length == 0)
181 | {
182 | return points;
183 | }
184 |
185 | var reorderer:EdgeReorderer = new EdgeReorderer(hullEdges, EdgeReorderer.edgeToLeftSite, EdgeReorderer.edgeToRightSite);
186 | hullEdges = reorderer.edges;
187 | var orientations:Array = reorderer.edgeOrientations;
188 | reorderer.dispose();
189 |
190 | var orientation:LR;
191 |
192 | var n:Int = hullEdges.length;
193 | for (i in 0...n)
194 | {
195 | var edge:Edge = hullEdges[i];
196 | orientation = orientations[i];
197 | points.push(edge.site(orientation).coord);
198 | }
199 | return points;
200 | }
201 |
202 | public function spanningTree(type:SortType = null):Array {
203 | var segments = SelectHelper.delaunayLinesForEdges(_edges);
204 | return Kruskal.kruskal(segments, type);
205 | }
206 |
207 | public function regions():Array>
208 | {
209 | return _sites.regions(_plotBounds);
210 | }
211 |
212 | public function siteColors():Array
213 | {
214 | return _sites.siteColors();
215 | }
216 |
217 | public function triangles():Array{
218 | return _triangles;
219 | }
220 |
221 | /**
222 | *
223 | * @param x
224 | * @param y
225 | * @return coordinates of nearest Site to (x, y)
226 | *
227 | */
228 | public function nearestSitePoint(x:Int, y:Int):Point
229 | {
230 | return _sites.nearestSitePoint(x, y);
231 | }
232 |
233 | public function siteCoords():Array {
234 | return _sites.siteCoords();
235 | }
236 |
237 | private function fortunesAlgorithm():Void {
238 |
239 | var newSite:Site;
240 | var newintstar:Point = null;
241 |
242 | var dataBounds = _sites.getSitesBounds();
243 |
244 | var sqrt_nsites = Std.int(Math.sqrt(_sites.length + 4));
245 | var heap = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrt_nsites);
246 | var edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrt_nsites);
247 | var halfEdges = new Array();
248 | var vertices = new Array();
249 |
250 | var bottomMostSite:Site = _sites.next();
251 |
252 | var leftRegion = function(he:Halfedge):Site {
253 | var edge:Edge = he.edge;
254 | if (edge == null)
255 | {
256 | return bottomMostSite;
257 | }
258 | return edge.site(he.leftRight);
259 | }
260 |
261 | var rightRegion = function(he:Halfedge):Site {
262 | var edge:Edge = he.edge;
263 | if (edge == null)
264 | {
265 | return bottomMostSite;
266 | }
267 | return edge.site(LR.other(he.leftRight));
268 | }
269 |
270 | newSite = _sites.next();
271 |
272 | while (true) {
273 |
274 | if (heap.empty() == false) {
275 | newintstar = heap.min();
276 | }
277 |
278 | if (newSite != null && (heap.empty() || isInf(newSite, newintstar))) {
279 |
280 | /* new site is smallest */
281 | //trace("smallest: new site " + newSite);
282 | // trace("edgeList" + edgeList);
283 | // Step 8:
284 | var lbnd = edgeList.edgeListLeftNeighbor(newSite.coord); // the Halfedge just to the left of newSite
285 | var rbnd = lbnd.edgeListRightNeighbor; // the Halfedge just to the right
286 | var bottomSite = rightRegion(lbnd); // this is the same as leftRegion(rbnd)
287 | // this Site determines the region containing the new site
288 | //trace("new Site is in region of existing site: " + bottomSite);
289 |
290 | // Step 9:
291 | var edge = Edge.createBisectingEdge(bottomSite, newSite);
292 | //trace("new edge: " + edge);
293 | _edges.push(edge);
294 |
295 | var bisector = Halfedge.create(edge, LR.LEFT);
296 | halfEdges.push(bisector);
297 | // inserting two Halfedges into edgeList constitutes Step 10:
298 | // insert bisector to the right of lbnd:
299 | edgeList.insert(lbnd, bisector);
300 |
301 | // first half of Step 11:
302 | var vertex = Vertex.intersect(lbnd, bisector);
303 | if (vertex != null) {
304 | heap.remove(lbnd);
305 |
306 | vertices.push(vertex);
307 | lbnd.vertex = vertex;
308 | lbnd.ystar = vertex.y + newSite.dist(vertex);
309 | heap.insert(lbnd);
310 | }
311 |
312 | lbnd = bisector;
313 | bisector = Halfedge.create(edge, LR.RIGHT);
314 | halfEdges.push(bisector);
315 | // second Halfedge for Step 10:
316 | // insert bisector to the right of lbnd:
317 | edgeList.insert(lbnd, bisector);
318 |
319 | // second half of Step 11:
320 | var vertex = Vertex.intersect(bisector, rbnd);
321 | if (vertex != null) {
322 | vertices.push(vertex);
323 | bisector.vertex = vertex;
324 | bisector.ystar = vertex.y + newSite.dist(vertex);
325 | heap.insert(bisector);
326 | }
327 |
328 | newSite = _sites.next();
329 | } else if (heap.empty() == false) {
330 |
331 | /* intersection is smallest */
332 | var lbnd = heap.extractMin();
333 | var llbnd = lbnd.edgeListLeftNeighbor;
334 | var rbnd = lbnd.edgeListRightNeighbor;
335 | var rrbnd = rbnd.edgeListRightNeighbor;
336 | var bottomSite = leftRegion(lbnd);
337 | var topSite = rightRegion(rbnd);
338 | // these three sites define a Delaunay triangle
339 | // (not actually using these for anything...)
340 | _triangles.push(new Triangle(bottomSite, topSite, rightRegion(lbnd)));
341 |
342 | var v = lbnd.vertex;
343 | v.setIndex();
344 | lbnd.edge.setVertex(lbnd.leftRight, v);
345 | rbnd.edge.setVertex(rbnd.leftRight, v);
346 | edgeList.remove(lbnd);
347 | heap.remove(rbnd);
348 | edgeList.remove(rbnd);
349 |
350 | var leftRight = LR.LEFT;
351 | if (bottomSite.y > topSite.y) {
352 | var tempSite = bottomSite;
353 | bottomSite = topSite;
354 | topSite = tempSite;
355 | leftRight = LR.RIGHT;
356 | }
357 |
358 | var edge = Edge.createBisectingEdge(bottomSite, topSite);
359 | _edges.push(edge);
360 | var bisector = Halfedge.create(edge, leftRight);
361 | halfEdges.push(bisector);
362 | edgeList.insert(llbnd, bisector);
363 | edge.setVertex(LR.other(leftRight), v);
364 | var vertex = Vertex.intersect(llbnd, bisector);
365 | if (vertex != null) {
366 | heap.remove(llbnd);
367 |
368 | vertices.push(vertex);
369 | llbnd.vertex = vertex;
370 | llbnd.ystar = vertex.y + bottomSite.dist(vertex);
371 | heap.insert(llbnd);
372 | }
373 | vertex = Vertex.intersect(bisector, rrbnd);
374 | if (vertex != null) {
375 |
376 | vertices.push(vertex);
377 | bisector.vertex = vertex;
378 | bisector.ystar = vertex.y + bottomSite.dist(vertex);
379 | heap.insert(bisector);
380 | }
381 | } else {
382 | break;
383 | }
384 | }
385 |
386 | // heap should be empty now
387 | heap.dispose();
388 | edgeList.dispose();
389 |
390 | for (halfEdge in halfEdges) {
391 | halfEdge.reallyDispose();
392 | }
393 | halfEdges.clear();
394 |
395 | // we need the vertices to clip the edges
396 | for (edge in _edges) {
397 | edge.clipVertices(_plotBounds);
398 | }
399 | // but we don't actually ever use them again!
400 | for (vertex in vertices) {
401 | vertex.dispose();
402 | }
403 | vertices.clear();
404 | }
405 |
406 |
407 | inline static public function isInf(s1:Site, s2:Point) : Bool {
408 | return (s1.y < s2.y) || (s1.y == s2.y && s1.x < s2.x);
409 | }
410 |
411 | inline static public function isInfSite(s1:Site, s2:Site) : Bool {
412 | return (s1.y < s2.y) || (s1.y == s2.y && s1.x < s2.x);
413 | }
414 |
415 | public static function compareByYThenX(s1:Site, s2:Site):Int {
416 | /*
417 | return
418 | (s1.y < s2.y)? -1 :(
419 | (s1.y > s2.y)? 1:(
420 | (s1.x < s2.x)? -1:(
421 | (s1.x > s2.x)? 1: 0)));
422 | */
423 | if (s1.y < s2.y) return -1;
424 | if (s1.y > s2.y) return 1;
425 | if (s1.x < s2.x) return -1;
426 | if (s1.x > s2.x) return 1;
427 | return 0;
428 | }
429 |
430 | }
431 |
--------------------------------------------------------------------------------
/com/nodename/geom/Circle.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 |
4 | class Circle {
5 | public var center:Point;
6 | public var radius:Float;
7 |
8 | public function new(centerX:Float, centerY:Float, radius:Float)
9 | {
10 | this.center = new Point(centerX, centerY);
11 | this.radius = radius;
12 | }
13 |
14 | public function toString():String
15 | {
16 | return "Circle (center: " + center + "; radius: " + radius + ")";
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/com/nodename/geom/LineSegment.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 | import com.nodename.geom.Point;
4 |
5 | class LineSegment {
6 | public static function compareLengths_MAX(segment0:LineSegment, segment1:LineSegment):Int {
7 | var length0 = Point.distanceSquared(segment0.p0, segment0.p1);
8 | var length1 = Point.distanceSquared(segment1.p0, segment1.p1);
9 | if (length0 < length1) {
10 | return 1;
11 | } else if (length0 > length1) {
12 | return -1;
13 | } else {
14 | return 0;
15 | }
16 | }
17 |
18 | public static function compareLengths(edge0:LineSegment, edge1:LineSegment):Int {
19 | return - compareLengths_MAX(edge0, edge1);
20 | }
21 |
22 | public var p0:Point;
23 | public var p1:Point;
24 |
25 | public function new(p0:Point, p1:Point) {
26 | this.p0 = p0;
27 | this.p1 = p1;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/com/nodename/geom/Point.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 |
4 | class Point {
5 |
6 | public var x:Float;
7 | public var y:Float;
8 |
9 | public function new(x:Float = 0, y:Float = 0) {
10 | this.x = x;
11 | this.y = y;
12 | }
13 |
14 | public function setTo(x:Float, y:Float):Point {
15 | this.x = x;
16 | this.y = y;
17 | return this;
18 | }
19 |
20 | public function normalize(length:Float):Point {
21 | var denom = Math.sqrt(x * x + y * y);
22 | if (denom != 0) {
23 | var f = length / denom;
24 | x *= f;
25 | y *= f;
26 | }
27 | return this;
28 | }
29 |
30 | public function clone():Point {
31 | return new Point(x, y);
32 | }
33 |
34 | public function toString():String {
35 | return '(${x}, ${y})';
36 | }
37 |
38 | static public function distance(p:Point, q:Point):Float {
39 | return Math.sqrt(distanceSquared(p, q));
40 | }
41 |
42 | static public function distanceSquared(p:Point, q:Point):Float {
43 | var dx = p.x - q.x;
44 | var dy = p.y - q.y;
45 | return dx * dx + dy * dy;
46 | }
47 | }
--------------------------------------------------------------------------------
/com/nodename/geom/Polygon.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 |
4 | class Polygon {
5 | private var _vertices:Array;
6 |
7 | public function new (vertices:Array) {
8 | _vertices = vertices;
9 | }
10 |
11 | public function area():Float {
12 | return Math.abs(signedDoubleArea() * 0.5);
13 | }
14 |
15 | public function winding():Winding {
16 | var signedDoubleArea = signedDoubleArea();
17 | if (signedDoubleArea < 0) {
18 | return Winding.CLOCKWISE;
19 | } else if (signedDoubleArea > 0) {
20 | return Winding.COUNTERCLOCKWISE;
21 | } else {
22 | return Winding.NONE;
23 | }
24 | }
25 |
26 | private function signedDoubleArea():Float
27 | {
28 | var index:Int, nextIndex:Int;
29 | var n:Int = _vertices.length;
30 | var point:Point, next:Point;
31 | var signedDoubleArea = 0.0;
32 | for (index in 0...n)
33 | {
34 | nextIndex = (index + 1) % n;
35 | point = _vertices[index];
36 | next = _vertices[nextIndex];
37 | signedDoubleArea += point.x * next.y - next.x * point.y;
38 | }
39 | return signedDoubleArea;
40 | }
41 | }
--------------------------------------------------------------------------------
/com/nodename/geom/Rectangle.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 |
4 | class Rectangle {
5 |
6 | public var x:Float;
7 | public var y:Float;
8 | public var width:Float;
9 | public var height:Float;
10 |
11 | public function new(x:Float = 0, y:Float = 0, w:Float = 0, h:Float = 0) {
12 | this.x = x;
13 | this.y = y;
14 | this.width = w;
15 | this.height = h;
16 | }
17 |
18 | public var left(get, never):Float;
19 | inline private function get_left():Float {
20 | return x;
21 | }
22 |
23 | public var right(get, never):Float;
24 | inline private function get_right():Float {
25 | return x + width;
26 | }
27 |
28 | public var top(get, never):Float;
29 | inline private function get_top():Float {
30 | return y;
31 | }
32 |
33 | public var bottom(get, never):Float;
34 | inline private function get_bottom():Float {
35 | return y + height;
36 | }
37 |
38 | public function setTo(x:Float, y:Float, w:Float, h:Float):Rectangle {
39 | this.x = x;
40 | this.y = y;
41 | this.width = w;
42 | this.height = h;
43 | return this;
44 | }
45 |
46 | public function clone():Rectangle {
47 | return new Rectangle(x, y, width, height);
48 | }
49 |
50 | public function toString():String {
51 | return '(${x}, ${y}, ${width}x${height})';
52 | }
53 | }
--------------------------------------------------------------------------------
/com/nodename/geom/Winding.hx:
--------------------------------------------------------------------------------
1 | package com.nodename.geom;
2 |
3 | enum Winding {
4 | CLOCKWISE;
5 | COUNTERCLOCKWISE;
6 | NONE;
7 | }
--------------------------------------------------------------------------------
/hxDelaunayTest.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | "$(CompilerPath)/haxelib" run lime build "$(OutputFile)" $(TargetBuild) -$(BuildConfig) -Dfdb
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/screenshots/delaunay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/delaunay.png
--------------------------------------------------------------------------------
/screenshots/girl-pearl-earring-320.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/girl-pearl-earring-320.png
--------------------------------------------------------------------------------
/screenshots/girl-pearl-earring-voronoi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/girl-pearl-earring-voronoi.png
--------------------------------------------------------------------------------
/screenshots/lena-320.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/lena-320.png
--------------------------------------------------------------------------------
/screenshots/lena-voronoi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/lena-voronoi.png
--------------------------------------------------------------------------------
/screenshots/mona-lisa-320.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/mona-lisa-320.png
--------------------------------------------------------------------------------
/screenshots/mona-lisa-hollow-voronoi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/mona-lisa-hollow-voronoi.png
--------------------------------------------------------------------------------
/screenshots/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/screenshot.png
--------------------------------------------------------------------------------
/screenshots/starry-night-320.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/starry-night-320.png
--------------------------------------------------------------------------------
/screenshots/starry-night-voronoi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azrafe7/hxDelaunay/75a8a0d774196b3b0dfac6e2274e155a30060dce/screenshots/starry-night-voronoi.png
--------------------------------------------------------------------------------
/src/Demo.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 |
4 | import com.nodename.delaunay.Voronoi;
5 | import com.nodename.geom.LineSegment;
6 | import com.nodename.geom.Point;
7 | import com.nodename.geom.Rectangle;
8 | import flash.display.Bitmap;
9 | import flash.display.BitmapData;
10 | import flash.display.Graphics;
11 | import flash.display.Sprite;
12 | import flash.events.Event;
13 | import flash.events.KeyboardEvent;
14 | import flash.events.MouseEvent;
15 | import flash.filters.GlowFilter;
16 | import flash.system.System;
17 | import flash.text.TextField;
18 | import flash.text.TextFieldAutoSize;
19 | import flash.text.TextFormat;
20 | import flash.text.TextFormatAlign;
21 | import flash.ui.Keyboard;
22 | import haxe.Timer;
23 | import openfl.Assets;
24 |
25 |
26 | using StringTools;
27 |
28 |
29 | @:bitmap("assets/mona-lisa.png")
30 | class Picture extends flash.display.BitmapData {}
31 |
32 | @:bitmap("assets/mona-lisa-mini.png")
33 | class PictureMini extends flash.display.BitmapData {}
34 |
35 | /**
36 | * hxDelaunay openFL demo.
37 | *
38 | * @author azrafe7
39 | */
40 | class Demo extends Sprite {
41 |
42 | private var g:Graphics;
43 |
44 | private var POINT_COLOR:Int = 0x00F000;
45 | private var REGION_COLOR:Int = 0x4000F0;
46 | private var MIN_FILL_COLOR:Int = 0x200040;
47 | private var MAX_FILL_COLOR:Int = 0x4000A0;
48 | private var TRIANGLE_COLOR:Int = 0xF00000;
49 | private var HULL_COLOR:Int = 0x30F090;
50 | private var TREE_COLOR:Int = 0xF0C020;
51 | private var ONION_COLOR:Int = 0x10A0FF;
52 | private var SELECTED_COLOR:Int = 0x8020F0;
53 | private var CENTROID_COLOR:Int = 0x111111;
54 | private var PICTURE:String = "assets/mona-lisa.png";
55 | private var PICTURE_MINI:String = "assets/mona-lisa-mini.png";
56 |
57 | private var THICKNESS:Float = 1.5;
58 | private var ALPHA:Float = 1.;
59 | private var FILL_ALPHA:Float = 1.;
60 | private var SAMPLE_FILL_ALPHA:Float = .8;
61 | private var CENTROID_ALPHA:Float = .5;
62 |
63 | private var TEXT_COLOR:Int = 0xFFFFFF;
64 | private var TEXT_FONT:String = "_typewriter";
65 | private var TEXT_SIZE:Int = 12;
66 | private var TEXT_OUTLINE:GlowFilter = new GlowFilter(0xFF000000, 1, 2, 2, 6);
67 |
68 | private var BOUNDS:Rectangle = new Rectangle(0, 0, 500, 500);
69 |
70 | private var voronoi:Voronoi;
71 | private var nPoints:Int = 25;
72 | private var points:Array;
73 | private var centroids:Array;
74 | private var directions:Array;
75 | private var regions:Array>;
76 | private var sortedRegions:Array>;
77 | private var fillColors:Array;
78 | private var triangles:Array;
79 | private var hull:Array;
80 | private var tree:Array;
81 | private var onion:Array>;
82 | private var proxymitySprite:Sprite;
83 | private var proxymityMap:BitmapData;
84 | private var selectedRegion:Array;
85 | private var pictureBMD:BitmapData;
86 | private var pictureBitmap:Bitmap;
87 | private var pictureMiniBMD:BitmapData; // downscaled by a factor of 4
88 |
89 | private var isMouseDown:Bool = false;
90 | private var prevMousePos:Point = new Point();
91 | private var mousePos:Point = new Point();
92 |
93 | private var showPoints:Bool = true;
94 | private var showRegions:Bool = true;
95 | private var fillRegions:Bool = false;
96 | private var showTriangles:Bool = false;
97 | private var fillTriangles:Bool = false;
98 | private var showHull:Bool = false;
99 | private var showTree:Bool = false;
100 | private var showOnion:Bool = false;
101 | private var showProximityMap:Bool = false;
102 |
103 | private var relax:Bool = false;
104 | private var animate:Bool = false;
105 | private var sampleImage:Bool = false;
106 |
107 | private var startTime:Float = 0;
108 | private var dt:Float = 0;
109 |
110 | private var text:TextField;
111 |
112 | private var TEXT:String =
113 | " hxDelaunay \n" +
114 | " (ported by azrafe7)\n" +
115 | "\n" +
116 | " TOGGLE:\n\n" +
117 | " 1 points : |POINTS|\n" +
118 | " 2 regions : |REGIONS|\n" +
119 | " 3 fill regions : |FILL_REGIONS|\n" +
120 | " 4 triangles : |TRIANGLES|\n" +
121 | " 5 fill tris : |FILL_TRIANGLES|\n" +
122 | " 6 convex hull : |HULL|\n" +
123 | " 7 spanning tree : |TREE|\n" +
124 | " 8 onion : |ONION|\n" +
125 | " 9 proximity map : |PROXIMITY|\n" +
126 | "\n" +
127 | " X relax : |RELAX|\n" +
128 | " A animate : |ANIMATE|\n" +
129 | " P picture : |PICTURE|\n" +
130 | "\n" +
131 | " POINTS: (|NPOINTS|)\n\n" +
132 | " [SHIFT] + \n" +
133 | " ▲ add point/s\n" +
134 | " ▼ remove point/s\n" +
135 | "\n" +
136 | " R randomize\n" +
137 | #if (openfl && !nme)
138 | "\n" +
139 | " S save png\n" +
140 | #end
141 | "\n" +
142 | " click & drag to\n" +
143 | " move region point" +
144 | "\n";
145 |
146 | var sprite:Sprite;
147 |
148 | public function new () {
149 | super ();
150 |
151 | sprite = new Sprite();
152 | addChild(sprite);
153 | g = sprite.graphics;
154 | g.lineStyle(THICKNESS, TEXT_COLOR, ALPHA);
155 |
156 | addChild(text = getTextField(TEXT, BOUNDS.width + 10, 15));
157 | text.height = stage.stageHeight - text.y; // makes sure it's _fully_ visible
158 |
159 | // picture
160 | #if (!jsprime)
161 | pictureBMD = new Picture(0, 0);
162 | pictureMiniBMD = new PictureMini(0, 0);
163 | #else
164 | pictureBMD = Assets.getBitmapData(PICTURE);
165 | pictureMiniBMD = Assets.getBitmapData(PICTURE_MINI);
166 | #end
167 | pictureBitmap = new Bitmap(pictureBMD);
168 | addChildAt(pictureBitmap, 0);
169 |
170 | // generate fill colors
171 | var MAX_COLORS = 10;
172 | fillColors = [for (i in 0...MAX_COLORS) colorLerp(MIN_FILL_COLOR, MAX_FILL_COLOR, i / MAX_COLORS)];
173 | centroids = new Array();
174 | directions = new Array();
175 |
176 | // first random set of points
177 | points = new Array();
178 | for (i in 0...nPoints) {
179 | points.push(new Point(Math.random() * BOUNDS.width, Math.random() * BOUNDS.height));
180 | }
181 |
182 | update(); // recalc
183 | render(); // draw
184 | updateText(); // info
185 |
186 | startTime = Timer.stamp();
187 | //stage.addChild(new FPS(5, 5, 0xFFFFFF));
188 | stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
189 | stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
190 | stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
191 | stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
192 | stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
193 | }
194 |
195 | public function update():Void
196 | {
197 | if (voronoi != null) {
198 | voronoi.dispose();
199 | voronoi = null;
200 | }
201 | voronoi = new Voronoi(points, null, BOUNDS);
202 | regions = [for (p in points) voronoi.region(p)];
203 | sortedRegions = voronoi.regions();
204 | triangles = voronoi.delaunayTriangulation();
205 | hull = voronoi.hull();
206 | tree = voronoi.spanningTree();
207 | onion = calcOnion(voronoi);
208 | updateProximityMap();
209 | }
210 |
211 | public function updateText():Void
212 | {
213 | text.text = TEXT
214 | .replace("|POINTS|", bool2OnOff(showPoints))
215 | .replace("|REGIONS|", bool2OnOff(showRegions))
216 | .replace("|FILL_REGIONS|", bool2OnOff(fillRegions))
217 | .replace("|TRIANGLES|", bool2OnOff(showTriangles))
218 | .replace("|FILL_TRIANGLES|", bool2OnOff(fillTriangles))
219 | .replace("|HULL|", bool2OnOff(showHull))
220 | .replace("|TREE|", bool2OnOff(showTree))
221 | .replace("|ONION|", bool2OnOff(showOnion))
222 | .replace("|PROXIMITY|", bool2OnOff(showProximityMap))
223 | .replace("|NPOINTS|", Std.string(nPoints))
224 | .replace("|RELAX|", bool2OnOff(relax))
225 | .replace("|ANIMATE|", bool2OnOff(animate))
226 | .replace("|PICTURE|", bool2OnOff(sampleImage));
227 | }
228 |
229 | public function render():Void {
230 | g.clear();
231 |
232 | if (showRegions || fillRegions) drawRegions();
233 | pictureBitmap.visible = (sampleImage && (!fillRegions || SAMPLE_FILL_ALPHA < FILL_ALPHA));
234 | if (showProximityMap) {
235 | g.beginBitmapFill(proxymityMap);
236 | g.drawRect(0, 0, BOUNDS.width, BOUNDS.height);
237 | g.endFill();
238 | }
239 | if (showTriangles || fillTriangles) drawTriangles();
240 | if (showTree) drawTree();
241 | if (showOnion) drawOnion();
242 | if (showHull) drawHull();
243 | if (showPoints) drawSiteCoords();
244 | if (selectedRegion != null) drawPoints(selectedRegion, SELECTED_COLOR);
245 | if (relax && showPoints) drawCentroids();
246 | }
247 |
248 | inline function bool2OnOff(v:Bool):String
249 | {
250 | return (v ? "[ON]" : "[OFF]");
251 | }
252 |
253 | public function updateProximityMap():Void {
254 | if (proxymityMap == null) {
255 | proxymityMap = new BitmapData(Std.int(BOUNDS.width), Std.int(BOUNDS.height), false);
256 | proxymitySprite = new Sprite();
257 | }
258 | var graphics = proxymitySprite.graphics;
259 | proxymityMap.fillRect(proxymityMap.rect, 0xFFFFFFFF);
260 | graphics.clear();
261 |
262 | for (i in 0...sortedRegions.length) {
263 | graphics.lineStyle(1, i, 1); // no borders
264 | graphics.beginFill(i);
265 | var points = sortedRegions[i];
266 | for (p in points) {
267 | if (p == points[0]) graphics.moveTo(p.x, p.y);
268 | else graphics.lineTo(p.x, p.y);
269 | }
270 | graphics.endFill();
271 | }
272 | proxymityMap.draw(proxymitySprite);
273 | }
274 |
275 | public function calcOnion(voronoi:Voronoi):Array>
276 | {
277 | var res = new Array>();
278 | var points = voronoi.siteCoords();
279 |
280 | while (points.length > 2) {
281 | var v:Voronoi = new Voronoi(points, null, BOUNDS);
282 | var peel = v.hullPointsInOrder();
283 | for (p in peel) points.remove(p);
284 | res.push(peel);
285 | v.dispose();
286 | v = null;
287 | }
288 | if (points.length > 0) res.push(points);
289 |
290 | return res;
291 | }
292 |
293 |
294 |
295 | public function drawSiteCoords():Void
296 | {
297 | g.lineStyle(THICKNESS, POINT_COLOR, ALPHA);
298 | for (p in points) {
299 | g.drawCircle(p.x, p.y, 2);
300 | }
301 | }
302 |
303 | public function drawCentroids():Void
304 | {
305 | if (centroids.length < points.length) return; // wait next frame
306 | for (i in 0...points.length) {
307 | var c = centroids[i];
308 | c.x = Math.round(c.x); c.y = Math.round(c.y);
309 | g.lineStyle(THICKNESS, CENTROID_COLOR, CENTROID_ALPHA);
310 | g.moveTo(c.x - 2, c.y); g.lineTo(c.x + 2, c.y);
311 | g.moveTo(c.x, c.y - 2); g.lineTo(c.x, c.y + 2);
312 | }
313 | }
314 |
315 | public function drawRegions():Void
316 | {
317 | if (!sampleImage) {
318 | var fillIdx = -1;
319 | for (region in regions) {
320 | fillIdx = (fillIdx + 1) % fillColors.length;
321 | var fillColor = fillRegions ? fillColors[fillIdx] : null;
322 |
323 | drawPoints(region, fillRegions && !showRegions ? fillColors[fillIdx] : REGION_COLOR, fillColor);
324 | }
325 | } else {
326 | for (p in points) {
327 | //var sampledColor = pictureBMD.getPixel(Std.int(p.x), Std.int(p.y)); // use fullscale bitmap for sampling
328 | var sampledColor = pictureMiniBMD.getPixel(Std.int(p.x / 4), Std.int(p.y / 4)); // use downscaled bitmap for sampling
329 |
330 | drawPoints(voronoi.region(p), fillRegions && showRegions ? REGION_COLOR : sampledColor, fillRegions ? sampledColor: null);
331 | }
332 | }
333 | }
334 |
335 | inline public function drawTriangles():Void
336 | {
337 | if (!sampleImage) {
338 | var fillIdx = -1;
339 | for (tri in voronoi.triangles()) {
340 | fillIdx = (fillIdx + 1) % fillColors.length;
341 | var fillColor = fillTriangles ? fillColors[fillIdx] & 0xFF0000 : null;
342 |
343 | drawPoints(tri.points, fillTriangles && !showTriangles ? fillColors[fillIdx] & 0xFF0000 : TRIANGLE_COLOR, fillColor);
344 | }
345 | } else {
346 | for (tri in voronoi.triangles()) {
347 | var p = getCentroid(tri.points);
348 | var sampledColor = pictureBMD.getPixel(Std.int(p.x), Std.int(p.y)); // use fullscale bitmap for sampling
349 | //var sampledColor = pictureMiniBMD.getPixel(Std.int(p.x / 4), Std.int(p.y / 4)); // use downscaled bitmap for sampling
350 |
351 | drawPoints(tri.points, fillTriangles && showTriangles ? TRIANGLE_COLOR: sampledColor, fillTriangles ? sampledColor: null);
352 | }
353 | }
354 | }
355 |
356 | inline public function drawHull():Void
357 | {
358 | drawSegments(hull, HULL_COLOR);
359 | }
360 |
361 | inline public function drawTree():Void
362 | {
363 | drawSegments(tree, TREE_COLOR);
364 | }
365 |
366 | public function drawOnion():Void
367 | {
368 | for (peel in onion) {
369 | drawPoints(peel, ONION_COLOR);
370 | }
371 | }
372 |
373 |
374 |
375 | // generic draw function for segments
376 | public function drawSegments(segments:Array, color:Int, ?fillColor:Int = null) {
377 | g.lineStyle(THICKNESS, color, ALPHA);
378 | if (fillColor != null) g.beginFill(fillColor, FILL_ALPHA);
379 | else g.beginFill(0, 0);
380 |
381 | for (segment in segments) {
382 | g.moveTo(segment.p0.x, segment.p0.y);
383 | g.lineTo(segment.p1.x, segment.p1.y);
384 | }
385 |
386 | g.endFill();
387 | }
388 |
389 |
390 | // generic draw function for points
391 | public function drawPoints(points:Array, color:Int, ?fillColor:Int = null) {
392 | if (points == null || points.length == 0) return;
393 |
394 | g.lineStyle(THICKNESS, color, sampleImage ? SAMPLE_FILL_ALPHA : ALPHA);
395 | if (fillColor != null) g.beginFill(fillColor, sampleImage ? SAMPLE_FILL_ALPHA : FILL_ALPHA);
396 | else g.beginFill(0, 0);
397 |
398 | var q = points[0];
399 | g.moveTo(q.x, q.y);
400 | for (i in 1...points.length) {
401 | var p = points[i];
402 | g.lineTo(p.x, p.y);
403 | }
404 | g.lineTo(q.x, q.y);
405 |
406 | g.endFill();
407 | }
408 |
409 | public function getCentroid(region:Array):Point
410 | {
411 | var c = new Point();
412 | var len = region.length;
413 | for (i in 0...len) {
414 | var p0 = region[i];
415 | var p1 = region[(i + 1) % len];
416 | var m = p0.x * p1.y - p1.x * p0.y;
417 | c.x += (p0.x + p1.x) * m;
418 | c.y += (p0.y + p1.y) * m;
419 | }
420 | var area = getArea(region);
421 | c.x /= 6 * area;
422 | c.y /= 6 * area;
423 | return c;
424 | }
425 |
426 | public function getArea(region:Array):Float
427 | {
428 | var area = 0.0;
429 | var len = region.length;
430 | for (i in 0...len) {
431 | var p0 = region[i];
432 | var p1 = region[(i + 1) % len];
433 | area += p0.x * p1.y - p1.x * p0.y;
434 | }
435 | return area = .5 * area;
436 | }
437 |
438 | public function getTextField(text:String = "", x:Float, y:Float):TextField
439 | {
440 | var tf:TextField = new TextField();
441 | var fmt:TextFormat = new TextFormat(TEXT_FONT, null, TEXT_COLOR);
442 | fmt.align = TextFormatAlign.LEFT;
443 | fmt.size = TEXT_SIZE;
444 | tf.defaultTextFormat = fmt;
445 | tf.autoSize = TextFieldAutoSize.LEFT;
446 | tf.selectable = false;
447 | tf.x = x;
448 | tf.y = y;
449 | #if (flash || html5)
450 | tf.filters = [TEXT_OUTLINE];
451 | #end
452 | tf.text = text;
453 | return tf;
454 | }
455 |
456 | public function onKeyDown(e:KeyboardEvent):Void
457 | {
458 | var deltaPoints = e.shiftKey ? 20 : 1;
459 |
460 | switch (e.keyCode)
461 | {
462 | // TOGGLE
463 | case Keyboard.NUMBER_1: showPoints = !showPoints;
464 | case Keyboard.NUMBER_2: showRegions = !showRegions;
465 | case Keyboard.NUMBER_3: fillRegions = !fillRegions;
466 | case Keyboard.NUMBER_4: showTriangles = !showTriangles;
467 | case Keyboard.NUMBER_5: fillTriangles = !fillTriangles;
468 | case Keyboard.NUMBER_6: showHull = !showHull;
469 | case Keyboard.NUMBER_7: showTree = !showTree;
470 | case Keyboard.NUMBER_8: showOnion = !showOnion;
471 | case Keyboard.NUMBER_9: showProximityMap = !showProximityMap;
472 |
473 | // POINTS
474 | case Keyboard.UP:
475 | for (i in 0...deltaPoints) points.push(new Point(Math.random() * BOUNDS.width, Math.random() * BOUNDS.height));
476 | nPoints = points.length;
477 | update();
478 | case Keyboard.DOWN:
479 | while (deltaPoints-- > 0 && nPoints > 3) {
480 | points.pop();
481 | nPoints = points.length;
482 | }
483 | update();
484 | case Keyboard.R:
485 | for (p in points) p.setTo(Math.random() * BOUNDS.width, Math.random() * BOUNDS.height);
486 | update();
487 | case Keyboard.X:
488 | relax = !relax;
489 | animate = false;
490 | case Keyboard.A:
491 | animate = !animate;
492 | relax = false;
493 | case Keyboard.P: sampleImage = !sampleImage;
494 | case Keyboard.S:
495 | #if (openfl && !nme)
496 | update();
497 | var oldSampleFillAlpha = SAMPLE_FILL_ALPHA;
498 | SAMPLE_FILL_ALPHA = sampleImage ? .97 : oldSampleFillAlpha;
499 |
500 | render();
501 |
502 | SAMPLE_FILL_ALPHA = oldSampleFillAlpha;
503 | var tempBMD = new BitmapData(pictureBMD.width, pictureBMD.height, false, 0);
504 | tempBMD.draw(sprite);
505 |
506 | //var tempBMP = new Bitmap(tempBMD);
507 | //addChild(tempBMP);
508 | //sprite.visible = false;
509 |
510 | savePNG(tempBMD, "voronoi.png");
511 | #end
512 | }
513 |
514 | updateText();
515 | render();
516 |
517 | if (e.keyCode == 27) {
518 | #if (flash || html5)
519 | System.exit(1);
520 | #else
521 | Sys.exit(1);
522 | #end
523 | }
524 | }
525 |
526 | public function savePNG(bmd:BitmapData, filename:String)
527 | {
528 | #if (openfl && !nme)
529 | var ba:openfl.utils.ByteArray = bmd.encode(bmd.rect, new openfl.display.PNGEncoderOptions());
530 | var fileRef = new openfl.net.FileReference();
531 | fileRef.save(ba, filename);
532 | #end
533 | }
534 |
535 | public function onMouseDown(e:MouseEvent):Void
536 | {
537 | isMouseDown = true;
538 | }
539 |
540 | public function onMouseUp(e:MouseEvent):Void
541 | {
542 | isMouseDown = false;
543 | selectedRegion = null;
544 | render();
545 | }
546 |
547 | public function onMouseMove(e:MouseEvent):Void
548 | {
549 | mousePos.setTo(e.stageX, e.stageY);
550 | }
551 |
552 | public function onEnterFrame(e:Event):Void {
553 | dt = Timer.stamp() - startTime;
554 | startTime = Timer.stamp();
555 |
556 | var mousePosChanged = !(mousePos.x == prevMousePos.x && mousePos.y == prevMousePos.y);
557 |
558 | if (relax) {
559 | for (i in 0...points.length) {
560 | var p = points[i];
561 | var r = voronoi.region(p);
562 | var c = getCentroid(r);
563 | (i == centroids.length) ? centroids.push(c) : centroids[i] = c;
564 |
565 | var changed = false;
566 | if (nPoints < 100) {
567 | var distSquared = Point.distanceSquared(c, p);
568 | if (distSquared > 4.5) { // slow down things a bit
569 | c.x -= p.x; c.y -= p.y;
570 | c.normalize(.75);
571 | p.x += c.x; p.y += c.y;
572 | changed = true;
573 | }
574 | }
575 | if (!changed) {
576 | p.x = c.x; p.y = c.y;
577 | }
578 | }
579 | }
580 |
581 | if (animate) {
582 | for (i in 0...points.length) {
583 | if (i == directions.length) {
584 | directions.push(new Point(30 * (Math.random() < .5 ? -1 : 1) * (Math.random() * .8 + .4), 30 * (Math.random() < .5 ? -1 : 1) * (Math.random() * .8 + .4)));
585 | }
586 | var p = points[i];
587 | var d = directions[i];
588 | var dx = d.x * dt;
589 | var dy = d.y * dt;
590 | if (p.x + dx < 0 || p.x + dx > BOUNDS.width) {
591 | d.x *= -1;
592 | dx *= -1;
593 | }
594 | if (p.y + dy < 0 || p.y + dy > BOUNDS.height) {
595 | d.y *= -1;
596 | dy *= -1;
597 | }
598 | p.x += dx;
599 | p.y += dy;
600 | }
601 | }
602 |
603 | if (relax || animate) {
604 | update();
605 | render();
606 | }
607 |
608 | if (isMouseDown && mousePos.x > 0 && mousePos.x < BOUNDS.width && mousePos.y > 0 && mousePos.y < BOUNDS.height) {
609 | var p = voronoi.nearestSitePoint(Std.int(mousePos.x), Std.int(mousePos.y));
610 | if (p != null) {
611 | points[points.indexOf(p)].setTo(mousePos.x, mousePos.y);
612 | if (mousePosChanged) update();
613 | selectedRegion = voronoi.region(p);
614 | render();
615 | }
616 | prevMousePos.setTo(mousePos.x, mousePos.y);
617 | }
618 | }
619 |
620 | public function colorLerp(fromColor:Int, toColor:Int, t:Float = 1):Int
621 | {
622 | if (t <= 0) { return fromColor; }
623 | if (t >= 1) { return toColor; }
624 | var r:Int = fromColor >> 16 & 0xFF,
625 | g:Int = fromColor >> 8 & 0xFF,
626 | b:Int = fromColor & 0xFF,
627 | dR:Int = (toColor >> 16 & 0xFF) - r,
628 | dG:Int = (toColor >> 8 & 0xFF) - g,
629 | dB:Int = (toColor & 0xFF) - b;
630 | r += Std.int(dR * t);
631 | g += Std.int(dG * t);
632 | b += Std.int(dB * t);
633 | return r << 16 | g << 8 | b;
634 | }
635 | }
--------------------------------------------------------------------------------
/src/DemoJs.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import js.Browser.*;
4 |
5 | import com.nodename.delaunay.*;
6 | import com.nodename.geom.*;
7 |
8 | /**
9 | * hxDelaunay haxe js demo.
10 | *
11 | * @author @MatthijsKamstra
12 | */
13 | class DemoJs {
14 |
15 | var container : js.html.DivElement;
16 | var canvas : js.html.CanvasElement;
17 | var context : js.html.CanvasRenderingContext2D;
18 |
19 | var displayWidth : Int = 500;
20 | var displayHeight : Int = 500;
21 |
22 | static function main () document.addEventListener("DOMContentLoaded", function(e) new DemoJs());
23 |
24 | public function new () {
25 | setup();
26 | generate();
27 | }
28 |
29 | function setup(){
30 | container = document.createDivElement();
31 | container.id = 'delaunay';
32 | container.className = 'container';
33 | document.body.appendChild(container);
34 |
35 | canvas = document.createCanvasElement();
36 | canvas.width = displayWidth;
37 | canvas.height = displayHeight;
38 | canvas.className = 'canvasOne';
39 | canvas.id = 'canvasOne';
40 | container.appendChild(canvas);
41 |
42 | context = canvas.getContext2d();
43 |
44 | canvas.onclick = onClickHandler;
45 | }
46 |
47 | function onClickHandler (e : js.html.MouseEvent){
48 | context.clearRect(0,0,canvas.width, canvas.height);
49 | generate();
50 | e.preventDefault();
51 | }
52 |
53 | function generate(){
54 | var rect = new Rectangle(0, 0, canvas.width, canvas.height);
55 |
56 | // random set of inner points
57 | var points = new Array();
58 | for (i in 0...25) {
59 | points.push(new Point(Math.random() * rect.width, Math.random() * rect.height));
60 | }
61 |
62 | // add outer viewport bounding points
63 | points.push(new Point(rect.left, rect.top));
64 | points.push(new Point(rect.right, rect.top));
65 | points.push(new Point(rect.right, rect.bottom));
66 | points.push(new Point(rect.left, rect.bottom));
67 |
68 | var voronoi : Voronoi = new Voronoi(points, null, rect);
69 | var regions:Array> = [for (p in points) voronoi.region(p)];
70 | var sortedRegions:Array> = voronoi.regions();
71 | // var triangles:Array = voronoi.delaunayTriangulation();
72 | // var hull:Array = voronoi.hull();
73 | // var tree:Array = voronoi.spanningTree();
74 |
75 | for (i in 0 ... voronoi.triangles().length)
76 | {
77 | var tri : Triangle = voronoi.triangles()[i];
78 | var sitesArr : Array = tri.sites;
79 | context.beginPath();
80 | context.moveTo(sitesArr[0].coord.x, sitesArr[0].coord.y);
81 | context.lineTo(sitesArr[1].coord.x, sitesArr[1].coord.y);
82 | context.lineTo(sitesArr[2].coord.x, sitesArr[2].coord.y);
83 |
84 | var color = 'rgba(' + Std.random(255) + ',' + Std.random(255) + ',' + Std.random(255) + ', 1)';
85 |
86 | context.closePath();
87 | context.fillStyle = color;
88 | context.lineWidth = 0.5;
89 | context.strokeStyle = color;
90 | context.stroke();
91 | context.fill();
92 | }
93 |
94 | /*
95 | for (i in 0 ... points.length) {
96 | var p = points[i];
97 | context.beginPath();
98 | context.arc(p.x, p.y, 2, 0, 2 * Math.PI, false);
99 | context.fillStyle = 'red';
100 | context.fill();
101 | context.closePath();
102 | }
103 | */
104 |
105 | }
106 |
107 | }
--------------------------------------------------------------------------------