From ad452cfccdfdf38282f270ba9db4d8aa2ddac1a3 Mon Sep 17 00:00:00 2001 From: mschuetz Date: Thu, 17 Dec 2015 13:28:02 +0100 Subject: [PATCH] update page template to potree 1.4RC --- PotreeConverter/include/BINPointWriter.hpp | 2 - PotreeConverter/include/CloudJS.hpp | 11 + PotreeConverter/include/PotreeConverter.h | 2 + PotreeConverter/include/PotreeWriter.h | 6 +- .../resources/page_template/LICENSE | 62 - .../resources/page_template/README.md | 55 - .../build/{js => potree}/laslaz.js | 0 .../page_template/build/potree/potree.css | 450 + .../build/{js => potree}/potree.js | 15112 +- .../page_template/build/potree/potree.min.js | 8 + .../page_template/build/potree/profile.html | 93 + .../page_template/build/potree/sidebar.html | 1100 + .../page_template/build/shaders/shaders.js | 819 - .../page_template/examples/css/potree.css | 40 - .../page_template/examples/js/ProgressBar.js | 81 - .../page_template/examples/js/viewer.js | 1588 - .../examples/lasmap_template.html | 405 + .../examples/viewer_template.html | 136 +- .../resources/page_template/libs/d3/LICENSE | 26 + .../resources/page_template/libs/d3/d3.js | 9504 + .../resources/page_template/libs/d3/d3.min.js | 5 + .../libs/jquery-2.1.4/jquery-2.1.4.min.js | 4 + .../external/jquery/jquery.js | 9789 ++ .../ui-bg_diagonals-thick_18_b81900_40x40.png | Bin 0 -> 418 bytes .../ui-bg_diagonals-thick_20_666666_40x40.png | Bin 0 -> 312 bytes .../images/ui-bg_flat_10_000000_40x100.png | Bin 0 -> 205 bytes .../images/ui-bg_glass_100_f6f6f6_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_100_fdf5ce_1x400.png | Bin 0 -> 348 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 207 bytes .../ui-bg_gloss-wave_35_f6a828_500x100.png | Bin 0 -> 5815 bytes .../ui-bg_highlight-soft_100_eeeeee_1x100.png | Bin 0 -> 278 bytes .../ui-bg_highlight-soft_75_ffe45c_1x100.png | Bin 0 -> 328 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 6922 bytes .../images/ui-icons_228ef1_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_ef8c08_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_ffd27a_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 6299 bytes .../libs/jquery-ui-1.11.4/index.html | 513 + .../libs/jquery-ui-1.11.4/jquery-ui.css | 1225 + .../libs/jquery-ui-1.11.4/jquery-ui.js | 16617 ++ .../libs/jquery-ui-1.11.4/jquery-ui.min.css | 7 + .../libs/jquery-ui-1.11.4/jquery-ui.min.js | 13 + .../jquery-ui-1.11.4/jquery-ui.structure.css | 833 + .../jquery-ui.structure.min.css | 5 + .../libs/jquery-ui-1.11.4/jquery-ui.theme.css | 410 + .../jquery-ui-1.11.4/jquery-ui.theme.min.css | 5 + .../page_template/libs/openlayers3/LICENSE | 4 + .../libs/openlayers3/ol-debug.js | 133528 +++++++++++++++ .../page_template/libs/openlayers3/ol-deps.js | 1716 + .../page_template/libs/openlayers3/ol.css | 1 + .../page_template/libs/openlayers3/ol.js | 1017 + .../page_template/libs/plasio/vendor/three.js | 37760 ++++ .../page_template/libs/proj4/README.md | 10 +- .../page_template/libs/proj4/proj4.js | 6 +- .../page_template/libs/signals/README.md | 97 - .../page_template/libs/signals/signals.min.js | 14 - .../resources/page_template/resources/LICENSE | 10 + .../page_template/resources/icons/area.png | Bin 776 -> 0 bytes .../page_template/resources/icons/area.svg | 23 +- .../resources/icons/clip_volume.png | Bin 1097 -> 0 bytes .../resources/icons/distance.png | Bin 680 -> 0 bytes .../resources/icons/distance.svg | 20 +- .../page_template/resources/icons/focus.png | Bin 1197 -> 0 bytes .../resources/icons/frontview.svg | 254 + .../resources/icons/leftview.svg | 254 + .../resources/icons/map_icon.png | Bin 0 -> 7937 bytes .../resources/icons/menu_button.svg | 95 + .../resources/icons/orbit_controls.png | Bin 1350 -> 0 bytes .../page_template/resources/icons/point.svg | 73 + .../page_template/resources/icons/profile.png | Bin 1044 -> 0 bytes .../page_template/resources/icons/remove.svg | 184 + .../resources/icons/reset_tools.svg | 184 + .../resources/icons/round_dropdown.svg | 237 + .../resources/icons/round_icon.svg | 230 + .../page_template/resources/icons/topview.svg | 254 + .../page_template/resources/icons/volume.png | Bin 933 -> 0 bytes .../page_template/resources/logo.png | Bin 0 -> 28793 bytes .../page_template/resources/logo.svg | 91 + .../page_template/resources/logo_small.png | Bin 0 -> 6718 bytes .../resources/textures/skyboxsun25degtest.txt | 1 + PotreeConverter/src/PotreeConverter.cpp | 122 +- PotreeConverter/src/PotreeWriter.cpp | 6 + PotreeConverter/src/main.cpp | 11 +- README.md | 9 + testcases.txt | 15 +- 85 files changed, 226602 insertions(+), 8550 deletions(-) delete mode 100644 PotreeConverter/resources/page_template/LICENSE delete mode 100644 PotreeConverter/resources/page_template/README.md rename PotreeConverter/resources/page_template/build/{js => potree}/laslaz.js (100%) create mode 100644 PotreeConverter/resources/page_template/build/potree/potree.css rename PotreeConverter/resources/page_template/build/{js => potree}/potree.js (67%) create mode 100644 PotreeConverter/resources/page_template/build/potree/potree.min.js create mode 100644 PotreeConverter/resources/page_template/build/potree/profile.html create mode 100644 PotreeConverter/resources/page_template/build/potree/sidebar.html delete mode 100644 PotreeConverter/resources/page_template/build/shaders/shaders.js delete mode 100644 PotreeConverter/resources/page_template/examples/css/potree.css delete mode 100644 PotreeConverter/resources/page_template/examples/js/ProgressBar.js delete mode 100644 PotreeConverter/resources/page_template/examples/js/viewer.js create mode 100644 PotreeConverter/resources/page_template/examples/lasmap_template.html create mode 100644 PotreeConverter/resources/page_template/libs/d3/LICENSE create mode 100644 PotreeConverter/resources/page_template/libs/d3/d3.js create mode 100644 PotreeConverter/resources/page_template/libs/d3/d3.min.js create mode 100644 PotreeConverter/resources/page_template/libs/jquery-2.1.4/jquery-2.1.4.min.js create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/external/jquery/jquery.js create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_18_b81900_40x40.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_20_666666_40x40.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_flat_10_000000_40x100.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_glass_100_f6f6f6_1x400.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_glass_100_fdf5ce_1x400.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_gloss-wave_35_f6a828_500x100.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_highlight-soft_100_eeeeee_1x100.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-bg_highlight-soft_75_ffe45c_1x100.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-icons_222222_256x240.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-icons_228ef1_256x240.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-icons_ef8c08_256x240.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-icons_ffd27a_256x240.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/images/ui-icons_ffffff_256x240.png create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/index.html create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.css create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.js create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.min.css create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.min.js create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.structure.css create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.structure.min.css create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.theme.css create mode 100644 PotreeConverter/resources/page_template/libs/jquery-ui-1.11.4/jquery-ui.theme.min.css create mode 100644 PotreeConverter/resources/page_template/libs/openlayers3/LICENSE create mode 100644 PotreeConverter/resources/page_template/libs/openlayers3/ol-debug.js create mode 100644 PotreeConverter/resources/page_template/libs/openlayers3/ol-deps.js create mode 100644 PotreeConverter/resources/page_template/libs/openlayers3/ol.css create mode 100644 PotreeConverter/resources/page_template/libs/openlayers3/ol.js create mode 100644 PotreeConverter/resources/page_template/libs/plasio/vendor/three.js delete mode 100644 PotreeConverter/resources/page_template/libs/signals/README.md delete mode 100644 PotreeConverter/resources/page_template/libs/signals/signals.min.js create mode 100644 PotreeConverter/resources/page_template/resources/LICENSE delete mode 100644 PotreeConverter/resources/page_template/resources/icons/area.png delete mode 100644 PotreeConverter/resources/page_template/resources/icons/clip_volume.png delete mode 100644 PotreeConverter/resources/page_template/resources/icons/distance.png delete mode 100644 PotreeConverter/resources/page_template/resources/icons/focus.png create mode 100644 PotreeConverter/resources/page_template/resources/icons/frontview.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/leftview.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/map_icon.png create mode 100644 PotreeConverter/resources/page_template/resources/icons/menu_button.svg delete mode 100644 PotreeConverter/resources/page_template/resources/icons/orbit_controls.png create mode 100644 PotreeConverter/resources/page_template/resources/icons/point.svg delete mode 100644 PotreeConverter/resources/page_template/resources/icons/profile.png create mode 100644 PotreeConverter/resources/page_template/resources/icons/remove.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/reset_tools.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/round_dropdown.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/round_icon.svg create mode 100644 PotreeConverter/resources/page_template/resources/icons/topview.svg delete mode 100644 PotreeConverter/resources/page_template/resources/icons/volume.png create mode 100644 PotreeConverter/resources/page_template/resources/logo.png create mode 100644 PotreeConverter/resources/page_template/resources/logo.svg create mode 100644 PotreeConverter/resources/page_template/resources/logo_small.png create mode 100644 PotreeConverter/resources/page_template/resources/textures/skyboxsun25degtest.txt diff --git a/PotreeConverter/include/BINPointWriter.hpp b/PotreeConverter/include/BINPointWriter.hpp index 96abb101..adb0c815 100644 --- a/PotreeConverter/include/BINPointWriter.hpp +++ b/PotreeConverter/include/BINPointWriter.hpp @@ -23,8 +23,6 @@ namespace Potree{ class BINPointWriter : public PointWriter{ public: - string file; - int numPoints; PointAttributes attributes; ofstream *writer; AABB aabb; diff --git a/PotreeConverter/include/CloudJS.hpp b/PotreeConverter/include/CloudJS.hpp index bd7774c1..8ad1cc0a 100644 --- a/PotreeConverter/include/CloudJS.hpp +++ b/PotreeConverter/include/CloudJS.hpp @@ -53,6 +53,7 @@ class CloudJS{ double scale; int hierarchyStepSize = -1; long long numAccepted = 0; + string projection = ""; CloudJS() = default; @@ -70,8 +71,16 @@ class CloudJS{ Value &vScale = d["scale"]; Value &vHierarchyStepSize = d["hierarchyStepSize"]; + + version = vVersion.GetString(); octreeDir = vOctreeDir.GetString(); + + if(d.HasMember("projection")){ + Value &vProjection = d["projection"]; + projection = vProjection.GetString(); + } + numAccepted = vPoints.GetInt64(); boundingBox = AABB( Vector3(vBoundingBox["lx"].GetDouble(), vBoundingBox["ly"].GetDouble(), vBoundingBox["lz"].GetDouble()), @@ -114,6 +123,7 @@ class CloudJS{ Value version(this->version.c_str(), (rapidjson::SizeType)this->version.size()); Value octreeDir("data"); + Value projection(this->projection.c_str(), (rapidjson::SizeType)this->projection.size()); Value boundingBox(rapidjson::kObjectType); { //Value min(rapidjson::kArrayType); @@ -179,6 +189,7 @@ class CloudJS{ d.AddMember("version", version, d.GetAllocator()); d.AddMember("octreeDir", octreeDir, d.GetAllocator()); + d.AddMember("projection", projection, d.GetAllocator()); d.AddMember("points", (uint64_t)numAccepted, d.GetAllocator()); d.AddMember("boundingBox", boundingBox, d.GetAllocator()); d.AddMember("tightBoundingBox", tightBoundingBox, d.GetAllocator()); diff --git a/PotreeConverter/include/PotreeConverter.h b/PotreeConverter/include/PotreeConverter.h index 652189ab..20ab0a90 100644 --- a/PotreeConverter/include/PotreeConverter.h +++ b/PotreeConverter/include/PotreeConverter.h @@ -46,6 +46,8 @@ class PotreeConverter{ vector aabbValues; string pageName = ""; StoreOption storeOption = StoreOption::ABORT_IF_EXISTS; + string projection = ""; + bool sourceListingOnly = false; PotreeConverter(string workDir, vector sources); diff --git a/PotreeConverter/include/PotreeWriter.h b/PotreeConverter/include/PotreeWriter.h index 2bbc5938..2f4fe753 100644 --- a/PotreeConverter/include/PotreeWriter.h +++ b/PotreeConverter/include/PotreeWriter.h @@ -37,7 +37,7 @@ class PWNode{ bool addCalledSinceLastFlush = false; PotreeWriter *potreeWriter; vector cache; - int storeLimit = 2'000; + int storeLimit = 20'000; vector store; bool isInMemory = true; @@ -112,6 +112,7 @@ class PotreeWriter{ vector store; thread storeThread; int pointsInMemory = 0; + string projection = ""; PotreeWriter(string workDir); @@ -138,7 +139,10 @@ class PotreeWriter{ flush(); } + void setProjection(string projection); + void loadStateFromDisk(); + private: }; diff --git a/PotreeConverter/resources/page_template/LICENSE b/PotreeConverter/resources/page_template/LICENSE deleted file mode 100644 index 86bee02f..00000000 --- a/PotreeConverter/resources/page_template/LICENSE +++ /dev/null @@ -1,62 +0,0 @@ - -============ -== POTREE == -============ - -http://potree.org - -Copyright (c) 2011-2014, Markus Schtz -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. - - -===================== -== PLASIO / LASLAZ == -===================== - -http://plas.io/ - -The MIT License (MIT) - -Copyright (c) 2014 Uday Verma, uday.karan@gmail.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/PotreeConverter/resources/page_template/README.md b/PotreeConverter/resources/page_template/README.md deleted file mode 100644 index 70b0e405..00000000 --- a/PotreeConverter/resources/page_template/README.md +++ /dev/null @@ -1,55 +0,0 @@ - -# README - -* [Getting Started](./docs/getting_started.md) -* [Changelog](./docs/changelog.md) -* [User Interface](./docs/user_interface.md) -* [FAQ](./docs/faq.md) -* [File Format](./docs/file_format.md) - -## About - -Potree is a free open-source WebGL based point cloud renderer for large point clouds. - -It is based on the [TU Wien Scanopy project](https://www.cg.tuwien.ac.at/research/projects/Scanopy/) - - - - ![](./docs/images/potree_screens.png) - -At the moment, this project is continued as master thesis under the [Harvest4D Project](https://harvest4d.org/) - -Newest information and work in progress is usually available on [twitter](https://twitter.com/m_schuetz) - -General infos, downloads, showcase, etc. at [potree.org](http://potree.org/) - -Contact: Markus Schütz - mschuetz@potree.org - -## Downloads - -[PotreeConverter source and Win64 binaries](https://github.com/potree/PotreeConverter/releases) - -## Showcase - -Take a look at the [potree showcase](http://potree.org/wp/demo/) for some live examples. - -## Compatibility - -| Browser | OS | Result | -| -------------------- |:-------:|:-------------:| -| Chrome 42 | Win7 | works | -| Firefox 34 | Win7 | works | -| Internet Explorer 11 | Win7 | partially works; performance issues | -| Firefox | Android | partially works, GUI and stability issues | -| Opera | Android | partially works, GUI and stability issues and slow | -| Chrome | Android | works | - -## Credits - -* The multi-res-octree algorithms used by this viewer were developed at the Vienna University of Technology by Michael Wimmer and Claus Scheiblauer as part of the [Scanopy Project](http://www.cg.tuwien.ac.at/research/projects/Scanopy/). -* [Three.js](https://github.com/mrdoob/three.js), the WebGL 3D rendering library on which potree is built. -* [plas.io](http://plas.io/) point cloud viewer. LAS and LAZ support have been taken from the laslaz.js implementation of plas.io. Thanks to [Uday Verma](https://twitter.com/udaykverma) and [Howard Butler](https://twitter.com/howardbutler) for this! -* [Harvest4D](https://harvest4d.org/) Potree currently runs as Master Thesis under the Harvest4D Project -* Christian Boucheny (EDL developer) and Daniel Girardeau-Montaut ([CloudCompare](http://www.danielgm.net/cc/)). The EDL shader was adapted from the CloudCompare source code! -* [Martin Isenburg](http://rapidlasso.com/), [Georepublic](http://georepublic.de/en/), -[Veesus](http://veesus.com/), [Sigeom Sa](http://www.sigeom.ch/), [SITN](http://www.ne.ch/sitn), [Pix4D](http://pix4d.com/) as well as all the contributers to potree and PotreeConverter and many more for their support. diff --git a/PotreeConverter/resources/page_template/build/js/laslaz.js b/PotreeConverter/resources/page_template/build/potree/laslaz.js similarity index 100% rename from PotreeConverter/resources/page_template/build/js/laslaz.js rename to PotreeConverter/resources/page_template/build/potree/laslaz.js diff --git a/PotreeConverter/resources/page_template/build/potree/potree.css b/PotreeConverter/resources/page_template/build/potree/potree.css new file mode 100644 index 00000000..992f7f25 --- /dev/null +++ b/PotreeConverter/resources/page_template/build/potree/potree.css @@ -0,0 +1,450 @@ + +/* CSS - Cascading Style Sheet */ +/* Palette color codes */ +/* Palette URL: http://paletton.com/#uid=13p0u0kex8W2uqu8af7lEqaulDE */ + +/* Feel free to copy&paste color codes to your application */ + + +/* As hex codes */ + +.color-primary-0 { color: #19282C } /* Main Primary color */ +.color-primary-1 { color: #7A8184 } +.color-primary-2 { color: #39474B } +.color-primary-3 { color: #2D6D82 } +.color-primary-4 { color: #108FB9 } + + + +/* As RGBa codes */ + +.rgba-primary-0 { color: rgba( 25, 40, 44,1) } /* Main Primary color */ +.rgba-primary-1 { color: rgba(122,129,132,1) } +.rgba-primary-2 { color: rgba( 57, 71, 75,1) } +.rgba-primary-3 { color: rgba( 45,109,130,1) } +.rgba-primary-4 { color: rgba( 16,143,185,1) } + + + +/* Generated by Paletton.com © 2002-2014 */ +/* http://paletton.com */ + + +.potree_container{ + /*font-size: 75%;*/ +} + +.potree_info_text{ + color: white; + font-weight: bold; + text-shadow: 1px 1px 1px black, + 1px -1px 1px black, + -1px 1px 1px black, + -1px -1px 1px black; +} + +#potree_description{ + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + text-align: center +} + +#potree_sidebar_container{ + position: absolute; + z-index: 0; + width: 300px; + height: 100%; + overflow-y: auto; + font-size: 85%; + +} + +.potree_sidebar_brand{ + margin: 1px 20px; + line-height: 2em; + font-size: 100%; + font-weight: bold; + position: relative; +} + +#potree_sidebar_container a{ + color: #8Aa1c4; +} + +.potree_menu_toggle{ + position: absolute; + float: left; + margin: 8px 8px; + background: none; + width: 2.5em; + height: 2.5em; + z-index: 100; + cursor: pointer; +} + +#potree_map_toggle{ + position: absolute; + float: left; + margin: 8px 8px; + background: none; + width: 2.5em; + height: 2.5em; + z-index: 100; + top: calc(2.5em + 8px); + cursor: pointer; +} + +.potree_menu_toggle_bars{ + border: 1px solid black; + background-color: white; + width: 90%; + height: 4px; +} + + +#potree_render_area{ + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + overflow: hidden; + z-index: 1; + -webkit-transition: left .35s; + transition: left .35s; +} + +.potree-panel { + border: 1px solid black; + border-radius: 0.4em; + padding: 0px; + background-color: rgba(255, 255, 255, 0.1); +} + +.potree-panel-heading{ + background-color: rgba(0, 0, 0, 0.5); +} + +a:hover, a:visited, a:link, a:active{ + color: #ccccff; + text-decoration: none; +} + +canvas { + width: 100%; + height: 100% +} + +body{ + margin: 0; + padding: 0 +} + +.axis { + font: 10px sans-serif; + color: white; +} + +.axis path{ + fill: none; + stroke: white; + shape-rendering: crispEdges; + opacity: 0.7; +} + +.axis line { + fill: none; + stroke: white; + shape-rendering: crispEdges; + opacity: 0.1; +} + +.annotation{ + -webkit-transition: opacity .5s; + transition: opacity .5s; +} + + +.measurement-detail-node-marker{ + border: 1px solid black; + border-radius: 6px; + margin: auto; + text-align: center; + background-color: rgba(255, 255, 255, 0.4); + color: black; +} + +.measurement-detail-node-area{ + border: 3px solid #749b74; + border-radius: 6px; + margin: auto; + margin-top: 15px; + text-align: center; + background-color: #eee; +} + +.measurement-detail-node-angle{ + border: 1px solid #000; + width: 50%; + margin: auto; + border-radius: 6px; + text-align: center; + background-color: rgba(255,255,255,0.1); + color: black; +} + +.measurement-detail-node-distance{ + border: 1px solid black; + width: 50%; + margin: auto; + border-radius: 6px; + text-align: center; + background-color: rgba(255,255,255,0.1); + color: black; +} + +.measurement-detail-edge{ + border: 1px solid black; + width: 0px; + height: 2px; + margin: auto; +} + +#measurement_details .panel-body{ + padding: 5px 3px; +} + +.measurement-detail-button{ + width: 100%; + margin-top: 8px; +} + +.pv-panel-heading{ + padding: 4px !important; + display: flex; + flex-direction: row +} + + +.pv-menu-list > li > .ui-slider{ + background-color: #7A8184 !important; + background: none; + border: 1px solid black; +} + +.panel-body > li > .ui-slider{ + background-color: #7A8184 !important; + background: none; + border: 1px solid black; +} + +.pv-menu-list > li > label{ + width: 100%; +} + +.pv-menu-list select{ + width: 100%; +} + +.pv-menu-list > li > span{ + width: 100% !important; +} + +.pv-select-label{ + margin: 1px; + font-size: 90%; + font-weight: 100; +} + +.pv-menu-list .ui-selectmenu-button span.ui-selectmenu-text{ + line-height: 0.8em; +} + +.button-icon:hover{ + background-color: #09181C; +} + +.ui-accordion-content{ + padding: 0px 0px !important; + border: none !important; +} + +.ui-widget-content{ + background-color: none !important; + color: #9AA1A4 !important; +} + +.navmenu{ + background-color: #39474B !important; + border-color: #39474B !important; +} + +#accordion{ + background-color: #19282C; + color: #7A8184; +} + +#accordion > h3{ + background-color: #7A8184 !important; + background: #f6f6f6 50% 50% repeat-x; + border: none; + color: #39474B; + padding: 4px 10px 4px 30px; +} + +.accordion > div{ + background: none !important; +} + +.pv-menu-list{ + list-style-type: none; + padding: 0; +} + +.pv-menu-list > li{ + margin: 8px 20px; +} + +#measurement_details .pv-menu-list > li{ + margin: 20px 10px; +} + +.pv-menu-list > .pv-divider{ + border-top: 1px solid black; + opacity: 0.2; + margin: 8px 0px; +} + +.pv-menu-list-header{ + opacity: 0.8; +} + +.pv-menu-item{ + width: 100%; +} + +.icon-bar{ + height: 4px !important; + border: 1px solid black; + background-color: white; + border-radius: 2px; +} + +.canvas{ + -webkit-transition: top .35s, left .35s, bottom .35s, right .35s, width .35s; + transition: top .35s, left .35s, bottom .35s, right .35s, width .35s; +} + +.profile-container-button{ + cursor: pointer; +} + + + +/* no scrollbar styling in firefox, sadly */ +.navmenu::-webkit-scrollbar{ + height: 12px; +} + +.navmenu::-webkit-scrollbar-track{ + background: rgba(0, 0, 0, 0.1); +} + +.navmenu::-webkit-scrollbar-thumb{ + background: rgba(0, 0, 0, 0.5); +} + +.pv-titlebar{ + background-color: #7A8184; + color: ##39474B; + font-weight: 700; + padding: 5px; +} + +.pv-main-color{ + background: #19282C; +} + +.profile-button:hover{ + background-color: #0000CC; +} + +.unselectable{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.selectable{ + -webkit-touch-callout: text; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.ol-dragbox { + background-color: rgba(255,255,255,0.4); + border-color: rgba(100,150,0,1); + border: 1px solid red; +} + + + + + + + + diff --git a/PotreeConverter/resources/page_template/build/js/potree.js b/PotreeConverter/resources/page_template/build/potree/potree.js similarity index 67% rename from PotreeConverter/resources/page_template/build/js/potree.js rename to PotreeConverter/resources/page_template/build/potree/potree.js index c04a45c4..f352081b 100644 --- a/PotreeConverter/resources/page_template/build/js/potree.js +++ b/PotreeConverter/resources/page_template/build/potree/potree.js @@ -4,6 +4,15 @@ function Potree(){ } +Potree.version = { + major: 1, + minor: 4, + suffix: "RC" +}; + +console.log("Potree " + Potree.version.major + "." + Potree.version.minor + Potree.version.suffix); + +Potree.pointBudget = 1*1000*1000; // contains WebWorkers with base64 encoded code Potree.workers = {}; @@ -11,6 +20,248 @@ Potree.workers = {}; Potree.Shaders = {}; +Potree.updatePointClouds = function(pointclouds, camera, renderer){ + + if(!Potree.lru){ + Potree.lru = new LRU(); + } + + for(var i = 0; i < pointclouds.length; i++){ + var pointcloud = pointclouds[i]; + for(var j = 0; j < pointcloud.profileRequests.length; j++){ + pointcloud.profileRequests[j].update(); + } + } + + var result = Potree.updateVisibility(pointclouds, camera, renderer); + + for(var i = 0; i < pointclouds.length; i++){ + var pointcloud = pointclouds[i]; + pointcloud.updateMaterial(pointcloud.material, pointcloud.visibleNodes, camera, renderer); + pointcloud.updateVisibleBounds(); + } + + Potree.getLRU().freeMemory(); + + return result; +}; + +Potree.getLRU = function(){ + if(!Potree.lru){ + Potree.lru = new LRU(); + } + + return Potree.lru; +}; + + +Potree.updateVisibility = function(pointclouds, camera, renderer){ + var numVisibleNodes = 0; + var numVisiblePoints = 0; + + var visibleNodes = []; + var visibleGeometry = []; + var unloadedGeometry = []; + + var frustums = []; + var camObjPositions = []; + + // calculate object space frustum and cam pos and setup priority queue + var priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); + for(var i = 0; i < pointclouds.length; i++){ + var pointcloud = pointclouds[i]; + + if(!pointcloud.initialized()){ + continue; + } + + pointcloud.numVisibleNodes = 0; + pointcloud.numVisiblePoints = 0; + pointcloud.visibleNodes = []; + pointcloud.visibleGeometry = []; + + // frustum in object space + camera.updateMatrixWorld(); + var frustum = new THREE.Frustum(); + var viewI = camera.matrixWorldInverse; + var world = pointcloud.matrixWorld; + var proj = camera.projectionMatrix; + var fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world); + frustum.setFromMatrix( fm ); + frustums.push(frustum); + + // camera position in object space + var view = camera.matrixWorld; + var worldI = new THREE.Matrix4().getInverse(world); + var camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view); + var camObjPos = new THREE.Vector3().setFromMatrixPosition( camMatrixObject ); + camObjPositions.push(camObjPos); + + if(pointcloud.visible && pointcloud.root !== null){ + priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE}); + } + + // hide all previously visible nodes + //if(pointcloud.root instanceof Potree.PointCloudOctreeNode){ + // pointcloud.hideDescendants(pointcloud.root.sceneNode); + //} + if(pointcloud.root.isTreeNode()){ + pointcloud.hideDescendants(pointcloud.root.sceneNode); + } + + for(var j = 0; j < pointcloud.boundingBoxNodes.length; j++){ + pointcloud.boundingBoxNodes[j].visible = false; + } + } + + while(priorityQueue.size() > 0){ + var element = priorityQueue.pop(); + var node = element.node; + var parent = element.parent; + var pointcloud = pointclouds[element.pointcloud]; + + var box = node.getBoundingBox(); + var frustum = frustums[element.pointcloud]; + var camObjPos = camObjPositions[element.pointcloud]; + + var insideFrustum = frustum.intersectsBox(box); + var visible = insideFrustum; + visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget); + + if(!visible){ + continue; + } + + numVisibleNodes++; + numVisiblePoints += node.getNumPoints(); + + pointcloud.numVisibleNodes++; + pointcloud.numVisiblePoints += node.getNumPoints(); + + if(node.isGeometryNode() && (!parent || parent.isTreeNode())){ + if(node.isLoaded()){ + node = pointcloud.toTreeNode(node, parent); + }else{ + unloadedGeometry.push(node); + visibleGeometry.push(node); + } + } + + if(node.isTreeNode()){ + Potree.getLRU().touch(node.geometryNode); + node.sceneNode.visible = true; + node.sceneNode.material = pointcloud.material; + + visibleNodes.push(node); + pointcloud.visibleNodes.push(node); + + if(node.parent){ + node.sceneNode.matrixWorld.multiplyMatrices( node.parent.sceneNode.matrixWorld, node.sceneNode.matrix ); + }else{ + node.sceneNode.matrixWorld.multiplyMatrices( pointcloud.matrixWorld, node.sceneNode.matrix ); + } + + if(pointcloud.showBoundingBox && !node.boundingBoxNode){ + var boxHelper = new THREE.BoxHelper(node.sceneNode); + pointcloud.add(boxHelper); + pointcloud.boundingBoxNodes.push(boxHelper); + node.boundingBoxNode = boxHelper; + node.boundingBoxNode.matrixWorld.copy(node.sceneNode.matrixWorld); + }else if(pointcloud.showBoundingBox){ + node.boundingBoxNode.visible = true; + node.boundingBoxNode.matrixWorld.copy(node.sceneNode.matrixWorld); + }else if(!pointcloud.showBoundingBox && node.boundingBoxNode){ + node.boundingBoxNode.visible = false; + } + } + + // add child nodes to priorityQueue + var children = node.getChildren(); + for(var i = 0; i < children.length; i++){ + var child = children[i]; + + var sphere = child.getBoundingSphere(); + var distance = sphere.center.distanceTo(camObjPos); + var radius = sphere.radius; + + var fov = camera.fov / 2 * Math.PI / 180.0; + var pr = 1 / Math.tan(fov) * radius / Math.sqrt(distance * distance - radius * radius); + + var screenPixelRadius = renderer.domElement.clientHeight * pr; + if(screenPixelRadius < pointcloud.minimumNodePixelSize){ + continue; + } + + var weight = pr; + if(distance - radius < 0){ + weight = Number.MAX_VALUE; + } + + priorityQueue.push({pointcloud: element.pointcloud, node: child, parent: node, weight: weight}); + } + + + }// end priority queue loop + + for(var i = 0; i < Math.min(5, unloadedGeometry.length); i++){ + unloadedGeometry[i].load(); + } + + return {visibleNodes: visibleNodes, numVisiblePoints: numVisiblePoints}; +}; + + + + + + + +Potree.PointCloudTreeNode = function(){ + + this.getChildren = function(){ + throw "override function"; + }; + + this.getBoundingBox = function(){ + throw "override function"; + }; + + this.isLoaded = function(){ + throw "override function"; + }; + + this.isGeometryNode = function(){ + throw "override function"; + }; + + this.isTreeNode = function(){ + throw "override function"; + }; + + this.getLevel = function(){ + throw "override function"; + }; + + this.getBoundingSphere = function(){ + throw "override function"; + }; + +}; + + +Potree.PointCloudTree = function(){ + THREE.Object3D.call( this ); + + this.initialized = function(){ + return this.root !== null; + }; + + +}; + +Potree.PointCloudTree.prototype = Object.create(THREE.Object3D.prototype); + + Potree.WorkerManager = function(code){ @@ -481,6 +732,8 @@ Potree.Shaders["pointcloud.fs"] = [ "//", "//#endif", "", + "uniform float blendHardness;", + "uniform float blendDepthSupplement;", "uniform float fov;", "uniform float spacing;", "uniform float near;", @@ -522,7 +775,7 @@ Potree.Shaders["pointcloud.fs"] = [ " #if defined weighted_splats", " vec2 uv = gl_FragCoord.xy / vec2(screenWidth, screenHeight);", " float sDepth = texture2D(depthMap, uv).r;", - " if(vLinearDepth > sDepth + vRadius){", + " if(vLinearDepth > sDepth + vRadius + blendDepthSupplement){", " discard;", " }", " #endif", @@ -552,7 +805,7 @@ Potree.Shaders["pointcloud.fs"] = [ " #endif", " ", " #if defined weighted_splats", - " float w = pow(1.0 - (u*u + v*v), 2.0);", + " float w = pow(1.0 - (u*u + v*v), blendHardness);", " gl_FragColor.rgb = gl_FragColor.rgb * w;", " gl_FragColor.a = w;", " #endif", @@ -774,13 +1027,6 @@ Potree.Shaders["edl.fs"] = [ " return 1.0 - (linearDepth - near) / (far - near);", "}", "", - "float expToLinear(float z){", - " z = 2.0 * z - 1.0;", - " float linear = (2.0 * near * far) / (far + near - z * (far - near));", - "", - " return linear;", - "}", - "", "// this actually only returns linear depth values if LOG_BIAS is 1.0", "// lower values work out more nicely, though.", "#define LOG_BIAS 0.01", @@ -788,12 +1034,7 @@ Potree.Shaders["edl.fs"] = [ " return (pow((1.0 + LOG_BIAS * far), z) - 1.0) / LOG_BIAS;", "}", "", - "float obscurance(float z, float dist){", - " return max(0.0, z) / dist;", - "}", - "", "float computeObscurance(float linearDepth){", - " vec4 P = vec4(0, 0, 1, -ztransform(linearDepth));", " vec2 uvRadius = radius / vec2(screenWidth, screenHeight);", " ", " float sum = 0.0;", @@ -805,10 +1046,9 @@ Potree.Shaders["edl.fs"] = [ " float neighbourDepth = logToLinear(texture2D(colorMap, N_abs_pos).a);", " ", " if(neighbourDepth != 0.0){", - " float Zn = ztransform(neighbourDepth);", - " float Znp = dot( vec4( N_rel_pos, Zn, 1.0), P );", + " float Znp = ztransform(neighbourDepth) - ztransform(linearDepth);", " ", - " sum += obscurance( Znp, 0.05 * linearDepth );", + " sum += max(0.0, Znp) / (0.05 * linearDepth);", " }", " }", " ", @@ -1051,6 +1291,7 @@ Potree.POCLoader.load = function load(url, callback) { tightBoundingBox.min.add(offset); tightBoundingBox.max.add(offset); + pco.projection = fMno.projection; pco.boundingBox = boundingBox; pco.tightBoundingBox = tightBoundingBox; pco.boundingSphere = boundingBox.getBoundingSphere(); @@ -1112,6 +1353,8 @@ Potree.POCLoader.load = function load(url, callback) { }catch(e){ console.log("loading failed: '" + url + "'"); console.log(e); + + callback(); } }; @@ -1803,6 +2046,8 @@ Potree.PointCloudMaterial = function(parameters){ var attributes = {}; var uniforms = { spacing: { type: "f", value: 1.0 }, + blendHardness: { type: "f", value: 2.0 }, + blendDepthSupplement: { type: "f", value: 0.0 }, fov: { type: "f", value: 1.0 }, screenWidth: { type: "f", value: 1.0 }, screenHeight: { type: "f", value: 1.0 }, @@ -2889,13 +3134,26 @@ THREE.FirstPersonControls = function ( object, domElement ) { event.preventDefault(); var direction = (event.detail<0 || event.wheelDelta>0) ? 1 : -1; - scope.moveSpeed += scope.moveSpeed * 0.1 * direction; - scope.moveSpeed = Math.max(0.1, scope.moveSpeed); + var moveSpeed = scope.moveSpeed + scope.moveSpeed * 0.1 * direction; + moveSpeed = Math.max(0.1, moveSpeed); + scope.setMoveSpeed(moveSpeed); + + scope.dispatchEvent( startEvent ); scope.dispatchEvent( endEvent ); } + + this.setMoveSpeed = function(value){ + if(scope.moveSpeed !== value){ + scope.moveSpeed = value; + scope.dispatchEvent( { + type: "move_speed_changed", + controls: scope + }); + } + }; function onKeyDown( event ) { if ( scope.enabled === false) return; @@ -2930,129 +3188,101 @@ THREE.FirstPersonControls = function ( object, domElement ) { this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox - window.addEventListener( 'keydown', onKeyDown, false ); - window.addEventListener( 'keyup', onKeyUp, false ); + if(this.domElement.tabIndex === -1){ + this.domElement.tabIndex = 2222; + } + this.domElement.addEventListener( 'keydown', onKeyDown, false ); + this.domElement.addEventListener( 'keyup', onKeyUp, false ); + + //document.body.addEventListener( 'keydown', onKeyDown, false ); + //document.body.addEventListener( 'keyup', onKeyUp, false ); }; THREE.FirstPersonControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + + /** - * @author mschuetz / http://mschuetz.at/ + * @author mschuetz / http://mschuetz.at + * + * adapted from THREE.OrbitControls by + * * @author qiao / https://github.com/qiao * @author mrdoob / http://mrdoob.com * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com + * + * This set of controls performs first person navigation without mouse lock. + * Instead, rotating the camera is done by dragging with the left mouse button. + * + * move: a/s/d/w or up/down/left/right + * rotate: left mouse + * pan: right mouse + * change speed: mouse wheel + * + * */ -/*global THREE, console */ -// -// Adapted from THREE.OrbitControls -// - Smooth movements -// - creates "proposeTransform" events -// -// -// This set of controls performs orbiting, dollying (zooming), and panning. It maintains -// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is -// supported. -// -// Orbit - left mouse / touch: one finger move -// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish -// Pan - right mouse, or arrow keys / touch: three finter swipe -// -// This is a drop-in replacement for (most) TrackballControls used in examples. -// That is, include this js file and wherever you see: -// controls = new THREE.TrackballControls( camera ); -// controls.target.z = 150; -// Simple substitute "OrbitControls" and the control should work as-is. -Potree.OrbitControls = function ( object, domElement ) { +Potree.GeoControls = function ( object, domElement ) { this.object = object; this.domElement = ( domElement !== undefined ) ? domElement : document; - - // API - + // Set to false to disable this control this.enabled = true; - // "target" sets the location of focus, where the control orbits around - // and where it pans with respect to. - this.target = new THREE.Vector3(); + // Set this to a THREE.SplineCurve3 instance + this.track = null; + // position on track in intervall [0,1] + this.trackPos = 0; + + this.rotateSpeed = 1.0; + this.moveSpeed = 10.0; - // center is old, deprecated; use "target" instead - this.center = this.target; + this.keys = { + LEFT: 37, + UP: 38, + RIGHT: 39, + BOTTOM: 40, + A: 'A'.charCodeAt(0), + S: 'S'.charCodeAt(0), + D: 'D'.charCodeAt(0), + W: 'W'.charCodeAt(0), + Q: 'Q'.charCodeAt(0), + E: 'E'.charCodeAt(0), + R: 'R'.charCodeAt(0), + F: 'F'.charCodeAt(0) + }; - // This option actually enables dollying in and out; left as "zoom" for - // backwards compatibility - this.noZoom = false; - this.zoomSpeed = 1.0; + var scope = this; - // Limits to how far you can dolly in and out - this.minDistance = 0; - this.maxDistance = Infinity; + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); - // Set to true to disable this control - this.noRotate = false; - this.rotateSpeed = 1.0; + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + var panOffset = new THREE.Vector3(); - // Set to true to disable this control - this.noPan = false; - this.keyPanSpeed = 7.0; // pixels moved per arrow key push + var offset = new THREE.Vector3(); - // Set to true to automatically rotate around the target - this.autoRotate = false; - this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + var phiDelta = 0; + var thetaDelta = 0; + var scale = 1; + var pan = new THREE.Vector3(); - this.fadeFactor = 10; + this.shiftDown = false; - // How far you can orbit vertically, upper and lower limits. - // Range is 0 to Math.PI radians. - this.minPolarAngle = 0; // radians - this.maxPolarAngle = Math.PI; // radians + var lastPosition = new THREE.Vector3(); - // Set to true to disable use of the keys - this.noKeys = false; - - // The four arrow keys - this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; - - //////////// - // internals - - var scope = this; - - var EPS = 0.000001; - - var rotateStart = new THREE.Vector2(); - var rotateEnd = new THREE.Vector2(); - var rotateDelta = new THREE.Vector2(); - - var panStart = new THREE.Vector2(); - var panEnd = new THREE.Vector2(); - var panDelta = new THREE.Vector2(); - var panOffset = new THREE.Vector3(); - - var offset = new THREE.Vector3(); - - var dollyStart = new THREE.Vector2(); - var dollyEnd = new THREE.Vector2(); - var dollyDelta = new THREE.Vector2(); - - var phiDelta = 0; - var thetaDelta = 0; - var scale = 1; - var pan = new THREE.Vector3(); - - var lastPosition = new THREE.Vector3(); - - var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; + var STATE = { NONE : -1, ROTATE : 0, SPEEDCHANGE : 1, PAN : 2 }; var state = STATE.NONE; // for reset - - this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); // events @@ -3060,29 +3290,44 @@ Potree.OrbitControls = function ( object, domElement ) { var changeEvent = { type: 'change' }; var startEvent = { type: 'start'}; var endEvent = { type: 'end'}; - - this.rotateLeft = function ( angle ) { - - if ( angle === undefined ) { - - angle = getAutoRotationAngle(); - + + this.setTrack = function(track){ + if(this.track !== track){ + this.track = track; + this.trackPos = 0; + } + }; + + this.setTrackPos = function(trackPos){ + + var newTrackPos = Math.max(0, Math.min(1, trackPos)); + var oldTrackPos = this.trackPos; + + var pStart = this.track.getPointAt(oldTrackPos); + var pEnd = this.track.getPointAt(newTrackPos); + var pDiff = pEnd.sub(pStart); + + this.trackPos = newTrackPos; + + if(newTrackPos !== oldTrackPos){ + var event = { + type: 'move', + translation: pan.clone() + }; + this.dispatchEvent(event); } + } + + this.getTrackPos = function(){ + return this.trackPos; + }; + this.rotateLeft = function ( angle ) { thetaDelta -= angle; - }; this.rotateUp = function ( angle ) { - - if ( angle === undefined ) { - - angle = getAutoRotationAngle(); - - } - phiDelta -= angle; - }; // pass in distance in world space to move left @@ -3111,17 +3356,31 @@ Potree.OrbitControls = function ( object, domElement ) { }; - // pass in x,y of change desired in pixel space, - // right and down are positive + // pass in distance in world space to move forward + this.panForward = function ( distance ) { + + if(this.track){ + this.setTrackPos( this.getTrackPos() - distance / this.track.getLength()); + }else{ + var te = this.object.matrix.elements; + + // get Y column of matrix + panOffset.set( te[ 8 ], te[ 9 ], te[ 10 ] ); + //panOffset.set( te[ 8 ], 0, te[ 10 ] ); + panOffset.multiplyScalar( distance ); + + pan.add( panOffset ); + } + }; + this.pan = function ( deltaX, deltaY ) { var element = scope.domElement === document ? scope.domElement.body : scope.domElement; if ( scope.object.fov !== undefined ) { - // perspective var position = scope.object.position; - var offset = position.clone().sub( scope.target ); + var offset = position.clone(); var targetDistance = offset.length(); // half of the fov is center to top of screen @@ -3130,98 +3389,95 @@ Potree.OrbitControls = function ( object, domElement ) { // we actually don't use screenWidth, since perspective camera is fixed to screen height scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); - } else if ( scope.object.top !== undefined ) { // orthographic scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); - } else { // camera neither orthographic or perspective - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); - + console.warn( 'WARNING: GeoControls.js encountered an unknown camera type - pan disabled.' ); } - }; - this.dollyIn = function ( dollyScale ) { - - if ( dollyScale === undefined ) { - - dollyScale = getZoomScale(); - + this.update = function (delta) { + this.object.rotation.order = 'ZYX'; + + var object = this.object; + + this.object = new THREE.Object3D(); + this.object.position.copy(object.position); + this.object.rotation.copy(object.rotation); + this.object.updateMatrix(); + this.object.updateMatrixWorld(); + + var position = this.object.position; + + if(delta !== undefined){ + + var multiplier = scope.shiftDown ? 4 : 1; + if(this.moveRight){ + this.panLeft(-delta * this.moveSpeed * multiplier); + } + if(this.moveLeft){ + this.panLeft(delta * this.moveSpeed * multiplier); + } + if(this.moveForward || this.moveForwardMouse){ + this.panForward(-delta * this.moveSpeed * multiplier); + } + if(this.moveBackward){ + this.panForward(delta * this.moveSpeed * multiplier); + } + if(this.rotLeft){ + scope.rotateLeft( -0.5 * Math.PI * delta / scope.rotateSpeed ); + } + if(this.rotRight){ + scope.rotateLeft( 0.5 * Math.PI * delta / scope.rotateSpeed ); + } + if(this.raiseCamera){ + //scope.rotateUp( -0.5 * Math.PI * delta / scope.rotateSpeed ); + scope.panUp( delta * this.moveSpeed * multiplier ); + } + if(this.lowerCamera){ + //scope.rotateUp( 0.5 * Math.PI * delta / scope.rotateSpeed ); + scope.panUp( -delta * this.moveSpeed * multiplier ); + } + } - - scale /= dollyScale; - - }; - - this.dollyOut = function ( dollyScale ) { - - if ( dollyScale === undefined ) { - - dollyScale = getZoomScale(); - + + if(!pan.equals(new THREE.Vector3(0,0,0))){ + var event = { + type: 'move', + translation: pan.clone() + }; + this.dispatchEvent(event); } - - scale *= dollyScale; - - }; - - this.update = function ( delta ) { - - var position = this.object.position.clone(); - - offset.copy( position ).sub( this.target ); - - // angle from z-axis around y-axis - - var theta = Math.atan2( offset.x, offset.z ); - - // angle from y-axis - - var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); - - if ( this.autoRotate ) { - - this.rotateLeft( getAutoRotationAngle() ); - + + position.add(pan); + + if(!(thetaDelta === 0.0 && phiDelta === 0.0)) { + var event = { + type: 'rotate', + thetaDelta: thetaDelta, + phiDelta: phiDelta + }; + this.dispatchEvent(event); } - - var progression = Math.min(1, this.fadeFactor * delta); - theta += progression * thetaDelta; - phi += progression * phiDelta; - - // restrict phi to be between desired limits - phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); - - // restrict phi to be betwee EPS and PI-EPS - phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); - - //var radius = offset.length() * scale; - var radius = offset.length(); - radius += (scale-1) * radius * progression; - - // restrict radius to be between desired limits - radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); + this.object.updateMatrix(); + var rot = new THREE.Matrix4().makeRotationY(thetaDelta); + var res = new THREE.Matrix4().multiplyMatrices(rot, this.object.matrix); + this.object.quaternion.setFromRotationMatrix(res); - // move target to panned location - this.target.add( pan.clone().multiplyScalar( progression ) ); - - offset.x = radius * Math.sin( phi ) * Math.sin( theta ); - offset.y = radius * Math.cos( phi ); - offset.z = radius * Math.sin( phi ) * Math.cos( theta ); - - position.copy( this.target ).add( offset ); + this.object.rotation.x += phiDelta; + this.object.updateMatrixWorld(); // send transformation proposal to listeners var proposeTransformEvent = { type: "proposeTransform", - oldPosition: this.object.position, - newPosition: position, + oldPosition: object.position, + newPosition: this.object.position, objections: 0, counterProposals: [] }; @@ -3229,105 +3485,75 @@ Potree.OrbitControls = function ( object, domElement ) { // check some counter proposals if transformation wasn't accepted if(proposeTransformEvent.objections > 0 ){ - if(proposeTransformEvent.counterProposals.length > 0){ var cp = proposeTransformEvent.counterProposals; - position.copy(cp[0]); + this.object.position.copy(cp[0]); proposeTransformEvent.objections = 0; proposeTransformEvent.counterProposals = []; } } - // apply transformation, if accepted if(proposeTransformEvent.objections > 0){ - thetaDelta = 0; - phiDelta = 0; - scale = 1; - pan.set(0,0,0); - }else{ - this.object.position.copy(position); - this.object.lookAt( this.target ); - - var attenuation = Math.max(0, 1 - this.fadeFactor * delta); - thetaDelta *= attenuation; - phiDelta *= attenuation; - scale = 1 + (scale-1) * attenuation; - pan.multiplyScalar( attenuation ); + }else{ + object.position.copy(this.object.position); } + + object.rotation.copy(this.object.rotation); + + this.object = object; - if ( lastPosition.distanceTo( this.object.position ) > 0 ) { + thetaDelta = 0; + phiDelta = 0; + scale = 1; + pan.set( 0, 0, 0 ); + if ( lastPosition.distanceTo( this.object.position ) > 0 ) { this.dispatchEvent( changeEvent ); lastPosition.copy( this.object.position ); - } - + + if(this.track){ + var pos = this.track.getPointAt(this.trackPos); + object.position.copy(pos); + } }; this.reset = function () { - state = STATE.NONE; - this.target.copy( this.target0 ); this.object.position.copy( this.position0 ); - - this.update(); - }; - function getAutoRotationAngle() { + function onMouseDown( event ) { + if ( scope.enabled === false ) return; + event.preventDefault(); - return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; - - } - - function getZoomScale() { - - return Math.pow( 0.95, scope.zoomSpeed ); - - } - - function onMouseDown( event ) { - - if ( scope.enabled === false ) return; - event.preventDefault(); - - if ( event.button === 0 ) { - if ( scope.noRotate === true ) return; - - state = STATE.ROTATE; + if ( event.button === 0 ) { + state = STATE.ROTATE; rotateStart.set( event.clientX, event.clientY ); - } else if ( event.button === 1 ) { - if ( scope.noZoom === true ) return; - - state = STATE.DOLLY; - - dollyStart.set( event.clientX, event.clientY ); - - } else if ( event.button === 2 ) { - if ( scope.noPan === true ) return; - state = STATE.PAN; - + panStart.set( event.clientX, event.clientY ); - + } else if ( event.button === 2 ) { + //state = STATE.PAN; + // + //panStart.set( event.clientX, event.clientY ); + scope.moveForwardMouse = true; } - scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); - scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); + //scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); + //scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); scope.dispatchEvent( startEvent ); - } function onMouseMove( event ) { - if ( scope.enabled === false ) return; event.preventDefault(); @@ -3335,9 +3561,6 @@ Potree.OrbitControls = function ( object, domElement ) { var element = scope.domElement === document ? scope.domElement.body : scope.domElement; if ( state === STATE.ROTATE ) { - - if ( scope.noRotate === true ) return; - rotateEnd.set( event.clientX, event.clientY ); rotateDelta.subVectors( rotateEnd, rotateStart ); @@ -3349,1157 +3572,2200 @@ Potree.OrbitControls = function ( object, domElement ) { rotateStart.copy( rotateEnd ); - } else if ( state === STATE.DOLLY ) { - - if ( scope.noZoom === true ) return; - - dollyEnd.set( event.clientX, event.clientY ); - dollyDelta.subVectors( dollyEnd, dollyStart ); - - if ( dollyDelta.y > 0 ) { - - scope.dollyIn(); - - } else { - - scope.dollyOut(); - - } - - dollyStart.copy( dollyEnd ); - } else if ( state === STATE.PAN ) { - - if ( scope.noPan === true ) return; - panEnd.set( event.clientX, event.clientY ); panDelta.subVectors( panEnd, panStart ); + //panDelta.multiplyScalar(this.moveSpeed).multiplyScalar(0.0001); + panDelta.multiplyScalar(0.002).multiplyScalar(scope.moveSpeed); scope.pan( panDelta.x, panDelta.y ); panStart.copy( panEnd ); - } - - //scope.update(); - } - function onMouseUp( /* event */ ) { - + function onMouseUp(event) { if ( scope.enabled === false ) return; - - scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); - scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); - scope.dispatchEvent( endEvent ); - state = STATE.NONE; - + + //console.log(event.which); + + if(event.button === 2){ + scope.moveForwardMouse = false; + }else{ + //scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); + //scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + } } - function onMouseWheel( event ) { - + function onMouseWheel(event) { if ( scope.enabled === false || scope.noZoom === true ) return; event.preventDefault(); - var delta = 0; - - if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 - - delta = event.wheelDelta; - - } else if ( event.detail !== undefined ) { // Firefox - - delta = - event.detail; - - } - - if ( delta > 0 ) { - - scope.dollyOut(); - - } else { - - scope.dollyIn(); + var direction = (event.detail<0 || event.wheelDelta>0) ? 1 : -1; + var moveSpeed = scope.moveSpeed + scope.moveSpeed * 0.1 * direction; + moveSpeed = Math.max(0.1, moveSpeed); - } + scope.setMoveSpeed(moveSpeed); - //scope.update(); scope.dispatchEvent( startEvent ); scope.dispatchEvent( endEvent ); - } + + this.setMoveSpeed = function(value){ + if(scope.moveSpeed !== value){ + scope.moveSpeed = value; + scope.dispatchEvent( { + type: "move_speed_changed", + controls: scope + }); + } + }; function onKeyDown( event ) { - - if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; + if ( scope.enabled === false) return; + + scope.shiftDown = event.shiftKey; + + switch ( event.keyCode ) { + case scope.keys.UP: scope.moveForward = true; break; + case scope.keys.BOTTOM: scope.moveBackward = true; break; + case scope.keys.LEFT: scope.moveLeft = true; break; + case scope.keys.RIGHT: scope.moveRight = true; break; + case scope.keys.W: scope.moveForward = true; break; + case scope.keys.S: scope.moveBackward = true; break; + case scope.keys.A: scope.moveLeft = true; break; + case scope.keys.D: scope.moveRight = true; break; + case scope.keys.Q: scope.rotLeft = true; break; + case scope.keys.E: scope.rotRight = true; break; + case scope.keys.R: scope.raiseCamera = true; break; + case scope.keys.F: scope.lowerCamera = true; break; + } + } + + function onKeyUp( event ) { + + scope.shiftDown = event.shiftKey; switch ( event.keyCode ) { + case scope.keys.W: scope.moveForward = false; break; + case scope.keys.S: scope.moveBackward = false; break; + case scope.keys.A: scope.moveLeft = false; break; + case scope.keys.D: scope.moveRight = false; break; + case scope.keys.UP: scope.moveForward = false; break; + case scope.keys.BOTTOM: scope.moveBackward = false; break; + case scope.keys.LEFT: scope.moveLeft = false; break; + case scope.keys.RIGHT: scope.moveRight = false; break; + case scope.keys.Q: scope.rotLeft = false; break; + case scope.keys.E: scope.rotRight = false; break; + case scope.keys.R: scope.raiseCamera = false; break; + case scope.keys.F: scope.lowerCamera = false; break; + } + } - case scope.keys.UP: - scope.pan( 0, scope.keyPanSpeed ); - //scope.update(); - break; + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox + + scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); + scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); - case scope.keys.BOTTOM: - scope.pan( 0, - scope.keyPanSpeed ); - //scope.update(); - break; + if(this.domElement.tabIndex === -1){ + this.domElement.tabIndex = 2222; + } + scope.domElement.addEventListener( 'keydown', onKeyDown, false ); + scope.domElement.addEventListener( 'keyup', onKeyUp, false ); - case scope.keys.LEFT: - scope.pan( scope.keyPanSpeed, 0 ); - //scope.update(); - break; +}; - case scope.keys.RIGHT: - scope.pan( - scope.keyPanSpeed, 0 ); - //scope.update(); - break; +Potree.GeoControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +/** + * @author mschuetz / http://mschuetz.at/ + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ +/*global THREE, console */ - } +// +// Adapted from THREE.OrbitControls +// - Smooth movements +// - creates "proposeTransform" events +// +// +// This set of controls performs orbiting, dollying (zooming), and panning. It maintains +// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is +// supported. +// +// Orbit - left mouse / touch: one finger move +// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish +// Pan - right mouse, or arrow keys / touch: three finter swipe +// +// This is a drop-in replacement for (most) TrackballControls used in examples. +// That is, include this js file and wherever you see: +// controls = new THREE.TrackballControls( camera ); +// controls.target.z = 150; +// Simple substitute "OrbitControls" and the control should work as-is. - } +Potree.OrbitControls = function ( object, domElement ) { - function touchstart( event ) { + this.object = object; + this.domElement = ( domElement !== undefined ) ? domElement : document; - if ( scope.enabled === false ) return; + // API - switch ( event.touches.length ) { + // Set to false to disable this control + this.enabled = true; - case 1: // one-fingered touch: rotate + // "target" sets the location of focus, where the control orbits around + // and where it pans with respect to. + this.target = new THREE.Vector3(); - if ( scope.noRotate === true ) return; + // center is old, deprecated; use "target" instead + this.center = this.target; - state = STATE.TOUCH_ROTATE; + // This option actually enables dollying in and out; left as "zoom" for + // backwards compatibility + this.noZoom = false; + this.zoomSpeed = 1.0; - rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - break; + // Limits to how far you can dolly in and out + this.minDistance = 0; + this.maxDistance = Infinity; - case 2: // two-fingered touch: dolly + // Set to true to disable this control + this.noRotate = false; + this.rotateSpeed = 1.0; - if ( scope.noZoom === true ) return; + // Set to true to disable this control + this.noPan = false; + this.keyPanSpeed = 7.0; // pixels moved per arrow key push - state = STATE.TOUCH_DOLLY; + // Set to true to automatically rotate around the target + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + this.fadeFactor = 10; - var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; - var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - var distance = Math.sqrt( dx * dx + dy * dy ); - dollyStart.set( 0, distance ); - break; + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians - case 3: // three-fingered touch: pan + // Set to true to disable use of the keys + this.noKeys = false; - if ( scope.noPan === true ) return; + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; - state = STATE.TOUCH_PAN; + //////////// + // internals - panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - break; + var scope = this; - default: + var EPS = 0.000001; - state = STATE.NONE; + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); - } + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + var panOffset = new THREE.Vector3(); - scope.dispatchEvent( startEvent ); + var offset = new THREE.Vector3(); - } + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); - function touchmove( event ) { + var phiDelta = 0; + var thetaDelta = 0; + var scale = 1; + var pan = new THREE.Vector3(); - if ( scope.enabled === false ) return; + var lastPosition = new THREE.Vector3(); - event.preventDefault(); - event.stopPropagation(); + var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; - var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + var state = STATE.NONE; - switch ( event.touches.length ) { + // for reset - case 1: // one-fingered touch: rotate + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); - if ( scope.noRotate === true ) return; - if ( state !== STATE.TOUCH_ROTATE ) return; + // events - rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - rotateDelta.subVectors( rotateEnd, rotateStart ); + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start'}; + var endEvent = { type: 'end'}; - // rotating across whole screen goes 360 degrees around - scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); - // rotating up and down along whole screen attempts to go 360, but limited to 180 - scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + this.rotateLeft = function ( angle ) { - rotateStart.copy( rotateEnd ); + if ( angle === undefined ) { - //scope.update(); - break; + angle = getAutoRotationAngle(); - case 2: // two-fingered touch: dolly + } - if ( scope.noZoom === true ) return; - if ( state !== STATE.TOUCH_DOLLY ) return; + thetaDelta -= angle; - var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; - var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - var distance = Math.sqrt( dx * dx + dy * dy ); + }; - dollyEnd.set( 0, distance ); - dollyDelta.subVectors( dollyEnd, dollyStart ); + this.rotateUp = function ( angle ) { - var ew = element.clientWidth; - var eh = element.clientHeight; - var diagonal = Math.sqrt(ew * ew + eh * eh); - var delta = dollyDelta.y / diagonal; + if ( angle === undefined ) { - if ( dollyDelta.y > 0 ) { + angle = getAutoRotationAngle(); - scope.dollyOut(1 - delta); + } - } else { + phiDelta -= angle; - scope.dollyIn(1 + delta); + }; - } + // pass in distance in world space to move left + this.panLeft = function ( distance ) { - dollyStart.copy( dollyEnd ); + var te = this.object.matrix.elements; - //scope.update(); - break; + // get X column of matrix + panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + panOffset.multiplyScalar( - distance ); + + pan.add( panOffset ); - case 3: // three-fingered touch: pan + }; - if ( scope.noPan === true ) return; - if ( state !== STATE.TOUCH_PAN ) return; + // pass in distance in world space to move up + this.panUp = function ( distance ) { - panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); - panDelta.subVectors( panEnd, panStart ); - - scope.pan( panDelta.x, panDelta.y ); + var te = this.object.matrix.elements; - panStart.copy( panEnd ); + // get Y column of matrix + panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + panOffset.multiplyScalar( distance ); + + pan.add( panOffset ); - //scope.update(); - break; + }; + + // pass in x,y of change desired in pixel space, + // right and down are positive + this.pan = function ( deltaX, deltaY ) { - default: + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - state = STATE.NONE; + if ( scope.object.fov !== undefined ) { + + // perspective + var position = scope.object.position; + var offset = position.clone().sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we actually don't use screenWidth, since perspective camera is fixed to screen height + scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); + scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); + + } else if ( scope.object.top !== undefined ) { + + // orthographic + scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); + scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); + + } else { + + // camera neither orthographic or perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); } - } + }; - function touchend( /* event */ ) { + this.dollyIn = function ( dollyScale ) { - if ( scope.enabled === false ) return; + if ( dollyScale === undefined ) { - scope.dispatchEvent( endEvent ); - state = STATE.NONE; + dollyScale = getZoomScale(); - } + } - this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); - this.domElement.addEventListener( 'mousedown', onMouseDown, false ); - this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); - this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox + scale /= dollyScale; - this.domElement.addEventListener( 'touchstart', touchstart, false ); - this.domElement.addEventListener( 'touchend', touchend, false ); - this.domElement.addEventListener( 'touchmove', touchmove, false ); + }; - window.addEventListener( 'keydown', onKeyDown, false ); + this.dollyOut = function ( dollyScale ) { -}; + if ( dollyScale === undefined ) { -Potree.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + dollyScale = getZoomScale(); + } -/** - * @author mschuetz / http://mschuetz.at - * - * - * Navigation similar to Google Earth. - * - * left mouse: Drag with respect to intersection - * wheel: zoom towards/away from intersection - * right mouse: Rotate camera around intersection - * - * - */ + scale *= dollyScale; -THREE.EarthControls = function ( camera, renderer, scene ) { - this.camera = camera; - this.renderer = renderer; - this.pointclouds = []; - this.domElement = renderer.domElement; - this.scene = scene; - - // Set to false to disable this control - this.enabled = true; + }; - var scope = this; - + this.update = function ( delta ) { - var STATE = { NONE : -1, DRAG : 0, ROTATE: 1 }; + var position = this.object.position.clone(); - var state = STATE.NONE; - - var dragStart = new THREE.Vector2(); - var dragEnd = new THREE.Vector2(); - - var sphereGeometry = new THREE.SphereGeometry(1, 32, 32); - var sphereMaterial = new THREE.MeshNormalMaterial({shading: THREE.SmoothShading, transparent: true, opacity: 0.5}); - this.pivotNode = new THREE.Mesh(sphereGeometry, sphereMaterial); + offset.copy( position ).sub( this.target ); - var mouseDelta = new THREE.Vector2(); - - var camStart = null; - var pivot = null; - - - this.minAngle = (10 / 180) * Math.PI; // 10° - this.maxAngle = (70 / 180) * Math.PI; // 70° + // angle from z-axis around y-axis - this.update = function (delta) { - var position = this.camera.position; - this.camera.updateMatrixWorld(); - - var proposal = new THREE.Object3D(); - proposal.position.copy(this.camera.position); - proposal.rotation.copy(this.camera.rotation); - proposal.updateMatrix(); - proposal.updateMatrixWorld(); + var theta = Math.atan2( offset.x, offset.z ); + + // angle from y-axis + + var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); + + if ( this.autoRotate ) { + + this.rotateLeft( getAutoRotationAngle() ); + + } + + var progression = Math.min(1, this.fadeFactor * delta); - if(pivot){ - if(state === STATE.DRAG){ - var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(new THREE.Vector3(0, 1, 0), pivot); - var mouse = { - x: ( dragEnd.x / this.domElement.clientWidth ) * 2 - 1, - y: - ( dragEnd.y / this.domElement.clientHeight ) * 2 + 1 - }; - - var vec = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); - vec.unproject(camStart); - var dir = vec.sub(camStart.position).normalize(); - - var ray = new THREE.Ray(camStart.position, dir); - var distanceToPlane = ray.distanceToPlane(plane); - - if(distanceToPlane > 0){ - var newCamPos = new THREE.Vector3().subVectors(pivot, dir.clone().multiplyScalar(distanceToPlane)); - proposal.position.copy(newCamPos); - } - - - }else if(state === STATE.ROTATE){ - // rotate around pivot point - - var diff = mouseDelta.clone().multiplyScalar(delta); - diff.x *= 0.3; - diff.y *= 0.2; - + theta += progression * thetaDelta; + phi += progression * phiDelta; - // do calculations on fresh nodes - var p = new THREE.Object3D(); - var c = new THREE.Object3D(); - p.add(c); - p.position.copy(pivot); - c.position.copy(this.camera.position).sub(pivot); - c.rotation.copy(this.camera.rotation); - - - // rotate left/right - p.rotation.y += -diff.x; - - - // rotate up/down - var dir = this.camera.getWorldDirection(); - var up = new THREE.Vector3(0,1,0); - var side = new THREE.Vector3().crossVectors(up, dir); + // restrict phi to be between desired limits + phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); - var dirp = c.position.clone(); - dirp.y = 0; - dirp.normalize(); - var ac = dirp.dot(c.position.clone().normalize()); - var angle = Math.acos(ac); - if(c.position.y < 0){ - angle = -angle; - } - - var amount = 0; - if(diff.y > 0){ - // rotate downwards and apply minAngle limit - amount = diff.y - Math.max(0, this.minAngle - (angle - diff.y)); - }else{ - // rotate upwards and apply maxAngle limit - amount = diff.y + Math.max(0, (angle - diff.y) - this.maxAngle); - } - p.rotateOnAxis(side, -amount); - - // apply changes to object - p.updateMatrixWorld(); - - proposal.position.copy(c.getWorldPosition()); - proposal.quaternion.copy(c.getWorldQuaternion()); + // restrict phi to be betwee EPS and PI-EPS + phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); - } - - var proposeTransformEvent = { - type: "proposeTransform", - oldPosition: this.camera.position, - newPosition: proposal.position, - objections: 0 - }; - this.dispatchEvent(proposeTransformEvent); + //var radius = offset.length() * scale; + var radius = offset.length(); + radius += (scale-1) * radius * progression; + + // restrict radius to be between desired limits + radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); + + // move target to panned location + this.target.add( pan.clone().multiplyScalar( progression ) ); + + offset.x = radius * Math.sin( phi ) * Math.sin( theta ); + offset.y = radius * Math.cos( phi ); + offset.z = radius * Math.sin( phi ) * Math.cos( theta ); + + position.copy( this.target ).add( offset ); + + // send transformation proposal to listeners + var proposeTransformEvent = { + type: "proposeTransform", + oldPosition: this.object.position, + newPosition: position, + objections: 0, + counterProposals: [] + }; + this.dispatchEvent(proposeTransformEvent); + + // check some counter proposals if transformation wasn't accepted + if(proposeTransformEvent.objections > 0 ){ - if(proposeTransformEvent.objections > 0){ + if(proposeTransformEvent.counterProposals.length > 0){ + var cp = proposeTransformEvent.counterProposals; + position.copy(cp[0]); - }else{ - this.camera.position.copy(proposal.position); - this.camera.rotation.copy(proposal.rotation); + proposeTransformEvent.objections = 0; + proposeTransformEvent.counterProposals = []; } - - var wp = this.pivotNode.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); - var w = Math.abs(wp.z / 30); - var l = this.pivotNode.scale.length(); - this.pivotNode.scale.multiplyScalar(w / l); } + // apply transformation, if accepted + if(proposeTransformEvent.objections > 0){ + thetaDelta = 0; + phiDelta = 0; + scale = 1; + pan.set(0,0,0); + }else{ + this.object.position.copy(position); + this.object.lookAt( this.target ); - mouseDelta.set(0,0); + var attenuation = Math.max(0, 1 - this.fadeFactor * delta); + + thetaDelta *= attenuation; + phiDelta *= attenuation; + scale = 1 + (scale-1) * attenuation; + pan.multiplyScalar( attenuation ); + } + + if ( lastPosition.distanceTo( this.object.position ) > 0 ) { + + this.dispatchEvent( changeEvent ); + + lastPosition.copy( this.object.position ); + + } + }; this.reset = function () { + state = STATE.NONE; - this.camera.position.copy( this.position0 ); + this.target.copy( this.target0 ); + this.object.position.copy( this.position0 ); + + this.update(); + }; + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + function onMouseDown( event ) { + if ( scope.enabled === false ) return; event.preventDefault(); - - var rect = scope.domElement.getBoundingClientRect(); - - var mouse = { - x: ( (event.clientX - rect.left) / scope.domElement.clientWidth ) * 2 - 1, - y: - ( (event.clientY - rect.top) / scope.domElement.clientHeight ) * 2 + 1 - }; - var I = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, scope.pointclouds); - if(!I){ - return; - } - var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(new THREE.Vector3(0, 1, 0), I); - - var vec = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); - vec.unproject(scope.camera); - var dir = vec.sub(scope.camera.position).normalize(); - - var ray = new THREE.Ray(scope.camera.position, dir); - pivot = ray.intersectPlane(plane); - - //pivot = I; - camStart = scope.camera.clone(); - camStart.rotation.copy(scope.camera.rotation); - dragStart.set( event.clientX - rect.left, event.clientY - rect.top); - dragEnd.set(event.clientX - rect.left, event.clientY - rect.top); - - - scope.scene.add(scope.pivotNode); - scope.pivotNode.position.copy(pivot); + if ( event.button === THREE.MOUSE.LEFT ) { + if ( scope.noRotate === true ) return; - if ( event.button === 0 ) { - state = STATE.DRAG; - } else if ( event.button === 2 ) { state = STATE.ROTATE; + + rotateStart.set( event.clientX, event.clientY ); + + } else if ( event.button === THREE.MOUSE.MIDDLE ) { + if ( scope.noZoom === true ) return; + + state = STATE.DOLLY; + + dollyStart.set( event.clientX, event.clientY ); + + } else if ( event.button === THREE.MOUSE.RIGHT ) { + if ( scope.noPan === true ) return; + + state = STATE.PAN; + + panStart.set( event.clientX, event.clientY ); + } - + scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( startEvent ); + } function onMouseMove( event ) { + if ( scope.enabled === false ) return; event.preventDefault(); - - var rect = scope.domElement.getBoundingClientRect(); var element = scope.domElement === document ? scope.domElement.body : scope.domElement; - mouseDelta.set(event.clientX - rect.left - dragEnd.x, event.clientY - rect.top - dragEnd.y); - dragEnd.set(event.clientX - rect.left, event.clientY - rect.top); - - } + if ( state === STATE.ROTATE ) { - function onMouseUp() { - if ( scope.enabled === false ) return; + if ( scope.noRotate === true ) return; - scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); - scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); - state = STATE.NONE; - - //scope.dragStartIndicator.style.display = "none"; - scope.scene.remove(scope.pivotNode); + rotateEnd.set( event.clientX, event.clientY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); - } + // rotating across whole screen goes 360 degrees around + scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); - function onMouseWheel(event) { - if ( scope.enabled === false || scope.noZoom === true ) return; + // rotating up and down along whole screen attempts to go 360, but limited to 180 + scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); - event.preventDefault(); - - var rect = scope.domElement.getBoundingClientRect(); + rotateStart.copy( rotateEnd ); - var amount = (event.detail<0 || event.wheelDelta>0) ? 1 : -1; - var mouse = { - x: ( (event.clientX - rect.left) / scope.domElement.clientWidth ) * 2 - 1, - y: - ( (event.clientY - rect.top) / scope.domElement.clientHeight ) * 2 + 1 - }; - var I = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, scope.pointclouds); - - if(I){ - var distance = I.distanceTo(scope.camera.position); - var dir = new THREE.Vector3().subVectors(I, scope.camera.position).normalize(); - scope.camera.position.add(dir.multiplyScalar(distance * 0.1 * amount)); - } + } else if ( state === STATE.DOLLY ) { - } + if ( scope.noZoom === true ) return; - this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); - this.domElement.addEventListener( 'mousedown', onMouseDown, false ); - this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); - this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox -}; + dollyEnd.set( event.clientX, event.clientY ); + dollyDelta.subVectors( dollyEnd, dollyStart ); -THREE.EarthControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + if ( dollyDelta.y > 0 ) { + scope.dollyIn(); -/** - * - * @param node - * @class an item in the lru list. - */ -function LRUItem(node){ - this.previous = null; - this.next = null; - this.node = node; -} + } else { -/** - * - * @class A doubly-linked-list of the least recently used elements. - */ -function LRU(){ - // the least recently used item - this.first = null; - // the most recently used item - this.last = null; - // a list of all items in the lru list - this.items = {}; - this.elements = 0; - this.numPoints = 0; -} + scope.dollyOut(); -/** - * number of elements in the list - * - * @returns {Number} - */ -LRU.prototype.size = function(){ - return this.elements; -}; + } -LRU.prototype.contains = function(node){ - return this.items[node.id] == null; -}; + dollyStart.copy( dollyEnd ); -/** - * makes node the most recently used item. if the list does not contain node, it will be added. - * - * @param node - */ -LRU.prototype.touch = function(node){ - if(!node.loaded){ - return; - } + } else if ( state === STATE.PAN ) { - var item; - if(this.items[node.id] == null){ - // add to list - item = new LRUItem(node); - item.previous = this.last; - this.last = item; - if(item.previous !== null){ - item.previous.next = item; - } - - this.items[node.id] = item; - this.elements++; - - if(this.first === null){ - this.first = item; - } - this.numPoints += node.numPoints; - }else{ - // update in list - item = this.items[node.id]; - if(item.previous === null){ - // handle touch on first element - if(item.next !== null){ - this.first = item.next; - this.first.previous = null; - item.previous = this.last; - item.next = null; - this.last = item; - item.previous.next = item; - } - }else if(item.next === null){ - // handle touch on last element - }else{ - // handle touch on any other element - item.previous.next = item.next; - item.next.previous = item.previous; - item.previous = this.last; - item.next = null; - this.last = item; - item.previous.next = item; - } - - - } -}; + if ( scope.noPan === true ) return; -///** -// * removes the least recently used item from the list and returns it. -// * if the list was empty, null will be returned. -// */ -//LRU.prototype.remove = function remove(){ -// if(this.first === null){ -// return null; -// } -// var lru = this.first; -// -// // if the lru list contains at least 2 items, the item after the least recently used elemnt will be the new lru item. -// if(lru.next !== null){ -// this.first = lru.next; -// this.first.previous = null; -// }else{ -// this.first = null; -// this.last = null; -// } -// -// delete this.items[lru.node.id]; -// this.elements--; -// this.numPoints -= lru.node.numPoints; -// -//// Logger.info("removed node: " + lru.node.id); -// return lru.node; -//}; + panEnd.set( event.clientX, event.clientY ); + panDelta.subVectors( panEnd, panStart ); + + scope.pan( panDelta.x, panDelta.y ); -LRU.prototype.remove = function remove(node){ + panStart.copy( panEnd ); - var lruItem = this.items[node.id]; - if(lruItem){ - - if(this.elements === 1){ - this.first = null; - this.last = null; - }else{ - if(!lruItem.previous){ - this.first = lruItem.next; - this.first.previous = null; - } - if(!lruItem.next){ - this.last = lruItem.previous; - this.last.next = null; - } - if(lruItem.previous &&lruItem.next){ - lruItem.previous.next = lruItem.next; - lruItem.next.previous = lruItem.previous; - } } - - delete this.items[node.id]; - this.elements--; - this.numPoints -= node.numPoints; + + //scope.update(); + } - -}; -LRU.prototype.getLRUItem = function(){ - if(this.first === null){ - return null; + function onMouseUp( /* event */ ) { + + if ( scope.enabled === false ) return; + + scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); + scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + } - var lru = this.first; - - return lru.node; -}; -LRU.prototype.toString = function(){ - var string = "{ "; - var curr = this.first; - while(curr !== null){ - string += curr.node.id; - if(curr.next !== null){ - string += ", "; + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.noZoom === true ) return; + + event.preventDefault(); + + var delta = 0; + + if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 + + delta = event.wheelDelta; + + } else if ( event.detail !== undefined ) { // Firefox + + delta = - event.detail; + } - curr = curr.next; - } - string += "}"; - string += "(" + this.size() + ")"; - return string; -}; -LRU.prototype.freeMemory = function(){ - if(this.elements <= 1){ - return; - } + if ( delta > 0 ) { - while(this.numPoints > Potree.pointLoadLimit){ - var element = this.first; - var node = element.node; - this.disposeDescendants(node); - - } -}; + scope.dollyOut(); + + } else { + + scope.dollyIn(); -LRU.prototype.disposeDescendants = function(node){ - var stack = []; - stack.push(node); - while(stack.length > 0){ - var current = stack.pop(); - - //console.log(current); - - current.dispose(); - this.remove(current); - - for(var key in current.children){ - if(current.children.hasOwnProperty(key)){ - var child = current.children[key]; - if(child.loaded){ - stack.push(current.children[key]); - } - } } + + //scope.update(); + scope.dispatchEvent( startEvent ); + scope.dispatchEvent( endEvent ); + } -}; -Potree.Annotation = function(viewer, args){ - var scope = this; - - Potree.Annotation.counter++; - - this.viewer = viewer; - this.ordinal = args.ordinal || Potree.Annotation.counter; - this.title = args.title || "No Title"; - this.description = args.description || ""; - this.scene = args.scene || null; - this.position = args.position || new THREE.Vector3(0,0,0); - this.cameraPosition = args.cameraPosition; - this.cameraTarget = args.cameraTarget || this.position; - this.view = args.view || null; - this.keepOpen = false; - - this.domElement = document.createElement("div"); - this.domElement.style.position = "fixed"; - this.domElement.style.opacity = "0.5"; - this.domElement.className = "annotation"; - this.elOrdinal = document.createElement("div"); - this.elOrdinal.style.position = "absolute"; - this.elOrdinal.style.width = "1.5em"; - this.elOrdinal.style.height = "1.5em"; - this.elOrdinal.style.color = "white"; - this.elOrdinal.style.backgroundColor = "black"; - this.elOrdinal.style.borderRadius = "1.5em"; - this.elOrdinal.style.fontSize = "1em"; - this.elOrdinal.style.opacity = "1"; - this.elOrdinal.style.zIndex = "100"; - this.domElement.appendChild(this.elOrdinal); - this.elOrdinal.onmouseenter = function(){ - //scope.openBar(); - }; - this.elOrdinal.onmouseleave = function(){ - - }; - this.elOrdinal.onclick = function(){ - scope.moveHere(scope.viewer.camera); - //scope.openBar(); - //scope.keepOpen = true; - }; - this.domElement.onmouseleave = function(){ - //scope.closeBar(); - }; + function onKeyDown( event ) { - - this.elOrdinalText = document.createElement("span"); - this.elOrdinalText.style.display = "inline-block"; - this.elOrdinalText.style.verticalAlign = "middle"; - this.elOrdinalText.style.lineHeight = "1.5em"; - this.elOrdinalText.style.textAlign = "center"; - this.elOrdinalText.style.width = "100%"; - //this.elOrdinalText.style.fontWeight = "bold"; - this.elOrdinalText.style.fontFamily = "Arial"; - this.elOrdinalText.style.cursor = "default"; - this.elOrdinalText.innerHTML = this.ordinal; - this.elOrdinalText.userSelect = "none"; - this.elOrdinal.appendChild(this.elOrdinalText); - - - this.elButtons = document.createElement("div"); - this.elButtons.style.position = "absolute"; - this.elButtons.style.display = "block"; - this.elButtons.style.height = "1.5em"; - this.elButtons.style.backgroundColor = "#333333"; - this.elButtons.style.zIndex = "50"; - this.elButtons.style.borderRadius = "1.5em 1.5em 1.5em 1.5em"; - this.elButtons.style.cursor = "default"; - this.elButtons.style.padding = "0.0em 0.0em 0em 1.5em"; - this.elButtons.style.whiteSpace = "nowrap"; - this.domElement.appendChild(this.elButtons); - - - if(this.description){ - this.elButtonsInfo = document.createElement("img"); - this.elButtonsInfo.src = "../resources/icons/info_32x32.png"; - this.elButtonsInfo.style.width = "1.5em"; - this.elButtonsInfo.style.padding = "0em 0em 0em 0.2em"; - this.elButtons.appendChild(this.elButtonsInfo); - this.elButtonsInfo.onclick = function(){ - scope.openDescriptionWindow(); - }; - } - - if(this.scene){ - this.elButtonsScene = document.createElement("img"); - this.elButtonsScene.src = "../resources/icons/goto_32x32.png"; - this.elButtonsScene.style.width = "1.5em"; - this.elButtonsScene.style.padding = "0em 0em 0em 0.2em"; - this.elButtonsScene.onclick = function(){loadScene(scope.scene)}; + if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; - this.elButtons.appendChild(this.elButtonsScene); - } - - this.domElement.onmouseenter = function(){ - scope.domElement.style.opacity = "0.8"; - }; - this.domElement.onmouseleave = function(){ - scope.domElement.style.opacity = "0.5"; - }; - - this.openBar = function(){ - scope.elOrdinal.style.opacity = 0.8; - scope.elButtons.style.display = "block"; - }; - - this.closeBar = function(){ - if(!this.keepOpen){ - scope.elOrdinal.style.opacity = 0.5; - scope.elButtons.style.display = "none"; - } - }; - - this.openDescriptionWindow = function(){ - if(this.elDescription){ - this.elDescription.style.display = "block"; - }else{ - this.elDescription = document.createElement("div"); - this.elDescription.className = "description"; - - this.elDescriptionHeader = document.createElement("div"); - this.elDescriptionHeader.className = "description-header"; - this.elDescription.appendChild(this.elDescriptionHeader); - - this.elDescriptionTitle = document.createElement("span"); - this.elDescriptionTitle.className = "description-title"; - this.elDescriptionTitle.innerHTML = scope.title; - this.elDescriptionHeader.appendChild(this.elDescriptionTitle); - - this.elDescriptionButtons = document.createElement("span"); - this.elDescriptionButtons.className = "description-buttons"; - this.elDescriptionHeader.appendChild(this.elDescriptionButtons); - - this.elDescriptionClose = document.createElement("img"); - this.elDescriptionClose.src = "../resources/icons/close_32x32_black.png"; - this.elDescriptionClose.onmouseenter = function(){this.src = '../resources/icons/close_32x32_black_shadow.png'}; - this.elDescriptionClose.onmouseleave = function(){this.src = '../resources/icons/close_32x32_black.png'}; - this.elDescriptionClose.style.height = "1.5em"; - this.elDescriptionClose.onclick = function(){ - scope.elDescription.style.display = "none"; - }; - this.elDescriptionButtons.appendChild(this.elDescriptionClose); - - this.elDescriptionContent = document.createElement("div"); - this.elDescriptionContent.className = "description-content"; - this.elDescriptionContent.innerHTML = this.description; - this.elDescription.appendChild(this.elDescriptionContent); - - document.body.appendChild(this.elDescription); - } + switch ( event.keyCode ) { - }; - - this.moveHere = function(camera){ - var animationDuration = 800; - var easing = TWEEN.Easing.Quartic.Out; + case scope.keys.UP: + scope.pan( 0, scope.keyPanSpeed ); + //scope.update(); + break; - // animate camera position - var tween = new TWEEN.Tween(camera.position).to(scope.cameraPosition, animationDuration); - tween.easing(easing); - tween.start(); - - // animate camera target - var camTargetDistance = camera.position.distanceTo(scope.cameraTarget); - var target = new THREE.Vector3().addVectors( - camera.position, - camera.getWorldDirection().clone().multiplyScalar(camTargetDistance) - ); - var tween = new TWEEN.Tween(target).to(scope.cameraTarget, animationDuration); - tween.easing(easing); - tween.onUpdate(function(){ - camera.lookAt(target); - scope.viewer.orbitControls.target.copy(target); - }); - tween.onComplete(function(){ - camera.lookAt(target); - scope.viewer.orbitControls.target.copy(target); - }); + case scope.keys.BOTTOM: + scope.pan( 0, - scope.keyPanSpeed ); + //scope.update(); + break; - tween.start(); - }; - - this.dispose = function(){ - if(this.descriptionDialog){ - var id = "annotation_description_" + scope.ordinal; - $( ("#" + id) ).dialog('destroy'); - } - - if(this.domElement.parentElement){ - this.domElement.parentElement.removeChild(this.domElement); - } - - if(this.elDescription){ - if(this.elDescription.parentElement){ - this.elDescription.parentElement.removeChild(this.elDescription); - } - } - }; -}; + case scope.keys.LEFT: + scope.pan( scope.keyPanSpeed, 0 ); + //scope.update(); + break; -Potree.Annotation.counter = 0; + case scope.keys.RIGHT: + scope.pan( - scope.keyPanSpeed, 0 ); + //scope.update(); + break; -Potree.ProfileData = function(profile){ - this.profile = profile; - - this.segments = []; - this.boundingBox = new THREE.Box3(); - this.projectedBoundingBox = new THREE.Box2(); - - var mileage = new THREE.Vector3(); - for(var i = 0; i < profile.points.length - 1; i++){ - var start = profile.points[i]; - var end = profile.points[i+1]; - - var center = new THREE.Vector3().addVectors(end, start).multiplyScalar(0.5); - var length = new THREE.Vector3(start.x, 0, start.z).distanceTo(new THREE.Vector3(end.x, 0, end.z)); - var side = new THREE.Vector3().subVectors(end, start).normalize(); - var up = new THREE.Vector3(0, 1, 0); - var forward = new THREE.Vector3().crossVectors(side, up).normalize(); - var N = forward; - var cutPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(N, start); - var halfPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(side, center); - - var project = function(_start, _end, _mileage){ - var start = _start; - var end = _end; - var mileage = _mileage; - - var xAxis = new THREE.Vector3(1,0,0); - var dir = new THREE.Vector3().subVectors(end, start); - dir.y = 0; - dir.normalize(); - var alpha = Math.acos(xAxis.dot(dir)); - if(dir.z > 0){ - alpha = -alpha; - } - - - return function(position){ - var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, 0, -start.z); - var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); - var applyMileage = new THREE.Matrix4().makeTranslation(mileage.x, 0, 0); + } - var pos = position.clone(); - pos.applyMatrix4(toOrigin); - pos.applyMatrix4(alignWithX); - pos.applyMatrix4(applyMileage); - - return pos; - }; - - }(start, end, mileage.clone()); - - var segment = { - start: start, - end: end, - cutPlane: cutPlane, - halfPlane: halfPlane, - length: length, - points: null, - project: project - }; - - this.segments.push(segment); - - mileage.x += new THREE.Vector3(start.x, 0, start.z).distanceTo(new THREE.Vector3(end.x, 0, end.z)); - mileage.y += end.y - start.y; } - - this.projectedBoundingBox.min.x = 0; - this.projectedBoundingBox.min.y = Number.POSITIVE_INFINITY; - this.projectedBoundingBox.max.x = mileage.x; - this.projectedBoundingBox.max.y = Number.NEGATIVE_INFINITY; - - this.size = function(){ - var size = 0; - for(var i = 0; i < this.segments.length; i++){ - if(this.segments[i].points){ - size += this.segments[i].points.numPoints; - } + + function touchstart( event ) { + + if ( scope.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.noRotate === true ) return; + + state = STATE.TOUCH_ROTATE; + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.noZoom === true ) return; + + state = STATE.TOUCH_DOLLY; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + dollyStart.set( 0, distance ); + break; + + case 3: // three-fingered touch: pan + + if ( scope.noPan === true ) return; + + state = STATE.TOUCH_PAN; + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + default: + + state = STATE.NONE; + } - return size; + + scope.dispatchEvent( startEvent ); + } - -}; -Potree.ProfileRequest = function(pointcloud, profile, maxDepth, callback){ + function touchmove( event ) { - this.pointcloud = pointcloud; - this.profile = profile; - this.maxDepth = maxDepth || Number.MAX_VALUE; - this.callback = callback; - this.temporaryResult = new Potree.ProfileData(this.profile); - this.pointsServed = 0; + if ( scope.enabled === false ) return; - this.priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); - - this.initialize = function(){ - this.priorityQueue.push({node: pointcloud.pcoGeometry.root, weight: 1}); - this.traverse(pointcloud.pcoGeometry.root); - }; - - // traverse the node and add intersecting descendants to queue - this.traverse = function(node){ - - var stack = []; - for(var i = 0; i < 8; i++){ - var child = node.children[i]; - if(child && pointcloud.nodeIntersectsProfile(child, this.profile)){ - stack.push(child); - } + event.preventDefault(); + event.stopPropagation(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.noRotate === true ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + // rotating across whole screen goes 360 degrees around + scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + // rotating up and down along whole screen attempts to go 360, but limited to 180 + scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + //scope.update(); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.noZoom === true ) return; + if ( state !== STATE.TOUCH_DOLLY ) return; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + dollyDelta.subVectors( dollyEnd, dollyStart ); + + var ew = element.clientWidth; + var eh = element.clientHeight; + var diagonal = Math.sqrt(ew * ew + eh * eh); + var delta = dollyDelta.y / diagonal; + + if ( dollyDelta.y > 0 ) { + + scope.dollyOut(1 - delta); + + } else { + + scope.dollyIn(1 + delta); + + } + + dollyStart.copy( dollyEnd ); + + //scope.update(); + break; + + case 3: // three-fingered touch: pan + + if ( scope.noPan === true ) return; + if ( state !== STATE.TOUCH_PAN ) return; + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + panDelta.subVectors( panEnd, panStart ); + + scope.pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + //scope.update(); + break; + + default: + + state = STATE.NONE; + + } + + } + + function touchend( /* event */ ) { + + if ( scope.enabled === false ) return; + + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + + } + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox + + this.domElement.addEventListener( 'touchstart', touchstart, false ); + this.domElement.addEventListener( 'touchend', touchend, false ); + this.domElement.addEventListener( 'touchmove', touchmove, false ); + + if(this.domElement.tabIndex === -1){ + this.domElement.tabIndex = 2222; + } + this.domElement.addEventListener( 'keydown', onKeyDown, false ); + +}; + +Potree.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + + +/** + * @author mschuetz / http://mschuetz.at + * + * + * Navigation similar to Google Earth. + * + * left mouse: Drag with respect to intersection + * wheel: zoom towards/away from intersection + * right mouse: Rotate camera around intersection + * + * + */ + +THREE.EarthControls = function ( camera, renderer, scene ) { + this.camera = camera; + this.renderer = renderer; + this.pointclouds = []; + this.domElement = renderer.domElement; + this.scene = scene; + + // Set to false to disable this control + this.enabled = true; + + var scope = this; + + + var STATE = { NONE : -1, DRAG : 0, ROTATE: 1 }; + + var state = STATE.NONE; + + var dragStart = new THREE.Vector2(); + var dragEnd = new THREE.Vector2(); + + var sphereGeometry = new THREE.SphereGeometry(1, 32, 32); + var sphereMaterial = new THREE.MeshNormalMaterial({shading: THREE.SmoothShading, transparent: true, opacity: 0.5}); + this.pivotNode = new THREE.Mesh(sphereGeometry, sphereMaterial); + + var mouseDelta = new THREE.Vector2(); + + var camStart = null; + var pivot = null; + + + this.minAngle = (10 / 180) * Math.PI; // 10° + this.maxAngle = (70 / 180) * Math.PI; // 70° + + this.update = function (delta) { + var position = this.camera.position; + this.camera.updateMatrixWorld(); + + var proposal = new THREE.Object3D(); + proposal.position.copy(this.camera.position); + proposal.rotation.copy(this.camera.rotation); + proposal.updateMatrix(); + proposal.updateMatrixWorld(); + + if(pivot){ + if(state === STATE.DRAG){ + var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(new THREE.Vector3(0, 1, 0), pivot); + var mouse = { + x: ( dragEnd.x / this.domElement.clientWidth ) * 2 - 1, + y: - ( dragEnd.y / this.domElement.clientHeight ) * 2 + 1 + }; + + var vec = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); + vec.unproject(camStart); + var dir = vec.sub(camStart.position).normalize(); + + var ray = new THREE.Ray(camStart.position, dir); + var distanceToPlane = ray.distanceToPlane(plane); + + if(distanceToPlane > 0){ + var newCamPos = new THREE.Vector3().subVectors(pivot, dir.clone().multiplyScalar(distanceToPlane)); + proposal.position.copy(newCamPos); + } + + + }else if(state === STATE.ROTATE){ + // rotate around pivot point + + var diff = mouseDelta.clone().multiplyScalar(delta); + diff.x *= 0.3; + diff.y *= -0.2; + + + // do calculations on fresh nodes + var p = new THREE.Object3D(); + var c = new THREE.Object3D(); + p.add(c); + p.position.copy(pivot); + c.position.copy(this.camera.position).sub(pivot); + c.rotation.copy(this.camera.rotation); + + + // rotate left/right + p.rotation.y += -diff.x; + + + // rotate up/down + var dir = this.camera.getWorldDirection(); + var up = new THREE.Vector3(0,1,0); + var side = new THREE.Vector3().crossVectors(up, dir); + + var dirp = c.position.clone(); + dirp.y = 0; + dirp.normalize(); + var ac = dirp.dot(c.position.clone().normalize()); + var angle = Math.acos(ac); + if(c.position.y < 0){ + angle = -angle; + } + + var amount = 0; + if(diff.y > 0){ + // rotate downwards and apply minAngle limit + amount = diff.y - Math.max(0, this.minAngle - (angle - diff.y)); + }else{ + // rotate upwards and apply maxAngle limit + amount = diff.y + Math.max(0, (angle - diff.y) - this.maxAngle); + } + p.rotateOnAxis(side, -amount); + + // apply changes to object + p.updateMatrixWorld(); + + proposal.position.copy(c.getWorldPosition()); + proposal.quaternion.copy(c.getWorldQuaternion()); + + } + + var proposeTransformEvent = { + type: "proposeTransform", + oldPosition: this.camera.position, + newPosition: proposal.position, + objections: 0 + }; + this.dispatchEvent(proposeTransformEvent); + + if(proposeTransformEvent.objections > 0){ + + }else{ + this.camera.position.copy(proposal.position); + this.camera.rotation.copy(proposal.rotation); + } + + var wp = this.pivotNode.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); + var w = Math.abs(wp.z / 30); + var l = this.pivotNode.scale.length(); + this.pivotNode.scale.multiplyScalar(w / l); + } + + + + mouseDelta.set(0,0); + }; + + + this.reset = function () { + state = STATE.NONE; + + this.camera.position.copy( this.position0 ); + }; + + function onMouseDown( event ) { + if ( scope.enabled === false ) return; + event.preventDefault(); + + var rect = scope.domElement.getBoundingClientRect(); + + var mouse = { + x: ( (event.clientX - rect.left) / scope.domElement.clientWidth ) * 2 - 1, + y: - ( (event.clientY - rect.top) / scope.domElement.clientHeight ) * 2 + 1 + }; + var I = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, scope.pointclouds); + if(!I){ + return; + } + + var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(new THREE.Vector3(0, 1, 0), I); + + var vec = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); + vec.unproject(scope.camera); + var dir = vec.sub(scope.camera.position).normalize(); + + var ray = new THREE.Ray(scope.camera.position, dir); + pivot = ray.intersectPlane(plane); + + //pivot = I; + camStart = scope.camera.clone(); + camStart.rotation.copy(scope.camera.rotation); + dragStart.set( event.clientX - rect.left, event.clientY - rect.top); + dragEnd.set(event.clientX - rect.left, event.clientY - rect.top); + + + scope.scene.add(scope.pivotNode); + scope.pivotNode.position.copy(pivot); + + if ( event.button === THREE.MOUSE.LEFT ) { + state = STATE.DRAG; + } else if ( event.button === THREE.MOUSE.RIGHT ) { + state = STATE.ROTATE; + } + + scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); + scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); + } + + function onMouseMove( event ) { + if ( scope.enabled === false ) return; + + event.preventDefault(); + + var rect = scope.domElement.getBoundingClientRect(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + mouseDelta.set(event.clientX - rect.left - dragEnd.x, event.clientY - rect.top - dragEnd.y); + dragEnd.set(event.clientX - rect.left, event.clientY - rect.top); + + } + + function onMouseUp() { + if ( scope.enabled === false ) return; + + scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); + scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); + state = STATE.NONE; + + //scope.dragStartIndicator.style.display = "none"; + scope.scene.remove(scope.pivotNode); + + } + + function onMouseWheel(event) { + if ( scope.enabled === false || scope.noZoom === true ) return; + + event.preventDefault(); + + var rect = scope.domElement.getBoundingClientRect(); + + var amount = (event.detail<0 || event.wheelDelta>0) ? 1 : -1; + var mouse = { + x: ( (event.clientX - rect.left) / scope.domElement.clientWidth ) * 2 - 1, + y: - ( (event.clientY - rect.top) / scope.domElement.clientHeight ) * 2 + 1 + }; + var I = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, scope.pointclouds); + + if(I){ + var distance = I.distanceTo(scope.camera.position); + var dir = new THREE.Vector3().subVectors(I, scope.camera.position).normalize(); + scope.camera.position.add(dir.multiplyScalar(distance * 0.1 * amount)); + } + + } + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox +}; + +THREE.EarthControls.prototype = Object.create( THREE.EventDispatcher.prototype ); + + +/** + * + * @param node + * @class an item in the lru list. + */ +function LRUItem(node){ + this.previous = null; + this.next = null; + this.node = node; +} + +/** + * + * @class A doubly-linked-list of the least recently used elements. + */ +function LRU(){ + // the least recently used item + this.first = null; + // the most recently used item + this.last = null; + // a list of all items in the lru list + this.items = {}; + this.elements = 0; + this.numPoints = 0; +} + +/** + * number of elements in the list + * + * @returns {Number} + */ +LRU.prototype.size = function(){ + return this.elements; +}; + +LRU.prototype.contains = function(node){ + return this.items[node.id] == null; +}; + +/** + * makes node the most recently used item. if the list does not contain node, it will be added. + * + * @param node + */ +LRU.prototype.touch = function(node){ + if(!node.loaded){ + return; + } + + var item; + if(this.items[node.id] == null){ + // add to list + item = new LRUItem(node); + item.previous = this.last; + this.last = item; + if(item.previous !== null){ + item.previous.next = item; + } + + this.items[node.id] = item; + this.elements++; + + if(this.first === null){ + this.first = item; + } + this.numPoints += node.numPoints; + }else{ + // update in list + item = this.items[node.id]; + if(item.previous === null){ + // handle touch on first element + if(item.next !== null){ + this.first = item.next; + this.first.previous = null; + item.previous = this.last; + item.next = null; + this.last = item; + item.previous.next = item; + } + }else if(item.next === null){ + // handle touch on last element + }else{ + // handle touch on any other element + item.previous.next = item.next; + item.next.previous = item.previous; + item.previous = this.last; + item.next = null; + this.last = item; + item.previous.next = item; + } + + + } +}; + +///** +// * removes the least recently used item from the list and returns it. +// * if the list was empty, null will be returned. +// */ +//LRU.prototype.remove = function remove(){ +// if(this.first === null){ +// return null; +// } +// var lru = this.first; +// +// // if the lru list contains at least 2 items, the item after the least recently used elemnt will be the new lru item. +// if(lru.next !== null){ +// this.first = lru.next; +// this.first.previous = null; +// }else{ +// this.first = null; +// this.last = null; +// } +// +// delete this.items[lru.node.id]; +// this.elements--; +// this.numPoints -= lru.node.numPoints; +// +//// Logger.info("removed node: " + lru.node.id); +// return lru.node; +//}; + +LRU.prototype.remove = function remove(node){ + + var lruItem = this.items[node.id]; + if(lruItem){ + + if(this.elements === 1){ + this.first = null; + this.last = null; + }else{ + if(!lruItem.previous){ + this.first = lruItem.next; + this.first.previous = null; + } + if(!lruItem.next){ + this.last = lruItem.previous; + this.last.next = null; + } + if(lruItem.previous &&lruItem.next){ + lruItem.previous.next = lruItem.next; + lruItem.next.previous = lruItem.previous; + } + } + + delete this.items[node.id]; + this.elements--; + this.numPoints -= node.numPoints; + } + +}; + +LRU.prototype.getLRUItem = function(){ + if(this.first === null){ + return null; + } + var lru = this.first; + + return lru.node; +}; + +LRU.prototype.toString = function(){ + var string = "{ "; + var curr = this.first; + while(curr !== null){ + string += curr.node.id; + if(curr.next !== null){ + string += ", "; + } + curr = curr.next; + } + string += "}"; + string += "(" + this.size() + ")"; + return string; +}; + +LRU.prototype.freeMemory = function(){ + if(this.elements <= 1){ + return; + } + + while(this.numPoints > Potree.pointLoadLimit){ + var element = this.first; + var node = element.node; + this.disposeDescendants(node); + + } +}; + +LRU.prototype.disposeDescendants = function(node){ + var stack = []; + stack.push(node); + while(stack.length > 0){ + var current = stack.pop(); + + //console.log(current); + + current.dispose(); + this.remove(current); + + for(var key in current.children){ + if(current.children.hasOwnProperty(key)){ + var child = current.children[key]; + if(child.loaded){ + stack.push(current.children[key]); + } + } + } + } +}; +Potree.Annotation = function(viewer, args){ + var scope = this; + + Potree.Annotation.counter++; + + this.viewer = viewer; + this.ordinal = args.title || Potree.Annotation.counter; + this.title = args.title || "No Title"; + this.description = args.description || ""; + this.scene = args.scene || null; + this.position = args.position || new THREE.Vector3(0,0,0); + this.cameraPosition = args.cameraPosition; + this.cameraTarget = args.cameraTarget || this.position; + this.view = args.view || null; + this.keepOpen = false; + this.descriptionVisible = false; + + this.domElement = document.createElement("div"); + this.domElement.style.position = "absolute"; + this.domElement.style.opacity = "0.5"; + //this.domElement.style.border = "1px solid red"; + this.domElement.style.padding = "10px"; + this.domElement.style.whiteSpace = "nowrap"; + this.domElement.className = "annotation"; + + this.elOrdinal = document.createElement("div"); + this.elOrdinal.style.position = "relative"; + //this.elOrdinal.style.width = "1.5em"; + //this.elOrdinal.style.height = "1.5em"; + this.elOrdinal.style.color = "white"; + this.elOrdinal.style.backgroundColor = "black"; + this.elOrdinal.style.borderRadius = "1.5em"; + this.elOrdinal.style.fontSize = "1em"; + this.elOrdinal.style.opacity = "1"; + this.elOrdinal.style.margin = "auto"; + this.elOrdinal.style.zIndex = "100"; + this.elOrdinal.style.width = "fit-content"; + this.domElement.appendChild(this.elOrdinal); + + this.domDescription = document.createElement("div"); + this.domDescription.style.position = "relative"; + this.domDescription.style.color = "white"; + this.domDescription.style.backgroundColor = "black"; + this.domDescription.style.padding = "10px"; + this.domDescription.style.margin = "5px 0px 0px 0px"; + this.domDescription.style.borderRadius = "4px"; + this.domDescription.style.display = "none"; + this.domDescription.className = "annotation"; + //this.domDescription.style.top = "20"; + //this.domDescription.style.left = "-100"; + this.domElement.appendChild(this.domDescription); + + this.elOrdinal.onmouseenter = function(){ + + }; + this.elOrdinal.onmouseleave = function(){ + + }; + this.elOrdinal.onclick = function(){ + scope.moveHere(scope.viewer.camera); + }; + + + this.elOrdinalText = document.createElement("span"); + this.elOrdinalText.style.display = "inline-block"; + this.elOrdinalText.style.verticalAlign = "middle"; + this.elOrdinalText.style.lineHeight = "1.5em"; + this.elOrdinalText.style.textAlign = "center"; + //this.elOrdinalText.style.width = "100%"; + this.elOrdinalText.style.fontFamily = "Arial"; + this.elOrdinalText.style.fontWeight = "bold"; + this.elOrdinalText.style.padding = "1px 8px 0px 8px"; + this.elOrdinalText.style.cursor = "default"; + this.elOrdinalText.innerHTML = this.ordinal; + this.elOrdinalText.userSelect = "none"; + this.elOrdinal.appendChild(this.elOrdinalText); + + this.elDescriptionText = document.createElement("span"); + this.elDescriptionText.style.color = "#ffffff"; + this.elDescriptionText.innerHTML = this.description; + this.domDescription.appendChild(this.elDescriptionText); + + this.domElement.onmouseenter = function(){ + scope.domElement.style.opacity = "0.8"; + scope.domElement.style.zIndex = "1000"; + if(scope.description){ + scope.descriptionVisible = true; + scope.domDescription.style.display = "block"; + } + }; + this.domElement.onmouseleave = function(){ + scope.domElement.style.opacity = "0.5"; + scope.domElement.style.zIndex = "100"; + scope.descriptionVisible = true; + scope.domDescription.style.display = "none"; + }; + + this.moveHere = function(camera){ + var animationDuration = 800; + var easing = TWEEN.Easing.Quartic.Out; + + // animate camera position + var tween = new TWEEN.Tween(camera.position).to(scope.cameraPosition, animationDuration); + tween.easing(easing); + tween.start(); + + // animate camera target + var camTargetDistance = camera.position.distanceTo(scope.cameraTarget); + var target = new THREE.Vector3().addVectors( + camera.position, + camera.getWorldDirection().clone().multiplyScalar(camTargetDistance) + ); + var tween = new TWEEN.Tween(target).to(scope.cameraTarget, animationDuration); + tween.easing(easing); + tween.onUpdate(function(){ + camera.lookAt(target); + scope.viewer.orbitControls.target.copy(target); + }); + tween.onComplete(function(){ + camera.lookAt(target); + scope.viewer.orbitControls.target.copy(target); + scope.dispatchEvent({type: "focusing_finished", target: scope}); + }); + + scope.dispatchEvent({type: "focusing_started", target: scope}); + tween.start(); + }; + + this.dispose = function(){ + + + if(this.domElement.parentElement){ + this.domElement.parentElement.removeChild(this.domElement); + } + + }; +}; + +Potree.Annotation.prototype = Object.create( THREE.EventDispatcher.prototype ); + +Potree.Annotation.counter = 0; + +Potree.ProfileData = function(profile){ + this.profile = profile; + + this.segments = []; + this.boundingBox = new THREE.Box3(); + this.projectedBoundingBox = new THREE.Box2(); + + var mileage = new THREE.Vector3(); + for(var i = 0; i < profile.points.length - 1; i++){ + var start = profile.points[i]; + var end = profile.points[i+1]; + + var startGround = new THREE.Vector3(start.x, 0, start.z); + var endGround = new THREE.Vector3(end.x, 0, end.z); + + var center = new THREE.Vector3().addVectors(endGround, startGround).multiplyScalar(0.5); + var length = startGround.distanceTo(endGround); + var side = new THREE.Vector3().subVectors(endGround, startGround).normalize(); + var up = new THREE.Vector3(0, 1, 0); + var forward = new THREE.Vector3().crossVectors(side, up).normalize(); + var N = forward; + var cutPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(N, startGround); + var halfPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(side, center); + + var project = function(_start, _end, _mileage){ + var start = _start; + var end = _end; + var mileage = _mileage; + + var xAxis = new THREE.Vector3(1,0,0); + var dir = new THREE.Vector3().subVectors(end, start); + dir.y = 0; + dir.normalize(); + var alpha = Math.acos(xAxis.dot(dir)); + if(dir.z > 0){ + alpha = -alpha; + } + + + return function(position){ + var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, 0, -start.z); + var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); + var applyMileage = new THREE.Matrix4().makeTranslation(mileage.x, 0, 0); + + var pos = position.clone(); + pos.applyMatrix4(toOrigin); + pos.applyMatrix4(alignWithX); + pos.applyMatrix4(applyMileage); + + return pos; + }; + + }(start, end, mileage.clone()); + + var segment = { + start: start, + end: end, + cutPlane: cutPlane, + halfPlane: halfPlane, + length: length, + points: null, + project: project + }; + + this.segments.push(segment); + + mileage.x += length; + mileage.y += end.y - start.y; + } + + this.projectedBoundingBox.min.x = 0; + this.projectedBoundingBox.min.y = Number.POSITIVE_INFINITY; + this.projectedBoundingBox.max.x = mileage.x; + this.projectedBoundingBox.max.y = Number.NEGATIVE_INFINITY; + + this.size = function(){ + var size = 0; + for(var i = 0; i < this.segments.length; i++){ + if(this.segments[i].points){ + size += this.segments[i].points.numPoints; + } + } + return size; + } + +}; + +Potree.ProfileRequest = function(pointcloud, profile, maxDepth, callback){ + + this.pointcloud = pointcloud; + this.profile = profile; + this.maxDepth = maxDepth || Number.MAX_VALUE; + this.callback = callback; + this.temporaryResult = new Potree.ProfileData(this.profile); + this.pointsServed = 0; + + this.priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); + + this.initialize = function(){ + this.priorityQueue.push({node: pointcloud.pcoGeometry.root, weight: 1}); + this.traverse(pointcloud.pcoGeometry.root); + }; + + // traverse the node and add intersecting descendants to queue + this.traverse = function(node){ + + var stack = []; + for(var i = 0; i < 8; i++){ + var child = node.children[i]; + if(child && pointcloud.nodeIntersectsProfile(child, this.profile)){ + stack.push(child); + } + } + + while(stack.length > 0){ + var node = stack.pop(); + var weight = node.boundingSphere.radius; + + this.priorityQueue.push({node: node, weight: weight}); + + // add children that intersect the cutting plane + if(node.level < this.maxDepth){ + for(var i = 0; i < 8; i++){ + var child = node.children[i]; + if(child && pointcloud.nodeIntersectsProfile(child, this.profile)){ + stack.push(child); + } + } + } + } + }; + + this.update = function(){ + + // load nodes in queue + // if hierarchy expands, also load nodes from expanded hierarchy + // once loaded, add data to this.points and remove node from queue + // only evaluate 1-50 nodes per frame to maintain responsiveness + + var intersectedNodes = []; + + for(var i = 0; i < Math.min(2, this.priorityQueue.size()); i++){ + var element = this.priorityQueue.pop(); + var node = element.node; + + + if(node.loaded){ + // add points to result + intersectedNodes.push(node); + Potree.getLRU().touch(node); + + if((node.level % node.pcoGeometry.hierarchyStepSize) === 0 && node.hasChildren){ + this.traverse(node); + } + }else{ + node.load(); + this.priorityQueue.push(element); + } + } + + if(intersectedNodes.length > 0){ + this.getPointsInsideProfile(intersectedNodes, this.temporaryResult); + if(this.temporaryResult.size() > 100){ + this.pointsServed += this.temporaryResult.size(); + callback.onProgress({request: this, points: this.temporaryResult}); + this.temporaryResult = new Potree.ProfileData(this.profile); + } + } + + if(this.priorityQueue.size() === 0){ + // we're done! inform callback and remove from pending requests + + if(this.temporaryResult.size() > 0){ + this.pointsServed += this.temporaryResult.size(); + callback.onProgress({request: this, points: this.temporaryResult}); + this.temporaryResult = new Potree.ProfileData(this.profile); + } + + callback.onFinish({request: this}); + + var index = pointcloud.profileRequests.indexOf(this); + if(index >= 0){ + pointcloud.profileRequests.splice(index, 1); + } + } + }; + + this.getPointsInsideProfile = function(nodes, target){ + + for(var pi = 0; pi < target.segments.length; pi++){ + var segment = target.segments[pi]; + + for(var ni = 0; ni < nodes.length; ni++){ + var node = nodes[ni]; + + var geometry = node.geometry; + var positions = geometry.attributes.position; + var p = positions.array; + var numPoints = node.numPoints; + + if(!segment.points){ + segment.points = {}; + segment.points.boundingBox = new THREE.Box3(); + + for (var property in geometry.attributes) { + if (geometry.attributes.hasOwnProperty(property)) { + if(property === "indices"){ + + }else{ + segment.points[property] = []; + } + } + } + } + + for(var i = 0; i < numPoints; i++){ + var pos = new THREE.Vector3(p[3*i], p[3*i+1], p[3*i+2]); + pos.applyMatrix4(pointcloud.matrixWorld); + var distance = Math.abs(segment.cutPlane.distanceToPoint(pos)); + var centerDistance = Math.abs(segment.halfPlane.distanceToPoint(pos)); + + if(distance < profile.width / 2 && centerDistance < segment.length / 2){ + segment.points.boundingBox.expandByPoint(pos); + + for (var property in geometry.attributes) { + if (geometry.attributes.hasOwnProperty(property)) { + + if(property === "position"){ + segment.points[property].push(pos); + }else if(property === "indices"){ + // skip indices + }else{ + var values = geometry.attributes[property]; + if(values.itemSize === 1){ + segment.points[property].push(values.array[i]); + }else{ + var value = []; + for(var j = 0; j < values.itemSize; j++){ + value.push(values.array[i*values.itemSize + j]); + } + segment.points[property].push(value); + } + } + + } + } + }else{ + var a; + } + } + } + + segment.points.numPoints = segment.points.position.length; + + if(segment.points.numPoints > 0){ + target.boundingBox.expandByPoint(segment.points.boundingBox.min); + target.boundingBox.expandByPoint(segment.points.boundingBox.max); + + target.projectedBoundingBox.expandByPoint(new THREE.Vector2(0, target.boundingBox.min.y)); + target.projectedBoundingBox.expandByPoint(new THREE.Vector2(0, target.boundingBox.max.y)); + } + } + }; + + this.cancel = function(){ + callback.onCancel(); + + this.priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); + + var index = pointcloud.profileRequests.indexOf(this); + if(index >= 0){ + pointcloud.profileRequests.splice(index, 1); + } + }; + + this.initialize(); + +}; + +Potree.PointCloudOctreeNode = function(){ + this.children = {}; + this.sceneNode = null; + this.octree = null; + + this.getNumPoints = function(){ + return this.geometryNode.numPoints; + }; + + this.isLoaded = function(){ + return true; + }; + + this.isTreeNode = function(){ + return true; + }; + + this.isGeometryNode = function(){ + return false; + }; + + this.getLevel = function(){ + return this.geometryNode.level; + }; + + this.getBoundingSphere = function(){ + return this.geometryNode.boundingSphere; + }; + + this.getBoundingBox = function(){ + return this.geometryNode.boundingBox; + }; + + this.getChildren = function(){ + var children = []; + + for(var i = 0; i < 8; i++){ + if(this.children[i]){ + children.push(this.children[i]); + } + } + + return children; + }; +}; + +Potree.PointCloudOctreeNode.prototype = Object.create(Potree.PointCloudTreeNode.prototype); + + +Potree.PointCloudOctree = function(geometry, material){ + //THREE.Object3D.call( this ); + Potree.PointCloudTree.call(this); + + this.pcoGeometry = geometry; + this.boundingBox = this.pcoGeometry.tightBoundingBox; + this.boundingSphere = this.boundingBox.getBoundingSphere(); + this.material = material || new Potree.PointCloudMaterial(); + this.visiblePointsTarget = 2*1000*1000; + this.minimumNodePixelSize = 150; + this.level = 0; + this.position.sub(geometry.offset); + this.updateMatrix(); + + this.showBoundingBox = false; + this.boundingBoxNodes = []; + this.loadQueue = []; + this.visibleBounds = new THREE.Box3(); + this.visibleNodes = []; + this.visibleGeometry = []; + this.pickTarget = null; + this.generateDEM = false; + this.profileRequests = []; + + // TODO read projection from file instead + this.projection = geometry.projection; + + this.root = this.pcoGeometry.root; +}; + +Potree.PointCloudOctree.prototype = Object.create(Potree.PointCloudTree.prototype); + +Potree.PointCloudOctree.prototype.toTreeNode = function(geometryNode, parent){ + var node = new Potree.PointCloudOctreeNode(); + var sceneNode = new THREE.PointCloud(geometryNode.geometry, pointcloud.material); + + node.geometryNode = geometryNode; + node.sceneNode = sceneNode; + node.pointcloud = pointcloud; + node.children = {}; + for(var key in geometryNode.children){ + node.children[key] = geometryNode.children[key]; + } + + if(!parent){ + this.root = node; + this.add(sceneNode); + }else{ + var childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]); + parent.sceneNode.add(sceneNode); + parent.children[childIndex] = node; + } + + var disposeListener = function(){ + var childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]); + parent.sceneNode.remove(node.sceneNode); + parent.children[childIndex] = geometryNode; + } + geometryNode.oneTimeDisposeHandlers.push(disposeListener); + + return node; +}; + +Potree.PointCloudOctree.prototype.updateVisibleBounds = function(){ + + var leafNodes = []; + for(var i = 0; i < this.visibleNodes.length; i++){ + var node = this.visibleNodes[i]; + var isLeaf = true; + + for(var j = 0; j < node.children.length; j++){ + var child = node.children[j]; + if(child instanceof Potree.PointCloudOctreeNode){ + isLeaf = isLeaf && !child.sceneNode.visible; + }else if(child instanceof Potree.PointCloudOctreeGeometryNode){ + isLeaf = true; + } } - while(stack.length > 0){ - var node = stack.pop(); - var weight = node.boundingSphere.radius; + if(isLeaf){ + leafNodes.push(node); + } + } + + this.visibleBounds.min = new THREE.Vector3( Infinity, Infinity, Infinity ); + this.visibleBounds.max = new THREE.Vector3( - Infinity, - Infinity, - Infinity ); + for(var i = 0; i < leafNodes.length; i++){ + var node = leafNodes[i]; + + this.visibleBounds.expandByPoint(node.getBoundingBox().min); + this.visibleBounds.expandByPoint(node.getBoundingBox().max); + } + +}; + +Potree.PointCloudOctree.prototype.updateMaterial = function(material, visibleNodes, camera, renderer){ + material.fov = camera.fov * (Math.PI / 180); + material.screenWidth = renderer.domElement.clientWidth; + material.screenHeight = renderer.domElement.clientHeight; + material.spacing = this.pcoGeometry.spacing; + material.near = camera.near; + material.far = camera.far; + material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.size().x; + + // update visibility texture + if(material.pointSizeType){ + if(material.pointSizeType === Potree.PointSizeType.ADAPTIVE + || material.pointColorType === Potree.PointColorType.OCTREE_DEPTH){ - this.priorityQueue.push({node: node, weight: weight}); + this.updateVisibilityTexture(material, visibleNodes); + } + } +}; + +Potree.PointCloudOctree.prototype.updateVisibilityTexture = function(material, visibleNodes){ + + if(!material){ + return; + } + + var texture = material.visibleNodesTexture; + var data = texture.image.data; + + // copy array + visibleNodes = visibleNodes.slice(); + + // sort by level and index, e.g. r, r0, r3, r4, r01, r07, r30, ... + var sort = function(a, b){ + var na = a.geometryNode.name; + var nb = b.geometryNode.name; + if(na.length != nb.length) return na.length - nb.length; + if(na < nb) return -1; + if(na > nb) return 1; + return 0; + }; + visibleNodes.sort(sort); + + + for(var i = 0; i < visibleNodes.length; i++){ + var node = visibleNodes[i]; + + var children = []; + for(var j = 0; j < 8; j++){ + var child = node.children[j]; + if(child instanceof Potree.PointCloudOctreeNode && child.sceneNode.visible && visibleNodes.indexOf(child) >= 0){ + children.push(child); + } + } + children.sort(function(a, b){ + if(a.geometryNode.name < b.geometryNode.name) return -1; + if(a.geometryNode.name > b.geometryNode.name) return 1; + return 0; + }); + + data[i*3 + 0] = 0; + data[i*3 + 1] = 0; + data[i*3 + 2] = 0; + for(var j = 0; j < children.length; j++){ + var child = children[j]; + var index = parseInt(child.geometryNode.name.substr(-1)); + data[i*3 + 0] += Math.pow(2, index); + + if(j === 0){ + var vArrayIndex = visibleNodes.indexOf(child); + data[i*3 + 1] = vArrayIndex - i; + } + + } + } + + + texture.needsUpdate = true; +}; + +Potree.PointCloudOctree.prototype.nodeIntersectsProfile = function(node, profile){ + var bbWorld = node.boundingBox.clone().applyMatrix4(this.matrixWorld); + var bsWorld = bbWorld.getBoundingSphere(); + + for(var i = 0; i < profile.points.length - 1; i++){ + var start = new THREE.Vector3(profile.points[i].x, bsWorld.center.y, profile.points[i].z); + var end = new THREE.Vector3(profile.points[i+1].x, bsWorld.center.y, profile.points[i+1].z); + + var ray1 = new THREE.Ray(start, new THREE.Vector3().subVectors(end, start).normalize()); + var ray2 = new THREE.Ray(end, new THREE.Vector3().subVectors(start, end).normalize()); + + if(ray1.isIntersectionSphere(bsWorld) && ray2.isIntersectionSphere(bsWorld)){ + return true; + } + } + + return false; +}; + + + + + + + + + + + + + + + + +Potree.PointCloudOctree.prototype.nodesOnRay = function(nodes, ray){ + var nodesOnRay = []; + + var _ray = ray.clone(); + for(var i = 0; i < nodes.length; i++){ + var node = nodes[i]; + //var inverseWorld = new THREE.Matrix4().getInverse(node.matrixWorld); + var sphere = node.getBoundingSphere().clone().applyMatrix4(node.sceneNode.matrixWorld); + + if(_ray.isIntersectionSphere(sphere)){ + nodesOnRay.push(node); + } + } + + return nodesOnRay; +}; + +Potree.PointCloudOctree.prototype.updateMatrixWorld = function( force ){ + //node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === undefined ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } +}; + +Potree.PointCloudOctree.prototype.hideDescendants = function(object){ + var stack = []; + for(var i = 0; i < object.children.length; i++){ + var child = object.children[i]; + if(child.visible){ + stack.push(child); + } + } + + while(stack.length > 0){ + var object = stack.shift(); + + object.visible = false; + + for(var i = 0; i < object.children.length; i++){ + var child = object.children[i]; + if(child.visible){ + stack.push(child); + } + } + } +}; + +Potree.PointCloudOctree.prototype.moveToOrigin = function(){ + this.position.set(0,0,0); + this.updateMatrixWorld(true); + var box = this.boundingBox; + var transform = this.matrixWorld; + var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); + this.position.set(0,0,0).sub(tBox.center()); +}; + +Potree.PointCloudOctree.prototype.moveToGroundPlane = function(){ + this.updateMatrixWorld(true); + var box = this.boundingBox; + var transform = this.matrixWorld; + var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); + this.position.y += -tBox.min.y; +}; + +Potree.PointCloudOctree.prototype.getBoundingBoxWorld = function(){ + this.updateMatrixWorld(true); + var box = this.boundingBox; + var transform = this.matrixWorld; + var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); + + return tBox; +}; + +/** + * returns points inside the profile points + * + * maxDepth: search points up to the given octree depth + * + * + * The return value is an array with all segments of the profile path + * var segment = { + * start: THREE.Vector3, + * end: THREE.Vector3, + * points: {} + * project: function() + * }; + * + * The project() function inside each segment can be used to transform + * that segments point coordinates to line up along the x-axis. + * + * + */ +Potree.PointCloudOctree.prototype.getPointsInProfile = function(profile, maxDepth, callback){ + + if(callback){ + var request = new Potree.ProfileRequest(this, profile, maxDepth, callback); + this.profileRequests.push(request); - // add children that intersect the cutting plane - if(node.level < this.maxDepth){ - for(var i = 0; i < 8; i++){ - var child = node.children[i]; - if(child && pointcloud.nodeIntersectsProfile(child, this.profile)){ - stack.push(child); - } - } - } - } + return request; + } + + var points = { + segments: [], + boundingBox: new THREE.Box3(), + projectedBoundingBox: new THREE.Box2() }; - this.update = function(){ + // evaluate segments + for(var i = 0; i < profile.points.length - 1; i++){ + var start = profile.points[i]; + var end = profile.points[i+1]; + var ps = this.getProfile(start, end, profile.width, maxDepth); - // load nodes in queue - // if hierarchy expands, also load nodes from expanded hierarchy - // once loaded, add data to this.points and remove node from queue - // only evaluate 1-50 nodes per frame to maintain responsiveness + var segment = { + start: start, + end: end, + points: ps, + project: null + }; - var intersectedNodes = []; + points.segments.push(segment); - for(var i = 0; i < Math.min(2, this.priorityQueue.size()); i++){ - var element = this.priorityQueue.pop(); - var node = element.node; + points.boundingBox.expandByPoint(ps.boundingBox.min); + points.boundingBox.expandByPoint(ps.boundingBox.max); + } + + // add projection functions to the segments + var mileage = new THREE.Vector3(); + for(var i = 0; i < points.segments.length; i++){ + var segment = points.segments[i]; + var start = segment.start; + var end = segment.end; + + var project = function(_start, _end, _mileage, _boundingBox){ + var start = _start; + var end = _end; + var mileage = _mileage; + var boundingBox = _boundingBox; + + var xAxis = new THREE.Vector3(1,0,0); + var dir = new THREE.Vector3().subVectors(end, start); + dir.y = 0; + dir.normalize(); + var alpha = Math.acos(xAxis.dot(dir)); + if(dir.z > 0){ + alpha = -alpha; + } - if(node.loaded){ - // add points to result - intersectedNodes.push(node); - Potree.PointCloudOctree.lru.touch(node); + return function(position){ + + var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, -boundingBox.min.y, -start.z); + var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); + var applyMileage = new THREE.Matrix4().makeTranslation(mileage.x, 0, 0); + + var pos = position.clone(); + pos.applyMatrix4(toOrigin); + pos.applyMatrix4(alignWithX); + pos.applyMatrix4(applyMileage); - if((node.level % node.pcoGeometry.hierarchyStepSize) === 0 && node.hasChildren){ - this.traverse(node); - } - }else{ - node.load(); - this.priorityQueue.push(element); - } - } + return pos; + }; + + }(start, end, mileage.clone(), points.boundingBox.clone()); - if(intersectedNodes.length > 0){ - this.getPointsInsideProfile(intersectedNodes, this.temporaryResult); - if(this.temporaryResult.size() > 100){ - this.pointsServed += this.temporaryResult.size(); - callback.onProgress({request: this, points: this.temporaryResult}); - this.temporaryResult = new Potree.ProfileData(this.profile); - } - } + segment.project = project; - if(this.priorityQueue.size() === 0){ - // we're done! inform callback and remove from pending requests + mileage.x += new THREE.Vector3(start.x, 0, start.z).distanceTo(new THREE.Vector3(end.x, 0, end.z)); + mileage.y += end.y - start.y; + } + + points.projectedBoundingBox.min.x = 0; + points.projectedBoundingBox.min.y = points.boundingBox.min.y; + points.projectedBoundingBox.max.x = mileage.x; + points.projectedBoundingBox.max.y = points.boundingBox.max.y; + + return points; +}; - if(this.temporaryResult.size() > 0){ - this.pointsServed += this.temporaryResult.size(); - callback.onProgress({request: this, points: this.temporaryResult}); - this.temporaryResult = new Potree.ProfileData(this.profile); - } +/** + * returns points inside the given profile bounds. + * + * start: + * end: + * width: + * depth: search points up to the given octree depth + * callback: if specified, points are loaded before searching + * + * + */ +Potree.PointCloudOctree.prototype.getProfile = function(start, end, width, depth, callback){ + if(callback !== undefined){ + var request = new Potree.ProfileRequest(start, end, width, depth, callback); + this.profileRequests.push(request); + }else{ + var stack = []; + stack.push(this); + + var center = new THREE.Vector3().addVectors(end, start).multiplyScalar(0.5); + var length = new THREE.Vector3().subVectors(end, start).length(); + var side = new THREE.Vector3().subVectors(end, start).normalize(); + var up = new THREE.Vector3(0, 1, 0); + var forward = new THREE.Vector3().crossVectors(side, up).normalize(); + var N = forward; + var cutPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(N, start); + var halfPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(side, center); + + var inside = null; + + var boundingBox = new THREE.Box3(); + + + while(stack.length > 0){ + var object = stack.shift(); - callback.onFinish({request: this}); - var index = pointcloud.profileRequests.indexOf(this); - if(index >= 0){ - pointcloud.profileRequests.splice(index, 1); - } - } - }; - - this.getPointsInsideProfile = function(nodes, target){ - - for(var pi = 0; pi < target.segments.length; pi++){ - var segment = target.segments[pi]; + var pointsFound = 0; - for(var ni = 0; ni < nodes.length; ni++){ - var node = nodes[ni]; - - var geometry = node.geometry; + if(object instanceof THREE.PointCloud){ + var geometry = object.geometry; var positions = geometry.attributes.position; var p = positions.array; - var numPoints = node.numPoints; + var numPoints = object.numPoints; - if(!segment.points){ - segment.points = {}; - segment.points.boundingBox = new THREE.Box3(); + if(!inside){ + inside = {}; for (var property in geometry.attributes) { if (geometry.attributes.hasOwnProperty(property)) { if(property === "indices"){ }else{ - segment.points[property] = []; + inside[property] = []; } } } @@ -4507,2212 +5773,2351 @@ Potree.ProfileRequest = function(pointcloud, profile, maxDepth, callback){ for(var i = 0; i < numPoints; i++){ var pos = new THREE.Vector3(p[3*i], p[3*i+1], p[3*i+2]); - pos.applyMatrix4(pointcloud.matrixWorld); - var distance = Math.abs(segment.cutPlane.distanceToPoint(pos)); - var centerDistance = Math.abs(segment.halfPlane.distanceToPoint(pos)); + pos.applyMatrix4(this.matrixWorld); + var distance = Math.abs(cutPlane.distanceToPoint(pos)); + var centerDistance = Math.abs(halfPlane.distanceToPoint(pos)); - if(distance < profile.width / 2 && centerDistance < segment.length / 2){ - segment.points.boundingBox.expandByPoint(pos); + if(distance < width / 2 && centerDistance < length / 2){ + boundingBox.expandByPoint(pos); for (var property in geometry.attributes) { if (geometry.attributes.hasOwnProperty(property)) { if(property === "position"){ - segment.points[property].push(pos); + inside[property].push(pos); }else if(property === "indices"){ // skip indices }else{ var values = geometry.attributes[property]; if(values.itemSize === 1){ - segment.points[property].push(values.array[i + j]); + inside[property].push(values.array[i + j]); }else{ var value = []; for(var j = 0; j < values.itemSize; j++){ value.push(values.array[i*values.itemSize + j]); } - segment.points[property].push(value); + inside[property].push(value); } } } } + + + pointsFound++; + } + } + } + + //console.log("traversing: " + object.name + ", #points found: " + pointsFound); + + if(object == this || object.level < depth){ + for(var i = 0; i < object.children.length; i++){ + var child = object.children[i]; + if(child instanceof THREE.PointCloud){ + var sphere = child.boundingSphere.clone().applyMatrix4(child.matrixWorld); + if(cutPlane.distanceToSphere(sphere) < sphere.radius){ + stack.push(child); + } } } } + } - segment.points.numPoints = segment.points.position.length; + inside.numPoints = inside.position.length; + + var project = function(_start, _end){ + var start = _start; + var end = _end; - if(segment.points.numPoints > 0){ - target.boundingBox.expandByPoint(segment.points.boundingBox.min); - target.boundingBox.expandByPoint(segment.points.boundingBox.max); + var xAxis = new THREE.Vector3(1,0,0); + var dir = new THREE.Vector3().subVectors(end, start); + dir.y = 0; + dir.normalize(); + var alpha = Math.acos(xAxis.dot(dir)); + if(dir.z > 0){ + alpha = -alpha; + } + + + return function(position){ + + var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, -start.y, -start.z); + var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); + + var pos = position.clone(); + pos.applyMatrix4(toOrigin); + pos.applyMatrix4(alignWithX); - target.projectedBoundingBox.expandByPoint(new THREE.Vector2(0, target.boundingBox.min.y)); - target.projectedBoundingBox.expandByPoint(new THREE.Vector2(0, target.boundingBox.max.y)); - } - } - }; - - this.cancel = function(){ - callback.onCancel(); + return pos; + }; + + }(start, end); - this.priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); + inside.project = project; + inside.boundingBox = boundingBox; - var index = pointcloud.profileRequests.indexOf(this); - if(index >= 0){ - pointcloud.profileRequests.splice(index, 1); - } - }; - - this.initialize(); - + return inside; + } }; -Potree.PointCloudOctreeNode = function(){ - this.children = {}; - this.sceneNode = null; - this.octree = null; +Potree.PointCloudOctree.prototype.getVisibleExtent = function(){ + return this.visibleBounds.applyMatrix4(this.matrixWorld); }; +/** + * + * + * + * params.pickWindowSize: Look for points inside a pixel window of this size. + * Use odd values: 1, 3, 5, ... + * + * + * TODO: only draw pixels that are actually read with readPixels(). + * + */ +Potree.PointCloudOctree.prototype.pick = function(renderer, camera, ray, params){ + // this function finds intersections by rendering point indices and then checking the point index at the mouse location. + // point indices are 3 byte and rendered to the RGB component. + // point cloud node indices are 1 byte and stored in the ALPHA component. + // this limits picking capabilities to 256 nodes and 2^24 points per node. -Potree.PointCloudOctree = function(geometry, material){ - THREE.Object3D.call( this ); - - Potree.PointCloudOctree.lru = Potree.PointCloudOctree.lru || new LRU(); + var params = params || {}; + var pickWindowSize = params.pickWindowSize || 17; + var pickOutsideClipRegion = params.pickOutsideClipRegion || false; - this.pcoGeometry = geometry; - this.boundingBox = this.pcoGeometry.tightBoundingBox; - this.boundingSphere = this.boundingBox.getBoundingSphere(); - this.material = material || new Potree.PointCloudMaterial(); - this.visiblePointsTarget = 2*1000*1000; - this.minimumNodePixelSize = 150; - this.level = 0; - this.position.sub(geometry.offset); - this.updateMatrix(); + var nodes = this.nodesOnRay(this.visibleNodes, ray); - this.showBoundingBox = false; - this.boundingBoxNodes = []; - this.loadQueue = []; - this.visibleBounds = new THREE.Box3(); - this.visibleNodes = []; - this.visibleGeometry = []; - this.pickTarget = null; - this.generateDEM = false; - this.profileRequests = []; + if(nodes.length === 0){ + return null; + } - this.root = this.pcoGeometry.root; -}; - -Potree.PointCloudOctree.prototype = Object.create(THREE.Object3D.prototype); - -Potree.PointCloudOctree.prototype.updateVisibility = function(camera, renderer){ - - this.numVisibleNodes = 0; - this.numVisiblePoints = 0; + var width = Math.ceil(renderer.domElement.clientWidth); + var height = Math.ceil(renderer.domElement.clientHeight); - // frustum in object space - camera.updateMatrixWorld(); - var frustum = new THREE.Frustum(); - var viewI = camera.matrixWorldInverse; - var world = this.matrixWorld; - var proj = camera.projectionMatrix; - var fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world); - frustum.setFromMatrix( fm ); - - // camera position in object space - var view = camera.matrixWorld; - var worldI = new THREE.Matrix4().getInverse(world); - var camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view); - var camObjPos = new THREE.Vector3().setFromMatrixPosition( camMatrixObject ); - - // traverse nodes with highest weight(depends on node size and distance to camera) first - var priorityQueue = new BinaryHeap(function(x){return 1 / x.weight;}); - priorityQueue.push({node: this.root, weight: 1}); + var pixelPos = new THREE.Vector3().addVectors(camera.position, ray.direction).project(camera); + pixelPos.addScalar(1).multiplyScalar(0.5); + pixelPos.x *= width; + pixelPos.y *= height; - var visibleNodes = []; - var visibleGeometry = []; - var unloadedGeometry = []; - var pointCount = 0; + if(!this.pickTarget){ + this.pickTarget = new THREE.WebGLRenderTarget( + 1, 1, + { minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat } + ); + }else if(this.pickTarget.width != width || this.pickTarget.height != height){ + this.pickTarget.dispose(); + this.pickTarget = new THREE.WebGLRenderTarget( + 1, 1, + { minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat } + ); + } + this.pickTarget.setSize(width, height); - // first, hide all visible nodes - if(this.root instanceof Potree.PointCloudOctreeNode){ - this.hideDescendants(this.root.sceneNode); + // setup pick material. + // use the same point size functions as the main material to get the same point sizes. + if(!this.pickMaterial){ + this.pickMaterial = new Potree.PointCloudMaterial(); + this.pickMaterial.pointColorType = Potree.PointColorType.POINT_INDEX; } - for(var i = 0; i < this.boundingBoxNodes.length; i++){ - this.boundingBoxNodes[i].visible = false; + + this.pickMaterial.pointSizeType = this.material.pointSizeType; + this.pickMaterial.size = this.material.size; + this.pickMaterial.pointShape = this.material.pointShape; + this.pickMaterial.interpolate = this.material.interpolate; + this.pickMaterial.minSize = this.material.minSize; + this.pickMaterial.maxSize = this.material.maxSize; + this.pickMaterial.classification = this.material.classification; + + if(pickOutsideClipRegion){ + this.pickMaterial.clipMode = Potree.ClipMode.DISABLED; + }else{ + this.pickMaterial.clipMode = this.material.clipMode; + if(this.material.clipMode === Potree.ClipMode.CLIP_OUTSIDE){ + this.pickMaterial.setClipBoxes(this.material.clipBoxes); + }else{ + this.pickMaterial.setClipBoxes([]); + } } + //this.pickMaterial.useClipBox = this.material.useClipBox; - while(priorityQueue.size() > 0){ - var element = priorityQueue.pop(); - var node = element.node; - var parent = element.parent; + this.updateMaterial(this.pickMaterial, nodes, camera, renderer); + + var _gl = renderer.context; + + _gl.enable(_gl.SCISSOR_TEST); + _gl.scissor(pixelPos.x - (pickWindowSize - 1) / 2, pixelPos.y - (pickWindowSize - 1) / 2,pickWindowSize,pickWindowSize); + _gl.disable(_gl.SCISSOR_TEST); + + var material = this.pickMaterial; + + renderer.setRenderTarget( this.pickTarget ); + + renderer.state.setDepthTest( material.depthTest ); + renderer.state.setDepthWrite( material.depthWrite ); + renderer.state.setBlending( THREE.NoBlending ); + + renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + + //TODO: UGLY HACK CHAMPIONSHIP SUBMISSION!! drawing first node does not work properly so we draw it twice. + if(nodes.length > 0){ + nodes.push(nodes[0]); + } + + for(var i = 0; i < nodes.length; i++){ + var object = nodes[i].sceneNode; + var geometry = object.geometry; - var box = node.boundingBox; + if(!geometry.attributes.indices.buffer){ + continue; + } - var insideFrustum = frustum.intersectsBox(box); - var visible = insideFrustum; - visible = visible && !(this.numVisiblePoints + node.numPoints > this.visiblePointsTarget); + material.pcIndex = i + 1; - //if(node instanceof Potree.PointCloudOctreeNode && viewer.profileTool.profiles.length > 0){ - // var profile = viewer.profileTool.profiles[0]; - // visible = this.nodeIntersectsProfile(node, profile); - //} + if(material.program){ + var program = material.program.program; + _gl.useProgram( program ); + //_gl.disable( _gl.BLEND ); + + var attributePointer = _gl.getAttribLocation(program, "indices"); + var attributeSize = 4; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.attributes.indices.buffer ); + //if(!bufferSubmitted){ + // _gl.bufferData( _gl.ARRAY_BUFFER, new Uint8Array(geometry.attributes.indices.array), _gl.STATIC_DRAW ); + // bufferSubmitted = true; + //} + _gl.enableVertexAttribArray( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.UNSIGNED_BYTE, true, 0, 0 ); - //visible = "r06642222234".indexOf(node.name) >= 0; + _gl.uniform1f(material.program.uniforms.pcIndex, material.pcIndex); + } - if(!visible){ - continue; - } + renderer.renderBufferDirect(camera, [], null, material, geometry, object); - this.numVisibleNodes++; - this.numVisiblePoints += node.numPoints; - - // if geometry is loaded, create a scene node - if(node instanceof Potree.PointCloudOctreeGeometryNode){ - var geometryNode = node; - var geometry = geometryNode.geometry; - - if((typeof parent === "undefined" || parent instanceof Potree.PointCloudOctreeNode) && geometryNode.loaded){ - var pcoNode = new Potree.PointCloudOctreeNode(); - var sceneNode = new THREE.PointCloud(geometry, this.material); - sceneNode.visible = false; - - pcoNode.octree = this; - pcoNode.name = geometryNode.name; - pcoNode.level = geometryNode.level; - pcoNode.numPoints = geometryNode.numPoints; - pcoNode.boundingBox = geometry.boundingBox; - pcoNode.tightBoundingBox = geometry.tightBoundingBox; - pcoNode.boundingSphere = pcoNode.boundingBox.getBoundingSphere(); - pcoNode.geometryNode = geometryNode; - pcoNode.parent = parent; - pcoNode.children = {}; - for(var key in geometryNode.children){ - pcoNode.children[key] = geometryNode.children[key]; - } - - sceneNode.boundingBox = pcoNode.boundingBox; - sceneNode.boundingSphere = pcoNode.boundingSphere; - sceneNode.numPoints = pcoNode.numPoints; - sceneNode.level = pcoNode.level; - - pcoNode.sceneNode = sceneNode; - - if(typeof node.parent === "undefined"){ - this.root = pcoNode; - this.add(pcoNode.sceneNode); - - sceneNode.matrixWorld.multiplyMatrices( this.matrixWorld, sceneNode.matrix ); - }else{ - var childIndex = parseInt(pcoNode.name[pcoNode.name.length - 1]); - parent.sceneNode.add(sceneNode); - parent.children[childIndex] = pcoNode; - - sceneNode.matrixWorld.multiplyMatrices( parent.sceneNode.matrixWorld, sceneNode.matrix ); - } - - // when a PointCloudOctreeGeometryNode is disposed, - // then replace reference to PointCloudOctreeNode with PointCloudOctreeGeometryNode - // as it was before it was loaded - var disposeListener = function(parent, pcoNode, geometryNode){ - return function(){ - var childIndex = parseInt(pcoNode.name[pcoNode.name.length - 1]); - parent.sceneNode.remove(pcoNode.sceneNode); - parent.children[childIndex] = geometryNode; - } - }(parent, pcoNode, node); - pcoNode.geometryNode.oneTimeDisposeHandlers.push(disposeListener); - - node = pcoNode; - } - - if(!geometryNode.loaded){ - unloadedGeometry.push(node); - visibleGeometry.push(node); - } - - } + var program = material.program.program; + _gl.useProgram( program ); + var attributePointer = _gl.getAttribLocation(program, "indices"); + _gl.disableVertexAttribArray( attributePointer ); + } + + var pixelCount = pickWindowSize * pickWindowSize; + var buffer = new ArrayBuffer(pixelCount*4); + var pixels = new Uint8Array(buffer); + var ibuffer = new Uint32Array(buffer); + renderer.context.readPixels( + pixelPos.x - (pickWindowSize-1) / 2, pixelPos.y - (pickWindowSize-1) / 2, + pickWindowSize, pickWindowSize, + renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, pixels); - if(node instanceof Potree.PointCloudOctreeNode){ - Potree.PointCloudOctree.lru.touch(node.geometryNode); - node.sceneNode.visible = true; - node.sceneNode.material = this.material; - visibleNodes.push(node); - visibleGeometry.push(node.geometryNode); - - if(node.parent){ - node.sceneNode.matrixWorld.multiplyMatrices( node.parent.sceneNode.matrixWorld, node.sceneNode.matrix ); - }else{ - node.sceneNode.matrixWorld.multiplyMatrices( this.matrixWorld, node.sceneNode.matrix ); - } - - if(this.showBoundingBox && !node.boundingBoxNode){ - var boxHelper = new THREE.BoxHelper(node.sceneNode); - this.add(boxHelper); - this.boundingBoxNodes.push(boxHelper); - node.boundingBoxNode = boxHelper; - node.boundingBoxNode.matrixWorld.copy(node.sceneNode.matrixWorld); - }else if(this.showBoundingBox){ - node.boundingBoxNode.visible = true; - node.boundingBoxNode.matrixWorld.copy(node.sceneNode.matrixWorld); - }else if(!this.showBoundingBox && node.boundingBoxNode){ - node.boundingBoxNode.visible = false; - } - - if(this.generateDEM && node.level <= 2){ - if(!node.dem){ - node.dem = this.createDEM(node); - } - } - } + + //{ // show big render target for debugging purposes + // var br = new ArrayBuffer(width*height*4); + // var bp = new Uint8Array(br); + // renderer.context.readPixels( 0, 0, width, height, + // renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, bp); + // + // var img = pixelsArrayToImage(bp, width, height); + // img.style.boder = "2px solid red"; + // img.style.position = "absolute"; + // img.style.top = "0px"; + // img.style.width = width + "px"; + // img.style.height = height + "px"; + // img.onclick = function(){document.body.removeChild(img)}; + // document.body.appendChild(img); + //} - // add child nodes to priorityQueue - for(var i = 0; i < 8; i++){ - if(!node.children[i]){ - continue; - } - - var child = node.children[i]; - - var sphere = child.boundingSphere; - var distance = sphere.center.distanceTo(camObjPos); - var radius = sphere.radius; + + + // find closest hit inside pixelWindow boundaries + var min = Number.MAX_VALUE; + var hit = null; + //console.log("finding closest hit"); + for(var u = 0; u < pickWindowSize; u++){ + for(var v = 0; v < pickWindowSize; v++){ + var offset = (u + v*pickWindowSize); + var distance = Math.pow(u - (pickWindowSize-1) / 2, 2) + Math.pow(v - (pickWindowSize-1) / 2, 2); - var fov = camera.fov / 2 * Math.PI / 180.0; - var pr = 1 / Math.tan(fov) * radius / Math.sqrt(distance * distance - radius * radius); + var pcIndex = pixels[4*offset + 3]; + pixels[4*offset + 3] = 0; + var pIndex = ibuffer[offset]; - var screenPixelRadius = renderer.domElement.clientHeight * pr; - if(screenPixelRadius < this.minimumNodePixelSize){ - continue; + if((pIndex !== 0 || pcIndex !== 0) && distance < min){ + + hit = { + pIndex: pIndex, + pcIndex: pcIndex - 1 + }; + min = distance; } + } + } + + if(hit){ + var point = {}; + + var pc = nodes[hit.pcIndex].sceneNode; + var attributes = pc.geometry.attributes; + + for (var property in attributes) { + if (attributes.hasOwnProperty(property)) { + var values = pc.geometry.attributes[property]; - var weight = pr; - if(distance - radius < 0){ - weight = Number.MAX_VALUE; + if(property === "position"){ + var positionArray = values.array; + var x = positionArray[3*hit.pIndex+0]; + var y = positionArray[3*hit.pIndex+1]; + var z = positionArray[3*hit.pIndex+2]; + var position = new THREE.Vector3(x, y, z); + position.applyMatrix4(this.matrixWorld); + + point[property] = position; + }else if(property === "indices"){ + + }else{ + if(values.itemSize === 1){ + point[property] = values.array[hit.pIndex]; + }else{ + var value = []; + for(var j = 0; j < values.itemSize; j++){ + value.push(values.array[values.itemSize*hit.pIndex + j]); + } + point[property] = value; + } + } } - - priorityQueue.push({node: child, parent: node, weight: weight}); } + + + return point; + }else{ + return null; } - - this.visibleNodes = visibleNodes; - this.visibleGeometry = visibleGeometry; - - // load next few unloaded geometries - for(var i = 0; i < Math.min(5, unloadedGeometry.length); i++){ - unloadedGeometry[i].load(); - } - }; -Potree.PointCloudOctree.prototype.updateVisibleBounds = function(){ +var demTime = 0; - var leafNodes = []; - for(var i = 0; i < this.visibleNodes.length; i++){ - var node = this.visibleNodes[i]; - var isLeaf = true; +Potree.PointCloudOctree.prototype.createDEM = function(node){ + var start = new Date().getTime(); + + var sceneNode = node.sceneNode; + + var world = sceneNode.matrixWorld; + + var boundingBox = sceneNode.boundingBox.clone().applyMatrix4(world); + var bbSize = boundingBox.size(); + var positions = sceneNode.geometry.attributes.position.array; + var demSize = 64; + var demMArray = new Array(demSize*demSize); + var dem = new Float32Array(demSize*demSize); + var n = positions.length / 3; + + var toWorld = function(dx, dy){ + var x = (dx * bbSize.x) / (demSize - 1) + boundingBox.min.x; + var y = dem[dx + dy * demSize]; + var z = (dy * bbSize.z) / (demSize - 1)+ boundingBox.min.z; - for(var j = 0; j < node.children.length; j++){ - var child = node.children[j]; - if(child instanceof Potree.PointCloudOctreeNode){ - isLeaf = isLeaf && !child.sceneNode.visible; - }else if(child instanceof Potree.PointCloudOctreeGeometryNode){ - isLeaf = true; - } - } + return [x, y, z]; + }; + + var toDem = function(x, y){ + var dx = parseInt(demSize * (x - boundingBox.min.x) / bbSize.x); + var dy = parseInt(demSize * (z - boundingBox.min.z) / bbSize.z); + dx = Math.min(dx, demSize - 1); + dy = Math.min(dy, demSize - 1); - if(isLeaf){ - leafNodes.push(node); + return [dx, dy]; + }; + + for(var i = 0; i < n; i++){ + var x = positions[3*i + 0]; + var y = positions[3*i + 1]; + var z = positions[3*i + 2]; + + var worldPos = new THREE.Vector3(x,y,z).applyMatrix4(world); + + var dx = parseInt(demSize * (worldPos.x - boundingBox.min.x) / bbSize.x); + var dy = parseInt(demSize * (worldPos.z - boundingBox.min.z) / bbSize.z); + dx = Math.min(dx, demSize - 1); + dy = Math.min(dy, demSize - 1); + + var index = dx + dy * demSize; + if(!demMArray[index]){ + demMArray[index] = []; } + demMArray[index].push(worldPos.y); + + //if(dem[dx + dy * demSize] === 0){ + // dem[dx + dy * demSize] = worldPos.y; + //}else{ + // dem[dx + dy * demSize] = Math.max(dem[dx + dy * demSize], worldPos.y); + //} } - this.visibleBounds.min = new THREE.Vector3( Infinity, Infinity, Infinity ); - this.visibleBounds.max = new THREE.Vector3( - Infinity, - Infinity, - Infinity ); - for(var i = 0; i < leafNodes.length; i++){ - var node = leafNodes[i]; + for(var i = 0; i < demMArray.length; i++){ + var values = demMArray[i]; - this.visibleBounds.expandByPoint(node.boundingBox.min); - this.visibleBounds.expandByPoint(node.boundingBox.max); + if(!values){ + dem[i] = 0; + }else if(values.length === 0){ + dem[i] = 0; + }else{ + var medianIndex = parseInt((values.length-1) / 2); + dem[i] = values[medianIndex]; + } } -}; - -Potree.PointCloudOctree.prototype.updateMaterial = function(material, visibleNodes, camera, renderer){ - material.fov = camera.fov * (Math.PI / 180); - material.screenWidth = renderer.domElement.clientWidth; - material.screenHeight = renderer.domElement.clientHeight; - material.spacing = this.pcoGeometry.spacing; - material.near = camera.near; - material.far = camera.far; - material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.size().x; + var box2 = new THREE.Box2(); + box2.expandByPoint(new THREE.Vector3(boundingBox.min.x, boundingBox.min.z)); + box2.expandByPoint(new THREE.Vector3(boundingBox.max.x, boundingBox.max.z)); - // update visibility texture - if(material.pointSizeType){ - if(material.pointSizeType === Potree.PointSizeType.ADAPTIVE - || material.pointColorType === Potree.PointColorType.OCTREE_DEPTH){ - - this.updateVisibilityTexture(material, visibleNodes); - } - } -}; - -Potree.PointCloudOctree.prototype.update = function(camera, renderer){ - - for(var i = 0; i < this.profileRequests.length; i++){ - this.profileRequests[i].update(); - } + var result = { + boundingBox: boundingBox, + boundingBox2D: box2, + dem: dem, + demSize: demSize + }; + + + + + //if(node.level === 6){ + // var geometry = new THREE.BufferGeometry(); + // var vertices = new Float32Array((demSize-1)*(demSize-1)*2*3*3); + // var offset = 0; + // for(var i = 0; i < demSize-1; i++){ + // for(var j = 0; j < demSize-1; j++){ + // //var offset = 18*i + 18*j*demSize; + // + // var dx = i; + // var dy = j; + // + // var v1 = toWorld(dx, dy); + // var v2 = toWorld(dx+1, dy); + // var v3 = toWorld(dx+1, dy+1); + // var v4 = toWorld(dx, dy+1); + // + // vertices[offset+0] = v3[0]; + // vertices[offset+1] = v3[1]; + // vertices[offset+2] = v3[2]; + // + // vertices[offset+3] = v2[0]; + // vertices[offset+4] = v2[1]; + // vertices[offset+5] = v2[2]; + // + // vertices[offset+6] = v1[0]; + // vertices[offset+7] = v1[1]; + // vertices[offset+8] = v1[2]; + // + // + // vertices[offset+9 ] = v3[0]; + // vertices[offset+10] = v3[1]; + // vertices[offset+11] = v3[2]; + // + // vertices[offset+12] = v1[0]; + // vertices[offset+13] = v1[1]; + // vertices[offset+14] = v1[2]; + // + // vertices[offset+15] = v4[0]; + // vertices[offset+16] = v4[1]; + // vertices[offset+17] = v4[2]; + // + // + // + // //var x = (dx * bbSize.min.x) / demSize + boundingBox.min.x; + // //var y = (dy * bbSize.min.y) / demSize + boundingBox.min.y; + // //var z = dem[dx + dy * demSize]; + // + // offset += 18; + // + // } + // } + // + // geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + // geometry.computeFaceNormals(); + // geometry.computeVertexNormals(); + // + // var material = new THREE.MeshNormalMaterial( { color: 0xff0000, shading: THREE.SmoothShading } ); + // var mesh = new THREE.Mesh( geometry, material ); + // viewer.scene.add(mesh); + //} - this.updateVisibility(camera, renderer); - this.updateMaterial(this.material, this.visibleNodes, camera, renderer); + //if(node.level == 0){ + // scene.add(mesh); + // + // var demb = new Uint8Array(demSize*demSize*4); + // for(var i = 0; i < demSize*demSize; i++){ + // demb[4*i + 0] = 255 * dem[i] / 300; + // demb[4*i + 1] = 255 * dem[i] / 300; + // demb[4*i + 2] = 255 * dem[i] / 300; + // demb[4*i + 3] = 255; + // } + // + // var img = pixelsArrayToImage(demb, demSize, demSize); + // img.style.boder = "2px solid red"; + // img.style.position = "absolute"; + // img.style.top = "0px"; + // img.style.width = "400px"; + // img.style.height = "200px"; + // var txt = document.createElement("div"); + // txt.innerHTML = node.name; + // //document.body.appendChild(txt); + // document.body.appendChild(img); + //} + - this.updateVisibleBounds(); - Potree.PointCloudOctree.lru.freeMemory(); + var end = new Date().getTime(); + var duration = end - start; - // TODO bounds -}; - - -Potree.PointCloudOctree.prototype.updateVisibilityTexture = function(material, visibleNodes){ + demTime += duration; - if(!material){ - return; - } - - var texture = material.visibleNodesTexture; - var data = texture.image.data; - - // copy array - visibleNodes = visibleNodes.slice(); - - // sort by level and index, e.g. r, r0, r3, r4, r01, r07, r30, ... - var sort = function(a, b){ - var na = a.name; - var nb = b.name; - if(na.length != nb.length) return na.length - nb.length; - if(na < nb) return -1; - if(na > nb) return 1; - return 0; - }; - visibleNodes.sort(sort); + return result; +}; +Potree.PointCloudOctree.prototype.getDEMHeight = function(position){ + var pos2 = new THREE.Vector2(position.x, position.z); - for(var i = 0; i < visibleNodes.length; i++){ - var node = visibleNodes[i]; - - var children = []; - for(var j = 0; j < 8; j++){ - var child = node.children[j]; - if(child instanceof Potree.PointCloudOctreeNode && child.sceneNode.visible && visibleNodes.indexOf(child) >= 0){ - children.push(child); - } - } - children.sort(function(a, b){ - if(a.name < b.name) return -1; - if(a.name > b.name) return 1; - return 0; - }); - - data[i*3 + 0] = 0; - data[i*3 + 1] = 0; - data[i*3 + 2] = 0; - for(var j = 0; j < children.length; j++){ - var child = children[j]; - var index = parseInt(child.name.substr(-1)); - data[i*3 + 0] += Math.pow(2, index); + var demHeight = function(dem){ + var demSize = dem.demSize; + var box = dem.boundingBox2D; + var insideBox = box.containsPoint(pos2); + if(box.containsPoint(pos2)){ + var uv = pos2.clone().sub(box.min).divide(box.size()); + var xy = uv.clone().multiplyScalar(demSize); - if(j === 0){ - var vArrayIndex = visibleNodes.indexOf(child); - data[i*3 + 1] = vArrayIndex - i; + var demHeight = 0; + + if((xy.x > 0.5 && xy.x < demSize - 0.5) && (xy.y > 0.5 && xy.y < demSize - 0.5)){ + var i = Math.floor(xy.x - 0.5); + var j = Math.floor(xy.y - 0.5); + i = (i === demSize - 1) ? (demSize-2) : i; + j = (j === demSize - 1) ? (demSize-2) : j; + + var u = xy.x - i - 0.5; + var v = xy.y - j - 0.5; + + var index00 = i + j * demSize; + var index10 = (i+1) + j * demSize; + var index01 = i + (j+1) * demSize; + var index11 = (i+1) + (j+1) * demSize; + + var height00 = dem.dem[index00]; + var height10 = dem.dem[index10]; + var height01 = dem.dem[index01]; + var height11 = dem.dem[index11]; + + if(height00 === 0 || height10 === 0 || height01 === 0 || height11 === 0){ + demHeight = null; + }else{ + + var hx1 = height00 * (1-u) + height10 * u; + var hx2 = height01 * (1-u) + height11 * u; + + demHeight = hx1 * (1-v) + hx2 * v; + } + + var bla; + }else{ + xy.x = Math.min(parseInt(Math.min(xy.x, demSize)), demSize-1); + xy.y = Math.min(parseInt(Math.min(xy.y, demSize)), demSize-1); + + var index = xy.x + xy.y * demSize; + demHeight = dem.dem[index]; } + + return demHeight; } - } - + + return null; + }; - texture.needsUpdate = true; -}; - -Potree.PointCloudOctree.prototype.nodeIntersectsProfile = function(node, profile){ - var bbWorld = node.boundingBox.clone().applyMatrix4(this.matrixWorld); - var bsWorld = bbWorld.getBoundingSphere(); + var height = null; - for(var i = 0; i < profile.points.length - 1; i++){ - var start = new THREE.Vector3(profile.points[i].x, bsWorld.center.y, profile.points[i].z); - var end = new THREE.Vector3(profile.points[i+1].x, bsWorld.center.y, profile.points[i+1].z); + var stack = []; + var chosenNode = null; + if(this.root.dem){ + stack.push(this.root); + } + while(stack.length > 0){ + var node = stack.shift(); + var dem = node.dem; - var ray1 = new THREE.Ray(start, new THREE.Vector3().subVectors(end, start).normalize()); - var ray2 = new THREE.Ray(end, new THREE.Vector3().subVectors(start, end).normalize()); + var demSize = dem.demSize; + var box = dem.boundingBox2D; + var insideBox = box.containsPoint(pos2); + if(!insideBox){ + continue; + } - if(ray1.isIntersectionSphere(bsWorld) && ray2.isIntersectionSphere(bsWorld)){ - return true; + var dh = demHeight(dem); + if(!height){ + height = dh; + }else if(dh != null && dh > 0){ + height = dh; + } + + if(node.level <= 6){ + for(var i = 0; i < 8; i++){ + var child = node.children[i]; + if(typeof child !== "undefined" && typeof child.dem !== "undefined"){ + stack.push(child); + } + } } } - return false; + + + return height; }; - -//Potree.PointCloudOctreeNode.prototype.intersectsProfile = function(profile){ -// var bbWorld = this.boundingBox.clone().applyMatrix4(this.octree.matrixWorld); -// var bsWorld = bbWorld.getBoundingSphere(); +//Potree.PointCloudOctree.prototype.generateTerain = function(){ +// var bb = this.boundingBox.clone().applyMatrix4(this.matrixWorld); // -// for(var i = 0; i < profile.points.length - 1; i++){ -// var start = new THREE.Vector3(profile.points[i].x, bsWorld.center.y, profile.points[i].z); -// var end = new THREE.Vector3(profile.points[i+1].x, bsWorld.center.y, profile.points[i+1].z); -// -// var ray1 = new THREE.Ray(start, new THREE.Vector3().subVectors(end, start).normalize()); -// var ray2 = new THREE.Ray(end, new THREE.Vector3().subVectors(start, end).normalize()); -// -// if(ray1.isIntersectionSphere(bsWorld) && ray2.isIntersectionSphere(bsWorld)){ -// return true; +// var width = 300; +// var height = 300; +// var geometry = new THREE.BufferGeometry(); +// var vertices = new Float32Array(width*height*3); +// +// var offset = 0; +// for(var i = 0; i < width; i++){ +// for( var j = 0; j < height; j++){ +// var u = i / width; +// var v = j / height; +// +// var x = u * bb.size().x + bb.min.x; +// var z = v * bb.size().z + bb.min.z; +// +// var y = this.getDEMHeight(new THREE.Vector3(x, 0, z)); +// if(!y){ +// y = 0; +// } +// +// vertices[offset + 0] = x; +// vertices[offset + 1] = y; +// vertices[offset + 2] = z; +// +// //var sm = new THREE.Mesh(sg); +// //sm.position.set(x,y,z); +// //scene.add(sm); +// +// offset += 3; // } // } // -// return false; +// geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); +// var material = new THREE.PointCloudMaterial({size: 20, color: 0x00ff00}); +// +// var pc = new THREE.PointCloud(geometry, material); +// scene.add(pc); +// //}; - - - - - - - - - - - - - - - - - - - -Potree.PointCloudOctree.prototype.nodesOnRay = function(nodes, ray){ - var nodesOnRay = []; - - var _ray = ray.clone(); - for(var i = 0; i < nodes.length; i++){ - var node = nodes[i]; - //var inverseWorld = new THREE.Matrix4().getInverse(node.matrixWorld); - var sphere = node.boundingSphere.clone().applyMatrix4(node.sceneNode.matrixWorld); - - if(_ray.isIntersectionSphere(sphere)){ - nodesOnRay.push(node); - } +Object.defineProperty(Potree.PointCloudOctree.prototype, "progress", { + get: function(){ + return this.visibleNodes.length / this.visibleGeometry.length; } - - return nodesOnRay; -}; - -Potree.PointCloudOctree.prototype.updateMatrixWorld = function( force ){ - //node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); - - if ( this.matrixAutoUpdate === true ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate === true || force === true ) { - - if ( this.parent === undefined ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; +}); - force = true; +var nodesLoadTimes = {}; - } +Potree.PointCloudOctreeGeometry = function(){ + this.url = null; + this.octreeDir = null; + this.spacing = 0; + this.boundingBox = null; + this.root = null; + this.numNodesLoading = 0; + this.nodes = null; + this.pointAttributes = null; + this.hierarchyStepSize = -1; + this.loader = null; }; -Potree.PointCloudOctree.prototype.hideDescendants = function(object){ - var stack = []; - for(var i = 0; i < object.children.length; i++){ - var child = object.children[i]; - if(child.visible){ - stack.push(child); - } - } - - while(stack.length > 0){ - var object = stack.shift(); - - object.visible = false; - - for(var i = 0; i < object.children.length; i++){ - var child = object.children[i]; - if(child.visible){ - stack.push(child); - } - } - } +Potree.PointCloudOctreeGeometryNode = function(name, pcoGeometry, boundingBox){ + this.id = Potree.PointCloudOctreeGeometryNode.IDCount++; + this.name = name; + this.index = parseInt(name.charAt(name.length-1)); + this.pcoGeometry = pcoGeometry; + this.geometry = null; + this.boundingBox = boundingBox; + this.boundingSphere = boundingBox.getBoundingSphere(); + this.children = {}; + this.numPoints = 0; + this.level = null; + this.loaded = false; + this.oneTimeDisposeHandlers = []; }; -Potree.PointCloudOctree.prototype.moveToOrigin = function(){ - this.position.set(0,0,0); - this.updateMatrixWorld(true); - var box = this.boundingBox; - var transform = this.matrixWorld; - var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); - this.position.set(0,0,0).sub(tBox.center()); +Potree.PointCloudOctreeGeometryNode.IDCount = 0; + +Potree.PointCloudOctreeGeometryNode.prototype = Object.create(Potree.PointCloudTreeNode.prototype); + +Potree.PointCloudOctreeGeometryNode.prototype.isGeometryNode = function(){ + return true; }; -Potree.PointCloudOctree.prototype.moveToGroundPlane = function(){ - this.updateMatrixWorld(true); - var box = this.boundingBox; - var transform = this.matrixWorld; - var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); - this.position.y += -tBox.min.y; +Potree.PointCloudOctreeGeometryNode.prototype.isTreeNode = function(){ + return false; }; -Potree.PointCloudOctree.prototype.getBoundingBoxWorld = function(){ - this.updateMatrixWorld(true); - var box = this.boundingBox; - var transform = this.matrixWorld; - var tBox = Potree.utils.computeTransformedBoundingBox(box, transform); +Potree.PointCloudOctreeGeometryNode.prototype.isLoaded = function(){ + return this.loaded; +}; + +Potree.PointCloudOctreeGeometryNode.prototype.getBoundingSphere = function(){ + return this.boundingSphere; +}; + +Potree.PointCloudOctreeGeometryNode.prototype.getBoundingBox = function(){ + return this.boundingBox; +}; + +Potree.PointCloudOctreeGeometryNode.prototype.getChildren = function(){ + var children = []; - return tBox; + for(var i = 0; i < 8; i++){ + if(this.children[i]){ + children.push(this.children[i]); + } + } + + return children; }; -/** - * returns points inside the profile points - * - * maxDepth: search points up to the given octree depth - * - * - * The return value is an array with all segments of the profile path - * var segment = { - * start: THREE.Vector3, - * end: THREE.Vector3, - * points: {} - * project: function() - * }; - * - * The project() function inside each segment can be used to transform - * that segments point coordinates to line up along the x-axis. - * - * - */ -Potree.PointCloudOctree.prototype.getPointsInProfile = function(profile, maxDepth, callback){ +Potree.PointCloudOctreeGeometryNode.prototype.getBoundingBox = function(){ + return this.boundingBox; +}; - if(callback){ - var request = new Potree.ProfileRequest(this, profile, maxDepth, callback); - this.profileRequests.push(request); - - return request; +Potree.PointCloudOctreeGeometryNode.prototype.getURL = function(){ + var url = ""; + + var version = this.pcoGeometry.loader.version; + + if(version.equalOrHigher("1.5")){ + url = this.pcoGeometry.octreeDir + "/" + this.getHierarchyPath() + "/" + this.name; + }else if(version.equalOrHigher("1.4")){ + url = this.pcoGeometry.octreeDir + "/" + this.name; + }else if(version.upTo("1.3")){ + url = this.pcoGeometry.octreeDir + "/" + this.name; } + + return url; +}; - var points = { - segments: [], - boundingBox: new THREE.Box3(), - projectedBoundingBox: new THREE.Box2() - }; +Potree.PointCloudOctreeGeometryNode.prototype.getHierarchyPath = function(){ + var path = "r/"; + + var hierarchyStepSize = this.pcoGeometry.hierarchyStepSize; + var indices = this.name.substr(1); - // evaluate segments - for(var i = 0; i < profile.points.length - 1; i++){ - var start = profile.points[i]; - var end = profile.points[i+1]; - var ps = this.getProfile(start, end, profile.width, maxDepth); - - var segment = { - start: start, - end: end, - points: ps, - project: null - }; - - points.segments.push(segment); - - points.boundingBox.expandByPoint(ps.boundingBox.min); - points.boundingBox.expandByPoint(ps.boundingBox.max); + var numParts = Math.floor(indices.length / hierarchyStepSize); + for(var i = 0; i < numParts; i++){ + path += indices.substr(i * hierarchyStepSize, hierarchyStepSize) + "/"; } - // add projection functions to the segments - var mileage = new THREE.Vector3(); - for(var i = 0; i < points.segments.length; i++){ - var segment = points.segments[i]; - var start = segment.start; - var end = segment.end; - - var project = function(_start, _end, _mileage, _boundingBox){ - var start = _start; - var end = _end; - var mileage = _mileage; - var boundingBox = _boundingBox; - - var xAxis = new THREE.Vector3(1,0,0); - var dir = new THREE.Vector3().subVectors(end, start); - dir.y = 0; - dir.normalize(); - var alpha = Math.acos(xAxis.dot(dir)); - if(dir.z > 0){ - alpha = -alpha; - } - - - return function(position){ - - var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, -boundingBox.min.y, -start.z); - var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); - var applyMileage = new THREE.Matrix4().makeTranslation(mileage.x, 0, 0); + path = path.slice(0,-1); - var pos = position.clone(); - pos.applyMatrix4(toOrigin); - pos.applyMatrix4(alignWithX); - pos.applyMatrix4(applyMileage); - - return pos; - }; - - }(start, end, mileage.clone(), points.boundingBox.clone()); - - segment.project = project; - - mileage.x += new THREE.Vector3(start.x, 0, start.z).distanceTo(new THREE.Vector3(end.x, 0, end.z)); - mileage.y += end.y - start.y; + return path; +}; + +Potree.PointCloudOctreeGeometryNode.prototype.addChild = function(child){ + this.children[child.index] = child; + child.parent = this; +}; + +Potree.PointCloudOctreeGeometryNode.prototype.load = function(){ + if(this.loading === true || this.loaded === true ||this.pcoGeometry.numNodesLoading > 3){ + return; + } + + this.loading = true; + + this.pcoGeometry.numNodesLoading++; + + + if(this.pcoGeometry.loader.version.equalOrHigher("1.5")){ + if((this.level % this.pcoGeometry.hierarchyStepSize) === 0 && this.hasChildren){ + this.loadHierachyThenPoints(); + }else{ + this.loadPoints(); + } + }else{ + this.loadPoints(); } - points.projectedBoundingBox.min.x = 0; - points.projectedBoundingBox.min.y = points.boundingBox.min.y; - points.projectedBoundingBox.max.x = mileage.x; - points.projectedBoundingBox.max.y = points.boundingBox.max.y; - return points; }; -/** - * returns points inside the given profile bounds. - * - * start: - * end: - * width: - * depth: search points up to the given octree depth - * callback: if specified, points are loaded before searching - * - * - */ -Potree.PointCloudOctree.prototype.getProfile = function(start, end, width, depth, callback){ - if(callback !== undefined){ - var request = new Potree.ProfileRequest(start, end, width, depth, callback); - this.profileRequests.push(request); - }else{ - var stack = []; - stack.push(this); - - var center = new THREE.Vector3().addVectors(end, start).multiplyScalar(0.5); - var length = new THREE.Vector3().subVectors(end, start).length(); - var side = new THREE.Vector3().subVectors(end, start).normalize(); - var up = new THREE.Vector3(0, 1, 0); - var forward = new THREE.Vector3().crossVectors(side, up).normalize(); - var N = forward; - var cutPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(N, start); - var halfPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(side, center); - - var inside = null; +Potree.PointCloudOctreeGeometryNode.prototype.loadPoints = function(){ + this.pcoGeometry.loader.load(this); +}; + + +Potree.PointCloudOctreeGeometryNode.prototype.loadHierachyThenPoints = function(){ + + var node = this; + + // load hierarchy + var callback = function(node, hbuffer){ + var count = hbuffer.byteLength / 5; + var view = new DataView(hbuffer); - var boundingBox = new THREE.Box3(); + var stack = []; + var children = view.getUint8(0); + var numPoints = view.getUint32(1, true); + node.numPoints = numPoints; + stack.push({children: children, numPoints: numPoints, name: node.name}); + var decoded = []; + var offset = 5; while(stack.length > 0){ - var object = stack.shift(); - - - var pointsFound = 0; - - if(object instanceof THREE.PointCloud){ - var geometry = object.geometry; - var positions = geometry.attributes.position; - var p = positions.array; - var numPoints = object.numPoints; - - if(!inside){ - inside = {}; + + var snode = stack.shift(); + var mask = 1; + for(var i = 0; i < 8; i++){ + if((snode.children & mask) !== 0){ + var childIndex = i; + var childName = snode.name + i; + + var childChildren = view.getUint8(offset); + var childNumPoints = view.getUint32(offset + 1, true); + + stack.push({children: childChildren, numPoints: childNumPoints, name: childName}); + + decoded.push({children: childChildren, numPoints: childNumPoints, name: childName}); - for (var property in geometry.attributes) { - if (geometry.attributes.hasOwnProperty(property)) { - if(property === "indices"){ - - }else{ - inside[property] = []; - } - } - } + offset += 5; } - for(var i = 0; i < numPoints; i++){ - var pos = new THREE.Vector3(p[3*i], p[3*i+1], p[3*i+2]); - pos.applyMatrix4(this.matrixWorld); - var distance = Math.abs(cutPlane.distanceToPoint(pos)); - var centerDistance = Math.abs(halfPlane.distanceToPoint(pos)); - - if(distance < width / 2 && centerDistance < length / 2){ - boundingBox.expandByPoint(pos); - - for (var property in geometry.attributes) { - if (geometry.attributes.hasOwnProperty(property)) { - - if(property === "position"){ - inside[property].push(pos); - }else if(property === "indices"){ - // skip indices - }else{ - var values = geometry.attributes[property]; - if(values.itemSize === 1){ - inside[property].push(values.array[i + j]); - }else{ - var value = []; - for(var j = 0; j < values.itemSize; j++){ - value.push(values.array[i*values.itemSize + j]); - } - inside[property].push(value); - } - } - - } - } - - - pointsFound++; - } - } + mask = mask * 2; } - //console.log("traversing: " + object.name + ", #points found: " + pointsFound); - - if(object == this || object.level < depth){ - for(var i = 0; i < object.children.length; i++){ - var child = object.children[i]; - if(child instanceof THREE.PointCloud){ - var sphere = child.boundingSphere.clone().applyMatrix4(child.matrixWorld); - if(cutPlane.distanceToSphere(sphere) < sphere.radius){ - stack.push(child); - } - } - } + if(offset === hbuffer.byteLength){ + break; } + } - inside.numPoints = inside.position.length; + //console.log(decoded); - var project = function(_start, _end){ - var start = _start; - var end = _end; + var nodes = {}; + nodes[node.name] = node; + var pco = node.pcoGeometry; + + + for( var i = 0; i < decoded.length; i++){ + var name = decoded[i].name; + var numPoints = decoded[i].numPoints; + var index = parseInt(name.charAt(name.length-1)); + var parentName = name.substring(0, name.length-1); + var parentNode = nodes[parentName]; + var level = name.length-1; + var boundingBox = Potree.POCLoader.createChildAABB(parentNode.boundingBox, index); - var xAxis = new THREE.Vector3(1,0,0); - var dir = new THREE.Vector3().subVectors(end, start); - dir.y = 0; - dir.normalize(); - var alpha = Math.acos(xAxis.dot(dir)); - if(dir.z > 0){ - alpha = -alpha; + var currentNode = new Potree.PointCloudOctreeGeometryNode(name, pco, boundingBox); + currentNode.level = level; + currentNode.numPoints = numPoints; + currentNode.hasChildren = decoded[i].children > 0; + parentNode.addChild(currentNode); + nodes[name] = currentNode; + } + + node.loadPoints(); + + }; + if((node.level % node.pcoGeometry.hierarchyStepSize) === 0){ + //var hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc"; + var hurl = node.pcoGeometry.octreeDir + "/" + node.getHierarchyPath() + "/" + node.name + ".hrc"; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', hurl, true); + xhr.responseType = 'arraybuffer'; + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + if (xhr.status === 200 || xhr.status === 0) { + var hbuffer = xhr.response; + callback(node, hbuffer); + } else { + console.log('Failed to load file! HTTP status: ' + xhr.status + ", file: " + url); + } } - - - return function(position){ - - var toOrigin = new THREE.Matrix4().makeTranslation(-start.x, -start.y, -start.z); - var alignWithX = new THREE.Matrix4().makeRotationY(-alpha); + }; + try{ + xhr.send(null); + }catch(e){ + console.log("fehler beim laden der punktwolke: " + e); + } + } - var pos = position.clone(); - pos.applyMatrix4(toOrigin); - pos.applyMatrix4(alignWithX); - - return pos; - }; - - }(start, end); - - inside.project = project; - inside.boundingBox = boundingBox; +}; + + +Potree.PointCloudOctreeGeometryNode.prototype.getNumPoints = function(){ + return this.numPoints; +}; + + +Potree.PointCloudOctreeGeometryNode.prototype.dispose = function(){ + if(this.geometry && this.parent != null){ + this.geometry.dispose(); + this.geometry = null; + this.loaded = false; - return inside; + //this.dispatchEvent( { type: 'dispose' } ); + for(var i = 0; i < this.oneTimeDisposeHandlers.length; i++){ + var handler = this.oneTimeDisposeHandlers[i]; + handler(); + } + this.oneTimeDisposeHandlers = []; + } +}; + +THREE.EventDispatcher.prototype.apply( Potree.PointCloudOctreeGeometryNode.prototype ); + +Potree.utils = function(){ + +}; + +Potree.utils.pathExists = function(url){ + var req = new XMLHttpRequest(); + req.open('GET', url, false); + req.send(null); + if (req.status !== 200) { + return false; + } + return true; +}; + +/** + * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561 + */ +Potree.utils.computeTransformedBoundingBox = function (box, transform) { + + var vertices = [ + new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), + new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), + new THREE.Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform), + new THREE.Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform), + new THREE.Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform), + new THREE.Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform), + new THREE.Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform), + new THREE.Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform), + new THREE.Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform) + ]; + + var boundingBox = new THREE.Box3(); + boundingBox.setFromPoints( vertices ); + + return boundingBox; +}; + +/** + * add separators to large numbers + * + * @param nStr + * @returns + */ +Potree.utils.addCommas = function(nStr){ + nStr += ''; + x = nStr.split('.'); + x1 = x[0]; + x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); } + return x1 + x2; +}; + +/** + * create worker from a string + * + * code from http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string + */ +Potree.utils.createWorker = function(code){ + var blob = new Blob([code], {type: 'application/javascript'}); + var worker = new Worker(URL.createObjectURL(blob)); + + return worker; }; -Potree.PointCloudOctree.prototype.getVisibleExtent = function(){ - return this.visibleBounds.applyMatrix4(this.matrixWorld); +Potree.utils.loadSkybox = function(path){ + var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100000 ); + var scene = new THREE.Scene(); + + var format = ".jpg"; + var urls = [ + path + 'px' + format, path + 'nx' + format, + path + 'py' + format, path + 'ny' + format, + path + 'pz' + format, path + 'nz' + format + ]; + + var textureCube = THREE.ImageUtils.loadTextureCube(urls, THREE.CubeRefractionMapping ); + + var shader = { + uniforms: { + "tCube": {type: "t", value: textureCube}, + "tFlip": {type: "f", value: -1} + }, + vertexShader: THREE.ShaderLib["cube"].vertexShader, + fragmentShader: THREE.ShaderLib["cube"].fragmentShader + }; + + var material = new THREE.ShaderMaterial({ + fragmentShader: shader.fragmentShader, + vertexShader: shader.vertexShader, + uniforms: shader.uniforms, + depthWrite: false, + side: THREE.BackSide + }); + var mesh = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material); + scene.add(mesh); + + return {"camera": camera, "scene": scene}; }; -/** - * - * - * - * params.pickWindowSize: Look for points inside a pixel window of this size. - * Use odd values: 1, 3, 5, ... - * - * - * TODO: only draw pixels that are actually read with readPixels(). - * - */ -Potree.PointCloudOctree.prototype.pick = function(renderer, camera, ray, params){ - // this function finds intersections by rendering point indices and then checking the point index at the mouse location. - // point indices are 3 byte and rendered to the RGB component. - // point cloud node indices are 1 byte and stored in the ALPHA component. - // this limits picking capabilities to 256 nodes and 2^24 points per node. - - var params = params || {}; - var pickWindowSize = params.pickWindowSize || 17; - - var nodes = this.nodesOnRay(this.visibleNodes, ray); - - if(nodes.length === 0){ - return null; - } - - var width = Math.ceil(renderer.domElement.clientWidth); - var height = Math.ceil(renderer.domElement.clientHeight); - - var pixelPos = new THREE.Vector3().addVectors(camera.position, ray.direction).project(camera); - pixelPos.addScalar(1).multiplyScalar(0.5); - pixelPos.x *= width; - pixelPos.y *= height; +Potree.utils.createGrid = function createGrid(width, length, spacing, color){ + var material = new THREE.LineBasicMaterial({ + color: color || 0x888888 + }); - if(!this.pickTarget){ - this.pickTarget = new THREE.WebGLRenderTarget( - 1, 1, - { minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat } - ); - }else if(this.pickTarget.width != width || this.pickTarget.height != height){ - this.pickTarget.dispose(); - this.pickTarget = new THREE.WebGLRenderTarget( - 1, 1, - { minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat } - ); + var geometry = new THREE.Geometry(); + for(var i = 0; i <= length; i++){ + geometry.vertices.push(new THREE.Vector3(-(spacing*width)/2, 0, i*spacing-(spacing*length)/2)); + geometry.vertices.push(new THREE.Vector3(+(spacing*width)/2, 0, i*spacing-(spacing*length)/2)); } - this.pickTarget.setSize(width, height); - // setup pick material. - // use the same point size functions as the main material to get the same point sizes. - if(!this.pickMaterial){ - this.pickMaterial = new Potree.PointCloudMaterial(); - this.pickMaterial.pointColorType = Potree.PointColorType.POINT_INDEX; + for(var i = 0; i <= width; i++){ + geometry.vertices.push(new THREE.Vector3(i*spacing-(spacing*width)/2, 0, -(spacing*length)/2)); + geometry.vertices.push(new THREE.Vector3(i*spacing-(spacing*width)/2, 0, +(spacing*length)/2)); } - this.pickMaterial.pointSizeType = this.material.pointSizeType; - this.pickMaterial.size = this.material.size; - this.pickMaterial.pointShape = this.material.pointShape; - this.pickMaterial.interpolate = this.material.interpolate; - this.pickMaterial.minSize = this.material.minSize; - this.pickMaterial.maxSize = this.material.maxSize; - this.pickMaterial.classification = this.material.classification; - - this.updateMaterial(this.pickMaterial, nodes, camera, renderer); + var line = new THREE.Line(geometry, material, THREE.LinePieces); + line.receiveShadow = true; + return line; +}; - var _gl = renderer.context; - - _gl.enable(_gl.SCISSOR_TEST); - _gl.scissor(pixelPos.x - (pickWindowSize - 1) / 2, pixelPos.y - (pickWindowSize - 1) / 2,pickWindowSize,pickWindowSize); - _gl.disable(_gl.SCISSOR_TEST); - - var material = this.pickMaterial; - - renderer.setRenderTarget( this.pickTarget ); - - renderer.state.setDepthTest( material.depthTest ); - renderer.state.setDepthWrite( material.depthWrite ); - renderer.state.setBlending( THREE.NoBlending ); - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - //TODO: UGLY HACK CHAMPIONSHIP SUBMISSION!! drawing first node does not work properly so we draw it twice. - if(nodes.length > 0){ - nodes.push(nodes[0]); - } - - for(var i = 0; i < nodes.length; i++){ - var object = nodes[i].sceneNode; - var geometry = object.geometry; - - if(!geometry.attributes.indices.buffer){ - continue; - } - - material.pcIndex = i; - - if(material.program){ - var program = material.program.program; - _gl.useProgram( program ); - //_gl.disable( _gl.BLEND ); + +Potree.utils.createBackgroundTexture = function(width, height){ + + function gauss(x, y){ + return (1 / (2 * Math.PI)) * Math.exp( - (x*x + y*y) / 2); + }; + + var map = THREE.ImageUtils.generateDataTexture( width, height, new THREE.Color() ); + map.magFilter = THREE.NearestFilter; + var data = map.image.data; + + //var data = new Uint8Array(width*height*4); + var chroma = [1, 1.5, 1.7]; + var max = gauss(0, 0); + + for(var x = 0; x < width; x++){ + for(var y = 0; y < height; y++){ + var u = 2 * (x / width) - 1; + var v = 2 * (y / height) - 1; - var attributePointer = _gl.getAttribLocation(program, "indices"); - var attributeSize = 4; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.attributes.indices.buffer ); - //if(!bufferSubmitted){ - // _gl.bufferData( _gl.ARRAY_BUFFER, new Uint8Array(geometry.attributes.indices.array), _gl.STATIC_DRAW ); - // bufferSubmitted = true; - //} - _gl.enableVertexAttribArray( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.UNSIGNED_BYTE, true, 0, 0 ); - - _gl.uniform1f(material.program.uniforms.pcIndex, material.pcIndex); - } - - renderer.renderBufferDirect(camera, [], null, material, geometry, object); + var i = x + width*y; + var d = gauss(2*u, 2*v) / max; + var r = (Math.random() + Math.random() + Math.random()) / 3; + r = (d * 0.5 + 0.5) * r * 0.03; + r = r * 0.4; + + //d = Math.pow(d, 0.6); + + data[3*i+0] = 255 * (d / 15 + 0.05 + r) * chroma[0]; + data[3*i+1] = 255 * (d / 15 + 0.05 + r) * chroma[1]; + data[3*i+2] = 255 * (d / 15 + 0.05 + r) * chroma[2]; + + //data[4*i+3] = 255; - var program = material.program.program; - _gl.useProgram( program ); - var attributePointer = _gl.getAttribLocation(program, "indices"); - _gl.disableVertexAttribArray( attributePointer ); + } } - var pixelCount = pickWindowSize * pickWindowSize; - var buffer = new ArrayBuffer(pixelCount*4); - var pixels = new Uint8Array(buffer); - var ibuffer = new Uint32Array(buffer); - renderer.context.readPixels( - pixelPos.x - (pickWindowSize-1) / 2, pixelPos.y - (pickWindowSize-1) / 2, - pickWindowSize, pickWindowSize, - renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, pixels); - + return map; +}; - //{ // show big render target for debugging purposes - // var br = new ArrayBuffer(width*height*4); - // var bp = new Uint8Array(br); - // renderer.context.readPixels( 0, 0, width, height, - // renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, bp); - // - // var img = pixelsArrayToImage(bp, width, height); - // img.style.boder = "2px solid red"; - // img.style.position = "absolute"; - // img.style.top = "0px"; - // img.style.width = width + "px"; - // img.style.height = height + "px"; - // img.onclick = function(){document.body.removeChild(img)}; - // document.body.appendChild(img); - //} - - - - // find closest hit inside pixelWindow boundaries - var min = Number.MAX_VALUE; - var hit = null; - //console.log("finding closest hit"); - for(var u = 0; u < pickWindowSize; u++){ - for(var v = 0; v < pickWindowSize; v++){ - var offset = (u + v*pickWindowSize); - var distance = Math.pow(u - (pickWindowSize-1) / 2, 2) + Math.pow(v - (pickWindowSize-1) / 2, 2); - - var pcIndex = pixels[4*offset + 3]; - pixels[4*offset + 3] = 0; - var pIndex = ibuffer[offset]; - - if((pIndex !== 0 || pcIndex !== 0) && distance < min){ - - hit = { - pIndex: pIndex, - pcIndex: pcIndex - }; - min = distance; - } - } - } + + +function getMousePointCloudIntersection(mouse, camera, renderer, pointclouds){ + var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); + vector.unproject(camera); + + var direction = vector.sub(camera.position).normalize(); + var ray = new THREE.Ray(camera.position, direction); - if(hit){ - var point = {}; - - var pc = nodes[hit.pcIndex].sceneNode; - var attributes = pc.geometry.attributes; - - for (var property in attributes) { - if (attributes.hasOwnProperty(property)) { - var values = pc.geometry.attributes[property]; - - if(property === "position"){ - var positionArray = pc.geometry.attributes.position.array; - var x = positionArray[3*hit.pIndex+0]; - var y = positionArray[3*hit.pIndex+1]; - var z = positionArray[3*hit.pIndex+2]; - var position = new THREE.Vector3(x, y, z); - position.applyMatrix4(this.matrixWorld); - - point[property] = position; - }else if(property === "indices"){ - - }else{ - if(values.itemSize === 1){ - point[property] = values.array[i + j]; - }else{ - var value = []; - for(var j = 0; j < values.itemSize; j++){ - value.push(values.array[i*values.itemSize + j]); - } - point[property] = value; - } - } - } + var closestPoint = null; + var closestPointDistance = null; + + for(var i = 0; i < pointclouds.length; i++){ + var pointcloud = pointclouds[i]; + var point = pointcloud.pick(renderer, camera, ray); + + if(!point){ + continue; } + var distance = camera.position.distanceTo(point.position); - return point; - }else{ - return null; + if(!closestPoint || distance < closestPointDistance){ + closestPoint = point; + closestPointDistance = distance; + } } + + return closestPoint ? closestPoint.position : null; }; + + +function pixelsArrayToImage(pixels, width, height){ + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; -var demTime = 0; + var context = canvas.getContext('2d'); + + pixels = new pixels.constructor(pixels); + + for(var i = 0; i < pixels.length; i++){ + pixels[i*4 + 3] = 255; + } -Potree.PointCloudOctree.prototype.createDEM = function(node){ - var start = new Date().getTime(); + var imageData = context.createImageData(width, height); + imageData.data.set(pixels); + context.putImageData(imageData, 0, 0); - var sceneNode = node.sceneNode; + var img = new Image(); + img.src = canvas.toDataURL(); + img.style.transform = "scaleY(-1)"; + + return img; +}; - var world = sceneNode.matrixWorld; +function projectedRadius(radius, fov, distance, screenHeight){ + var projFactor = (1 / Math.tan(fov / 2)) / distance; + projFactor = projFactor * screenHeight / 2; + + return radius * projFactor; +}; + + +Potree.utils.topView = function(camera, node){ + camera.position.set(0, 1, 0); + camera.rotation.set(-Math.PI / 2, 0, 0); + camera.zoomTo(node, 1); +}; - var boundingBox = sceneNode.boundingBox.clone().applyMatrix4(world); - var bbSize = boundingBox.size(); - var positions = sceneNode.geometry.attributes.position.array; - var demSize = 64; - var demMArray = new Array(demSize*demSize); - var dem = new Float32Array(demSize*demSize); - var n = positions.length / 3; +Potree.utils.frontView = function(camera, node){ + camera.position.set(0, 0, 1); + camera.rotation.set(0, 0, 0); + camera.zoomTo(node, 1); +}; + + +Potree.utils.leftView = function(camera, node){ + camera.position.set(-1, 0, 0); + camera.rotation.set(0, -Math.PI / 2, 0); + camera.zoomTo(node, 1); +}; + +Potree.utils.rightView = function(camera, node){ + camera.position.set(1, 0, 0); + camera.rotation.set(0, Math.PI / 2, 0); + camera.zoomTo(node, 1); +}; - var toWorld = function(dx, dy){ - var x = (dx * bbSize.x) / (demSize - 1) + boundingBox.min.x; - var y = dem[dx + dy * demSize]; - var z = (dy * bbSize.z) / (demSize - 1)+ boundingBox.min.z; - - return [x, y, z]; - }; +/** + * + * 0: no intersection + * 1: intersection + * 2: fully inside + */ +Potree.utils.frustumSphereIntersection = function(frustum, sphere){ + var planes = frustum.planes; + var center = sphere.center; + var negRadius = - sphere.radius; + + var minDistance = Number.MAX_VALUE; - var toDem = function(x, y){ - var dx = parseInt(demSize * (x - boundingBox.min.x) / bbSize.x); - var dy = parseInt(demSize * (z - boundingBox.min.z) / bbSize.z); - dx = Math.min(dx, demSize - 1); - dy = Math.min(dy, demSize - 1); - - return [dx, dy]; - }; + for ( var i = 0; i < 6; i ++ ) { + + var distance = planes[ i ].distanceToPoint( center ); + + if ( distance < negRadius ) { + + return 0; - for(var i = 0; i < n; i++){ - var x = positions[3*i + 0]; - var y = positions[3*i + 1]; - var z = positions[3*i + 2]; - - var worldPos = new THREE.Vector3(x,y,z).applyMatrix4(world); - - var dx = parseInt(demSize * (worldPos.x - boundingBox.min.x) / bbSize.x); - var dy = parseInt(demSize * (worldPos.z - boundingBox.min.z) / bbSize.z); - dx = Math.min(dx, demSize - 1); - dy = Math.min(dy, demSize - 1); - - var index = dx + dy * demSize; - if(!demMArray[index]){ - demMArray[index] = []; } - demMArray[index].push(worldPos.y); - - //if(dem[dx + dy * demSize] === 0){ - // dem[dx + dy * demSize] = worldPos.y; - //}else{ - // dem[dx + dy * demSize] = Math.max(dem[dx + dy * demSize], worldPos.y); - //} - } - - for(var i = 0; i < demMArray.length; i++){ - var values = demMArray[i]; - if(!values){ - dem[i] = 0; - }else if(values.length === 0){ - dem[i] = 0; - }else{ - var medianIndex = parseInt((values.length-1) / 2); - dem[i] = values[medianIndex]; - } + minDistance = Math.min(minDistance, distance); + } - - var box2 = new THREE.Box2(); - box2.expandByPoint(new THREE.Vector3(boundingBox.min.x, boundingBox.min.z)); - box2.expandByPoint(new THREE.Vector3(boundingBox.max.x, boundingBox.max.z)); - - var result = { - boundingBox: boundingBox, - boundingBox2D: box2, - dem: dem, - demSize: demSize - }; - - + + return (minDistance >= sphere.radius) ? 2 : 1; +}; - //if(node.level == 2){ - // var geometry = new THREE.BufferGeometry(); - // var vertices = new Float32Array((demSize-1)*(demSize-1)*2*3*3); - // var offset = 0; - // for(var i = 0; i < demSize-1; i++){ - // for(var j = 0; j < demSize-1; j++){ - // //var offset = 18*i + 18*j*demSize; - // - // var dx = i; - // var dy = j; - // - // var v1 = toWorld(dx, dy); - // var v2 = toWorld(dx+1, dy); - // var v3 = toWorld(dx+1, dy+1); - // var v4 = toWorld(dx, dy+1); - // - // vertices[offset+0] = v3[0]; - // vertices[offset+1] = v3[1]; - // vertices[offset+2] = v3[2]; - // - // vertices[offset+3] = v2[0]; - // vertices[offset+4] = v2[1]; - // vertices[offset+5] = v2[2]; - // - // vertices[offset+6] = v1[0]; - // vertices[offset+7] = v1[1]; - // vertices[offset+8] = v1[2]; - // - // - // vertices[offset+9 ] = v3[0]; - // vertices[offset+10] = v3[1]; - // vertices[offset+11] = v3[2]; - // - // vertices[offset+12] = v1[0]; - // vertices[offset+13] = v1[1]; - // vertices[offset+14] = v1[2]; - // - // vertices[offset+15] = v4[0]; - // vertices[offset+16] = v4[1]; - // vertices[offset+17] = v4[2]; - // - // - // - // //var x = (dx * bbSize.min.x) / demSize + boundingBox.min.x; - // //var y = (dy * bbSize.min.y) / demSize + boundingBox.min.y; - // //var z = dem[dx + dy * demSize]; - // - // offset += 18; - // - // } - // } - // - // geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); - // geometry.computeFaceNormals(); - // geometry.computeVertexNormals(); - // - // var material = new THREE.MeshNormalMaterial( { color: 0xff0000, shading: THREE.SmoothShading } ); - // var mesh = new THREE.Mesh( geometry, material ); - // scene.add(mesh); - //} - // - // - //if(node.level == 0){ - // scene.add(mesh); - // - // var demb = new Uint8Array(demSize*demSize*4); - // for(var i = 0; i < demSize*demSize; i++){ - // demb[4*i + 0] = 255 * dem[i] / 300; - // demb[4*i + 1] = 255 * dem[i] / 300; - // demb[4*i + 2] = 255 * dem[i] / 300; - // demb[4*i + 3] = 255; - // } - // - // var img = pixelsArrayToImage(demb, demSize, demSize); - // img.style.boder = "2px solid red"; - // img.style.position = "absolute"; - // img.style.top = "0px"; - // img.style.width = "400px"; - // img.style.height = "200px"; - // var txt = document.createElement("div"); - // txt.innerHTML = node.name; - // //document.body.appendChild(txt); - // document.body.appendChild(img); - //} +Potree.utils.screenPass = new function(){ + + this.screenScene = new THREE.Scene(); + this.screenQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2, 0)); + this.screenQuad.material.depthTest = true; + this.screenQuad.material.depthWrite = true; + this.screenQuad.material.transparent = true; + this.screenScene.add(this.screenQuad); + this.camera = new THREE.Camera(); + this.render = function(renderer, material, target){ + this.screenQuad.material = material; + + if(typeof target === undefined){ + renderer.render(this.screenScene, this.camera); + }else{ + renderer.render(this.screenScene, this.camera, target); + } + }; +}(); - var end = new Date().getTime(); - var duration = end - start; - demTime += duration; - return result; -}; +Potree.Features = function(){ -Potree.PointCloudOctree.prototype.getDEMHeight = function(position){ - var pos2 = new THREE.Vector2(position.x, position.z); - - var demHeight = function(dem){ - var demSize = dem.demSize; - var box = dem.boundingBox2D; - var insideBox = box.containsPoint(pos2); - if(box.containsPoint(pos2)){ - var uv = pos2.clone().sub(box.min).divide(box.size()); - var xy = uv.clone().multiplyScalar(demSize); - - var demHeight = 0; - - if((xy.x > 0.5 && xy.x < demSize - 0.5) && (xy.y > 0.5 && xy.y < demSize - 0.5)){ - var i = Math.floor(xy.x - 0.5); - var j = Math.floor(xy.y - 0.5); - i = (i === demSize - 1) ? (demSize-2) : i; - j = (j === demSize - 1) ? (demSize-2) : j; - - var u = xy.x - i - 0.5; - var v = xy.y - j - 0.5; - - var index00 = i + j * demSize; - var index10 = (i+1) + j * demSize; - var index01 = i + (j+1) * demSize; - var index11 = (i+1) + (j+1) * demSize; + var ftCanvas = document.createElement("canvas"); + var gl = ftCanvas.getContext("webgl") || ftCanvas.getContext("experimental-webgl"); + if (gl === null) + return null; + + // -- code taken from THREE.WebGLRenderer -- + var _vertexShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ); + var _vertexShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ); + var _vertexShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.LOW_FLOAT ); + + var _fragmentShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ); + var _fragmentShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ); + var _fragmentShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.LOW_FLOAT ); + + var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; + var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + // ----------------------------------------- + + var precision; + if(highpAvailable){ + precision = "highp"; + }else if(mediumpAvailable){ + precision = "mediump"; + }else{ + precision = "lowp"; + } + + return { + SHADER_INTERPOLATION: { + isSupported: function(){ + + //if(typeof this.shaderInterpolationSupported === "undefined"){ + // var material = new Potree.PointCloudMaterial(); + // material.interpolate = true; + // + // var vs = gl.createShader(gl.VERTEX_SHADER); + // var fs = gl.createShader(gl.FRAGMENT_SHADER); + // gl.shaderSource(vs, material.vertexShader); + // gl.shaderSource(fs, material.fragmentShader); + // + // gl.compileShader(vs); + // gl.compileShader(fs); + // + // var successVS = gl.getShaderParameter(vs, gl.COMPILE_STATUS); + // var successFS = gl.getShaderParameter(fs, gl.COMPILE_STATUS); + // this.shaderInterpolationSupported = successVS && successFS; + //} + // + //return this.shaderInterpolationSupported; + + + var supported = true; + + supported = supported && gl.getExtension("EXT_frag_depth"); + supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; + + return supported; + } + }, + SHADER_SPLATS: { + isSupported: function(){ + + var supported = true; + + supported = supported && gl.getExtension("EXT_frag_depth"); + supported = supported && gl.getExtension("OES_texture_float"); + supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; + + return supported; + + } + + }, + SHADER_EDL: { + isSupported: function(){ - var height00 = dem.dem[index00]; - var height10 = dem.dem[index10]; - var height01 = dem.dem[index01]; - var height11 = dem.dem[index11]; + var supported = true; - if(height00 === 0 || height10 === 0 || height01 === 0 || height11 === 0){ - demHeight = null; - }else{ + supported = supported && gl.getExtension("EXT_frag_depth"); + supported = supported && gl.getExtension("OES_texture_float"); + supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; - var hx1 = height00 * (1-u) + height10 * u; - var hx2 = height01 * (1-u) + height11 * u; - - demHeight = hx1 * (1-v) + hx2 * v; - } + return supported; - var bla; - }else{ - xy.x = Math.min(parseInt(Math.min(xy.x, demSize)), demSize-1); - xy.y = Math.min(parseInt(Math.min(xy.y, demSize)), demSize-1); - - var index = xy.x + xy.y * demSize; - demHeight = dem.dem[index]; } - - - return demHeight; - } - return null; + }, + precision: precision }; + +}(); + +/** + * adapted from http://stemkoski.github.io/Three.js/Sprite-Text-Labels.html + */ + +Potree.TextSprite = function(text){ + + THREE.Object3D.call(this); - var height = null; + var scope = this; + + var texture = new THREE.Texture(); + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + var spriteMaterial = new THREE.SpriteMaterial( { + map: texture, + useScreenCoordinates: false, + depthTest: false, + depthWrite: false} ); - var stack = []; - var chosenNode = null; - if(this.root.dem){ - stack.push(this.root); + this.material = spriteMaterial; + this.sprite = new THREE.Sprite(spriteMaterial); + this.add(this.sprite); + + //THREE.Sprite.call(this, spriteMaterial); + + this.borderThickness = 4; + this.fontface = "Arial"; + this.fontsize = 28; + this.borderColor = { r:0, g:0, b:0, a:1.0 }; + this.backgroundColor = { r:255, g:255, b:255, a:1.0 }; + this.textColor = {r: 255, g: 255, b: 255, a: 1.0}; + this.text = ""; + + this.setText(text); +}; + +Potree.TextSprite.prototype = new THREE.Object3D(); + +Potree.TextSprite.prototype.setText = function(text){ + if(this.text !== text){ + this.text = text; + + this.update(); } - while(stack.length > 0){ - var node = stack.shift(); - var dem = node.dem; +}; + +Potree.TextSprite.prototype.setTextColor = function(color){ + this.textColor = color; + + this.update(); +}; + +Potree.TextSprite.prototype.setBorderColor = function(color){ + this.borderColor = color; + + this.update(); +}; + +Potree.TextSprite.prototype.setBackgroundColor = function(color){ + this.backgroundColor = color; + + this.update(); +}; + +Potree.TextSprite.prototype.update = function(){ + + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + context.font = "Bold " + this.fontsize + "px " + this.fontface; + + // get size data (height depends only on font size) + var metrics = context.measureText( this.text ); + var textWidth = metrics.width; + var margin = 5; + var spriteWidth = 2*margin + textWidth + 2 * this.borderThickness; + var spriteHeight = this.fontsize * 1.4 + 2 * this.borderThickness; + + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + context.canvas.width = spriteWidth; + context.canvas.height = spriteHeight; + context.font = "Bold " + this.fontsize + "px " + this.fontface; + + // background color + context.fillStyle = "rgba(" + this.backgroundColor.r + "," + this.backgroundColor.g + "," + + this.backgroundColor.b + "," + this.backgroundColor.a + ")"; + // border color + context.strokeStyle = "rgba(" + this.borderColor.r + "," + this.borderColor.g + "," + + this.borderColor.b + "," + this.borderColor.a + ")"; + + context.lineWidth = this.borderThickness; + this.roundRect(context, this.borderThickness/2, this.borderThickness/2, + textWidth + this.borderThickness + 2*margin, this.fontsize * 1.4 + this.borderThickness, 6); + + // text color + context.strokeStyle = "rgba(0, 0, 0, 1.0)"; + context.strokeText( this.text, this.borderThickness + margin, this.fontsize + this.borderThickness); + + context.fillStyle = "rgba(" + this.textColor.r + "," + this.textColor.g + "," + + this.textColor.b + "," + this.textColor.a + ")"; + context.fillText( this.text, this.borderThickness + margin, this.fontsize + this.borderThickness); + + + var texture = new THREE.Texture(canvas); + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.needsUpdate = true; + + //var spriteMaterial = new THREE.SpriteMaterial( + // { map: texture, useScreenCoordinates: false } ); + this.sprite.material.map = texture; - var demSize = dem.demSize; - var box = dem.boundingBox2D; - var insideBox = box.containsPoint(pos2); - if(!box.containsPoint(pos2)){ - continue; - } + this.sprite.scale.set(spriteWidth*0.01,spriteHeight*0.01,1.0); - var dh = demHeight(dem); - if(!height){ - height = dh; - }else if(dh != null && dh > 0){ - height = dh; - } + //this.material = spriteMaterial; +}; - if(node.level <= 2){ - for(var i = 0; i < node.children.length; i++){ - var child = node.children[i]; - if(child.dem){ - stack.push(child); - } - } - } +Potree.TextSprite.prototype.roundRect = function(ctx, x, y, w, h, r) { + ctx.beginPath(); + ctx.moveTo(x+r, y); + ctx.lineTo(x+w-r, y); + ctx.quadraticCurveTo(x+w, y, x+w, y+r); + ctx.lineTo(x+w, y+h-r); + ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); + ctx.lineTo(x+r, y+h); + ctx.quadraticCurveTo(x, y+h, x, y+h-r); + ctx.lineTo(x, y+r); + ctx.quadraticCurveTo(x, y, x+r, y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); +}; + +Potree.Version = function(version){ + this.version = version; + var vmLength = (version.indexOf(".") === -1) ? version.length : version.indexOf("."); + this.versionMajor = parseInt(version.substr(0, vmLength)); + this.versionMinor = parseInt(version.substr(vmLength + 1)); + if(this.versionMinor.length === 0){ + this.versionMinor = 0; } - - - return height; }; -Potree.PointCloudOctree.prototype.generateTerain = function(){ - var bb = this.boundingBox.clone().applyMatrix4(this.matrixWorld); - - var width = 300; - var height = 300; - var geometry = new THREE.BufferGeometry(); - var vertices = new Float32Array(width*height*3); +Potree.Version.prototype.newerThan = function(version){ + var v = new Potree.Version(version); - var offset = 0; - for(var i = 0; i < width; i++){ - for( var j = 0; j < height; j++){ - var u = i / width; - var v = j / height; - - var x = u * bb.size().x + bb.min.x; - var z = v * bb.size().z + bb.min.z; - - var y = this.getDEMHeight(new THREE.Vector3(x, 0, z)); - if(!y){ - y = 0; - } - - vertices[offset + 0] = x; - vertices[offset + 1] = y; - vertices[offset + 2] = z; - - //var sm = new THREE.Mesh(sg); - //sm.position.set(x,y,z); - //scene.add(sm); - - offset += 3; - } + if( this.versionMajor > v.versionMajor){ + return true; + }else if( this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor){ + return true; + }else{ + return false; } - - geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); - var material = new THREE.PointCloudMaterial({size: 20, color: 0x00ff00}); - - var pc = new THREE.PointCloud(geometry, material); - scene.add(pc); - }; -Object.defineProperty(Potree.PointCloudOctree.prototype, "progress", { - get: function(){ - return this.visibleNodes.length / this.visibleGeometry.length; +Potree.Version.prototype.equalOrHigher = function(version){ + var v = new Potree.Version(version); + + if( this.versionMajor > v.versionMajor){ + return true; + }else if( this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor){ + return true; + }else{ + return false; } -}); - -var nodesLoadTimes = {}; - -Potree.PointCloudOctreeGeometry = function(){ - Potree.PointCloudOctree.lru = Potree.PointCloudOctree.lru || new LRU(); - - this.url = null; - this.octreeDir = null; - this.spacing = 0; - this.boundingBox = null; - this.root = null; - this.numNodesLoading = 0; - this.nodes = null; - this.pointAttributes = null; - this.hierarchyStepSize = -1; - this.loader = null; }; -Potree.PointCloudOctreeGeometryNode = function(name, pcoGeometry, boundingBox){ - this.id = Potree.PointCloudOctreeGeometryNode.IDCount++; - this.name = name; - this.index = parseInt(name.charAt(name.length-1)); - this.pcoGeometry = pcoGeometry; - this.geometry = null; - this.boundingBox = boundingBox; - this.boundingSphere = boundingBox.getBoundingSphere(); - this.children = {}; - this.numPoints = 0; - this.level = null; - this.loaded = false; - this.oneTimeDisposeHandlers = []; +Potree.Version.prototype.upTo = function(version){ + return !this.newerThan(version); }; -Potree.PointCloudOctreeGeometryNode.IDCount = 0; - -Potree.PointCloudOctreeGeometryNode.prototype.getURL = function(){ - var url = ""; +Potree.Measure = function(){ + var scope = this; - var version = this.pcoGeometry.loader.version; + THREE.Object3D.call( this ); - if(version.equalOrHigher("1.5")){ - url = this.pcoGeometry.octreeDir + "/" + this.getHierarchyPath() + "/" + this.name; - }else if(version.equalOrHigher("1.4")){ - url = this.pcoGeometry.octreeDir + "/" + this.name; - }else if(version.upTo("1.3")){ - url = this.pcoGeometry.octreeDir + "/" + this.name; - } + this.points = []; + this._showDistances = true; + this._showCoordinates = false; + this._showArea = true; + this._closed = true; + this._showAngles = false; + this.maxMarkers = Number.MAX_SAFE_INTEGER; - return url; -}; - -Potree.PointCloudOctreeGeometryNode.prototype.getHierarchyPath = function(){ - var path = "r/"; - - var hierarchyStepSize = this.pcoGeometry.hierarchyStepSize; - var indices = this.name.substr(1); + this.spheres = []; + this.edges = []; + this.sphereLabels = []; + this.edgeLabels = []; + this.angleLabels = []; + this.coordinateLabels = []; - var numParts = Math.floor(indices.length / hierarchyStepSize); - for(var i = 0; i < numParts; i++){ - path += indices.substr(i * hierarchyStepSize, hierarchyStepSize) + "/"; - } + this.areaLabel = new Potree.TextSprite(""); + this.areaLabel.setBorderColor({r:0, g:0, b:0, a:0.8}); + this.areaLabel.setBackgroundColor({r:0, g:0, b:0, a:0.3}); + this.areaLabel.setTextColor({r:180, g:220, b:180, a:1.0}); + this.areaLabel.material.depthTest = false; + this.areaLabel.material.opacity = 1; + this.areaLabel.visible = false;; + this.add(this.areaLabel); - path = path.slice(0,-1); - - return path; -}; - -Potree.PointCloudOctreeGeometryNode.prototype.addChild = function(child){ - this.children[child.index] = child; - child.parent = this; -}; - -Potree.PointCloudOctreeGeometryNode.prototype.load = function(){ - if(this.loading === true || this.loaded === true ||this.pcoGeometry.numNodesLoading > 3){ - return; - } + var sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10); + this.color = new THREE.Color( 0xff0000 ); - this.loading = true; + var createSphereMaterial = function(){ + var sphereMaterial = new THREE.MeshLambertMaterial({ + shading: THREE.SmoothShading, + color: scope.color, + ambient: 0xaaaaaa, + depthTest: false, + depthWrite: false} + ); + + return sphereMaterial; + }; - //if(Potree.PointCloudOctree.lru.numPoints + this.numPoints >= Potree.pointLoadLimit){ - // Potree.PointCloudOctree.disposeLeastRecentlyUsed(this.numPoints); - //} + var moveEvent = function(event){ + event.target.material.emissive.setHex(0x888888); + }; - this.pcoGeometry.numNodesLoading++; + var leaveEvent = function(event){ + event.target.material.emissive.setHex(0x000000); + }; + var dragEvent = function(event){ + var tool = event.tool; + var dragstart = tool.dragstart; + var mouse = tool.mouse; - if(this.pcoGeometry.loader.version.equalOrHigher("1.5")){ - if((this.level % this.pcoGeometry.hierarchyStepSize) === 0 && this.hasChildren){ - this.loadHierachyThenPoints(); - }else{ - this.loadPoints(); + var point = tool.getMousePointCloudIntersection(); + + if(point){ + var position = point.position; + var index = scope.spheres.indexOf(tool.dragstart.object); + //scope.setPosition(index, position); + scope.setMarker(index, point); } - }else{ - this.loadPoints(); - } + + event.event.stopImmediatePropagation(); + }; + var dropEvent = function(event){ -}; - -Potree.PointCloudOctreeGeometryNode.prototype.loadPoints = function(){ - this.pcoGeometry.loader.load(this); -}; - - -Potree.PointCloudOctreeGeometryNode.prototype.loadHierachyThenPoints = function(){ - - var node = this; - - // load hierarchy - var callback = function(node, hbuffer){ - var count = hbuffer.byteLength / 5; - var view = new DataView(hbuffer); + }; + + this.addMarker = function(point){ + if(point instanceof THREE.Vector3){ + point = {position: point}; + } + this.points.push(point); - var stack = []; - var children = view.getUint8(0); - var numPoints = view.getUint32(1, true); - node.numPoints = numPoints; - stack.push({children: children, numPoints: numPoints, name: node.name}); + // sphere + var sphere = new THREE.Mesh(sphereGeometry, createSphereMaterial()); + sphere.addEventListener("move", moveEvent); + sphere.addEventListener("leave", leaveEvent); + sphere.addEventListener("drag", dragEvent); + sphere.addEventListener("drop", dropEvent); + + this.add(sphere); + this.spheres.push(sphere); + + { // edges + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3()); + lineGeometry.colors.push(this.color, this.color, this.color); + var lineMaterial = new THREE.LineBasicMaterial( { + linewidth: 1 + }); + lineMaterial.depthTest = false; + var edge = new THREE.Line(lineGeometry, lineMaterial); + edge.visible = true; + + this.add(edge); + this.edges.push(edge); + } - var decoded = []; + { // edge labels + var edgeLabel = new Potree.TextSprite(); + edgeLabel.setBorderColor({r:0, g:0, b:0, a:0.8}); + edgeLabel.setBackgroundColor({r:0, g:0, b:0, a:0.3}); + edgeLabel.material.depthTest = false; + edgeLabel.visible = false; + this.edgeLabels.push(edgeLabel); + this.add(edgeLabel); + } - var offset = 5; - while(stack.length > 0){ + { // angle labels + var angleLabel = new Potree.TextSprite(); + angleLabel.setBorderColor({r:0, g:0, b:0, a:0.8}); + angleLabel.setBackgroundColor({r:0, g:0, b:0, a:0.3}); + angleLabel.material.depthTest = false; + angleLabel.material.opacity = 1; + angleLabel.visible = false; + this.angleLabels.push(angleLabel); + this.add(angleLabel); + } - var snode = stack.shift(); - var mask = 1; - for(var i = 0; i < 8; i++){ - if((snode.children & mask) !== 0){ - var childIndex = i; - var childName = snode.name + i; - - var childChildren = view.getUint8(offset); - var childNumPoints = view.getUint32(offset + 1, true); - - stack.push({children: childChildren, numPoints: childNumPoints, name: childName}); - - decoded.push({children: childChildren, numPoints: childNumPoints, name: childName}); - - offset += 5; - } - - mask = mask * 2; - } - - if(offset === hbuffer.byteLength){ - break; - } - + { // coordinate labels + var coordinateLabel = new Potree.TextSprite(); + coordinateLabel.setBorderColor({r:0, g:0, b:0, a:0.8}); + coordinateLabel.setBackgroundColor({r:0, g:0, b:0, a:0.3}); + coordinateLabel.material.depthTest = false; + coordinateLabel.material.opacity = 1; + coordinateLabel.visible = false; + this.coordinateLabels.push(coordinateLabel); + this.add(coordinateLabel); } + - //console.log(decoded); - var nodes = {}; - nodes[node.name] = node; - var pco = node.pcoGeometry; + var event = { + type: "marker_added", + measurement: this + }; + this.dispatchEvent(event); + //this.setPosition(this.points.length-1, point.position); + this.setMarker(this.points.length-1, point); + }; + + this.removeMarker = function(index){ + this.points.splice(index, 1); - for( var i = 0; i < decoded.length; i++){ - var name = decoded[i].name; - var numPoints = decoded[i].numPoints; - var index = parseInt(name.charAt(name.length-1)); - var parentName = name.substring(0, name.length-1); - var parentNode = nodes[parentName]; - var level = name.length-1; - var boundingBox = Potree.POCLoader.createChildAABB(parentNode.boundingBox, index); - - var currentNode = new Potree.PointCloudOctreeGeometryNode(name, pco, boundingBox); - currentNode.level = level; - currentNode.numPoints = numPoints; - currentNode.hasChildren = decoded[i].children > 0; - parentNode.addChild(currentNode); - nodes[name] = currentNode; - } + this.remove(this.spheres[index]); - node.loadPoints(); + var edgeIndex = (index === 0) ? 0 : (index - 1); + this.remove(this.edges[edgeIndex]); + this.edges.splice(edgeIndex, 1); + + this.remove(this.edgeLabels[edgeIndex]); + this.edgeLabels.splice(edgeIndex, 1); + this.coordinateLabels.splice(index, 1); + this.spheres.splice(index, 1); + + this.update(); + + this.dispatchEvent({type: "marker_removed", measurement: this}); }; - if((node.level % node.pcoGeometry.hierarchyStepSize) === 0){ - //var hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc"; - var hurl = node.pcoGeometry.octreeDir + "/" + node.getHierarchyPath() + "/" + node.name + ".hrc"; + + this.setMarker = function(index, point){ + this.points[index] = point; - var xhr = new XMLHttpRequest(); - xhr.open('GET', hurl, true); - xhr.responseType = 'arraybuffer'; - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - if (xhr.status === 200 || xhr.status === 0) { - var hbuffer = xhr.response; - callback(node, hbuffer); - } else { - console.log('Failed to load file! HTTP status: ' + xhr.status + ", file: " + url); - } - } + var event = { + type: 'marker_moved', + measure: this, + index: index, + position: point.position.clone() }; - try{ - xhr.send(null); - }catch(e){ - console.log("fehler beim laden der punktwolke: " + e); - } - } - -}; - - - -Potree.PointCloudOctreeGeometryNode.prototype.dispose = function(){ - if(this.geometry && this.parent != null){ - this.geometry.dispose(); - this.geometry = null; - this.loaded = false; + this.dispatchEvent(event); - //this.dispatchEvent( { type: 'dispose' } ); - for(var i = 0; i < this.oneTimeDisposeHandlers.length; i++){ - var handler = this.oneTimeDisposeHandlers[i]; - handler(); - } - this.oneTimeDisposeHandlers = []; - } -}; - -THREE.EventDispatcher.prototype.apply( Potree.PointCloudOctreeGeometryNode.prototype ); - -Potree.utils = function(){ - -}; - -Potree.utils.pathExists = function(url){ - var req = new XMLHttpRequest(); - req.open('GET', url, false); - req.send(null); - if (req.status !== 200) { - return false; + this.update(); } - return true; -}; - -/** - * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561 - */ -Potree.utils.computeTransformedBoundingBox = function (box, transform) { - - var vertices = [ - new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), - new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), - new THREE.Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform), - new THREE.Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform), - new THREE.Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform), - new THREE.Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform), - new THREE.Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform), - new THREE.Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform), - new THREE.Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform) - ]; - var boundingBox = new THREE.Box3(); - boundingBox.setFromPoints( vertices ); + this.setPosition = function(index, position){ + var point = this.points[index]; + point.position.copy(position); + + var event = { + type: 'marker_moved', + measure: this, + index: index, + position: position.clone() + }; + this.dispatchEvent(event); + + this.update(); + }; - return boundingBox; -}; - -/** - * add separators to large numbers - * - * @param nStr - * @returns - */ -Potree.utils.addCommas = function(nStr){ - nStr += ''; - x = nStr.split('.'); - x1 = x[0]; - x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; -}; - -/** - * create worker from a string - * - * code from http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string - */ -Potree.utils.createWorker = function(code){ - var blob = new Blob([code], {type: 'application/javascript'}); - var worker = new Worker(URL.createObjectURL(blob)); - - return worker; -}; - -Potree.utils.loadSkybox = function(path){ - var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100000 ); - var scene = new THREE.Scene(); - - var format = ".jpg"; - var urls = [ - path + 'px' + format, path + 'nx' + format, - path + 'py' + format, path + 'ny' + format, - path + 'pz' + format, path + 'nz' + format - ]; - - var textureCube = THREE.ImageUtils.loadTextureCube(urls, THREE.CubeRefractionMapping ); - - var shader = { - uniforms: { - "tCube": {type: "t", value: textureCube}, - "tFlip": {type: "f", value: -1} - }, - vertexShader: THREE.ShaderLib["cube"].vertexShader, - fragmentShader: THREE.ShaderLib["cube"].fragmentShader - }; - - var material = new THREE.ShaderMaterial({ - fragmentShader: shader.fragmentShader, - vertexShader: shader.vertexShader, - uniforms: shader.uniforms, - depthWrite: false, - side: THREE.BackSide - }); - var mesh = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material); - scene.add(mesh); - - return {"camera": camera, "scene": scene}; -}; - -Potree.utils.createGrid = function createGrid(width, length, spacing, color){ - var material = new THREE.LineBasicMaterial({ - color: color || 0x888888 - }); + this.getArea = function(){ + var area = 0; + var j = this.points.length - 1; + + for(var i = 0; i < this.points.length; i++){ + var p1 = this.points[i].position; + var p2 = this.points[j].position; + area += (p2.x + p1.x) * (p1.z - p2.z); + j = i; + } + + return Math.abs(area / 2); + }; - var geometry = new THREE.Geometry(); - for(var i = 0; i <= length; i++){ - geometry.vertices.push(new THREE.Vector3(-(spacing*width)/2, 0, i*spacing-(spacing*length)/2)); - geometry.vertices.push(new THREE.Vector3(+(spacing*width)/2, 0, i*spacing-(spacing*length)/2)); - } + this.getAngleBetweenLines = function(cornerPoint, point1, point2) { + var v1 = new THREE.Vector3().subVectors(point1.position, cornerPoint.position); + var v2 = new THREE.Vector3().subVectors(point2.position, cornerPoint.position); + return v1.angleTo(v2); + }; - for(var i = 0; i <= width; i++){ - geometry.vertices.push(new THREE.Vector3(i*spacing-(spacing*width)/2, 0, -(spacing*length)/2)); - geometry.vertices.push(new THREE.Vector3(i*spacing-(spacing*width)/2, 0, +(spacing*length)/2)); - } + this.getAngle = function(index){ - var line = new THREE.Line(geometry, material, THREE.LinePieces); - line.receiveShadow = true; - return line; -}; - - -Potree.utils.createBackgroundTexture = function(width, height){ - - function gauss(x, y){ - return (1 / (2 * Math.PI)) * Math.exp( - (x*x + y*y) / 2); + if(this.points.length < 3 || index >= this.points.length){ + return 0; + } + + var previous = (index === 0) ? this.points[this.points.length-1] : this.points[index-1]; + var point = this.points[index]; + var next = this.points[(index + 1) % (this.points.length)]; + + return this.getAngleBetweenLines(point, previous, next); }; + + this.update = function(){ + + if(this.points.length === 0){ + return; + }else if(this.points.length === 1){ + var point = this.points[0]; + var position = point.position; + this.spheres[0].position.copy(position); + + {// coordinate labels + var coordinateLabel = this.coordinateLabels[0]; + + var labelPos = position.clone().add(new THREE.Vector3(0,1,0)); + coordinateLabel.position.copy(labelPos); + + var msg = position.x.toFixed(2) + " / " + position.y.toFixed(2) + " / " + position.z.toFixed(2); + coordinateLabel.setText(msg); + + coordinateLabel.visible = this.showCoordinates && (index < lastIndex || this.closed); + } + + return; + } + + var lastIndex = this.points.length - 1; + + var centroid = new THREE.Vector3(); + for(var i = 0; i <= lastIndex; i++){ + var point = this.points[i]; + centroid.add(point.position); + } + centroid.divideScalar(this.points.length); + + for(var i = 0; i <= lastIndex; i++){ + var index = i; + var nextIndex = ( i + 1 > lastIndex ) ? 0 : i + 1; + var previousIndex = (i === 0) ? lastIndex : i - 1; + + var point = this.points[index]; + var nextPoint = this.points[nextIndex]; + var previousPoint = this.points[previousIndex]; + + var sphere = this.spheres[index]; + + // spheres + sphere.position.copy(point.position); + sphere.material.color = scope.color; - var map = THREE.ImageUtils.generateDataTexture( width, height, new THREE.Color() ); - map.magFilter = THREE.NearestFilter; - var data = map.image.data; - - //var data = new Uint8Array(width*height*4); - var chroma = [1, 1.5, 1.7]; - var max = gauss(0, 0); - - for(var x = 0; x < width; x++){ - for(var y = 0; y < height; y++){ - var u = 2 * (x / width) - 1; - var v = 2 * (y / height) - 1; + {// edges + var edge = this.edges[index]; + + edge.material.color = this.color; + + edge.geometry.vertices[0].copy(point.position); + edge.geometry.vertices[1].copy(nextPoint.position); + + edge.geometry.verticesNeedUpdate = true; + edge.geometry.computeBoundingSphere(); + edge.visible = index < lastIndex || this.closed; + } - var i = x + width*y; - var d = gauss(2*u, 2*v) / max; - var r = (Math.random() + Math.random() + Math.random()) / 3; - r = (d * 0.5 + 0.5) * r * 0.03; - r = r * 0.4; + {// edge labels + var edgeLabel = this.edgeLabels[i]; - //d = Math.pow(d, 0.6); + var center = new THREE.Vector3().add(point.position); + center.add(nextPoint.position); + center = center.multiplyScalar(0.5); + var distance = point.position.distanceTo(nextPoint.position); + + edgeLabel.position.copy(center); + edgeLabel.setText(distance.toFixed(2)); + edgeLabel.visible = this.showDistances && (index < lastIndex || this.closed) && this.points.length >= 2 && distance > 0; + } - data[3*i+0] = 255 * (d / 15 + 0.05 + r) * chroma[0]; - data[3*i+1] = 255 * (d / 15 + 0.05 + r) * chroma[1]; - data[3*i+2] = 255 * (d / 15 + 0.05 + r) * chroma[2]; + {// angle labels + var angleLabel = this.angleLabels[i]; + var angle = this.getAngleBetweenLines(point, previousPoint, nextPoint); + + var dir = nextPoint.position.clone().sub(previousPoint.position); + dir.multiplyScalar(0.5); + dir = previousPoint.position.clone().add(dir).sub(point.position).normalize(); + + var dist = Math.min(point.position.distanceTo(previousPoint.position), point.position.distanceTo(nextPoint.position)); + dist = dist / 9; + + var labelPos = point.position.clone().add(dir.multiplyScalar(dist)); + angleLabel.position.copy(labelPos); + + var msg = Potree.utils.addCommas((angle*(180.0/Math.PI)).toFixed(1)) + '\u00B0'; + angleLabel.setText(msg); + + angleLabel.visible = this.showAngles && (index < lastIndex || this.closed) && this.points.length >= 3 && angle > 0; + } - //data[4*i+3] = 255; - + {// coordinate labels + var coordinateLabel = this.coordinateLabels[0]; + + var labelPos = point.position.clone().add(new THREE.Vector3(0,1,0)); + coordinateLabel.position.copy(labelPos); + + var msg = point.position.x.toFixed(2) + " / " + point.position.y.toFixed(2) + " / " + point.position.z.toFixed(2); + coordinateLabel.setText(msg); + + coordinateLabel.visible = this.showCoordinates && (index < lastIndex || this.closed); + } } - } - - return map; -}; - - - -function getMousePointCloudIntersection(mouse, camera, renderer, pointclouds){ - var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); - vector.unproject(camera); - - var direction = vector.sub(camera.position).normalize(); - var ray = new THREE.Ray(camera.position, direction); - - var closestPoint = null; - var closestPointDistance = null; + + // update area label + this.areaLabel.position.copy(centroid); + this.areaLabel.visible = this.showArea && this.points.length >= 3; + var msg = Potree.utils.addCommas(this.getArea().toFixed(1)) + "²"; + this.areaLabel.setText(msg); + }; - for(var i = 0; i < pointclouds.length; i++){ - var pointcloud = pointclouds[i]; - var point = pointcloud.pick(renderer, camera, ray); + this.raycast = function(raycaster, intersects){ - if(!point){ - continue; + for(var i = 0; i < this.points.length; i++){ + var sphere = this.spheres[i]; + + sphere.raycast(raycaster, intersects); } - var distance = camera.position.distanceTo(point.position); - - if(!closestPoint || distance < closestPointDistance){ - closestPoint = point; - closestPointDistance = distance; + // recalculate distances because they are not necessarely correct + // for scaled objects. + // see https://github.com/mrdoob/three.js/issues/5827 + // TODO: remove this once the bug has been fixed + for(var i = 0; i < intersects.length; i++){ + var I = intersects[i]; + I.distance = raycaster.ray.origin.distanceTo(I.point); } - } - - return closestPoint ? closestPoint.position : null; + intersects.sort( function ( a, b ) { return a.distance - b.distance;} ); + }; }; - - -function pixelsArrayToImage(pixels, width, height){ - var canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - var context = canvas.getContext('2d'); - - pixels = new pixels.constructor(pixels); - - for(var i = 0; i < pixels.length; i++){ - pixels[i*4 + 3] = 255; +Potree.Measure.prototype = Object.create( THREE.Object3D.prototype ); + +Object.defineProperty(Potree.Measure.prototype, "showCoordinates", { + get: function(){ + return this._showCoordinates; + }, + set: function(value){ + this._showCoordinates = value; + this.update(); } +}); - var imageData = context.createImageData(width, height); - imageData.data.set(pixels); - context.putImageData(imageData, 0, 0); +Object.defineProperty(Potree.Measure.prototype, "showAngles", { + get: function(){ + return this._showAngles; + }, + set: function(value){ + this._showAngles = value; + this.update(); + } +}); - var img = new Image(); - img.src = canvas.toDataURL(); - img.style.transform = "scaleY(-1)"; - - return img; -}; +Object.defineProperty(Potree.Measure.prototype, "showArea", { + get: function(){ + return this._showArea; + }, + set: function(value){ + this._showArea = value; + this.update(); + } +}); -function projectedRadius(radius, fov, distance, screenHeight){ - var projFactor = (1 / Math.tan(fov / 2)) / distance; - projFactor = projFactor * screenHeight / 2; - - return radius * projFactor; -}; - - -Potree.utils.topView = function(camera, node){ - camera.position.set(0, 1, 0); - camera.rotation.set(-Math.PI / 2, 0, 0); - camera.zoomTo(node, 1); -}; +Object.defineProperty(Potree.Measure.prototype, "closed", { + get: function(){ + return this._closed; + }, + set: function(value){ + this._closed = value; + this.update(); + } +}); -Potree.utils.frontView = function(camera, node){ - camera.position.set(0, 0, 1); - camera.rotation.set(0, 0, 0); - camera.zoomTo(node, 1); -}; +Object.defineProperty(Potree.Measure.prototype, "showDistances", { + get: function(){ + return this._showDistances; + }, + set: function(value){ + this._showDistances = value; + this.update(); + } +}); -Potree.utils.leftView = function(camera, node){ - camera.position.set(-1, 0, 0); - camera.rotation.set(0, -Math.PI / 2, 0); - camera.zoomTo(node, 1); -}; -Potree.utils.rightView = function(camera, node){ - camera.position.set(1, 0, 0); - camera.rotation.set(0, Math.PI / 2, 0); - camera.zoomTo(node, 1); -}; - -/** - * - * 0: no intersection - * 1: intersection - * 2: fully inside - */ -Potree.utils.frustumSphereIntersection = function(frustum, sphere){ - var planes = frustum.planes; - var center = sphere.center; - var negRadius = - sphere.radius; - var minDistance = Number.MAX_VALUE; - - for ( var i = 0; i < 6; i ++ ) { - var distance = planes[ i ].distanceToPoint( center ); - if ( distance < negRadius ) { - return 0; - } - - minDistance = Math.min(minDistance, distance); - } - return (minDistance >= sphere.radius) ? 2 : 1; -}; + + +Potree.MeasuringTool = function(scene, camera, renderer, toGeo){ + var scope = this; + this.enabled = false; + this.toGeo = toGeo; -Potree.utils.screenPass = new function(){ - - this.screenScene = new THREE.Scene(); - this.screenQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2, 0)); - this.screenQuad.material.depthTest = true; - this.screenQuad.material.depthWrite = true; - this.screenQuad.material.transparent = true; - this.screenScene.add(this.screenQuad); - this.camera = new THREE.Camera(); + this.scene = scene; + this.camera = camera; + this.renderer = renderer; + this.domElement = renderer.domElement; + this.mouse = {x: 0, y: 0}; - this.render = function(renderer, material, target){ - this.screenQuad.material = material; + var STATE = { + DEFAULT: 0, + INSERT: 1 + }; + + var state = STATE.DEFAULT; + + this.activeMeasurement= null; + this.measurements = []; + this.sceneMeasurement = new THREE.Scene(); + this.sceneRoot = new THREE.Object3D(); + this.sceneMeasurement.add(this.sceneRoot); + + this.light = new THREE.DirectionalLight( 0xffffff, 1 ); + this.light.position.set( 0, 0, 10 ); + this.light.lookAt(new THREE.Vector3(0,0,0)); + this.sceneMeasurement.add( this.light ); + + this.hoveredElement = null; + + function onClick(event){ + if(state === STATE.INSERT){ + var point = scope.getMousePointCloudIntersection(); + if(point){ + var pos = point.position.clone(); + + scope.activeMeasurement.addMarker(pos); + + var event = { + type: 'newpoint', + position: pos.clone() + }; + scope.dispatchEvent(event); + + if(scope.activeMeasurement.points.length > scope.activeMeasurement.maxMarkers){ + scope.finishInsertion(); + } + + } + } + }; + + function onMouseMove(event){ + + var rect = scope.domElement.getBoundingClientRect(); + scope.mouse.x = ((event.clientX - rect.left) / scope.domElement.clientWidth) * 2 - 1; + scope.mouse.y = -((event.clientY - rect.top) / scope.domElement.clientHeight) * 2 + 1; - if(typeof target === undefined){ - renderer.render(this.screenScene, this.camera); + //console.log(scope.mouse); + + if(scope.dragstart){ + var arg = { + type: "drag", + event: event, + tool: scope + }; + scope.dragstart.object.dispatchEvent(arg); + + }else if(state == STATE.INSERT && scope.activeMeasurement){ + var point = scope.getMousePointCloudIntersection(); + + if(point){ + var position = point.position; + var lastIndex = scope.activeMeasurement.points.length-1; + //scope.activeMeasurement.setPosition(lastIndex, position); + scope.activeMeasurement.setMarker(lastIndex, point); + } + }else{ - renderer.render(this.screenScene, this.camera, target); + var I = getHoveredElement(); + + if(I){ + + I.object.dispatchEvent({type: "move", target: I.object, event: event}); + + if(scope.hoveredElement && scope.hoveredElement !== I.object){ + scope.hoveredElement.dispatchEvent({type: "leave", target: scope.hoveredElement, event: event}); + } + + scope.hoveredElement = I.object; + + }else{ + + if(scope.hoveredElement){ + scope.hoveredElement.dispatchEvent({type: "leave", target: scope.hoveredElement, event: event}); + } + + scope.hoveredElement = null; + + } } }; -}(); + function onRightClick(event){ + if(state == STATE.INSERT){ + scope.finishInsertion(); + } + } + this.getState = function(){ + // TODO remove + return state; + }; + + function onMouseDown(event){ -Potree.Features = function(){ - - var ftCanvas = document.createElement("canvas"); - var gl = ftCanvas.getContext("webgl") || ftCanvas.getContext("experimental-webgl"); - if (gl === null) - return null; - - // -- code taken from THREE.WebGLRenderer -- - var _vertexShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ); - var _vertexShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ); - var _vertexShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.LOW_FLOAT ); - - var _fragmentShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ); - var _fragmentShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ); - var _fragmentShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.LOW_FLOAT ); - - var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; - var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; - // ----------------------------------------- - - var precision; - if(highpAvailable){ - precision = "highp"; - }else if(mediumpAvailable){ - precision = "mediump"; - }else{ - precision = "lowp"; - } - - return { - SHADER_INTERPOLATION: { - isSupported: function(){ - - //if(typeof this.shaderInterpolationSupported === "undefined"){ - // var material = new Potree.PointCloudMaterial(); - // material.interpolate = true; - // - // var vs = gl.createShader(gl.VERTEX_SHADER); - // var fs = gl.createShader(gl.FRAGMENT_SHADER); - // gl.shaderSource(vs, material.vertexShader); - // gl.shaderSource(fs, material.fragmentShader); - // - // gl.compileShader(vs); - // gl.compileShader(fs); - // - // var successVS = gl.getShaderParameter(vs, gl.COMPILE_STATUS); - // var successFS = gl.getShaderParameter(fs, gl.COMPILE_STATUS); - // this.shaderInterpolationSupported = successVS && successFS; - //} - // - //return this.shaderInterpolationSupported; - - - var supported = true; - - supported = supported && gl.getExtension("EXT_frag_depth"); - supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; - - return supported; + if(event.which === 1){ + + if(state !== STATE.DEFAULT){ + event.stopImmediatePropagation(); } - }, - SHADER_SPLATS: { - isSupported: function(){ - - var supported = true; - - supported = supported && gl.getExtension("EXT_frag_depth"); - supported = supported && gl.getExtension("OES_texture_float"); - supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; - - return supported; - + + var I = getHoveredElement(); + + if(I){ + + scope.dragstart = { + object: I.object, + sceneClickPos: I.point, + sceneStartPos: scope.sceneRoot.position.clone(), + mousePos: {x: scope.mouse.x, y: scope.mouse.y} + }; + + event.stopImmediatePropagation(); + } + + }else if(event.which === 3){ + onRightClick(event); + } + } + + function onDoubleClick(event){ + + // fix move event after double click + // see: http://stackoverflow.com/questions/8125165/event-listener-for-dblclick-causes-event-for-mousemove-to-not-work-and-show-a-ci + if (window.getSelection){ + window.getSelection().removeAllRanges(); + }else if (document.selection){ + document.selection.empty(); + } + + + if(scope.activeMeasurement && state === STATE.INSERT){ + scope.activeMeasurement.removeMarker(scope.activeMeasurement.points.length-1); + scope.finishInsertion(); + event.stopImmediatePropagation(); + } + } + + function onMouseUp(event){ - }, - SHADER_EDL: { - isSupported: function(){ - - var supported = true; - - supported = supported && gl.getExtension("EXT_frag_depth"); - supported = supported && gl.getExtension("OES_texture_float"); - supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; - - return supported; - + if(scope.dragstart){ + scope.dragstart.object.dispatchEvent({type: "drop", event: event}); + scope.dragstart = null; + } + + } + + function getHoveredElement(){ + + var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); + vector.unproject(scope.camera); + + var raycaster = new THREE.Raycaster(); + raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); + + var spheres = []; + for(var i = 0; i < scope.measurements.length; i++){ + var m = scope.measurements[i]; + + for(var j = 0; j < m.spheres.length; j++){ + spheres.push(m.spheres[j]); } + } - }, - precision: precision + var intersections = raycaster.intersectObjects(spheres, true); + if(intersections.length > 0){ + return intersections[0]; + }else{ + return false; + } }; - -}(); - -/** - * adapted from http://stemkoski.github.io/Three.js/Sprite-Text-Labels.html - */ - -Potree.TextSprite = function(text){ - - THREE.Object3D.call(this); - - var texture = new THREE.Texture(); - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - var spriteMaterial = new THREE.SpriteMaterial( { - map: texture, - useScreenCoordinates: false, - depthTest: false, - depthWrite: false} ); - - this.material = spriteMaterial; - this.sprite = new THREE.Sprite(spriteMaterial); - this.add(this.sprite); - - //THREE.Sprite.call(this, spriteMaterial); - - this.borderThickness = 4; - this.fontface = "Arial"; - this.fontsize = 28; - this.borderColor = { r:0, g:0, b:0, a:1.0 }; - this.backgroundColor = { r:255, g:255, b:255, a:1.0 }; - this.textColor = {r: 255, g: 255, b: 255, a: 1.0}; - this.text = ""; - this.setText(text); -}; - -Potree.TextSprite.prototype = new THREE.Object3D(); + this.getMousePointCloudIntersection = function(){ + var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); + vector.unproject(scope.camera); -Potree.TextSprite.prototype.setText = function(text){ - this.text = text; + var direction = vector.sub(scope.camera.position).normalize(); + var ray = new THREE.Ray(scope.camera.position, direction); + + var pointClouds = []; + scope.scene.traverse(function(object){ + if(object instanceof Potree.PointCloudOctree || object instanceof Potree.PointCloudArena4D){ + pointClouds.push(object); + } + }); + + var closestPoint = null; + var closestPointDistance = null; + + for(var i = 0; i < pointClouds.length; i++){ + var pointcloud = pointClouds[i]; + var point = pointcloud.pick(scope.renderer, scope.camera, ray); + + if(!point){ + continue; + } + + var distance = scope.camera.position.distanceTo(point.position); + + if(!closestPoint || distance < closestPointDistance){ + closestPoint = point; + closestPointDistance = distance; + } + } + + return closestPoint ? closestPoint : null; + }; - this.update(); -}; + this.startInsertion = function(args){ + state = STATE.INSERT; + + var args = args || {}; + var showDistances = (typeof args.showDistances != "undefined") ? args.showDistances : true; + var showArea = (typeof args.showArea != "undefined") ? args.showArea : false; + var showAngles = (typeof args.showAngles != "undefined") ? args.showAngles : false; + var closed = (typeof args.closed != "undefined") ? args.closed : false; + var showCoordinates = (typeof args.showCoordinates != "undefined") ? args.showCoordinates : false; + var maxMarkers = args.maxMarkers || Number.MAX_SAFE_INTEGER; + + var measurement = new Potree.Measure(); + measurement.showDistances = showDistances; + measurement.showArea = showArea; + measurement.showAngles = showAngles; + measurement.closed = closed; + measurement.showCoordinates = showCoordinates; + measurement.maxMarkers = maxMarkers; -Potree.TextSprite.prototype.setTextColor = function(color){ - this.textColor = color; + this.addMeasurement(measurement); + measurement.addMarker(new THREE.Vector3(Infinity,Infinity,Infinity)); + + this.activeMeasurement = measurement; + }; - this.update(); -}; - -Potree.TextSprite.prototype.setBorderColor = function(color){ - this.borderColor = color; + this.finishInsertion = function(){ + this.activeMeasurement.removeMarker(this.activeMeasurement.points.length-1); + + var event = { + type: "insertion_finished", + measurement: this.activeMeasurement + }; + this.dispatchEvent(event); + + this.activeMeasurement = null; + state = STATE.DEFAULT; + }; - this.update(); -}; - -Potree.TextSprite.prototype.setBackgroundColor = function(color){ - this.backgroundColor = color; + this.addMeasurement = function(measurement){ + this.sceneMeasurement.add(measurement); + this.measurements.push(measurement); + + this.dispatchEvent({"type": "measurement_added", measurement: measurement}); + measurement.addEventListener("marker_added", function(event){ + scope.dispatchEvent(event); + }); + measurement.addEventListener("marker_removed", function(event){ + scope.dispatchEvent(event); + }); + measurement.addEventListener("marker_moved", function(event){ + scope.dispatchEvent(event); + }); + }; - this.update(); -}; - -Potree.TextSprite.prototype.update = function(){ - - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - context.font = "Bold " + this.fontsize + "px " + this.fontface; + this.removeMeasurement = function(measurement){ + this.sceneMeasurement.remove(measurement); + var index = this.measurements.indexOf(measurement); + if(index >= 0){ + this.measurements.splice(index, 1); + + this.dispatchEvent({"type": "measurement_removed", measurement: measurement}); + } + }; - // get size data (height depends only on font size) - var metrics = context.measureText( this.text ); - var textWidth = metrics.width; - var spriteWidth = textWidth + 2 * this.borderThickness; - var spriteHeight = this.fontsize * 1.4 + 2 * this.borderThickness; + this.reset = function(){ + for(var i = this.measurements.length - 1; i >= 0; i--){ + var measurement = this.measurements[i]; + this.removeMeasurement(measurement); + } + }; - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - context.canvas.width = spriteWidth; - context.canvas.height = spriteHeight; - context.font = "Bold " + this.fontsize + "px " + this.fontface; + this.update = function(){ + var measurements = []; + for(var i = 0; i < this.measurements.length; i++){ + measurements.push(this.measurements[i]); + } + if(this.activeMeasurement){ + measurements.push(this.activeMeasurement); + } + + // make sizes independant of distance and fov + for(var i = 0; i < measurements.length; i++){ + var measurement = measurements[i]; + + // spheres + for(var j = 0; j < measurement.spheres.length; j++){ + var sphere = measurement.spheres[j]; + + var distance = scope.camera.position.distanceTo(sphere.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (15 / pr); + sphere.scale.set(scale, scale, scale); + + } + + // edgeLabels + for(var j = 0; j < measurement.edgeLabels.length; j++){ + var label = measurement.edgeLabels[j]; + + var distance = scope.camera.position.distanceTo(label.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (70 / pr); + label.scale.set(scale, scale, scale); + } + + // angle labels + for(var j = 0; j < measurement.edgeLabels.length; j++){ + var label = measurement.angleLabels[j]; + + var distance = scope.camera.position.distanceTo(label.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (70 / pr); + label.scale.set(scale, scale, scale); + } + + // coordinate labels + for(var j = 0; j < measurement.coordinateLabels.length; j++){ + var label = measurement.coordinateLabels[j]; + var sphere = measurement.spheres[j]; + var point = measurement.points[j]; + + var distance = scope.camera.position.distanceTo(sphere.getWorldPosition()); + + var screenPos = sphere.getWorldPosition().clone().project( camera ); + screenPos.x = Math.round( ( screenPos.x + 1 ) * scope.renderer.domElement.clientWidth / 2 ), + screenPos.y = Math.round( ( - screenPos.y + 1 ) * scope.renderer.domElement.clientHeight / 2 ); + screenPos.z = 0; + screenPos.y -= 30; + + var labelPos = new THREE.Vector3( + (screenPos.x / scope.renderer.domElement.clientWidth) * 2 - 1, + -(screenPos.y / scope.renderer.domElement.clientHeight) * 2 + 1, + 0.5 ); + labelPos.unproject(scope.camera); + + var direction = labelPos.sub(scope.camera.position).normalize(); + labelPos = new THREE.Vector3().addVectors( + scope.camera.position, direction.multiplyScalar(distance)); + + label.position.copy(labelPos); + + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (70 / pr); + label.scale.set(scale, scale, scale); + + var geoCoord = scope.toGeo(point.position); + var txt = geoCoord.x.toFixed(2) + " / "; + txt += geoCoord.y.toFixed(2) + " / "; + txt += geoCoord.z.toFixed(2); + label.setText(txt); + } + + // areaLabel + var distance = scope.camera.position.distanceTo(measurement.areaLabel.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (80 / pr); + measurement.areaLabel.scale.set(scale, scale, scale); + } - // background color - context.fillStyle = "rgba(" + this.backgroundColor.r + "," + this.backgroundColor.g + "," - + this.backgroundColor.b + "," + this.backgroundColor.a + ")"; - // border color - context.strokeStyle = "rgba(" + this.borderColor.r + "," + this.borderColor.g + "," - + this.borderColor.b + "," + this.borderColor.a + ")"; - - context.lineWidth = this.borderThickness; - this.roundRect(context, this.borderThickness/2, this.borderThickness/2, - textWidth + this.borderThickness, this.fontsize * 1.4 + this.borderThickness, 6); + this.light.position.copy(this.camera.position); + this.light.lookAt(this.camera.getWorldDirection().add(this.camera.position)); - // text color - context.strokeStyle = "rgba(0, 0, 0, 1.0)"; - context.strokeText( this.text, this.borderThickness, this.fontsize + this.borderThickness); - - context.fillStyle = "rgba(" + this.textColor.r + "," + this.textColor.g + "," - + this.textColor.b + "," + this.textColor.a + ")"; - context.fillText( this.text, this.borderThickness, this.fontsize + this.borderThickness); + }; - - var texture = new THREE.Texture(canvas); - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.needsUpdate = true; + this.render = function(){ + this.update(); + this.renderer.render(this.sceneMeasurement, this.camera); + }; - //var spriteMaterial = new THREE.SpriteMaterial( - // { map: texture, useScreenCoordinates: false } ); - this.sprite.material.map = texture; - - this.sprite.scale.set(spriteWidth*0.01,spriteHeight*0.01,1.0); - - //this.material = spriteMaterial; -}; - -Potree.TextSprite.prototype.roundRect = function(ctx, x, y, w, h, r) { - ctx.beginPath(); - ctx.moveTo(x+r, y); - ctx.lineTo(x+w-r, y); - ctx.quadraticCurveTo(x+w, y, x+w, y+r); - ctx.lineTo(x+w, y+h-r); - ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); - ctx.lineTo(x+r, y+h); - ctx.quadraticCurveTo(x, y+h, x, y+h-r); - ctx.lineTo(x, y+r); - ctx.quadraticCurveTo(x, y, x+r, y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); + this.domElement.addEventListener( 'click', onClick, false); + this.domElement.addEventListener( 'dblclick', onDoubleClick, false); + this.domElement.addEventListener( 'mousemove', onMouseMove, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mouseup', onMouseUp, true ); }; +Potree.MeasuringTool.prototype = Object.create( THREE.EventDispatcher.prototype ); -Potree.Version = function(version){ - this.version = version; - var vmLength = (version.indexOf(".") === -1) ? version.length : version.indexOf("."); - this.versionMajor = parseInt(version.substr(0, vmLength)); - this.versionMinor = parseInt(version.substr(vmLength + 1)); - if(this.versionMinor.length === 0){ - this.versionMinor = 0; - } - -}; - -Potree.Version.prototype.newerThan = function(version){ - var v = new Potree.Version(version); - - if( this.versionMajor > v.versionMajor){ - return true; - }else if( this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor){ - return true; - }else{ - return false; - } -}; - -Potree.Version.prototype.equalOrHigher = function(version){ - var v = new Potree.Version(version); - - if( this.versionMajor > v.versionMajor){ - return true; - }else if( this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor){ - return true; - }else{ - return false; - } -}; - -Potree.Version.prototype.upTo = function(version){ - return !this.newerThan(version); -}; -Potree.Measure = function(){ +Potree.HeightProfile = function(){ var scope = this; THREE.Object3D.call( this ); - + this.points = []; - this._showDistances = true; - this._showArea = true; - this._closed = true; - this.maxMarkers = Number.MAX_SAFE_INTEGER; - this.spheres = []; this.edges = []; - this.sphereLabels = []; - this.edgeLabels = []; - this.angleLabels = []; - - this.areaLabel = new Potree.TextSprite(""); - this.areaLabel.setBorderColor({r:0, g:255, b:0, a:0.0}); - this.areaLabel.setBackgroundColor({r:0, g:255, b:0, a:0.0}); - this.areaLabel.setTextColor({r:180, g:220, b:180, a:1.0}); - this.areaLabel.material.depthTest = false; - this.areaLabel.material.opacity = 1; - this.add(this.areaLabel); + this.boxes = []; + this.width = 1; + this.height = 20; + this._modifiable = true; var sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10); - this.color = new THREE.Color( 0xff0000 ); + var lineColor = new THREE.Color( 0xff0000 ); var createSphereMaterial = function(){ var sphereMaterial = new THREE.MeshLambertMaterial({ shading: THREE.SmoothShading, - color: scope.color, + color: 0xff0000, ambient: 0xaaaaaa, depthTest: false, depthWrite: false} @@ -6730,15 +8135,31 @@ Potree.Measure = function(){ }; var dragEvent = function(event){ + var tool = event.tool; var dragstart = tool.dragstart; var mouse = tool.mouse; - var I = tool.getMousePointCloudIntersection(); + if(event.event.ctrlKey){ + + var mouseStart = new THREE.Vector3(dragstart.mousePos.x, dragstart.mousePos.y, 0); + var mouseEnd = new THREE.Vector3(mouse.x, mouse.y, 0); + var widthStart = dragstart.widthStart; - if(I){ - var index = scope.spheres.indexOf(tool.dragstart.object); - scope.setPosition(index, I); + var scale = 1 - 10 * (mouseStart.y - mouseEnd.y); + scale = Math.max(0.01, scale); + if(widthStart){ + scope.setWidth(widthStart * scale); + } + + }else{ + + var I = tool.getMousePointCloudIntersection(); + + if(I){ + var index = scope.spheres.indexOf(tool.dragstart.object); + scope.setPosition(index, I); + } } event.event.stopImmediatePropagation(); @@ -6748,60 +8169,54 @@ Potree.Measure = function(){ }; - this.addMarker = function(point){ - this.points.push(point); + this.addMarker = function(point){ + this.points.push(point); + // sphere var sphere = new THREE.Mesh(sphereGeometry, createSphereMaterial()); - sphere.addEventListener("move", moveEvent); - sphere.addEventListener("leave", leaveEvent); - sphere.addEventListener("drag", dragEvent); + sphere.addEventListener("mousemove", moveEvent); + sphere.addEventListener("mouseleave", leaveEvent); + sphere.addEventListener("mousedrag", dragEvent); sphere.addEventListener("drop", dropEvent); this.add(sphere); this.spheres.push(sphere); - { // edges + // edges & boxes + if(this.points.length > 1){ + var lineGeometry = new THREE.Geometry(); lineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3()); - lineGeometry.colors.push(this.color, this.color, this.color); + lineGeometry.colors.push(lineColor, lineColor, lineColor); var lineMaterial = new THREE.LineBasicMaterial( { - linewidth: 1 + vertexColors: THREE.VertexColors, + linewidth: 2, + transparent: true, + opacity: 0.4 }); lineMaterial.depthTest = false; var edge = new THREE.Line(lineGeometry, lineMaterial); - edge.visible = true; + edge.visible = false; this.add(edge); this.edges.push(edge); - } - - { // edge labels - var edgeLabel = new Potree.TextSprite(0); - edgeLabel.setBorderColor({r:0, g:255, b:0, a:0.0}); - edgeLabel.setBackgroundColor({r:0, g:255, b:0, a:0.0}); - edgeLabel.material.depthTest = false; - edgeLabel.visible = false; - this.edgeLabels.push(edgeLabel); - this.add(edgeLabel); - } - - { // angle labels - var angleLabel = new Potree.TextSprite(); - angleLabel.setBorderColor({r:0, g:255, b:0, a:0.0}); - angleLabel.setBackgroundColor({r:0, g:255, b:0, a:0.0}); - angleLabel.material.depthTest = false; - angleLabel.material.opacity = 1; - angleLabel.visible = false; - this.angleLabels.push(angleLabel); - this.add(angleLabel); + + + var boxGeometry = new THREE.BoxGeometry(1, 1, 1); + var boxMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: 0.2}); + var box = new THREE.Mesh(boxGeometry, boxMaterial); + box.visible = false; + + this.add(box); + this.boxes.push(box); + } - var event = { - type: "marker_added", - measurement: this + "type": "marker_added", + "profile": this }; this.dispatchEvent(event); @@ -6816,13 +8231,35 @@ Potree.Measure = function(){ var edgeIndex = (index === 0) ? 0 : (index - 1); this.remove(this.edges[edgeIndex]); this.edges.splice(edgeIndex, 1); - - this.remove(this.edgeLabels[edgeIndex]); - this.edgeLabels.splice(edgeIndex, 1); + this.remove(this.boxes[edgeIndex]); + this.boxes.splice(edgeIndex, 1); this.spheres.splice(index, 1); this.update(); + + var event = { + "type": "marker_removed", + "profile": this + }; + this.dispatchEvent(event); + }; + + /** + * see http://www.mathopenref.com/coordpolygonarea2.html + */ + this.getArea = function(){ + var area = 0; + var j = this.points.length - 1; + + for(var i = 0; i < this.points.length; i++){ + var p1 = this.points[i]; + var p2 = this.points[j]; + area += (p2.x + p1.x) * (p1.z - p2.z); + j = i; + } + + return Math.abs(area / 2); }; this.setPosition = function(index, position){ @@ -6831,7 +8268,7 @@ Potree.Measure = function(){ var event = { type: 'marker_moved', - measure: this, + profile: this, index: index, position: position.clone() }; @@ -6840,25 +8277,22 @@ Potree.Measure = function(){ this.update(); }; - this.getArea = function(){ - var area = 0; - var j = this.points.length - 1; + this.setWidth = function(width){ + this.width = width; - for(var i = 0; i < this.points.length; i++){ - var p1 = this.points[i]; - var p2 = this.points[j]; - area += (p2.x + p1.x) * (p1.z - p2.z); - j = i; - } + var event = { + type: 'width_changed', + profile: this, + width: width + }; + this.dispatchEvent(event); - return Math.abs(area / 2); + this.update(); }; - this.getAngleBetweenLines = function(cornerPoint, point1, point2) { - var v1 = new THREE.Vector3().subVectors(point1, cornerPoint); - var v2 = new THREE.Vector3().subVectors(point2, cornerPoint); - return v1.angleTo(v2); - }; + this.getWidth = function(){ + return this.width; + }; this.update = function(){ @@ -6871,82 +8305,79 @@ Potree.Measure = function(){ return; } - var lastIndex = this.points.length - 1; - + var min = this.points[0].clone(); + var max = this.points[0].clone(); var centroid = new THREE.Vector3(); + var lastIndex = this.points.length - 1; for(var i = 0; i <= lastIndex; i++){ var point = this.points[i]; - centroid.add(point); - } - centroid.divideScalar(this.points.length); - - for(var i = 0; i <= lastIndex; i++){ - var index = i; - var nextIndex = ( i + 1 > lastIndex ) ? 0 : i + 1; - var previousIndex = (i === 0) ? lastIndex : i - 1; - - var point = this.points[index]; - var nextPoint = this.points[nextIndex]; - var previousPoint = this.points[previousIndex]; + var sphere = this.spheres[i]; + var leftIndex = (i === 0) ? lastIndex : i - 1; + var rightIndex = (i === lastIndex) ? 0 : i + 1; + var leftVertex = this.points[leftIndex]; + var rightVertex = this.points[rightIndex]; + var leftEdge = this.edges[leftIndex]; + var rightEdge = this.edges[i]; + var leftBox = this.boxes[leftIndex]; + var rightBox = this.boxes[i]; - var sphere = this.spheres[index]; + var leftEdgeLength = point.distanceTo(leftVertex); + var rightEdgeLength = point.distanceTo(rightVertex); + var leftEdgeCenter = new THREE.Vector3().addVectors(leftVertex, point).multiplyScalar(0.5); + var rightEdgeCenter = new THREE.Vector3().addVectors(point, rightVertex).multiplyScalar(0.5); - // spheres sphere.position.copy(point); - sphere.material.color = scope.color; - - {// edges - var edge = this.edges[index]; - - edge.material.color = this.color; - - edge.geometry.vertices[0].copy(point); - edge.geometry.vertices[1].copy(nextPoint); - - edge.geometry.verticesNeedUpdate = true; - edge.geometry.computeBoundingSphere(); - edge.visible = index < lastIndex || this.closed; + + if(this._modifiable){ + sphere.visible = true; + }else{ + sphere.visible = false; } - {// edge labels - var edgeLabel = this.edgeLabels[i]; + if(leftEdge){ + leftEdge.geometry.vertices[1].copy(point); + leftEdge.geometry.verticesNeedUpdate = true; + leftEdge.geometry.computeBoundingSphere(); + } - var center = new THREE.Vector3().add(point); - center.add(nextPoint); - center = center.multiplyScalar(0.5); - var distance = point.distanceTo(nextPoint); - - edgeLabel.position.copy(center); - edgeLabel.setText(distance.toFixed(2)); - edgeLabel.visible = this.showDistances && (index < lastIndex || this.closed) && this.points.length >= 2 && distance > 0; + if(rightEdge){ + rightEdge.geometry.vertices[0].copy(point); + rightEdge.geometry.verticesNeedUpdate = true; + rightEdge.geometry.computeBoundingSphere(); } - {// angle labels - var angleLabel = this.angleLabels[i]; - var angle = this.getAngleBetweenLines(point, previousPoint, nextPoint); - - var dir = nextPoint.clone().sub(previousPoint); - dir.multiplyScalar(0.5); - dir = previousPoint.clone().add(dir).sub(point).normalize(); - - var dist = Math.min(point.distanceTo(previousPoint), point.distanceTo(nextPoint)); - dist = dist / 9; - - var labelPos = point.clone().add(dir.multiplyScalar(dist)); - angleLabel.position.copy(labelPos); + if(leftBox){ + var start = leftVertex; + var end = point; + var length = start.clone().setY(0).distanceTo(end.clone().setY(0)); + leftBox.scale.set(length, this.height, this.width); - var msg = Potree.utils.addCommas((angle*(180.0/Math.PI)).toFixed(1)) + '\u00B0'; - angleLabel.setText(msg); + var center = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); + var diff = new THREE.Vector3().subVectors(end, start); + var target = new THREE.Vector3(diff.z, 0, -diff.x); - angleLabel.visible = this.showAngles && (index < lastIndex || this.closed) && this.points.length >= 3 && angle > 0; + leftBox.position.set(0,0,0); + leftBox.lookAt(target); + leftBox.position.copy(center); } + + + + + centroid.add(point); + min.min(point); + max.max(point); + } + centroid.multiplyScalar(1 / this.points.length); + + for(var i = 0; i < this.boxes.length; i++){ + var box = this.boxes[i]; + + box.position.y = min.y + (max.y - min.y) / 2; + //box.scale.y = max.y - min.y + 50; + box.scale.y = 1000000; } - // update area label - this.areaLabel.position.copy(centroid); - this.areaLabel.visible = this.showArea && this.points.length >= 3; - var msg = Potree.utils.addCommas(this.getArea().toFixed(1)) + "²"; - this.areaLabel.setText(msg); }; this.raycast = function(raycaster, intersects){ @@ -6967,41 +8398,34 @@ Potree.Measure = function(){ } intersects.sort( function ( a, b ) { return a.distance - b.distance;} ); }; + + }; -Potree.Measure.prototype = Object.create( THREE.Object3D.prototype ); +Potree.HeightProfile.prototype = Object.create( THREE.Object3D.prototype ); -Object.defineProperty(Potree.Measure.prototype, "showArea", { +Object.defineProperty(Potree.HeightProfile.prototype, "modifiable", { get: function(){ - return this._showArea; + return this.modifiable; }, set: function(value){ - this._showArea = value; + this._modifiable = value; this.update(); } }); -Object.defineProperty(Potree.Measure.prototype, "closed", { - get: function(){ - return this._closed; - }, - set: function(value){ - this._closed = value; - this.update(); - } -}); -Object.defineProperty(Potree.Measure.prototype, "showDistances", { - get: function(){ - return this._showDistances; - }, - set: function(value){ - this._showDistances = value; - this.update(); - } -}); -Potree.MeasuringTool = function(scene, camera, renderer){ + + +// +// calculating area of a polygon: +// http://www.mathopenref.com/coordpolygonarea2.html +// +// +// + +Potree.ProfileTool = function(scene, camera, renderer){ var scope = this; this.enabled = false; @@ -7019,26 +8443,46 @@ Potree.MeasuringTool = function(scene, camera, renderer){ var state = STATE.DEFAULT; - this.activeMeasurement= null; - this.measurements = []; - this.sceneMeasurement = new THREE.Scene(); + var sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10); + + this.activeProfile = null; + this.profiles = []; + this.sceneProfile = new THREE.Scene(); this.sceneRoot = new THREE.Object3D(); - this.sceneMeasurement.add(this.sceneRoot); + this.sceneProfile.add(this.sceneRoot); this.light = new THREE.DirectionalLight( 0xffffff, 1 ); this.light.position.set( 0, 0, 10 ); this.light.lookAt(new THREE.Vector3(0,0,0)); - this.sceneMeasurement.add( this.light ); + this.sceneProfile.add( this.light ); this.hoveredElement = null; + function createSphereMaterial(){ + var sphereMaterial = new THREE.MeshLambertMaterial({ + shading: THREE.SmoothShading, + color: 0xff0000, + ambient: 0xaaaaaa, + depthTest: false, + depthWrite: false} + ); + + return sphereMaterial; + }; + + function onClick(event){ + if(state === STATE.INSERT){ var I = scope.getMousePointCloudIntersection(); if(I){ var pos = I.clone(); - scope.activeMeasurement.addMarker(pos); + if(scope.activeProfile.points.length === 1 && scope.activeProfile.width === null){ + scope.activeProfile.setWidth((camera.position.distanceTo(pos) / 50)); + } + + scope.activeProfile.addMarker(pos); var event = { type: 'newpoint', @@ -7046,35 +8490,30 @@ Potree.MeasuringTool = function(scene, camera, renderer){ }; scope.dispatchEvent(event); - if(scope.activeMeasurement.points.length > scope.activeMeasurement.maxMarkers){ - scope.finishInsertion(); - } - } } }; function onMouseMove(event){ - var rect = scope.domElement.getBoundingClientRect(); scope.mouse.x = ((event.clientX - rect.left) / scope.domElement.clientWidth) * 2 - 1; scope.mouse.y = -((event.clientY - rect.top) / scope.domElement.clientHeight) * 2 + 1; if(scope.dragstart){ var arg = { - type: "drag", + type: "mousedrag", event: event, tool: scope }; scope.dragstart.object.dispatchEvent(arg); - }else if(state == STATE.INSERT && scope.activeMeasurement){ + }else if(state == STATE.INSERT && scope.activeProfile){ var I = scope.getMousePointCloudIntersection(); if(I){ - var lastIndex = scope.activeMeasurement.points.length-1; - scope.activeMeasurement.setPosition(lastIndex, I); + var lastIndex = scope.activeProfile.points.length-1; + scope.activeProfile.setPosition(lastIndex, I); } }else{ @@ -7082,10 +8521,10 @@ Potree.MeasuringTool = function(scene, camera, renderer){ if(I){ - I.object.dispatchEvent({type: "move", target: I.object, event: event}); + I.object.dispatchEvent({type: "mousemove", target: I.object, event: event}); if(scope.hoveredElement && scope.hoveredElement !== I.object){ - scope.hoveredElement.dispatchEvent({type: "leave", target: scope.hoveredElement, event: event}); + scope.hoveredElement.dispatchEvent({type: "mouseleave", target: scope.hoveredElement, event: event}); } scope.hoveredElement = I.object; @@ -7093,7 +8532,7 @@ Potree.MeasuringTool = function(scene, camera, renderer){ }else{ if(scope.hoveredElement){ - scope.hoveredElement.dispatchEvent({type: "leave", target: scope.hoveredElement, event: event}); + scope.hoveredElement.dispatchEvent({type: "mouseleave", target: scope.hoveredElement, event: event}); } scope.hoveredElement = null; @@ -7108,31 +8547,37 @@ Potree.MeasuringTool = function(scene, camera, renderer){ } } - this.getState = function(){ - // TODO remove + function onMouseDown(event){ - return state; - }; + if(state !== STATE.DEFAULT){ + event.stopImmediatePropagation(); + } - function onMouseDown(event){ - if(event.which === 1){ - - if(state !== STATE.DEFAULT){ - event.stopImmediatePropagation(); - } var I = getHoveredElement(); if(I){ + + var widthStart = null; + for(var i = 0; i < scope.profiles.length; i++){ + var profile = scope.profiles[i]; + for(var j = 0; j < profile.spheres.length; j++){ + var sphere = profile.spheres[j]; + + if(sphere === I.object){ + widthStart = profile.width; + } + } + } scope.dragstart = { object: I.object, sceneClickPos: I.point, sceneStartPos: scope.sceneRoot.position.clone(), - mousePos: {x: scope.mouse.x, y: scope.mouse.y} + mousePos: {x: scope.mouse.x, y: scope.mouse.y}, + widthStart: widthStart }; - event.stopImmediatePropagation(); } @@ -7151,16 +8596,16 @@ Potree.MeasuringTool = function(scene, camera, renderer){ }else if (document.selection){ document.selection.empty(); } - - - if(scope.activeMeasurement && state === STATE.INSERT){ - scope.activeMeasurement.removeMarker(scope.activeMeasurement.points.length-1); + + if(scope.activeProfile && state === STATE.INSERT){ + scope.activeProfile.removeMarker(scope.activeProfile.points.length-1); scope.finishInsertion(); + event.stopImmediatePropagation(); } } function onMouseUp(event){ - + if(scope.dragstart){ scope.dragstart.object.dispatchEvent({type: "drop", event: event}); scope.dragstart = null; @@ -7176,16 +8621,8 @@ Potree.MeasuringTool = function(scene, camera, renderer){ var raycaster = new THREE.Raycaster(); raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); - var spheres = []; - for(var i = 0; i < scope.measurements.length; i++){ - var m = scope.measurements[i]; - - for(var j = 0; j < m.spheres.length; j++){ - spheres.push(m.spheres[j]); - } - } + var intersections = raycaster.intersectObjects(scope.profiles); - var intersections = raycaster.intersectObjects(spheres, true); if(intersections.length > 0){ return intersections[0]; }else{ @@ -7212,7 +8649,9 @@ Potree.MeasuringTool = function(scene, camera, renderer){ for(var i = 0; i < pointClouds.length; i++){ var pointcloud = pointClouds[i]; - var point = pointcloud.pick(scope.renderer, scope.camera, ray); + var point = pointcloud.pick(scope.renderer, scope.camera, ray, { + pickOutsideClipRegion: true + }); if(!point){ continue; @@ -7233,657 +8672,742 @@ Potree.MeasuringTool = function(scene, camera, renderer){ state = STATE.INSERT; var args = args || {}; - var showDistances = (typeof args.showDistances != "undefined") ? args.showDistances : true; - var showArea = (typeof args.showArea != "undefined") ? args.showArea : false; - var showAngles = (typeof args.showAngles != "undefined") ? args.showAngles : false; - var closed = (typeof args.closed != "undefined") ? args.closed : false; - var maxMarkers = args.maxMarkers || Number.MAX_SAFE_INTEGER; + var clip = args.clip || false; + var width = args.width || null; - var measurement = new Potree.Measure(); - measurement.showDistances = showDistances; - measurement.showArea = showArea; - measurement.showAngles = showAngles; - measurement.closed = closed; - measurement.maxMarkers = maxMarkers; - - this.addMeasurement(measurement); - measurement.addMarker(new THREE.Vector3(0,0,0)); + this.activeProfile = new Potree.HeightProfile(); + this.activeProfile.clip = clip; + this.activeProfile.setWidth(width); + this.addProfile(this.activeProfile); + this.activeProfile.addMarker(new THREE.Vector3(0,0,0)); - this.activeMeasurement = measurement; + return this.activeProfile; }; this.finishInsertion = function(){ - this.activeMeasurement.removeMarker(this.activeMeasurement.points.length-1); + this.activeProfile.removeMarker(this.activeProfile.points.length-1); var event = { type: "insertion_finished", - measurement: this.activeMeasurement + profile: this.activeProfile }; this.dispatchEvent(event); - this.activeMeasurement = null; + this.activeProfile = null; state = STATE.DEFAULT; }; - this.addMeasurement = function(measurement){ - this.sceneMeasurement.add(measurement); - this.measurements.push(measurement); + this.addProfile = function(profile){ + this.profiles.push(profile); + this.sceneProfile.add(profile); + profile.update(); - this.dispatchEvent({"type": "measurement_added", measurement: measurement}); - measurement.addEventListener("marker_added", function(event){ + this.dispatchEvent({"type": "profile_added", profile: profile}); + profile.addEventListener("marker_added", function(event){ scope.dispatchEvent(event); }); - measurement.addEventListener("marker_removed", function(event){ + profile.addEventListener("marker_removed", function(event){ scope.dispatchEvent(event); }); - measurement.addEventListener("marker_moved", function(event){ + profile.addEventListener("marker_moved", function(event){ + scope.dispatchEvent(event); + }); + profile.addEventListener("width_changed", function(event){ scope.dispatchEvent(event); }); }; - this.removeMeasurement = function(measurement){ - this.sceneMeasurement.remove(measurement); - var index = this.measurements.indexOf(measurement); + this.removeProfile = function(profile){ + this.sceneProfile.remove(profile); + var index = this.profiles.indexOf(profile); if(index >= 0){ - this.measurements.splice(index, 1); + this.profiles.splice(index, 1); + + this.dispatchEvent({"type": "profile_removed", profile: profile}); + } + + }; + + this.reset = function(){ + for(var i = this.profiles.length - 1; i >= 0; i--){ + var profile = this.profiles[i]; + this.removeProfile(profile); + } + }; + + this.update = function(){ + + for(var i = 0; i < this.profiles.length; i++){ + var profile = this.profiles[i]; + for(var j = 0; j < profile.spheres.length; j++){ + var sphere = profile.spheres[j]; + + var distance = scope.camera.position.distanceTo(sphere.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); + var scale = (15 / pr); + sphere.scale.set(scale, scale, scale); + } } + + this.light.position.copy(this.camera.position); + this.light.lookAt(this.camera.getWorldDirection().add(this.camera.position)); + + }; + + this.render = function(){ + this.update(); + renderer.render(this.sceneProfile, this.camera); + }; + + + this.domElement.addEventListener( 'click', onClick, false); + this.domElement.addEventListener( 'dblclick', onDoubleClick, false); + this.domElement.addEventListener( 'mousemove', onMouseMove, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mouseup', onMouseUp, true ); + +}; + + +Potree.ProfileTool.prototype = Object.create( THREE.EventDispatcher.prototype ); + + +Potree.TransformationTool = function(scene, camera, renderer){ + + var scope = this; + this.enabled = false; + + this.scene = scene; + this.camera = camera; + this.renderer = renderer; + this.domElement = renderer.domElement; + this.mouse = {x: 0, y: 0}; + this.dragstart = null; + + this.sceneTransformation = new THREE.Scene(); + this.sceneRoot = new THREE.Object3D(); + this.sceneTransformation.add(this.sceneRoot); + + this.sceneRotation = new THREE.Scene(); + + this.translationNode = new THREE.Object3D(); + this.rotationNode = new THREE.Object3D(); + this.scaleNode = new THREE.Object3D(); + + this.sceneRoot.add(this.translationNode); + this.sceneRoot.add(this.rotationNode); + this.sceneRoot.add(this.scaleNode); + + this.sceneRoot.visible = false; + + this.hoveredElement = null; + + this.STATE = { + DEFAULT: 0, + TRANSLATE_X: 1, + TRANSLATE_Y: 2, + TRANSLATE_Z: 3, + SCALE_X: 1, + SCALE_Y: 2, + SCALE_Z: 3 + }; + + this.parts = { + ARROW_X : {name: "arrow_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.TRANSLATE_X}, + ARROW_Z : {name: "arrow_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.TRANSLATE_Z}, + ARROW_Y : {name: "arrow_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.TRANSLATE_Y}, + SCALE_X : {name: "scale_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.SCALE_X}, + SCALE_Z : {name: "scale_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.SCALE_Z}, + SCALE_Y : {name: "scale_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.SCALE_Y}, + ROTATE_X : {name: "rotate_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.ROTATE_X}, + ROTATE_Z : {name: "rotate_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.ROTATE_Z}, + ROTATE_Y : {name: "rotate_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.ROTATE_Y} + }; + + this.buildTranslationNode = function(){ + var arrowX = scope.createArrow(scope.parts.ARROW_X, scope.parts.ARROW_X.color); + arrowX.rotation.z = -Math.PI/2; + + var arrowY = scope.createArrow(scope.parts.ARROW_Y, scope.parts.ARROW_Y.color); + + var arrowZ = scope.createArrow(scope.parts.ARROW_Z, scope.parts.ARROW_Z.color); + arrowZ.rotation.x = -Math.PI/2; + + this.translationNode.add(arrowX); + this.translationNode.add(arrowY); + this.translationNode.add(arrowZ); + }; + + this.buildScaleNode = function(){ + var xHandle = this.createScaleHandle(scope.parts.SCALE_X, 0xff0000); + xHandle.rotation.z = -Math.PI/2; + + var yHandle = this.createScaleHandle(scope.parts.SCALE_Y, 0x00ff00); + + var zHandle = this.createScaleHandle(scope.parts.SCALE_Z, 0x0000ff); + zHandle.rotation.x = -Math.PI/2; + + this.scaleNode.add(xHandle); + this.scaleNode.add(yHandle); + this.scaleNode.add(zHandle); + }; + + this.buildRotationNode = function(){ + var xHandle = this.createRotationCircle(scope.parts.ROTATE_X, 0xff0000); + xHandle.rotation.y = -Math.PI/2; + + var yHandle = this.createRotationCircle(scope.parts.ROTATE_Y, 0x00ff00); + + var zHandle = this.createRotationCircle(scope.parts.ROTATE_Z, 0x0000ff); + yHandle.rotation.x = -Math.PI/2; + + this.rotationNode.add(xHandle); + this.rotationNode.add(yHandle); + this.rotationNode.add(zHandle); + + + var sg = new THREE.SphereGeometry(2.9, 24, 24); + var sphere = new THREE.Mesh(sg, new THREE.MeshBasicMaterial({color: 0xaaaaaa, transparent: true, opacity: 0.4})); + + this.sceneRotation.add(sphere); + + var moveEvent = function(event){ + sphere.material.color.setHex(0x555555); + }; + + var leaveEvent = function(event){ + sphere.material.color.setHex(0xaaaaaa); + }; + + var dragEvent = function(event){ + event.event.stopImmediatePropagation(); + + var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0.1); + var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0.1); + var mouseDiff = new THREE.Vector3().subVectors(mouseEnd, mouseStart); + + var sceneStart = mouseStart.clone().unproject(scope.camera); + var sceneEnd = mouseEnd.clone().unproject(scope.camera); + var sceneDiff = new THREE.Vector3().subVectors(sceneEnd, sceneStart); + var sceneDir = sceneDiff.clone().normalize(); + var toCamDir = new THREE.Vector3().subVectors(scope.camera.position, sceneStart).normalize(); + var rotationAxis = toCamDir.clone().cross(sceneDir); + var rotationAmount = 6 * mouseDiff.length(); + + for(var i = 0; i < scope.targets.length; i++){ + var target = scope.targets[i]; + var startRotation = scope.dragstart.rotations[i]; + + target.rotation.copy(startRotation); + + var q = new THREE.Quaternion(); + + q.setFromAxisAngle( rotationAxis, rotationAmount ); + target.quaternion.multiplyQuaternions( q, target.quaternion ); + + } + }; + + var dropEvent = function(event){ + + }; + + sphere.addEventListener("mousemove", moveEvent); + sphere.addEventListener("mouseleave", leaveEvent); + sphere.addEventListener("mousedrag", dragEvent); + sphere.addEventListener("drop", dropEvent); + + }; + + + + this.createBox = function(color){ + var boxGeometry = new THREE.BoxGeometry(1, 1, 1); + var boxMaterial = new THREE.MeshBasicMaterial({color: color, transparent: true, opacity: 0.5}); + var box = new THREE.Mesh(boxGeometry, boxMaterial); + + return box; }; - this.reset = function(){ - for(var i = this.measurements.length - 1; i >= 0; i--){ - var measurement = this.measurements[i]; - this.removeMeasurement(measurement); - } - }; + var sph1, sph2, sph3; - this.update = function(){ - var measurements = []; - for(var i = 0; i < this.measurements.length; i++){ - measurements.push(this.measurements[i]); + this.createRotationCircle = function(partID, color){ + //var geometry = new THREE.TorusGeometry(3, 0.1, 12, 48); + //var material = new THREE.MeshBasicMaterial({color: color}); + // + //var ring = new THREE.Mesh(geometry, material); + + var vertices = []; + var segments = 128; + for(var i = 0; i <= segments; i++){ + var u = (2 * Math.PI * i) / segments; + var x = 3 * Math.cos(u); + var y = 3 * Math.sin(u); + + vertices.push(new THREE.Vector3(x, y, 0)); } - if(this.activeMeasurement){ - measurements.push(this.activeMeasurement); + var geometry = new THREE.Geometry(); + for(var i = 0; i < vertices.length; i++){ + geometry.vertices.push(vertices[i]); } + var material = new THREE.LineBasicMaterial({color: color}); + var ring = new THREE.Line( geometry, material); + ring.mode = THREE.LineStrip; + ring.scale.set(1, 1, 1); + //this.rotationNode.add(ring); - // make sizes independant of distance and fov - for(var i = 0; i < measurements.length; i++){ - var measurement = measurements[i]; - - // spheres - for(var j = 0; j < measurement.spheres.length; j++){ - var sphere = measurement.spheres[j]; - - var distance = scope.camera.position.distanceTo(sphere.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); - var scale = (15 / pr); - sphere.scale.set(scale, scale, scale); - + + var moveEvent = function(event){ + material.color.setRGB(1, 1, 0); + }; + + var leaveEvent = function(event){ + material.color.setHex(color); + }; + + var dragEvent = function(event){ + + event.event.stopImmediatePropagation(); + + var normal = new THREE.Vector3(); + if(partID === scope.parts.ROTATE_X){ + normal.x = 1; + }else if(partID === scope.parts.ROTATE_Y){ + normal.y = 1; + }else if(partID === scope.parts.ROTATE_Z){ + normal.z = -1; } - // edgeLabels - for(var j = 0; j < measurement.edgeLabels.length; j++){ - var label = measurement.edgeLabels[j]; - - var distance = scope.camera.position.distanceTo(label.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); - var scale = (70 / pr); - label.scale.set(scale, scale, scale); + var sceneClickPos = scope.dragstart.sceneClickPos.clone(); + var sceneOrigin = scope.sceneRoot.position.clone(); + var sceneNormal = sceneClickPos.clone().sub(sceneOrigin).normalize(); + + var screenClickPos = sceneClickPos.clone().project(scope.camera); + var screenOrigin = sceneOrigin.clone().project(scope.camera); + var screenNormal = screenClickPos.clone().sub(screenOrigin).normalize(); + var screenTangent = new THREE.Vector3(screenNormal.y, screenNormal.x, 0); + + var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); + var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); + + var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, scope.sceneRoot.position); + var camOrigin = scope.camera.position; + var camDirection = new THREE.Vector3( 0, 0, -1 ).applyQuaternion( scope.camera.quaternion ); + var direction = new THREE.Vector3( mouseEnd.x, mouseEnd.y, 0.5 ).unproject(scope.camera).sub( scope.camera.position ).normalize(); + var ray = new THREE.Ray( camOrigin, direction); + var I = ray.intersectPlane(plane); + + if(!I){ + return; } - // angle labels - for(var j = 0; j < measurement.edgeLabels.length; j++){ - var label = measurement.angleLabels[j]; - - var distance = scope.camera.position.distanceTo(label.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); - var scale = (70 / pr); - label.scale.set(scale, scale, scale); + sceneTargetNormal = I.clone().sub(sceneOrigin).normalize(); + + var angleToClick; + var angleToTarget; + + if(partID === scope.parts.ROTATE_X){ + angleToClick = 2 * Math.PI + Math.atan2(sceneNormal.y, -sceneNormal.z); + angleToTarget = 4 * Math.PI + Math.atan2(sceneTargetNormal.y, -sceneTargetNormal.z); + }else if(partID === scope.parts.ROTATE_Y){ + angleToClick = 2 * Math.PI + Math.atan2(-sceneNormal.z, sceneNormal.x); + angleToTarget = 4 * Math.PI + Math.atan2(-sceneTargetNormal.z, sceneTargetNormal.x); + }else if(partID === scope.parts.ROTATE_Z){ + angleToClick = 2 * Math.PI + Math.atan2(sceneNormal.x, sceneNormal.y); + angleToTarget = 4 * Math.PI + Math.atan2(sceneTargetNormal.x, sceneTargetNormal.y); } - // areaLabel - var distance = scope.camera.position.distanceTo(measurement.areaLabel.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); - var scale = (80 / pr); - measurement.areaLabel.scale.set(scale, scale, scale); - } - - this.light.position.copy(this.camera.position); - this.light.lookAt(this.camera.getWorldDirection().add(this.camera.position)); - - }; - - this.render = function(){ - this.update(); - this.renderer.render(this.sceneMeasurement, this.camera); - }; - - this.domElement.addEventListener( 'click', onClick, false); - this.domElement.addEventListener( 'dblclick', onDoubleClick, false); - this.domElement.addEventListener( 'mousemove', onMouseMove, false ); - this.domElement.addEventListener( 'mousedown', onMouseDown, false ); - this.domElement.addEventListener( 'mouseup', onMouseUp, true ); -}; - - -Potree.MeasuringTool.prototype = Object.create( THREE.EventDispatcher.prototype ); - + var diff = angleToTarget - angleToClick; + + for(var i = 0; i < scope.targets.length; i++){ + var target = scope.targets[i]; + var startRotation = scope.dragstart.rotations[i]; + + target.rotation.copy(startRotation); + var q = new THREE.Quaternion(); -Potree.HeightProfile = function(){ - var scope = this; - - THREE.Object3D.call( this ); + q.setFromAxisAngle( normal, diff ); // axis must be normalized, angle in radians + target.quaternion.multiplyQuaternions( q, target.quaternion ); - this.points = []; - this.spheres = []; - this.edges = []; - this.boxes = []; - this.width = 1; - this.height = 20; - this._modifiable = true; - - var sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10); - var lineColor = new THREE.Color( 0xff0000 ); - - var createSphereMaterial = function(){ - var sphereMaterial = new THREE.MeshLambertMaterial({ - shading: THREE.SmoothShading, - color: 0xff0000, - ambient: 0xaaaaaa, - depthTest: false, - depthWrite: false} - ); + } + + + + + }; - return sphereMaterial; - }; - - var moveEvent = function(event){ - event.target.material.emissive.setHex(0x888888); - }; - - var leaveEvent = function(event){ - event.target.material.emissive.setHex(0x000000); - }; - - var dragEvent = function(event){ - - var tool = event.tool; - var dragstart = tool.dragstart; - var mouse = tool.mouse; - - if(event.event.ctrlKey){ + var dropEvent = function(event){ - var mouseStart = new THREE.Vector3(dragstart.mousePos.x, dragstart.mousePos.y, 0); - var mouseEnd = new THREE.Vector3(mouse.x, mouse.y, 0); - var widthStart = dragstart.widthStart; - - var scale = 1 - 10 * (mouseStart.y - mouseEnd.y); - scale = Math.max(0.01, scale); - if(widthStart){ - scope.setWidth(widthStart * scale); - } + }; - }else{ - - var I = tool.getMousePointCloudIntersection(); - - if(I){ - var index = scope.spheres.indexOf(tool.dragstart.object); - scope.setPosition(index, I); - } - } + ring.addEventListener("mousemove", moveEvent); + ring.addEventListener("mouseleave", leaveEvent); + ring.addEventListener("mousedrag", dragEvent); + ring.addEventListener("drop", dropEvent); - event.event.stopImmediatePropagation(); - }; - - var dropEvent = function(event){ - + return ring; }; - this.addMarker = function(point){ + this.createScaleHandle = function(partID, color){ + var boxGeometry = new THREE.BoxGeometry(1, 1, 1); + var material = new THREE.MeshBasicMaterial({color: color, depthTest: false, depthWrite: false}); + + var box = new THREE.Mesh(boxGeometry, material); + box.scale.set(0.3, 0.3, 0.3); + box.position.set(0, 3, 0); + + var shaftGeometry = new THREE.Geometry(); + shaftGeometry.vertices.push(new THREE.Vector3(0, 0, 0)); + shaftGeometry.vertices.push(new THREE.Vector3(0, 3, 0)); + var shaftMaterial = new THREE.LineBasicMaterial({color: color, depthTest: false, depthWrite: false}); + var shaft = new THREE.Line(shaftGeometry, shaftMaterial); + + var handle = new THREE.Object3D(); + handle.add(box); + handle.add(shaft); + + handle.partID = partID; + + + var moveEvent = function(event){ + shaftMaterial.color.setRGB(1, 1, 0); + material.color.setRGB(1, 1, 0); + }; - this.points.push(point); - - // sphere - var sphere = new THREE.Mesh(sphereGeometry, createSphereMaterial()); - sphere.addEventListener("mousemove", moveEvent); - sphere.addEventListener("mouseleave", leaveEvent); - sphere.addEventListener("mousedrag", dragEvent); - sphere.addEventListener("drop", dropEvent); + var leaveEvent = function(event){ + shaftMaterial.color.setHex(color); + material.color.setHex(color); + }; - this.add(sphere); - this.spheres.push(sphere); + var dragEvent = function(event){ - // edges & boxes - if(this.points.length > 1){ + var sceneDirection = new THREE.Vector3(); + if(partID === scope.parts.SCALE_X){ + sceneDirection.x = 1; + }else if(partID === scope.parts.SCALE_Y){ + sceneDirection.y = 1; + }else if(partID === scope.parts.SCALE_Z){ + sceneDirection.z = -1; + } + + var sceneClickPos = scope.dragstart.sceneClickPos.clone(); + sceneClickPos.multiply(sceneDirection); + sceneClickPos.z *= -1; - var lineGeometry = new THREE.Geometry(); - lineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3()); - lineGeometry.colors.push(lineColor, lineColor, lineColor); - var lineMaterial = new THREE.LineBasicMaterial( { - vertexColors: THREE.VertexColors, - linewidth: 2, - transparent: true, - opacity: 0.4 - }); - lineMaterial.depthTest = false; - var edge = new THREE.Line(lineGeometry, lineMaterial); - edge.visible = false; + var lineStart = scope.dragstart.sceneStartPos.clone().project(scope.camera); + var lineEnd = scope.dragstart.sceneStartPos.clone().add(sceneDirection).project(scope.camera); - this.add(edge); - this.edges.push(edge); + var origin = lineStart.clone(); + var screenDirection = lineEnd.clone().sub(lineStart); + screenDirection.normalize(); + var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); + var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); + + var directionDistance = new THREE.Vector3().subVectors(mouseEnd, mouseStart).dot(screenDirection); + var pointOnLine = screenDirection.clone().multiplyScalar(directionDistance).add(origin); - var boxGeometry = new THREE.BoxGeometry(1, 1, 1); - var boxMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: 0.2}); - var box = new THREE.Mesh(boxGeometry, boxMaterial); - box.visible = false; + pointOnLine.unproject(scope.camera); - this.add(box); - this.boxes.push(box); + var diff = scope.sceneRoot.position.clone().sub(pointOnLine); + diff.multiply(new THREE.Vector3(-1, -1, 1)); - } + for(var i = 0; i < scope.targets.length; i++){ + var target = scope.targets[i]; + var startScale = scope.dragstart.scales[i]; + target.scale.copy(startScale).add(diff); + target.scale.x = Math.max(target.scale.x, 0.01); + target.scale.y = Math.max(target.scale.y, 0.01); + target.scale.z = Math.max(target.scale.z, 0.01); + } + + event.event.stopImmediatePropagation(); + }; - var event = { - "type": "marker_added", - "profile": this + var dropEvent = function(event){ + material.color.set(color); }; - this.dispatchEvent(event); - this.setPosition(this.points.length-1, point); + box.addEventListener("mousemove", moveEvent); + box.addEventListener("mouseleave", leaveEvent); + box.addEventListener("mousedrag", dragEvent); + box.addEventListener("drop", dropEvent); + shaft.addEventListener("mousemove", moveEvent); + shaft.addEventListener("mouseleave", leaveEvent); + shaft.addEventListener("mousedrag", dragEvent); + shaft.addEventListener("drop", dropEvent); + + return handle; }; - this.removeMarker = function(index){ - this.points.splice(index, 1); - - this.remove(this.spheres[index]); + this.createArrow = function(partID, color){ + var material = new THREE.MeshBasicMaterial({color: color, depthTest: false, depthWrite: false}); - var edgeIndex = (index === 0) ? 0 : (index - 1); - this.remove(this.edges[edgeIndex]); - this.edges.splice(edgeIndex, 1); - this.remove(this.boxes[edgeIndex]); - this.boxes.splice(edgeIndex, 1); + //var shaftGeometry = new THREE.CylinderGeometry(0.05, 0.05, 3, 10, 1, false); + //var shaftMaterial = material; + //var shaft = new THREE.Mesh(shaftGeometry, shaftMaterial); + //shaft.position.y = 1.5; - this.spheres.splice(index, 1); + var shaftGeometry = new THREE.Geometry(); + shaftGeometry.vertices.push(new THREE.Vector3(0, 0, 0)); + shaftGeometry.vertices.push(new THREE.Vector3(0, 3, 0)); + var shaftMaterial = new THREE.LineBasicMaterial({color: color, depthTest: false, depthWrite: false}); + var shaft = new THREE.Line(shaftGeometry, shaftMaterial); - this.update(); - var event = { - "type": "marker_removed", - "profile": this - }; - this.dispatchEvent(event); - }; - - /** - * see http://www.mathopenref.com/coordpolygonarea2.html - */ - this.getArea = function(){ - var area = 0; - var j = this.points.length - 1; - for(var i = 0; i < this.points.length; i++){ - var p1 = this.points[i]; - var p2 = this.points[j]; - area += (p2.x + p1.x) * (p1.z - p2.z); - j = i; - } + var headGeometry = new THREE.CylinderGeometry(0, 0.2, 0.5, 10, 1, false); + var headMaterial = material; + var head = new THREE.Mesh(headGeometry, headMaterial); + head.position.y = 3; - return Math.abs(area / 2); - }; - - this.setPosition = function(index, position){ - var point = this.points[index]; - point.copy(position); + var arrow = new THREE.Object3D(); + arrow.add(shaft); + arrow.add(head); + arrow.partID = partID; + arrow.material = material; - var event = { - type: 'marker_moved', - profile: this, - index: index, - position: position.clone() + var moveEvent = function(event){ + headMaterial.color.setRGB(1, 1, 0); + shaftMaterial.color.setRGB(1, 1, 0); }; - this.dispatchEvent(event); - - this.update(); - }; - - this.setWidth = function(width){ - this.width = width; - var event = { - type: 'width_changed', - profile: this, - width: width + var leaveEvent = function(event){ + headMaterial.color.set(color); + shaftMaterial.color.set(color); }; - this.dispatchEvent(event); - this.update(); - }; - - this.update = function(){ - - if(this.points.length === 0){ - return; - }else if(this.points.length === 1){ - var point = this.points[0]; - this.spheres[0].position.copy(point); + var dragEvent = function(event){ + + var sceneDirection = new THREE.Vector3(); + if(partID === scope.parts.ARROW_X){ + sceneDirection.x = 1; + }else if(partID === scope.parts.ARROW_Y){ + sceneDirection.y = 1; + }else if(partID === scope.parts.ARROW_Z){ + sceneDirection.z = -1; + } - return; - } + var sceneClickPos = scope.dragstart.sceneClickPos.clone(); + sceneClickPos.multiply(sceneDirection); + sceneClickPos.z *= -1; - var min = this.points[0].clone(); - var max = this.points[0].clone(); - var centroid = new THREE.Vector3(); - var lastIndex = this.points.length - 1; - for(var i = 0; i <= lastIndex; i++){ - var point = this.points[i]; - var sphere = this.spheres[i]; - var leftIndex = (i === 0) ? lastIndex : i - 1; - var rightIndex = (i === lastIndex) ? 0 : i + 1; - var leftVertex = this.points[leftIndex]; - var rightVertex = this.points[rightIndex]; - var leftEdge = this.edges[leftIndex]; - var rightEdge = this.edges[i]; - var leftBox = this.boxes[leftIndex]; - var rightBox = this.boxes[i]; + //var lineStart = new THREE.Vector3(); + //lineStart.x = scope.dragstart.mousePos.x; + //lineStart.y = scope.dragstart.mousePos.y; + var lineStart = scope.dragstart.sceneStartPos.clone().project(scope.camera); + var lineEnd = scope.dragstart.sceneStartPos.clone().add(sceneDirection).project(scope.camera); - var leftEdgeLength = point.distanceTo(leftVertex); - var rightEdgeLength = point.distanceTo(rightVertex); - var leftEdgeCenter = new THREE.Vector3().addVectors(leftVertex, point).multiplyScalar(0.5); - var rightEdgeCenter = new THREE.Vector3().addVectors(point, rightVertex).multiplyScalar(0.5); + var origin = lineStart.clone(); + var screenDirection = lineEnd.clone().sub(lineStart); + screenDirection.normalize(); - sphere.position.copy(point); - if(this._modifiable){ - sphere.visible = true; - }else{ - sphere.visible = false; - } - if(leftEdge){ - leftEdge.geometry.vertices[1].copy(point); - leftEdge.geometry.verticesNeedUpdate = true; - leftEdge.geometry.computeBoundingSphere(); - } + var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); + var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); - if(rightEdge){ - rightEdge.geometry.vertices[0].copy(point); - rightEdge.geometry.verticesNeedUpdate = true; - rightEdge.geometry.computeBoundingSphere(); - } + //var htmlStart = mouseStart.clone().addScalar(1).multiplyScalar(0.5); + //htmlStart.x *= scope.domElement.clientWidth; + //htmlStart.y *= scope.domElement.clientHeight; + // + //var htmlEnd = mouseEnd.clone().addScalar(1).multiplyScalar(0.5); + //htmlEnd.x *= scope.domElement.clientWidth; + //htmlEnd.y *= scope.domElement.clientHeight; + // + //var el = document.getElementById("testDiv"); + //el.style.left = htmlStart.x; + //el.style.width = htmlEnd.x - htmlStart.x; + //el.style.bottom = htmlStart.y; + //el.style.top = scope.domElement.clientHeight - htmlEnd.y; - if(leftBox){ - var start = leftVertex; - var end = point; - var length = start.clone().setY(0).distanceTo(end.clone().setY(0)); - leftBox.scale.set(length, this.height, this.width); - - var center = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); - var diff = new THREE.Vector3().subVectors(end, start); - var target = new THREE.Vector3(diff.z, 0, -diff.x); - - leftBox.position.set(0,0,0); - leftBox.lookAt(target); - leftBox.position.copy(center); - } + //var directionDistance = new THREE.Vector3().subVectors(mouseEnd, origin).dot(screenDirection); + var directionDistance = new THREE.Vector3().subVectors(mouseEnd, mouseStart).dot(screenDirection); + var pointOnLine = screenDirection.clone().multiplyScalar(directionDistance).add(origin); - centroid.add(point); - min.min(point); - max.max(point); - } - centroid.multiplyScalar(1 / this.points.length); - - for(var i = 0; i < this.boxes.length; i++){ - var box = this.boxes[i]; + pointOnLine.unproject(scope.camera); - box.position.y = min.y + (max.y - min.y) / 2; - //box.scale.y = max.y - min.y + 50; - box.scale.y = 1000000; - } - - }; - - this.raycast = function(raycaster, intersects){ - - for(var i = 0; i < this.points.length; i++){ - var sphere = this.spheres[i]; + var diff = scope.sceneRoot.position.clone(); + //scope.position.copy(pointOnLine); + var offset = sceneClickPos.clone().sub(scope.dragstart.sceneStartPos); + scope.sceneRoot.position.copy(pointOnLine); + //scope.sceneRoot.position.sub(offset); + diff.sub(scope.sceneRoot.position); - sphere.raycast(raycaster, intersects); - } - - // recalculate distances because they are not necessarely correct - // for scaled objects. - // see https://github.com/mrdoob/three.js/issues/5827 - // TODO: remove this once the bug has been fixed - for(var i = 0; i < intersects.length; i++){ - var I = intersects[i]; - I.distance = raycaster.ray.origin.distanceTo(I.point); - } - intersects.sort( function ( a, b ) { return a.distance - b.distance;} ); - }; - - -}; - -Potree.HeightProfile.prototype = Object.create( THREE.Object3D.prototype ); - -Object.defineProperty(Potree.HeightProfile.prototype, "modifiable", { - get: function(){ - return this.modifiable; - }, - set: function(value){ - this._modifiable = value; - this.update(); - } -}); - - - - + for(var i = 0; i < scope.targets.length; i++){ + var target = scope.targets[i]; + target.position.sub(diff); + } + + //if(!sph1){ + // var g = new THREE.SphereGeometry(0.2); + // + // var m1 = new THREE.MeshBasicMaterial({color: 0xff0000}); + // var m2 = new THREE.MeshBasicMaterial({color: 0x00ff00}); + // var m3 = new THREE.MeshBasicMaterial({color: 0x0000ff}); + // + // sph1 = new THREE.Mesh(g, m1); + // sph2 = new THREE.Mesh(g, m2); + // sph3 = new THREE.Mesh(g, m3); + // + // scope.scene.add(sph1); + // scope.scene.add(sph2); + // scope.scene.add(sph3); + //} + //sph1.position.copy(scope.dragstart.sceneStartPos); + //sph2.position.copy(scope.dragstart.sceneClickPos); + //sph3.position.copy(pointOnLine); -// -// calculating area of a polygon: -// http://www.mathopenref.com/coordpolygonarea2.html -// -// -// + event.event.stopImmediatePropagation(); -Potree.ProfileTool = function(scene, camera, renderer){ - - var scope = this; - this.enabled = false; - - this.scene = scene; - this.camera = camera; - this.renderer = renderer; - this.domElement = renderer.domElement; - this.mouse = {x: 0, y: 0}; - - var STATE = { - DEFAULT: 0, - INSERT: 1 - }; - - var state = STATE.DEFAULT; - - var sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10); - - this.activeProfile = null; - this.profiles = []; - this.sceneProfile = new THREE.Scene(); - this.sceneRoot = new THREE.Object3D(); - this.sceneProfile.add(this.sceneRoot); - - this.light = new THREE.DirectionalLight( 0xffffff, 1 ); - this.light.position.set( 0, 0, 10 ); - this.light.lookAt(new THREE.Vector3(0,0,0)); - this.sceneProfile.add( this.light ); - - this.hoveredElement = null; - - function createSphereMaterial(){ - var sphereMaterial = new THREE.MeshLambertMaterial({ - shading: THREE.SmoothShading, - color: 0xff0000, - ambient: 0xaaaaaa, - depthTest: false, - depthWrite: false} - ); + }; - return sphereMaterial; - }; - - - function onClick(event){ - - if(state === STATE.INSERT){ - var I = scope.getMousePointCloudIntersection(); - if(I){ - var pos = I.clone(); - - scope.activeProfile.addMarker(pos); - - var event = { - type: 'newpoint', - position: pos.clone() - }; - scope.dispatchEvent(event); - - } - } + var dropEvent = function(event){ + shaftMaterial.color.set(color); + }; + + shaft.addEventListener("mousemove", moveEvent); + head.addEventListener("mousemove", moveEvent); + + shaft.addEventListener("mouseleave", leaveEvent); + head.addEventListener("mouseleave", leaveEvent); + + shaft.addEventListener("mousedrag", dragEvent); + head.addEventListener("mousedrag", dragEvent); + + shaft.addEventListener("drop", dropEvent); + head.addEventListener("drop", dropEvent); + + + + return arrow; }; function onMouseMove(event){ + var rect = scope.domElement.getBoundingClientRect(); scope.mouse.x = ((event.clientX - rect.left) / scope.domElement.clientWidth) * 2 - 1; scope.mouse.y = -((event.clientY - rect.top) / scope.domElement.clientHeight) * 2 + 1; if(scope.dragstart){ - var arg = { - type: "mousedrag", - event: event, - tool: scope - }; - scope.dragstart.object.dispatchEvent(arg); - - }else if(state == STATE.INSERT && scope.activeProfile){ - var I = scope.getMousePointCloudIntersection(); - - if(I){ - var lastIndex = scope.activeProfile.points.length-1; - scope.activeProfile.setPosition(lastIndex, I); - } + scope.dragstart.object.dispatchEvent({ + type: "mousedrag", + event: event + }); }else{ + + var I = getHoveredElement(); - if(I){ + var object = I.object; - I.object.dispatchEvent({type: "mousemove", target: I.object, event: event}); + //var g = new THREE.SphereGeometry(2); + //var m = new THREE.Mesh(g); + //scope.scene.add(m); + //m.position.copy(I.point); - if(scope.hoveredElement && scope.hoveredElement !== I.object){ - scope.hoveredElement.dispatchEvent({type: "mouseleave", target: scope.hoveredElement, event: event}); + object.dispatchEvent({type: "mousemove", event: event}); + + if(scope.hoveredElement && scope.hoveredElement !== object){ + scope.hoveredElement.dispatchEvent({type: "mouseleave", event: event}); } - scope.hoveredElement = I.object; + scope.hoveredElement = object; }else{ - if(scope.hoveredElement){ - scope.hoveredElement.dispatchEvent({type: "mouseleave", target: scope.hoveredElement, event: event}); + scope.hoveredElement.dispatchEvent({type: "mouseleave", event: event}); } - - scope.hoveredElement = null; + scope.hoveredElement = null; } + } + + }; - function onRightClick(event){ - if(state == STATE.INSERT){ - scope.finishInsertion(); - } - } - function onMouseDown(event){ - if(state !== STATE.DEFAULT){ - event.stopImmediatePropagation(); - } if(event.which === 1){ - + // left click var I = getHoveredElement(); - if(I){ - - var widthStart = null; - for(var i = 0; i < scope.profiles.length; i++){ - var profile = scope.profiles[i]; - for(var j = 0; j < profile.spheres.length; j++){ - var sphere = profile.spheres[j]; - - if(sphere === I.object){ - widthStart = profile.width; - } - } - } + var scales = []; + var rotations = []; + for(var i = 0; i < scope.targets.length; i++){ + scales.push(scope.targets[i].scale.clone()); + rotations.push(scope.targets[i].rotation.clone()); + } + scope.dragstart = { object: I.object, sceneClickPos: I.point, sceneStartPos: scope.sceneRoot.position.clone(), mousePos: {x: scope.mouse.x, y: scope.mouse.y}, - widthStart: widthStart + scales: scales, + rotations: rotations }; event.stopImmediatePropagation(); - } + }else if(event.which === 3){ + // right click - }else if(event.which === 3){ - onRightClick(event); - } - } - - function onDoubleClick(event){ - - // fix move event after double click - // see: http://stackoverflow.com/questions/8125165/event-listener-for-dblclick-causes-event-for-mousemove-to-not-work-and-show-a-ci - if (window.getSelection){ - window.getSelection().removeAllRanges(); - }else if (document.selection){ - document.selection.empty(); - } - - if(scope.activeProfile && state === STATE.INSERT){ - scope.activeProfile.removeMarker(scope.activeProfile.points.length-1); - scope.finishInsertion(); + scope.setTargets([]); } - } + }; function onMouseUp(event){ - + if(scope.dragstart){ scope.dragstart.object.dispatchEvent({type: "drop", event: event}); scope.dragstart = null; } - - } + }; function getHoveredElement(){ + + if(scope.targets.length === 0){ + return; + } var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); vector.unproject(scope.camera); var raycaster = new THREE.Raycaster(); raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); + raycaster.linePrecision = 0.2; - var intersections = raycaster.intersectObjects(scope.profiles); + var objects = []; + if(scope.translationNode.visible){ + objects.push(scope.translationNode); + }else if(scope.scaleNode.visible){ + objects.push(scope.scaleNode); + }else if(scope.rotationNode.visible){ + objects.push(scope.rotationNode); + objects.push(scope.sceneRotation); + } + + var intersections = raycaster.intersectObjects(objects, true); + + // recalculate distances because they are not necessarely correct + // for scaled objects. + // see https://github.com/mrdoob/three.js/issues/5827 + // TODO: remove this once the bug has been fixed + for(var i = 0; i < intersections.length; i++){ + var I = intersections[i]; + I.distance = scope.camera.position.distanceTo(I.point); + } + intersections.sort( function ( a, b ) { return a.distance - b.distance;} ); if(intersections.length > 0){ return intersections[0]; @@ -7892,2338 +9416,4576 @@ Potree.ProfileTool = function(scene, camera, renderer){ } }; - this.getMousePointCloudIntersection = function(){ - var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); - vector.unproject(scope.camera); - - var direction = vector.sub(scope.camera.position).normalize(); - var ray = new THREE.Ray(scope.camera.position, direction); + this.setTargets = function(targets){ + scope.targets = targets; - var pointClouds = []; - scope.scene.traverse(function(object){ - if(object instanceof Potree.PointCloudOctree || object instanceof Potree.PointCloudArena4D){ - pointClouds.push(object); - } - }); + if(scope.targets.length === 0){ + this.sceneRoot.visible = false; + this.sceneRotation.visible = false; - var closestPoint = null; - var closestPointDistance = null; + return; + }else{ + this.sceneRoot.visible = true; + } - for(var i = 0; i < pointClouds.length; i++){ - var pointcloud = pointClouds[i]; - var point = pointcloud.pick(scope.renderer, scope.camera, ray); - - if(!point){ - continue; + //TODO calculate centroid of all targets + var target = targets[0]; + var bb; + if(target.geometry && target.geometry.boundingBox){ + bb = target.geometry.boundingBox; + }else{ + bb = target.boundingBox; + } + + if(bb){ + var centroid = bb.clone().applyMatrix4(target.matrixWorld).center(); + scope.sceneRoot.position.copy(centroid); + } + }; + + this.getBoundingBox = function(){ + var box = new THREE.Box3(); + + for(var i = 0; i < scope.targets.length; i++){ + var target = scope.targets[i]; + var targetBB; + + if(target.boundingBox){ + targetBB = target.boundingBox; + }else if(target.boundingSphere){ + targetBB = target.boundingSphere.getBoundingBox(); + }else if(target.geometry){ + if(target.geometry.boundingBox){ + targetBB = target.geometry.boundingBox; + }else if(target.geometry.boundingSphere){ + targetBB = target.geometry.boundingSphere.getBoundingBox(); + } } - var distance = scope.camera.position.distanceTo(point.position); + targetBB = Potree.utils.computeTransformedBoundingBox(targetBB, target.matrixWorld); - if(!closestPoint || distance < closestPointDistance){ - closestPoint = point; - closestPointDistance = distance; - } + box.union(targetBB); } - return closestPoint ? closestPoint.position : null; + return box; }; - this.startInsertion = function(args){ - state = STATE.INSERT; - - var args = args || {}; - var clip = args.clip || false; - var width = args.width || 1.0; + this.update = function(){ + var node = this.sceneRoot; + var wp = node.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); + var pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix); + var w = Math.abs((wp.z / 20)); // * (2 - pp.z / pp.w); + node.scale.set(w, w, w); - this.activeProfile = new Potree.HeightProfile(); - this.activeProfile.clip = clip; - this.activeProfile.setWidth(width); - this.addProfile(this.activeProfile); - this.activeProfile.addMarker(new THREE.Vector3(0,0,0)); + if(this.targets && this.targets.length === 1){ + this.scaleNode.rotation.copy(this.targets[0].rotation); + } - return this.activeProfile; + this.sceneRotation.scale.set(w,w,w); }; - this.finishInsertion = function(){ - this.activeProfile.removeMarker(this.activeProfile.points.length-1); - - var event = { - type: "insertion_finished", - profile: this.activeProfile - }; - this.dispatchEvent(event); + this.render = function(){ + this.update(); + this.sceneRotation.position.copy(this.sceneRoot.position); + this.sceneRotation.visible = this.rotationNode.visible && this.sceneRoot.visible; - this.activeProfile = null; - state = STATE.DEFAULT; + renderer.render(this.sceneRotation, this.camera); + renderer.render(this.sceneTransformation, this.camera); + }; + + this.translate = function(){ + this.translationNode.visible = true; + this.scaleNode.visible = false; + this.rotationNode.visible = false; + }; + + this.scale = function(){ + this.translationNode.visible = false; + this.scaleNode.visible = true; + this.rotationNode.visible = false; + }; + + this.rotate = function(){ + this.translationNode.visible = false; + this.scaleNode.visible = false; + this.rotationNode.visible = true; + }; + + this.reset = function(){ + this.setTargets([]); }; - this.addProfile = function(profile){ - this.profiles.push(profile); - this.sceneProfile.add(profile); - profile.update(); + this.buildTranslationNode(); + this.buildScaleNode(); + this.buildRotationNode(); + + //this.translate(); + this.rotate(); + + this.setTargets([]); + + //this.domElement.addEventListener( 'click', onClick, false); + this.domElement.addEventListener( 'mousemove', onMouseMove, true ); + this.domElement.addEventListener( 'mousedown', onMouseDown, true ); + this.domElement.addEventListener( 'mouseup', onMouseUp, true ); +}; + +Potree.Volume = function(args){ + + THREE.Object3D.call( this ); + + args = args || {}; + this._clip = args.clip || false; + this._modifiable = args.modifiable || true; + + var boxGeometry = new THREE.BoxGeometry(1, 1, 1); + boxGeometry.computeBoundingBox(); + + var boxFrameGeometry = new THREE.Geometry(); + // bottom + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); + // top + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); + // sides + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); + boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); + + this.dimension = new THREE.Vector3(1,1,1); + var material = new THREE.MeshBasicMaterial( { + color: 0x00ff00, + transparent: true, + opacity: 0.3, + depthTest: true, + depthWrite: true} ); + this.box = new THREE.Mesh( boxGeometry, material); + this.box.geometry.computeBoundingBox(); + this.boundingBox = this.box.geometry.boundingBox; + this.add(this.box); + + this.frame = new THREE.Line( boxFrameGeometry, new THREE.LineBasicMaterial({color: 0x000000})); + this.frame.mode = THREE.LinePieces; + this.add(this.frame); + + this.label = new Potree.TextSprite("0"); + this.label.setBorderColor({r:0, g:255, b:0, a:0.0}); + this.label.setBackgroundColor({r:0, g:255, b:0, a:0.0}); + this.label.material.depthTest = false; + this.label.material.depthWrite = false; + this.label.material.transparent = true; + this.label.position.y -= 0.5; + this.add(this.label); + + var v = this; + this.label.updateMatrixWorld = function(){ + var volumeWorldPos = new THREE.Vector3(); + volumeWorldPos.setFromMatrixPosition( v.matrixWorld ); + v.label.position.copy(volumeWorldPos); + v.label.updateMatrix(); + v.label.matrixWorld.copy(v.label.matrix); + v.label.matrixWorldNeedsUpdate = false; - this.dispatchEvent({"type": "profile_added", profile: profile}); - profile.addEventListener("marker_added", function(event){ - scope.dispatchEvent(event); - }); - profile.addEventListener("marker_removed", function(event){ - scope.dispatchEvent(event); - }); - profile.addEventListener("marker_moved", function(event){ - scope.dispatchEvent(event); - }); - }; - - this.removeProfile = function(profile){ - this.sceneProfile.remove(profile); - var index = this.profiles.indexOf(profile); - if(index >= 0){ - this.profiles.splice(index, 1); + for ( var i = 0, l = v.label.children.length; i < l; i ++ ) { + v.label.children[ i ].updateMatrixWorld( true ); } - - this.dispatchEvent({"type": "profile_removed", profile: profile}); }; - this.reset = function(){ - for(var i = this.profiles.length - 1; i >= 0; i--){ - var profile = this.profiles[i]; - this.removeProfile(profile); - } + this.setDimension = function(x,y,z){ + this.dimension.set(x,y,z); + this.box.scale.set(x,y,z); + this.frame.scale.set(x,y,z); + }; + + this.volume = function(){ + return Math.abs(this.scale.x * this.scale.y * this.scale.z); + //return Math.abs(this.dimension.x * this.dimension.y * this.dimension.z); }; this.update = function(){ + this.boundingBox = this.box.geometry.boundingBox; + this.boundingSphere = this.boundingBox.getBoundingSphere(); - for(var i = 0; i < this.profiles.length; i++){ - var profile = this.profiles[i]; - for(var j = 0; j < profile.spheres.length; j++){ - var sphere = profile.spheres[j]; - - var distance = scope.camera.position.distanceTo(sphere.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); - var scale = (15 / pr); - sphere.scale.set(scale, scale, scale); - } + if(this._clip){ + this.box.visible = false; + this.label.visible = false; + }else{ + this.box.visible = true; + this.label.visible = true; } + }; - this.light.position.copy(this.camera.position); - this.light.lookAt(this.camera.getWorldDirection().add(this.camera.position)); + this.raycast = function(raycaster, intersects){ - }; + var is = []; + this.box.raycast(raycaster, is); - this.render = function(){ - this.update(); - renderer.render(this.sceneProfile, this.camera); + if(is.length > 0){ + var I = is[0]; + intersects.push({ + distance: I.distance, + object: this, + point: I.point.clone() + }); + } }; - this.domElement.addEventListener( 'click', onClick, false); - this.domElement.addEventListener( 'dblclick', onDoubleClick, false); - this.domElement.addEventListener( 'mousemove', onMouseMove, false ); - this.domElement.addEventListener( 'mousedown', onMouseDown, false ); - this.domElement.addEventListener( 'mouseup', onMouseUp, true ); + this.update(); }; +Potree.Volume.prototype = Object.create( THREE.Object3D.prototype ); -Potree.ProfileTool.prototype = Object.create( THREE.EventDispatcher.prototype ); +Object.defineProperty(Potree.Volume.prototype, "clip", { + get: function(){ + return this._clip; + }, + + set: function(value){ + this._clip = value; + + this.update(); + } +}); +Object.defineProperty(Potree.Volume.prototype, "modifiable", { + get: function(){ + return this._modifiable; + }, + + set: function(value){ + this._modifiable = value; + + this.update(); + } +}); -Potree.TransformationTool = function(scene, camera, renderer){ +Potree.VolumeTool = function(scene, camera, renderer, transformationTool){ + var scope = this; this.enabled = false; this.scene = scene; + this.sceneVolume = new THREE.Scene(); this.camera = camera; this.renderer = renderer; - this.domElement = renderer.domElement; + this.transformationTool = transformationTool; + this.domElement = this.renderer.domElement; this.mouse = {x: 0, y: 0}; - this.dragstart = null; - - this.sceneTransformation = new THREE.Scene(); - this.sceneRoot = new THREE.Object3D(); - this.sceneTransformation.add(this.sceneRoot); - - this.sceneRotation = new THREE.Scene(); - - this.translationNode = new THREE.Object3D(); - this.rotationNode = new THREE.Object3D(); - this.scaleNode = new THREE.Object3D(); - - this.sceneRoot.add(this.translationNode); - this.sceneRoot.add(this.rotationNode); - this.sceneRoot.add(this.scaleNode); - - this.sceneRoot.visible = false; - - this.hoveredElement = null; - - this.STATE = { - DEFAULT: 0, - TRANSLATE_X: 1, - TRANSLATE_Y: 2, - TRANSLATE_Z: 3, - SCALE_X: 1, - SCALE_Y: 2, - SCALE_Z: 3 - }; - - this.parts = { - ARROW_X : {name: "arrow_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.TRANSLATE_X}, - ARROW_Z : {name: "arrow_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.TRANSLATE_Z}, - ARROW_Y : {name: "arrow_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.TRANSLATE_Y}, - SCALE_X : {name: "scale_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.SCALE_X}, - SCALE_Z : {name: "scale_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.SCALE_Z}, - SCALE_Y : {name: "scale_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.SCALE_Y}, - ROTATE_X : {name: "rotate_x", object: undefined, color: new THREE.Color( 0xff0000 ), state: this.STATE.ROTATE_X}, - ROTATE_Z : {name: "rotate_z", object: undefined, color: new THREE.Color( 0x0000ff ), state: this.STATE.ROTATE_Z}, - ROTATE_Y : {name: "rotate_y", object: undefined, color: new THREE.Color( 0x00ff00 ), state: this.STATE.ROTATE_Y} - }; - - this.buildTranslationNode = function(){ - var arrowX = scope.createArrow(scope.parts.ARROW_X, scope.parts.ARROW_X.color); - arrowX.rotation.z = -Math.PI/2; - - var arrowY = scope.createArrow(scope.parts.ARROW_Y, scope.parts.ARROW_Y.color); - - var arrowZ = scope.createArrow(scope.parts.ARROW_Z, scope.parts.ARROW_Z.color); - arrowZ.rotation.x = -Math.PI/2; - - this.translationNode.add(arrowX); - this.translationNode.add(arrowY); - this.translationNode.add(arrowZ); - }; - - this.buildScaleNode = function(){ - var xHandle = this.createScaleHandle(scope.parts.SCALE_X, 0xff0000); - xHandle.rotation.z = -Math.PI/2; - - var yHandle = this.createScaleHandle(scope.parts.SCALE_Y, 0x00ff00); - - var zHandle = this.createScaleHandle(scope.parts.SCALE_Z, 0x0000ff); - zHandle.rotation.x = -Math.PI/2; - - this.scaleNode.add(xHandle); - this.scaleNode.add(yHandle); - this.scaleNode.add(zHandle); - }; - this.buildRotationNode = function(){ - var xHandle = this.createRotationCircle(scope.parts.ROTATE_X, 0xff0000); - xHandle.rotation.y = -Math.PI/2; - - var yHandle = this.createRotationCircle(scope.parts.ROTATE_Y, 0x00ff00); - - var zHandle = this.createRotationCircle(scope.parts.ROTATE_Z, 0x0000ff); - yHandle.rotation.x = -Math.PI/2; - - this.rotationNode.add(xHandle); - this.rotationNode.add(yHandle); - this.rotationNode.add(zHandle); - - - var sg = new THREE.SphereGeometry(2.9, 24, 24); - var sphere = new THREE.Mesh(sg, new THREE.MeshBasicMaterial({color: 0xaaaaaa, transparent: true, opacity: 0.4})); - - this.sceneRotation.add(sphere); - - var moveEvent = function(event){ - sphere.material.color.setHex(0x555555); - }; - - var leaveEvent = function(event){ - sphere.material.color.setHex(0xaaaaaa); - }; - - var dragEvent = function(event){ - event.event.stopImmediatePropagation(); - - var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0.1); - var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0.1); - var mouseDiff = new THREE.Vector3().subVectors(mouseEnd, mouseStart); - - var sceneStart = mouseStart.clone().unproject(scope.camera); - var sceneEnd = mouseEnd.clone().unproject(scope.camera); - var sceneDiff = new THREE.Vector3().subVectors(sceneEnd, sceneStart); - var sceneDir = sceneDiff.clone().normalize(); - var toCamDir = new THREE.Vector3().subVectors(scope.camera.position, sceneStart).normalize(); - var rotationAxis = toCamDir.clone().cross(sceneDir); - var rotationAmount = 6 * mouseDiff.length(); - - for(var i = 0; i < scope.targets.length; i++){ - var target = scope.targets[i]; - var startRotation = scope.dragstart.rotations[i]; - - target.rotation.copy(startRotation); - - var q = new THREE.Quaternion(); - - q.setFromAxisAngle( rotationAxis, rotationAmount ); - target.quaternion.multiplyQuaternions( q, target.quaternion ); - - } - }; - - var dropEvent = function(event){ - - }; - - sphere.addEventListener("mousemove", moveEvent); - sphere.addEventListener("mouseleave", leaveEvent); - sphere.addEventListener("mousedrag", dragEvent); - sphere.addEventListener("drop", dropEvent); + this.volumes = []; + + var STATE = { + DEFAULT: 0, + INSERT_VOLUME: 1 }; + var state = STATE.DEFAULT; - this.createBox = function(color){ - var boxGeometry = new THREE.BoxGeometry(1, 1, 1); - var boxMaterial = new THREE.MeshBasicMaterial({color: color, transparent: true, opacity: 0.5}); - var box = new THREE.Mesh(boxGeometry, boxMaterial); + function onMouseMove(event){ + var rect = scope.domElement.getBoundingClientRect(); + scope.mouse.x = ((event.clientX - rect.left) / scope.domElement.clientWidth) * 2 - 1; + scope.mouse.y = -((event.clientY - rect.top) / scope.domElement.clientHeight) * 2 + 1; + }; + + function onMouseClick(event){ - return box; + //if(state === STATE.INSERT_VOLUME){ + // scope.finishInsertion(); + //}else if(event.which === 1){ + // var I = getHoveredElement(); + // + // if(I){ + // transformationTool.setTargets([I.object]); + // } + //} }; - var sph1, sph2, sph3; + function onMouseDown(event){ - this.createRotationCircle = function(partID, color){ - //var geometry = new THREE.TorusGeometry(3, 0.1, 12, 48); - //var material = new THREE.MeshBasicMaterial({color: color}); - // - //var ring = new THREE.Mesh(geometry, material); - - var vertices = []; - var segments = 128; - for(var i = 0; i <= segments; i++){ - var u = (2 * Math.PI * i) / segments; - var x = 3 * Math.cos(u); - var y = 3 * Math.sin(u); + if(state !== STATE.DEFAULT){ + event.stopImmediatePropagation(); + } + + if(state === STATE.INSERT_VOLUME){ + scope.finishInsertion(); + }else if(event.which === 1){ + var I = getHoveredElement(); - vertices.push(new THREE.Vector3(x, y, 0)); + if(I && I.object.modifiable){ + scope.transformationTool.setTargets([I.object]); + } } - var geometry = new THREE.Geometry(); - for(var i = 0; i < vertices.length; i++){ - geometry.vertices.push(vertices[i]); + + + if(event.which === 3){ + // open context menu + + //var element = getHoveredElement(); + // + //if(element){ + // var menu = document.createElement("div"); + // menu.style.position = "fixed"; + // menu.style.backgroundColor = "#bbbbbb"; + // menu.style.top = event.clientY + "px"; + // menu.style.left = event.clientX + "px"; + // menu.style.width = "200px"; + // menu.style.height = "100px"; + // menu.innerHTML = "abc"; + // menu.addEventListener("contextmenu", function(event){ + // event.preventDefault(); + // return false; + // }, false); + // + // scope.renderer.domElement.parentElement.appendChild(menu); + //} } - var material = new THREE.LineBasicMaterial({color: color}); - var ring = new THREE.Line( geometry, material); - ring.mode = THREE.LineStrip; - ring.scale.set(1, 1, 1); - //this.rotationNode.add(ring); + }; + + function onContextMenu(event){ + event.preventDefault(); + return false; + } + + function getHoveredElement(){ + + var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); + vector.unproject(scope.camera); + var raycaster = new THREE.Raycaster(); + raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); - var moveEvent = function(event){ - material.color.setRGB(1, 1, 0); - }; + var objects = []; + for(var i = 0; i < scope.volumes.length; i++){ + var object = scope.volumes[i]; + objects.push(object); + } - var leaveEvent = function(event){ - material.color.setHex(color); - }; + var intersections = raycaster.intersectObjects(objects, false); + if(intersections.length > 0){ + return intersections[0]; + }else{ + return false; + } + }; + + function getMousePointCloudIntersection(){ + var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); + vector.unproject(scope.camera); + + var direction = vector.sub(scope.camera.position).normalize(); + var ray = new THREE.Ray(scope.camera.position, direction); - var dragEvent = function(event){ + var pointClouds = []; + scope.scene.traverse(function(object){ + if(object instanceof Potree.PointCloudOctree || object instanceof Potree.PointCloudArena4D){ + pointClouds.push(object); + } + }); - event.event.stopImmediatePropagation(); + var closestPoint = null; + var closestPointDistance = null; - var normal = new THREE.Vector3(); - if(partID === scope.parts.ROTATE_X){ - normal.x = 1; - }else if(partID === scope.parts.ROTATE_Y){ - normal.y = 1; - }else if(partID === scope.parts.ROTATE_Z){ - normal.z = -1; - } - - var sceneClickPos = scope.dragstart.sceneClickPos.clone(); - var sceneOrigin = scope.sceneRoot.position.clone(); - var sceneNormal = sceneClickPos.clone().sub(sceneOrigin).normalize(); - - var screenClickPos = sceneClickPos.clone().project(scope.camera); - var screenOrigin = sceneOrigin.clone().project(scope.camera); - var screenNormal = screenClickPos.clone().sub(screenOrigin).normalize(); - var screenTangent = new THREE.Vector3(screenNormal.y, screenNormal.x, 0); - - var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); - var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); - - var plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, scope.sceneRoot.position); - var camOrigin = scope.camera.position; - var camDirection = new THREE.Vector3( 0, 0, -1 ).applyQuaternion( scope.camera.quaternion ); - var direction = new THREE.Vector3( mouseEnd.x, mouseEnd.y, 0.5 ).unproject(scope.camera).sub( scope.camera.position ).normalize(); - var ray = new THREE.Ray( camOrigin, direction); - var I = ray.intersectPlane(plane); + for(var i = 0; i < pointClouds.length; i++){ + var pointcloud = pointClouds[i]; + var point = pointcloud.pick(scope.renderer, scope.camera, ray); - if(!I){ - return; + if(!point){ + continue; } - sceneTargetNormal = I.clone().sub(sceneOrigin).normalize(); - - var angleToClick; - var angleToTarget; + var distance = scope.camera.position.distanceTo(point.position); - if(partID === scope.parts.ROTATE_X){ - angleToClick = 2 * Math.PI + Math.atan2(sceneNormal.y, -sceneNormal.z); - angleToTarget = 4 * Math.PI + Math.atan2(sceneTargetNormal.y, -sceneTargetNormal.z); - }else if(partID === scope.parts.ROTATE_Y){ - angleToClick = 2 * Math.PI + Math.atan2(-sceneNormal.z, sceneNormal.x); - angleToTarget = 4 * Math.PI + Math.atan2(-sceneTargetNormal.z, sceneTargetNormal.x); - }else if(partID === scope.parts.ROTATE_Z){ - angleToClick = 2 * Math.PI + Math.atan2(sceneNormal.x, sceneNormal.y); - angleToTarget = 4 * Math.PI + Math.atan2(sceneTargetNormal.x, sceneTargetNormal.y); + if(!closestPoint || distance < closestPointDistance){ + closestPoint = point; + closestPointDistance = distance; } + } + + return closestPoint ? closestPoint.position : null; + } + + this.update = function(delta){ + + if(state === STATE.INSERT_VOLUME){ + var I = getMousePointCloudIntersection(); - var diff = angleToTarget - angleToClick; - - for(var i = 0; i < scope.targets.length; i++){ - var target = scope.targets[i]; - var startRotation = scope.dragstart.rotations[i]; + if(I){ + this.activeVolume.position.copy(I); - target.rotation.copy(startRotation); - - var q = new THREE.Quaternion(); - - q.setFromAxisAngle( normal, diff ); // axis must be normalized, angle in radians - target.quaternion.multiplyQuaternions( q, target.quaternion ); - + var wp = this.activeVolume.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); + var pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(this.camera.projectionMatrix); + var w = Math.abs((wp.z / 10)); + //this.activeVolume.setDimension(w, w, w); + this.activeVolume.scale.set(w,w,w); } - - - - - }; - - var dropEvent = function(event){ - - }; - - ring.addEventListener("mousemove", moveEvent); - ring.addEventListener("mouseleave", leaveEvent); - ring.addEventListener("mousedrag", dragEvent); - ring.addEventListener("drop", dropEvent); - - return ring; - }; - - this.createScaleHandle = function(partID, color){ - var boxGeometry = new THREE.BoxGeometry(1, 1, 1); - var material = new THREE.MeshBasicMaterial({color: color, depthTest: false, depthWrite: false}); - - var box = new THREE.Mesh(boxGeometry, material); - box.scale.set(0.3, 0.3, 0.3); - box.position.set(0, 3, 0); + } - var shaftGeometry = new THREE.Geometry(); - shaftGeometry.vertices.push(new THREE.Vector3(0, 0, 0)); - shaftGeometry.vertices.push(new THREE.Vector3(0, 3, 0)); - var shaftMaterial = new THREE.LineBasicMaterial({color: color, depthTest: false, depthWrite: false}); - var shaft = new THREE.Line(shaftGeometry, shaftMaterial); + var volumes = []; + for(var i = 0; i < this.volumes.length; i++){ + volumes.push(this.volumes[i]); + } + if(this.activeVolume){ + volumes.push(this.activeVolume); + } - var handle = new THREE.Object3D(); - handle.add(box); - handle.add(shaft); + for(var i = 0; i < volumes.length; i++){ + var volume = volumes[i]; + var box = volume.box; + var label = volume.label; + + var capacity = volume.volume(); + var msg = Potree.utils.addCommas(capacity.toFixed(1)) + "³"; + label.setText(msg); + + var distance = scope.camera.position.distanceTo(label.getWorldPosition()); + var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, scope.renderer.domElement.clientHeight); + var scale = (70 / pr); + label.scale.set(scale, scale, scale); + } - handle.partID = partID; + }; + + this.startInsertion = function(args){ + state = STATE.INSERT_VOLUME; + var args = args || {}; + var clip = args.clip || false; - var moveEvent = function(event){ - shaftMaterial.color.setRGB(1, 1, 0); - material.color.setRGB(1, 1, 0); - }; + this.activeVolume = new Potree.Volume(); + this.activeVolume.clip = clip; + this.sceneVolume.add(this.activeVolume); + this.volumes.push(this.activeVolume); + }; + + this.finishInsertion = function(){ + scope.transformationTool.setTargets([this.activeVolume]); - var leaveEvent = function(event){ - shaftMaterial.color.setHex(color); - material.color.setHex(color); + var event = { + type: "insertion_finished", + volume: this.activeVolume }; + this.dispatchEvent(event); - var dragEvent = function(event){ + this.activeVolume = null; + state = STATE.DEFAULT; + }; + + this.addVolume = function(volume){ + this.sceneVolume.add(volume); + this.volumes.push(volume); + }; + + this.removeVolume = function(volume){ + this.sceneVolume.remove(volume); + var index = this.volumes.indexOf(volume); + if(index >= 0){ + this.volumes.splice(index, 1); + } + }; + + this.reset = function(){ + for(var i = this.volumes.length - 1; i >= 0; i--){ + var volume = this.volumes[i]; + this.removeVolume(volume); + } + }; + + + this.render = function(target){ - var sceneDirection = new THREE.Vector3(); - if(partID === scope.parts.SCALE_X){ - sceneDirection.x = 1; - }else if(partID === scope.parts.SCALE_Y){ - sceneDirection.y = 1; - }else if(partID === scope.parts.SCALE_Z){ - sceneDirection.z = -1; - } - - var sceneClickPos = scope.dragstart.sceneClickPos.clone(); - sceneClickPos.multiply(sceneDirection); - sceneClickPos.z *= -1; + scope.renderer.render(this.sceneVolume, this.camera, target); - var lineStart = scope.dragstart.sceneStartPos.clone().project(scope.camera); - var lineEnd = scope.dragstart.sceneStartPos.clone().add(sceneDirection).project(scope.camera); - - var origin = lineStart.clone(); - var screenDirection = lineEnd.clone().sub(lineStart); - screenDirection.normalize(); - - var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); - var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); + }; - var directionDistance = new THREE.Vector3().subVectors(mouseEnd, mouseStart).dot(screenDirection); - var pointOnLine = screenDirection.clone().multiplyScalar(directionDistance).add(origin); - - pointOnLine.unproject(scope.camera); - - var diff = scope.sceneRoot.position.clone().sub(pointOnLine); - diff.multiply(new THREE.Vector3(-1, -1, 1)); - - for(var i = 0; i < scope.targets.length; i++){ - var target = scope.targets[i]; - var startScale = scope.dragstart.scales[i]; - target.scale.copy(startScale).add(diff); - target.scale.x = Math.max(target.scale.x, 0.01); - target.scale.y = Math.max(target.scale.y, 0.01); - target.scale.z = Math.max(target.scale.z, 0.01); - } + this.domElement.addEventListener( 'click', onMouseClick, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousemove', onMouseMove, false ); + this.domElement.addEventListener( 'contextmenu', onContextMenu, false ); +}; - event.event.stopImmediatePropagation(); +Potree.VolumeTool.prototype = Object.create( THREE.EventDispatcher.prototype ); - }; - - var dropEvent = function(event){ - material.color.set(color); - }; - - box.addEventListener("mousemove", moveEvent); - box.addEventListener("mouseleave", leaveEvent); - box.addEventListener("mousedrag", dragEvent); - box.addEventListener("drop", dropEvent); - shaft.addEventListener("mousemove", moveEvent); - shaft.addEventListener("mouseleave", leaveEvent); - shaft.addEventListener("mousedrag", dragEvent); - shaft.addEventListener("drop", dropEvent); - - return handle; +Potree.PointCloudArena4DNode = function(){ + this.left = null; + this.right = null; + this.sceneNode = null; + this.kdtree = null; + + this.getNumPoints = function(){ + return this.geometryNode.numPoints; }; - this.createArrow = function(partID, color){ - var material = new THREE.MeshBasicMaterial({color: color, depthTest: false, depthWrite: false}); - - //var shaftGeometry = new THREE.CylinderGeometry(0.05, 0.05, 3, 10, 1, false); - //var shaftMaterial = material; - //var shaft = new THREE.Mesh(shaftGeometry, shaftMaterial); - //shaft.position.y = 1.5; + this.isLoaded = function(){ + return true; + }; + + this.isTreeNode = function(){ + return true; + }; + + this.isGeometryNode = function(){ + return false; + }; + + this.getLevel = function(){ + return this.geometryNode.level; + }; + + this.getBoundingSphere = function(){ + return this.geometryNode.boundingSphere; + }; + + this.getBoundingBox = function(){ + return this.geometryNode.boundingBox; + }; + + this.toTreeNode = function(child){ + var geometryNode = null; - var shaftGeometry = new THREE.Geometry(); - shaftGeometry.vertices.push(new THREE.Vector3(0, 0, 0)); - shaftGeometry.vertices.push(new THREE.Vector3(0, 3, 0)); - var shaftMaterial = new THREE.LineBasicMaterial({color: color, depthTest: false, depthWrite: false}); - var shaft = new THREE.Line(shaftGeometry, shaftMaterial); + if(this.left === child){ + geometryNode = this.left; + }else if(this.right === child){ + geometryNode = this.right; + } + if(!geometryNode.loaded){ + return; + } + var node = new Potree.PointCloudArena4DNode(); + var sceneNode = THREE.PointCloud(geometryNode.geometry, this.kdtree.material); + sceneNode.visible = false; - var headGeometry = new THREE.CylinderGeometry(0, 0.2, 0.5, 10, 1, false); - var headMaterial = material; - var head = new THREE.Mesh(headGeometry, headMaterial); - head.position.y = 3; + node.kdtree = this.kdtree; + node.geometryNode = geometryNode; + node.sceneNode = sceneNode; + node.parent = this; + node.left = this.geometryNode.left; + node.right = this.geometryNode.right; + }; + + this.getChildren = function(){ + var children = []; - var arrow = new THREE.Object3D(); - arrow.add(shaft); - arrow.add(head); - arrow.partID = partID; - arrow.material = material; + if(this.left){ + children.push(this.left); + } - var moveEvent = function(event){ - headMaterial.color.setRGB(1, 1, 0); - shaftMaterial.color.setRGB(1, 1, 0); - }; + if(this.right){ + children.push(this.right); + } - var leaveEvent = function(event){ - headMaterial.color.set(color); - shaftMaterial.color.set(color); - }; + return children; + }; +}; + +Potree.PointCloudOctreeNode.prototype = Object.create(Potree.PointCloudTreeNode.prototype); + + + +Potree.PointCloudArena4D = function(geometry){ + THREE.Object3D.call( this ); + + var scope = this; + + this.root = null; + if(geometry.root){ + this.root = geometry.root; + }else{ + geometry.addEventListener("hierarchy_loaded", function(){ + scope.root = geometry.root; + }); + } + + this.visiblePointsTarget = 2*1000*1000; + this.minimumNodePixelSize = 150; + + this.position.sub(geometry.offset); + this.updateMatrix(); + + this.numVisibleNodes = 0; + this.numVisiblePoints = 0; + + this.boundingBoxNodes = []; + this.loadQueue = []; + this.visibleNodes = []; + + this.pcoGeometry = geometry; + this.boundingBox = this.pcoGeometry.boundingBox; + this.boundingSphere = this.pcoGeometry.boundingSphere; + this.material = new Potree.PointCloudMaterial({vertexColors: THREE.VertexColors, size: 0.05, treeType: Potree.TreeType.KDTREE}); + this.material.sizeType = Potree.PointSizeType.ATTENUATED; + this.material.size = 0.05; + this.profileRequests = []; + + this.pickTarget; + this.pickMaterial; + + this.updateMatrixWorld(); +}; + +Potree.PointCloudArena4D.prototype = new Potree.PointCloudTree(); + +Potree.PointCloudArena4D.prototype.toTreeNode = function(geometryNode, parent){ + var node = new Potree.PointCloudArena4DNode(); + var sceneNode = new THREE.PointCloud(geometryNode.geometry, pointcloud.material); + + node.geometryNode = geometryNode; + node.sceneNode = sceneNode; + node.pointcloud = pointcloud; + node.left = geometryNode.left; + node.right = geometryNode.right; + + if(!parent){ + this.root = node; + this.add(sceneNode); + }else{ + parent.sceneNode.add(sceneNode); - var dragEvent = function(event){ + if(parent.left === geometryNode){ + parent.left = node; + }else if(parent.right === geometryNode){ + parent.right = node; + } + } + + var disposeListener = function(){ + parent.sceneNode.remove(node.sceneNode); - var sceneDirection = new THREE.Vector3(); - if(partID === scope.parts.ARROW_X){ - sceneDirection.x = 1; - }else if(partID === scope.parts.ARROW_Y){ - sceneDirection.y = 1; - }else if(partID === scope.parts.ARROW_Z){ - sceneDirection.z = -1; - } + if(parent.left === node){ + parent.left = geometryNode; + }else if(parent.right === node){ + parent.right = geometryNode; + } + } + geometryNode.oneTimeDisposeHandlers.push(disposeListener); + + return node; +}; + +Potree.PointCloudArena4D.prototype.updateMaterial = function(material, visibleNodes, camera, renderer){ + material.fov = camera.fov * (Math.PI / 180); + material.screenWidth = renderer.domElement.clientWidth; + material.screenHeight = renderer.domElement.clientHeight; + material.spacing = this.pcoGeometry.spacing; + material.near = camera.near; + material.far = camera.far; + + // reduce shader source updates by setting maxLevel slightly higher than actually necessary + if(this.maxLevel > material.levels){ + material.levels = this.maxLevel + 2; + } + + material.minSize = 3; + + //material.uniforms.octreeSize.value = this.boundingBox.size().x; + var bbSize = this.boundingBox.size(); + material.bbSize = [bbSize.x, bbSize.y, bbSize.z]; + + // update visibility texture + if(material.pointSizeType){ + if(material.pointSizeType === Potree.PointSizeType.ADAPTIVE + || material.pointColorType === Potree.PointColorType.OCTREE_DEPTH){ - var sceneClickPos = scope.dragstart.sceneClickPos.clone(); - sceneClickPos.multiply(sceneDirection); - sceneClickPos.z *= -1; + this.updateVisibilityTexture(material, visibleNodes); + } + } +}; + +Potree.PointCloudArena4D.prototype.updateVisibleBounds = function(){ + +}; + +Potree.PointCloudArena4D.prototype.hideDescendants = function(object){ + var stack = []; + for(var i = 0; i < object.children.length; i++){ + var child = object.children[i]; + if(child.visible){ + stack.push(child); + } + } + + while(stack.length > 0){ + var object = stack.shift(); - //var lineStart = new THREE.Vector3(); - //lineStart.x = scope.dragstart.mousePos.x; - //lineStart.y = scope.dragstart.mousePos.y; - var lineStart = scope.dragstart.sceneStartPos.clone().project(scope.camera); - var lineEnd = scope.dragstart.sceneStartPos.clone().add(sceneDirection).project(scope.camera); - - var origin = lineStart.clone(); - var screenDirection = lineEnd.clone().sub(lineStart); - screenDirection.normalize(); - - - - var mouseStart = new THREE.Vector3(scope.dragstart.mousePos.x, scope.dragstart.mousePos.y, 0); - var mouseEnd = new THREE.Vector3(scope.mouse.x, scope.mouse.y, 0); - - //var htmlStart = mouseStart.clone().addScalar(1).multiplyScalar(0.5); - //htmlStart.x *= scope.domElement.clientWidth; - //htmlStart.y *= scope.domElement.clientHeight; - // - //var htmlEnd = mouseEnd.clone().addScalar(1).multiplyScalar(0.5); - //htmlEnd.x *= scope.domElement.clientWidth; - //htmlEnd.y *= scope.domElement.clientHeight; - // - //var el = document.getElementById("testDiv"); - //el.style.left = htmlStart.x; - //el.style.width = htmlEnd.x - htmlStart.x; - //el.style.bottom = htmlStart.y; - //el.style.top = scope.domElement.clientHeight - htmlEnd.y; - - - - - //var directionDistance = new THREE.Vector3().subVectors(mouseEnd, origin).dot(screenDirection); - var directionDistance = new THREE.Vector3().subVectors(mouseEnd, mouseStart).dot(screenDirection); - var pointOnLine = screenDirection.clone().multiplyScalar(directionDistance).add(origin); - - pointOnLine.unproject(scope.camera); - - var diff = scope.sceneRoot.position.clone(); - //scope.position.copy(pointOnLine); - var offset = sceneClickPos.clone().sub(scope.dragstart.sceneStartPos); - scope.sceneRoot.position.copy(pointOnLine); - //scope.sceneRoot.position.sub(offset); - diff.sub(scope.sceneRoot.position); - - for(var i = 0; i < scope.targets.length; i++){ - var target = scope.targets[i]; - target.position.sub(diff); + object.visible = false; + if(object.boundingBoxNode){ + object.boundingBoxNode.visible = false; + } + + for(var i = 0; i < object.children.length; i++){ + var child = object.children[i]; + if(child.visible){ + stack.push(child); } - - //if(!sph1){ - // var g = new THREE.SphereGeometry(0.2); - // - // var m1 = new THREE.MeshBasicMaterial({color: 0xff0000}); - // var m2 = new THREE.MeshBasicMaterial({color: 0x00ff00}); - // var m3 = new THREE.MeshBasicMaterial({color: 0x0000ff}); - // - // sph1 = new THREE.Mesh(g, m1); - // sph2 = new THREE.Mesh(g, m2); - // sph3 = new THREE.Mesh(g, m3); - // - // scope.scene.add(sph1); - // scope.scene.add(sph2); - // scope.scene.add(sph3); - //} - //sph1.position.copy(scope.dragstart.sceneStartPos); - //sph2.position.copy(scope.dragstart.sceneClickPos); - //sph3.position.copy(pointOnLine); + } + } +}; - event.event.stopImmediatePropagation(); +Potree.PointCloudArena4D.prototype.updateMatrixWorld = function( force ){ + //node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); - }; - - var dropEvent = function(event){ - shaftMaterial.color.set(color); - }; + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === undefined ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } +}; + +Potree.PointCloudArena4D.prototype.nodesOnRay = function(nodes, ray){ + var nodesOnRay = []; + + var _ray = ray.clone(); + for(var i = 0; i < nodes.length; i++){ + var node = nodes[i]; + var sphere = node.getBoundingSphere().clone().applyMatrix4(node.sceneNode.matrixWorld); + var box = node.getBoundingBox().clone().applyMatrix4(node.sceneNode.matrixWorld); - shaft.addEventListener("mousemove", moveEvent); - head.addEventListener("mousemove", moveEvent); - shaft.addEventListener("mouseleave", leaveEvent); - head.addEventListener("mouseleave", leaveEvent); + if(_ray.isIntersectionBox(box)){ + nodesOnRay.push(node); + } + } + + return nodesOnRay; +}; + +Potree.PointCloudArena4D.prototype.pick = function(renderer, camera, ray, params){ + + var params = params || {}; + var pickWindowSize = params.pickWindowSize || 17; + var pickOutsideClipRegion = params.pickOutsideClipRegion || false; + + var nodes = this.nodesOnRay(this.visibleNodes, ray); + + if(nodes.length === 0){ + return null; + } + + var width = Math.ceil(renderer.domElement.clientWidth); + var height = Math.ceil(renderer.domElement.clientHeight); + + var pixelPos = new THREE.Vector3().addVectors(camera.position, ray.direction).project(camera); + pixelPos.addScalar(1).multiplyScalar(0.5); + pixelPos.x *= width; + pixelPos.y *= height; + + if(!this.pickTarget){ + this.pickTarget = new THREE.WebGLRenderTarget( + 1, 1, + { minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat } + ); + }else if(this.pickTarget.width != width || this.pickTarget.height != height){ + this.pickTarget.dispose(); + this.pickTarget = new THREE.WebGLRenderTarget( + 1, 1, + { minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat } + ); + } + this.pickTarget.setSize(width, height); + + // setup pick material. + // use the same point size functions as the main material to get the same point sizes. + if(!this.pickMaterial){ + this.pickMaterial = new Potree.PointCloudMaterial({treeType: Potree.TreeType.KDTREE}); + this.pickMaterial.pointColorType = Potree.PointColorType.POINT_INDEX; + this.pickMaterial.pointSizeType = Potree.PointSizeType.FIXED; + } + + this.pickMaterial.pointSizeType = this.material.pointSizeType; + this.pickMaterial.size = this.material.size; + + if(this.pickMaterial.pointSizeType === Potree.PointSizeType.ADAPTIVE){ + this.updateVisibilityTexture(this.pickMaterial, nodes); + } + + this.pickMaterial.fov = this.material.fov; + this.pickMaterial.screenWidth = this.material.screenWidth; + this.pickMaterial.screenHeight = this.material.screenHeight; + this.pickMaterial.spacing = this.material.spacing; + this.pickMaterial.near = this.material.near; + this.pickMaterial.far = this.material.far; + this.pickMaterial.levels = this.material.levels; + this.pickMaterial.pointShape = this.material.pointShape; + + if(pickOutsideClipRegion){ + this.pickMaterial.clipMode = Potree.ClipMode.DISABLED; + }else{ + this.pickMaterial.clipMode = this.material.clipMode; + if(this.material.clipMode === Potree.ClipMode.CLIP_OUTSIDE){ + this.pickMaterial.setClipBoxes(this.material.clipBoxes); + }else{ + this.pickMaterial.setClipBoxes([]); + } + } + + + var _gl = renderer.context; + + _gl.enable(_gl.SCISSOR_TEST); + _gl.scissor(pixelPos.x - (pickWindowSize - 1) / 2, pixelPos.y - (pickWindowSize - 1) / 2,pickWindowSize,pickWindowSize); + _gl.disable(_gl.SCISSOR_TEST); + + var material = this.pickMaterial; + + renderer.setRenderTarget( this.pickTarget ); + + renderer.state.setDepthTest( material.depthTest ); + renderer.state.setDepthWrite( material.depthWrite ); + renderer.state.setBlending( THREE.NoBlending ); + + renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + + //TODO: UGLY HACK CHAMPIONSHIP SUBMISSION!! drawing first node does not work properly so we draw it twice. + if(nodes.length > 0){ + nodes.push(nodes[0]); + } + + for(var i = 0; i < nodes.length; i++){ + var object = nodes[i].sceneNode; + var geometry = object.geometry; - shaft.addEventListener("mousedrag", dragEvent); - head.addEventListener("mousedrag", dragEvent); + if(!geometry.attributes.indices.buffer){ + continue; + } - shaft.addEventListener("drop", dropEvent); - head.addEventListener("drop", dropEvent); + material.pcIndex = i; + if(material.program){ + var program = material.program.program; + _gl.useProgram( program ); + //_gl.disable( _gl.BLEND ); + + var attributePointer = _gl.getAttribLocation(program, "indices"); + var attributeSize = 4; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.attributes.indices.buffer ); + //if(!bufferSubmitted){ + // _gl.bufferData( _gl.ARRAY_BUFFER, new Uint8Array(geometry.attributes.indices.array), _gl.STATIC_DRAW ); + // bufferSubmitted = true; + //} + _gl.enableVertexAttribArray( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.UNSIGNED_BYTE, true, 0, 0 ); + _gl.uniform1f(material.program.uniforms.pcIndex, material.pcIndex); + } - return arrow; - }; + renderer.renderBufferDirect(camera, [], null, material, geometry, object); + } - function onMouseMove(event){ - scope.mouse.x = ( event.clientX / scope.domElement.clientWidth ) * 2 - 1; - scope.mouse.y = - ( event.clientY / scope.domElement.clientHeight ) * 2 + 1; - if(scope.dragstart){ + var pickWindowSize = 17; + var pixelCount = pickWindowSize * pickWindowSize; + var buffer = new ArrayBuffer(pixelCount*4); + var pixels = new Uint8Array(buffer); + var ibuffer = new Uint32Array(buffer); + renderer.context.readPixels( + pixelPos.x - (pickWindowSize-1) / 2, pixelPos.y - (pickWindowSize-1) / 2, + pickWindowSize, pickWindowSize, + renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, pixels); + + // find closest hit inside pixelWindow boundaries + var min = Number.MAX_VALUE; + var hit = null; + //console.log("finding closest hit"); + for(var u = 0; u < pickWindowSize; u++){ + for(var v = 0; v < pickWindowSize; v++){ + var offset = (u + v*pickWindowSize); + var distance = Math.pow(u - (pickWindowSize-1) / 2, 2) + Math.pow(v - (pickWindowSize-1) / 2, 2); - scope.dragstart.object.dispatchEvent({ - type: "mousedrag", - event: event - }); + var pcIndex = pixels[4*offset + 3]; + pixels[4*offset + 3] = 0; + var pIndex = ibuffer[offset]; - }else{ - - - var I = getHoveredElement(); - if(I){ - var object = I.object; - - //var g = new THREE.SphereGeometry(2); - //var m = new THREE.Mesh(g); - //scope.scene.add(m); - //m.position.copy(I.point); - - object.dispatchEvent({type: "mousemove", event: event}); + if((pIndex !== 0 || pcIndex !== 0) && distance < min){ - if(scope.hoveredElement && scope.hoveredElement !== object){ - scope.hoveredElement.dispatchEvent({type: "mouseleave", event: event}); - } + hit = { + pIndex: pIndex, + pcIndex: pcIndex + }; + min = distance; + } + } + } + + if(hit){ + var point = {}; + + var pc = nodes[hit.pcIndex]; + var geometry = pc.sceneNode.geometry; + var attributes = geometry.attributes; + + for (var property in attributes) { + if (attributes.hasOwnProperty(property)) { + var values = geometry.attributes[property]; + + if(property === "position"){ + var positionArray = geometry.attributes.position.array; + var x = positionArray[3*hit.pIndex+0]; + var y = positionArray[3*hit.pIndex+1]; + var z = positionArray[3*hit.pIndex+2]; + var position = new THREE.Vector3(x, y, z); + position.applyMatrix4(this.matrixWorld); - scope.hoveredElement = object; + point[property] = position; + }else if(property === "indices"){ - }else{ - if(scope.hoveredElement){ - scope.hoveredElement.dispatchEvent({type: "mouseleave", event: event}); + }else{ + if(values.itemSize === 1){ + point[property] = values.array[i + j]; + }else{ + var value = []; + for(var j = 0; j < values.itemSize; j++){ + value.push(values.array[i*values.itemSize + j]); + } + point[property] = value; + } } - - scope.hoveredElement = null; } - } - }; + return point; + }else{ + return null; + } +}; + + +Potree.PointCloudArena4D.prototype.updateVisibilityTexture = function(material, visibleNodes){ + + if(!material){ + return; + } - function onMouseDown(event){ + var texture = material.visibleNodesTexture; + var data = texture.image.data; + // copy array + visibleNodes = visibleNodes.slice(); - if(event.which === 1){ - // left click - var I = getHoveredElement(); - if(I){ - - var scales = []; - var rotations = []; - for(var i = 0; i < scope.targets.length; i++){ - scales.push(scope.targets[i].scale.clone()); - rotations.push(scope.targets[i].rotation.clone()); - } - - scope.dragstart = { - object: I.object, - sceneClickPos: I.point, - sceneStartPos: scope.sceneRoot.position.clone(), - mousePos: {x: scope.mouse.x, y: scope.mouse.y}, - scales: scales, - rotations: rotations - }; - event.stopImmediatePropagation(); - } - }else if(event.which === 3){ - // right click - - scope.setTargets([]); - } + // sort by level and number + var sort = function(a, b){ + var la = a.geometryNode.level; + var lb = b.geometryNode.level; + var na = a.geometryNode.number; + var nb = b.geometryNode.number; + if(la != lb) return la - lb; + if(na < nb) return -1; + if(na > nb) return 1; + return 0; }; + visibleNodes.sort(sort); - function onMouseUp(event){ + var visibleNodeNames = []; + for(var i = 0; i < visibleNodes.length; i++){ + //visibleNodeNames[visibleNodes[i].pcoGeometry.number] = true; + visibleNodeNames.push(visibleNodes[i].geometryNode.number); + } - if(scope.dragstart){ - scope.dragstart.object.dispatchEvent({type: "drop", event: event}); - scope.dragstart = null; + for(var i = 0; i < visibleNodes.length; i++){ + var node = visibleNodes[i]; + + var b1 = 0; // children + var b2 = 0; // offset to first child + var b3 = 0; // split + + if(node.geometryNode.left && visibleNodeNames.indexOf(node.geometryNode.left.number) > 0){ + b1 += 1; + b2 = visibleNodeNames.indexOf(node.geometryNode.left.number) - i; } - }; + if(node.geometryNode.right && visibleNodeNames.indexOf(node.geometryNode.right.number) > 0){ + b1 += 2; + b2 = (b2 === 0) ? visibleNodeNames.indexOf(node.geometryNode.right.number) - i : b2; + } + + if(node.geometryNode.split === "X"){ + b3 = 1; + }else if(node.geometryNode.split === "Y"){ + b3 = 2; + }else if(node.geometryNode.split === "Z"){ + b3 = 4; + } + + + data[i*3+0] = b1; + data[i*3+1] = b2; + data[i*3+2] = b3; + } + + + texture.needsUpdate = true; +}; + + + +Object.defineProperty(Potree.PointCloudArena4D.prototype, "progress", { + get: function(){ + if(this.pcoGeometry.root){ + return Potree.PointCloudArena4DGeometryNode.nodesLoading > 0 ? 0 : 1; + }else{ + return 0; + } + } +}); + + + +Potree.PointCloudArena4DGeometryNode = function(){ + var scope = this; + + this.left = null; + this.right = null; + this.boundingBox = null; + this.number = null; + this.pcoGeometry = null; + this.loaded = false; + this.numPoints = 0; + this.level = 0; + this.children = []; + this.oneTimeDisposeHandlers = []; +}; + +Potree.PointCloudArena4DGeometryNode.nodesLoading = 0; + +Potree.PointCloudArena4DGeometryNode.prototype.isGeometryNode = function(){ + return true; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.isTreeNode = function(){ + return false; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.isLoaded = function(){ + return this.loaded; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.getBoundingSphere = function(){ + return this.boundingSphere; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.getBoundingBox = function(){ + return this.boundingBox; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.getChildren = function(){ + var children = []; + + if(this.left){ + children.push(this.left); + } + + if(this.right){ + children.push(this.right); + } + + return children; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.getBoundingBox = function(){ + return this.boundingBox; +}; + +Potree.PointCloudArena4DGeometryNode.prototype.load = function(){ + + if(this.loaded || this.loading){ + return; + } + + if(Potree.PointCloudArena4DGeometryNode.nodesLoading >= 5){ + return; + } + + this.loading = true; + + Potree.PointCloudArena4DGeometryNode.nodesLoading++; + + var url = this.pcoGeometry.url + "?node=" + this.number; + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; - function getHoveredElement(){ + var scope = this; - if(scope.targets.length === 0){ + xhr.onreadystatechange = function(){ + if(!(xhr.readyState === 4 && xhr.status === 200)){ return; } - - var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); - vector.unproject(scope.camera); - var raycaster = new THREE.Raycaster(); - raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); - raycaster.linePrecision = 0.2; + var buffer = xhr.response; + var view = new DataView(buffer); + var numPoints = buffer.byteLength / 17; - var objects = []; - if(scope.translationNode.visible){ - objects.push(scope.translationNode); - }else if(scope.scaleNode.visible){ - objects.push(scope.scaleNode); - }else if(scope.rotationNode.visible){ - objects.push(scope.rotationNode); - objects.push(scope.sceneRotation); + var positions = new Float32Array(numPoints*3); + var colors = new Float32Array(numPoints*3); + var indices = new Uint32Array(numPoints); + + for(var i = 0; i < numPoints; i++){ + var x = view.getFloat32(i*17 + 0, true) + scope.boundingBox.min.x; + var y = view.getFloat32(i*17 + 4, true) + scope.boundingBox.min.y; + var z = view.getFloat32(i*17 + 8, true) + scope.boundingBox.min.z; + var r = view.getUint8(i*17 + 12, true) / 256; + var g = view.getUint8(i*17 + 13, true) / 256; + var b = view.getUint8(i*17 + 14, true) / 256; + + positions[i*3+0] = x; + positions[i*3+1] = y; + positions[i*3+2] = z; + + colors[i*3+0] = r; + colors[i*3+1] = g; + colors[i*3+2] = b; + + indices[i] = i; } - var intersections = raycaster.intersectObjects(objects, true); + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3)); + geometry.addAttribute("color", new THREE.BufferAttribute(colors, 3)); + geometry.addAttribute("indices", new THREE.BufferAttribute(indices, 1)); + geometry.addAttribute("normal", new THREE.BufferAttribute(new Float32Array(numPoints*3), 3)); - // recalculate distances because they are not necessarely correct - // for scaled objects. - // see https://github.com/mrdoob/three.js/issues/5827 - // TODO: remove this once the bug has been fixed - for(var i = 0; i < intersections.length; i++){ - var I = intersections[i]; - I.distance = scope.camera.position.distanceTo(I.point); - } - intersections.sort( function ( a, b ) { return a.distance - b.distance;} ); + scope.geometry = geometry; + scope.loaded = true; + Potree.PointCloudArena4DGeometryNode.nodesLoading--; - if(intersections.length > 0){ - return intersections[0]; - }else{ - return false; - } + geometry.boundingBox = scope.boundingBox; + geometry.boundingSphere = scope.boundingSphere; + + scope.numPoints = numPoints; + scope.loading = false; }; - this.setTargets = function(targets){ - scope.targets = targets; + xhr.send(null); +}; + +Potree.PointCloudArena4DGeometryNode.prototype.dispose = function(){ + if(this.geometry && this.parent != null){ + this.geometry.dispose(); + this.geometry = null; + this.loaded = false; - if(scope.targets.length === 0){ - this.sceneRoot.visible = false; - this.sceneRotation.visible = false; + //this.dispatchEvent( { type: 'dispose' } ); + for(var i = 0; i < this.oneTimeDisposeHandlers.length; i++){ + var handler = this.oneTimeDisposeHandlers[i]; + handler(); + } + this.oneTimeDisposeHandlers = []; + } +}; + +Potree.PointCloudArena4DGeometryNode.prototype.getNumPoints = function(){ + return this.numPoints; +}; + + +Potree.PointCloudArena4DGeometry = function(){ + var scope = this; + + this.numPoints = 0; + this.version = 0; + this.boundingBox = null; + this.numNodes = 0; + this.name = null; + this.provider = null; + this.url = null; + this.root = null; + this.levels = 0; + this._spacing = null; + this.pointAttributes = new Potree.PointAttributes([ + "POSITION_CARTESIAN", + "COLOR_PACKED" + ]); +}; + +Potree.PointCloudArena4DGeometry.prototype = Object.create( THREE.EventDispatcher.prototype ); + +Potree.PointCloudArena4DGeometry.load = function(url, callback){ + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url + "?info", true); + + xhr.onreadystatechange = function(){ + try{ + if(xhr.readyState === 4 && xhr.status === 200){ + var response = JSON.parse(xhr.responseText); + + var geometry = new Potree.PointCloudArena4DGeometry(); + geometry.url = url; + geometry.name = response.Name; + geometry.provider = response.Provider; + geometry.numNodes = response.Nodes; + geometry.numPoints = response.Points; + geometry.version = response.Version; + geometry.boundingBox = new THREE.Box3( + new THREE.Vector3().fromArray(response.BoundingBox.slice(0,3)), + new THREE.Vector3().fromArray(response.BoundingBox.slice(3,6)) + ); + if(response.Spacing){ + geometry.spacing = response.Spacing; + } + + var offset = geometry.boundingBox.min.clone().multiplyScalar(-1); + + geometry.boundingBox.min.add(offset); + geometry.boundingBox.max.add(offset); + geometry.offset = offset; + + var center = geometry.boundingBox.center(); + var radius = geometry.boundingBox.size().length() / 2; + geometry.boundingSphere = new THREE.Sphere(center, radius); + + geometry.loadHierarchy(); + + callback(geometry); + }else if(xhr.readyState === 4){ + callback(null); + } + }catch(e){ + console.error(e.message); + callback(null); + } + }; + xhr.send(null); + +}; + +Potree.PointCloudArena4DGeometry.prototype.loadHierarchy = function(){ + var url = this.url + "?tree"; + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + + var scope = this; + + xhr.onreadystatechange = function(){ + if(!(xhr.readyState === 4 && xhr.status === 200)){ return; - }else{ - this.sceneRoot.visible = true; } + + var buffer = xhr.response; + var numNodes = buffer.byteLength / 3; + var view = new DataView(buffer); + var stack = []; + var root = null; - //TODO calculate centroid of all targets - var target = targets[0]; - var bb; - if(target.geometry && target.geometry.boundingBox){ - bb = target.geometry.boundingBox; - }else{ - bb = target.boundingBox; - } + var levels = 0; - if(bb){ - var centroid = bb.clone().applyMatrix4(target.matrixWorld).center(); - scope.sceneRoot.position.copy(centroid); + var start = new Date().getTime(); + // read hierarchy + for(var i = 0; i < numNodes; i++){ + var mask = view.getUint8(i*3+0, true); + var numPoints = view.getUint16(i*3+1, true); + + + var hasLeft = (mask & 1) > 0; + var hasRight = (mask & 2) > 0; + var splitX = (mask & 4) > 0; + var splitY = (mask & 8) > 0; + var splitZ = (mask & 16) > 0; + var split = null; + if(splitX){ + split = "X"; + }else if(splitY){ + split = "Y"; + }if(splitZ){ + split = "Z"; + } + + + var node = new Potree.PointCloudArena4DGeometryNode(); + node.hasLeft = hasLeft; + node.hasRight = hasRight; + node.split = split; + node.isLeaf = !hasLeft && !hasRight; + node.number = i; + node.left = null; + node.right = null; + node.pcoGeometry = scope; + node.level = stack.length; + levels = Math.max(levels, node.level); + + if(stack.length > 0){ + var parent = stack[stack.length-1]; + node.boundingBox = parent.boundingBox.clone(); + var parentBBSize = parent.boundingBox.size(); + + if(parent.hasLeft && !parent.left){ + parent.left = node; + parent.children.push(node); + + if(parent.split === "X"){ + node.boundingBox.max.x = node.boundingBox.min.x + parentBBSize.x / 2; + }else if(parent.split === "Y"){ + node.boundingBox.max.y = node.boundingBox.min.y + parentBBSize.y / 2; + }else if(parent.split === "Z"){ + node.boundingBox.max.z = node.boundingBox.min.z + parentBBSize.z / 2; + } + + var center = node.boundingBox.center(); + var radius = node.boundingBox.size().length() / 2; + node.boundingSphere = new THREE.Sphere(center, radius); + + }else{ + parent.right = node; + parent.children.push(node); + + if(parent.split === "X"){ + node.boundingBox.min.x = node.boundingBox.min.x + parentBBSize.x / 2; + }else if(parent.split === "Y"){ + node.boundingBox.min.y = node.boundingBox.min.y + parentBBSize.y / 2; + }else if(parent.split === "Z"){ + node.boundingBox.min.z = node.boundingBox.min.z + parentBBSize.z / 2; + } + + var center = node.boundingBox.center(); + var radius = node.boundingBox.size().length() / 2; + node.boundingSphere = new THREE.Sphere(center, radius); + } + }else{ + root = node; + root.boundingBox = scope.boundingBox.clone(); + var center = root.boundingBox.center(); + var radius = root.boundingBox.size().length() / 2; + root.boundingSphere = new THREE.Sphere(center, radius); + } + + var bbSize = node.boundingBox.size(); + node.spacing = ((bbSize.x + bbSize.y + bbSize.z) / 3) / 75; + + stack.push(node); + + if(node.isLeaf){ + var done = false; + while(!done && stack.length > 0){ + stack.pop(); + + var top = stack[stack.length-1]; + + done = stack.length > 0 && top.hasRight && top.right == null; + } + } } + var end = new Date().getTime(); + var parseDuration = end - start; + var msg = parseDuration; + //document.getElementById("lblDebug").innerHTML = msg; - //for(var i = 0; i < targets.length; i++){ - // var target = targets[i]; - //} + scope.root = root; + scope.levels = levels; + //console.log(this.root); + scope.dispatchEvent({type: "hierarchy_loaded"}); }; - this.update = function(){ - var node = this.sceneRoot; - var wp = node.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); - var pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix); - var w = Math.abs((wp.z / 20)); // * (2 - pp.z / pp.w); - node.scale.set(w, w, w); - - if(this.targets && this.targets.length === 1){ - this.scaleNode.rotation.copy(this.targets[0].rotation); + xhr.send(null); + + +}; + +Object.defineProperty(Potree.PointCloudArena4DGeometry.prototype, "spacing", { + get: function(){ + if(this._spacing){ + return this._spacing; + }else if(this.root){ + return this.root.spacing; + }else{ + null; } - - this.sceneRotation.scale.set(w,w,w); - }; + }, + set: function(value){ + this._spacing = value; + } +}); + + + + + +function ProgressBar(){ + this._progress = 0; + this._message = ""; - this.render = function(){ - this.update(); - this.sceneRotation.position.copy(this.sceneRoot.position); - this.sceneRotation.visible = this.rotationNode.visible && this.sceneRoot.visible; - - renderer.render(this.sceneRotation, this.camera); - renderer.render(this.sceneTransformation, this.camera); - }; + this.maxOpacity = 0.6; - this.translate = function(){ - this.translationNode.visible = true; - this.scaleNode.visible = false; - this.rotationNode.visible = false; - }; + this.element = document.createElement("div"); + this.elProgress = document.createElement("div"); + this.elProgressMessage = document.createElement("div"); - this.scale = function(){ - this.translationNode.visible = false; - this.scaleNode.visible = true; - this.rotationNode.visible = false; - }; + //this.element.innerHTML = "element"; + //this.elProgress.innerHTML = "progress"; - this.rotate = function(){ - this.translationNode.visible = false; - this.scaleNode.visible = false; - this.rotationNode.visible = true; - }; + this.element.innerHTML = ""; + this.element.style.position = "fixed"; + this.element.style.bottom = "40px"; + this.element.style.width = "200px"; + this.element.style.marginLeft = "-100px"; + this.element.style.left = "50%"; + this.element.style.borderRadius = "5px"; + this.element.style.border = "1px solid #727678"; + this.element.style.height = "16px"; + this.element.style.padding = "1px"; + this.element.style.textAlign = "center"; + this.element.style.backgroundColor = "#6ba8e5"; + this.element.style.opacity = this.maxOpacity; + this.element.style.pointerEvents = "none"; - this.reset = function(){ - this.setTargets([]); - }; + this.elProgress.innerHTML = " "; + this.elProgress.style.backgroundColor = "#b8e1fc"; + this.elProgress.style.position = "absolute"; + this.elProgress.style.borderRadius = "5px"; + this.elProgress.style.width = "0%"; + this.elProgress.style.height = "100%"; + this.elProgress.style.margin = "0px"; + this.elProgress.style.padding = "0px"; - this.buildTranslationNode(); - this.buildScaleNode(); - this.buildRotationNode(); + this.elProgressMessage.style.position = "absolute"; + this.elProgressMessage.style.width = "100%"; + this.elProgressMessage.innerHTML = "loading 1 / 10"; - //this.translate(); - this.rotate(); - this.setTargets([]); - //this.domElement.addEventListener( 'click', onClick, false); - this.domElement.addEventListener( 'mousemove', onMouseMove, true ); - this.domElement.addEventListener( 'mousedown', onMouseDown, true ); - this.domElement.addEventListener( 'mouseup', onMouseUp, true ); + document.body.appendChild(this.element); + this.element.appendChild(this.elProgress); + this.element.appendChild(this.elProgressMessage); + + this.hide(); }; -Potree.Volume = function(args){ +ProgressBar.prototype.hide = function(){ + this.element.style.opacity = 0; + this.element.style.transition = "all 0.2s ease"; +}; - THREE.Object3D.call( this ); +ProgressBar.prototype.show = function(){ + this.element.style.opacity = this.maxOpacity; + this.element.style.transition = "all 0.2s ease"; +}; - args = args || {}; - this._clip = args.clip || false; - this._modifiable = args.modifiable || true; +Object.defineProperty(ProgressBar.prototype, "progress", { + get: function(){ + return this._progress; + }, + set: function(value){ + this._progress = value; + this.elProgress.style.width = (value * 100) + "%"; + } +}); + +Object.defineProperty(ProgressBar.prototype, "message", { + get: function(){ + return this._message; + }, + set: function(message){ + this._message = message; + this.elProgressMessage.innerHTML = message; + } +}); + +Potree.Viewer = function(domElement, args){ + var scope = this; + var arguments = args || {}; + var pointCloudLoadedCallback = arguments.onPointCloudLoaded || function(){}; - var boxGeometry = new THREE.BoxGeometry(1, 1, 1); - boxGeometry.computeBoundingBox(); + this.renderArea = domElement; - var boxFrameGeometry = new THREE.Geometry(); - // bottom - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); - // top - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); - // sides - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); - boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); - this.dimension = new THREE.Vector3(1,1,1); - var material = new THREE.MeshBasicMaterial( { - color: 0x00ff00, - transparent: true, - opacity: 0.3, - depthTest: true, - depthWrite: true} ); - this.box = new THREE.Mesh( boxGeometry, material); - this.box.geometry.computeBoundingBox(); - this.boundingBox = this.box.geometry.boundingBox; - this.add(this.box); + //if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { + // defaultSettings.navigation = "Orbit"; + //} - this.frame = new THREE.Line( boxFrameGeometry, new THREE.LineBasicMaterial({color: 0x000000})); - this.frame.mode = THREE.LinePieces; - this.add(this.frame); + this.annotations = []; + this.fov = 60; + this.pointSize = 1; + this.opacity = 1; + this.sizeType = "Fixed"; + this.pointSizeType = Potree.PointSizeType.FIXED; + this.pointColorType = null; + this.clipMode = Potree.ClipMode.HIGHLIGHT_INSIDE; + this.quality = "Squares"; + this.isFlipYZ = false; + this.useDEMCollisions = false; + this.minNodeSize = 100; + this.directionalLight; + this.edlScale = 1; + this.edlRadius = 3; + this.useEDL = false; + this.minimumJumpDistance = 0.2; + this.jumpDistance = null; + this.intensityMax = null; + this.heightMin = null; + this.heightMax = null; + this.moveSpeed = 10; + + this.showDebugInfos = false; + this.showStats = true; + this.showBoundingBox = false; + this.freeze = false; + + this.fpControls; + this.orbitControls; + this.earthControls; + this.geoControls; + this.controls; + + this.progressBar = new ProgressBar(); + + var gui; + + this.renderer; + this.camera; + this.scene; + this.scenePointCloud; + this.sceneBG; + this.cameraBG; + this.pointclouds = []; + this.measuringTool; + this.volumeTool; + this.transformationTool; + + var skybox; + var stats; + var clock = new THREE.Clock(); + this.showSkybox = false; + this.referenceFrame; + + + + + + + + + + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------ +// Viewer API +//------------------------------------------------------------------------------------ + + this.addPointCloud = function(path, callback){ + callback = callback || function(){}; + var initPointcloud = function(pointcloud){ + + if(!scope.mapView){ + if(pointcloud.projection){ + scope.mapView = new Potree.Viewer.MapView(viewer); + scope.mapView.init(viewer); + } + } + + scope.pointclouds.push(pointcloud); + + scope.referenceFrame.add(pointcloud); + + var sg = pointcloud.boundingSphere.clone().applyMatrix4(pointcloud.matrixWorld); + + scope.referenceFrame.updateMatrixWorld(true); + + if(sg.radius > 50*1000){ + scope.camera.near = 10; + }else if(sg.radius > 10*1000){ + scope.camera.near = 2; + }else if(sg.radius > 1000){ + scope.camera.near = 1; + }else if(sg.radius > 100){ + scope.camera.near = 0.5; + }else{ + scope.camera.near = 0.1; + } + + if(scope.pointclouds.length === 1){ + scope.referenceFrame.position.sub(sg.center); + scope.referenceFrame.updateMatrixWorld(true); + var moveSpeed = sg.radius / 6; + scope.setMoveSpeed(moveSpeed); + } + + //scope.flipYZ(); + + scope.zoomTo(pointcloud, 1); + + + var hr = scope.getHeightRange(); + if(hr.min === null || hr.max === null){ + var bbWorld = scope.getBoundingBox(); + + scope.setHeightRange(bbWorld.min.y, bbWorld.max.y); + } + + scope.earthControls.pointclouds.push(pointcloud); + + + if(scope.pointclouds.length === 1){ + scope.setNavigationMode("Orbit"); + scope.flipYZ(); + scope.zoomTo(pointcloud, 1); + } + + + + scope.dispatchEvent({"type": "pointcloud_loaded", "pointcloud": pointcloud}); + + callback({type: "pointclouad_loaded", pointcloud: pointcloud}); + }; + this.addEventListener("pointcloud_loaded", pointCloudLoadedCallback); + + // load pointcloud + if(!path){ + + }else if(path.indexOf("cloud.js") > 0){ + Potree.POCLoader.load(path, function(geometry){ + if(!geometry){ + callback({type: "loading_failed"}); + }else{ + pointcloud = new Potree.PointCloudOctree(geometry); + initPointcloud(pointcloud); + } + }); + }else if(path.indexOf(".vpc") > 0){ + Potree.PointCloudArena4DGeometry.load(path, function(geometry){ + if(!geometry){ + callback({type: "loading_failed"}); + }else{ + pointcloud = new Potree.PointCloudArena4D(geometry); + initPointcloud(pointcloud); + } + }); + }else{ + callback({"type": "loading_failed"}); + } + }; + + this.toLocal = (function(viewer){ + return function(position){ + var scenePos = position.clone().applyMatrix4(viewer.referenceFrame.matrixWorld); + + return scenePos; + } + })(this); + + + this.toGeo = (function(viewer){ + return function(position){ + var inverse = new THREE.Matrix4().getInverse(viewer.referenceFrame.matrixWorld); + var geoPos = position.clone().applyMatrix4(inverse); + + return geoPos; + } + })(this); + + this.getMinNodeSize = function(){ + return scope.minNodeSize; + }; - this.label = new Potree.TextSprite("0"); - this.label.setBorderColor({r:0, g:255, b:0, a:0.0}); - this.label.setBackgroundColor({r:0, g:255, b:0, a:0.0}); - this.label.material.depthTest = false; - this.label.material.depthWrite = false; - this.label.material.transparent = true; - this.label.position.y -= 0.5; - this.add(this.label); + this.setMinNodeSize = function(value){ + if(scope.minNodeSize !== value){ + scope.minNodeSize = value; + scope.dispatchEvent({"type": "minnodesize_changed", "viewer": scope}); + } + }; - var v = this; - this.label.updateMatrixWorld = function(){ - var volumeWorldPos = new THREE.Vector3(); - volumeWorldPos.setFromMatrixPosition( v.matrixWorld ); - v.label.position.copy(volumeWorldPos); - v.label.updateMatrix(); - v.label.matrixWorld.copy(v.label.matrix); - v.label.matrixWorldNeedsUpdate = false; + this.setDescription = function(value){ + $('#potree_description')[0].innerHTML = value; + }; + + this.setNavigationMode = function(value){ + if(value === "Orbit"){ + scope.useOrbitControls(); + }else if(value === "Flight"){ + scope.useFPSControls(); + }else if(value === "Earth"){ + scope.useEarthControls(); + } - for ( var i = 0, l = v.label.children.length; i < l; i ++ ) { - v.label.children[ i ].updateMatrixWorld( true ); + }; + + this.setShowBoundingBox = function(value){ + if(scope.showBoundingBox !== value){ + scope.showBoundingBox = value; + scope.dispatchEvent({"type": "show_boundingbox_changed", "viewer": scope}); } }; - this.setDimension = function(x,y,z){ - this.dimension.set(x,y,z); - this.box.scale.set(x,y,z); - this.frame.scale.set(x,y,z); + this.getShowBoundingBox = function(){ + return scope.showBoundingBox; }; - - this.volume = function(){ - return Math.abs(this.scale.x * this.scale.y * this.scale.z); - //return Math.abs(this.dimension.x * this.dimension.y * this.dimension.z); + + this.setMoveSpeed = function(value){ + if(scope.moveSpeed !== value){ + scope.moveSpeed = value; + scope.fpControls.setMoveSpeed(value); + scope.geoControls.setMoveSpeed(value); + scope.dispatchEvent({"type": "move_speed_changed", "viewer": scope, "speed": value}); + } }; - this.update = function(){ - this.boundingBox = this.box.geometry.boundingBox; - this.boundingSphere = this.boundingBox.getBoundingSphere(); - - if(this._clip){ - this.box.visible = false; - this.label.visible = false; - }else{ - this.box.visible = true; - this.label.visible = true; + this.getMoveSpeed = function(){ + return scope.fpControls.moveSpeed; + }; + + this.setShowSkybox = function(value){ + if(scope.showSkybox !== value){ + scope.showSkybox = value; + scope.dispatchEvent({"type": "show_skybox_changed", "viewer": scope}); } }; - this.raycast = function(raycaster, intersects){ - - var is = []; - this.box.raycast(raycaster, is); + this.getShowSkybox = function(){ + return scope.showSkybox; + }; - if(is.length > 0){ - var I = is[0]; - intersects.push({ - distance: I.distance, - object: this, - point: I.point.clone() - }); + this.setHeightRange = function(min, max){ + if(scope.heightMin !== min || scope.heightMax !== max){ + scope.heightMin = min || scope.heightMin; + scope.heightMax = max || scope.heightMax; + scope.dispatchEvent({"type": "height_range_changed", "viewer": scope}); } }; - this.update(); + this.getHeightRange = function(){ + return {min: scope.heightMin, max: scope.heightMax}; + }; -}; - -Potree.Volume.prototype = Object.create( THREE.Object3D.prototype ); - -Object.defineProperty(Potree.Volume.prototype, "clip", { - get: function(){ - return this._clip; - }, + this.setIntensityMax = function(max){ + if(scope.intensityMax !== max){ + scope.intensityMax = max; + scope.dispatchEvent({"type": "intensity_max_changed", "viewer": scope}); + } + }; - set: function(value){ - this._clip = value; - - this.update(); - } -}); - -Object.defineProperty(Potree.Volume.prototype, "modifiable", { - get: function(){ - return this._modifiable; - }, + this.getIntensityMax = function(){ + return scope.intensityMax; + }; - set: function(value){ - this._modifiable = value; - - this.update(); - } -}); - - -Potree.VolumeTool = function(scene, camera, renderer, transformationTool){ + this.setFreeze = function(value){ + if(scope.freeze != value){ + scope.freeze = value; + scope.dispatchEvent({"type": "freeze_changed", "viewer": scope}); + } + }; - var scope = this; - this.enabled = false; + this.getFreeze = function(){ + return scope.freeze; + }; - this.scene = scene; - this.sceneVolume = new THREE.Scene(); - this.camera = camera; - this.renderer = renderer; - this.transformationTool = transformationTool; - this.domElement = this.renderer.domElement; - this.mouse = {x: 0, y: 0}; + this.setPointBudget = function(value){ + if(Potree.pointBudget != value){ + Potree.pointBudget = parseInt(value); + scope.dispatchEvent({"type": "point_budget_changed", "viewer": scope}); + } + }; - this.volumes = []; + this.getPointBudget = function(){ + return Potree.pointBudget; + }; - var STATE = { - DEFAULT: 0, - INSERT_VOLUME: 1 - + this.setClipMode = function(clipMode){ + if(scope.clipMode != clipMode){ + scope.clipMode = clipMode; + scope.dispatchEvent({"type": "clip_mode_changed", "viewer": scope}); + } }; - var state = STATE.DEFAULT; + this.getClipMode = function(){ + return scope.clipMode; + }; + this.setDEMCollisionsEnabled = function(value){ + if(scope.useDEMCollisions !== value){ + scope.useDEMCollisions = value; + scope.dispatchEvent({"type": "use_demcollisions_changed", "viewer": scope}); + }; + }; - function onMouseMove(event){ - var rect = scope.domElement.getBoundingClientRect(); - scope.mouse.x = ((event.clientX - rect.left) / scope.domElement.clientWidth) * 2 - 1; - scope.mouse.y = -((event.clientY - rect.top) / scope.domElement.clientHeight) * 2 + 1; + this.getDEMCollisionsEnabled = function(){ + return scope.useDEMCollisions; }; - function onMouseClick(event){ - - //if(state === STATE.INSERT_VOLUME){ - // scope.finishInsertion(); - //}else if(event.which === 1){ - // var I = getHoveredElement(); - // - // if(I){ - // transformationTool.setTargets([I.object]); - // } - //} + this.setEDLEnabled = function(value){ + if(scope.useEDL != value){ + scope.useEDL = value; + scope.dispatchEvent({"type": "use_edl_changed", "viewer": scope}); + } }; - function onMouseDown(event){ + this.getEDLEnabled = function(){ + return scope.useEDL; + }; - if(state !== STATE.DEFAULT){ - event.stopImmediatePropagation(); + this.setEDLRadius = function(value){ + if(scope.edlRadius !== value){ + scope.edlRadius = value; + scope.dispatchEvent({"type": "edl_radius_changed", "viewer": scope}); } + }; - if(state === STATE.INSERT_VOLUME){ - scope.finishInsertion(); - }else if(event.which === 1){ - var I = getHoveredElement(); - - if(I && I.object.modifiable){ - scope.transformationTool.setTargets([I.object]); - } + this.getEDLRadius = function(){ + return scope.edlRadius; + }; + + this.setEDLStrength = function(value){ + if(scope.edlScale !== value){ + scope.edlScale = value; + scope.dispatchEvent({"type": "edl_strength_changed", "viewer": scope}); } + }; + this.getEDLStrength = function(){ + return scope.edlScale; + }; - if(event.which === 3){ - // open context menu - - //var element = getHoveredElement(); - // - //if(element){ - // var menu = document.createElement("div"); - // menu.style.position = "fixed"; - // menu.style.backgroundColor = "#bbbbbb"; - // menu.style.top = event.clientY + "px"; - // menu.style.left = event.clientX + "px"; - // menu.style.width = "200px"; - // menu.style.height = "100px"; - // menu.innerHTML = "abc"; - // menu.addEventListener("contextmenu", function(event){ - // event.preventDefault(); - // return false; - // }, false); - // - // scope.renderer.domElement.parentElement.appendChild(menu); - //} + this.setPointSize = function(value){ + if(scope.pointSize !== value){ + scope.pointSize = value; + scope.dispatchEvent({"type": "point_size_changed", "viewer": scope}); } }; - function onContextMenu(event){ - event.preventDefault(); - return false; + this.getPointSize = function(){ + return scope.pointSize; } - function getHoveredElement(){ - - var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); - vector.unproject(scope.camera); - - var raycaster = new THREE.Raycaster(); - raycaster.ray.set( scope.camera.position, vector.sub( scope.camera.position ).normalize() ); - - var objects = []; - for(var i = 0; i < scope.volumes.length; i++){ - var object = scope.volumes[i]; - objects.push(object); + this.setFOV = function(value){ + if(scope.fov !== value){ + scope.fov = value; + scope.dispatchEvent({"type": "fov_changed", "viewer": scope}); } - - var intersections = raycaster.intersectObjects(objects, false); - if(intersections.length > 0){ - return intersections[0]; - }else{ - return false; + }; + + this.getFOV = function(){ + return scope.fov; + }; + + this.setOpacity = function(value){ + if(scope.opacity !== value){ + scope.opacity = value; + scope.dispatchEvent({"type": "opacity_changed", "viewer": scope}); } }; - function getMousePointCloudIntersection(){ - var vector = new THREE.Vector3( scope.mouse.x, scope.mouse.y, 0.5 ); - vector.unproject(scope.camera); + this.getOpacity = function(){ + return scope.opacity; + }; - var direction = vector.sub(scope.camera.position).normalize(); - var ray = new THREE.Ray(scope.camera.position, direction); - - var pointClouds = []; - scope.scene.traverse(function(object){ - if(object instanceof Potree.PointCloudOctree || object instanceof Potree.PointCloudArena4D){ - pointClouds.push(object); - } - }); - - var closestPoint = null; - var closestPointDistance = null; - - for(var i = 0; i < pointClouds.length; i++){ - var pointcloud = pointClouds[i]; - var point = pointcloud.pick(scope.renderer, scope.camera, ray); - - if(!point){ - continue; + this.setPointSizing = function(value){ + if(scope.sizeType !== value){ + scope.sizeType = value; + if(value === "Fixed"){ + scope.pointSizeType = Potree.PointSizeType.FIXED; + }else if(value === "Attenuated"){ + scope.pointSizeType = Potree.PointSizeType.ATTENUATED; + }else if(value === "Adaptive"){ + scope.pointSizeType = Potree.PointSizeType.ADAPTIVE; } - var distance = scope.camera.position.distanceTo(point.position); - - if(!closestPoint || distance < closestPointDistance){ - closestPoint = point; - closestPointDistance = distance; - } + scope.dispatchEvent({"type": "point_sizing_changed", "viewer": scope}); + } + }; + + this.getPointSizing = function(){ + return scope.sizeType; + }; + + this.setQuality = function(value){ + var oldQuality = scope.quality; + if(value == "Interpolation" && !Potree.Features.SHADER_INTERPOLATION.isSupported()){ + scope.quality = "Squares"; + }else if(value == "Splats" && !Potree.Features.SHADER_SPLATS.isSupported()){ + scope.quality = "Squares"; + }else{ + scope.quality = value; } - return closestPoint ? closestPoint.position : null; - } + if(oldQuality !== scope.quality){ + scope.dispatchEvent({"type": "quality_changed", "viewer": scope}); + } + }; - this.update = function(delta){ + this.getQuality = function(){ + return scope.quality; + }; - if(state === STATE.INSERT_VOLUME){ - var I = getMousePointCloudIntersection(); + this.setClassificationVisibility = function(key, value){ + var changed = false; + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + var newClass = pointcloud.material.classification; + var oldValue = newClass[key].w; + newClass[key].w = value ? 1 : 0; - if(I){ - this.activeVolume.position.copy(I); - - var wp = this.activeVolume.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse); - var pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(this.camera.projectionMatrix); - var w = Math.abs((wp.z / 10)); - //this.activeVolume.setDimension(w, w, w); - this.activeVolume.scale.set(w,w,w); + if(oldValue !== newClass[key].w){ + changed = true; } + + pointcloud.material.classification = newClass; + } + + if(changed){ + scope.dispatchEvent({"type": "classification_visibility_changed", "viewer": scope}); + } + }; + + this.setMaterial = function(value){ + if(this.material !== value){ + this.material = scope.toMaterialID(value); + + scope.dispatchEvent({"type": "material_changed", "viewer": scope}); + } + }; + + this.getMaterial = function(){ + return scope.pointColorType; + }; + + this.getMaterialName = function(){ + return scope.toMaterialName(scope.pointColorType); + }; + + this.toMaterialID = function(materialName){ + if(materialName === "RGB"){ + scope.pointColorType = Potree.PointColorType.RGB; + }else if(materialName === "Color"){ + scope.pointColorType = Potree.PointColorType.COLOR; + }else if(materialName === "Elevation"){ + scope.pointColorType = Potree.PointColorType.HEIGHT; + }else if(materialName === "Intensity"){ + scope.pointColorType = Potree.PointColorType.INTENSITY; + }else if(materialName === "Intensity Gradient"){ + scope.pointColorType = Potree.PointColorType.INTENSITY_GRADIENT; + }else if(materialName === "Classification"){ + scope.pointColorType = Potree.PointColorType.CLASSIFICATION; + }else if(materialName === "Return Number"){ + scope.pointColorType = Potree.PointColorType.RETURN_NUMBER; + }else if(materialName === "Source"){ + scope.pointColorType = Potree.PointColorType.SOURCE; + }else if(materialName === "Tree Depth"){ + scope.pointColorType = Potree.PointColorType.TREE_DEPTH; + }else if(materialName === "Point Index"){ + scope.pointColorType = Potree.PointColorType.POINT_INDEX; + }else if(materialName === "Normal"){ + scope.pointColorType = Potree.PointColorType.NORMAL; + }else if(materialName === "Phong"){ + scope.pointColorType = Potree.PointColorType.PHONG; + } + }; + + this.toMaterialName = function(materialID){ + if(materialID === Potree.PointColorType.RGB){ + return "RGB"; + }else if(materialID === Potree.PointColorType.COLOR){ + return "Color"; + }else if(materialID === Potree.PointColorType.HEIGHT){ + return "Elevation"; + }else if(materialID === Potree.PointColorType.INTENSITY){ + return "Intensity"; + }else if(materialID === Potree.PointColorType.INTENSITY_GRADIENT){ + return "Intensity Gradient"; + }else if(materialID === Potree.PointColorType.CLASSIFICATION){ + return "Classification"; + }else if(materialID === Potree.PointColorType.RETURN_NUMBER){ + return "Return Number"; + }else if(materialID === Potree.PointColorType.SOURCE){ + return "Source"; + }else if(materialID === Potree.PointColorType.TREE_DEPTH){ + return "Tree Depth"; + }else if(materialID === Potree.PointColorType.POINT_INDEX){ + return "Point Index"; + }else if(materialID === Potree.PointColorType.NORMAL){ + return "Normal"; + }else if(materialID === Potree.PointColorType.PHONG){ + return "Phong"; + } + }; + + this.zoomTo = function(node, factor){ + scope.camera.zoomTo(node, factor); + + var bs; + if(node.boundingSphere){ + bs = node.boundingSphere; + }else if(node.geometry && node.geometry.boundingSphere){ + bs = node.geometry.boundingSphere; + }else{ + bs = node.boundingBox.getBoundingSphere(); } - var volumes = []; - for(var i = 0; i < this.volumes.length; i++){ - volumes.push(this.volumes[i]); - } - if(this.activeVolume){ - volumes.push(this.activeVolume); - } - - for(var i = 0; i < volumes.length; i++){ - var volume = volumes[i]; - var box = volume.box; - var label = volume.label; + bs = bs.clone().applyMatrix4(node.matrixWorld); + + scope.orbitControls.target.copy(bs.center); + + scope.dispatchEvent({"type": "zoom_to", "viewer": scope}); + }; + + this.showAbout = function(){ + $(function() { + $( "#about-panel" ).dialog(); + }); + }; + + this.getBoundingBox = function(pointclouds){ + pointclouds = pointclouds || scope.pointclouds; + + var box = new THREE.Box3(); + + scope.scenePointCloud.updateMatrixWorld(true); + scope.referenceFrame.updateMatrixWorld(true); + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; - var capacity = volume.volume(); - var msg = Potree.utils.addCommas(capacity.toFixed(1)) + "³"; - label.setText(msg); + pointcloud.updateMatrixWorld(true); - var distance = scope.camera.position.distanceTo(label.getWorldPosition()); - var pr = projectedRadius(1, scope.camera.fov * Math.PI / 180, distance, scope.renderer.domElement.clientHeight); - var scale = (70 / pr); - label.scale.set(scale, scale, scale); + //var boxWorld = pointcloud.boundingBox.clone().applyMatrix4(pointcloud.matrixWorld); + var boxWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrixWorld) + box.union(boxWorld); } + return box; }; - this.startInsertion = function(args){ - state = STATE.INSERT_VOLUME; + this.getBoundingBoxGeo = function(pointclouds){ + pointclouds = pointclouds || scope.pointclouds; - var args = args || {}; - var clip = args.clip || false; + var box = new THREE.Box3(); - this.activeVolume = new Potree.Volume(); - this.activeVolume.clip = clip; - this.sceneVolume.add(this.activeVolume); - this.volumes.push(this.activeVolume); + scope.scenePointCloud.updateMatrixWorld(true); + scope.referenceFrame.updateMatrixWorld(true); + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + + pointcloud.updateMatrixWorld(true); + + var boxWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrix) + box.union(boxWorld); + } + + return box; }; - this.finishInsertion = function(){ - scope.transformationTool.setTargets([this.activeVolume]); + this.fitToScreen = function(){ + var box = this.getBoundingBox(scope.pointclouds); - var event = { - type: "insertion_finished", - volume: this.activeVolume - }; - this.dispatchEvent(event); + if(scope.transformationTool.targets.length > 0){ + box = scope.transformationTool.getBoundingBox(); + } - this.activeVolume = null; - state = STATE.DEFAULT; + var node = new THREE.Object3D(); + node.boundingBox = box; + + //scope.camera.zoomTo(node, 1); + scope.zoomTo(node, 1); }; - this.addVolume = function(volume){ - this.sceneVolume.add(volume); - this.volumes.push(volume); + this.setTopView = function(){ + var box = this.getBoundingBox(scope.pointclouds); + + if(scope.transformationTool.targets.length > 0){ + box = scope.transformationTool.getBoundingBox(); + } + + var node = new THREE.Object3D(); + node.boundingBox = box; + + scope.camera.position.set(0, 1, 0); + scope.camera.rotation.set(-Math.PI / 2, 0, 0); + scope.camera.zoomTo(node, 1); }; - this.removeVolume = function(volume){ - this.sceneVolume.remove(volume); - var index = this.volumes.indexOf(volume); - if(index >= 0){ - this.volumes.splice(index, 1); + this.setFrontView = function(){ + var box = this.getBoundingBox(scope.pointclouds); + + if(scope.transformationTool.targets.length > 0){ + box = scope.transformationTool.getBoundingBox(); } + + var node = new THREE.Object3D(); + node.boundingBox = box; + + scope.camera.position.set(0, 0, 1); + scope.camera.rotation.set(0, 0, 0); + scope.camera.zoomTo(node, 1); }; - this.reset = function(){ - for(var i = this.volumes.length - 1; i >= 0; i--){ - var volume = this.volumes[i]; - this.removeVolume(volume); + this.setLeftView = function(){ + var box = this.getBoundingBox(scope.pointclouds); + + if(scope.transformationTool.targets.length > 0){ + box = scope.transformationTool.getBoundingBox(); } + + var node = new THREE.Object3D(); + node.boundingBox = box; + + scope.camera.position.set(-1, 0, 0); + scope.camera.rotation.set(0, -Math.PI / 2, 0); + scope.camera.zoomTo(node, 1); }; - - this.render = function(target){ + this.setRightView = function(){ + var box = this.getBoundingBox(scope.pointclouds); - scope.renderer.render(this.sceneVolume, this.camera, target); + if(scope.transformationTool.targets.length > 0){ + box = scope.transformationTool.getBoundingBox(); + } + var node = new THREE.Object3D(); + node.boundingBox = box; + + scope.camera.position.set(1, 0, 0); + scope.camera.rotation.set(0, Math.PI / 2, 0); + scope.camera.zoomTo(node, 1); }; - this.domElement.addEventListener( 'click', onMouseClick, false ); - this.domElement.addEventListener( 'mousedown', onMouseDown, false ); - this.domElement.addEventListener( 'mousemove', onMouseMove, false ); - this.domElement.addEventListener( 'contextmenu', onContextMenu, false ); -}; - -Potree.VolumeTool.prototype = Object.create( THREE.EventDispatcher.prototype ); - -Potree.PointCloudArena4DProxyNode = function(geometryNode){ - THREE.Object3D.call( this ); - - this.geometryNode = geometryNode; - this.pcoGeometry = geometryNode; - this.boundingBox = geometryNode.boundingBox; - this.boundingSphere = geometryNode.boundingSphere; - this.number = geometryNode.name; - this.numPoints = geometryNode.numPoints; - this.level = geometryNode.level; -}; - -Potree.PointCloudArena4DProxyNode.prototype = Object.create(THREE.Object3D.prototype); - - - -Potree.PointCloudArena4D = function(geometry){ - THREE.Object3D.call( this ); - - this.root = null; - - this.visiblePointsTarget = 2*1000*1000; - this.minimumNodePixelSize = 150; - - this.position.sub(geometry.offset); - this.updateMatrix(); - - this.numVisibleNodes = 0; - this.numVisiblePoints = 0; - - this.boundingBoxNodes = []; - this.loadQueue = []; - this.visibleNodes = []; - - this.pcoGeometry = geometry; - this.boundingBox = this.pcoGeometry.boundingBox; - this.boundingSphere = this.pcoGeometry.boundingSphere; - this.material = new Potree.PointCloudMaterial({vertexColors: THREE.VertexColors, size: 0.05, treeType: Potree.TreeType.KDTREE}); - this.material.sizeType = Potree.PointSizeType.ATTENUATED; - this.material.size = 0.05; - - this.pickTarget; - this.pickMaterial; - - this.updateMatrixWorld(); -}; - -Potree.PointCloudArena4D.prototype = Object.create(THREE.Object3D.prototype); - -Potree.PointCloudArena4D.prototype.updateMaterial = function(camera, renderer){ - this.material.fov = camera.fov * (Math.PI / 180); - this.material.screenWidth = renderer.domElement.clientWidth; - this.material.screenHeight = renderer.domElement.clientHeight; - this.material.spacing = this.pcoGeometry.spacing; - this.material.near = camera.near; - this.material.far = camera.far; - - // reduce shader source updates by setting maxLevel slightly higher than actually necessary - if(this.maxLevel > this.material.levels){ - this.material.levels = this.maxLevel + 2; + this.flipYZ = function(){ + scope.isFlipYZ = !scope.isFlipYZ; + + scope.referenceFrame.matrix.copy(new THREE.Matrix4()); + if(scope.isFlipYZ){ + scope.referenceFrame.applyMatrix(new THREE.Matrix4().set( + 1,0,0,0, + 0,0,1,0, + 0,-1,0,0, + 0,0,0,1 + )); + + }else{ + scope.referenceFrame.applyMatrix(new THREE.Matrix4().set( + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 + )); + } + + scope.referenceFrame.updateMatrixWorld(true); + var box = scope.getBoundingBox(); + scope.referenceFrame.position.copy(box.center()).multiplyScalar(-1); + scope.referenceFrame.position.y = -box.min.y; + scope.referenceFrame.updateMatrixWorld(true); + + scope.updateHeightRange(); + + + //scope.referenceFrame.updateMatrixWorld(true); + //scope.pointclouds[0].updateMatrixWorld(); + //var sg = scope.pointclouds[0].boundingSphere.clone().applyMatrix4(scope.pointclouds[0].matrixWorld); + //scope.referenceFrame.position.copy(sg.center).multiplyScalar(-1); + //scope.referenceFrame.updateMatrixWorld(true); + //scope.referenceFrame.position.y -= scope.pointclouds[0].getWorldPosition().y; + //scope.referenceFrame.updateMatrixWorld(true); } - this.material.minSize = 3; + this.updateHeightRange = function(){ + var bbWorld = scope.getBoundingBox(); + scope.setHeightRange(bbWorld.min.y, bbWorld.max.y); + }; - var bbSize = this.boundingBox.size(); - this.material.bbSize = [bbSize.x, bbSize.y, bbSize.z]; -}; + this.useEarthControls = function(){ + if(scope.controls){ + scope.controls.enabled = false; + } -Potree.PointCloudArena4D.prototype.hideDescendants = function(object){ - var stack = []; - for(var i = 0; i < object.children.length; i++){ - var child = object.children[i]; - if(child.visible){ - stack.push(child); - } + scope.controls = scope.earthControls; + scope.controls.enabled = true; } - while(stack.length > 0){ - var object = stack.shift(); - - object.visible = false; - if(object.boundingBoxNode){ - object.boundingBoxNode.visible = false; + this.useGeoControls = function(){ + if(scope.controls){ + scope.controls.enabled = false; } + + scope.controls = scope.geoControls; + scope.controls.enabled = true; - for(var i = 0; i < object.children.length; i++){ - var child = object.children[i]; - if(child.visible){ - stack.push(child); - } - } + //scope.controls.moveSpeed = scope.pointclouds[0].boundingSphere.radius / 6; } -}; - -Potree.PointCloudArena4D.prototype.updateMatrixWorld = function( force ){ - //node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); - - if ( this.matrixAutoUpdate === true ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate === true || force === true ) { - - if ( this.parent === undefined ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + this.useFPSControls = function(){ + if(scope.controls){ + scope.controls.enabled = false; } - this.matrixWorldNeedsUpdate = false; - - force = true; - + scope.controls = scope.fpControls; + scope.controls.enabled = true; + + //scope.controls.moveSpeed = scope.pointclouds[0].boundingSphere.radius / 6; } -}; - -var dbgFullyInside = 0; -Potree.PointCloudArena4D.prototype.update = function(camera, renderer){ - var geometry = this.pcoGeometry; - - if(!geometry.root){ - return; - }else if(!this.rootProxyGenerated){ - var rootProxy = new Potree.PointCloudArena4DProxyNode(this.pcoGeometry.root); - this.add(rootProxy); - this.rootProxyGenerated = true; + this.useOrbitControls = function(){ + if(scope.controls){ + scope.controls.enabled = false; + } + + scope.controls = scope.orbitControls; + scope.controls.enabled = true; + + if(scope.pointclouds.length > 0){ + scope.controls.target.copy(scope.pointclouds[0].boundingSphere.center.clone().applyMatrix4(scope.pointclouds[0].matrixWorld)); + } + }; + + this.addAnnotation = function(position, args){ + var cameraPosition = args.cameraPosition; + var cameraTarget = args.cameraTarget || position; + var description = args.description || null; + var title = args.title || null; + + var annotation = new Potree.Annotation(scope, { + "position": position, + "cameraPosition": cameraPosition, + "cameraTarget": cameraTarget, + "title": title, + "description": description + }); + + scope.annotations.push(annotation); + scope.renderArea.appendChild(annotation.domElement); + + scope.dispatchEvent({"type": "annotation_added", "viewer": scope}); + + return annotation; } - this.updateMatrixWorld(true); + this.getAnnotations = function(){ + return scope.annotations; + }; - this.loadQueue = []; - this.visibleNodes = []; - this.numVisibleNodes = 0; - this.numVisiblePoints = 0; - dbgFullyInside = 0; - if(!this.showBoundingBox){ - for(var i = 0; i < this.boundingBoxNodes.length; i++){ - var bbNode = this.boundingBoxNodes[i]; - this.remove(bbNode); - bbNode.geometry.dispose(); - } - } - this.updateMaterial(camera, renderer); - this.hideDescendants(this.children[0]); - // create frustum in object space - camera.updateMatrixWorld(); - var frustum = new THREE.Frustum(); - var viewI = camera.matrixWorldInverse; - var world = this.matrixWorld; - var proj = camera.projectionMatrix; - var fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world); - frustum.setFromMatrix( fm ); - // calculate camera position in object space - var view = camera.matrixWorld; - var worldI = new THREE.Matrix4().getInverse(world); - var camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view); - var camObjPos = new THREE.Vector3().setFromMatrixPosition( camMatrixObject ); - var stack = []; - stack.push({node: this.children[0], weight: 1}); - while(stack.length > 0){ - var element = stack.shift(); - var node = element.node; - var weight = element.weight; + + + + +//------------------------------------------------------------------------------------ +// Viewer Internals +//------------------------------------------------------------------------------------ - //if(node.level > 3){ - // continue; - //} - - node.matrixWorld.multiplyMatrices( this.matrixWorld, node.matrix ); + this.toggleSidebar = function(){ - var box = node.boundingBox.clone(); - //box.min.sub(this.boundingBox.min); - //box.max.sub(this.boundingBox.min); - var insideFrustum = frustum.intersectsBox(box); + var renderArea = $('#potree_render_area'); + var sidebar = $('#potree_sidebar_container'); + var isVisible = renderArea.css("left") !== "0px"; - var visible = insideFrustum; - node.visible = visible; + if(isVisible){ + renderArea.css("left", "0px"); + }else{ + renderArea.css("left", "300px"); + } + }; + + this.toggleMap = function(){ + var map = $('#potree_map'); + map.toggle(100); + }; + + this.loadGUI = function(){ + var sidebarContainer = $('#potree_sidebar_container'); + sidebarContainer.load("../build/potree/sidebar.html"); + sidebarContainer.css("width", "300px"); + sidebarContainer.css("height", "100%"); - if(!visible){ - continue; - } + //$('head').append( $('').attr('href', '../src/viewer/viewer.css') ); + //$('head').append( $('').attr('href', "../libs/bootstrap/css/bootstrap.min.css")); + //$('head').append( $('').attr('href', "../libs/jasny-bootstrap/css/jasny-bootstrap.css")); + //$('head').append( $('').attr('href', "../libs/jasny-bootstrap/css/navmenu-reveal.css" )); + //$('head').append( $('').attr('href', "../libs/jquery-ui-1.11.4/jquery-ui.css" )); - var pointsInside = 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.min.x, box.min.y, box.min.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.min.x, box.min.y, box.max.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.min.x, box.max.y, box.min.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.min.x, box.max.y, box.max.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.max.x, box.min.y, box.min.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.max.x, box.min.y, box.max.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.max.x, box.max.y, box.min.z)) ? 1 : 0; - //pointsInside += frustum.containsPoint(new THREE.Vector3(box.max.x, box.max.y, box.max.z)) ? 1 : 0; + //var elProfile = $('
'); + var elProfile = $('
').load("../build/potree/profile.html", function(){ + $('#potree_render_area').append(elProfile.children()); + scope._2dprofile = new Potree.Viewer.Profile(scope, document.getElementById("profile_draw_container")); + }); - if(pointsInside === 8){ - dbgFullyInside++; - } + } + + this.createControls = function(){ - - if (node instanceof Potree.PointCloudArena4DProxyNode) { - var geometryNode = node.geometryNode; - if(geometryNode.loaded === true){ - this.replaceProxy(node); - }else{ - this.loadQueue.push(element); + var demCollisionHandler = function(event){ + + if(!scope.useDEMCollisions){ + return } - }else if(node instanceof THREE.PointCloud){ - if(this.numVisiblePoints + node.pcoGeometry.numPoints > pointcloud.visiblePointsTarget){ - break; + + var demHeight = null; + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + pointcloud.generateDEM = true; + + var height = pointcloud.getDEMHeight(event.newPosition); + + if(demHeight){ + demHeight = Math.max(demHeight, height); + }else{ + demHeight = height; + } } - this.numVisibleNodes++; - this.numVisiblePoints += node.pcoGeometry.numPoints; - this.visibleNodes.push({node: node, weight: weight}); - - if(this.showBoundingBox && !node.boundingBoxNode){ - var boxHelper = new THREE.BoxHelper(node); - this.add(boxHelper); - this.boundingBoxNodes.push(boxHelper); - node.boundingBoxNode = boxHelper; - node.boundingBoxNode.matrixWorld.copy(node.matrixWorld); - }else if(this.showBoundingBox && node.boundingBoxNode){ - node.boundingBoxNode.visible = true; - }else if(!this.showBoundingBox){ - delete node.boundingBoxNode; + + if(event.newPosition.y < demHeight){ + event.objections++; + var counterProposal = event.newPosition.clone(); + counterProposal.y = demHeight; + event.counterProposals.push(counterProposal); } + }; + + { // create FIRST PERSON CONTROLS + scope.fpControls = new THREE.FirstPersonControls(scope.camera, scope.renderer.domElement); + scope.fpControls.enabled = false; + scope.fpControls.addEventListener("proposeTransform", demCollisionHandler); + scope.fpControls.addEventListener("move_speed_changed", function(event){ + scope.setMoveSpeed(scope.fpControls.moveSpeed); + }); + } + + { // create GEO CONTROLS + scope.geoControls = new Potree.GeoControls(scope.camera, scope.renderer.domElement); + scope.geoControls.enabled = false; + scope.geoControls.addEventListener("proposeTransform", demCollisionHandler); + scope.geoControls.addEventListener("move_speed_changed", function(event){ + scope.setMoveSpeed(scope.geoControls.moveSpeed); + }); + } + + { // create ORBIT CONTROLS + scope.orbitControls = new Potree.OrbitControls(scope.camera, scope.renderer.domElement); + scope.orbitControls.enabled = false; + scope.orbitControls.addEventListener("proposeTransform", demCollisionHandler); + scope.renderArea.addEventListener("dblclick", function(event){ + if(scope.pointclouds.length === 0){ + return; + } + + event.preventDefault(); - for(var i = 0; i < node.children.length; i++){ - var child = node.children[i]; - //var box = child.geometryNode.boundingBox; - var sphere = child.boundingSphere; - var distance = sphere.center.distanceTo(camObjPos); + var rect = scope.renderArea.getBoundingClientRect(); - var radius = box.size().length() / 2; - var fov = camera.fov / 2 * Math.PI / 180.0; - var pr = 1 / Math.tan(fov) * radius / Math.sqrt(distance * distance - radius * radius); + var mouse = { + x: ( (event.clientX - rect.left) / scope.renderArea.clientWidth ) * 2 - 1, + y: - ( (event.clientY - rect.top) / scope.renderArea.clientHeight ) * 2 + 1 + }; - if(distance < radius){ - pr = Number.MAX_VALUE; - } + var pointcloud = null; + var distance = Number.POSITIVE_INFINITY; + var I = null; - var screenPixelRadius = renderer.domElement.clientHeight * pr; - if(screenPixelRadius < this.minimumNodePixelSize){ - continue; + for(var i = 0; i < scope.pointclouds.length; i++){ + intersection = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, [scope.pointclouds[i]]); + if(!intersection){ + continue; + } + + var tDist = scope.camera.position.distanceTo(intersection); + if(tDist < distance){ + pointcloud = scope.pointclouds[i]; + distance = tDist; + I = intersection; + } } - var weight = pr; + if(I != null){ - if(stack.length === 0){ - stack.push({node: child, weight: weight}); - }else{ - var ipos = 0; - - for(var j = 0; j < stack.length; j++){ - if(weight > stack[j].weight){ - var ipos = j; - break; - }else if(j == stack.length -1){ - ipos = stack.length; - break; - } - + var targetRadius = 0; + if(!scope.jumpDistance){ + var camTargetDistance = scope.camera.position.distanceTo(scope.orbitControls.target); + + var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); + vector.unproject(scope.camera); + + var direction = vector.sub(scope.camera.position).normalize(); + var ray = new THREE.Ray(scope.camera.position, direction); + var nodes = pointcloud.nodesOnRay(pointcloud.visibleNodes, ray); + var lastNode = nodes[nodes.length - 1]; + var radius = lastNode.getBoundingSphere().radius; + var targetRadius = Math.min(camTargetDistance, radius); + var targetRadius = Math.max(scope.minimumJumpDistance, targetRadius); + }else{ + targetRadius = scope.jumpDistance; } - stack.splice(ipos, 0, {node: child, weight: weight}); + var d = scope.camera.getWorldDirection().multiplyScalar(-1); + var cameraTargetPosition = new THREE.Vector3().addVectors(I, d.multiplyScalar(targetRadius)); + var controlsTargetPosition = I; + + var animationDuration = 600; + + var easing = TWEEN.Easing.Quartic.Out; + + scope.controls.enabled = false; + + // animate position + var tween = new TWEEN.Tween(scope.camera.position).to(cameraTargetPosition, animationDuration); + tween.easing(easing); + tween.start(); + + // animate target + var tween = new TWEEN.Tween(scope.orbitControls.target).to(I, animationDuration); + tween.easing(easing); + tween.onComplete(function(){ + scope.controls.enabled = true; + scope.fpControls.moveSpeed = radius / 2; + scope.geoControls.moveSpeed = radius / 2; + }); + tween.start(); } - - //stack.push({node: child, weight: 1}); - } + }); } - } - - - - this.updateLoadQueue(); - - this.maxLevel = 0; - for(var i = 0; i < this.visibleNodes.length; i++){ - this.maxLevel = Math.max(this.visibleNodes[i].node.pcoGeometry.level, this.maxLevel); - } + + { // create EARTH CONTROLS + scope.earthControls = new THREE.EarthControls(scope.camera, scope.renderer, scope.scenePointCloud); + scope.earthControls.enabled = false; + scope.earthControls.addEventListener("proposeTransform", demCollisionHandler); + } + }; - var vn = []; - for(var i = 0; i < this.visibleNodes.length; i++){ - vn.push(this.visibleNodes[i].node); - } - this.updateVisibilityTexture(this.material, vn); - - //{ // only show nodes on ray - // var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); - // vector.unproject(camera); - // - // var direction = vector.sub(camera.position).normalize(); - // var ray = new THREE.Ray(camera.position, direction); - // - // - // var nodesOnRay = pointcloud.nodesOnRay(pointcloud.visibleNodes, ray); - // - // for(var i = 0; i < this.visibleNodes.length; i++){ - // var node = this.visibleNodes[i].node; - // - // node.visible = false; - // if(node.boundingBoxNode){ - // node.boundingBoxNode.visible = false; - // } - // } - // - // for(var i = 0; i < nodesOnRay.length; i++){ - // var node = nodesOnRay[i]; - // - // node.visible = true; - // this.numVisiblePoints += node.pcoGeometry.numPoints; - // if(node.boundingBoxNode){ - // node.boundingBoxNode.visible = true; - // } - // } - // this.numVisibleNodes = nodesOnRay.length; - // - // var pickPos = this.pick(renderer, camera, ray, {}); - // if(pickPos){ - // var sg = new THREE.SphereGeometry(0.2); - // var sm = new THREE.Mesh(sg); - // sm.position.copy(pickPos.position); - // scene.add(sm); - // } - //} -}; + this.initThree = function(){ + var width = scope.renderArea.clientWidth; + var height = scope.renderArea.clientHeight; + var aspect = width / height; + var near = 0.1; + var far = 1000*1000; -Potree.PointCloudArena4D.prototype.replaceProxy = function(proxy){ - - var geometryNode = proxy.geometryNode; - if(geometryNode.loaded === true){ - var geometry = geometryNode.geometry; - var node = new THREE.PointCloud(geometry, this.material); - node.number = proxy.number; - node.numPoints = proxy.numPoints; - node.boundingBox = geometryNode.boundingBox; - node.boundingSphere = geometryNode.boundingSphere; - node.pcoGeometry = geometryNode; - var parent = proxy.parent; - parent.remove(proxy); - parent.add(node); - //node.position.copy(node.boundingBox.min); - //node.position.sub(this.pcoGeometry.boundingBox.min); - //var current = parent; - //while(!(current instanceof Potree.PointCloudArena4D)){ - // node.position.sub(current.boundingBox.min); - // - // current = current.parent; - //} + scope.scene = new THREE.Scene(); + scope.scenePointCloud = new THREE.Scene(); + scope.sceneBG = new THREE.Scene(); + + scope.camera = new THREE.PerspectiveCamera(scope.fov, aspect, near, far); + //camera = new THREE.OrthographicCamera(-50, 50, 50, -50, 1, 100000); + scope.cameraBG = new THREE.Camera(); + scope.camera.rotation.order = 'ZYX'; + + scope.referenceFrame = new THREE.Object3D(); + scope.scenePointCloud.add(scope.referenceFrame); + + scope.renderer = new THREE.WebGLRenderer(); + scope.renderer.setSize(width, height); + scope.renderer.autoClear = false; + scope.renderArea.appendChild(scope.renderer.domElement); + scope.renderer.domElement.tabIndex = "2222"; + scope.renderer.domElement.addEventListener("mousedown", function(){scope.renderer.domElement.focus();}); - node.updateMatrix(); + skybox = Potree.utils.loadSkybox("../resources/textures/skybox/"); + + // camera and controls + scope.camera.position.set(-304, 372, 318); + scope.camera.rotation.y = -Math.PI / 4; + scope.camera.rotation.x = -Math.PI / 6; - //console.log(geometryNode.number + ": " + node.position.x + ", " + node.position.y + ", " + node.position.z); + this.createControls(); - node.matrixWorld.multiplyMatrices( this.matrixWorld, node.matrix ); + //scope.useEarthControls(); - if(geometryNode.left){ - var child = geometryNode.left; - var childProxy = new Potree.PointCloudArena4DProxyNode(child); - node.add(childProxy); - } - if(geometryNode.right){ - var child = geometryNode.right; - var childProxy = new Potree.PointCloudArena4DProxyNode(child); - node.add(childProxy); - } + // enable frag_depth extension for the interpolation shader, if available + scope.renderer.context.getExtension("EXT_frag_depth"); + + //this.addPointCloud(pointcloudPath); + + var grid = Potree.utils.createGrid(5, 5, 2); + scope.scene.add(grid); + + scope.measuringTool = new Potree.MeasuringTool(scope.scenePointCloud, scope.camera, scope.renderer, scope.toGeo); + scope.profileTool = new Potree.ProfileTool(scope.scenePointCloud, scope.camera, scope.renderer); + scope.transformationTool = new Potree.TransformationTool(scope.scenePointCloud, scope.camera, scope.renderer); + scope.volumeTool = new Potree.VolumeTool(scope.scenePointCloud, scope.camera, scope.renderer, scope.transformationTool); + + scope.profileTool.addEventListener("profile_added", function(profileEvent){ + + //var poSelect = document.getElementById("profile_selection"); + //var po = document.createElement("option"); + //po.innerHTML = "profile " + scope.profileTool.profiles.length; + //poSelect.appendChild(po); + + + var profileButton = document.createElement("button"); + profileButton.type = "button"; + profileButton.classList.add("btn"); + profileButton.classList.add("btn-primary"); + profileButton.id = "btn_rofile_" + scope.profileTool.profiles.length; + //profileButton.style.width = "100%"; + profileButton.value = "profile " + scope.profileTool.profiles.length; + profileButton.innerHTML = "profile " + scope.profileTool.profiles.length; + + //type="button" class="btn btn-primary" + + profileButton.onclick = function(clickEvent){ + scope.profileTool.draw( + profileEvent.profile, + $("#profile_draw_container")[0], + scope.toGeo); + profileEvent.profile.addEventListener("marker_moved", function(){ + scope.profileTool.draw( + profileEvent.profile, + $("#profile_draw_container")[0], + scope.toGeo); + }); + profileEvent.profile.addEventListener("width_changed", function(){ + scope.profileTool.draw( + profileEvent.profile, + $("#profile_draw_container")[0], + scope.toGeo); + }); + }; + }); + + + // background + // var texture = THREE.ImageUtils.loadTexture( '../resources/textures/background.gif' ); + var texture = Potree.utils.createBackgroundTexture(512, 512); + + texture.minFilter = texture.magFilter = THREE.NearestFilter; + texture.minFilter = texture.magFilter = THREE.LinearFilter; + + var bg = new THREE.Mesh( + new THREE.PlaneBufferGeometry(2, 2, 0), + new THREE.MeshBasicMaterial({ + map: texture + }) + ); + //bg.position.z = -1; + bg.material.depthTest = false; + bg.material.depthWrite = false; + scope.sceneBG.add(bg); + + window.addEventListener( 'keydown', onKeyDown, false ); + + scope.directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); + scope.directionalLight.position.set( 10, 10, 10 ); + scope.directionalLight.lookAt( new THREE.Vector3(0, 0, 0)); + scope.scenePointCloud.add( scope.directionalLight ); + + var light = new THREE.AmbientLight( 0x555555 ); // soft white light + scope.scenePointCloud.add( light ); - return node; } -}; -Potree.PointCloudArena4D.prototype.updateLoadQueue = function(vn){ - if(this.loadQueue.length > 0){ - if(this.loadQueue.length >= 2){ - this.loadQueue.sort(function(a,b){return b.weight - a.weight}); - } + function onKeyDown(event){ + //console.log(event.keyCode); - for(var i = 0; i < Math.min(5, this.loadQueue.length); i++){ - this.loadQueue[i].node.geometryNode.load(); + if(event.keyCode === 69){ + // e pressed + + scope.transformationTool.translate(); + }else if(event.keyCode === 82){ + // r pressed + + scope.transformationTool.scale(); + }else if(event.keyCode === 84){ + // r pressed + + scope.transformationTool.rotate(); } - } -}; + }; -Potree.PointCloudArena4D.prototype.getVisibleGeometry = function(camera){ - var visibleGeometry = []; - - // create frustum in object space - camera.updateMatrixWorld(); - var frustum = new THREE.Frustum(); - var viewI = camera.matrixWorldInverse; - var world = this.matrixWorld; - var proj = camera.projectionMatrix; - var fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world); - frustum.setFromMatrix( fm ); - - var stack = []; - var pointCount = 0; - - stack.push(this.pcoGeometry.root); - while(stack.length > 0){ - if(visibleGeometry.length > 12){ - break; - } - - var node = stack.shift(); + this.update = function(delta, timestamp){ + Potree.pointLoadLimit = Potree.pointBudget * 2; - var box = node.boundingBox.clone(); - box.max.sub(box.min); - box.min.sub(box.min); - var insideFrustum = frustum.intersectsBox(box); + scope.directionalLight.position.copy(scope.camera.position); + scope.directionalLight.lookAt(new THREE.Vector3().addVectors(scope.camera.position, scope.camera.getWorldDirection())); - var visible = insideFrustum; + var visibleNodes = 0; + var visiblePoints = 0; + var progress = 0; - if(!visible){ - continue; + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + var bbWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrixWorld); + + if(!scope.intensityMax){ + var root = pointcloud.pcoGeometry.root; + if(root != null && root.loaded){ + var attributes = pointcloud.pcoGeometry.root.geometry.attributes; + if(attributes.intensity){ + var array = attributes.intensity.array; + var max = 0; + for(var i = 0; i < array.length; i++){ + max = Math.max(array[i]); + } + + if(max <= 1){ + scope.intensityMax = 1; + }else if(max <= 256){ + scope.intensityMax = 255; + }else{ + scope.intensityMax = max; + } + } + } + } + + //if(scope.heightMin === null){ + // scope.setHeightRange(bbWorld.min.y, bbWorld.max.y); + //} + + pointcloud.material.clipMode = scope.clipMode; + pointcloud.material.heightMin = scope.heightMin; + pointcloud.material.heightMax = scope.heightMax; + pointcloud.material.intensityMin = 0; + pointcloud.material.intensityMax = scope.intensityMax; + pointcloud.showBoundingBox = scope.showBoundingBox; + pointcloud.generateDEM = scope.useDEMCollisions; + pointcloud.minimumNodePixelSize = scope.minNodeSize; + + //if(!scope.freeze){ + // pointcloud.update(scope.camera, scope.renderer); + //} + + visibleNodes += pointcloud.numVisibleNodes; + visiblePoints += pointcloud.numVisiblePoints; + + progress += pointcloud.progress; } - if(pointCount + node.numPoints > this.visiblePointsTarget){ - break; + if(!scope.freeze){ + var result = Potree.updatePointClouds(scope.pointclouds, scope.camera, scope.renderer); + visibleNodes = result.visibleNodes.length; + visiblePoints = result.numVisiblePoints; } - pointCount += node.numPoints; - visibleGeometry.push(node); + //if(stats && scope.showStats){ + // document.getElementById("lblNumVisibleNodes").style.display = ""; + // document.getElementById("lblNumVisiblePoints").style.display = ""; + // stats.domElement.style.display = ""; + // + // stats.update(); + // + // document.getElementById("lblNumVisibleNodes").innerHTML = "visible nodes: " + visibleNodes; + // document.getElementById("lblNumVisiblePoints").innerHTML = "visible points: " + Potree.utils.addCommas(visiblePoints); + //}else if(stats){ + // document.getElementById("lblNumVisibleNodes").style.display = "none"; + // document.getElementById("lblNumVisiblePoints").style.display = "none"; + // stats.domElement.style.display = "none"; + //} + + scope.camera.fov = scope.fov; - if(node.loaded){ - if(node.left){ - stack.push(node.left); + if(scope.controls){ + scope.controls.update(delta); + } + + // update progress bar + if(scope.pointclouds.length > 0){ + scope.progressBar.progress = progress / scope.pointclouds.length; + + var message; + if(progress === 0){ + message = "loading"; + }else{ + message = "loading: " + parseInt(progress*100 / scope.pointclouds.length) + "%"; } + scope.progressBar.message = message; - if(node.right){ - stack.push(node.right); + if(progress >= 0.999){ + scope.progressBar.hide(); + }else if(progress < 1){ + scope.progressBar.show(); } } - } - - return visibleGeometry; - -}; - -Potree.PointCloudArena4D.prototype.nodesOnRay = function(nodes, ray){ - var nodesOnRay = []; - - var _ray = ray.clone(); - for(var i = 0; i < nodes.length; i++){ - var node = nodes[i].node; - //var inverseWorld = new THREE.Matrix4().getInverse(node.matrixWorld); - var sphere = node.boundingSphere.clone().applyMatrix4(node.matrixWorld); - var box = node.boundingBox.clone().applyMatrix4(node.matrixWorld); + scope.volumeTool.update(); + scope.transformationTool.update(); + scope.profileTool.update(); - if(_ray.isIntersectionBox(box)){ - //if(_ray.isIntersectionSphere(sphere)){ - nodesOnRay.push(node); - } - } - - return nodesOnRay; -}; - -Potree.PointCloudArena4D.prototype.pick = function(renderer, camera, ray, params){ - - var params = params || {}; - var pickWindowSize = params.pickWindowSize || 17; - - var nodes = this.nodesOnRay(this.visibleNodes, ray); - - if(nodes.length === 0){ - return null; - } - - var width = Math.ceil(renderer.domElement.clientWidth); - var height = Math.ceil(renderer.domElement.clientHeight); - - var pixelPos = new THREE.Vector3().addVectors(camera.position, ray.direction).project(camera); - pixelPos.addScalar(1).multiplyScalar(0.5); - pixelPos.x *= width; - pixelPos.y *= height; - - if(!this.pickTarget){ - this.pickTarget = new THREE.WebGLRenderTarget( - 1, 1, - { minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat } - ); - }else if(this.pickTarget.width != width || this.pickTarget.height != height){ - this.pickTarget.dispose(); - this.pickTarget = new THREE.WebGLRenderTarget( - 1, 1, - { minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat } - ); - } - this.pickTarget.setSize(width, height); - - // setup pick material. - // use the same point size functions as the main material to get the same point sizes. - if(!this.pickMaterial){ - this.pickMaterial = new Potree.PointCloudMaterial({treeType: Potree.TreeType.KDTREE}); - this.pickMaterial.pointColorType = Potree.PointColorType.POINT_INDEX; - this.pickMaterial.pointSizeType = Potree.PointSizeType.FIXED; - } - - this.pickMaterial.pointSizeType = this.material.pointSizeType; - this.pickMaterial.size = this.material.size; - - if(this.pickMaterial.pointSizeType === Potree.PointSizeType.ADAPTIVE){ - this.updateVisibilityTexture(this.pickMaterial, nodes); - } - - this.pickMaterial.fov = this.material.fov; - this.pickMaterial.screenWidth = this.material.screenWidth; - this.pickMaterial.screenHeight = this.material.screenHeight; - this.pickMaterial.spacing = this.material.spacing; - this.pickMaterial.near = this.material.near; - this.pickMaterial.far = this.material.far; - this.pickMaterial.levels = this.material.levels; - this.pickMaterial.pointShape = this.material.pointShape; - - - - var _gl = renderer.context; - - _gl.enable(_gl.SCISSOR_TEST); - _gl.scissor(pixelPos.x - (pickWindowSize - 1) / 2, pixelPos.y - (pickWindowSize - 1) / 2,pickWindowSize,pickWindowSize); - _gl.disable(_gl.SCISSOR_TEST); - - var material = this.pickMaterial; - - renderer.setRenderTarget( this.pickTarget ); - - renderer.state.setDepthTest( material.depthTest ); - renderer.state.setDepthWrite( material.depthWrite ); - renderer.state.setBlending( THREE.NoBlending ); - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - //TODO: UGLY HACK CHAMPIONSHIP SUBMISSION!! drawing first node does not work properly so we draw it twice. - if(nodes.length > 0){ - nodes.push(nodes[0]); - } - - for(var i = 0; i < nodes.length; i++){ - var object = nodes[i]; - var geometry = object.geometry; - - if(!geometry.attributes.indices.buffer){ - continue; - } - material.pcIndex = i; + var clipBoxes = []; - if(material.program){ - var program = material.program.program; - _gl.useProgram( program ); - //_gl.disable( _gl.BLEND ); + for(var i = 0; i < scope.profileTool.profiles.length; i++){ + var profile = scope.profileTool.profiles[i]; - var attributePointer = _gl.getAttribLocation(program, "indices"); - var attributeSize = 4; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.attributes.indices.buffer ); - //if(!bufferSubmitted){ - // _gl.bufferData( _gl.ARRAY_BUFFER, new Uint8Array(geometry.attributes.indices.array), _gl.STATIC_DRAW ); - // bufferSubmitted = true; - //} - _gl.enableVertexAttribArray( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.UNSIGNED_BYTE, true, 0, 0 ); - - _gl.uniform1f(material.program.uniforms.pcIndex, material.pcIndex); - } - - renderer.renderBufferDirect(camera, [], null, material, geometry, object); - } - - - - var pickWindowSize = 17; - var pixelCount = pickWindowSize * pickWindowSize; - var buffer = new ArrayBuffer(pixelCount*4); - var pixels = new Uint8Array(buffer); - var ibuffer = new Uint32Array(buffer); - renderer.context.readPixels( - pixelPos.x - (pickWindowSize-1) / 2, pixelPos.y - (pickWindowSize-1) / 2, - pickWindowSize, pickWindowSize, - renderer.context.RGBA, renderer.context.UNSIGNED_BYTE, pixels); + for(var j = 0; j < profile.boxes.length; j++){ + var box = profile.boxes[j]; + box.updateMatrixWorld(); + var boxInverse = new THREE.Matrix4().getInverse(box.matrixWorld); + clipBoxes.push(boxInverse); + } + } - // find closest hit inside pixelWindow boundaries - var min = Number.MAX_VALUE; - var hit = null; - //console.log("finding closest hit"); - for(var u = 0; u < pickWindowSize; u++){ - for(var v = 0; v < pickWindowSize; v++){ - var offset = (u + v*pickWindowSize); - var distance = Math.pow(u - (pickWindowSize-1) / 2, 2) + Math.pow(v - (pickWindowSize-1) / 2, 2); - - var pcIndex = pixels[4*offset + 3]; - pixels[4*offset + 3] = 0; - var pIndex = ibuffer[offset]; + for(var i = 0; i < scope.volumeTool.volumes.length; i++){ + var volume = scope.volumeTool.volumes[i]; - if((pIndex !== 0 || pcIndex !== 0) && distance < min){ - - hit = { - pIndex: pIndex, - pcIndex: pcIndex - }; - min = distance; + if(volume.clip){ + volume.updateMatrixWorld(); + var boxInverse = new THREE.Matrix4().getInverse(volume.matrixWorld); + + clipBoxes.push(boxInverse); } } - } - - if(hit){ - var point = {}; - var pc = nodes[hit.pcIndex]; - var attributes = pc.geometry.attributes; - for (var property in attributes) { - if (attributes.hasOwnProperty(property)) { - var values = pc.geometry.attributes[property]; - - if(property === "position"){ - var positionArray = pc.geometry.attributes.position.array; - var x = positionArray[3*hit.pIndex+0]; - var y = positionArray[3*hit.pIndex+1]; - var z = positionArray[3*hit.pIndex+2]; - var position = new THREE.Vector3(x, y, z); - position.applyMatrix4(this.matrixWorld); + for(var i = 0; i < scope.pointclouds.length; i++){ + scope.pointclouds[i].material.setClipBoxes(clipBoxes); + } + + {// update annotations + var distances = []; + for(var i = 0; i < scope.annotations.length; i++){ + var ann = scope.annotations[i]; + var screenPos = ann.position.clone().project(scope.camera); - point[property] = position; - }else if(property === "indices"){ + screenPos.x = scope.renderArea.clientWidth * (screenPos.x + 1) / 2; + screenPos.y = scope.renderArea.clientHeight * (1 - (screenPos.y + 1) / 2); + ann.domElement.style.left = Math.floor(screenPos.x - ann.domElement.clientWidth / 2) + "px"; + ann.domElement.style.top = Math.floor(screenPos.y) + "px"; + + //ann.domDescription.style.left = screenPos.x - ann.domDescription.clientWidth / 2 + 10; + //ann.domDescription.style.top = screenPos.y + 30; + + distances.push({annotation: ann, distance: screenPos.z}); + + if(-1 > screenPos.z || screenPos.z > 1){ + ann.domElement.style.display = "none"; }else{ - if(values.itemSize === 1){ - point[property] = values.array[i + j]; - }else{ - var value = []; - for(var j = 0; j < values.itemSize; j++){ - value.push(values.array[i*values.itemSize + j]); - } - point[property] = value; - } + ann.domElement.style.display = "initial"; + } + } + distances.sort(function(a,b){return b.distance - a.distance}); + for(var i = 0; i < distances.length; i++){ + var ann = distances[i].annotation; + ann.domElement.style.zIndex = "" + i; + if(ann.descriptionVisible){ + ann.domElement.style.zIndex += 100; } } } + if(scope.showDebugInfos){ + scope.infos.set("camera.position", "camera.position: " + + viewer.camera.position.x.toFixed(2) + + ", " + viewer.camera.position.y.toFixed(2) + + ", " + viewer.camera.position.z.toFixed(2) + ); + } - return point; - }else{ - return null; + if(scope.mapView){ + scope.mapView.update(delta, scope.camera); + } + + TWEEN.update(timestamp); + + scope.dispatchEvent({"type": "update", "delta": delta, "timestamp": timestamp}); } -}; + + + + + + + + + + + + + + + + -Potree.PointCloudArena4D.prototype.updateVisibilityTexture = function(material, visibleNodes){ - if(!material){ - return; - } - - var texture = material.visibleNodesTexture; - var data = texture.image.data; - - // copy array - visibleNodes = visibleNodes.slice(); - // sort by level and number - var sort = function(a, b){ - var la = a.pcoGeometry.level; - var lb = b.pcoGeometry.level; - var na = a.pcoGeometry.number; - var nb = b.pcoGeometry.number; - if(la != lb) return la - lb; - if(na < nb) return -1; - if(na > nb) return 1; - return 0; +//------------------------------------------------------------------------------------ +// Renderers +//------------------------------------------------------------------------------------ + + var PotreeRenderer = function(){ + + this.render = function(){ + {// resize + var width = scope.renderArea.clientWidth; + var height = scope.renderArea.clientHeight; + var aspect = width / height; + + scope.camera.aspect = aspect; + scope.camera.updateProjectionMatrix(); + + scope.renderer.setSize(width, height); + } + + + // render skybox + if(scope.showSkybox){ + skybox.camera.rotation.copy(scope.camera.rotation); + scope.renderer.render(skybox.scene, skybox.camera); + }else{ + scope.renderer.render(scope.sceneBG, scope.cameraBG); + } + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + if(pointcloud.originalMaterial){ + pointcloud.material = pointcloud.originalMaterial; + } + + var bbWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrixWorld); + + pointcloud.material.size = scope.pointSize; + pointcloud.material.opacity = scope.opacity; + pointcloud.material.pointColorType = scope.pointColorType; + pointcloud.material.pointSizeType = scope.pointSizeType; + pointcloud.material.pointShape = (scope.quality === "Circles") ? Potree.PointShape.CIRCLE : Potree.PointShape.SQUARE; + pointcloud.material.interpolate = (scope.quality === "Interpolation"); + pointcloud.material.weighted = false; + } + + // render scene + scope.renderer.render(scope.scene, scope.camera); + scope.renderer.render(scope.scenePointCloud, scope.camera); + + scope.profileTool.render(); + scope.volumeTool.render(); + + scope.renderer.clearDepth(); + scope.measuringTool.render(); + scope.transformationTool.render(); + }; }; - visibleNodes.sort(sort); - - var visibleNodeNames = []; - for(var i = 0; i < visibleNodes.length; i++){ - //visibleNodeNames[visibleNodes[i].pcoGeometry.number] = true; - visibleNodeNames.push(visibleNodes[i].pcoGeometry.number); - } - - for(var i = 0; i < visibleNodes.length; i++){ - var node = visibleNodes[i]; + var potreeRenderer = new PotreeRenderer(); + + // high quality rendering using splats + var highQualityRenderer = null; + var HighQualityRenderer = function(){ + + var depthMaterial = null; + var attributeMaterial = null; + var normalizationMaterial = null; - var b1 = 0; // children - var b2 = 0; // offset to first child - var b3 = 0; // split + var rtDepth; + var rtNormalize; - if(node.pcoGeometry.left && visibleNodeNames.indexOf(node.pcoGeometry.left.number) > 0){ - b1 += 1; - b2 = visibleNodeNames.indexOf(node.pcoGeometry.left.number) - i; + var initHQSPlats = function(){ + if(depthMaterial != null){ + return; + } + + depthMaterial = new Potree.PointCloudMaterial(); + attributeMaterial = new Potree.PointCloudMaterial(); + + depthMaterial.pointColorType = Potree.PointColorType.DEPTH; + depthMaterial.pointShape = Potree.PointShape.CIRCLE; + depthMaterial.interpolate = false; + depthMaterial.weighted = false; + depthMaterial.minSize = 2; + + attributeMaterial.pointShape = Potree.PointShape.CIRCLE; + attributeMaterial.interpolate = false; + attributeMaterial.weighted = true; + attributeMaterial.minSize = 2; + + rtDepth = new THREE.WebGLRenderTarget( 1024, 1024, { + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.FloatType + } ); + + rtNormalize = new THREE.WebGLRenderTarget( 1024, 1024, { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.FloatType + } ); + + var uniformsNormalize = { + depthMap: { type: "t", value: rtDepth }, + texture: { type: "t", value: rtNormalize } + }; + + normalizationMaterial = new THREE.ShaderMaterial({ + uniforms: uniformsNormalize, + vertexShader: Potree.Shaders["normalize.vs"], + fragmentShader: Potree.Shaders["normalize.fs"] + }); } - if(node.pcoGeometry.right && visibleNodeNames.indexOf(node.pcoGeometry.right.number) > 0){ - b1 += 2; - b2 = (b2 === 0) ? visibleNodeNames.indexOf(node.pcoGeometry.right.number) - i : b2; + + var resize = function(width, height){ + if(rtDepth.width == width && rtDepth.height == height){ + return; + } + + rtDepth.dispose(); + rtNormalize.dispose(); + + scope.camera.aspect = width / height; + scope.camera.updateProjectionMatrix(); + + scope.renderer.setSize(width, height); + rtDepth.setSize(width, height); + rtNormalize.setSize(width, height); + }; + + // render with splats + this.render = function(renderer){ + + var width = scope.renderArea.clientWidth; + var height = scope.renderArea.clientHeight; + + initHQSPlats(); + + resize(width, height); + + + scope.renderer.clear(); + if(scope.showSkybox){ + skybox.camera.rotation.copy(scope.camera.rotation); + scope.renderer.render(skybox.scene, skybox.camera); + }else{ + scope.renderer.render(scope.sceneBG, scope.cameraBG); + } + scope.renderer.render(scope.scene, scope.camera); + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + + depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x; + attributeMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x; + + var originalMaterial = pointcloud.material; + + {// DEPTH PASS + depthMaterial.size = scope.pointSize; + depthMaterial.pointSizeType = scope.pointSizeType; + depthMaterial.screenWidth = width; + depthMaterial.screenHeight = height; + depthMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture; + depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x; + depthMaterial.fov = scope.camera.fov * (Math.PI / 180); + depthMaterial.spacing = pointcloud.pcoGeometry.spacing; + depthMaterial.near = scope.camera.near; + depthMaterial.far = scope.camera.far; + depthMaterial.heightMin = scope.heightMin; + depthMaterial.heightMax = scope.heightMax; + depthMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture; + depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x; + depthMaterial.bbSize = pointcloud.material.bbSize; + depthMaterial.treeType = pointcloud.material.treeType; + depthMaterial.uniforms.classificationLUT.value = pointcloud.material.uniforms.classificationLUT.value; + + scope.scenePointCloud.overrideMaterial = depthMaterial; + scope.renderer.clearTarget( rtDepth, true, true, true ); + scope.renderer.render(scope.scenePointCloud, scope.camera, rtDepth); + scope.scenePointCloud.overrideMaterial = null; + } + + {// ATTRIBUTE PASS + attributeMaterial.size = scope.pointSize; + attributeMaterial.pointSizeType = scope.pointSizeType; + attributeMaterial.screenWidth = width; + attributeMaterial.screenHeight = height; + attributeMaterial.pointColorType = scope.pointColorType; + attributeMaterial.depthMap = rtDepth; + attributeMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture; + attributeMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x; + attributeMaterial.fov = scope.camera.fov * (Math.PI / 180); + attributeMaterial.uniforms.blendHardness.value = pointcloud.material.uniforms.blendHardness.value; + attributeMaterial.uniforms.blendDepthSupplement.value = pointcloud.material.uniforms.blendDepthSupplement.value; + attributeMaterial.spacing = pointcloud.pcoGeometry.spacing; + attributeMaterial.near = scope.camera.near; + attributeMaterial.far = scope.camera.far; + attributeMaterial.heightMin = scope.heightMin; + attributeMaterial.heightMax = scope.heightMax; + attributeMaterial.intensityMin = pointcloud.material.intensityMin; + attributeMaterial.intensityMax = pointcloud.material.intensityMax; + attributeMaterial.setClipBoxes(pointcloud.material.clipBoxes); + attributeMaterial.clipMode = pointcloud.material.clipMode; + attributeMaterial.bbSize = pointcloud.material.bbSize; + attributeMaterial.treeType = pointcloud.material.treeType; + attributeMaterial.uniforms.classificationLUT.value = pointcloud.material.uniforms.classificationLUT.value; + + scope.scenePointCloud.overrideMaterial = attributeMaterial; + scope.renderer.clearTarget( rtNormalize, true, true, true ); + scope.renderer.render(scope.scenePointCloud, scope.camera, rtNormalize); + scope.scenePointCloud.overrideMaterial = null; + + pointcloud.material = originalMaterial; + } + } + + if(scope.pointclouds.length > 0){ + {// NORMALIZATION PASS + normalizationMaterial.uniforms.depthMap.value = rtDepth; + normalizationMaterial.uniforms.texture.value = rtNormalize; + Potree.utils.screenPass.render(scope.renderer, normalizationMaterial); + } + + scope.volumeTool.render(); + scope.renderer.clearDepth(); + scope.profileTool.render(); + scope.measuringTool.render(); + scope.transformationTool.render(); + } + + } + }; + + + + var edlRenderer = null; + var EDLRenderer = function(){ + + var edlMaterial = null; + var attributeMaterials = []; + + //var depthTexture = null; + + var rtColor = null; + var gl = scope.renderer.context; + + var initEDL = function(){ + if(edlMaterial != null){ + return; + } + + //var depthTextureExt = gl.getExtension("WEBGL_depth_texture"); + + edlMaterial = new Potree.EyeDomeLightingMaterial(); + + + rtColor = new THREE.WebGLRenderTarget( 1024, 1024, { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.FloatType, + //type: THREE.UnsignedByteType, + //depthBuffer: false, + //stencilBuffer: false + } ); + + }; + + var resize = function(){ + var width = scope.renderArea.clientWidth; + var height = scope.renderArea.clientHeight; + var aspect = width / height; + + var needsResize = (rtColor.width != width || rtColor.height != height); + + // disposal will be unnecessary once this fix made it into three.js master: + // https://github.com/mrdoob/three.js/pull/6355 + if(needsResize){ + rtColor.dispose(); + } + + scope.camera.aspect = aspect; + scope.camera.updateProjectionMatrix(); + + scope.renderer.setSize(width, height); + rtColor.setSize(width, height); } + + this.render = function(){ - if(node.pcoGeometry.split === "X"){ - b3 = 1; - }else if(node.pcoGeometry.split === "Y"){ - b3 = 2; - }else if(node.pcoGeometry.split === "Z"){ - b3 = 4; + initEDL(); + + resize(); + + scope.renderer.clear(); + if(scope.showSkybox){ + skybox.camera.rotation.copy(scope.camera.rotation); + scope.renderer.render(skybox.scene, skybox.camera); + }else{ + scope.renderer.render(scope.sceneBG, scope.cameraBG); + } + scope.renderer.render(scope.scene, scope.camera); + + var originalMaterials = []; + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + var width = scope.renderArea.clientWidth; + var height = scope.renderArea.clientHeight; + + if(attributeMaterials.length <= i ){ + var attributeMaterial = new Potree.PointCloudMaterial(); + + attributeMaterial.pointShape = Potree.PointShape.CIRCLE; + attributeMaterial.interpolate = false; + attributeMaterial.weighted = false; + attributeMaterial.minSize = 2; + attributeMaterial.useLogarithmicDepthBuffer = false; + attributeMaterial.useEDL = true; + attributeMaterials.push(attributeMaterial); + } + var attributeMaterial = attributeMaterials[i]; + + var octreeSize = pointcloud.pcoGeometry.boundingBox.size().x; + + originalMaterials.push(pointcloud.material); + + scope.renderer.clearTarget( rtColor, true, true, true ); + + {// COLOR & DEPTH PASS + attributeMaterial = pointcloud.material; + attributeMaterial.pointShape = Potree.PointShape.CIRCLE; + attributeMaterial.interpolate = false; + attributeMaterial.weighted = false; + attributeMaterial.minSize = 2; + attributeMaterial.useLogarithmicDepthBuffer = false; + attributeMaterial.useEDL = true; + + attributeMaterial.size = scope.pointSize; + attributeMaterial.pointSizeType = scope.pointSizeType; + attributeMaterial.screenWidth = width; + attributeMaterial.screenHeight = height; + attributeMaterial.pointColorType = scope.pointColorType; + attributeMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture; + attributeMaterial.uniforms.octreeSize.value = octreeSize; + attributeMaterial.fov = scope.camera.fov * (Math.PI / 180); + attributeMaterial.spacing = pointcloud.pcoGeometry.spacing; + attributeMaterial.near = scope.camera.near; + attributeMaterial.far = scope.camera.far; + attributeMaterial.heightMin = scope.heightMin; + attributeMaterial.heightMax = scope.heightMax; + attributeMaterial.intensityMin = pointcloud.material.intensityMin; + attributeMaterial.intensityMax = pointcloud.material.intensityMax; + attributeMaterial.setClipBoxes(pointcloud.material.clipBoxes); + attributeMaterial.clipMode = pointcloud.material.clipMode; + attributeMaterial.bbSize = pointcloud.material.bbSize; + attributeMaterial.treeType = pointcloud.material.treeType; + attributeMaterial.uniforms.classificationLUT.value = pointcloud.material.uniforms.classificationLUT.value; + + pointcloud.material = attributeMaterial; + for(var j = 0; j < pointcloud.visibleNodes.length; j++){ + var node = pointcloud.visibleNodes[j]; + if(pointcloud instanceof Potree.PointCloudOctree){ + node.sceneNode.material = attributeMaterial; + }else if(pointcloud instanceof Potree.PointCloudArena4D){ + node.material = attributeMaterial; + } + } + } + + } + + scope.renderer.render(scope.scenePointCloud, scope.camera, rtColor); + // bit of a hack here. The EDL pass will mess up the text of the volume tool + // so volume tool is rendered again afterwards + scope.volumeTool.render(rtColor); + + for(var i = 0; i < scope.pointclouds.length; i++){ + var pointcloud = scope.pointclouds[i]; + var originalMaterial = originalMaterials[i]; + pointcloud.material = originalMaterial; + for(var j = 0; j < pointcloud.visibleNodes.length; j++){ + var node = pointcloud.visibleNodes[j]; + if(pointcloud instanceof Potree.PointCloudOctree){ + node.sceneNode.material = originalMaterial; + }else if(pointcloud instanceof Potree.PointCloudArena4D){ + node.material = originalMaterial; + } + } + } + + if(scope.pointclouds.length > 0){ + { // EDL OCCLUSION PASS + edlMaterial.uniforms.screenWidth.value = width; + edlMaterial.uniforms.screenHeight.value = height; + edlMaterial.uniforms.near.value = scope.camera.near; + edlMaterial.uniforms.far.value = scope.camera.far; + edlMaterial.uniforms.colorMap.value = rtColor; + edlMaterial.uniforms.expScale.value = scope.camera.far; + edlMaterial.uniforms.edlScale.value = scope.edlScale; + edlMaterial.uniforms.radius.value = scope.edlRadius; + edlMaterial.uniforms.opacity.value = scope.opacity; + edlMaterial.depthTest = true; + edlMaterial.depthWrite = true; + edlMaterial.transparent = true; + + Potree.utils.screenPass.render(scope.renderer, edlMaterial); + } + + scope.renderer.render(scope.scene, scope.camera); + + scope.profileTool.render(); + scope.volumeTool.render(); + scope.renderer.clearDepth(); + scope.measuringTool.render(); + scope.transformationTool.render(); + } + + } + }; + + //var toggleMessage = 0; + + function loop(timestamp) { + requestAnimationFrame(loop); + //var start = new Date().getTime(); + scope.update(clock.getDelta(), timestamp); + //var end = new Date().getTime(); + //var duration = end - start; + //toggleMessage++; + //if(toggleMessage > 30){ + // document.getElementById("lblMessage").innerHTML = "update: " + duration + "ms"; + // toggleMessage = 0; + //} - data[i*3+0] = b1; - data[i*3+1] = b2; - data[i*3+2] = b3; - } - + if(scope.useEDL && Potree.Features.SHADER_EDL.isSupported()){ + if(!edlRenderer){ + edlRenderer = new EDLRenderer(); + } + edlRenderer.render(scope.renderer); + }else if(scope.quality === "Splats"){ + if(!highQualityRenderer){ + highQualityRenderer = new HighQualityRenderer(); + } + highQualityRenderer.render(scope.renderer); + }else{ + potreeRenderer.render(); + } + }; + + scope.initThree(); + //scope.initGUI(); - texture.needsUpdate = true; + // set defaults + scope.setPointSize(1); + scope.setFOV(60); + scope.setOpacity(1); + scope.setEDLEnabled(false); + scope.setEDLRadius(2); + scope.setEDLStrength(1); + scope.setClipMode(Potree.ClipMode.HIGHLIGHT_INSIDE); + scope.setPointBudget(1*1000*1000); + scope.setShowBoundingBox(false); + //scope.setShowDebugInfos(false); + //scope.setShowStats(false); + scope.setFreeze(false); + scope.setNavigationMode("Orbit"); + + // start rendering! + requestAnimationFrame(loop); }; +Potree.Viewer.prototype = Object.create( THREE.EventDispatcher.prototype ); + + + -Object.defineProperty(Potree.PointCloudArena4D.prototype, "progress", { - get: function(){ - if(this.pcoGeometry.root){ - return Potree.PointCloudArena4DGeometryNode.nodesLoading > 0 ? 0 : 1; - }else{ - return 0; - } - } -}); -//Potree.PointCloudArena4D.prototype.updateMatrixWorld = function( force ){ -// //node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); -// -// if ( this.matrixAutoUpdate === true ) this.updateMatrix(); -// -// if ( this.matrixWorldNeedsUpdate === true || force === true ) { -// -// if ( this.parent === undefined ) { -// -// this.matrixWorld.copy( this.matrix ); -// -// } else { -// -// this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); -// -// } -// -// this.matrixWorldNeedsUpdate = false; -// -// force = true; -// -// } -//}; -Potree.PointCloudArena4DGeometryNode = function(){ - var scope = this; - this.left = null; - this.right = null; - this.boundingBox = null; - this.number = null; - this.pcoGeometry = null; - this.loaded = false; - this.numPoints = 0; - this.level = 0; -}; -Potree.PointCloudArena4DGeometryNode.nodesLoading = 0; -Potree.PointCloudArena4DGeometryNode.prototype.load = function(){ - if(this.loaded || this.loading){ - return; - } +Potree.Viewer.Profile = function(viewer, element){ + var scope = this; + + this.viewer = viewer; + this.enabled = true; + this.element = element; + this.currentProfile = null; + this.requests = []; + this.pointsProcessed = 0; + this.margin = {top: 0, right: 0, bottom: 20, left: 40}; + this.maximized = false; + this.threshold = 20*1000; + + + $('#closeProfileContainer').click(function(){ + scope.hide(); + scope.enabled = false; + }); - if(Potree.PointCloudArena4DGeometryNode.nodesLoading >= 5){ - return; - } + $('#profile_toggle_size_button').click(function(){ + scope.maximized = !scope.maximized; - Potree.PointCloudArena4DGeometryNode.nodesLoading++; + if(scope.maximized){ + $('#profile_window').css("height", "100%"); + }else{ + $('#profile_window').css("height", "30%"); + } + }); - var url = this.pcoGeometry.url + "?node=" + this.number; - var xhr = new XMLHttpRequest(); - xhr.open("GET", url, true); - xhr.responseType = "arraybuffer"; + this.show = function(){ + $('#profile_window').fadeIn(); + scope.enabled = true; + }; - var scope = this; + this.hide = function(){ + $('#profile_window').fadeOut(); + }; - xhr.onreadystatechange = function(){ - if(!(xhr.readyState === 4 && xhr.status === 200)){ - return; + this.cancel = function(){ + for(var i = 0; i < scope.requests.length; i++){ + scope.requests[i].cancel(); } - var buffer = xhr.response; + scope.requests = []; + }; + + this.getLAS = function(){ + var points = scope.points; + var boundingBox = new THREE.Box3(); + + for(var i = 0; i < points.length; i++){ + var point = points[i]; + var position = new THREE.Vector3(point.x, point.y, point.z); + + boundingBox.expandByPoint(position); + } + var offset = boundingBox.min.clone(); + var diagonal = boundingBox.min.distanceTo(boundingBox.max); + var scale = new THREE.Vector3(0.01, 0.01, 0.01); + if(diagonal > 100*1000){ + scale = new THREE.Vector3(0.01, 0.01, 0.01); + }else{ + scale = new THREE.Vector3(0.001, 0.001, 0.001); + } + + var setString = function(string, offset, buffer){ + var view = new Uint8Array(buffer); + + for(var i = 0; i < string.length; i++){ + var charCode = string.charCodeAt(i); + view[offset + i] = charCode; + } + } + + var buffer = new ArrayBuffer(227 + 28 * points.length); var view = new DataView(buffer); - var numPoints = buffer.byteLength / 17; + var u8View = new Uint8Array(buffer); + //var u16View = new Uint16Array(buffer); - var positions = new Float32Array(numPoints*3); - var colors = new Float32Array(numPoints*3); - var indices = new Uint32Array(numPoints); + setString("LASF", 0, buffer); + u8View[24] = 1; + u8View[25] = 2; - for(var i = 0; i < numPoints; i++){ - var x = view.getFloat32(i*17 + 0, true) + scope.boundingBox.min.x; - var y = view.getFloat32(i*17 + 4, true) + scope.boundingBox.min.y; - var z = view.getFloat32(i*17 + 8, true) + scope.boundingBox.min.z; - var r = view.getUint8(i*17 + 12, true) / 256; - var g = view.getUint8(i*17 + 13, true) / 256; - var b = view.getUint8(i*17 + 14, true) / 256; + // system identifier o:26 l:32 + + // generating software o:58 l:32 + setString("potree 1.4", 58, buffer); + + // file creation day of year o:90 l:2 + // file creation year o:92 l:2 + + // header size o:94 l:2 + view.setUint16(94, 227, true); + + // offset to point data o:96 l:4 + view.setUint32(96, 227, true); + + // number of variable length records o:100 l:4 + + // point data record format 104 1 + u8View[104] = 2; + + // point data record length 105 2 + view.setUint16(105, 28, true); + + // number of point records 107 4 + view.setUint32(107, points.length, true); + + // number of points by return 111 20 + + // x scale factor 131 8 + view.setFloat64(131, scale.x, true); + + // y scale factor 139 8 + view.setFloat64(139, scale.y, true); + + // z scale factor 147 8 + view.setFloat64(147, scale.z, true); + + // x offset 155 8 + view.setFloat64(155, offset.x, true); + + // y offset 163 8 + view.setFloat64(163, offset.y, true); + + // z offset 171 8 + view.setFloat64(171, offset.z, true); + + var boffset = 227; + for(var i = 0; i < points.length; i++){ + var point = points[i]; + var position = new THREE.Vector3(point.x, point.y, point.z); - positions[i*3+0] = x; - positions[i*3+1] = y; - positions[i*3+2] = z; + var ux = parseInt((position.x - offset.x) / scale.x); + var uy = parseInt((position.y - offset.y) / scale.y); + var uz = parseInt((position.z - offset.z) / scale.z); - colors[i*3+0] = r; - colors[i*3+1] = g; - colors[i*3+2] = b; + view.setUint32(boffset + 0, ux, true); + view.setUint32(boffset + 4, uy, true); + view.setUint32(boffset + 8, uz, true); - indices[i] = i; + view.setUint16(boffset + 12, (point.intensity), true); + var rt = point.returnNumber; + rt += (point.numberOfReturns << 3); + view.setUint8(boffset + 14, rt); + + // classification + view.setUint8(boffset + 15, point.classification); + // scan angle rank + // user data + // point source id + view.setUint16(boffset + 18, point.pointSourceID); + + view.setUint16(boffset + 20, (point.color[0] * 255), true); + view.setUint16(boffset + 22, (point.color[1] * 255), true); + view.setUint16(boffset + 24, (point.color[2] * 255), true); + + boffset += 28; } - var geometry = new THREE.BufferGeometry(); - geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3)); - geometry.addAttribute("color", new THREE.BufferAttribute(colors, 3)); - geometry.addAttribute("indices", new THREE.BufferAttribute(indices, 1)); - geometry.addAttribute("normal", new THREE.BufferAttribute(new Float32Array(numPoints*3), 3)); - scope.geometry = geometry; - scope.loaded = true; - Potree.PointCloudArena4DGeometryNode.nodesLoading--; + // max x 179 8 + view.setFloat64(179, boundingBox.max.x, true); - geometry.boundingBox = scope.boundingBox; - geometry.boundingSphere = scope.boundingSphere; + // min x 187 8 + view.setFloat64(187, boundingBox.min.x, true); - scope.numPoints = numPoints; + // max y 195 8 + view.setFloat64(195, boundingBox.max.y, true); + + // min y 203 8 + view.setFloat64(203, boundingBox.min.y, true); + + // max z 211 8 + view.setFloat64(211, boundingBox.max.z, true); + + // min z 219 8 + view.setFloat64(219, boundingBox.min.z, true); + + return buffer; }; - xhr.send(null); -}; + this.preparePoints = function(profileProgress){ + + var segments = profileProgress.segments; + if (segments.length === 0){ + return false; + } + + var data = []; + var distance = 0; + var totalDistance = 0; + var minX = Math.max(); + var minY = Math.max(); + var minZ = Math.max(); + var maxX = 0; + var maxY = 0; + var maxZ = 0; + + // Get the same color map as Three + var hr = scope.viewer.getHeightRange(); + var hrGeo = { + min: scope.viewer.toGeo(new THREE.Vector3(0, hr.min, 0)).z, + max: scope.viewer.toGeo(new THREE.Vector3(0, hr.max, 0)).z, + }; + + //var minRange = scope.viewer.toGeo(new THREE.Vector3(0, args.heightMin, 0)); + //var maxRange = scope.viewer.toGeo(new THREE.Vector3(0, args.heightMax, 0)); + var heightRange = hrGeo.max - hrGeo.min; + var colorRange = []; + var colorDomain = []; + + // Read the altitude gradient used in 3D scene + var gradient = viewer.pointclouds[0].material.gradient; + for (var c = 0; c < gradient.length; c++){ + colorDomain.push(hrGeo.min + heightRange * gradient[c][0]); + colorRange.push('#' + gradient[c][1].getHexString()); + } + + // Altitude color map scale + var colorRamp = d3.scale.linear() + .domain(colorDomain) + .range(colorRange) + .clamp(true); + + // Iterate the profile's segments + for(var i = 0; i < segments.length; i++){ + var segment = segments[i]; + var segStartGeo = scope.viewer.toGeo(segment.start); + var segEndGeo = scope.viewer.toGeo(segment.end); + var xOA = segEndGeo.x - segStartGeo.x; + var yOA = segEndGeo.y - segStartGeo.y; + var segmentLength = Math.sqrt(xOA * xOA + yOA * yOA); + var points = segment.points; + + // Iterate the segments' points + for(var j = 0; j < points.numPoints; j++){ + var p = scope.viewer.toGeo(points.position[j]); + // get min/max values + if (p.x < minX) { minX = p.x;} + + if (p.y < minY) { minY = p.y;} + + if (p.z < minZ) { minZ = p.z;} + + if (p.x > maxX) { maxX = p.x;} + + if (p.y < maxY) { maxY = p.y;} + + if (p.z < maxZ) { maxZ = p.z;} + + var xOB = p.x - segStartGeo.x; + var yOB = p.y - segStartGeo.y; + var hypo = Math.sqrt(xOB * xOB + yOB * yOB); + var cosAlpha = (xOA * xOB + yOA * yOB)/(Math.sqrt(xOA * xOA + yOA * yOA) * hypo); + var alpha = Math.acos(cosAlpha); + var dist = hypo * cosAlpha + totalDistance; + if (!isNaN(dist)) { + var d = { }; + d.distance = dist; + d.x = p.x; + d.y = p.y; + d.z = p.z; + d.altitude = p.z; + d.heightColor = colorRamp(p.z); + d.color = points.color ? points.color[j] : [0, 0, 0]; + d.intensity = points.intensity ? points.intensity[j] : 0; + d.classification = points.classification ? points.classification[j] : 0; + d.returnNumber = points.returnNumber ? points.returnNumber[j] : 0; + d.numberOfReturns = points.numberOfReturns ? points.numberOfReturns[j] : 0; + d.pointSourceID = points.pointSourceID ? points.pointSourceID[j] : 0; + data.push(d); + } + } + // Increment distance from the profile start point + totalDistance += segmentLength; + } -Potree.PointCloudArena4DGeometry = function(){ - var scope = this; + var output = { + 'data': data, + 'minX': minX, + 'minY': minY, + 'minZ': minZ, + 'maxX': maxX, + 'maxY': maxY, + 'maxZ': maxZ + }; - this.numPoints = 0; - this.version = 0; - this.boundingBox = null; - this.numNodes = 0; - this.name = null; - this.provider = null; - this.url = null; - this.root = null; - this.levels = 0; - this._spacing = null; - this.pointAttributes = new Potree.PointAttributes([ - "POSITION_CARTESIAN", - "COLOR_PACKED" - ]); -}; + return output; + }; + + this.pointHighlight = function(event){ + + var pointSize = 6; + + // Find the hovered point if applicable + var d = scope.points; + var sx = scope.scaleX; + var sy = scope.scaleY; + var coordinates = [0, 0]; + coordinates = d3.mouse(this); + var xs = coordinates[0]; + var ys = coordinates[1]; + + // Fix FF vs Chrome discrepancy + //if(navigator.userAgent.indexOf("Firefox") == -1 ) { + // xs = xs - scope.margin.left; + // ys = ys - scope.margin.top; + //} + var hP = []; + var tol = pointSize; -Potree.PointCloudArena4DGeometry.load = function(url, callback){ + for (var i=0; i < d.length; i++){ + if(sx(d[i].distance) < xs + tol && sx(d[i].distance) > xs - tol && sy(d[i].altitude) < ys + tol && sy(d[i].altitude) > ys -tol){ + hP.push(d[i]); + } + } - var xhr = new XMLHttpRequest(); - xhr.open('GET', url + "?info", true); + if(hP.length > 0){ + var p = hP[0]; + this.hoveredPoint = hP[0]; + if(navigator.userAgent.indexOf("Firefox") == -1 ) { + cx = scope.scaleX(p.distance) + scope.margin.left; + cy = scope.scaleY(p.altitude) + scope.margin.top; + } else { + cx = scope.scaleX(p.distance); + cy = scope.scaleY(p.altitude); + } + + //cx -= pointSize / 2; + cy -= pointSize / 2; + + var svg = d3.select("svg"); + d3.selectAll("rect").remove(); + var rectangle = svg.append("rect") + .attr("x", cx) + .attr("y", cy) + .attr("id", p.id) + .attr("width", pointSize) + .attr("height", pointSize) + .style("fill", 'yellow'); + + + var marker = $("#profile_selection_marker"); + marker.css("display", "initial"); + marker.css("left", cx + "px"); + marker.css("top", cy + "px"); + marker.css("width", pointSize + "px"); + marker.css("height", pointSize + "px"); + marker.css("background-color", "yellow"); + + //var html = 'x: ' + Math.round(10 * p.x) / 10 + ' y: ' + Math.round(10 * p.y) / 10 + ' z: ' + Math.round( 10 * p.altitude) / 10 + ' - '; + //html += i18n.t('tools.classification') + ': ' + p.classificationCode + ' - '; + //html += i18n.t('tools.intensity') + ': ' + p.intensityCode; + + var html = 'x: ' + Math.round(10 * p.x) / 10 + ' y: ' + Math.round(10 * p.y) / 10 + ' z: ' + Math.round( 10 * p.altitude) / 10 + ' - '; + html += "offset: " + p.distance.toFixed(3) + ' - '; + html += "Classification: " + p.classification + ' - '; + html += "Intensity: " + p.intensity; + + $('#profileInfo').css('color', 'yellow'); + $('#profileInfo').html(html); + + } else { + d3.selectAll("rect").remove(); + $('#profileInfo').html(""); + + var marker = $("#profile_selection_marker"); + marker.css("display", "none"); + } + }; + + this.strokeColor = function (d) { + var material = scope.viewer.getMaterial(); + if (material === Potree.PointColorType.RGB) { + //return d.color; + return 'rgb(' + (d.color[0] * 100) + '%,' + (d.color[1] * 100) + '%,' + (d.color[2] * 100) + '%)'; + } else if (material === Potree.PointColorType.INTENSITY) { + //return d.intensity; + return 'rgb(' + points.intensity + '%,' + points.intensity + '%,' + points.intensity + '%)'; + } else if (material === Potree.PointColorType.CLASSIFICATION) { + var classif = scope.viewer.pointclouds[0].material.classification; + if (typeof classif[d.classification] != 'undefined'){ + var color = 'rgb(' + classif[d.classification].x * 100 + '%,'; + color += classif[d.classification].y * 100 + '%,'; + color += classif[d.classification].z * 100 + '%)'; + return color; + } else { + return 'rgb(255,255,255)'; + } + } else if (material === Potree.PointColorType.HEIGHT) { + return d.heightColor; + } else if (material === Potree.PointColorType.RETURN_NUMBER) { + + if(d.numberOfReturns === 1){ + return 'rgb(255, 255, 0)'; + }else{ + if(d.returnNumber === 1){ + return 'rgb(255, 0, 0)'; + }else if(d.returnNumber === d.numberOfReturns){ + return 'rgb(0, 0, 255)'; + }else{ + return 'rgb(0, 255, 0)'; + } + } + + return d.heightColor; + } else { + return d.color; + } + }; - xhr.onreadystatechange = function(){ - try{ - if(xhr.readyState === 4 && xhr.status === 200){ - var response = JSON.parse(xhr.responseText); + this.redraw = function(){ + scope.draw(scope.currentProfile); + }; + + this.draw = function(profile){ + // TODO handle all pointclouds + // TODO are the used closures safe for garbage collection? + + if(!scope.enabled){ + return; + } + if(profile){ + if(profile.points.length < 2){ + return; + } + }else{ + return; + } + if(scope.viewer.pointclouds.length === 0){ + return; + } + + + scope.currentProfile = profile; + + if(!scope.__drawData){ + scope.__drawData = {}; + } + scope.points = []; + scope.rangeX = [Infinity, -Infinity]; + scope.rangeY = [Infinity, -Infinity]; + + scope.pointsProcessed = 0; + + for(var i = 0; i < scope.requests.length; i++){ + scope.requests[i].cancel(); + } + scope.requests = []; + + var drawPoints = function(points, rangeX, rangeY) { + + + var mileage = 0; + for(var i = 0; i < profile.points.length; i++){ + var point = profile.points[i]; + var pointGeo = scope.viewer.toGeo(point); - var geometry = new Potree.PointCloudArena4DGeometry(); - geometry.url = url; - geometry.name = response.Name; - geometry.provider = response.Provider; - geometry.numNodes = response.Nodes; - geometry.numPoints = response.Points; - geometry.version = response.Version; - geometry.boundingBox = new THREE.Box3( - new THREE.Vector3().fromArray(response.BoundingBox.slice(0,3)), - new THREE.Vector3().fromArray(response.BoundingBox.slice(3,6)) - ); - if(response.Spacing){ - geometry.spacing = response.Spacing; + if(i > 0){ + var previousGeo = scope.viewer.toGeo(profile.points[i-1]); + var dx = pointGeo.x - previousGeo.x; + var dy = pointGeo.y - previousGeo.y; + var distance = Math.sqrt(dx * dx + dy * dy); + mileage += distance; } - var offset = geometry.boundingBox.min.clone().multiplyScalar(-1); + var radius = 4; - geometry.boundingBox.min.add(offset); - geometry.boundingBox.max.add(offset); - geometry.offset = offset; + var cx = scope.scaleX(mileage); + var cy = scope.context.canvas.clientHeight; - var center = geometry.boundingBox.center(); - var radius = geometry.boundingBox.size().length() / 2; - geometry.boundingSphere = new THREE.Sphere(center, radius); + scope.context.beginPath(); + scope.context.arc(cx, cy, radius, 0, 2 * Math.PI, false); + scope.context.fillStyle = '#a22'; + scope.context.fill(); + }; + + + var pointSize = 2; + var i = -1, n = points.length, d, cx, cy; + while (++i < n) { + d = points[i]; + cx = scope.scaleX(d.distance); + cy = scope.scaleY(d.altitude); + scope.context.moveTo(cx, cy); + scope.context.fillStyle = scope.strokeColor(d); + scope.context.fillRect(cx, cy, pointSize, pointSize); + //context.fillStyle = pv.profile.strokeColor(d); + } + }; + + var projectedBoundingBox = null; + + var setupAndDraw = function(){ + var containerWidth = scope.element.clientWidth; + var containerHeight = scope.element.clientHeight; + + var width = containerWidth - (scope.margin.left + scope.margin.right); + var height = containerHeight - (scope.margin.top + scope.margin.bottom); + + scope.scaleX = d3.scale.linear(); + scope.scaleY = d3.scale.linear(); + + var domainProfileWidth = scope.rangeX[1] - scope.rangeX[0]; + var domainProfileHeight = scope.rangeY[1] - scope.rangeY[0]; + var domainRatio = domainProfileWidth / domainProfileHeight; + var rangeProfileWidth = width; + var rangeProfileHeight = height; + var rangeRatio = rangeProfileWidth / rangeProfileHeight; + + if(domainRatio < rangeRatio){ + // canvas scale + var targetWidth = domainProfileWidth * (rangeProfileHeight / domainProfileHeight); + scope.scaleY.range([height, 0]); + scope.scaleX.range([width / 2 - targetWidth / 2, width / 2 + targetWidth / 2]); - geometry.loadHierarchy(); + // axis scale + var domainScale = rangeRatio / domainRatio; + var domainScaledWidth = domainProfileWidth * domainScale; + scope.axisScaleX = d3.scale.linear() + .domain([ + domainProfileWidth / 2 - domainScaledWidth / 2 , + domainProfileWidth / 2 + domainScaledWidth / 2 ]) + .range([0, width]); + scope.axisScaleY = d3.scale.linear() + .domain(scope.rangeY) + .range([height, 0]); + }else{ + // canvas scale + var targetHeight = domainProfileHeight* (rangeProfileWidth / domainProfileWidth); + scope.scaleX.range([0, width]); + scope.scaleY.range([height / 2 + targetHeight / 2, height / 2 - targetHeight / 2]); - callback(geometry); - }else if(xhr.readyState === 4){ - callback(null); + // axis scale + var domainScale = domainRatio / rangeRatio; + var domainScaledHeight = domainProfileHeight * domainScale; + var domainHeightCentroid = (scope.rangeY[1] + scope.rangeY[0]) / 2; + scope.axisScaleX = d3.scale.linear() + .domain(scope.rangeX) + .range([0, width]); + scope.axisScaleY = d3.scale.linear() + .domain([ + domainHeightCentroid - domainScaledHeight / 2 , + domainHeightCentroid + domainScaledHeight / 2 ]) + .range([height, 0]); } - }catch(e){ - callback(null); + scope.scaleX.domain(scope.rangeX); + scope.scaleY.domain(scope.rangeY); + + + + scope.axisZoom = d3.behavior.zoom() + .x(scope.axisScaleX) + .y(scope.axisScaleY) + .scaleExtent([0,128]) + .size([width, height]); + + scope.zoom = d3.behavior.zoom() + .x(scope.scaleX) + .y(scope.scaleY) + .scaleExtent([0,128]) + .size([width, height]) + .on("zoom", function(){ + //var t = zoom.translate(); + //var tx = t[0]; + //var ty = t[1]; + // + //tx = Math.min(tx, 0); + //tx = Math.max(tx, width - projectedBoundingBox.max.x); + //zoom.translate([tx, ty]); + + scope.axisZoom.translate(scope.zoom.translate()); + scope.axisZoom.scale(scope.zoom.scale()); + + svg.select(".x.axis").call(xAxis); + svg.select(".y.axis").call(yAxis); + + scope.context.clearRect(0, 0, width, height); + drawPoints(scope.points, scope.rangeX, scope.rangeY); + }); + + scope.context = d3.select("#profileCanvas") + .attr("width", width) + .attr("height", height) + .call(scope.zoom) + .node().getContext("2d"); + + + //d3.select("svg#profile_draw_container").remove(); + d3.select("svg#profileSVG").selectAll("*").remove(); + + svg = d3.select("svg#profileSVG") + .call(scope.zoom) + .attr("width", (width + scope.margin.left + scope.margin.right).toString()) + .attr("height", (height + scope.margin.top + scope.margin.bottom).toString()) + .attr("transform", "translate(" + scope.margin.left + "," + scope.margin.top + ")") + .on("mousemove", function(){ +// scope.pointHighlight + // TODO implement pointHighlight + }); + //scope.context.canvas.addEventListener("mousemove", scope.pointHighlight); + + d3.select("#profileCanvas") + .on("mousemove", scope.pointHighlight); + + + // Create x axis + var xAxis = d3.svg.axis() + .scale(scope.axisScaleX) + .innerTickSize(-height) + .outerTickSize(5) + .orient("bottom") + .ticks(10, "m"); + + // Create y axis + var yAxis = d3.svg.axis() + .scale(scope.axisScaleY) + .innerTickSize(-width) + .outerTickSize(5) + .orient("left") + .ticks(10, "m"); + + // Append axis to the chart + var gx = svg.append("g") + .attr("class", "x axis") + .call(xAxis); + + svg.append("g") + .attr("class", "y axis") + .call(yAxis); + + if(navigator.userAgent.indexOf("Firefox") == -1 ) { + svg.select(".y.axis").attr("transform", "translate("+ (scope.margin.left).toString() + "," + scope.margin.top.toString() + ")"); + svg.select(".x.axis").attr("transform", "translate(" + scope.margin.left.toString() + "," + (height + scope.margin.top).toString() + ")"); + } else { + svg.select(".x.axis").attr("transform", "translate( 0 ," + height.toString() + ")"); + } + + drawPoints(scope.points, scope.rangeX, scope.rangeY); + + document.getElementById("profile_num_points").innerHTML = Potree.utils.addCommas(scope.pointsProcessed) + " "; + }; + + + for(var i = 0; i < scope.viewer.pointclouds.length; i++){ + var pointcloud = scope.viewer.pointclouds[i]; + var request = pointcloud.getPointsInProfile(profile, null, { + "onProgress": function(event){ + if(!scope.enabled){ + return; + } + + if(!projectedBoundingBox){ + projectedBoundingBox = event.points.projectedBoundingBox; + }else{ + projectedBoundingBox.union(event.points.projectedBoundingBox); + } + + var result = scope.preparePoints(event.points); + var points = result.data; + scope.points = scope.points.concat(points); + + var batchRangeX = [d3.min(points, function(d) { return d.distance; }), d3.max(points, function(d) { return d.distance; })]; + var batchRangeY = [d3.min(points, function(d) { return d.altitude; }), d3.max(points, function(d) { return d.altitude; })]; + + scope.rangeX = [ Math.min(scope.rangeX[0], batchRangeX[0]), Math.max(scope.rangeX[1], batchRangeX[1]) ]; + scope.rangeY = [ Math.min(scope.rangeY[0], batchRangeY[0]), Math.max(scope.rangeY[1], batchRangeY[1]) ]; + + scope.pointsProcessed += result.data.length; + + setupAndDraw(); + + if(scope.pointsProcessed > scope.threshold){ + scope.cancel(); + } + }, + "onFinish": function(event){ + if(!scope.enabled){ + return; + } + }, + "onCancel": function(){ + if(!scope.enabled){ + return; + } + } + }); + + scope.requests.push(request); } }; + + this.setThreshold = function(value){ + scope.threshold = value; - xhr.send(null); - + scope.redraw(); + }; + + var drawOnChange = function(event){ + if(event.profile === scope.currentProfile){ + scope.redraw(); + } + }; + + viewer.profileTool.addEventListener("marker_moved", drawOnChange); + viewer.profileTool.addEventListener("width_changed", drawOnChange); + viewer.addEventListener("material_changed", function(){ + drawOnChange({profile: scope.currentProfile}); + }); + viewer.addEventListener("height_range_changed", function(){ + drawOnChange({profile: scope.currentProfile}); + }); + + var width = document.getElementById('profile_window').clientWidth; + var height = document.getElementById('profile_window').clientHeight; + function resizeLoop(){ + requestAnimationFrame(resizeLoop); + + var newWidth = document.getElementById('profile_window').clientWidth; + var newHeight = document.getElementById('profile_window').clientHeight; + + if(newWidth !== width || newHeight !== height){ + setTimeout(drawOnChange, 50, {profile: scope.currentProfile}); + } + + width = newWidth; + height = newHeight; + }; + requestAnimationFrame(resizeLoop); + + + }; -Potree.PointCloudArena4DGeometry.prototype.loadHierarchy = function(){ - var url = this.url + "?tree"; - var xhr = new XMLHttpRequest(); - xhr.open("GET", url, true); - xhr.responseType = "arraybuffer"; - +// http://epsg.io/ +proj4.defs("UTM10N", "+proj=utm +zone=10 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"); + +Potree.Viewer.MapView = function(viewer){ var scope = this; - xhr.onreadystatechange = function(){ - if(!(xhr.readyState === 4 && xhr.status === 200)){ - return; - } + this.viewer = viewer; - var buffer = xhr.response; - var numNodes = buffer.byteLength / 3; - var view = new DataView(buffer); - var stack = []; - var root = null; + this.webMapService = "WMTS"; + this.mapProjectionName = "EPSG:3857"; + this.mapProjection = proj4.defs(scope.mapProjectionName); + this.sceneProjection = null; + + this.init = function(){ + //scope.setSceneProjection("+proj=utm +zone=10 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"); - var levels = 0; + $( "#potree_map" ).draggable({ handle: $('#potree_map_header') }); + $( "#potree_map" ).resizable(); + //$( "#potree_map" ).css("display", "block"); + $( "#potree_map_toggle" ).css("display", "block"); + + scope.gExtent = new ol.geom.LineString([[0,0], [0,0]]); - var start = new Date().getTime(); - // read hierarchy - for(var i = 0; i < numNodes; i++){ - var mask = view.getUint8(i*3+0, true); - var numPoints = view.getUint16(i*3+1, true); + // EXTENT LAYER + var feature = new ol.Feature(scope.gExtent); + var featureVector = new ol.source.Vector({ + features: [feature] + }); + var visibleBoundsLayer = new ol.layer.Vector({ + source: featureVector, + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }), + stroke: new ol.style.Stroke({ + color: '#0000ff', + width: 2 + }), + image: new ol.style.Circle({ + radius: 3, + fill: new ol.style.Fill({ + color: '#0000ff' + }) + }) + }) + }); + + // CAMERA LAYER + scope.gCamera = new ol.geom.LineString([[0,0], [0,0], [0,0], [0,0]]); + var feature = new ol.Feature(scope.gCamera); + var featureVector = new ol.source.Vector({ + features: [feature] + }); + var cameraLayer = new ol.layer.Vector({ + source: featureVector, + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#0000ff', + width: 2 + }) + }) + }); + + // TOOL DRAWINGS LAYER + scope.toolLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + }), + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 0, 0, 1)' + }), + stroke: new ol.style.Stroke({ + color: 'rgba(255, 0, 0, 1)', + width: 2 + }) + }) + }); + + // SOURCES EXTENT LAYER + scope.sourcesLayer = new ol.layer.Vector({ + source: new ol.source.Vector({}), + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 0, 0, 0.1)' + }), + stroke: new ol.style.Stroke({ + color: 'rgba(0, 0, 150, 1)', + width: 1 + }) + }) + }); + + // SOURCES LABEL LAYER + scope.sourcesLabelLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + }), + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 0, 0, 0.1)' + }), + stroke: new ol.style.Stroke({ + color: 'rgba(255, 0, 0, 1)', + width: 2 + }) + }), + minResolution: 2, + maxResolution: 20 + }); + + var mousePositionControl = new ol.control.MousePosition({ + coordinateFormat: ol.coordinate.createStringXY(4), + projection: scope.sceneProjection, + undefinedHTML: ' ' + }); + var DownloadSelectionControl = function(opt_options) { + var options = opt_options || {}; - var hasLeft = (mask & 1) > 0; - var hasRight = (mask & 2) > 0; - var splitX = (mask & 4) > 0; - var splitY = (mask & 8) > 0; - var splitZ = (mask & 16) > 0; - var split = null; - if(splitX){ - split = "X"; - }else if(splitY){ - split = "Y"; - }if(splitZ){ - split = "Z"; - } + // TOGGLE TILES + var btToggleTiles = document.createElement('button'); + btToggleTiles.innerHTML = 'T'; + btToggleTiles.addEventListener('click', function(){ + var visible = scope.sourcesLayer.getVisible(); + scope.sourcesLayer.setVisible(!visible); + scope.sourcesLabelLayer.setVisible(!visible); + }, false); + btToggleTiles.style.float = "left"; + btToggleTiles.title = "show / hide tiles"; - var node = new Potree.PointCloudArena4DGeometryNode(); - node.hasLeft = hasLeft; - node.hasRight = hasRight; - node.split = split; - node.isLeaf = !hasLeft && !hasRight; - node.number = i; - node.left = null; - node.right = null; - node.pcoGeometry = scope; - node.level = stack.length; - levels = Math.max(levels, node.level); - if(stack.length > 0){ - var parent = stack[stack.length-1]; - node.boundingBox = parent.boundingBox.clone(); - var parentBBSize = parent.boundingBox.size(); + // DOWNLOAD SELECTED TILES + var link = document.createElement("a"); + link.href = "#"; + link.download = "list.txt"; + link.style.float = "left"; + + var button = document.createElement('button'); + button.innerHTML = 'D'; + link.appendChild(button); + + var this_ = this; + var handleDownload = function(e) { + var features = selectedFeatures.getArray(); - if(parent.hasLeft && !parent.left){ - parent.left = node; + if(features.length === 0){ + alert("No tiles were selected. Select area with ctrl + left mouse button!"); + e.preventDefault(); + e.stopImmediatePropagation(); + return false; - if(parent.split === "X"){ - node.boundingBox.max.x = node.boundingBox.min.x + parentBBSize.x / 2; - }else if(parent.split === "Y"){ - node.boundingBox.max.y = node.boundingBox.min.y + parentBBSize.y / 2; - }else if(parent.split === "Z"){ - node.boundingBox.max.z = node.boundingBox.min.z + parentBBSize.z / 2; - } - - var center = node.boundingBox.center(); - var radius = node.boundingBox.size().length() / 2; - node.boundingSphere = new THREE.Sphere(center, radius); - - }else{ - parent.right = node; + } + + var content = ""; + for(var i = 0; i < features.length; i++){ + var feature = features[i]; - if(parent.split === "X"){ - node.boundingBox.min.x = node.boundingBox.min.x + parentBBSize.x / 2; - }else if(parent.split === "Y"){ - node.boundingBox.min.y = node.boundingBox.min.y + parentBBSize.y / 2; - }else if(parent.split === "Z"){ - node.boundingBox.min.z = node.boundingBox.min.z + parentBBSize.z / 2; + if(feature.source){ + var cloudjsurl = feature.pointcloud.pcoGeometry.url; + var pcurl = cloudjsurl.substring(0, cloudjsurl.lastIndexOf("/") + 1); + var sourceurl = pcurl + "/source"; + content += sourceurl + "/" + feature.source.name + "\n"; } - - var center = node.boundingBox.center(); - var radius = node.boundingBox.size().length() / 2; - node.boundingSphere = new THREE.Sphere(center, radius); } - }else{ - root = node; - root.boundingBox = scope.boundingBox.clone(); - var center = root.boundingBox.center(); - var radius = root.boundingBox.size().length() / 2; - root.boundingSphere = new THREE.Sphere(center, radius); - } + + var uri = "data:application/octet-stream;base64,"+btoa(content); + link.href = uri; + + }; - var bbSize = node.boundingBox.size(); - node.spacing = ((bbSize.x + bbSize.y + bbSize.z) / 3) / 75; + button.addEventListener('click', handleDownload, false); - stack.push(node); + // assemble container + var element = document.createElement('div'); + element.className = 'ol-unselectable ol-control'; + element.appendChild(link); + element.appendChild(btToggleTiles); + element.style.bottom = "0.5em"; + element.style.left = "0.5em"; + element.title = "Download list of selected tiles. Select area using ctrl + left mouse."; - if(node.isLeaf){ - var done = false; - while(!done && stack.length > 0){ - stack.pop(); - - var top = stack[stack.length-1]; - - done = stack.length > 0 && top.hasRight && top.right == null; - } - } + ol.control.Control.call(this, { + element: element, + target: options.target + }); + + }; + ol.inherits(DownloadSelectionControl, ol.control.Control); + + + //scope.controls = {}; + //scope.controls.zoomToExtent = new ol.control.ZoomToExtent({ + // extent: undefined, + // closest: true + //}) + + scope.map = new ol.Map({ + controls: ol.control.defaults({ + attributionOptions: ({ + collapsible: false + }) + }).extend([ + //scope.controls.zoomToExtent, + new DownloadSelectionControl(), + mousePositionControl + ]), + layers: [ + new ol.layer.Tile({source: new ol.source.OSM()}), + scope.toolLayer, + scope.sourcesLayer, + scope.sourcesLabelLayer, + visibleBoundsLayer, + cameraLayer + ], + target: 'potree_map_content', + view: new ol.View({ + center: scope.olCenter, + zoom: 9 + }) + }); + + // DRAGBOX / SELECTION + scope.dragBoxLayer = new ol.layer.Vector({ + source: new ol.source.Vector({}), + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'rgba(0, 0, 255, 1)', + width: 2 + }) + }) + }); + scope.map.addLayer(scope.dragBoxLayer); + + var select = new ol.interaction.Select(); + scope.map.addInteraction(select); + + var selectedFeatures = select.getFeatures(); + + var dragBox = new ol.interaction.DragBox({ + condition: ol.events.condition.platformModifierKeyOnly + }); + + scope.map.addInteraction(dragBox); + + + dragBox.on('boxend', function(e) { + // features that intersect the box are added to the collection of + // selected features, and their names are displayed in the "info" + // div + var extent = dragBox.getGeometry().getExtent(); + scope.sourcesLayer.getSource().forEachFeatureIntersectingExtent(extent, function(feature) { + selectedFeatures.push(feature); + }); + }); + + // clear selection when drawing a new box and when clicking on the map + dragBox.on('boxstart', function(e) { + selectedFeatures.clear(); + }); + scope.map.on('click', function() { + selectedFeatures.clear(); + }); + + + + + // adding pointclouds to map + scope.viewer.addEventListener("pointcloud_loaded", function(event){ + scope.load(event.pointcloud); + }); + for(var i = 0; i < scope.viewer.pointclouds.length; i++){ + scope.load(scope.viewer.pointclouds[i]); } - var end = new Date().getTime(); - var parseDuration = end - start; - var msg = parseDuration; - //document.getElementById("lblDebug").innerHTML = msg; - scope.root = root; - scope.levels = levels; - //console.log(this.root); + scope.viewer.profileTool.addEventListener("profile_added", scope.updateToolDrawings); + scope.viewer.profileTool.addEventListener("profile_removed", scope.updateToolDrawings); + scope.viewer.profileTool.addEventListener("marker_moved", scope.updateToolDrawings); + scope.viewer.profileTool.addEventListener("marker_removed", scope.updateToolDrawings); + scope.viewer.profileTool.addEventListener("marker_added", scope.updateToolDrawings); + scope.viewer.measuringTool.addEventListener("measurement_added", scope.updateToolDrawings); + scope.viewer.measuringTool.addEventListener("marker_added", scope.updateToolDrawings); + scope.viewer.measuringTool.addEventListener("marker_removed", scope.updateToolDrawings); + scope.viewer.measuringTool.addEventListener("marker_moved", scope.updateToolDrawings); + }; - xhr.send(null); + this.setSceneProjection = function(sceneProjection){ + scope.sceneProjection = sceneProjection; + this.toMap = proj4(scope.sceneProjection, scope.mapProjection); + this.toScene = proj4(scope.mapProjection, scope.sceneProjection); + }; + this.getMapExtent = function(){ + var bb = scope.viewer.getBoundingBoxGeo(); + + var bottomLeft = scope.toMap.forward([bb.min.x, bb.min.y]); + var bottomRight = scope.toMap.forward([bb.max.x, bb.min.y]); + var topRight = scope.toMap.forward([bb.max.x, bb.max.y]); + var topLeft = scope.toMap.forward([bb.min.x, bb.max.y]); + + var extent = { + bottomLeft: bottomLeft, + bottomRight: bottomRight, + topRight: topRight, + topLeft: topLeft + }; + + return extent; + }; -}; + this.getMapCenter = function(){ + var mapExtent = scope.getMapExtent(); + + var mapCenter = [ + (mapExtent.bottomLeft[0] + mapExtent.topRight[0]) / 2, + (mapExtent.bottomLeft[1] + mapExtent.topRight[1]) / 2 + ]; + + return mapCenter; + }; + + this.updateToolDrawings = function(){ + scope.toolLayer.getSource().clear(); + + var profiles = scope.viewer.profileTool.profiles; + for(var i = 0; i < profiles.length; i++){ + var profile = profiles[i]; + var coordinates = []; + + for(var j = 0; j < profile.points.length; j++){ + var point = profile.points[j]; + var pointGeo = scope.viewer.toGeo(point); + var pointMap = scope.toMap.forward([pointGeo.x, pointGeo.y]); + coordinates.push(pointMap); + } + + var line = new ol.geom.LineString(coordinates); + var feature = new ol.Feature(line); + scope.toolLayer.getSource().addFeature(feature); + } + + var measurements = scope.viewer.measuringTool.measurements; + for(var i = 0; i < measurements.length; i++){ + var measurement = measurements[i]; + var coordinates = []; + + for(var j = 0; j < measurement.points.length; j++){ + var point = measurement.points[j].position; + var pointGeo = scope.viewer.toGeo(point); + var pointMap = scope.toMap.forward([pointGeo.x, pointGeo.y]); + coordinates.push(pointMap); + } + + if(measurement.closed && measurement.points.length > 0){ + coordinates.push(coordinates[0]); + } + + var line = new ol.geom.LineString(coordinates); + var feature = new ol.Feature(line); + scope.toolLayer.getSource().addFeature(feature); + } + + }; + + + this.load = function(pointcloud){ + + if(!(pointcloud instanceof Potree.PointCloudOctree)){ + return; + } + + if(!scope.sceneProjection){ + scope.setSceneProjection(pointcloud.projection); + } + + var mapExtent = scope.getMapExtent(); + var mapCenter = scope.getMapCenter(); + + //viewer.mapView.controls.zoomToExtent.extent_ = [ mapExtent.bottomLeft, mapExtent.topRight ]; + //viewer.mapView.controls.zoomToExtent.set("extent", [ mapExtent.bottomLeft, mapExtent.topRight ]); + + var view = scope.map.getView(); + view.setCenter(mapCenter); + + scope.gExtent.setCoordinates([ + mapExtent.bottomLeft, + mapExtent.bottomRight, + mapExtent.topRight, + mapExtent.topLeft, + mapExtent.bottomLeft + ]); + + //view.fit(scope.gExtent, scope.map.getSize()); + view.fit(scope.gExtent, [300, 300], { + constrainResolution: false + }); -Object.defineProperty(Potree.PointCloudArena4DGeometry.prototype, "spacing", { - get: function(){ - if(this._spacing){ - return this._spacing; - }else if(this.root){ - return this.root.spacing; - }else{ - null; + var createLabelStyle = function(text){ + var style = new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ + color: 'rgba(100,50,200,0.5)' + }), + stroke: new ol.style.Stroke({ + color: 'rgba(120,30,100,0.8)', + width: 3 + }) + }), + text: new ol.style.Text({ + font: '12px helvetica,sans-serif', + text: text, + fill: new ol.style.Fill({ + color: '#000' + }), + stroke: new ol.style.Stroke({ + color: '#fff', + width: 2 + }) + }) + }); + + return style; } - }, - set: function(value){ - this._spacing = value; + + var url = pointcloud.pcoGeometry.url + "/../sources.json"; + $.getJSON(url, function(data){ + var sources = data.sources; + + for(var i = 0; i < sources.length; i++){ + var source = sources[i]; + var name = source.name; + var points = source.points; + var bounds = source.bounds; + + var mapBounds = { + min: scope.toMap.forward( [bounds.min[0], bounds.min[1]] ), + max: scope.toMap.forward( [bounds.max[0], bounds.max[1]] ) + } + var mapCenter = [ + (mapBounds.min[0] + mapBounds.max[0]) / 2, + (mapBounds.min[1] + mapBounds.max[1]) / 2, + ]; + + var p1 = scope.toMap.forward( [bounds.min[0], bounds.min[1]] ); + var p2 = scope.toMap.forward( [bounds.max[0], bounds.min[1]] ); + var p3 = scope.toMap.forward( [bounds.max[0], bounds.max[1]] ); + var p4 = scope.toMap.forward( [bounds.min[0], bounds.max[1]] ); + + //var boxes = []; + //var feature = new ol.Feature({ + // 'geometry': new ol.geom.LineString([p1, p2, p3, p4, p1]) + //}); + //feature.source = source; + //feature.pointcloud = pointcloud; + //scope.sourcesLayer.getSource().addFeature(feature); + // + // + //feature = new ol.Feature({ + // geometry: new ol.geom.Point(mapCenter), + // name: name + //}); + //feature.setStyle(createLabelStyle(name)); + //scope.sourcesLabelLayer.getSource().addFeature(feature); + } + }); } -}); + + this.update = function(delta){ + var pm = $( "#potree_map" ); + + if(!pm.is(":visible")){ + return; + } + + // resize + var mapSize = scope.map.getSize(); + var resized = (pm.width() != mapSize[0] || pm.height() != mapSize[1]); + if(resized){ + scope.map.updateSize(); + } + + // camera + var scale = scope.map.getView().getResolution(); + var camera = scope.viewer.camera; + var campos = camera.position; + var camdir = camera.getWorldDirection(); + var sceneLookAt = camdir.clone().multiplyScalar(30 * scale).add(campos); + var geoPos = scope.viewer.toGeo(camera.position); + var geoLookAt = scope.viewer.toGeo(sceneLookAt); + var mapPos = new THREE.Vector2().fromArray(scope.toMap.forward([geoPos.x, geoPos.y])); + var mapLookAt = new THREE.Vector2().fromArray(scope.toMap.forward([geoLookAt.x, geoLookAt.y])); + var mapDir = new THREE.Vector2().subVectors(mapLookAt, mapPos).normalize(); + mapLookAt = mapPos.clone().add(mapDir.clone().multiplyScalar(30 * scale)); + var mapLength = mapPos.distanceTo(mapLookAt); + var mapSide = new THREE.Vector2(-mapDir.y, mapDir.x); + + var p1 = mapPos.toArray(); + var p2 = mapLookAt.clone().sub(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray(); + var p3 = mapLookAt.clone().add(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray(); + + + scope.gCamera.setCoordinates([p1, p2, p3, p1]); + // + //viewer.mapView.map.getPixelFromCoordinate(p1); + + + }; + +}; + diff --git a/PotreeConverter/resources/page_template/build/potree/potree.min.js b/PotreeConverter/resources/page_template/build/potree/potree.min.js new file mode 100644 index 00000000..a26d3eba --- /dev/null +++ b/PotreeConverter/resources/page_template/build/potree/potree.min.js @@ -0,0 +1,8 @@ +function Potree(){}function LRUItem(e){this.previous=null,this.next=null,this.node=e}function LRU(){this.first=null,this.last=null,this.items={},this.elements=0,this.numPoints=0}function getMousePointCloudIntersection(e,t,o,i){var n=new THREE.Vector3(e.x,e.y,.5);n.unproject(t);for(var r=n.sub(t.position).normalize(),a=new THREE.Ray(t.position,r),s=null,l=null,d=0;du)&&(s=p,l=u)}}return s?s.position:null}function pixelsArrayToImage(e,t,o){var i=document.createElement("canvas");i.width=t,i.height=o;var n=i.getContext("2d");e=new e.constructor(e);for(var r=0;r0;){var R=c.pop(),x=R.node,C=R.parent,u=e[R.pointcloud],w=x.getBoundingBox(),h=l[R.pointcloud],b=d[R.pointcloud],S=h.intersectsBox(w),H=S;if(H=H&&!(n+x.getNumPoints()>Potree.pointBudget)){if(i++,n+=x.getNumPoints(),u.numVisibleNodes++,u.numVisiblePoints+=x.getNumPoints(),!x.isGeometryNode()||C&&!C.isTreeNode()||(x.isLoaded()?x=u.toTreeNode(x,C):(s.push(x),a.push(x))),x.isTreeNode())if(Potree.getLRU().touch(x.geometryNode),x.sceneNode.visible=!0,x.sceneNode.material=u.material,r.push(x),u.visibleNodes.push(x),x.parent?x.sceneNode.matrixWorld.multiplyMatrices(x.parent.sceneNode.matrixWorld,x.sceneNode.matrix):x.sceneNode.matrixWorld.multiplyMatrices(u.matrixWorld,x.sceneNode.matrix),u.showBoundingBox&&!x.boundingBoxNode){var M=new THREE.BoxHelper(x.sceneNode);u.add(M),u.boundingBoxNodes.push(M),x.boundingBoxNode=M,x.boundingBoxNode.matrixWorld.copy(x.sceneNode.matrixWorld)}else u.showBoundingBox?(x.boundingBoxNode.visible=!0,x.boundingBoxNode.matrixWorld.copy(x.sceneNode.matrixWorld)):!u.showBoundingBox&&x.boundingBoxNode&&(x.boundingBoxNode.visible=!1);for(var B=x.getChildren(),p=0;pI-L&&(_=Number.MAX_VALUE),c.push({pointcloud:R.pointcloud,node:A,parent:x,weight:_})}}}}for(var p=0;p 0",""," uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];"," uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];"," uniform float pointLightDistance[ MAX_POINT_LIGHTS ];"," uniform float pointLightDecay[ MAX_POINT_LIGHTS ];",""," #endif",""," #if MAX_DIR_LIGHTS > 0",""," uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];"," uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",""," #endif","","#endif","","//#if MAX_SPOT_LIGHTS > 0","//","// uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];","// uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];","// uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];","// uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];","// uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];","//","// uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];","//","//#endif","","uniform float blendHardness;","uniform float blendDepthSupplement;","uniform float fov;","uniform float spacing;","uniform float near;","uniform float far;","uniform float pcIndex;","uniform float screenWidth;","uniform float screenHeight;","","uniform sampler2D depthMap;","","varying vec3 vColor;","varying float vOpacity;","varying float vLinearDepth;","varying float vLogDepth;","varying vec3 vViewPosition;","varying float vRadius;","varying vec3 vWorldPosition;","varying vec3 vNormal;","","float specularStrength = 1.0;","","void main() {",""," vec3 color = vColor;"," float depth = gl_FragCoord.z;",""," #if defined(circle_point_shape) || defined(use_interpolation) || defined (weighted_splats)"," float u = 2.0 * gl_PointCoord.x - 1.0;"," float v = 2.0 * gl_PointCoord.y - 1.0;"," #endif"," "," #if defined(circle_point_shape) || defined (weighted_splats)"," float cc = u*u + v*v;"," if(cc > 1.0){"," discard;"," }"," #endif"," "," #if defined weighted_splats"," vec2 uv = gl_FragCoord.xy / vec2(screenWidth, screenHeight);"," float sDepth = texture2D(depthMap, uv).r;"," if(vLinearDepth > sDepth + vRadius + blendDepthSupplement){"," discard;"," }"," #endif"," "," #if defined use_interpolation"," float wi = 0.0 - ( u*u + v*v);"," vec4 pos = vec4(-vViewPosition, 1.0);"," pos.z += wi * vRadius;"," float linearDepth = pos.z;"," pos = projectionMatrix * pos;"," pos = pos / pos.w;"," float expDepth = pos.z;"," depth = (pos.z + 1.0) / 2.0;"," gl_FragDepthEXT = depth;"," "," #if defined(color_type_depth)"," color.r = linearDepth;"," color.g = expDepth;"," #endif"," "," #endif"," "," #if defined color_type_point_index"," gl_FragColor = vec4(color, pcIndex / 255.0);"," #else"," gl_FragColor = vec4(color, vOpacity);"," #endif"," "," #if defined weighted_splats"," float w = pow(1.0 - (u*u + v*v), blendHardness);"," gl_FragColor.rgb = gl_FragColor.rgb * w;"," gl_FragColor.a = w;"," #endif"," "," vec3 normal = normalize( vNormal );"," normal.z = abs(normal.z);"," vec3 viewPosition = normalize( vViewPosition );"," "," #if defined(color_type_phong)",""," // code taken from three.js phong light fragment shader"," "," #if MAX_POINT_LIGHTS > 0",""," vec3 pointDiffuse = vec3( 0.0 );"," vec3 pointSpecular = vec3( 0.0 );",""," for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",""," vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );"," vec3 lVector = lPosition.xyz + vViewPosition.xyz;",""," float lDistance = 1.0;"," if ( pointLightDistance[ i ] > 0.0 )"," lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",""," lVector = normalize( lVector );",""," // diffuse",""," float dotProduct = dot( normal, lVector );",""," #ifdef WRAP_AROUND",""," float pointDiffuseWeightFull = max( dotProduct, 0.0 );"," float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",""," vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",""," #else",""," float pointDiffuseWeight = max( dotProduct, 0.0 );",""," #endif",""," pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;",""," // specular",""," vec3 pointHalfVector = normalize( lVector + viewPosition );"," float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );"," float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );",""," float specularNormalization = ( shininess + 2.0 ) / 8.0;",""," vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );"," pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;"," pointSpecular = vec3(0.0, 0.0, 0.0);"," }"," "," #endif"," "," #if MAX_DIR_LIGHTS > 0",""," vec3 dirDiffuse = vec3( 0.0 );"," vec3 dirSpecular = vec3( 0.0 );",""," for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",""," vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );"," vec3 dirVector = normalize( lDirection.xyz );",""," // diffuse",""," float dotProduct = dot( normal, dirVector );",""," #ifdef WRAP_AROUND",""," float dirDiffuseWeightFull = max( dotProduct, 0.0 );"," float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",""," vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );",""," #else",""," float dirDiffuseWeight = max( dotProduct, 0.0 );",""," #endif",""," dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;",""," // specular",""," vec3 dirHalfVector = normalize( dirVector + viewPosition );"," float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );"," float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );",""," float specularNormalization = ( shininess + 2.0 ) / 8.0;",""," vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );"," dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;"," }",""," #endif"," "," vec3 totalDiffuse = vec3( 0.0 );"," vec3 totalSpecular = vec3( 0.0 );"," "," #if MAX_POINT_LIGHTS > 0",""," totalDiffuse += pointDiffuse;"," totalSpecular += pointSpecular;",""," #endif"," "," #if MAX_DIR_LIGHTS > 0",""," totalDiffuse += dirDiffuse;"," totalSpecular += dirSpecular;",""," #endif"," "," gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;",""," #endif"," "," "," #if defined(use_edl)"," gl_FragColor.a = vLogDepth;"," #endif"," ","}","","",""].join("\n"),Potree.Shaders["normalize.vs"]=["","varying vec2 vUv;","","void main() {"," vUv = uv;",""," gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);","}"].join("\n"),Potree.Shaders["normalize.fs"]=["","#extension GL_EXT_frag_depth : enable","","uniform sampler2D depthMap;","uniform sampler2D texture;","","varying vec2 vUv;","","void main() {"," float depth = texture2D(depthMap, vUv).g; "," "," if(depth <= 0.0){"," discard;"," }"," "," vec4 color = texture2D(texture, vUv); "," color = color / color.w;"," "," gl_FragColor = vec4(color.xyz, 1.0); "," "," gl_FragDepthEXT = depth;","}"].join("\n"),Potree.Shaders["edl.vs"]=["","","varying vec2 vUv;","","void main() {"," vUv = uv;"," "," vec4 mvPosition = modelViewMatrix * vec4(position,1.0);",""," gl_Position = projectionMatrix * mvPosition;","}"].join("\n"),Potree.Shaders["edl.fs"]=["","// ","// adapted from the EDL shader code from Christian Boucheny in cloud compare:","// https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL","//","","#define NEIGHBOUR_COUNT 8","","uniform mat4 projectionMatrix;","","uniform float screenWidth;","uniform float screenHeight;","uniform float near;","uniform float far;","uniform vec2 neighbours[NEIGHBOUR_COUNT];","uniform vec3 lightDir;","uniform float expScale;","uniform float edlScale;","uniform float radius;","uniform float opacity;","","//uniform sampler2D depthMap;","uniform sampler2D colorMap;","","varying vec2 vUv;","","/**"," * transform linear depth to [0,1] interval with 1 beeing closest to the camera."," */","float ztransform(float linearDepth){"," return 1.0 - (linearDepth - near) / (far - near);","}","","// this actually only returns linear depth values if LOG_BIAS is 1.0","// lower values work out more nicely, though.","#define LOG_BIAS 0.01","float logToLinear(float z){"," return (pow((1.0 + LOG_BIAS * far), z) - 1.0) / LOG_BIAS;","}","","float computeObscurance(float linearDepth){"," vec2 uvRadius = radius / vec2(screenWidth, screenHeight);"," "," float sum = 0.0;"," "," for(int c = 0; c < NEIGHBOUR_COUNT; c++){"," vec2 N_rel_pos = uvRadius * neighbours[c];"," vec2 N_abs_pos = vUv + N_rel_pos;"," "," float neighbourDepth = logToLinear(texture2D(colorMap, N_abs_pos).a);"," "," if(neighbourDepth != 0.0){"," float Znp = ztransform(neighbourDepth) - ztransform(linearDepth);"," "," sum += max(0.0, Znp) / (0.05 * linearDepth);"," }"," }"," "," return sum;","}","","void main(){"," float linearDepth = logToLinear(texture2D(colorMap, vUv).a);"," "," float f = computeObscurance(linearDepth);"," f = exp(-expScale * edlScale * f);"," "," vec4 color = texture2D(colorMap, vUv);"," if(color.a == 0.0 && f >= 1.0){"," discard;"," }"," "," gl_FragColor = vec4(color.rgb * f, opacity);","}",""].join("\n"),Potree.Shaders["blur.vs"]=["","varying vec2 vUv;","","void main() {"," vUv = uv;",""," gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);","}"].join("\n"),Potree.Shaders["blur.fs"]=["","uniform mat4 projectionMatrix;","","uniform float screenWidth;","uniform float screenHeight;","uniform float near;","uniform float far;","","uniform sampler2D map;","","varying vec2 vUv;","","void main() {",""," float dx = 1.0 / screenWidth;"," float dy = 1.0 / screenHeight;",""," vec3 color = vec3(0.0, 0.0, 0.0);"," color += texture2D(map, vUv + vec2(-dx, -dy)).rgb;"," color += texture2D(map, vUv + vec2( 0, -dy)).rgb;"," color += texture2D(map, vUv + vec2(+dx, -dy)).rgb;"," color += texture2D(map, vUv + vec2(-dx, 0)).rgb;"," color += texture2D(map, vUv + vec2( 0, 0)).rgb;"," color += texture2D(map, vUv + vec2(+dx, 0)).rgb;"," color += texture2D(map, vUv + vec2(-dx, dy)).rgb;"," color += texture2D(map, vUv + vec2( 0, dy)).rgb;"," color += texture2D(map, vUv + vec2(+dx, dy)).rgb;"," "," color = color / 9.0;"," "," gl_FragColor = vec4(color, 1.0);"," "," ","}"].join("\n"),THREE.PerspectiveCamera.prototype.zoomTo=function(e,t){if(e.geometry||e.boundingSphere||e.boundingBox){e.geometry&&null===e.geometry.boundingSphere&&e.geometry.computeBoundingSphere(),e.updateMatrixWorld();var o;o=e.boundingSphere?e.boundingSphere:e.geometry&&e.geometry.boundingSphere?e.geometry.boundingSphere:e.boundingBox.getBoundingSphere();var i=t||1;o=o.clone().applyMatrix4(e.matrixWorld);var n=o.radius,r=this.fov*Math.PI/180;this.aspect<1&&(r*=this.aspect);var a=Math.abs(n/Math.sin(r/2))*i,s=this.getWorldDirection().multiplyScalar(-a);this.position.copy(o.center.clone().add(s))}},THREE.Ray.prototype.distanceToPlaneWithNegative=function(e){var t=e.normal.dot(this.direction);if(0===t)return 0===e.distanceToPoint(this.origin)?0:null;var o=-(this.origin.dot(e.normal)+e.constant)/t;return o},Potree.POCLoader=function(){},Potree.POCLoader.load=function(e,t){try{var o=new Potree.PointCloudOctreeGeometry;o.url=e;var i=new XMLHttpRequest;i.open("GET",e,!0),i.onreadystatechange=function(){if(4===i.readyState&&(200===i.status||0===i.status)){var n=JSON.parse(i.responseText),r=new Potree.Version(n.version);0===n.octreeDir.indexOf("http")?o.octreeDir=n.octreeDir:o.octreeDir=e+"/../"+n.octreeDir,o.spacing=n.spacing,o.hierarchyStepSize=n.hierarchyStepSize,o.pointAttributes=n.pointAttributes;var a=new THREE.Vector3(n.boundingBox.lx,n.boundingBox.ly,n.boundingBox.lz),s=new THREE.Vector3(n.boundingBox.ux,n.boundingBox.uy,n.boundingBox.uz),l=new THREE.Box3(a,s),d=l.clone();n.tightBoundingBox&&(d.min.copy(new THREE.Vector3(n.tightBoundingBox.lx,n.tightBoundingBox.ly,n.tightBoundingBox.lz)),d.max.copy(new THREE.Vector3(n.tightBoundingBox.ux,n.tightBoundingBox.uy,n.tightBoundingBox.uz)));var c=new THREE.Vector3(0,0,0);c.set(-a.x,-a.y,-a.z),l.min.add(c),l.max.add(c),d.min.add(c),d.max.add(c),o.projection=n.projection,o.boundingBox=l,o.tightBoundingBox=d,o.boundingSphere=l.getBoundingSphere(),o.tightBoundingSphere=d.getBoundingSphere(),o.offset=c,"LAS"===n.pointAttributes?o.loader=new Potree.LasLazLoader(n.version):"LAZ"===n.pointAttributes?o.loader=new Potree.LasLazLoader(n.version):(o.loader=new Potree.BinaryLoader(n.version,l,n.scale),o.pointAttributes=new Potree.PointAttributes(o.pointAttributes));var p={},u="r",h=new Potree.PointCloudOctreeGeometryNode(u,o,l);if(h.level=0,h.hasChildren=!0,r.upTo("1.5")?h.numPoints=n.hierarchy[0][1]:h.numPoints=0,o.root=h,o.root.load(),p[u]=h,r.upTo("1.4"))for(var m=1;m=n?o.pointsCount:o.pointsCount/n,s=function(){var e=t.readData(1e6,0,n);return e.then(function(e){return i.push(new LASDecoder(e.buffer,o.pointsFormatId,o.pointsStructSize,e.count,o.scale,o.offset,o.mins,o.maxs)),r+=e.count,Potree.LasLazLoader.progressCB(r/a),e.hasMoreData?s():(o.totalRead=r,o.versionAsString=t.versionAsString,o.isCompressed=t.isCompressed,[t,o,i])})};return s()}).then(function(e){var t=e[0];return Potree.LasLazLoader.progressCB(1),t.close().then(function(){return t.isOpen=!1,Promise.delay(200).cancellable()}).then(function(){return e.slice(1)})})["catch"](Promise.CancellationError,function(e){if(o.isOpen)return o.close().then(function(){throw o.isOpen=!1,e});throw e})},Potree.LasLazLoader.prototype.handle=function(e,t){},Potree.LasLazBatcher=function(e){this.push=function(t){var o=Potree.workers.lasdecoder.getWorker(),i=new THREE.Vector3(t.mins[0],t.mins[1],t.mins[2]),n=new THREE.Vector3(t.maxs[0],t.maxs[1],t.maxs[2]);i.add(e.pcoGeometry.offset),n.add(e.pcoGeometry.offset),o.onmessage=function(r){for(var a=new THREE.BufferGeometry,s=t.pointsCount,l=r.data.position,d=r.data.color,c=r.data.intensity,p=new Uint8Array(r.data.classification),u=new Float32Array(p.byteLength),h=new Uint8Array(r.data.returnNumber),m=new Uint8Array(r.data.numberOfReturns),f=new Float32Array(h.byteLength),g=new Float32Array(m.byteLength),v=new Uint16Array(r.data.pointSourceID),y=new Float32Array(v.length),E=new ArrayBuffer(4*s),T=new Uint32Array(E),b=new THREE.Box3,P=new Float32Array(l),R=0;s>R;R++)u[R]=p[R],f[R]=h[R],g[R]=m[R],y[R]=v[R],T[R]=R,b.expandByPoint(new THREE.Vector3(P[3*R+0],P[3*R+1],P[3*R+2]));a.addAttribute("position",new THREE.BufferAttribute(new Float32Array(l),3)),a.addAttribute("color",new THREE.BufferAttribute(new Float32Array(d),3)),a.addAttribute("intensity",new THREE.BufferAttribute(new Float32Array(c),1)),a.addAttribute("classification",new THREE.BufferAttribute(new Float32Array(u),1)),a.addAttribute("returnNumber",new THREE.BufferAttribute(new Float32Array(f),1)),a.addAttribute("numberOfReturns",new THREE.BufferAttribute(new Float32Array(g),1)),a.addAttribute("pointSourceID",new THREE.BufferAttribute(new Float32Array(y),1)),a.addAttribute("indices",new THREE.BufferAttribute(E,1)),a.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(3*s),3));var x=new THREE.Box3((new THREE.Vector3).fromArray(r.data.tightBoundingBox.min),(new THREE.Vector3).fromArray(r.data.tightBoundingBox.max));a.boundingBox=new THREE.Box3(i,n),e.tightBoundingBox=x,e.geometry=a,e.loaded=!0,e.loading=!1,e.pcoGeometry.numNodesLoading--,Potree.workers.lasdecoder.returnWorker(o)};var r={buffer:t.arrayb,numPoints:t.pointsCount,pointSize:t.pointSize,pointFormatID:2,scale:t.scale,offset:t.offset,mins:[e.pcoGeometry.boundingBox.min.x,e.pcoGeometry.boundingBox.min.y,e.pcoGeometry.boundingBox.min.z],maxs:[e.pcoGeometry.boundingBox.max.x,e.pcoGeometry.boundingBox.max.y,e.pcoGeometry.boundingBox.max.z],bbOffset:[e.pcoGeometry.offset.x,e.pcoGeometry.offset.y,e.pcoGeometry.offset.z]};o.postMessage(r,[r.buffer])}},Potree.Gradients={RAINBOW:[[0,new THREE.Color(.278,0,.714)],[1/6,new THREE.Color(0,0,1)],[2/6,new THREE.Color(0,1,1)],[.5,new THREE.Color(0,1,0)],[4/6,new THREE.Color(1,1,0)],[5/6,new THREE.Color(1,.64,0)],[1,new THREE.Color(1,0,0)]],GRAYSCALE:[[0,new THREE.Color(0,0,0)],[1,new THREE.Color(1,1,1)]]},Potree.Classification={DEFAULT:{0:new THREE.Vector4(.5,.5,.5,1),1:new THREE.Vector4(.5,.5,.5,1),2:new THREE.Vector4(.63,.32,.18,1),3:new THREE.Vector4(0,1,0,1),4:new THREE.Vector4(0,.8,0,1),5:new THREE.Vector4(0,.6,0,1),6:new THREE.Vector4(1,.66,0,1),7:new THREE.Vector4(1,0,1,1),8:new THREE.Vector4(1,0,0,1),9:new THREE.Vector4(0,0,1,1),12:new THREE.Vector4(1,1,0,1),DEFAULT:new THREE.Vector4(.3,.6,.6,1)}},Potree.PointSizeType={FIXED:0,ATTENUATED:1,ADAPTIVE:2},Potree.PointShape={SQUARE:0,CIRCLE:1},Potree.PointColorType={RGB:0,COLOR:1,DEPTH:2,HEIGHT:3,INTENSITY:4,INTENSITY_GRADIENT:5,TREE_DEPTH:6,POINT_INDEX:7,CLASSIFICATION:8,RETURN_NUMBER:9,SOURCE:10,NORMAL:11,PHONG:12},Potree.ClipMode={DISABLED:0,CLIP_OUTSIDE:1,HIGHLIGHT_INSIDE:2},Potree.TreeType={OCTREE:0,KDTREE:1},Potree.PointCloudMaterial=function(e){THREE.Material.call(this),e=e||{};var t=new THREE.Color(16777215),o=THREE.ImageUtils.generateDataTexture(2048,1,t);o.magFilter=THREE.NearestFilter,this.visibleNodesTexture=o;var i=e.size||1,n=e.minSize||1,r=e.maxSize||50,a=e.treeType||Potree.TreeType.OCTREE,s=1;this._pointSizeType=Potree.PointSizeType.ATTENUATED,this._pointShape=Potree.PointShape.SQUARE,this._interpolate=!1,this._pointColorType=Potree.PointColorType.RGB,this._useClipBox=!1,this.numClipBoxes=0,this._clipMode=Potree.ClipMode.DISABLED,this._weighted=!1,this._depthMap=null,this._gradient=Potree.Gradients.RAINBOW,this._classification=Potree.Classification.DEFAULT,this.gradientTexture=Potree.PointCloudMaterial.generateGradientTexture(this._gradient),this.classificationTexture=Potree.PointCloudMaterial.generateClassificationTexture(this._classification),this.lights=!0,this._treeType=a,this._useLogarithmicDepthBuffer=!1,this._useEDL=!1;var l={},d={spacing:{type:"f",value:1},blendHardness:{type:"f",value:2},blendDepthSupplement:{type:"f",value:0},fov:{type:"f",value:1},screenWidth:{type:"f",value:1},screenHeight:{type:"f",value:1},near:{type:"f",value:.1},far:{type:"f",value:1},uColor:{type:"c",value:new THREE.Color(16777215)},opacity:{type:"f",value:1},size:{type:"f",value:10},minSize:{type:"f",value:2},maxSize:{type:"f",value:2},octreeSize:{type:"f",value:0},bbSize:{type:"fv",value:[0,0,0]},heightMin:{type:"f",value:0},heightMax:{type:"f",value:1},intensityMin:{type:"f",value:0},intensityMax:{type:"f",value:1},clipBoxCount:{type:"f",value:0},visibleNodes:{type:"t",value:this.visibleNodesTexture},pcIndex:{type:"f",value:0},gradient:{type:"t",value:this.gradientTexture},classificationLUT:{type:"t",value:this.classificationTexture},clipBoxes:{type:"Matrix4fv",value:[]},depthMap:{type:"t",value:null},diffuse:{type:"fv",value:[1,1,1]},ambient:{type:"fv",value:[.1,.1,.1]},ambientLightColor:{type:"fv",value:[1,1,1]},directionalLightColor:{type:"fv",value:null},directionalLightDirection:{type:"fv",value:null},pointLightColor:{type:"fv",value:null},pointLightPosition:{type:"fv",value:null},pointLightDistance:{type:"fv1",value:null},pointLightDecay:{type:"fv1",value:null},spotLightColor:{type:"fv",value:null},spotLightPosition:{type:"fv",value:null},spotLightDistance:{type:"fv1",value:null},spotLightDecay:{type:"fv1",value:null},spotLightDirection:{type:"fv",value:null},spotLightAngleCos:{type:"fv1",value:null},spotLightExponent:{type:"fv1",value:null},hemisphereLightSkyColor:{type:"fv",value:null},hemisphereLightGroundColor:{type:"fv",value:null},hemisphereLightDirection:{type:"fv",value:null}};this.defaultAttributeValues.normal=[0,0,0],this.defaultAttributeValues.classification=[0,0,0],this.setValues({uniforms:d,attributes:l,vertexShader:this.getDefines()+Potree.Shaders["pointcloud.vs"],fragmentShader:this.getDefines()+Potree.Shaders["pointcloud.fs"],vertexColors:THREE.VertexColors,size:i,minSize:n,maxSize:r,nodeSize:s,pcIndex:0,alphaTest:.9})},Potree.PointCloudMaterial.prototype=new THREE.ShaderMaterial,Potree.PointCloudMaterial.prototype.updateShaderSource=function(){var e={};this.pointColorType===Potree.PointColorType.INTENSITY||this.pointColorType===Potree.PointColorType.INTENSITY_GRADIENT?e.intensity={type:"f",value:[]}:this.pointColorType===Potree.PointColorType.CLASSIFICATION||(this.pointColorType===Potree.PointColorType.RETURN_NUMBER?(e.returnNumber={type:"f",value:[]},e.numberOfReturns={type:"f",value:[]}):this.pointColorType===Potree.PointColorType.SOURCE?e.pointSourceID={type:"f",value:[]}:(this.pointColorType===Potree.PointColorType.NORMAL||this.pointColorType===Potree.PointColorType.PHONG)&&(e.normal={type:"f",value:[]})),e.classification={type:"f",value:0};var t=this.getDefines()+Potree.Shaders["pointcloud.vs"],o=this.getDefines()+Potree.Shaders["pointcloud.fs"];this.setValues({attributes:e,vertexShader:t,fragmentShader:o}),this.depthMap&&(this.uniforms.depthMap.value=this.depthMap,this.setValues({depthMap:this.depthMap})),1===this.opacity?this.setValues({blending:THREE.NoBlending,transparent:!1,depthTest:!0,depthWrite:!0}):this.setValues({blending:THREE.AdditiveBlending,transparent:!0,depthTest:!1,depthWrite:!0}),this.weighted&&this.setValues({blending:THREE.AdditiveBlending,transparent:!0,depthTest:!0,depthWrite:!1}),this.needsUpdate=!0},Potree.PointCloudMaterial.prototype.getDefines=function(){var e="";return this.pointSizeType===Potree.PointSizeType.FIXED?e+="#define fixed_point_size\n":this.pointSizeType===Potree.PointSizeType.ATTENUATED?e+="#define attenuated_point_size\n":this.pointSizeType===Potree.PointSizeType.ADAPTIVE&&(e+="#define adaptive_point_size\n"),this.pointShape===Potree.PointShape.SQUARE?e+="#define square_point_shape\n":this.pointShape===Potree.PointShape.CIRCLE&&(e+="#define circle_point_shape\n"),this._interpolate&&(e+="#define use_interpolation\n"),this._useLogarithmicDepthBuffer&&(e+="#define use_logarithmic_depth_buffer\n"),this._useEDL&&(e+="#define use_edl\n"),this._pointColorType===Potree.PointColorType.RGB?e+="#define color_type_rgb\n":this._pointColorType===Potree.PointColorType.COLOR?e+="#define color_type_color\n":this._pointColorType===Potree.PointColorType.DEPTH?e+="#define color_type_depth\n":this._pointColorType===Potree.PointColorType.HEIGHT?e+="#define color_type_height\n":this._pointColorType===Potree.PointColorType.INTENSITY?e+="#define color_type_intensity\n":this._pointColorType===Potree.PointColorType.INTENSITY_GRADIENT?e+="#define color_type_intensity_gradient\n":this._pointColorType===Potree.PointColorType.TREE_DEPTH?e+="#define color_type_tree_depth\n":this._pointColorType===Potree.PointColorType.POINT_INDEX?e+="#define color_type_point_index\n":this._pointColorType===Potree.PointColorType.CLASSIFICATION?e+="#define color_type_classification\n":this._pointColorType===Potree.PointColorType.RETURN_NUMBER?e+="#define color_type_return_number\n":this._pointColorType===Potree.PointColorType.SOURCE?e+="#define color_type_source\n":this._pointColorType===Potree.PointColorType.NORMAL?e+="#define color_type_normal\n":this._pointColorType===Potree.PointColorType.PHONG&&(e+="#define color_type_phong\n"), +this.clipMode===Potree.ClipMode.DISABLED?e+="#define clip_disabled\n":this.clipMode===Potree.ClipMode.CLIP_OUTSIDE?e+="#define clip_outside\n":this.clipMode===Potree.ClipMode.HIGHLIGHT_INSIDE&&(e+="#define clip_highlight_inside\n"),this._treeType===Potree.TreeType.OCTREE?e+="#define tree_type_octree\n":this._treeType===Potree.TreeType.KDTREE&&(e+="#define tree_type_kdtree\n"),this.weighted&&(e+="#define weighted_splats\n"),this.numClipBoxes>0&&(e+="#define use_clip_box\n"),e},Potree.PointCloudMaterial.prototype.setClipBoxes=function(e){if(e){this.clipBoxes=e;var t=this.numClipBoxes!=e.length&&(0===e.length||0===this.numClipBoxes);this.numClipBoxes=e.length,this.uniforms.clipBoxCount.value=this.numClipBoxes,t&&this.updateShaderSource(),this.uniforms.clipBoxes.value=new Float32Array(16*this.numClipBoxes);for(var o=0;or;r++)for(var a=0;o>a;a++){var s,l=r+t*a;s=e[r]?e[r]:e.DEFAULT,n[4*l+0]=255*s.x,n[4*l+1]=255*s.y,n[4*l+2]=255*s.z,n[4*l+3]=255*s.w}var d=new THREE.DataTexture(n,t,o,THREE.RGBAFormat);return d.magFilter=THREE.NearestFilter,d.needsUpdate=!0,d},Potree.EyeDomeLightingMaterial=function(e){THREE.Material.call(this),e=e||{};for(var t=8,o=new Float32Array(2*t),i=0;t>i;i++)o[2*i+0]=Math.cos(2*i*Math.PI/t),o[2*i+1]=Math.sin(2*i*Math.PI/t);var n=new THREE.Vector3(0,0,1).normalize(),r={screenWidth:{type:"f",value:0},screenHeight:{type:"f",value:0},near:{type:"f",value:0},far:{type:"f",value:0},expScale:{type:"f",value:100},edlScale:{type:"f",value:1},radius:{type:"f",value:3},lightDir:{type:"v3",value:n},neighbours:{type:"2fv",value:o},depthMap:{type:"t",value:null},colorMap:{type:"t",value:null},opacity:{type:"f",value:1}};this.setValues({uniforms:r,vertexShader:Potree.Shaders["edl.vs"],fragmentShader:Potree.Shaders["edl.fs"]})},Potree.EyeDomeLightingMaterial.prototype=new THREE.ShaderMaterial,Potree.BlurMaterial=function(e){THREE.Material.call(this),e=e||{};var t={near:{type:"f",value:0},far:{type:"f",value:0},screenWidth:{type:"f",value:0},screenHeight:{type:"f",value:0},map:{type:"t",value:null}};this.setValues({uniforms:t,vertexShader:Potree.Shaders["blur.vs"],fragmentShader:Potree.Shaders["blur.fs"]})},Potree.BlurMaterial.prototype=new THREE.ShaderMaterial,THREE.FirstPersonControls=function(e,t){function o(e){l.enabled!==!1&&(e.preventDefault(),0===e.button?(P=b.ROTATE,d.set(e.clientX,e.clientY)):2===e.button&&(P=b.PAN,u.set(e.clientX,e.clientY)),l.domElement.addEventListener("mousemove",i,!1),l.domElement.addEventListener("mouseup",n,!1),l.dispatchEvent(x))}function i(e){if(l.enabled!==!1){e.preventDefault();var t=l.domElement===document?l.domElement.body:l.domElement;P===b.ROTATE?(c.set(e.clientX,e.clientY),p.subVectors(c,d),l.rotateLeft(2*Math.PI*p.x/t.clientWidth*l.rotateSpeed),l.rotateUp(2*Math.PI*p.y/t.clientHeight*l.rotateSpeed),d.copy(c)):P===b.PAN&&(h.set(e.clientX,e.clientY),m.subVectors(h,u),m.multiplyScalar(5e-4).multiplyScalar(l.moveSpeed),l.pan(m.x,m.y),u.copy(h))}}function n(){l.enabled!==!1&&(l.domElement.removeEventListener("mousemove",i,!1),l.domElement.removeEventListener("mouseup",n,!1),l.dispatchEvent(C),P=b.NONE)}function r(e){if(l.enabled!==!1&&l.noZoom!==!0){e.preventDefault();var t=e.detail<0||e.wheelDelta>0?1:-1,o=l.moveSpeed+.1*l.moveSpeed*t;o=Math.max(.1,o),l.setMoveSpeed(o),l.dispatchEvent(x),l.dispatchEvent(C)}}function a(e){if(l.enabled!==!1)switch(e.keyCode){case l.keys.UP:l.moveForward=!0;break;case l.keys.BOTTOM:l.moveBackward=!0;break;case l.keys.LEFT:l.moveLeft=!0;break;case l.keys.RIGHT:l.moveRight=!0;break;case l.keys.W:l.moveForward=!0;break;case l.keys.S:l.moveBackward=!0;break;case l.keys.A:l.moveLeft=!0;break;case l.keys.D:l.moveRight=!0}}function s(e){switch(e.keyCode){case l.keys.W:l.moveForward=!1;break;case l.keys.S:l.moveBackward=!1;break;case l.keys.A:l.moveLeft=!1;break;case l.keys.D:l.moveRight=!1;break;case l.keys.UP:l.moveForward=!1;break;case l.keys.BOTTOM:l.moveBackward=!1;break;case l.keys.LEFT:l.moveLeft=!1;break;case l.keys.RIGHT:l.moveRight=!1}}this.object=e,this.domElement=void 0!==t?t:document,this.enabled=!0,this.rotateSpeed=1,this.moveSpeed=10,this.keys={LEFT:37,UP:38,RIGHT:39,BOTTOM:40,A:"A".charCodeAt(0),S:"S".charCodeAt(0),D:"D".charCodeAt(0),W:"W".charCodeAt(0)};var l=this,d=new THREE.Vector2,c=new THREE.Vector2,p=new THREE.Vector2,u=new THREE.Vector2,h=new THREE.Vector2,m=new THREE.Vector2,f=new THREE.Vector3,g=(new THREE.Vector3,0),v=0,y=1,E=new THREE.Vector3,T=new THREE.Vector3,b={NONE:-1,ROTATE:0,SPEEDCHANGE:1,PAN:2},P=b.NONE;this.position0=this.object.position.clone();var R={type:"change"},x={type:"start"},C={type:"end"};this.rotateLeft=function(e){v-=e},this.rotateUp=function(e){g-=e},this.panLeft=function(e){var t=this.object.matrix.elements;f.set(t[0],t[1],t[2]),f.multiplyScalar(-e),E.add(f)},this.panUp=function(e){var t=this.object.matrix.elements;f.set(t[4],t[5],t[6]),f.multiplyScalar(e),E.add(f)},this.panForward=function(e){var t=this.object.matrix.elements;f.set(t[8],t[9],t[10]),f.multiplyScalar(e),E.add(f)},this.pan=function(e,t){var o=l.domElement===document?l.domElement.body:l.domElement;if(void 0!==l.object.fov){var i=l.object.position,n=i.clone(),r=n.length();r*=Math.tan(l.object.fov/2*Math.PI/180),l.panLeft(2*e*r/o.clientHeight),l.panUp(2*t*r/o.clientHeight)}else void 0!==l.object.top?(l.panLeft(e*(l.object.right-l.object.left)/o.clientWidth),l.panUp(t*(l.object.top-l.object.bottom)/o.clientHeight)):console.warn("WARNING: FirstPersonControls.js encountered an unknown camera type - pan disabled.")},this.update=function(e){this.object.rotation.order="ZYX";var t=this.object;this.object=new THREE.Object3D,this.object.position.copy(t.position),this.object.rotation.copy(t.rotation),this.object.updateMatrix(),this.object.updateMatrixWorld();var o=this.object.position;if(void 0!==e&&(this.moveRight&&this.panLeft(-e*this.moveSpeed),this.moveLeft&&this.panLeft(e*this.moveSpeed),this.moveForward&&this.panForward(-e*this.moveSpeed),this.moveBackward&&this.panForward(e*this.moveSpeed)),!E.equals(new THREE.Vector3(0,0,0))){var i={type:"move",translation:E.clone()};this.dispatchEvent(i)}if(o.add(E),0!==v||0!==g){var i={type:"rotate",thetaDelta:v,phiDelta:g};this.dispatchEvent(i)}this.object.updateMatrix();var n=(new THREE.Matrix4).makeRotationY(v),r=(new THREE.Matrix4).multiplyMatrices(n,this.object.matrix);this.object.quaternion.setFromRotationMatrix(r),this.object.rotation.x+=g,this.object.updateMatrixWorld();var a={type:"proposeTransform",oldPosition:t.position,newPosition:this.object.position,objections:0,counterProposals:[]};if(this.dispatchEvent(a),a.objections>0&&a.counterProposals.length>0){var s=a.counterProposals;this.object.position.copy(s[0]),a.objections=0,a.counterProposals=[]}a.objections>0||t.position.copy(this.object.position),t.rotation.copy(this.object.rotation),this.object=t,v=0,g=0,y=1,E.set(0,0,0),T.distanceTo(this.object.position)>0&&(this.dispatchEvent(R),T.copy(this.object.position))},this.reset=function(){P=b.NONE,this.object.position.copy(this.position0)},this.setMoveSpeed=function(e){l.moveSpeed!==e&&(l.moveSpeed=e,l.dispatchEvent({type:"move_speed_changed",controls:l}))},this.domElement.addEventListener("contextmenu",function(e){e.preventDefault()},!1),this.domElement.addEventListener("mousedown",o,!1),this.domElement.addEventListener("mousewheel",r,!1),this.domElement.addEventListener("DOMMouseScroll",r,!1),-1===this.domElement.tabIndex&&(this.domElement.tabIndex=2222),this.domElement.addEventListener("keydown",a,!1),this.domElement.addEventListener("keyup",s,!1)},THREE.FirstPersonControls.prototype=Object.create(THREE.EventDispatcher.prototype),Potree.GeoControls=function(e,t){function o(e){l.enabled!==!1&&(e.preventDefault(),0===e.button?(P=b.ROTATE,d.set(e.clientX,e.clientY)):1===e.button?(P=b.PAN,u.set(e.clientX,e.clientY)):2===e.button&&(l.moveForwardMouse=!0),l.dispatchEvent(x))}function i(e){if(l.enabled!==!1){e.preventDefault();var t=l.domElement===document?l.domElement.body:l.domElement;P===b.ROTATE?(c.set(e.clientX,e.clientY),p.subVectors(c,d),l.rotateLeft(2*Math.PI*p.x/t.clientWidth*l.rotateSpeed),l.rotateUp(2*Math.PI*p.y/t.clientHeight*l.rotateSpeed),d.copy(c)):P===b.PAN&&(h.set(e.clientX,e.clientY),m.subVectors(h,u),m.multiplyScalar(.002).multiplyScalar(l.moveSpeed),l.pan(m.x,m.y),u.copy(h))}}function n(e){l.enabled!==!1&&(2===e.button?l.moveForwardMouse=!1:(l.dispatchEvent(C),P=b.NONE))}function r(e){if(l.enabled!==!1&&l.noZoom!==!0){e.preventDefault();var t=e.detail<0||e.wheelDelta>0?1:-1,o=l.moveSpeed+.1*l.moveSpeed*t;o=Math.max(.1,o),l.setMoveSpeed(o),l.dispatchEvent(x),l.dispatchEvent(C)}}function a(e){if(l.enabled!==!1)switch(l.shiftDown=e.shiftKey,e.keyCode){case l.keys.UP:l.moveForward=!0;break;case l.keys.BOTTOM:l.moveBackward=!0;break;case l.keys.LEFT:l.moveLeft=!0;break;case l.keys.RIGHT:l.moveRight=!0;break;case l.keys.W:l.moveForward=!0;break;case l.keys.S:l.moveBackward=!0;break;case l.keys.A:l.moveLeft=!0;break;case l.keys.D:l.moveRight=!0;break;case l.keys.Q:l.rotLeft=!0;break;case l.keys.E:l.rotRight=!0;break;case l.keys.R:l.raiseCamera=!0;break;case l.keys.F:l.lowerCamera=!0}}function s(e){switch(l.shiftDown=e.shiftKey,e.keyCode){case l.keys.W:l.moveForward=!1;break;case l.keys.S:l.moveBackward=!1;break;case l.keys.A:l.moveLeft=!1;break;case l.keys.D:l.moveRight=!1;break;case l.keys.UP:l.moveForward=!1;break;case l.keys.BOTTOM:l.moveBackward=!1;break;case l.keys.LEFT:l.moveLeft=!1;break;case l.keys.RIGHT:l.moveRight=!1;break;case l.keys.Q:l.rotLeft=!1;break;case l.keys.E:l.rotRight=!1;break;case l.keys.R:l.raiseCamera=!1;break;case l.keys.F:l.lowerCamera=!1}}this.object=e,this.domElement=void 0!==t?t:document,this.enabled=!0,this.track=null,this.trackPos=0,this.rotateSpeed=1,this.moveSpeed=10,this.keys={LEFT:37,UP:38,RIGHT:39,BOTTOM:40,A:"A".charCodeAt(0),S:"S".charCodeAt(0),D:"D".charCodeAt(0),W:"W".charCodeAt(0),Q:"Q".charCodeAt(0),E:"E".charCodeAt(0),R:"R".charCodeAt(0),F:"F".charCodeAt(0)};var l=this,d=new THREE.Vector2,c=new THREE.Vector2,p=new THREE.Vector2,u=new THREE.Vector2,h=new THREE.Vector2,m=new THREE.Vector2,f=new THREE.Vector3,g=(new THREE.Vector3,0),v=0,y=1,E=new THREE.Vector3;this.shiftDown=!1;var T=new THREE.Vector3,b={NONE:-1,ROTATE:0,SPEEDCHANGE:1,PAN:2},P=b.NONE;this.position0=this.object.position.clone();var R={type:"change"},x={type:"start"},C={type:"end"};this.setTrack=function(e){this.track!==e&&(this.track=e,this.trackPos=0)},this.setTrackPos=function(e){var t=Math.max(0,Math.min(1,e)),o=this.trackPos,i=this.track.getPointAt(o),n=this.track.getPointAt(t);n.sub(i);if(this.trackPos=t,t!==o){var r={type:"move",translation:E.clone()};this.dispatchEvent(r)}},this.getTrackPos=function(){return this.trackPos},this.rotateLeft=function(e){v-=e},this.rotateUp=function(e){g-=e},this.panLeft=function(e){var t=this.object.matrix.elements;f.set(t[0],t[1],t[2]),f.multiplyScalar(-e),E.add(f)},this.panUp=function(e){var t=this.object.matrix.elements;f.set(t[4],t[5],t[6]),f.multiplyScalar(e),E.add(f)},this.panForward=function(e){if(this.track)this.setTrackPos(this.getTrackPos()-e/this.track.getLength());else{var t=this.object.matrix.elements;f.set(t[8],t[9],t[10]),f.multiplyScalar(e),E.add(f)}},this.pan=function(e,t){var o=l.domElement===document?l.domElement.body:l.domElement;if(void 0!==l.object.fov){var i=l.object.position,n=i.clone(),r=n.length();r*=Math.tan(l.object.fov/2*Math.PI/180),l.panLeft(2*e*r/o.clientHeight),l.panUp(2*t*r/o.clientHeight)}else void 0!==l.object.top?(l.panLeft(e*(l.object.right-l.object.left)/o.clientWidth),l.panUp(t*(l.object.top-l.object.bottom)/o.clientHeight)):console.warn("WARNING: GeoControls.js encountered an unknown camera type - pan disabled.")},this.update=function(e){this.object.rotation.order="ZYX";var t=this.object;this.object=new THREE.Object3D,this.object.position.copy(t.position),this.object.rotation.copy(t.rotation),this.object.updateMatrix(),this.object.updateMatrixWorld();var o=this.object.position;if(void 0!==e){var i=l.shiftDown?4:1;this.moveRight&&this.panLeft(-e*this.moveSpeed*i),this.moveLeft&&this.panLeft(e*this.moveSpeed*i),(this.moveForward||this.moveForwardMouse)&&this.panForward(-e*this.moveSpeed*i),this.moveBackward&&this.panForward(e*this.moveSpeed*i),this.rotLeft&&l.rotateLeft(-.5*Math.PI*e/l.rotateSpeed),this.rotRight&&l.rotateLeft(.5*Math.PI*e/l.rotateSpeed),this.raiseCamera&&l.panUp(e*this.moveSpeed*i),this.lowerCamera&&l.panUp(-e*this.moveSpeed*i)}if(!E.equals(new THREE.Vector3(0,0,0))){var n={type:"move",translation:E.clone()};this.dispatchEvent(n)}if(o.add(E),0!==v||0!==g){var n={type:"rotate",thetaDelta:v,phiDelta:g};this.dispatchEvent(n)}this.object.updateMatrix();var r=(new THREE.Matrix4).makeRotationY(v),a=(new THREE.Matrix4).multiplyMatrices(r,this.object.matrix);this.object.quaternion.setFromRotationMatrix(a),this.object.rotation.x+=g,this.object.updateMatrixWorld();var s={type:"proposeTransform",oldPosition:t.position,newPosition:this.object.position,objections:0,counterProposals:[]};if(this.dispatchEvent(s),s.objections>0&&s.counterProposals.length>0){var d=s.counterProposals;this.object.position.copy(d[0]),s.objections=0,s.counterProposals=[]}if(s.objections>0||t.position.copy(this.object.position),t.rotation.copy(this.object.rotation),this.object=t,v=0,g=0,y=1,E.set(0,0,0),T.distanceTo(this.object.position)>0&&(this.dispatchEvent(R),T.copy(this.object.position)),this.track){var c=this.track.getPointAt(this.trackPos);t.position.copy(c)}},this.reset=function(){P=b.NONE,this.object.position.copy(this.position0)},this.setMoveSpeed=function(e){l.moveSpeed!==e&&(l.moveSpeed=e,l.dispatchEvent({type:"move_speed_changed",controls:l}))},this.domElement.addEventListener("contextmenu",function(e){e.preventDefault()},!1),this.domElement.addEventListener("mousedown",o,!1),this.domElement.addEventListener("mousewheel",r,!1),this.domElement.addEventListener("DOMMouseScroll",r,!1),l.domElement.addEventListener("mousemove",i,!1),l.domElement.addEventListener("mouseup",n,!1),-1===this.domElement.tabIndex&&(this.domElement.tabIndex=2222),l.domElement.addEventListener("keydown",a,!1),l.domElement.addEventListener("keyup",s,!1)},Potree.GeoControls.prototype=Object.create(THREE.EventDispatcher.prototype),Potree.OrbitControls=function(e,t){function o(){return 2*Math.PI/60/60*u.autoRotateSpeed}function i(){return Math.pow(.95,u.zoomSpeed)}function n(e){if(u.enabled!==!1){if(e.preventDefault(),e.button===THREE.MOUSE.LEFT){if(u.noRotate===!0)return;A=B.ROTATE,m.set(e.clientX,e.clientY)}else if(e.button===THREE.MOUSE.MIDDLE){if(u.noZoom===!0)return;A=B.DOLLY,P.set(e.clientX,e.clientY)}else if(e.button===THREE.MOUSE.RIGHT){if(u.noPan===!0)return;A=B.PAN,v.set(e.clientX,e.clientY)}u.domElement.addEventListener("mousemove",r,!1),u.domElement.addEventListener("mouseup",a,!1),u.dispatchEvent(I)}}function r(e){if(u.enabled!==!1){e.preventDefault();var t=u.domElement===document?u.domElement.body:u.domElement;if(A===B.ROTATE){if(u.noRotate===!0)return;f.set(e.clientX,e.clientY),g.subVectors(f,m),u.rotateLeft(2*Math.PI*g.x/t.clientWidth*u.rotateSpeed),u.rotateUp(2*Math.PI*g.y/t.clientHeight*u.rotateSpeed),m.copy(f)}else if(A===B.DOLLY){if(u.noZoom===!0)return;R.set(e.clientX,e.clientY),x.subVectors(R,P),x.y>0?u.dollyIn():u.dollyOut(),P.copy(R)}else if(A===B.PAN){if(u.noPan===!0)return;y.set(e.clientX,e.clientY),E.subVectors(y,v),u.pan(E.x,E.y),v.copy(y)}}}function a(){u.enabled!==!1&&(u.domElement.removeEventListener("mousemove",r,!1),u.domElement.removeEventListener("mouseup",a,!1),u.dispatchEvent(L),A=B.NONE)}function s(e){if(u.enabled!==!1&&u.noZoom!==!0){e.preventDefault();var t=0;void 0!==e.wheelDelta?t=e.wheelDelta:void 0!==e.detail&&(t=-e.detail),t>0?u.dollyOut():u.dollyIn(),u.dispatchEvent(I),u.dispatchEvent(L)}}function l(e){if(u.enabled!==!1&&u.noKeys!==!0&&u.noPan!==!0)switch(e.keyCode){case u.keys.UP:u.pan(0,u.keyPanSpeed);break;case u.keys.BOTTOM:u.pan(0,-u.keyPanSpeed);break;case u.keys.LEFT:u.pan(u.keyPanSpeed,0);break;case u.keys.RIGHT:u.pan(-u.keyPanSpeed,0)}}function d(e){if(u.enabled!==!1){switch(e.touches.length){case 1:if(u.noRotate===!0)return;A=B.TOUCH_ROTATE,m.set(e.touches[0].pageX,e.touches[0].pageY);break;case 2:if(u.noZoom===!0)return;A=B.TOUCH_DOLLY;var t=e.touches[0].pageX-e.touches[1].pageX,o=e.touches[0].pageY-e.touches[1].pageY,i=Math.sqrt(t*t+o*o);P.set(0,i);break;case 3:if(u.noPan===!0)return;A=B.TOUCH_PAN,v.set(e.touches[0].pageX,e.touches[0].pageY);break;default:A=B.NONE}u.dispatchEvent(I)}}function c(e){if(u.enabled!==!1){e.preventDefault(),e.stopPropagation();var t=u.domElement===document?u.domElement.body:u.domElement;switch(e.touches.length){case 1:if(u.noRotate===!0)return;if(A!==B.TOUCH_ROTATE)return;f.set(e.touches[0].pageX,e.touches[0].pageY),g.subVectors(f,m),u.rotateLeft(2*Math.PI*g.x/t.clientWidth*u.rotateSpeed),u.rotateUp(2*Math.PI*g.y/t.clientHeight*u.rotateSpeed),m.copy(f);break;case 2:if(u.noZoom===!0)return;if(A!==B.TOUCH_DOLLY)return;var o=e.touches[0].pageX-e.touches[1].pageX,i=e.touches[0].pageY-e.touches[1].pageY,n=Math.sqrt(o*o+i*i);R.set(0,n),x.subVectors(R,P);var r=t.clientWidth,a=t.clientHeight,s=Math.sqrt(r*r+a*a),l=x.y/s;x.y>0?u.dollyOut(1-l):u.dollyIn(1+l),P.copy(R);break;case 3:if(u.noPan===!0)return;if(A!==B.TOUCH_PAN)return;y.set(e.touches[0].pageX,e.touches[0].pageY),E.subVectors(y,v),u.pan(E.x,E.y),v.copy(y);break;default:A=B.NONE}}}function p(){u.enabled!==!1&&(u.dispatchEvent(L),A=B.NONE)}this.object=e,this.domElement=void 0!==t?t:document,this.enabled=!0,this.target=new THREE.Vector3,this.center=this.target,this.noZoom=!1,this.zoomSpeed=1,this.minDistance=0,this.maxDistance=1/0,this.noRotate=!1,this.rotateSpeed=1,this.noPan=!1,this.keyPanSpeed=7,this.autoRotate=!1,this.autoRotateSpeed=2,this.fadeFactor=10,this.minPolarAngle=0,this.maxPolarAngle=Math.PI,this.noKeys=!1,this.keys={LEFT:37,UP:38,RIGHT:39,BOTTOM:40};var u=this,h=1e-6,m=new THREE.Vector2,f=new THREE.Vector2,g=new THREE.Vector2,v=new THREE.Vector2,y=new THREE.Vector2,E=new THREE.Vector2,T=new THREE.Vector3,b=new THREE.Vector3,P=new THREE.Vector2,R=new THREE.Vector2,x=new THREE.Vector2,C=0,w=0,S=1,H=new THREE.Vector3,M=new THREE.Vector3,B={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_DOLLY:4,TOUCH_PAN:5},A=B.NONE;this.target0=this.target.clone(),this.position0=this.object.position.clone();var V={type:"change"},I={type:"start"},L={type:"end"};this.rotateLeft=function(e){void 0===e&&(e=o()),w-=e},this.rotateUp=function(e){void 0===e&&(e=o()),C-=e},this.panLeft=function(e){var t=this.object.matrix.elements;T.set(t[0],t[1],t[2]),T.multiplyScalar(-e),H.add(T)},this.panUp=function(e){var t=this.object.matrix.elements;T.set(t[4],t[5],t[6]),T.multiplyScalar(e),H.add(T)},this.pan=function(e,t){var o=u.domElement===document?u.domElement.body:u.domElement;if(void 0!==u.object.fov){var i=u.object.position,n=i.clone().sub(u.target),r=n.length();r*=Math.tan(u.object.fov/2*Math.PI/180),u.panLeft(2*e*r/o.clientHeight),u.panUp(2*t*r/o.clientHeight)}else void 0!==u.object.top?(u.panLeft(e*(u.object.right-u.object.left)/o.clientWidth),u.panUp(t*(u.object.top-u.object.bottom)/o.clientHeight)):console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.")},this.dollyIn=function(e){void 0===e&&(e=i()),S/=e},this.dollyOut=function(e){void 0===e&&(e=i()),S*=e},this.update=function(e){var t=this.object.position.clone();b.copy(t).sub(this.target);var i=Math.atan2(b.x,b.z),n=Math.atan2(Math.sqrt(b.x*b.x+b.z*b.z),b.y);this.autoRotate&&this.rotateLeft(o());var r=Math.min(1,this.fadeFactor*e);i+=r*w,n+=r*C,n=Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle,n)),n=Math.max(h,Math.min(Math.PI-h,n));var a=b.length();a+=(S-1)*a*r,a=Math.max(this.minDistance,Math.min(this.maxDistance,a)),this.target.add(H.clone().multiplyScalar(r)),b.x=a*Math.sin(n)*Math.sin(i),b.y=a*Math.cos(n),b.z=a*Math.sin(n)*Math.cos(i),t.copy(this.target).add(b);var s={type:"proposeTransform",oldPosition:this.object.position,newPosition:t,objections:0,counterProposals:[]};if(this.dispatchEvent(s),s.objections>0&&s.counterProposals.length>0){var l=s.counterProposals;t.copy(l[0]),s.objections=0,s.counterProposals=[]}if(s.objections>0)w=0,C=0,S=1,H.set(0,0,0);else{this.object.position.copy(t),this.object.lookAt(this.target);var d=Math.max(0,1-this.fadeFactor*e);w*=d,C*=d,S=1+(S-1)*d,H.multiplyScalar(d)}M.distanceTo(this.object.position)>0&&(this.dispatchEvent(V),M.copy(this.object.position))},this.reset=function(){A=B.NONE,this.target.copy(this.target0),this.object.position.copy(this.position0),this.update()},this.domElement.addEventListener("contextmenu",function(e){e.preventDefault()},!1),this.domElement.addEventListener("mousedown",n,!1),this.domElement.addEventListener("mousewheel",s,!1),this.domElement.addEventListener("DOMMouseScroll",s,!1),this.domElement.addEventListener("touchstart",d,!1),this.domElement.addEventListener("touchend",p,!1),this.domElement.addEventListener("touchmove",c,!1),-1===this.domElement.tabIndex&&(this.domElement.tabIndex=2222),this.domElement.addEventListener("keydown",l,!1)},Potree.OrbitControls.prototype=Object.create(THREE.EventDispatcher.prototype),THREE.EarthControls=function(e,t,o){function i(e){if(s.enabled!==!1){e.preventDefault();var t=s.domElement.getBoundingClientRect(),o={x:(e.clientX-t.left)/s.domElement.clientWidth*2-1,y:2*-((e.clientY-t.top)/s.domElement.clientHeight)+1},i=getMousePointCloudIntersection(o,s.camera,s.renderer,s.pointclouds);if(i){var a=(new THREE.Plane).setFromNormalAndCoplanarPoint(new THREE.Vector3(0,1,0),i),u=new THREE.Vector3(o.x,o.y,.5);u.unproject(s.camera);var h=u.sub(s.camera.position).normalize(),m=new THREE.Ray(s.camera.position,h);g=m.intersectPlane(a),f=s.camera.clone(),f.rotation.copy(s.camera.rotation),c.set(e.clientX-t.left,e.clientY-t.top),p.set(e.clientX-t.left,e.clientY-t.top),s.scene.add(s.pivotNode),s.pivotNode.position.copy(g),e.button===THREE.MOUSE.LEFT?d=l.DRAG:e.button===THREE.MOUSE.RIGHT&&(d=l.ROTATE),s.domElement.addEventListener("mousemove",n,!1),s.domElement.addEventListener("mouseup",r,!1)}}}function n(e){if(s.enabled!==!1){e.preventDefault();var t=s.domElement.getBoundingClientRect();s.domElement===document?s.domElement.body:s.domElement;m.set(e.clientX-t.left-p.x,e.clientY-t.top-p.y),p.set(e.clientX-t.left,e.clientY-t.top)}}function r(){s.enabled!==!1&&(s.domElement.removeEventListener("mousemove",n,!1),s.domElement.removeEventListener("mouseup",r,!1),d=l.NONE,s.scene.remove(s.pivotNode))}function a(e){if(s.enabled!==!1&&s.noZoom!==!0){e.preventDefault();var t=s.domElement.getBoundingClientRect(),o=e.detail<0||e.wheelDelta>0?1:-1,i={x:(e.clientX-t.left)/s.domElement.clientWidth*2-1,y:2*-((e.clientY-t.top)/s.domElement.clientHeight)+1},n=getMousePointCloudIntersection(i,s.camera,s.renderer,s.pointclouds);if(n){var r=n.distanceTo(s.camera.position),a=(new THREE.Vector3).subVectors(n,s.camera.position).normalize();s.camera.position.add(a.multiplyScalar(.1*r*o))}}}this.camera=e,this.renderer=t,this.pointclouds=[],this.domElement=t.domElement,this.scene=o,this.enabled=!0;var s=this,l={NONE:-1,DRAG:0,ROTATE:1},d=l.NONE,c=new THREE.Vector2,p=new THREE.Vector2,u=new THREE.SphereGeometry(1,32,32),h=new THREE.MeshNormalMaterial({shading:THREE.SmoothShading,transparent:!0,opacity:.5});this.pivotNode=new THREE.Mesh(u,h);var m=new THREE.Vector2,f=null,g=null;this.minAngle=10/180*Math.PI,this.maxAngle=70/180*Math.PI,this.update=function(e){this.camera.position;this.camera.updateMatrixWorld();var t=new THREE.Object3D;if(t.position.copy(this.camera.position),t.rotation.copy(this.camera.rotation),t.updateMatrix(),t.updateMatrixWorld(),g){if(d===l.DRAG){var o=(new THREE.Plane).setFromNormalAndCoplanarPoint(new THREE.Vector3(0,1,0),g),i={x:p.x/this.domElement.clientWidth*2-1,y:2*-(p.y/this.domElement.clientHeight)+1},n=new THREE.Vector3(i.x,i.y,.5);n.unproject(f);var r=n.sub(f.position).normalize(),a=new THREE.Ray(f.position,r),s=a.distanceToPlane(o);if(s>0){var c=(new THREE.Vector3).subVectors(g,r.clone().multiplyScalar(s));t.position.copy(c)}}else if(d===l.ROTATE){var u=m.clone().multiplyScalar(e);u.x*=.3,u.y*=-.2;var h=new THREE.Object3D,v=new THREE.Object3D;h.add(v),h.position.copy(g),v.position.copy(this.camera.position).sub(g),v.rotation.copy(this.camera.rotation),h.rotation.y+=-u.x;var r=this.camera.getWorldDirection(),y=new THREE.Vector3(0,1,0),E=(new THREE.Vector3).crossVectors(y,r),T=v.position.clone();T.y=0,T.normalize();var b=T.dot(v.position.clone().normalize()),P=Math.acos(b);v.position.y<0&&(P=-P);var R=0;R=u.y>0?u.y-Math.max(0,this.minAngle-(P-u.y)):u.y+Math.max(0,P-u.y-this.maxAngle),h.rotateOnAxis(E,-R),h.updateMatrixWorld(),t.position.copy(v.getWorldPosition()),t.quaternion.copy(v.getWorldQuaternion())}var x={type:"proposeTransform",oldPosition:this.camera.position,newPosition:t.position,objections:0};this.dispatchEvent(x),x.objections>0||(this.camera.position.copy(t.position),this.camera.rotation.copy(t.rotation));var C=this.pivotNode.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse),w=Math.abs(C.z/30),S=this.pivotNode.scale.length();this.pivotNode.scale.multiplyScalar(w/S)}m.set(0,0)},this.reset=function(){d=l.NONE,this.camera.position.copy(this.position0)},this.domElement.addEventListener("contextmenu",function(e){e.preventDefault()},!1),this.domElement.addEventListener("mousedown",i,!1), +this.domElement.addEventListener("mousewheel",a,!1),this.domElement.addEventListener("DOMMouseScroll",a,!1)},THREE.EarthControls.prototype=Object.create(THREE.EventDispatcher.prototype),LRU.prototype.size=function(){return this.elements},LRU.prototype.contains=function(e){return null==this.items[e.id]},LRU.prototype.touch=function(e){if(e.loaded){var t;null==this.items[e.id]?(t=new LRUItem(e),t.previous=this.last,this.last=t,null!==t.previous&&(t.previous.next=t),this.items[e.id]=t,this.elements++,null===this.first&&(this.first=t),this.numPoints+=e.numPoints):(t=this.items[e.id],null===t.previous?null!==t.next&&(this.first=t.next,this.first.previous=null,t.previous=this.last,t.next=null,this.last=t,t.previous.next=t):null===t.next||(t.previous.next=t.next,t.next.previous=t.previous,t.previous=this.last,t.next=null,this.last=t,t.previous.next=t))}},LRU.prototype.remove=function(e){var t=this.items[e.id];t&&(1===this.elements?(this.first=null,this.last=null):(t.previous||(this.first=t.next,this.first.previous=null),t.next||(this.last=t.previous,this.last.next=null),t.previous&&t.next&&(t.previous.next=t.next,t.next.previous=t.previous)),delete this.items[e.id],this.elements--,this.numPoints-=e.numPoints)},LRU.prototype.getLRUItem=function(){if(null===this.first)return null;var e=this.first;return e.node},LRU.prototype.toString=function(){for(var e="{ ",t=this.first;null!==t;)e+=t.node.id,null!==t.next&&(e+=", "),t=t.next;return e+="}",e+="("+this.size()+")"},LRU.prototype.freeMemory=function(){if(!(this.elements<=1))for(;this.numPoints>Potree.pointLoadLimit;){var e=this.first,t=e.node;this.disposeDescendants(t)}},LRU.prototype.disposeDescendants=function(e){var t=[];for(t.push(e);t.length>0;){var o=t.pop();o.dispose(),this.remove(o);for(var i in o.children)if(o.children.hasOwnProperty(i)){var n=o.children[i];n.loaded&&t.push(o.children[i])}}},Potree.Annotation=function(e,t){var o=this;Potree.Annotation.counter++,this.viewer=e,this.ordinal=t.title||Potree.Annotation.counter,this.title=t.title||"No Title",this.description=t.description||"",this.scene=t.scene||null,this.position=t.position||new THREE.Vector3(0,0,0),this.cameraPosition=t.cameraPosition,this.cameraTarget=t.cameraTarget||this.position,this.view=t.view||null,this.keepOpen=!1,this.descriptionVisible=!1,this.domElement=document.createElement("div"),this.domElement.style.position="absolute",this.domElement.style.opacity="0.5",this.domElement.style.padding="10px",this.domElement.style.whiteSpace="nowrap",this.domElement.className="annotation",this.elOrdinal=document.createElement("div"),this.elOrdinal.style.position="relative",this.elOrdinal.style.color="white",this.elOrdinal.style.backgroundColor="black",this.elOrdinal.style.borderRadius="1.5em",this.elOrdinal.style.fontSize="1em",this.elOrdinal.style.opacity="1",this.elOrdinal.style.margin="auto",this.elOrdinal.style.zIndex="100",this.elOrdinal.style.width="fit-content",this.domElement.appendChild(this.elOrdinal),this.domDescription=document.createElement("div"),this.domDescription.style.position="relative",this.domDescription.style.color="white",this.domDescription.style.backgroundColor="black",this.domDescription.style.padding="10px",this.domDescription.style.margin="5px 0px 0px 0px",this.domDescription.style.borderRadius="4px",this.domDescription.style.display="none",this.domDescription.className="annotation",this.domElement.appendChild(this.domDescription),this.elOrdinal.onmouseenter=function(){},this.elOrdinal.onmouseleave=function(){},this.elOrdinal.onclick=function(){o.moveHere(o.viewer.camera)},this.elOrdinalText=document.createElement("span"),this.elOrdinalText.style.display="inline-block",this.elOrdinalText.style.verticalAlign="middle",this.elOrdinalText.style.lineHeight="1.5em",this.elOrdinalText.style.textAlign="center",this.elOrdinalText.style.fontFamily="Arial",this.elOrdinalText.style.fontWeight="bold",this.elOrdinalText.style.padding="1px 8px 0px 8px",this.elOrdinalText.style.cursor="default",this.elOrdinalText.innerHTML=this.ordinal,this.elOrdinalText.userSelect="none",this.elOrdinal.appendChild(this.elOrdinalText),this.elDescriptionText=document.createElement("span"),this.elDescriptionText.style.color="#ffffff",this.elDescriptionText.innerHTML=this.description,this.domDescription.appendChild(this.elDescriptionText),this.domElement.onmouseenter=function(){o.domElement.style.opacity="0.8",o.domElement.style.zIndex="1000",o.description&&(o.descriptionVisible=!0,o.domDescription.style.display="block")},this.domElement.onmouseleave=function(){o.domElement.style.opacity="0.5",o.domElement.style.zIndex="100",o.descriptionVisible=!0,o.domDescription.style.display="none"},this.moveHere=function(e){var t=800,i=TWEEN.Easing.Quartic.Out,n=new TWEEN.Tween(e.position).to(o.cameraPosition,t);n.easing(i),n.start();var r=e.position.distanceTo(o.cameraTarget),a=(new THREE.Vector3).addVectors(e.position,e.getWorldDirection().clone().multiplyScalar(r)),n=new TWEEN.Tween(a).to(o.cameraTarget,t);n.easing(i),n.onUpdate(function(){e.lookAt(a),o.viewer.orbitControls.target.copy(a)}),n.onComplete(function(){e.lookAt(a),o.viewer.orbitControls.target.copy(a),o.dispatchEvent({type:"focusing_finished",target:o})}),o.dispatchEvent({type:"focusing_started",target:o}),n.start()},this.dispose=function(){this.domElement.parentElement&&this.domElement.parentElement.removeChild(this.domElement)}},Potree.Annotation.prototype=Object.create(THREE.EventDispatcher.prototype),Potree.Annotation.counter=0,Potree.ProfileData=function(e){this.profile=e,this.segments=[],this.boundingBox=new THREE.Box3,this.projectedBoundingBox=new THREE.Box2;for(var t=new THREE.Vector3,o=0;o0&&(l=-l),function(e){var t=(new THREE.Matrix4).makeTranslation(-i.x,0,-i.z),o=(new THREE.Matrix4).makeRotationY(-l),n=(new THREE.Matrix4).makeTranslation(r.x,0,0),a=e.clone();return a.applyMatrix4(t),a.applyMatrix4(o),a.applyMatrix4(n),a}}(i,n,t.clone()),g={start:i,end:n,cutPlane:h,halfPlane:m,length:l,points:null,project:f};this.segments.push(g),t.x+=l,t.y+=n.y-i.y}this.projectedBoundingBox.min.x=0,this.projectedBoundingBox.min.y=Number.POSITIVE_INFINITY,this.projectedBoundingBox.max.x=t.x,this.projectedBoundingBox.max.y=Number.NEGATIVE_INFINITY,this.size=function(){for(var e=0,t=0;ti;i++){var n=t.children[i];n&&e.nodeIntersectsProfile(n,this.profile)&&o.push(n)}for(;o.length>0;){var t=o.pop(),r=t.boundingSphere.radius;if(this.priorityQueue.push({node:t,weight:r}),t.leveli;i++){var n=t.children[i];n&&e.nodeIntersectsProfile(n,this.profile)&&o.push(n)}}},this.update=function(){for(var t=[],o=0;o0&&(this.getPointsInsideProfile(t,this.temporaryResult),this.temporaryResult.size()>100&&(this.pointsServed+=this.temporaryResult.size(),i.onProgress({request:this,points:this.temporaryResult}),this.temporaryResult=new Potree.ProfileData(this.profile))),0===this.priorityQueue.size()){this.temporaryResult.size()>0&&(this.pointsServed+=this.temporaryResult.size(),i.onProgress({request:this,points:this.temporaryResult}),this.temporaryResult=new Potree.ProfileData(this.profile)),i.onFinish({request:this});var a=e.profileRequests.indexOf(this);a>=0&&e.profileRequests.splice(a,1)}},this.getPointsInsideProfile=function(o,i){for(var n=0;nh;h++){var m=new THREE.Vector3(c[3*h],c[3*h+1],c[3*h+2]);m.applyMatrix4(e.matrixWorld);var f=Math.abs(r.cutPlane.distanceToPoint(m)),g=Math.abs(r.halfPlane.distanceToPoint(m));if(f0&&(i.boundingBox.expandByPoint(r.points.boundingBox.min),i.boundingBox.expandByPoint(r.points.boundingBox.max),i.projectedBoundingBox.expandByPoint(new THREE.Vector2(0,i.boundingBox.min.y)),i.projectedBoundingBox.expandByPoint(new THREE.Vector2(0,i.boundingBox.max.y)))}},this.cancel=function(){i.onCancel(),this.priorityQueue=new BinaryHeap(function(e){return 1/e.weight});var t=e.profileRequests.indexOf(this);t>=0&&e.profileRequests.splice(t,1)},this.initialize()},Potree.PointCloudOctreeNode=function(){this.children={},this.sceneNode=null,this.octree=null,this.getNumPoints=function(){return this.geometryNode.numPoints},this.isLoaded=function(){return!0},this.isTreeNode=function(){return!0},this.isGeometryNode=function(){return!1},this.getLevel=function(){return this.geometryNode.level},this.getBoundingSphere=function(){return this.geometryNode.boundingSphere},this.getBoundingBox=function(){return this.geometryNode.boundingBox},this.getChildren=function(){for(var e=[],t=0;8>t;t++)this.children[t]&&e.push(this.children[t]);return e}},Potree.PointCloudOctreeNode.prototype=Object.create(Potree.PointCloudTreeNode.prototype),Potree.PointCloudOctree=function(e,t){Potree.PointCloudTree.call(this),this.pcoGeometry=e,this.boundingBox=this.pcoGeometry.tightBoundingBox,this.boundingSphere=this.boundingBox.getBoundingSphere(),this.material=t||new Potree.PointCloudMaterial,this.visiblePointsTarget=2e6,this.minimumNodePixelSize=150,this.level=0,this.position.sub(e.offset),this.updateMatrix(),this.showBoundingBox=!1,this.boundingBoxNodes=[],this.loadQueue=[],this.visibleBounds=new THREE.Box3,this.visibleNodes=[],this.visibleGeometry=[],this.pickTarget=null,this.generateDEM=!1,this.profileRequests=[],this.projection=e.projection,this.root=this.pcoGeometry.root},Potree.PointCloudOctree.prototype=Object.create(Potree.PointCloudTree.prototype),Potree.PointCloudOctree.prototype.toTreeNode=function(e,t){var o=new Potree.PointCloudOctreeNode,i=new THREE.PointCloud(e.geometry,pointcloud.material);o.geometryNode=e,o.sceneNode=i,o.pointcloud=pointcloud,o.children={};for(var n in e.children)o.children[n]=e.children[n];if(t){var r=parseInt(e.name[e.name.length-1]);t.sceneNode.add(i),t.children[r]=o}else this.root=o,this.add(i);var a=function(){var i=parseInt(e.name[e.name.length-1]);t.sceneNode.remove(o.sceneNode),t.children[i]=e};return e.oneTimeDisposeHandlers.push(a),o},Potree.PointCloudOctree.prototype.updateVisibleBounds=function(){for(var e=[],t=0;to?-1:o>i?1:0};t.sort(n);for(var r=0;rl;l++){var d=a.children[l];d instanceof Potree.PointCloudOctreeNode&&d.sceneNode.visible&&t.indexOf(d)>=0&&s.push(d)}s.sort(function(e,t){return e.geometryNode.namet.geometryNode.name?1:0}),i[3*r+0]=0,i[3*r+1]=0,i[3*r+2]=0;for(var l=0;l0;){var e=t.shift();e.visible=!1;for(var o=0;o0&&(c=-c),function(e){var t=(new THREE.Matrix4).makeTranslation(-n.x,-s.min.y,-n.z),o=(new THREE.Matrix4).makeRotationY(-c),i=(new THREE.Matrix4).makeTranslation(a.x,0,0),r=e.clone();return r.applyMatrix4(t),r.applyMatrix4(o),r.applyMatrix4(i),r}}(a,s,c.clone(),n.boundingBox.clone());d.project=p,c.x+=new THREE.Vector3(a.x,0,a.z).distanceTo(new THREE.Vector3(s.x,0,s.z)),c.y+=s.y-a.y}return n.projectedBoundingBox.min.x=0,n.projectedBoundingBox.min.y=n.boundingBox.min.y,n.projectedBoundingBox.max.x=c.x,n.projectedBoundingBox.max.y=n.boundingBox.max.y,n},Potree.PointCloudOctree.prototype.getProfile=function(e,t,o,i,n){if(void 0===n){var r=[];r.push(this);for(var a=(new THREE.Vector3).addVectors(t,e).multiplyScalar(.5),s=(new THREE.Vector3).subVectors(t,e).length(),l=(new THREE.Vector3).subVectors(t,e).normalize(),d=new THREE.Vector3(0,1,0),c=(new THREE.Vector3).crossVectors(l,d).normalize(),p=c,u=(new THREE.Plane).setFromNormalAndCoplanarPoint(p,e),h=(new THREE.Plane).setFromNormalAndCoplanarPoint(l,a),m=null,f=new THREE.Box3;r.length>0;){var g=r.shift(),v=0;if(g instanceof THREE.PointCloud){var y=g.geometry,E=y.attributes.position,T=E.array,b=g.numPoints;if(!m){m={};for(var P in y.attributes)y.attributes.hasOwnProperty(P)&&("indices"===P||(m[P]=[]))}for(var R=0;b>R;R++){var x=new THREE.Vector3(T[3*R],T[3*R+1],T[3*R+2]);x.applyMatrix4(this.matrixWorld);var C=Math.abs(u.distanceToPoint(x)),w=Math.abs(h.distanceToPoint(x));if(o/2>C&&s/2>w){f.expandByPoint(x);for(var P in y.attributes)if(y.attributes.hasOwnProperty(P))if("position"===P)m[P].push(x);else if("indices"===P);else{var S=y.attributes[P];if(1===S.itemSize)m[P].push(S.array[R+M]);else{for(var H=[],M=0;M0&&(a=-a),function(e){var t=(new THREE.Matrix4).makeTranslation(-o.x,-o.y,-o.z),i=(new THREE.Matrix4).makeRotationY(-a),n=e.clone();return n.applyMatrix4(t),n.applyMatrix4(i),n}}(e,t);return m.project=V,m.boundingBox=f,m}var I=new Potree.ProfileRequest(e,t,o,i,n);this.profileRequests.push(I)},Potree.PointCloudOctree.prototype.getVisibleExtent=function(){return this.visibleBounds.applyMatrix4(this.matrixWorld)},Potree.PointCloudOctree.prototype.pick=function(e,t,o,i){var i=i||{},n=i.pickWindowSize||17,r=i.pickOutsideClipRegion||!1,a=this.nodesOnRay(this.visibleNodes,o);if(0===a.length)return null;var s=Math.ceil(e.domElement.clientWidth),l=Math.ceil(e.domElement.clientHeight),d=(new THREE.Vector3).addVectors(t.position,o.direction).project(t);d.addScalar(1).multiplyScalar(.5),d.x*=s,d.y*=l,this.pickTarget?(this.pickTarget.width!=s||this.pickTarget.height!=l)&&(this.pickTarget.dispose(),this.pickTarget=new THREE.WebGLRenderTarget(1,1,{minFilter:THREE.LinearFilter,magFilter:THREE.NearestFilter,format:THREE.RGBAFormat})):this.pickTarget=new THREE.WebGLRenderTarget(1,1,{minFilter:THREE.LinearFilter,magFilter:THREE.NearestFilter,format:THREE.RGBAFormat}),this.pickTarget.setSize(s,l),this.pickMaterial||(this.pickMaterial=new Potree.PointCloudMaterial,this.pickMaterial.pointColorType=Potree.PointColorType.POINT_INDEX),this.pickMaterial.pointSizeType=this.material.pointSizeType,this.pickMaterial.size=this.material.size,this.pickMaterial.pointShape=this.material.pointShape,this.pickMaterial.interpolate=this.material.interpolate,this.pickMaterial.minSize=this.material.minSize,this.pickMaterial.maxSize=this.material.maxSize,this.pickMaterial.classification=this.material.classification,r?this.pickMaterial.clipMode=Potree.ClipMode.DISABLED:(this.pickMaterial.clipMode=this.material.clipMode,this.material.clipMode===Potree.ClipMode.CLIP_OUTSIDE?this.pickMaterial.setClipBoxes(this.material.clipBoxes):this.pickMaterial.setClipBoxes([])),this.updateMaterial(this.pickMaterial,a,t,e);var c=e.context;c.enable(c.SCISSOR_TEST),c.scissor(d.x-(n-1)/2,d.y-(n-1)/2,n,n),c.disable(c.SCISSOR_TEST);var p=this.pickMaterial;e.setRenderTarget(this.pickTarget),e.state.setDepthTest(p.depthTest),e.state.setDepthWrite(p.depthWrite),e.state.setBlending(THREE.NoBlending),e.clear(e.autoClearColor,e.autoClearDepth,e.autoClearStencil),a.length>0&&a.push(a[0]);for(var u=0;ux;x++)for(var C=0;n>C;C++){var w=x+C*n,S=Math.pow(x-(n-1)/2,2)+Math.pow(C-(n-1)/2,2),H=T[4*w+3];T[4*w+3]=0;var M=b[w];(0!==M||0!==H)&&P>S&&(R={pIndex:M,pcIndex:H-1},P=S)}if(R){var B={},A=a[R.pcIndex].sceneNode,V=A.geometry.attributes;for(var I in V)if(V.hasOwnProperty(I)){var L=A.geometry.attributes[I];if("position"===I){var N=L.array,D=N[3*R.pIndex+0],G=N[3*R.pIndex+1],_=N[3*R.pIndex+2],k=new THREE.Vector3(D,G,_);k.applyMatrix4(this.matrixWorld),B[I]=k}else if("indices"===I);else if(1===L.itemSize)B[I]=L.array[R.pIndex];else{for(var O=[],z=0;zp;p++){var u=a[3*p+0],h=a[3*p+1],m=a[3*p+2],f=new THREE.Vector3(u,h,m).applyMatrix4(i),g=parseInt(s*(f.x-n.min.x)/r.x),v=parseInt(s*(f.z-n.min.z)/r.z);g=Math.min(g,s-1),v=Math.min(v,s-1);var y=g+v*s;l[y]||(l[y]=[]),l[y].push(f.y)}for(var p=0;p.5&&r.x.5&&r.y0;){var r=n.shift(),a=r.dem,s=(a.demSize,a.boundingBox2D),l=s.containsPoint(t);if(l){var d=o(a);if(i?null!=d&&d>0&&(i=d):i=d,r.level<=6)for(var c=0;8>c;c++){var p=r.children[c];"undefined"!=typeof p&&"undefined"!=typeof p.dem&&n.push(p)}}}return i},Object.defineProperty(Potree.PointCloudOctree.prototype,"progress",{get:function(){return this.visibleNodes.length/this.visibleGeometry.length}});var nodesLoadTimes={};Potree.PointCloudOctreeGeometry=function(){this.url=null,this.octreeDir=null,this.spacing=0,this.boundingBox=null,this.root=null,this.numNodesLoading=0,this.nodes=null,this.pointAttributes=null,this.hierarchyStepSize=-1,this.loader=null},Potree.PointCloudOctreeGeometryNode=function(e,t,o){this.id=Potree.PointCloudOctreeGeometryNode.IDCount++,this.name=e,this.index=parseInt(e.charAt(e.length-1)),this.pcoGeometry=t,this.geometry=null,this.boundingBox=o,this.boundingSphere=o.getBoundingSphere(),this.children={},this.numPoints=0,this.level=null,this.loaded=!1,this.oneTimeDisposeHandlers=[]},Potree.PointCloudOctreeGeometryNode.IDCount=0,Potree.PointCloudOctreeGeometryNode.prototype=Object.create(Potree.PointCloudTreeNode.prototype),Potree.PointCloudOctreeGeometryNode.prototype.isGeometryNode=function(){return!0},Potree.PointCloudOctreeGeometryNode.prototype.isTreeNode=function(){return!1},Potree.PointCloudOctreeGeometryNode.prototype.isLoaded=function(){return this.loaded},Potree.PointCloudOctreeGeometryNode.prototype.getBoundingSphere=function(){return this.boundingSphere},Potree.PointCloudOctreeGeometryNode.prototype.getBoundingBox=function(){return this.boundingBox},Potree.PointCloudOctreeGeometryNode.prototype.getChildren=function(){for(var e=[],t=0;8>t;t++)this.children[t]&&e.push(this.children[t]);return e},Potree.PointCloudOctreeGeometryNode.prototype.getBoundingBox=function(){return this.boundingBox},Potree.PointCloudOctreeGeometryNode.prototype.getURL=function(){var e="",t=this.pcoGeometry.loader.version;return t.equalOrHigher("1.5")?e=this.pcoGeometry.octreeDir+"/"+this.getHierarchyPath()+"/"+this.name:t.equalOrHigher("1.4")?e=this.pcoGeometry.octreeDir+"/"+this.name:t.upTo("1.3")&&(e=this.pcoGeometry.octreeDir+"/"+this.name),e},Potree.PointCloudOctreeGeometryNode.prototype.getHierarchyPath=function(){for(var e="r/",t=this.pcoGeometry.hierarchyStepSize,o=this.name.substr(1),i=Math.floor(o.length/t),n=0;i>n;n++)e+=o.substr(n*t,t)+"/";return e=e.slice(0,-1)},Potree.PointCloudOctreeGeometryNode.prototype.addChild=function(e){this.children[e.index]=e,e.parent=this},Potree.PointCloudOctreeGeometryNode.prototype.load=function(){this.loading===!0||this.loaded===!0||this.pcoGeometry.numNodesLoading>3||(this.loading=!0,this.pcoGeometry.numNodesLoading++,this.pcoGeometry.loader.version.equalOrHigher("1.5")&&this.level%this.pcoGeometry.hierarchyStepSize===0&&this.hasChildren?this.loadHierachyThenPoints():this.loadPoints())},Potree.PointCloudOctreeGeometryNode.prototype.loadPoints=function(){this.pcoGeometry.loader.load(this)},Potree.PointCloudOctreeGeometryNode.prototype.loadHierachyThenPoints=function(){var e=this,t=function(e,t){var o=(t.byteLength/5,new DataView(t)),i=[],n=o.getUint8(0),r=o.getUint32(1,!0);e.numPoints=r,i.push({children:n,numPoints:r,name:e.name});for(var a=[],s=5;i.length>0;){for(var l=i.shift(),d=1,c=0;8>c;c++){if(0!==(l.children&d)){var p=l.name+c,u=o.getUint8(s),h=o.getUint32(s+1,!0);i.push({children:u,numPoints:h,name:p}),a.push({children:u,numPoints:h,name:p}),s+=5}d=2*d}if(s===t.byteLength)break}var m={};m[e.name]=e;for(var f=e.pcoGeometry,c=0;c0,E.addChild(P),m[g]=P}e.loadPoints()};if(e.level%e.pcoGeometry.hierarchyStepSize===0){var o=e.pcoGeometry.octreeDir+"/"+e.getHierarchyPath()+"/"+e.name+".hrc",i=new XMLHttpRequest;i.open("GET",o,!0),i.responseType="arraybuffer",i.overrideMimeType("text/plain; charset=x-user-defined"),i.onreadystatechange=function(){if(4===i.readyState)if(200===i.status||0===i.status){var o=i.response;t(e,o)}else console.log("Failed to load file! HTTP status: "+i.status+", file: "+url)};try{i.send(null)}catch(n){console.log("fehler beim laden der punktwolke: "+n)}}},Potree.PointCloudOctreeGeometryNode.prototype.getNumPoints=function(){return this.numPoints},Potree.PointCloudOctreeGeometryNode.prototype.dispose=function(){if(this.geometry&&null!=this.parent){this.geometry.dispose(),this.geometry=null,this.loaded=!1;for(var e=0;e1?"."+x[1]:"";for(var t=/(\d+)(\d{3})/;t.test(x1);)x1=x1.replace(t,"$1,$2");return x1+x2},Potree.utils.createWorker=function(e){var t=new Blob([e],{type:"application/javascript"}),o=new Worker(URL.createObjectURL(t));return o},Potree.utils.loadSkybox=function(e){var t=new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,1,1e5),o=new THREE.Scene,i=".jpg",n=[e+"px"+i,e+"nx"+i,e+"py"+i,e+"ny"+i,e+"pz"+i,e+"nz"+i],r=THREE.ImageUtils.loadTextureCube(n,THREE.CubeRefractionMapping),a={uniforms:{tCube:{type:"t",value:r},tFlip:{type:"f",value:-1}},vertexShader:THREE.ShaderLib.cube.vertexShader,fragmentShader:THREE.ShaderLib.cube.fragmentShader},s=new THREE.ShaderMaterial({fragmentShader:a.fragmentShader,vertexShader:a.vertexShader,uniforms:a.uniforms,depthWrite:!1,side:THREE.BackSide}),l=new THREE.Mesh(new THREE.BoxGeometry(100,100,100),s);return o.add(l),{camera:t,scene:o}},Potree.utils.createGrid=function(e,t,o,i){for(var n=new THREE.LineBasicMaterial({color:i||8947848}),r=new THREE.Geometry,a=0;t>=a;a++)r.vertices.push(new THREE.Vector3(-(o*e)/2,0,a*o-o*t/2)),r.vertices.push(new THREE.Vector3(+(o*e)/2,0,a*o-o*t/2));for(var a=0;e>=a;a++)r.vertices.push(new THREE.Vector3(a*o-o*e/2,0,-(o*t)/2)),r.vertices.push(new THREE.Vector3(a*o-o*e/2,0,+(o*t)/2));var s=new THREE.Line(r,n,THREE.LinePieces);return s.receiveShadow=!0,s},Potree.utils.createBackgroundTexture=function(e,t){function o(e,t){return 1/(2*Math.PI)*Math.exp(-(e*e+t*t)/2)}var i=THREE.ImageUtils.generateDataTexture(e,t,new THREE.Color);i.magFilter=THREE.NearestFilter;for(var n=i.image.data,r=[1,1.5,1.7],a=o(0,0),s=0;e>s;s++)for(var l=0;t>l;l++){var d=2*(s/e)-1,c=2*(l/t)-1,p=s+e*l,u=o(2*d,2*c)/a,h=(Math.random()+Math.random()+Math.random())/3;h=(.5*u+.5)*h*.03,h=.4*h,n[3*p+0]=255*(u/15+.05+h)*r[0],n[3*p+1]=255*(u/15+.05+h)*r[1],n[3*p+2]=255*(u/15+.05+h)*r[2]}return i},Potree.utils.topView=function(e,t){e.position.set(0,1,0),e.rotation.set(-Math.PI/2,0,0),e.zoomTo(t,1)},Potree.utils.frontView=function(e,t){e.position.set(0,0,1),e.rotation.set(0,0,0),e.zoomTo(t,1)},Potree.utils.leftView=function(e,t){e.position.set(-1,0,0),e.rotation.set(0,-Math.PI/2,0),e.zoomTo(t,1)},Potree.utils.rightView=function(e,t){e.position.set(1,0,0),e.rotation.set(0,Math.PI/2,0),e.zoomTo(t,1); +},Potree.utils.frustumSphereIntersection=function(e,t){for(var o=e.planes,i=t.center,n=-t.radius,r=Number.MAX_VALUE,a=0;6>a;a++){var s=o[a].distanceToPoint(i);if(n>s)return 0;r=Math.min(r,s)}return r>=t.radius?2:1},Potree.utils.screenPass=new function(){this.screenScene=new THREE.Scene,this.screenQuad=new THREE.Mesh(new THREE.PlaneBufferGeometry(2,2,0)),this.screenQuad.material.depthTest=!0,this.screenQuad.material.depthWrite=!0,this.screenQuad.material.transparent=!0,this.screenScene.add(this.screenQuad),this.camera=new THREE.Camera,this.render=function(e,t,o){this.screenQuad.material=t,void 0===typeof o?e.render(this.screenScene,this.camera):e.render(this.screenScene,this.camera,o)}},Potree.Features=function(){var e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(null===t)return null;var o,i=t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.HIGH_FLOAT),n=t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.MEDIUM_FLOAT),r=(t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.LOW_FLOAT),t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.HIGH_FLOAT)),a=t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.MEDIUM_FLOAT),s=(t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.LOW_FLOAT),i.precision>0&&r.precision>0),l=n.precision>0&&a.precision>0;return o=s?"highp":l?"mediump":"lowp",{SHADER_INTERPOLATION:{isSupported:function(){var e=!0;return e=e&&t.getExtension("EXT_frag_depth"),e=e&&t.getParameter(t.MAX_VARYING_VECTORS)>=8}},SHADER_SPLATS:{isSupported:function(){var e=!0;return e=e&&t.getExtension("EXT_frag_depth"),e=e&&t.getExtension("OES_texture_float"),e=e&&t.getParameter(t.MAX_VARYING_VECTORS)>=8}},SHADER_EDL:{isSupported:function(){var e=!0;return e=e&&t.getExtension("EXT_frag_depth"),e=e&&t.getExtension("OES_texture_float"),e=e&&t.getParameter(t.MAX_VARYING_VECTORS)>=8}},precision:o}}(),Potree.TextSprite=function(e){THREE.Object3D.call(this);var t=new THREE.Texture;t.minFilter=THREE.LinearFilter,t.magFilter=THREE.LinearFilter;var o=new THREE.SpriteMaterial({map:t,useScreenCoordinates:!1,depthTest:!1,depthWrite:!1});this.material=o,this.sprite=new THREE.Sprite(o),this.add(this.sprite),this.borderThickness=4,this.fontface="Arial",this.fontsize=28,this.borderColor={r:0,g:0,b:0,a:1},this.backgroundColor={r:255,g:255,b:255,a:1},this.textColor={r:255,g:255,b:255,a:1},this.text="",this.setText(e)},Potree.TextSprite.prototype=new THREE.Object3D,Potree.TextSprite.prototype.setText=function(e){this.text!==e&&(this.text=e,this.update())},Potree.TextSprite.prototype.setTextColor=function(e){this.textColor=e,this.update()},Potree.TextSprite.prototype.setBorderColor=function(e){this.borderColor=e,this.update()},Potree.TextSprite.prototype.setBackgroundColor=function(e){this.backgroundColor=e,this.update()},Potree.TextSprite.prototype.update=function(){var e=document.createElement("canvas"),t=e.getContext("2d");t.font="Bold "+this.fontsize+"px "+this.fontface;var o=t.measureText(this.text),i=o.width,n=5,r=2*n+i+2*this.borderThickness,a=1.4*this.fontsize+2*this.borderThickness,e=document.createElement("canvas"),t=e.getContext("2d");t.canvas.width=r,t.canvas.height=a,t.font="Bold "+this.fontsize+"px "+this.fontface,t.fillStyle="rgba("+this.backgroundColor.r+","+this.backgroundColor.g+","+this.backgroundColor.b+","+this.backgroundColor.a+")",t.strokeStyle="rgba("+this.borderColor.r+","+this.borderColor.g+","+this.borderColor.b+","+this.borderColor.a+")",t.lineWidth=this.borderThickness,this.roundRect(t,this.borderThickness/2,this.borderThickness/2,i+this.borderThickness+2*n,1.4*this.fontsize+this.borderThickness,6),t.strokeStyle="rgba(0, 0, 0, 1.0)",t.strokeText(this.text,this.borderThickness+n,this.fontsize+this.borderThickness),t.fillStyle="rgba("+this.textColor.r+","+this.textColor.g+","+this.textColor.b+","+this.textColor.a+")",t.fillText(this.text,this.borderThickness+n,this.fontsize+this.borderThickness);var s=new THREE.Texture(e);s.minFilter=THREE.LinearFilter,s.magFilter=THREE.LinearFilter,s.needsUpdate=!0,this.sprite.material.map=s,this.sprite.scale.set(.01*r,.01*a,1)},Potree.TextSprite.prototype.roundRect=function(e,t,o,i,n,r){e.beginPath(),e.moveTo(t+r,o),e.lineTo(t+i-r,o),e.quadraticCurveTo(t+i,o,t+i,o+r),e.lineTo(t+i,o+n-r),e.quadraticCurveTo(t+i,o+n,t+i-r,o+n),e.lineTo(t+r,o+n),e.quadraticCurveTo(t,o+n,t,o+n-r),e.lineTo(t,o+r),e.quadraticCurveTo(t,o,t+r,o),e.closePath(),e.fill(),e.stroke()},Potree.Version=function(e){this.version=e;var t=-1===e.indexOf(".")?e.length:e.indexOf(".");this.versionMajor=parseInt(e.substr(0,t)),this.versionMinor=parseInt(e.substr(t+1)),0===this.versionMinor.length&&(this.versionMinor=0)},Potree.Version.prototype.newerThan=function(e){var t=new Potree.Version(e);return this.versionMajor>t.versionMajor?!0:this.versionMajor===t.versionMajor&&this.versionMinor>t.versionMinor?!0:!1},Potree.Version.prototype.equalOrHigher=function(e){var t=new Potree.Version(e);return this.versionMajor>t.versionMajor?!0:this.versionMajor===t.versionMajor&&this.versionMinor>=t.versionMinor?!0:!1},Potree.Version.prototype.upTo=function(e){return!this.newerThan(e)},Potree.Measure=function(){var e=this;THREE.Object3D.call(this),this.points=[],this._showDistances=!0,this._showCoordinates=!1,this._showArea=!0,this._closed=!0,this._showAngles=!1,this.maxMarkers=Number.MAX_SAFE_INTEGER,this.spheres=[],this.edges=[],this.sphereLabels=[],this.edgeLabels=[],this.angleLabels=[],this.coordinateLabels=[],this.areaLabel=new Potree.TextSprite(""),this.areaLabel.setBorderColor({r:0,g:0,b:0,a:.8}),this.areaLabel.setBackgroundColor({r:0,g:0,b:0,a:.3}),this.areaLabel.setTextColor({r:180,g:220,b:180,a:1}),this.areaLabel.material.depthTest=!1,this.areaLabel.material.opacity=1,this.areaLabel.visible=!1,this.add(this.areaLabel);var t=new THREE.SphereGeometry(.4,10,10);this.color=new THREE.Color(16711680);var o=function(){var t=new THREE.MeshLambertMaterial({shading:THREE.SmoothShading,color:e.color,ambient:11184810,depthTest:!1,depthWrite:!1});return t},i=function(e){e.target.material.emissive.setHex(8947848)},n=function(e){e.target.material.emissive.setHex(0)},r=function(t){var o=t.tool,i=(o.dragstart,o.mouse,o.getMousePointCloudIntersection());if(i){var n=(i.position,e.spheres.indexOf(o.dragstart.object));e.setMarker(n,i)}t.event.stopImmediatePropagation()},a=function(e){};this.addMarker=function(e){e instanceof THREE.Vector3&&(e={position:e}),this.points.push(e);var s=new THREE.Mesh(t,o());s.addEventListener("move",i),s.addEventListener("leave",n),s.addEventListener("drag",r),s.addEventListener("drop",a),this.add(s),this.spheres.push(s);var l=new THREE.Geometry;l.vertices.push(new THREE.Vector3,new THREE.Vector3),l.colors.push(this.color,this.color,this.color);var d=new THREE.LineBasicMaterial({linewidth:1});d.depthTest=!1;var c=new THREE.Line(l,d);c.visible=!0,this.add(c),this.edges.push(c);var p=new Potree.TextSprite;p.setBorderColor({r:0,g:0,b:0,a:.8}),p.setBackgroundColor({r:0,g:0,b:0,a:.3}),p.material.depthTest=!1,p.visible=!1,this.edgeLabels.push(p),this.add(p);var u=new Potree.TextSprite;u.setBorderColor({r:0,g:0,b:0,a:.8}),u.setBackgroundColor({r:0,g:0,b:0,a:.3}),u.material.depthTest=!1,u.material.opacity=1,u.visible=!1,this.angleLabels.push(u),this.add(u);var h=new Potree.TextSprite;h.setBorderColor({r:0,g:0,b:0,a:.8}),h.setBackgroundColor({r:0,g:0,b:0,a:.3}),h.material.depthTest=!1,h.material.opacity=1,h.visible=!1,this.coordinateLabels.push(h),this.add(h);var m={type:"marker_added",measurement:this};this.dispatchEvent(m),this.setMarker(this.points.length-1,e)},this.removeMarker=function(e){this.points.splice(e,1),this.remove(this.spheres[e]);var t=0===e?0:e-1;this.remove(this.edges[t]),this.edges.splice(t,1),this.remove(this.edgeLabels[t]),this.edgeLabels.splice(t,1),this.coordinateLabels.splice(e,1),this.spheres.splice(e,1),this.update(),this.dispatchEvent({type:"marker_removed",measurement:this})},this.setMarker=function(e,t){this.points[e]=t;var o={type:"marker_moved",measure:this,index:e,position:t.position.clone()};this.dispatchEvent(o),this.update()},this.setPosition=function(e,t){var o=this.points[e];o.position.copy(t);var i={type:"marker_moved",measure:this,index:e,position:t.clone()};this.dispatchEvent(i),this.update()},this.getArea=function(){for(var e=0,t=this.points.length-1,o=0;o=this.points.length)return 0;var t=0===e?this.points[this.points.length-1]:this.points[e-1],o=this.points[e],i=this.points[(e+1)%this.points.length];return this.getAngleBetweenLines(o,t,i)},this.update=function(){if(0!==this.points.length){if(1===this.points.length){var t=this.points[0],o=t.position;this.spheres[0].position.copy(o);var i=this.coordinateLabels[0],n=o.clone().add(new THREE.Vector3(0,1,0));i.position.copy(n);var r=o.x.toFixed(2)+" / "+o.y.toFixed(2)+" / "+o.z.toFixed(2);return i.setText(r),void(i.visible=this.showCoordinates&&(a>d||this.closed))}for(var a=this.points.length-1,s=new THREE.Vector3,l=0;a>=l;l++){var t=this.points[l];s.add(t.position)}s.divideScalar(this.points.length);for(var l=0;a>=l;l++){var d=l,c=l+1>a?0:l+1,p=0===l?a:l-1,t=this.points[d],u=this.points[c],h=this.points[p],m=this.spheres[d];m.position.copy(t.position),m.material.color=e.color;var f=this.edges[d];f.material.color=this.color,f.geometry.vertices[0].copy(t.position),f.geometry.vertices[1].copy(u.position),f.geometry.verticesNeedUpdate=!0,f.geometry.computeBoundingSphere(),f.visible=a>d||this.closed;var g=this.edgeLabels[l],v=(new THREE.Vector3).add(t.position);v.add(u.position),v=v.multiplyScalar(.5);var y=t.position.distanceTo(u.position);g.position.copy(v),g.setText(y.toFixed(2)),g.visible=this.showDistances&&(a>d||this.closed)&&this.points.length>=2&&y>0;var E=this.angleLabels[l],T=this.getAngleBetweenLines(t,h,u),b=u.position.clone().sub(h.position);b.multiplyScalar(.5),b=h.position.clone().add(b).sub(t.position).normalize();var P=Math.min(t.position.distanceTo(h.position),t.position.distanceTo(u.position));P/=9;var n=t.position.clone().add(b.multiplyScalar(P));E.position.copy(n);var r=Potree.utils.addCommas((T*(180/Math.PI)).toFixed(1))+"°";E.setText(r),E.visible=this.showAngles&&(a>d||this.closed)&&this.points.length>=3&&T>0;var i=this.coordinateLabels[0],n=t.position.clone().add(new THREE.Vector3(0,1,0));i.position.copy(n);var r=t.position.x.toFixed(2)+" / "+t.position.y.toFixed(2)+" / "+t.position.z.toFixed(2);i.setText(r),i.visible=this.showCoordinates&&(a>d||this.closed)}this.areaLabel.position.copy(s),this.areaLabel.visible=this.showArea&&this.points.length>=3;var r=Potree.utils.addCommas(this.getArea().toFixed(1))+"²";this.areaLabel.setText(r)}},this.raycast=function(e,t){for(var o=0;op.activeMeasurement.maxMarkers&&p.finishInsertion()}}}function r(e){var t=p.domElement.getBoundingClientRect();if(p.mouse.x=(e.clientX-t.left)/p.domElement.clientWidth*2-1,p.mouse.y=2*-((e.clientY-t.top)/p.domElement.clientHeight)+1,p.dragstart){var o={type:"drag",event:e,tool:p};p.dragstart.object.dispatchEvent(o)}else if(h==u.INSERT&&p.activeMeasurement){var i=p.getMousePointCloudIntersection();if(i){var n=(i.position,p.activeMeasurement.points.length-1);p.activeMeasurement.setMarker(n,i)}}else{var r=c();r?(r.object.dispatchEvent({type:"move",target:r.object,event:e}),p.hoveredElement&&p.hoveredElement!==r.object&&p.hoveredElement.dispatchEvent({type:"leave",target:p.hoveredElement,event:e}),p.hoveredElement=r.object):(p.hoveredElement&&p.hoveredElement.dispatchEvent({type:"leave",target:p.hoveredElement,event:e}),p.hoveredElement=null)}}function a(e){h==u.INSERT&&p.finishInsertion()}function s(e){if(1===e.which){h!==u.DEFAULT&&e.stopImmediatePropagation();var t=c();t&&(p.dragstart={object:t.object,sceneClickPos:t.point,sceneStartPos:p.sceneRoot.position.clone(),mousePos:{x:p.mouse.x,y:p.mouse.y}},e.stopImmediatePropagation())}else 3===e.which&&a(e)}function l(e){window.getSelection?window.getSelection().removeAllRanges():document.selection&&document.selection.empty(),p.activeMeasurement&&h===u.INSERT&&(p.activeMeasurement.removeMarker(p.activeMeasurement.points.length-1),p.finishInsertion(),e.stopImmediatePropagation())}function d(e){p.dragstart&&(p.dragstart.object.dispatchEvent({type:"drop",event:e}),p.dragstart=null)}function c(){var e=new THREE.Vector3(p.mouse.x,p.mouse.y,.5);e.unproject(p.camera);var t=new THREE.Raycaster;t.ray.set(p.camera.position,e.sub(p.camera.position).normalize());for(var o=[],i=0;i0?a[0]:!1}var p=this;this.enabled=!1,this.toGeo=i,this.scene=e,this.camera=t,this.renderer=o,this.domElement=o.domElement,this.mouse={x:0,y:0};var u={DEFAULT:0,INSERT:1},h=u.DEFAULT;this.activeMeasurement=null,this.measurements=[],this.sceneMeasurement=new THREE.Scene,this.sceneRoot=new THREE.Object3D,this.sceneMeasurement.add(this.sceneRoot),this.light=new THREE.DirectionalLight(16777215,1),this.light.position.set(0,0,10),this.light.lookAt(new THREE.Vector3(0,0,0)),this.sceneMeasurement.add(this.light),this.hoveredElement=null,this.getState=function(){return h},this.getMousePointCloudIntersection=function(){var e=new THREE.Vector3(p.mouse.x,p.mouse.y,.5);e.unproject(p.camera);var t=e.sub(p.camera.position).normalize(),o=new THREE.Ray(p.camera.position,t),i=[];p.scene.traverse(function(e){(e instanceof Potree.PointCloudOctree||e instanceof Potree.PointCloudArena4D)&&i.push(e)});for(var n=null,r=null,a=0;ad)&&(n=l,r=d)}}return n?n:null},this.startInsertion=function(e){h=u.INSERT;var e=e||{},t="undefined"!=typeof e.showDistances?e.showDistances:!0,o="undefined"!=typeof e.showArea?e.showArea:!1,i="undefined"!=typeof e.showAngles?e.showAngles:!1,n="undefined"!=typeof e.closed?e.closed:!1,r="undefined"!=typeof e.showCoordinates?e.showCoordinates:!1,a=e.maxMarkers||Number.MAX_SAFE_INTEGER,s=new Potree.Measure;s.showDistances=t,s.showArea=o,s.showAngles=i,s.closed=n,s.showCoordinates=r,s.maxMarkers=a,this.addMeasurement(s),s.addMarker(new THREE.Vector3(1/0,1/0,1/0)),this.activeMeasurement=s},this.finishInsertion=function(){this.activeMeasurement.removeMarker(this.activeMeasurement.points.length-1);var e={type:"insertion_finished",measurement:this.activeMeasurement};this.dispatchEvent(e),this.activeMeasurement=null,h=u.DEFAULT},this.addMeasurement=function(e){this.sceneMeasurement.add(e),this.measurements.push(e),this.dispatchEvent({type:"measurement_added",measurement:e}),e.addEventListener("marker_added",function(e){p.dispatchEvent(e)}),e.addEventListener("marker_removed",function(e){p.dispatchEvent(e)}),e.addEventListener("marker_moved",function(e){p.dispatchEvent(e)})},this.removeMeasurement=function(e){this.sceneMeasurement.remove(e);var t=this.measurements.indexOf(e);t>=0&&(this.measurements.splice(t,1),this.dispatchEvent({type:"measurement_removed",measurement:e}))},this.reset=function(){for(var e=this.measurements.length-1;e>=0;e--){var t=this.measurements[e];this.removeMeasurement(t)}},this.update=function(){for(var e=[],i=0;i1){var d=new THREE.Geometry;d.vertices.push(new THREE.Vector3,new THREE.Vector3),d.colors.push(o,o,o);var c=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors,linewidth:2,transparent:!0,opacity:.4});c.depthTest=!1;var p=new THREE.Line(d,c);p.visible=!1,this.add(p),this.edges.push(p);var u=new THREE.BoxGeometry(1,1,1),h=new THREE.MeshBasicMaterial({color:16711680,transparent:!0,opacity:.2}),m=new THREE.Mesh(u,h);m.visible=!1,this.add(m),this.boxes.push(m)}var f={type:"marker_added",profile:this};this.dispatchEvent(f),this.setPosition(this.points.length-1,e)},this.removeMarker=function(e){this.points.splice(e,1),this.remove(this.spheres[e]);var t=0===e?0:e-1;this.remove(this.edges[t]),this.edges.splice(t,1),this.remove(this.boxes[t]),this.boxes.splice(t,1),this.spheres.splice(e,1),this.update();var o={type:"marker_removed",profile:this};this.dispatchEvent(o)},this.getArea=function(){for(var e=0,t=this.points.length-1,o=0;o=r;r++){var e=this.points[r],a=this.spheres[r],s=0===r?n:r-1,l=r===n?0:r+1,d=this.points[s],c=this.points[l],p=this.edges[s],u=this.edges[r],h=this.boxes[s];this.boxes[r],e.distanceTo(d),e.distanceTo(c),(new THREE.Vector3).addVectors(d,e).multiplyScalar(.5),(new THREE.Vector3).addVectors(e,c).multiplyScalar(.5);if(a.position.copy(e),this._modifiable?a.visible=!0:a.visible=!1,p&&(p.geometry.vertices[1].copy(e),p.geometry.verticesNeedUpdate=!0,p.geometry.computeBoundingSphere()),u&&(u.geometry.vertices[0].copy(e),u.geometry.verticesNeedUpdate=!0,u.geometry.computeBoundingSphere()),h){var m=d,f=e,g=m.clone().setY(0).distanceTo(f.clone().setY(0));h.scale.set(g,this.height,this.width);var v=(new THREE.Vector3).addVectors(m,f).multiplyScalar(.5),y=(new THREE.Vector3).subVectors(f,m),E=new THREE.Vector3(y.z,0,-y.x);h.position.set(0,0,0),h.lookAt(E),h.position.copy(v)}i.add(e),t.min(e),o.max(e)}i.multiplyScalar(1/this.points.length);for(var r=0;r0?o[0]:!1}var c=this;this.enabled=!1,this.scene=e,this.camera=t,this.renderer=o,this.domElement=o.domElement,this.mouse={x:0,y:0};var p={DEFAULT:0,INSERT:1},u=p.DEFAULT;new THREE.SphereGeometry(.4,10,10);this.activeProfile=null,this.profiles=[],this.sceneProfile=new THREE.Scene,this.sceneRoot=new THREE.Object3D,this.sceneProfile.add(this.sceneRoot),this.light=new THREE.DirectionalLight(16777215,1),this.light.position.set(0,0,10),this.light.lookAt(new THREE.Vector3(0,0,0)),this.sceneProfile.add(this.light),this.hoveredElement=null,this.getMousePointCloudIntersection=function(){var e=new THREE.Vector3(c.mouse.x,c.mouse.y,.5);e.unproject(c.camera);var t=e.sub(c.camera.position).normalize(),o=new THREE.Ray(c.camera.position,t),i=[];c.scene.traverse(function(e){(e instanceof Potree.PointCloudOctree||e instanceof Potree.PointCloudArena4D)&&i.push(e)});for(var n=null,r=null,a=0;ad)&&(n=l,r=d)}}return n?n.position:null},this.startInsertion=function(e){u=p.INSERT;var e=e||{},t=e.clip||!1,o=e.width||null;return this.activeProfile=new Potree.HeightProfile,this.activeProfile.clip=t,this.activeProfile.setWidth(o),this.addProfile(this.activeProfile),this.activeProfile.addMarker(new THREE.Vector3(0,0,0)),this.activeProfile},this.finishInsertion=function(){this.activeProfile.removeMarker(this.activeProfile.points.length-1);var e={type:"insertion_finished",profile:this.activeProfile};this.dispatchEvent(e),this.activeProfile=null,u=p.DEFAULT},this.addProfile=function(e){this.profiles.push(e),this.sceneProfile.add(e),e.update(),this.dispatchEvent({type:"profile_added",profile:e}),e.addEventListener("marker_added",function(e){c.dispatchEvent(e)}),e.addEventListener("marker_removed",function(e){c.dispatchEvent(e)}),e.addEventListener("marker_moved",function(e){c.dispatchEvent(e)}),e.addEventListener("width_changed",function(e){c.dispatchEvent(e)})},this.removeProfile=function(e){this.sceneProfile.remove(e);var t=this.profiles.indexOf(e);t>=0&&(this.profiles.splice(t,1),this.dispatchEvent({type:"profile_removed",profile:e}))},this.reset=function(){for(var e=this.profiles.length-1;e>=0;e--){var t=this.profiles[e];this.removeProfile(t)}},this.update=function(){for(var e=0;e0?i[0]:!1}}var s=this;this.enabled=!1,this.scene=e,this.camera=t,this.renderer=o,this.domElement=o.domElement,this.mouse={x:0,y:0},this.dragstart=null,this.sceneTransformation=new THREE.Scene,this.sceneRoot=new THREE.Object3D,this.sceneTransformation.add(this.sceneRoot),this.sceneRotation=new THREE.Scene,this.translationNode=new THREE.Object3D,this.rotationNode=new THREE.Object3D,this.scaleNode=new THREE.Object3D,this.sceneRoot.add(this.translationNode),this.sceneRoot.add(this.rotationNode),this.sceneRoot.add(this.scaleNode),this.sceneRoot.visible=!1,this.hoveredElement=null,this.STATE={DEFAULT:0,TRANSLATE_X:1,TRANSLATE_Y:2,TRANSLATE_Z:3,SCALE_X:1,SCALE_Y:2,SCALE_Z:3},this.parts={ARROW_X:{name:"arrow_x",object:void 0,color:new THREE.Color(16711680),state:this.STATE.TRANSLATE_X},ARROW_Z:{name:"arrow_z",object:void 0,color:new THREE.Color(255),state:this.STATE.TRANSLATE_Z},ARROW_Y:{name:"arrow_y",object:void 0,color:new THREE.Color(65280),state:this.STATE.TRANSLATE_Y},SCALE_X:{name:"scale_x",object:void 0,color:new THREE.Color(16711680),state:this.STATE.SCALE_X},SCALE_Z:{name:"scale_z",object:void 0,color:new THREE.Color(255),state:this.STATE.SCALE_Z},SCALE_Y:{name:"scale_y",object:void 0,color:new THREE.Color(65280),state:this.STATE.SCALE_Y},ROTATE_X:{name:"rotate_x",object:void 0,color:new THREE.Color(16711680),state:this.STATE.ROTATE_X},ROTATE_Z:{name:"rotate_z",object:void 0,color:new THREE.Color(255),state:this.STATE.ROTATE_Z},ROTATE_Y:{name:"rotate_y",object:void 0,color:new THREE.Color(65280),state:this.STATE.ROTATE_Y}},this.buildTranslationNode=function(){var e=s.createArrow(s.parts.ARROW_X,s.parts.ARROW_X.color);e.rotation.z=-Math.PI/2; +var t=s.createArrow(s.parts.ARROW_Y,s.parts.ARROW_Y.color),o=s.createArrow(s.parts.ARROW_Z,s.parts.ARROW_Z.color);o.rotation.x=-Math.PI/2,this.translationNode.add(e),this.translationNode.add(t),this.translationNode.add(o)},this.buildScaleNode=function(){var e=this.createScaleHandle(s.parts.SCALE_X,16711680);e.rotation.z=-Math.PI/2;var t=this.createScaleHandle(s.parts.SCALE_Y,65280),o=this.createScaleHandle(s.parts.SCALE_Z,255);o.rotation.x=-Math.PI/2,this.scaleNode.add(e),this.scaleNode.add(t),this.scaleNode.add(o)},this.buildRotationNode=function(){var e=this.createRotationCircle(s.parts.ROTATE_X,16711680);e.rotation.y=-Math.PI/2;var t=this.createRotationCircle(s.parts.ROTATE_Y,65280),o=this.createRotationCircle(s.parts.ROTATE_Z,255);t.rotation.x=-Math.PI/2,this.rotationNode.add(e),this.rotationNode.add(t),this.rotationNode.add(o);var i=new THREE.SphereGeometry(2.9,24,24),n=new THREE.Mesh(i,new THREE.MeshBasicMaterial({color:11184810,transparent:!0,opacity:.4}));this.sceneRotation.add(n);var r=function(e){n.material.color.setHex(5592405)},a=function(e){n.material.color.setHex(11184810)},l=function(e){e.event.stopImmediatePropagation();for(var t=new THREE.Vector3(s.dragstart.mousePos.x,s.dragstart.mousePos.y,.1),o=new THREE.Vector3(s.mouse.x,s.mouse.y,.1),i=(new THREE.Vector3).subVectors(o,t),n=t.clone().unproject(s.camera),r=o.clone().unproject(s.camera),a=(new THREE.Vector3).subVectors(r,n),l=a.clone().normalize(),d=(new THREE.Vector3).subVectors(s.camera.position,n).normalize(),c=d.clone().cross(l),p=6*i.length(),u=0;u=n;n++){var r=2*Math.PI*n/i,a=3*Math.cos(r),l=3*Math.sin(r);o.push(new THREE.Vector3(a,l,0))}for(var d=new THREE.Geometry,n=0;nt;t++)n.label.children[t].updateMatrixWorld(!0)},this.setDimension=function(e,t,o){this.dimension.set(e,t,o),this.box.scale.set(e,t,o),this.frame.scale.set(e,t,o)},this.volume=function(){return Math.abs(this.scale.x*this.scale.y*this.scale.z)},this.update=function(){this.boundingBox=this.box.geometry.boundingBox,this.boundingSphere=this.boundingBox.getBoundingSphere(),this._clip?(this.box.visible=!1,this.label.visible=!1):(this.box.visible=!0,this.label.visible=!0)},this.raycast=function(e,t){var o=[];if(this.box.raycast(e,o),o.length>0){var i=o[0];t.push({distance:i.distance,object:this,point:i.point.clone()})}},this.update()},Potree.Volume.prototype=Object.create(THREE.Object3D.prototype),Object.defineProperty(Potree.Volume.prototype,"clip",{get:function(){return this._clip},set:function(e){this._clip=e,this.update()}}),Object.defineProperty(Potree.Volume.prototype,"modifiable",{get:function(){return this._modifiable},set:function(e){this._modifiable=e,this.update()}}),Potree.VolumeTool=function(e,t,o,i){function n(e){var t=c.domElement.getBoundingClientRect();c.mouse.x=(e.clientX-t.left)/c.domElement.clientWidth*2-1,c.mouse.y=2*-((e.clientY-t.top)/c.domElement.clientHeight)+1}function r(e){}function a(e){if(u!==p.DEFAULT&&e.stopImmediatePropagation(),u===p.INSERT_VOLUME)c.finishInsertion();else if(1===e.which){var t=l();t&&t.object.modifiable&&c.transformationTool.setTargets([t.object])}3===e.which}function s(e){return e.preventDefault(),!1}function l(){var e=new THREE.Vector3(c.mouse.x,c.mouse.y,.5);e.unproject(c.camera);var t=new THREE.Raycaster;t.ray.set(c.camera.position,e.sub(c.camera.position).normalize());for(var o=[],i=0;i0?r[0]:!1}function d(){var e=new THREE.Vector3(c.mouse.x,c.mouse.y,.5);e.unproject(c.camera);var t=e.sub(c.camera.position).normalize(),o=new THREE.Ray(c.camera.position,t),i=[];c.scene.traverse(function(e){(e instanceof Potree.PointCloudOctree||e instanceof Potree.PointCloudArena4D)&&i.push(e)});for(var n=null,r=null,a=0;ad)&&(n=l,r=d)}}return n?n.position:null}var c=this;this.enabled=!1,this.scene=e,this.sceneVolume=new THREE.Scene,this.camera=t,this.renderer=o,this.transformationTool=i,this.domElement=this.renderer.domElement,this.mouse={x:0,y:0},this.volumes=[];var p={DEFAULT:0,INSERT_VOLUME:1},u=p.DEFAULT;this.update=function(e){if(u===p.INSERT_VOLUME){var t=d();if(t){this.activeVolume.position.copy(t);var o=this.activeVolume.getWorldPosition().applyMatrix4(this.camera.matrixWorldInverse),i=(new THREE.Vector4(o.x,o.y,o.z).applyMatrix4(this.camera.projectionMatrix),Math.abs(o.z/10));this.activeVolume.scale.set(i,i,i)}}for(var n=[],r=0;r=0&&this.volumes.splice(t,1)},this.reset=function(){for(var e=this.volumes.length-1;e>=0;e--){var t=this.volumes[e];this.removeVolume(t)}},this.render=function(e){c.renderer.render(this.sceneVolume,this.camera,e)},this.domElement.addEventListener("click",r,!1),this.domElement.addEventListener("mousedown",a,!1),this.domElement.addEventListener("mousemove",n,!1),this.domElement.addEventListener("contextmenu",s,!1)},Potree.VolumeTool.prototype=Object.create(THREE.EventDispatcher.prototype),Potree.PointCloudArena4DNode=function(){this.left=null,this.right=null,this.sceneNode=null,this.kdtree=null,this.getNumPoints=function(){return this.geometryNode.numPoints},this.isLoaded=function(){return!0},this.isTreeNode=function(){return!0},this.isGeometryNode=function(){return!1},this.getLevel=function(){return this.geometryNode.level},this.getBoundingSphere=function(){return this.geometryNode.boundingSphere},this.getBoundingBox=function(){return this.geometryNode.boundingBox},this.toTreeNode=function(e){var t=null;if(this.left===e?t=this.left:this.right===e&&(t=this.right),t.loaded){var o=new Potree.PointCloudArena4DNode,i=THREE.PointCloud(t.geometry,this.kdtree.material);i.visible=!1,o.kdtree=this.kdtree,o.geometryNode=t,o.sceneNode=i,o.parent=this,o.left=this.geometryNode.left,o.right=this.geometryNode.right}},this.getChildren=function(){var e=[];return this.left&&e.push(this.left),this.right&&e.push(this.right),e}},Potree.PointCloudOctreeNode.prototype=Object.create(Potree.PointCloudTreeNode.prototype),Potree.PointCloudArena4D=function(e){THREE.Object3D.call(this);var t=this;this.root=null,e.root?this.root=e.root:e.addEventListener("hierarchy_loaded",function(){t.root=e.root}),this.visiblePointsTarget=2e6,this.minimumNodePixelSize=150,this.position.sub(e.offset),this.updateMatrix(),this.numVisibleNodes=0,this.numVisiblePoints=0,this.boundingBoxNodes=[],this.loadQueue=[],this.visibleNodes=[],this.pcoGeometry=e,this.boundingBox=this.pcoGeometry.boundingBox,this.boundingSphere=this.pcoGeometry.boundingSphere,this.material=new Potree.PointCloudMaterial({vertexColors:THREE.VertexColors,size:.05,treeType:Potree.TreeType.KDTREE}),this.material.sizeType=Potree.PointSizeType.ATTENUATED,this.material.size=.05,this.profileRequests=[],this.pickTarget,this.pickMaterial,this.updateMatrixWorld()},Potree.PointCloudArena4D.prototype=new Potree.PointCloudTree,Potree.PointCloudArena4D.prototype.toTreeNode=function(e,t){var o=new Potree.PointCloudArena4DNode,i=new THREE.PointCloud(e.geometry,pointcloud.material);o.geometryNode=e,o.sceneNode=i,o.pointcloud=pointcloud,o.left=e.left,o.right=e.right,t?(t.sceneNode.add(i),t.left===e?t.left=o:t.right===e&&(t.right=o)):(this.root=o,this.add(i));var n=function(){t.sceneNode.remove(o.sceneNode),t.left===o?t.left=e:t.right===o&&(t.right=e)};return e.oneTimeDisposeHandlers.push(n),o},Potree.PointCloudArena4D.prototype.updateMaterial=function(e,t,o,i){e.fov=o.fov*(Math.PI/180),e.screenWidth=i.domElement.clientWidth,e.screenHeight=i.domElement.clientHeight,e.spacing=this.pcoGeometry.spacing,e.near=o.near,e.far=o.far,this.maxLevel>e.levels&&(e.levels=this.maxLevel+2),e.minSize=3;var n=this.boundingBox.size();e.bbSize=[n.x,n.y,n.z],e.pointSizeType&&(e.pointSizeType===Potree.PointSizeType.ADAPTIVE||e.pointColorType===Potree.PointColorType.OCTREE_DEPTH)&&this.updateVisibilityTexture(e,t)},Potree.PointCloudArena4D.prototype.updateVisibleBounds=function(){},Potree.PointCloudArena4D.prototype.hideDescendants=function(e){for(var t=[],o=0;o0;){var e=t.shift();e.visible=!1,e.boundingBoxNode&&(e.boundingBoxNode.visible=!1);for(var o=0;o0&&a.push(a[0]);for(var u=0;ux;x++)for(var C=0;n>C;C++){var w=x+C*n,S=Math.pow(x-(n-1)/2,2)+Math.pow(C-(n-1)/2,2),H=T[4*w+3];T[4*w+3]=0;var M=b[w];(0!==M||0!==H)&&P>S&&(R={pIndex:M,pcIndex:H},P=S)}if(R){var B={},A=a[R.pcIndex],m=A.sceneNode.geometry,V=m.attributes;for(var I in V)if(V.hasOwnProperty(I)){var L=m.attributes[I];if("position"===I){var N=m.attributes.position.array,D=N[3*R.pIndex+0],G=N[3*R.pIndex+1],_=N[3*R.pIndex+2],k=new THREE.Vector3(D,G,_);k.applyMatrix4(this.matrixWorld),B[I]=k}else if("indices"===I);else if(1===L.itemSize)B[I]=L.array[u+z];else{for(var O=[],z=0;zn?-1:n>r?1:0};t.sort(n);for(var r=[],a=0;a0&&(l+=1,d=r.indexOf(s.geometryNode.left.number)-a),s.geometryNode.right&&r.indexOf(s.geometryNode.right.number)>0&&(l+=2,d=0===d?r.indexOf(s.geometryNode.right.number)-a:d),"X"===s.geometryNode.split?c=1:"Y"===s.geometryNode.split?c=2:"Z"===s.geometryNode.split&&(c=4),i[3*a+0]=l,i[3*a+1]=d,i[3*a+2]=c}o.needsUpdate=!0}},Object.defineProperty(Potree.PointCloudArena4D.prototype,"progress",{get:function(){return this.pcoGeometry.root?Potree.PointCloudArena4DGeometryNode.nodesLoading>0?0:1:0}}),Potree.PointCloudArena4DGeometryNode=function(){this.left=null,this.right=null,this.boundingBox=null,this.number=null,this.pcoGeometry=null,this.loaded=!1,this.numPoints=0,this.level=0,this.children=[],this.oneTimeDisposeHandlers=[]},Potree.PointCloudArena4DGeometryNode.nodesLoading=0,Potree.PointCloudArena4DGeometryNode.prototype.isGeometryNode=function(){return!0},Potree.PointCloudArena4DGeometryNode.prototype.isTreeNode=function(){return!1},Potree.PointCloudArena4DGeometryNode.prototype.isLoaded=function(){return this.loaded},Potree.PointCloudArena4DGeometryNode.prototype.getBoundingSphere=function(){return this.boundingSphere},Potree.PointCloudArena4DGeometryNode.prototype.getBoundingBox=function(){return this.boundingBox},Potree.PointCloudArena4DGeometryNode.prototype.getChildren=function(){var e=[];return this.left&&e.push(this.left),this.right&&e.push(this.right),e},Potree.PointCloudArena4DGeometryNode.prototype.getBoundingBox=function(){return this.boundingBox},Potree.PointCloudArena4DGeometryNode.prototype.load=function(){if(!(this.loaded||this.loading||Potree.PointCloudArena4DGeometryNode.nodesLoading>=5)){this.loading=!0,Potree.PointCloudArena4DGeometryNode.nodesLoading++;var e=this.pcoGeometry.url+"?node="+this.number,t=new XMLHttpRequest;t.open("GET",e,!0),t.responseType="arraybuffer";var o=this;t.onreadystatechange=function(){if(4===t.readyState&&200===t.status){for(var e=t.response,i=new DataView(e),n=e.byteLength/17,r=new Float32Array(3*n),a=new Float32Array(3*n),s=new Uint32Array(n),l=0;n>l;l++){var d=i.getFloat32(17*l+0,!0)+o.boundingBox.min.x,c=i.getFloat32(17*l+4,!0)+o.boundingBox.min.y,p=i.getFloat32(17*l+8,!0)+o.boundingBox.min.z,u=i.getUint8(17*l+12,!0)/256,h=i.getUint8(17*l+13,!0)/256,m=i.getUint8(17*l+14,!0)/256;r[3*l+0]=d,r[3*l+1]=c,r[3*l+2]=p,a[3*l+0]=u,a[3*l+1]=h,a[3*l+2]=m,s[l]=l}var f=new THREE.BufferGeometry;f.addAttribute("position",new THREE.BufferAttribute(r,3)),f.addAttribute("color",new THREE.BufferAttribute(a,3)),f.addAttribute("indices",new THREE.BufferAttribute(s,1)),f.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(3*n),3)),o.geometry=f,o.loaded=!0,Potree.PointCloudArena4DGeometryNode.nodesLoading--,f.boundingBox=o.boundingBox,f.boundingSphere=o.boundingSphere,o.numPoints=n,o.loading=!1}},t.send(null)}},Potree.PointCloudArena4DGeometryNode.prototype.dispose=function(){if(this.geometry&&null!=this.parent){this.geometry.dispose(),this.geometry=null,this.loaded=!1;for(var e=0;el;l++){var d=n.getUint8(3*l+0,!0),c=(n.getUint16(3*l+1,!0),(1&d)>0),p=(2&d)>0,u=(4&d)>0,h=(8&d)>0,m=(16&d)>0,f=null;u?f="X":h&&(f="Y"),m&&(f="Z");var g=new Potree.PointCloudArena4DGeometryNode;if(g.hasLeft=c,g.hasRight=p,g.split=f,g.isLeaf=!c&&!p,g.number=l,g.left=null,g.right=null,g.pcoGeometry=o,g.level=r.length,s=Math.max(s,g.level),r.length>0){var v=r[r.length-1];g.boundingBox=v.boundingBox.clone();var y=v.boundingBox.size();if(v.hasLeft&&!v.left){v.left=g,v.children.push(g),"X"===v.split?g.boundingBox.max.x=g.boundingBox.min.x+y.x/2:"Y"===v.split?g.boundingBox.max.y=g.boundingBox.min.y+y.y/2:"Z"===v.split&&(g.boundingBox.max.z=g.boundingBox.min.z+y.z/2);var E=g.boundingBox.center(),T=g.boundingBox.size().length()/2;g.boundingSphere=new THREE.Sphere(E,T)}else{v.right=g,v.children.push(g),"X"===v.split?g.boundingBox.min.x=g.boundingBox.min.x+y.x/2:"Y"===v.split?g.boundingBox.min.y=g.boundingBox.min.y+y.y/2:"Z"===v.split&&(g.boundingBox.min.z=g.boundingBox.min.z+y.z/2);var E=g.boundingBox.center(),T=g.boundingBox.size().length()/2;g.boundingSphere=new THREE.Sphere(E,T)}}else{a=g,a.boundingBox=o.boundingBox.clone();var E=a.boundingBox.center(),T=a.boundingBox.size().length()/2;a.boundingSphere=new THREE.Sphere(E,T)}var b=g.boundingBox.size();if(g.spacing=(b.x+b.y+b.z)/3/75,r.push(g),g.isLeaf)for(var P=!1;!P&&r.length>0;){r.pop();var R=r[r.length-1];P=r.length>0&&R.hasRight&&null==R.right}}(new Date).getTime();o.root=a,o.levels=s,o.dispatchEvent({type:"hierarchy_loaded"})}},t.send(null)},Object.defineProperty(Potree.PointCloudArena4DGeometry.prototype,"spacing",{get:function(){return this._spacing?this._spacing:this.root?this.root.spacing:void 0},set:function(e){this._spacing=e}}),ProgressBar.prototype.hide=function(){this.element.style.opacity=0,this.element.style.transition="all 0.2s ease"},ProgressBar.prototype.show=function(){this.element.style.opacity=this.maxOpacity,this.element.style.transition="all 0.2s ease"},Object.defineProperty(ProgressBar.prototype,"progress",{get:function(){return this._progress},set:function(e){this._progress=e,this.elProgress.style.width=100*e+"%"}}),Object.defineProperty(ProgressBar.prototype,"message",{get:function(){return this._message},set:function(e){this._message=e,this.elProgressMessage.innerHTML=e}}),Potree.Viewer=function(e,t){function o(e){69===e.keyCode?n.transformationTool.translate():82===e.keyCode?n.transformationTool.scale():84===e.keyCode&&n.transformationTool.rotate()}function i(e){requestAnimationFrame(i),n.update(l.getDelta(),e),n.useEDL&&Potree.Features.SHADER_EDL.isSupported()?(h||(h=new m),h.render(n.renderer)):"Splats"===n.quality?(p||(p=new u),p.render(n.renderer)):c.render()}var n=this,r=t||{},a=r.onPointCloudLoaded||function(){};this.renderArea=e,this.annotations=[],this.fov=60,this.pointSize=1,this.opacity=1,this.sizeType="Fixed",this.pointSizeType=Potree.PointSizeType.FIXED,this.pointColorType=null,this.clipMode=Potree.ClipMode.HIGHLIGHT_INSIDE,this.quality="Squares",this.isFlipYZ=!1,this.useDEMCollisions=!1,this.minNodeSize=100,this.directionalLight,this.edlScale=1,this.edlRadius=3,this.useEDL=!1,this.minimumJumpDistance=.2,this.jumpDistance=null,this.intensityMax=null,this.heightMin=null,this.heightMax=null,this.moveSpeed=10,this.showDebugInfos=!1,this.showStats=!0,this.showBoundingBox=!1,this.freeze=!1,this.fpControls,this.orbitControls,this.earthControls, +this.geoControls,this.controls,this.progressBar=new ProgressBar;this.renderer,this.camera,this.scene,this.scenePointCloud,this.sceneBG,this.cameraBG,this.pointclouds=[],this.measuringTool,this.volumeTool,this.transformationTool;var s,l=new THREE.Clock;this.showSkybox=!1,this.referenceFrame,this.addPointCloud=function(e,t){t=t||function(){};var o=function(e){n.mapView||e.projection&&(n.mapView=new Potree.Viewer.MapView(viewer),n.mapView.init(viewer)),n.pointclouds.push(e),n.referenceFrame.add(e);var o=e.boundingSphere.clone().applyMatrix4(e.matrixWorld);if(n.referenceFrame.updateMatrixWorld(!0),o.radius>5e4?n.camera.near=10:o.radius>1e4?n.camera.near=2:o.radius>1e3?n.camera.near=1:o.radius>100?n.camera.near=.5:n.camera.near=.1,1===n.pointclouds.length){n.referenceFrame.position.sub(o.center),n.referenceFrame.updateMatrixWorld(!0);var i=o.radius/6;n.setMoveSpeed(i)}n.zoomTo(e,1);var r=n.getHeightRange();if(null===r.min||null===r.max){var a=n.getBoundingBox();n.setHeightRange(a.min.y,a.max.y)}n.earthControls.pointclouds.push(e),1===n.pointclouds.length&&(n.setNavigationMode("Orbit"),n.flipYZ(),n.zoomTo(e,1)),n.dispatchEvent({type:"pointcloud_loaded",pointcloud:e}),t({type:"pointclouad_loaded",pointcloud:e})};this.addEventListener("pointcloud_loaded",a),e&&(e.indexOf("cloud.js")>0?Potree.POCLoader.load(e,function(e){e?(pointcloud=new Potree.PointCloudOctree(e),o(pointcloud)):t({type:"loading_failed"})}):e.indexOf(".vpc")>0?Potree.PointCloudArena4DGeometry.load(e,function(e){e?(pointcloud=new Potree.PointCloudArena4D(e),o(pointcloud)):t({type:"loading_failed"})}):t({type:"loading_failed"}))},this.toLocal=function(e){return function(t){var o=t.clone().applyMatrix4(e.referenceFrame.matrixWorld);return o}}(this),this.toGeo=function(e){return function(t){var o=(new THREE.Matrix4).getInverse(e.referenceFrame.matrixWorld),i=t.clone().applyMatrix4(o);return i}}(this),this.getMinNodeSize=function(){return n.minNodeSize},this.setMinNodeSize=function(e){n.minNodeSize!==e&&(n.minNodeSize=e,n.dispatchEvent({type:"minnodesize_changed",viewer:n}))},this.setDescription=function(e){$("#potree_description")[0].innerHTML=e},this.setNavigationMode=function(e){"Orbit"===e?n.useOrbitControls():"Flight"===e?n.useFPSControls():"Earth"===e&&n.useEarthControls()},this.setShowBoundingBox=function(e){n.showBoundingBox!==e&&(n.showBoundingBox=e,n.dispatchEvent({type:"show_boundingbox_changed",viewer:n}))},this.getShowBoundingBox=function(){return n.showBoundingBox},this.setMoveSpeed=function(e){n.moveSpeed!==e&&(n.moveSpeed=e,n.fpControls.setMoveSpeed(e),n.geoControls.setMoveSpeed(e),n.dispatchEvent({type:"move_speed_changed",viewer:n,speed:e}))},this.getMoveSpeed=function(){return n.fpControls.moveSpeed},this.setShowSkybox=function(e){n.showSkybox!==e&&(n.showSkybox=e,n.dispatchEvent({type:"show_skybox_changed",viewer:n}))},this.getShowSkybox=function(){return n.showSkybox},this.setHeightRange=function(e,t){(n.heightMin!==e||n.heightMax!==t)&&(n.heightMin=e||n.heightMin,n.heightMax=t||n.heightMax,n.dispatchEvent({type:"height_range_changed",viewer:n}))},this.getHeightRange=function(){return{min:n.heightMin,max:n.heightMax}},this.setIntensityMax=function(e){n.intensityMax!==e&&(n.intensityMax=e,n.dispatchEvent({type:"intensity_max_changed",viewer:n}))},this.getIntensityMax=function(){return n.intensityMax},this.setFreeze=function(e){n.freeze!=e&&(n.freeze=e,n.dispatchEvent({type:"freeze_changed",viewer:n}))},this.getFreeze=function(){return n.freeze},this.setPointBudget=function(e){Potree.pointBudget!=e&&(Potree.pointBudget=parseInt(e),n.dispatchEvent({type:"point_budget_changed",viewer:n}))},this.getPointBudget=function(){return Potree.pointBudget},this.setClipMode=function(e){n.clipMode!=e&&(n.clipMode=e,n.dispatchEvent({type:"clip_mode_changed",viewer:n}))},this.getClipMode=function(){return n.clipMode},this.setDEMCollisionsEnabled=function(e){n.useDEMCollisions!==e&&(n.useDEMCollisions=e,n.dispatchEvent({type:"use_demcollisions_changed",viewer:n}))},this.getDEMCollisionsEnabled=function(){return n.useDEMCollisions},this.setEDLEnabled=function(e){n.useEDL!=e&&(n.useEDL=e,n.dispatchEvent({type:"use_edl_changed",viewer:n}))},this.getEDLEnabled=function(){return n.useEDL},this.setEDLRadius=function(e){n.edlRadius!==e&&(n.edlRadius=e,n.dispatchEvent({type:"edl_radius_changed",viewer:n}))},this.getEDLRadius=function(){return n.edlRadius},this.setEDLStrength=function(e){n.edlScale!==e&&(n.edlScale=e,n.dispatchEvent({type:"edl_strength_changed",viewer:n}))},this.getEDLStrength=function(){return n.edlScale},this.setPointSize=function(e){n.pointSize!==e&&(n.pointSize=e,n.dispatchEvent({type:"point_size_changed",viewer:n}))},this.getPointSize=function(){return n.pointSize},this.setFOV=function(e){n.fov!==e&&(n.fov=e,n.dispatchEvent({type:"fov_changed",viewer:n}))},this.getFOV=function(){return n.fov},this.setOpacity=function(e){n.opacity!==e&&(n.opacity=e,n.dispatchEvent({type:"opacity_changed",viewer:n}))},this.getOpacity=function(){return n.opacity},this.setPointSizing=function(e){n.sizeType!==e&&(n.sizeType=e,"Fixed"===e?n.pointSizeType=Potree.PointSizeType.FIXED:"Attenuated"===e?n.pointSizeType=Potree.PointSizeType.ATTENUATED:"Adaptive"===e&&(n.pointSizeType=Potree.PointSizeType.ADAPTIVE),n.dispatchEvent({type:"point_sizing_changed",viewer:n}))},this.getPointSizing=function(){return n.sizeType},this.setQuality=function(e){var t=n.quality;("Interpolation"!=e||Potree.Features.SHADER_INTERPOLATION.isSupported())&&("Splats"!=e||Potree.Features.SHADER_SPLATS.isSupported())?n.quality=e:n.quality="Squares",t!==n.quality&&n.dispatchEvent({type:"quality_changed",viewer:n})},this.getQuality=function(){return n.quality},this.setClassificationVisibility=function(e,t){for(var o=!1,i=0;i0&&(e=n.transformationTool.getBoundingBox());var t=new THREE.Object3D;t.boundingBox=e,n.zoomTo(t,1)},this.setTopView=function(){var e=this.getBoundingBox(n.pointclouds);n.transformationTool.targets.length>0&&(e=n.transformationTool.getBoundingBox());var t=new THREE.Object3D;t.boundingBox=e,n.camera.position.set(0,1,0),n.camera.rotation.set(-Math.PI/2,0,0),n.camera.zoomTo(t,1)},this.setFrontView=function(){var e=this.getBoundingBox(n.pointclouds);n.transformationTool.targets.length>0&&(e=n.transformationTool.getBoundingBox());var t=new THREE.Object3D;t.boundingBox=e,n.camera.position.set(0,0,1),n.camera.rotation.set(0,0,0),n.camera.zoomTo(t,1)},this.setLeftView=function(){var e=this.getBoundingBox(n.pointclouds);n.transformationTool.targets.length>0&&(e=n.transformationTool.getBoundingBox());var t=new THREE.Object3D;t.boundingBox=e,n.camera.position.set(-1,0,0),n.camera.rotation.set(0,-Math.PI/2,0),n.camera.zoomTo(t,1)},this.setRightView=function(){var e=this.getBoundingBox(n.pointclouds);n.transformationTool.targets.length>0&&(e=n.transformationTool.getBoundingBox());var t=new THREE.Object3D;t.boundingBox=e,n.camera.position.set(1,0,0),n.camera.rotation.set(0,Math.PI/2,0),n.camera.zoomTo(t,1)},this.flipYZ=function(){n.isFlipYZ=!n.isFlipYZ,n.referenceFrame.matrix.copy(new THREE.Matrix4),n.isFlipYZ?n.referenceFrame.applyMatrix((new THREE.Matrix4).set(1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1)):n.referenceFrame.applyMatrix((new THREE.Matrix4).set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)),n.referenceFrame.updateMatrixWorld(!0);var e=n.getBoundingBox();n.referenceFrame.position.copy(e.center()).multiplyScalar(-1),n.referenceFrame.position.y=-e.min.y,n.referenceFrame.updateMatrixWorld(!0),n.updateHeightRange()},this.updateHeightRange=function(){var e=n.getBoundingBox();n.setHeightRange(e.min.y,e.max.y)},this.useEarthControls=function(){n.controls&&(n.controls.enabled=!1),n.controls=n.earthControls,n.controls.enabled=!0},this.useGeoControls=function(){n.controls&&(n.controls.enabled=!1),n.controls=n.geoControls,n.controls.enabled=!0},this.useFPSControls=function(){n.controls&&(n.controls.enabled=!1),n.controls=n.fpControls,n.controls.enabled=!0},this.useOrbitControls=function(){n.controls&&(n.controls.enabled=!1),n.controls=n.orbitControls,n.controls.enabled=!0,n.pointclouds.length>0&&n.controls.target.copy(n.pointclouds[0].boundingSphere.center.clone().applyMatrix4(n.pointclouds[0].matrixWorld))},this.addAnnotation=function(e,t){var o=t.cameraPosition,i=t.cameraTarget||e,r=t.description||null,a=t.title||null,s=new Potree.Annotation(n,{position:e,cameraPosition:o,cameraTarget:i,title:a,description:r});return n.annotations.push(s),n.renderArea.appendChild(s.domElement),n.dispatchEvent({type:"annotation_added",viewer:n}),s},this.getAnnotations=function(){return n.annotations},this.toggleSidebar=function(){var e=$("#potree_render_area"),t=($("#potree_sidebar_container"),"0px"!==e.css("left"));t?e.css("left","0px"):e.css("left","300px")},this.toggleMap=function(){var e=$("#potree_map");e.toggle(100)},this.loadGUI=function(){var e=$("#potree_sidebar_container");e.load("../build/potree/sidebar.html"),e.css("width","300px"),e.css("height","100%");var t=$("
").load("../build/potree/profile.html",function(){$("#potree_render_area").append(t.children()),n._2dprofile=new Potree.Viewer.Profile(n,document.getElementById("profile_draw_container"))})},this.createControls=function(){var e=function(e){if(n.useDEMCollisions){for(var t=null,o=0;ol&&(i=n.pointclouds[s],r=l,a=intersection)}if(null!=a){var d=0;if(n.jumpDistance)d=n.jumpDistance;else{var c=n.camera.position.distanceTo(n.orbitControls.target),p=new THREE.Vector3(o.x,o.y,.5);p.unproject(n.camera);var u=p.sub(n.camera.position).normalize(),h=new THREE.Ray(n.camera.position,u),m=i.nodesOnRay(i.visibleNodes,h),f=m[m.length-1],g=f.getBoundingSphere().radius,d=Math.min(c,g),d=Math.max(n.minimumJumpDistance,d)}var v=n.camera.getWorldDirection().multiplyScalar(-1),y=(new THREE.Vector3).addVectors(a,v.multiplyScalar(d)),E=600,T=TWEEN.Easing.Quartic.Out;n.controls.enabled=!1;var b=new TWEEN.Tween(n.camera.position).to(y,E);b.easing(T),b.start();var b=new TWEEN.Tween(n.orbitControls.target).to(a,E);b.easing(T),b.onComplete(function(){n.controls.enabled=!0,n.fpControls.moveSpeed=g/2,n.geoControls.moveSpeed=g/2}),b.start()}}}),n.earthControls=new THREE.EarthControls(n.camera,n.renderer,n.scenePointCloud),n.earthControls.enabled=!1,n.earthControls.addEventListener("proposeTransform",e)},this.initThree=function(){var e=n.renderArea.clientWidth,t=n.renderArea.clientHeight,i=e/t,r=.1,a=1e6;n.scene=new THREE.Scene,n.scenePointCloud=new THREE.Scene,n.sceneBG=new THREE.Scene,n.camera=new THREE.PerspectiveCamera(n.fov,i,r,a),n.cameraBG=new THREE.Camera,n.camera.rotation.order="ZYX",n.referenceFrame=new THREE.Object3D,n.scenePointCloud.add(n.referenceFrame),n.renderer=new THREE.WebGLRenderer,n.renderer.setSize(e,t),n.renderer.autoClear=!1,n.renderArea.appendChild(n.renderer.domElement),n.renderer.domElement.tabIndex="2222",n.renderer.domElement.addEventListener("mousedown",function(){n.renderer.domElement.focus()}),s=Potree.utils.loadSkybox("../resources/textures/skybox/"),n.camera.position.set(-304,372,318),n.camera.rotation.y=-Math.PI/4,n.camera.rotation.x=-Math.PI/6,this.createControls(),n.renderer.context.getExtension("EXT_frag_depth");var l=Potree.utils.createGrid(5,5,2);n.scene.add(l),n.measuringTool=new Potree.MeasuringTool(n.scenePointCloud,n.camera,n.renderer,n.toGeo),n.profileTool=new Potree.ProfileTool(n.scenePointCloud,n.camera,n.renderer),n.transformationTool=new Potree.TransformationTool(n.scenePointCloud,n.camera,n.renderer),n.volumeTool=new Potree.VolumeTool(n.scenePointCloud,n.camera,n.renderer,n.transformationTool),n.profileTool.addEventListener("profile_added",function(e){var t=document.createElement("button");t.type="button",t.classList.add("btn"),t.classList.add("btn-primary"),t.id="btn_rofile_"+n.profileTool.profiles.length,t.value="profile "+n.profileTool.profiles.length,t.innerHTML="profile "+n.profileTool.profiles.length,t.onclick=function(t){n.profileTool.draw(e.profile,$("#profile_draw_container")[0],n.toGeo),e.profile.addEventListener("marker_moved",function(){n.profileTool.draw(e.profile,$("#profile_draw_container")[0],n.toGeo)}),e.profile.addEventListener("width_changed",function(){n.profileTool.draw(e.profile,$("#profile_draw_container")[0],n.toGeo)})}});var d=Potree.utils.createBackgroundTexture(512,512);d.minFilter=d.magFilter=THREE.NearestFilter,d.minFilter=d.magFilter=THREE.LinearFilter;var c=new THREE.Mesh(new THREE.PlaneBufferGeometry(2,2,0),new THREE.MeshBasicMaterial({map:d}));c.material.depthTest=!1,c.material.depthWrite=!1,n.sceneBG.add(c),window.addEventListener("keydown",o,!1),n.directionalLight=new THREE.DirectionalLight(16777215,.5),n.directionalLight.position.set(10,10,10),n.directionalLight.lookAt(new THREE.Vector3(0,0,0)),n.scenePointCloud.add(n.directionalLight);var p=new THREE.AmbientLight(5592405);n.scenePointCloud.add(p)},this.update=function(e,t){Potree.pointLoadLimit=2*Potree.pointBudget,n.directionalLight.position.copy(n.camera.position),n.directionalLight.lookAt((new THREE.Vector3).addVectors(n.camera.position,n.camera.getWorldDirection()));for(var o=0,i=0,r=0,a=0;a=p?n.intensityMax=1:256>=p?n.intensityMax=255:n.intensityMax=p}}}s.material.clipMode=n.clipMode,s.material.heightMin=n.heightMin,s.material.heightMax=n.heightMax,s.material.intensityMin=0,s.material.intensityMax=n.intensityMax,s.showBoundingBox=n.showBoundingBox,s.generateDEM=n.useDEMCollisions,s.minimumNodePixelSize=n.minNodeSize,o+=s.numVisibleNodes,i+=s.numVisiblePoints,r+=s.progress}if(!n.freeze){var u=Potree.updatePointClouds(n.pointclouds,n.camera,n.renderer);o=u.visibleNodes.length,i=u.numVisiblePoints}if(n.camera.fov=n.fov,n.controls&&n.controls.update(e),n.pointclouds.length>0){n.progressBar.progress=r/n.pointclouds.length;var h;h=0===r?"loading":"loading: "+parseInt(100*r/n.pointclouds.length)+"%",n.progressBar.message=h,r>=.999?n.progressBar.hide():1>r&&n.progressBar.show()}n.volumeTool.update(),n.transformationTool.update(),n.profileTool.update();for(var m=[],a=0;aP.z||P.z>1?b.domElement.style.display="none":b.domElement.style.display="initial"}T.sort(function(e,t){return t.distance-e.distance});for(var a=0;a0&&(r.uniforms.depthMap.value=e,r.uniforms.texture.value=t,Potree.utils.screenPass.render(n.renderer,r),n.volumeTool.render(),n.renderer.clearDepth(),n.profileTool.render(),n.measuringTool.render(),n.transformationTool.render())}},h=null,m=function(){var e=null,t=[],o=null,i=(n.renderer.context,function(){null==e&&(e=new Potree.EyeDomeLightingMaterial,o=new THREE.WebGLRenderTarget(1024,1024,{minFilter:THREE.LinearFilter,magFilter:THREE.NearestFilter,format:THREE.RGBAFormat,type:THREE.FloatType}))}),r=function(){var e=n.renderArea.clientWidth,t=n.renderArea.clientHeight,i=e/t,r=o.width!=e||o.height!=t;r&&o.dispose(),n.camera.aspect=i,n.camera.updateProjectionMatrix(),n.renderer.setSize(e,t),o.setSize(e,t)};this.render=function(){i(),r(),n.renderer.clear(),n.showSkybox?(s.camera.rotation.copy(n.camera.rotation),n.renderer.render(s.scene,s.camera)):n.renderer.render(n.sceneBG,n.cameraBG),n.renderer.render(n.scene,n.camera);for(var a=[],l=0;l0&&(e.uniforms.screenWidth.value=c,e.uniforms.screenHeight.value=p,e.uniforms.near.value=n.camera.near,e.uniforms.far.value=n.camera.far,e.uniforms.colorMap.value=o,e.uniforms.expScale.value=n.camera.far,e.uniforms.edlScale.value=n.edlScale,e.uniforms.radius.value=n.edlRadius,e.uniforms.opacity.value=n.opacity,e.depthTest=!0,e.depthWrite=!0,e.transparent=!0,Potree.utils.screenPass.render(n.renderer,e),n.renderer.render(n.scene,n.camera),n.profileTool.render(),n.volumeTool.render(),n.renderer.clearDepth(),n.measuringTool.render(),n.transformationTool.render())}};n.initThree(),n.setPointSize(1),n.setFOV(60),n.setOpacity(1),n.setEDLEnabled(!1),n.setEDLRadius(2),n.setEDLStrength(1),n.setClipMode(Potree.ClipMode.HIGHLIGHT_INSIDE),n.setPointBudget(1e6),n.setShowBoundingBox(!1),n.setFreeze(!1),n.setNavigationMode("Orbit"),requestAnimationFrame(i)},Potree.Viewer.prototype=Object.create(THREE.EventDispatcher.prototype),Potree.Viewer.Profile=function(e,t){function o(){requestAnimationFrame(o);var e=document.getElementById("profile_window").clientWidth,t=document.getElementById("profile_window").clientHeight;(e!==r||t!==a)&&setTimeout(n,50,{profile:i.currentProfile}),r=e,a=t}var i=this;this.viewer=e,this.enabled=!0,this.element=t,this.currentProfile=null,this.requests=[],this.pointsProcessed=0,this.margin={top:0,right:0,bottom:20,left:40},this.maximized=!1,this.threshold=2e4,$("#closeProfileContainer").click(function(){i.hide(),i.enabled=!1}),$("#profile_toggle_size_button").click(function(){i.maximized=!i.maximized,i.maximized?$("#profile_window").css("height","100%"):$("#profile_window").css("height","30%")}),this.show=function(){$("#profile_window").fadeIn(),i.enabled=!0},this.hide=function(){$("#profile_window").fadeOut()},this.cancel=function(){for(var e=0;e1e5?new THREE.Vector3(.01,.01,.01):new THREE.Vector3(.001,.001,.001);var d=function(e,t,o){for(var i=new Uint8Array(o),n=0;nd&&(d=M.x),M.ys-c&&r(o[p].altitude)l-c&&d.push(o[p]);if(d.length>0){var u=d[0];this.hoveredPoint=d[0],-1==navigator.userAgent.indexOf("Firefox")?(cx=i.scaleX(u.distance)+i.margin.left, +cy=i.scaleY(u.altitude)+i.margin.top):(cx=i.scaleX(u.distance),cy=i.scaleY(u.altitude)),cy-=t/2;var h=d3.select("svg");d3.selectAll("rect").remove();var m=(h.append("rect").attr("x",cx).attr("y",cy).attr("id",u.id).attr("width",t).attr("height",t).style("fill","yellow"),$("#profile_selection_marker"));m.css("display","initial"),m.css("left",cx+"px"),m.css("top",cy+"px"),m.css("width",t+"px"),m.css("height",t+"px"),m.css("background-color","yellow");var f="x: "+Math.round(10*u.x)/10+" y: "+Math.round(10*u.y)/10+" z: "+Math.round(10*u.altitude)/10+" - ";f+="offset: "+u.distance.toFixed(3)+" - ",f+="Classification: "+u.classification+" - ",f+="Intensity: "+u.intensity,$("#profileInfo").css("color","yellow"),$("#profileInfo").html(f)}else{d3.selectAll("rect").remove(),$("#profileInfo").html("");var m=$("#profile_selection_marker");m.css("display","none")}},this.strokeColor=function(e){var t=i.viewer.getMaterial();if(t===Potree.PointColorType.RGB)return"rgb("+100*e.color[0]+"%,"+100*e.color[1]+"%,"+100*e.color[2]+"%)";if(t===Potree.PointColorType.INTENSITY)return"rgb("+points.intensity+"%,"+points.intensity+"%,"+points.intensity+"%)";if(t===Potree.PointColorType.CLASSIFICATION){var o=i.viewer.pointclouds[0].material.classification;if("undefined"!=typeof o[e.classification]){var n="rgb("+100*o[e.classification].x+"%,";return n+=100*o[e.classification].y+"%,",n+=100*o[e.classification].z+"%)"}return"rgb(255,255,255)"}return t===Potree.PointColorType.HEIGHT?e.heightColor:t===Potree.PointColorType.RETURN_NUMBER?1===e.numberOfReturns?"rgb(255, 255, 0)":1===e.returnNumber?"rgb(255, 0, 0)":e.returnNumber===e.numberOfReturns?"rgb(0, 0, 255)":"rgb(0, 255, 0)":e.color},this.redraw=function(){i.draw(i.currentProfile)},this.draw=function(e){if(i.enabled&&e&&!(e.points.length<2)&&0!==i.viewer.pointclouds.length){i.currentProfile=e,i.__drawData||(i.__drawData={}),i.points=[],i.rangeX=[1/0,-(1/0)],i.rangeY=[1/0,-(1/0)],i.pointsProcessed=0;for(var t=0;t0){var d=i.viewer.toGeo(e.points[a-1]),c=l.x-d.x,p=l.y-d.y,u=Math.sqrt(c*c+p*p);r+=u}var h=4,m=i.scaleX(r),f=i.context.canvas.clientHeight;i.context.beginPath(),i.context.arc(m,f,h,0,2*Math.PI,!1),i.context.fillStyle="#a22",i.context.fill()}for(var g,m,f,v=2,a=-1,y=t.length;++al){var u=a*(c/s);i.scaleY.range([r,0]),i.scaleX.range([n/2-u/2,n/2+u/2]);var h=p/l,m=a*h;i.axisScaleX=d3.scale.linear().domain([a/2-m/2,a/2+m/2]).range([0,n]),i.axisScaleY=d3.scale.linear().domain(i.rangeY).range([r,0])}else{var f=s*(d/a);i.scaleX.range([0,n]),i.scaleY.range([r/2+f/2,r/2-f/2]);var h=l/p,g=s*h,v=(i.rangeY[1]+i.rangeY[0])/2;i.axisScaleX=d3.scale.linear().domain(i.rangeX).range([0,n]),i.axisScaleY=d3.scale.linear().domain([v-g/2,v+g/2]).range([r,0])}i.scaleX.domain(i.rangeX),i.scaleY.domain(i.rangeY),i.axisZoom=d3.behavior.zoom().x(i.axisScaleX).y(i.axisScaleY).scaleExtent([0,128]).size([n,r]),i.zoom=d3.behavior.zoom().x(i.scaleX).y(i.scaleY).scaleExtent([0,128]).size([n,r]).on("zoom",function(){i.axisZoom.translate(i.zoom.translate()),i.axisZoom.scale(i.zoom.scale()),svg.select(".x.axis").call(y),svg.select(".y.axis").call(E),i.context.clearRect(0,0,n,r),o(i.points,i.rangeX,i.rangeY)}),i.context=d3.select("#profileCanvas").attr("width",n).attr("height",r).call(i.zoom).node().getContext("2d"),d3.select("svg#profileSVG").selectAll("*").remove(),svg=d3.select("svg#profileSVG").call(i.zoom).attr("width",(n+i.margin.left+i.margin.right).toString()).attr("height",(r+i.margin.top+i.margin.bottom).toString()).attr("transform","translate("+i.margin.left+","+i.margin.top+")").on("mousemove",function(){}),d3.select("#profileCanvas").on("mousemove",i.pointHighlight);var y=d3.svg.axis().scale(i.axisScaleX).innerTickSize(-r).outerTickSize(5).orient("bottom").ticks(10,"m"),E=d3.svg.axis().scale(i.axisScaleY).innerTickSize(-n).outerTickSize(5).orient("left").ticks(10,"m");svg.append("g").attr("class","x axis").call(y);svg.append("g").attr("class","y axis").call(E),-1==navigator.userAgent.indexOf("Firefox")?(svg.select(".y.axis").attr("transform","translate("+i.margin.left.toString()+","+i.margin.top.toString()+")"),svg.select(".x.axis").attr("transform","translate("+i.margin.left.toString()+","+(r+i.margin.top).toString()+")")):svg.select(".x.axis").attr("transform","translate( 0 ,"+r.toString()+")"),o(i.points,i.rangeX,i.rangeY),document.getElementById("profile_num_points").innerHTML=Potree.utils.addCommas(i.pointsProcessed)+" "},t=0;ti.threshold&&i.cancel()}},onFinish:function(e){!i.enabled},onCancel:function(){!i.enabled}});i.requests.push(s)}}},this.setThreshold=function(e){i.threshold=e,i.redraw()};var n=function(e){e.profile===i.currentProfile&&i.redraw()};e.profileTool.addEventListener("marker_moved",n),e.profileTool.addEventListener("width_changed",n),e.addEventListener("material_changed",function(){n({profile:i.currentProfile})}),e.addEventListener("height_range_changed",function(){n({profile:i.currentProfile})});var r=document.getElementById("profile_window").clientWidth,a=document.getElementById("profile_window").clientHeight;requestAnimationFrame(o)},proj4.defs("UTM10N","+proj=utm +zone=10 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"),Potree.Viewer.MapView=function(e){var t=this;this.viewer=e,this.webMapService="WMTS",this.mapProjectionName="EPSG:3857",this.mapProjection=proj4.defs(t.mapProjectionName),this.sceneProjection=null,this.init=function(){$("#potree_map").draggable({handle:$("#potree_map_header")}),$("#potree_map").resizable(),$("#potree_map_toggle").css("display","block"),t.gExtent=new ol.geom.LineString([[0,0],[0,0]]);var e=new ol.Feature(t.gExtent),o=new ol.source.Vector({features:[e]}),i=new ol.layer.Vector({source:o,style:new ol.style.Style({fill:new ol.style.Fill({color:"rgba(255, 255, 255, 0.2)"}),stroke:new ol.style.Stroke({color:"#0000ff",width:2}),image:new ol.style.Circle({radius:3,fill:new ol.style.Fill({color:"#0000ff"})})})});t.gCamera=new ol.geom.LineString([[0,0],[0,0],[0,0],[0,0]]);var e=new ol.Feature(t.gCamera),o=new ol.source.Vector({features:[e]}),n=new ol.layer.Vector({source:o,style:new ol.style.Style({stroke:new ol.style.Stroke({color:"#0000ff",width:2})})});t.toolLayer=new ol.layer.Vector({source:new ol.source.Vector({}),style:new ol.style.Style({fill:new ol.style.Fill({color:"rgba(255, 0, 0, 1)"}),stroke:new ol.style.Stroke({color:"rgba(255, 0, 0, 1)",width:2})})}),t.sourcesLayer=new ol.layer.Vector({source:new ol.source.Vector({}),style:new ol.style.Style({fill:new ol.style.Fill({color:"rgba(255, 0, 0, 0.1)"}),stroke:new ol.style.Stroke({color:"rgba(0, 0, 150, 1)",width:1})})}),t.sourcesLabelLayer=new ol.layer.Vector({source:new ol.source.Vector({}),style:new ol.style.Style({fill:new ol.style.Fill({color:"rgba(255, 0, 0, 0.1)"}),stroke:new ol.style.Stroke({color:"rgba(255, 0, 0, 1)",width:2})}),minResolution:2,maxResolution:20});var r=new ol.control.MousePosition({coordinateFormat:ol.coordinate.createStringXY(4),projection:t.sceneProjection,undefinedHTML:" "}),a=function(e){var o=e||{},i=document.createElement("button");i.innerHTML="T",i.addEventListener("click",function(){var e=t.sourcesLayer.getVisible();t.sourcesLayer.setVisible(!e),t.sourcesLabelLayer.setVisible(!e)},!1),i.style["float"]="left",i.title="show / hide tiles";var n=document.createElement("a");n.href="#",n.download="list.txt",n.style["float"]="left";var r=document.createElement("button");r.innerHTML="D",n.appendChild(r);var a=function(e){var t=l.getArray();if(0===t.length)return alert("No tiles were selected. Select area with ctrl + left mouse button!"),e.preventDefault(),e.stopImmediatePropagation(),!1;for(var o="",i=0;i0&&n.push(n[0]);var d=new ol.geom.LineString(n),c=new ol.Feature(d);t.toolLayer.getSource().addFeature(c)}},this.load=function(e){if(e instanceof Potree.PointCloudOctree){t.sceneProjection||t.setSceneProjection(e.projection);var o=t.getMapExtent(),i=t.getMapCenter(),n=t.map.getView();n.setCenter(i),t.gExtent.setCoordinates([o.bottomLeft,o.bottomRight,o.topRight,o.topLeft,o.bottomLeft]),n.fit(t.gExtent,[300,300],{constrainResolution:!1});var r=e.pcoGeometry.url+"/../sources.json";$.getJSON(r,function(e){for(var o=e.sources,i=0;i
+ + + \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/build/potree/sidebar.html b/PotreeConverter/resources/page_template/build/potree/sidebar.html new file mode 100644 index 00000000..8c19f2f2 --- /dev/null +++ b/PotreeConverter/resources/page_template/build/potree/sidebar.html @@ -0,0 +1,1100 @@ + + + + + + \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/build/shaders/shaders.js b/PotreeConverter/resources/page_template/build/shaders/shaders.js deleted file mode 100644 index 644372d3..00000000 --- a/PotreeConverter/resources/page_template/build/shaders/shaders.js +++ /dev/null @@ -1,819 +0,0 @@ -Potree.Shaders["pointcloud.vs"] = [ - "", - "// the following is an incomplete list of attributes, uniforms and defines", - "// which are automatically added through the THREE.ShaderMaterial", - "", - "//attribute vec3 position;", - "//attribute vec3 color;", - "//attribute vec3 normal;", - "", - "//uniform mat4 modelMatrix;", - "//uniform mat4 modelViewMatrix;", - "//uniform mat4 projectionMatrix;", - "//uniform mat4 viewMatrix;", - "//uniform mat3 normalMatrix;", - "//uniform vec3 cameraPosition;", - "", - "//#define MAX_DIR_LIGHTS 0", - "//#define MAX_POINT_LIGHTS 1", - "//#define MAX_SPOT_LIGHTS 0", - "//#define MAX_HEMI_LIGHTS 0", - "//#define MAX_SHADOWS 0", - "//#define MAX_BONES 58", - "", - "#define max_clip_boxes 30", - "", - "attribute float intensity;", - "attribute float classification;", - "attribute float returnNumber;", - "attribute float numberOfReturns;", - "attribute float pointSourceID;", - "attribute vec4 indices;", - "", - "uniform float screenWidth;", - "uniform float screenHeight;", - "uniform float fov;", - "uniform float spacing;", - "uniform float near;", - "uniform float far;", - "", - "#if defined use_clip_box", - " uniform mat4 clipBoxes[max_clip_boxes];", - "#endif", - "", - "", - "uniform float heightMin;", - "uniform float heightMax;", - "uniform float intensityMin;", - "uniform float intensityMax;", - "uniform float size; // pixel size factor", - "uniform float minSize; // minimum pixel size", - "uniform float maxSize; // maximum pixel size", - "uniform float octreeSize;", - "uniform vec3 bbSize;", - "uniform vec3 uColor;", - "uniform float opacity;", - "uniform float clipBoxCount;", - "", - "", - "uniform sampler2D visibleNodes;", - "uniform sampler2D gradient;", - "uniform sampler2D classificationLUT;", - "uniform sampler2D depthMap;", - "", - "varying float vOpacity;", - "varying vec3 vColor;", - "varying float vLinearDepth;", - "varying float vLogDepth;", - "varying vec3 vViewPosition;", - "varying float vRadius;", - "varying vec3 vWorldPosition;", - "varying vec3 vNormal;", - "", - "", - "// ---------------------", - "// OCTREE", - "// ---------------------", - "", - "#if (defined(adaptive_point_size) || defined(color_type_tree_depth)) && defined(tree_type_octree)", - "/**", - " * number of 1-bits up to inclusive index position", - " * number is treated as if it were an integer in the range 0-255", - " *", - " */", - "float numberOfOnes(float number, float index){", - " float tmp = mod(number, pow(2.0, index + 1.0));", - " float numOnes = 0.0;", - " for(float i = 0.0; i < 8.0; i++){", - " if(mod(tmp, 2.0) != 0.0){", - " numOnes++;", - " }", - " tmp = floor(tmp / 2.0);", - " }", - " return numOnes;", - "}", - "", - "", - "/**", - " * checks whether the bit at index is 1", - " * number is treated as if it were an integer in the range 0-255", - " *", - " */", - "bool isBitSet(float number, float index){", - " return mod(floor(number / pow(2.0, index)), 2.0) != 0.0;", - "}", - "", - "", - "/**", - " * find the tree depth at the point position", - " */", - "float getLocalTreeDepth(){", - " vec3 offset = vec3(0.0, 0.0, 0.0);", - " float iOffset = 0.0;", - " float depth = 0.0;", - " for(float i = 0.0; i <= 1000.0; i++){", - " float nodeSizeAtLevel = octreeSize / pow(2.0, i);", - " vec3 index3d = (position - offset) / nodeSizeAtLevel;", - " index3d = floor(index3d + 0.5);", - " float index = 4.0*index3d.x + 2.0*index3d.y + index3d.z;", - " ", - " vec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));", - " float mask = value.r * 255.0;", - " if(isBitSet(mask, index)){", - " // there are more visible child nodes at this position", - " iOffset = iOffset + value.g * 255.0 + numberOfOnes(mask, index - 1.0);", - " depth++;", - " }else{", - " // no more visible child nodes at this position", - " return depth;", - " }", - " offset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;", - " }", - " ", - " return depth;", - "}", - "", - "float getPointSizeAttenuation(){", - " return pow(1.9, getLocalTreeDepth());", - "}", - "", - "", - "#endif", - "", - "", - "// ---------------------", - "// KD-TREE", - "// ---------------------", - "", - "#if (defined(adaptive_point_size) || defined(color_type_tree_depth)) && defined(tree_type_kdtree)", - "", - "float getLocalTreeDepth(){", - " vec3 offset = vec3(0.0, 0.0, 0.0);", - " float iOffset = 0.0;", - " float depth = 0.0;", - " ", - " ", - " vec3 size = bbSize; ", - " vec3 pos = position;", - " ", - " for(float i = 0.0; i <= 1000.0; i++){", - " ", - " vec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));", - " ", - " int children = int(value.r * 255.0);", - " float next = value.g * 255.0;", - " int split = int(value.b * 255.0);", - " ", - " if(next == 0.0){", - " return depth;", - " }", - " ", - " vec3 splitv = vec3(0.0, 0.0, 0.0);", - " if(split == 1){", - " splitv.x = 1.0;", - " }else if(split == 2){", - " splitv.y = 1.0;", - " }else if(split == 4){", - " splitv.z = 1.0;", - " }", - " ", - " iOffset = iOffset + next;", - " ", - " float factor = length(pos * splitv / size);", - " if(factor < 0.5){", - " // left", - " if(children == 0 || children == 2){", - " return depth;", - " }", - " }else{", - " // right", - " pos = pos - size * splitv * 0.5;", - " if(children == 0 || children == 1){", - " return depth;", - " }", - " if(children == 3){", - " iOffset = iOffset + 1.0;", - " }", - " }", - " size = size * ((1.0 - (splitv + 1.0) / 2.0) + 0.5);", - " ", - " depth++;", - " }", - " ", - " ", - " return depth; ", - "}", - "", - "float getPointSizeAttenuation(){", - " return pow(1.3, getLocalTreeDepth());", - "}", - "", - "#endif", - "", - "void main() {", - " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - " vViewPosition = -mvPosition.xyz;", - " vWorldPosition = worldPosition.xyz;", - " gl_Position = projectionMatrix * mvPosition;", - " vOpacity = opacity;", - " vLinearDepth = -mvPosition.z;", - " vNormal = normalize(normalMatrix * normal);", - " ", - " #if defined(use_edl)", - " vLogDepth = log2(gl_Position.w + 1.0) / log2(far + 1.0);", - " #endif", - " ", - " //#if defined(use_logarithmic_depth_buffer)", - " // float logarithmicZ = (2.0 * log2(gl_Position.w + 1.0) / log2(far + 1.0) - 1.0) * gl_Position.w;", - " // gl_Position.z = logarithmicZ;", - " //#endif", - "", - " // ---------------------", - " // POINT COLOR", - " // ---------------------", - " ", - " #ifdef color_type_rgb", - " vColor = color;", - " #elif defined color_type_height", - " vec4 world = modelMatrix * vec4( position, 1.0 );", - " float w = (world.y - heightMin) / (heightMax-heightMin);", - " vColor = texture2D(gradient, vec2(w,1.0-w)).rgb;", - " #elif defined color_type_depth", - " float linearDepth = -mvPosition.z ;", - " float expDepth = (gl_Position.z / gl_Position.w) * 0.5 + 0.5;", - " vColor = vec3(linearDepth, expDepth, 0.0);", - " #elif defined color_type_intensity", - " float w = (intensity - intensityMin) / (intensityMax - intensityMin);", - " vColor = vec3(w, w, w);", - " #elif defined color_type_intensity_gradient", - " float w = (intensity - intensityMin) / intensityMax;", - " vColor = texture2D(gradient, vec2(w,1.0-w)).rgb;", - " #elif defined color_type_color", - " vColor = uColor;", - " #elif defined color_type_tree_depth", - " float depth = getLocalTreeDepth();", - " float w = depth / 10.0;", - " vColor = texture2D(gradient, vec2(w,1.0-w)).rgb;", - " #elif defined color_type_point_index", - " vColor = indices.rgb;", - " #elif defined color_type_classification", - " float c = mod(classification, 16.0);", - " vec2 uv = vec2(c / 255.0, 0.5);", - " vec4 classColor = texture2D(classificationLUT, uv);", - " vColor = classColor.rgb;", - " #elif defined color_type_return_number", - " if(numberOfReturns == 1.0){", - " vColor = vec3(1.0, 1.0, 0.0);", - " }else{", - " if(returnNumber == 1.0){", - " vColor = vec3(1.0, 0.0, 0.0);", - " }else if(returnNumber == numberOfReturns){", - " vColor = vec3(0.0, 0.0, 1.0);", - " }else{", - " vColor = vec3(0.0, 1.0, 0.0);", - " }", - " }", - " #elif defined color_type_source", - " float w = mod(pointSourceID, 10.0) / 10.0;", - " vColor = texture2D(gradient, vec2(w,1.0 - w)).rgb;", - " #elif defined color_type_normal", - " vColor = (modelMatrix * vec4(normal, 0.0)).xyz;", - " #elif defined color_type_phong", - " vColor = color;", - " #endif", - " ", - " {", - " // TODO might want to combine with the define block above to avoid reading same LUT two times", - " float c = mod(classification, 16.0);", - " vec2 uv = vec2(c / 255.0, 0.5);", - " ", - " if(texture2D(classificationLUT, uv).a == 0.0){", - " gl_Position = vec4(100.0, 100.0, 100.0, 0.0);", - " }", - " }", - " ", - " //if(vNormal.z < 0.0){", - " // gl_Position = vec4(1000.0, 1000.0, 1000.0, 1.0);", - " //}", - " ", - " // ---------------------", - " // POINT SIZE", - " // ---------------------", - " float pointSize = 1.0;", - " ", - " float projFactor = 1.0 / tan(fov / 2.0);", - " projFactor /= vViewPosition.z;", - " projFactor *= screenHeight / 2.0;", - " float r = spacing * 1.5;", - " vRadius = r;", - " #if defined fixed_point_size", - " pointSize = size;", - " #elif defined attenuated_point_size", - " pointSize = size * projFactor;", - " #elif defined adaptive_point_size", - " float worldSpaceSize = size * r / getPointSizeAttenuation();", - " pointSize = worldSpaceSize * projFactor;", - " #endif", - "", - " pointSize = max(minSize, pointSize);", - " pointSize = min(maxSize, pointSize);", - " ", - " vRadius = pointSize / projFactor;", - " ", - " gl_PointSize = pointSize;", - " ", - " ", - " // ---------------------", - " // CLIPPING", - " // ---------------------", - " ", - " #if defined use_clip_box", - " bool insideAny = false;", - " for(int i = 0; i < max_clip_boxes; i++){", - " if(i == int(clipBoxCount)){", - " break;", - " }", - " ", - " vec4 clipPosition = clipBoxes[i] * modelMatrix * vec4( position, 1.0 );", - " bool inside = -0.5 <= clipPosition.x && clipPosition.x <= 0.5;", - " inside = inside && -0.5 <= clipPosition.y && clipPosition.y <= 0.5;", - " inside = inside && -0.5 <= clipPosition.z && clipPosition.z <= 0.5;", - " insideAny = insideAny || inside;", - " }", - " if(!insideAny){", - " ", - " #if defined clip_outside", - " gl_Position = vec4(1000.0, 1000.0, 1000.0, 1.0);", - " #elif defined clip_highlight_inside && !defined(color_type_depth)", - " float c = (vColor.r + vColor.g + vColor.b) / 6.0;", - " #endif", - " }else{", - " #if defined clip_highlight_inside", - " vColor.r += 0.5;", - " #endif", - " }", - " ", - " #endif", - " ", - "}", - "", -].join("\n"); - -Potree.Shaders["pointcloud.fs"] = [ - "", - "#if defined use_interpolation", - " #extension GL_EXT_frag_depth : enable", - "#endif", - "", - "", - "// the following is an incomplete list of attributes, uniforms and defines", - "// which are automatically added through the THREE.ShaderMaterial", - "", - "// #define USE_COLOR", - "// ", - "// uniform mat4 viewMatrix;", - "// uniform vec3 cameraPosition;", - "", - "", - "uniform mat4 projectionMatrix;", - "uniform float opacity;", - "", - "", - "#if defined(color_type_phong)", - "", - " uniform vec3 diffuse;", - " uniform vec3 ambient;", - " uniform vec3 emissive;", - " uniform vec3 specular;", - " uniform float shininess;", - " uniform vec3 ambientLightColor;", - "", - " #if MAX_POINT_LIGHTS > 0", - "", - " uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", - " uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - " uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", - " uniform float pointLightDecay[ MAX_POINT_LIGHTS ];", - "", - " #endif", - "", - " #if MAX_DIR_LIGHTS > 0", - "", - " uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", - " uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", - "", - " #endif", - "", - "#endif", - "", - "//#if MAX_SPOT_LIGHTS > 0", - "//", - "// uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", - "// uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - "// uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", - "// uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", - "// uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", - "//", - "// uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", - "//", - "//#endif", - "", - "uniform float fov;", - "uniform float spacing;", - "uniform float near;", - "uniform float far;", - "uniform float pcIndex;", - "uniform float screenWidth;", - "uniform float screenHeight;", - "", - "uniform sampler2D depthMap;", - "", - "varying vec3 vColor;", - "varying float vOpacity;", - "varying float vLinearDepth;", - "varying float vLogDepth;", - "varying vec3 vViewPosition;", - "varying float vRadius;", - "varying vec3 vWorldPosition;", - "varying vec3 vNormal;", - "", - "float specularStrength = 1.0;", - "", - "void main() {", - "", - " vec3 color = vColor;", - " float depth = gl_FragCoord.z;", - "", - " #if defined(circle_point_shape) || defined(use_interpolation) || defined (weighted_splats)", - " float u = 2.0 * gl_PointCoord.x - 1.0;", - " float v = 2.0 * gl_PointCoord.y - 1.0;", - " #endif", - " ", - " #if defined(circle_point_shape) || defined (weighted_splats)", - " float cc = u*u + v*v;", - " if(cc > 1.0){", - " discard;", - " }", - " #endif", - " ", - " #if defined weighted_splats", - " vec2 uv = gl_FragCoord.xy / vec2(screenWidth, screenHeight);", - " float sDepth = texture2D(depthMap, uv).r;", - " if(vLinearDepth > sDepth + vRadius){", - " discard;", - " }", - " #endif", - " ", - " #if defined use_interpolation", - " float wi = 0.0 - ( u*u + v*v);", - " vec4 pos = vec4(-vViewPosition, 1.0);", - " pos.z += wi * vRadius;", - " float linearDepth = pos.z;", - " pos = projectionMatrix * pos;", - " pos = pos / pos.w;", - " float expDepth = pos.z;", - " depth = (pos.z + 1.0) / 2.0;", - " gl_FragDepthEXT = depth;", - " ", - " #if defined(color_type_depth)", - " color.r = linearDepth;", - " color.g = expDepth;", - " #endif", - " ", - " #endif", - " ", - " #if defined color_type_point_index", - " gl_FragColor = vec4(color, pcIndex / 255.0);", - " #else", - " gl_FragColor = vec4(color, vOpacity);", - " #endif", - " ", - " #if defined weighted_splats", - " float w = pow(1.0 - (u*u + v*v), 2.0);", - " gl_FragColor.rgb = gl_FragColor.rgb * w;", - " gl_FragColor.a = w;", - " #endif", - " ", - " vec3 normal = normalize( vNormal );", - " normal.z = abs(normal.z);", - " vec3 viewPosition = normalize( vViewPosition );", - " ", - " #if defined(color_type_phong)", - "", - " // code taken from three.js phong light fragment shader", - " ", - " #if MAX_POINT_LIGHTS > 0", - "", - " vec3 pointDiffuse = vec3( 0.0 );", - " vec3 pointSpecular = vec3( 0.0 );", - "", - " for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", - "", - " vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - " vec3 lVector = lPosition.xyz + vViewPosition.xyz;", - "", - " float lDistance = 1.0;", - " if ( pointLightDistance[ i ] > 0.0 )", - " lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", - "", - " lVector = normalize( lVector );", - "", - " // diffuse", - "", - " float dotProduct = dot( normal, lVector );", - "", - " #ifdef WRAP_AROUND", - "", - " float pointDiffuseWeightFull = max( dotProduct, 0.0 );", - " float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", - "", - " vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", - "", - " #else", - "", - " float pointDiffuseWeight = max( dotProduct, 0.0 );", - "", - " #endif", - "", - " pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;", - "", - " // specular", - "", - " vec3 pointHalfVector = normalize( lVector + viewPosition );", - " float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", - " float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );", - "", - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", - "", - " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );", - " pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;", - " pointSpecular = vec3(0.0, 0.0, 0.0);", - " }", - " ", - " #endif", - " ", - " #if MAX_DIR_LIGHTS > 0", - "", - " vec3 dirDiffuse = vec3( 0.0 );", - " vec3 dirSpecular = vec3( 0.0 );", - "", - " for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {", - "", - " vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", - " vec3 dirVector = normalize( lDirection.xyz );", - "", - " // diffuse", - "", - " float dotProduct = dot( normal, dirVector );", - "", - " #ifdef WRAP_AROUND", - "", - " float dirDiffuseWeightFull = max( dotProduct, 0.0 );", - " float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", - "", - " vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );", - "", - " #else", - "", - " float dirDiffuseWeight = max( dotProduct, 0.0 );", - "", - " #endif", - "", - " dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;", - "", - " // specular", - "", - " vec3 dirHalfVector = normalize( dirVector + viewPosition );", - " float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", - " float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );", - "", - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", - "", - " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );", - " dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", - " }", - "", - " #endif", - " ", - " vec3 totalDiffuse = vec3( 0.0 );", - " vec3 totalSpecular = vec3( 0.0 );", - " ", - " #if MAX_POINT_LIGHTS > 0", - "", - " totalDiffuse += pointDiffuse;", - " totalSpecular += pointSpecular;", - "", - " #endif", - " ", - " #if MAX_DIR_LIGHTS > 0", - "", - " totalDiffuse += dirDiffuse;", - " totalSpecular += dirSpecular;", - "", - " #endif", - " ", - " gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", - "", - " #endif", - " ", - " ", - " #if defined(use_edl)", - " gl_FragColor.a = vLogDepth;", - " #endif", - " ", - "}", - "", - "", - "", -].join("\n"); - -Potree.Shaders["normalize.vs"] = [ - "", - "varying vec2 vUv;", - "", - "void main() {", - " vUv = uv;", - "", - " gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);", - "}", -].join("\n"); - -Potree.Shaders["normalize.fs"] = [ - "", - "#extension GL_EXT_frag_depth : enable", - "", - "uniform sampler2D depthMap;", - "uniform sampler2D texture;", - "", - "varying vec2 vUv;", - "", - "void main() {", - " float depth = texture2D(depthMap, vUv).g; ", - " ", - " if(depth <= 0.0){", - " discard;", - " }", - " ", - " vec4 color = texture2D(texture, vUv); ", - " color = color / color.w;", - " ", - " gl_FragColor = vec4(color.xyz, 1.0); ", - " ", - " gl_FragDepthEXT = depth;", - "}", -].join("\n"); - -Potree.Shaders["edl.vs"] = [ - "", - "", - "varying vec2 vUv;", - "", - "void main() {", - " vUv = uv;", - " ", - " vec4 mvPosition = modelViewMatrix * vec4(position,1.0);", - "", - " gl_Position = projectionMatrix * mvPosition;", - "}", -].join("\n"); - -Potree.Shaders["edl.fs"] = [ - "", - "// ", - "// adapted from the EDL shader code from Christian Boucheny in cloud compare:", - "// https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL", - "//", - "", - "#define NEIGHBOUR_COUNT 8", - "", - "uniform mat4 projectionMatrix;", - "", - "uniform float screenWidth;", - "uniform float screenHeight;", - "uniform float near;", - "uniform float far;", - "uniform vec2 neighbours[NEIGHBOUR_COUNT];", - "uniform vec3 lightDir;", - "uniform float expScale;", - "uniform float edlScale;", - "uniform float radius;", - "uniform float opacity;", - "", - "//uniform sampler2D depthMap;", - "uniform sampler2D colorMap;", - "", - "varying vec2 vUv;", - "", - "/**", - " * transform linear depth to [0,1] interval with 1 beeing closest to the camera.", - " */", - "float ztransform(float linearDepth){", - " return 1.0 - (linearDepth - near) / (far - near);", - "}", - "", - "float expToLinear(float z){", - " z = 2.0 * z - 1.0;", - " float linear = (2.0 * near * far) / (far + near - z * (far - near));", - "", - " return linear;", - "}", - "", - "// this actually only returns linear depth values if LOG_BIAS is 1.0", - "// lower values work out more nicely, though.", - "#define LOG_BIAS 0.01", - "float logToLinear(float z){", - " return (pow((1.0 + LOG_BIAS * far), z) - 1.0) / LOG_BIAS;", - "}", - "", - "float obscurance(float z, float dist){", - " return max(0.0, z) / dist;", - "}", - "", - "float computeObscurance(float linearDepth){", - " vec4 P = vec4(0, 0, 1, -ztransform(linearDepth));", - " vec2 uvRadius = radius / vec2(screenWidth, screenHeight);", - " ", - " float sum = 0.0;", - " ", - " for(int c = 0; c < NEIGHBOUR_COUNT; c++){", - " vec2 N_rel_pos = uvRadius * neighbours[c];", - " vec2 N_abs_pos = vUv + N_rel_pos;", - " ", - " float neighbourDepth = logToLinear(texture2D(colorMap, N_abs_pos).a);", - " ", - " if(neighbourDepth != 0.0){", - " float Zn = ztransform(neighbourDepth);", - " float Znp = dot( vec4( N_rel_pos, Zn, 1.0), P );", - " ", - " sum += obscurance( Znp, 0.05 * linearDepth );", - " }", - " }", - " ", - " return sum;", - "}", - "", - "void main(){", - " float linearDepth = logToLinear(texture2D(colorMap, vUv).a);", - " ", - " float f = computeObscurance(linearDepth);", - " f = exp(-expScale * edlScale * f);", - " ", - " vec4 color = texture2D(colorMap, vUv);", - " if(color.a == 0.0 && f >= 1.0){", - " discard;", - " }", - " ", - " gl_FragColor = vec4(color.rgb * f, opacity);", - "}", - "", -].join("\n"); - -Potree.Shaders["blur.vs"] = [ - "", - "varying vec2 vUv;", - "", - "void main() {", - " vUv = uv;", - "", - " gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);", - "}", -].join("\n"); - -Potree.Shaders["blur.fs"] = [ - "", - "uniform mat4 projectionMatrix;", - "", - "uniform float screenWidth;", - "uniform float screenHeight;", - "uniform float near;", - "uniform float far;", - "", - "uniform sampler2D map;", - "", - "varying vec2 vUv;", - "", - "void main() {", - "", - " float dx = 1.0 / screenWidth;", - " float dy = 1.0 / screenHeight;", - "", - " vec3 color = vec3(0.0, 0.0, 0.0);", - " color += texture2D(map, vUv + vec2(-dx, -dy)).rgb;", - " color += texture2D(map, vUv + vec2( 0, -dy)).rgb;", - " color += texture2D(map, vUv + vec2(+dx, -dy)).rgb;", - " color += texture2D(map, vUv + vec2(-dx, 0)).rgb;", - " color += texture2D(map, vUv + vec2( 0, 0)).rgb;", - " color += texture2D(map, vUv + vec2(+dx, 0)).rgb;", - " color += texture2D(map, vUv + vec2(-dx, dy)).rgb;", - " color += texture2D(map, vUv + vec2( 0, dy)).rgb;", - " color += texture2D(map, vUv + vec2(+dx, dy)).rgb;", - " ", - " color = color / 9.0;", - " ", - " gl_FragColor = vec4(color, 1.0);", - " ", - " ", - "}", -].join("\n"); - diff --git a/PotreeConverter/resources/page_template/examples/css/potree.css b/PotreeConverter/resources/page_template/examples/css/potree.css deleted file mode 100644 index 12c28ed4..00000000 --- a/PotreeConverter/resources/page_template/examples/css/potree.css +++ /dev/null @@ -1,40 +0,0 @@ -/* this style is to test non fullscreen canvas */ -/*#renderArea{ - position: absolute; - width: 90%; - height: 80%; - top: 100px; - left: 100px; - right: 100px; - bottom: 100px; -}*/ - -#renderArea{ - width: 100%; - height: 100%; - overflow: hidden; -} - -.info{ - color: white; - font-weight: bold; - text-shadow: 1px 1px 1px black, - 1px -1px 1px black, - -1px 1px 1px black, - -1px -1px 1px black; -} - -a:hover, a:visited, a:link, a:active{ - color: #ccccff; - text-decoration: none; -} - -canvas { - width: 100%; - height: 100% -} - -body{ - margin: 0; - padding: 0 -} \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/examples/js/ProgressBar.js b/PotreeConverter/resources/page_template/examples/js/ProgressBar.js deleted file mode 100644 index fb648533..00000000 --- a/PotreeConverter/resources/page_template/examples/js/ProgressBar.js +++ /dev/null @@ -1,81 +0,0 @@ - - -function ProgressBar(){ - this._progress = 0; - this._message = ""; - - this.maxOpacity = 0.6; - - this.element = document.createElement("div"); - this.elProgress = document.createElement("div"); - this.elProgressMessage = document.createElement("div"); - - //this.element.innerHTML = "element"; - //this.elProgress.innerHTML = "progress"; - - this.element.innerHTML = ""; - this.element.style.position = "fixed"; - this.element.style.bottom = "40px"; - this.element.style.width = "200px"; - this.element.style.marginLeft = "-100px"; - this.element.style.left = "50%"; - this.element.style.borderRadius = "5px"; - this.element.style.border = "1px solid #727678"; - this.element.style.height = "16px"; - this.element.style.padding = "1px"; - this.element.style.textAlign = "center"; - this.element.style.backgroundColor = "#6ba8e5"; - this.element.style.opacity = this.maxOpacity; - this.element.style.pointerEvents = "none"; - - this.elProgress.innerHTML = " "; - this.elProgress.style.backgroundColor = "#b8e1fc"; - this.elProgress.style.position = "absolute"; - this.elProgress.style.borderRadius = "5px"; - this.elProgress.style.width = "0%"; - this.elProgress.style.height = "100%"; - this.elProgress.style.margin = "0px"; - this.elProgress.style.padding = "0px"; - - this.elProgressMessage.style.position = "absolute"; - this.elProgressMessage.style.width = "100%"; - this.elProgressMessage.innerHTML = "loading 1 / 10"; - - - - document.body.appendChild(this.element); - this.element.appendChild(this.elProgress); - this.element.appendChild(this.elProgressMessage); - - this.hide(); -}; - -ProgressBar.prototype.hide = function(){ - this.element.style.opacity = 0; - this.element.style.transition = "all 0.2s ease"; -}; - -ProgressBar.prototype.show = function(){ - this.element.style.opacity = this.maxOpacity; - this.element.style.transition = "all 0.2s ease"; -}; - -Object.defineProperty(ProgressBar.prototype, "progress", { - get: function(){ - return this._progress; - }, - set: function(value){ - this._progress = value; - this.elProgress.style.width = (value * 100) + "%"; - } -}); - -Object.defineProperty(ProgressBar.prototype, "message", { - get: function(){ - return this._message; - }, - set: function(message){ - this._message = message; - this.elProgressMessage.innerHTML = message; - } -}); \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/examples/js/viewer.js b/PotreeConverter/resources/page_template/examples/js/viewer.js deleted file mode 100644 index d1f74e2b..00000000 --- a/PotreeConverter/resources/page_template/examples/js/viewer.js +++ /dev/null @@ -1,1588 +0,0 @@ - -Potree.Viewer = function(domElement, settings, args){ - var scope = this; - var defaultSettings = settings; - var arguments = args || {}; - - this.renderArea = domElement; - - { // create stats fields - var createField = function(id, top){ - var field = document.createElement("div"); - field.id = id; - field.classList.add("info"); - field.style.position = "absolute"; - field.style.left = "10px"; - field.style.top = top + "px"; - field.style.width = "400px"; - field.style.color = "white"; - - return field; - }; - - var elNumVisibleNodes = createField("lblNumVisibleNodes", 80); - var elNumVisiblePoints = createField("lblNumVisiblePoints", 100); - - scope.renderArea.appendChild(elNumVisibleNodes); - scope.renderArea.appendChild(elNumVisiblePoints); - } - - { // infos - scope.infos = new function(){ - - var _this = this; - - this.elements = {}; - - this.domElement = document.createElement("div"); - this.domElement.id = "infos"; - this.domElement.classList.add("info"); - this.domElement.style.position = "fixed"; - this.domElement.style.left = "10px"; - this.domElement.style.top = "120px"; - this.domElement.style.pointerEvents = "none"; - - scope.renderArea.appendChild(this.domElement); - - this.set = function(key, value){ - var element = this.elements[key]; - if(typeof element === "undefined"){ - element = document.createElement("div"); - _this.domElement.appendChild(element); - this.elements[key] = element; - - } - - element.innerHTML = value; - }; - - }; - } - - { // create toolbar - var elToolbar = document.createElement("div"); - elToolbar.style.position = "absolute"; - elToolbar.style.width = "400px"; - elToolbar.style.bottom = "10px"; - elToolbar.style.right = "10px"; - this.renderArea.appendChild(elToolbar); - - var createToolIcon = function(icon, title, callback){ - var tool = document.createElement("img"); - tool.src = icon; - tool.title = title; - tool.onclick = callback; - - return tool; - }; - - elToolbar.appendChild(createToolIcon( - "../resources/icons/earth_controls_1.png", - "Earth Controls", - function(){scope.useEarthControls()} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/fps_controls.png", - "Flight Controls", - function(){scope.useFPSControls()} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/orbit_controls.png", - "Orbit Controls", - function(){scope.useOrbitControls()} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/focus.png", - "focus on pointcloud", - function(){scope.zoomTo(viewer.pointcloud)} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/flip_y_z.png", - "flip y and z coordinates", - function(){scope.flipYZ()} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/angle.png", - "angle measurements", - function(){scope.measuringTool.startInsertion({showDistances: false, showAngles: true, showArea: false, closed: true, maxMarkers: 3})} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/distance.png", - "distance measurements", - function(){scope.measuringTool.startInsertion({showDistances: true, showArea: false, closed: false})} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/area.png", - "area measurements", - function(){scope.measuringTool.startInsertion({showDistances: true, showArea: true, closed: true})} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/volume.png", - "volume measurements", - function(){scope.volumeTool.startInsertion()} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/profile.png", - "height profiles", - function(){scope.profileTool.startInsertion({width: viewer.pointcloud.boundingSphere.radius / 100})} - )); - - elToolbar.appendChild(createToolIcon( - "../resources/icons/clip_volume.png", - "clipping volumes", - function(){scope.volumeTool.startInsertion({clip: true})} - )); - - - } - - - - - - - - - - - if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { - defaultSettings.navigation = "Orbit"; - } - - if(defaultSettings.useEDL && !Potree.Features.SHADER_EDL.isSupported()){ - defaultSettings.useEDL = false; - } - - if(typeof arguments.onPointCloudLoaded !== "undefined"){ - this.addEventListener("pointcloud_loaded", arguments.onPointCloudLoaded); - } - - this.annotations = []; - this.fov = defaultSettings.fov || 60; - this.pointSize = defaultSettings.pointSize || 1; - this.pointCountTarget = defaultSettings.pointLimit || 1; - this.opacity = 1; - this.pointSizeType = null; - this.pointColorType = null; - this.clipMode = Potree.ClipMode.HIGHLIGHT_INSIDE; - this.quality = defaultSettings.quality || "Squares"; - this.isFlipYZ = false; - this.useDEMCollisions = false; - this.minNodeSize = 100; - this.directionalLight; - this.edlScale = defaultSettings.edlScale || 1; - this.edlRadius = defaultSettings.edlRadius || 3; - this.useEDL = defaultSettings.useEDL || false; - - this.showDebugInfos = false; - this.showStats = false; - this.showBoundingBox = false; - this.freeze = false; - - this.fpControls; - this.orbitControls; - this.earthControls; - this.controls; - - var progressBar = new ProgressBar(); - - var pointcloudPath = defaultSettings.path; - - var gui; - - this.renderer; - this.camera; - this.scene; - this.scenePointCloud; - this.sceneBG; - this.cameraBG; - this.pointcloud = null; - this.measuringTool; - this.volumeTool; - this.transformationTool; - - var skybox; - var stats; - var clock = new THREE.Clock(); - var showSkybox = false; - var referenceFrame; - - this.setPointSizeType = function(value){ - if(value === "Fixed"){ - scope.pointSizeType = Potree.PointSizeType.FIXED; - }else if(value === "Attenuated"){ - scope.pointSizeType = Potree.PointSizeType.ATTENUATED; - }else if(value === "Adaptive"){ - scope.pointSizeType = Potree.PointSizeType.ADAPTIVE; - } - }; - - this.setQuality = function(value){ - if(value == "Interpolation" && !Potree.Features.SHADER_INTERPOLATION.isSupported()){ - scope.quality = "Squares"; - }else if(value == "Splats" && !Potree.Features.SHADER_SPLATS.isSupported()){ - scope.quality = "Squares"; - }else{ - scope.quality = value; - } - }; - - this.setMaterial = function(value){ - if(value === "RGB"){ - scope.pointColorType = Potree.PointColorType.RGB; - }else if(value === "Color"){ - scope.pointColorType = Potree.PointColorType.COLOR; - }else if(value === "Elevation"){ - scope.pointColorType = Potree.PointColorType.HEIGHT; - }else if(value === "Intensity"){ - scope.pointColorType = Potree.PointColorType.INTENSITY; - }else if(value === "Intensity Gradient"){ - scope.pointColorType = Potree.PointColorType.INTENSITY_GRADIENT; - }else if(value === "Classification"){ - scope.pointColorType = Potree.PointColorType.CLASSIFICATION; - }else if(value === "Return Number"){ - scope.pointColorType = Potree.PointColorType.RETURN_NUMBER; - }else if(value === "Source"){ - scope.pointColorType = Potree.PointColorType.SOURCE; - }else if(value === "Tree Depth"){ - scope.pointColorType = Potree.PointColorType.TREE_DEPTH; - }else if(value === "Point Index"){ - scope.pointColorType = Potree.PointColorType.POINT_INDEX; - }else if(value === "Normal"){ - scope.pointColorType = Potree.PointColorType.NORMAL; - }else if(value === "Phong"){ - scope.pointColorType = Potree.PointColorType.PHONG; - } - }; - - this.zoomTo = function(node, factor){ - scope.camera.zoomTo(node, factor); - - var bs; - if(node.boundingSphere){ - bs = node.boundingSphere; - }else if(node.geometry && node.geometry.boundingSphere){ - bs = node.geometry.boundingSphere; - }else{ - bs = node.boundingBox.getBoundingSphere(); - } - - bs = bs.clone().applyMatrix4(node.matrixWorld); - - scope.orbitControls.target.copy(bs.center); - }; - - this.initGUI = function(){ - - scope.setPointSizeType(defaultSettings.sizeType); - scope.setQuality(defaultSettings.quality); - scope.setMaterial(defaultSettings.material); - - // dat.gui - gui = new dat.GUI({ - autoPlace: false - //height : 5 * 32 - 1 - }); - gui.domElement.style.position = "absolute"; - gui.domElement.style.top = "5px"; - gui.domElement.style.right = "5px"; - this.renderArea.appendChild(gui.domElement); - - params = { - "max. points(m)": scope.pointCountTarget, - PointSize: scope.pointSize, - "FOV": scope.fov, - "opacity": scope.opacity, - "SizeType" : defaultSettings.sizeType, - "show octree" : false, - "Materials" : defaultSettings.material, - "Clip Mode": "Highlight Inside", - "quality": defaultSettings.quality, - "EDL": defaultSettings.useEDL, - "EDLScale": scope.edlScale, - "skybox": false, - "stats": scope.showStats, - "debugInfos": scope.showDebugInfos, - "BoundingBox": scope.showBoundingBox, - "DEM Collisions": scope.useDEMCollisions, - "MinNodeSize": scope.minNodeSize, - "freeze": scope.freeze - }; - - var pPoints = gui.add(params, 'max. points(m)', 0, 4); - pPoints.onChange(function(value){ - scope.pointCountTarget = value ; - }); - - var fAppearance = gui.addFolder('Appearance'); - - var pPointSize = fAppearance.add(params, 'PointSize', 0, 3); - pPointSize.onChange(function(value){ - scope.pointSize = value; - }); - - var fFOV = fAppearance.add(params, 'FOV', 20, 100); - fFOV.onChange(function(value){ - scope.fov = value; - }); - - var pOpacity = fAppearance.add(params, 'opacity', 0, 1); - pOpacity.onChange(function(value){ - scope.opacity = value; - }); - - var pSizeType = fAppearance.add(params, 'SizeType', [ "Fixed", "Attenuated", "Adaptive"]); - pSizeType.onChange(function(value){ - scope.setPointSizeType(value); - }); - - var options = []; - var attributes = scope.pointcloud.pcoGeometry.pointAttributes - if(attributes === "LAS" || attributes === "LAZ"){ - options = [ - "RGB", "Color", "Elevation", "Intensity", "Intensity Gradient", - "Classification", "Return Number", "Source", - "Tree Depth"]; - }else{ - for(var i = 0; i < attributes.attributes.length; i++){ - var attribute = attributes.attributes[i]; - - if(attribute === Potree.PointAttribute.COLOR_PACKED){ - options.push("RGB"); - }else if(attribute === Potree.PointAttribute.INTENSITY){ - options.push("Intensity"); - options.push("Intensity Gradient"); - }else if(attribute === Potree.PointAttribute.CLASSIFICATION){ - options.push("Classification"); - } - } - if(attributes.hasNormals()){ - options.push("Phong"); - options.push("Normal"); - } - - options.push("Elevation"); - options.push("Color"); - options.push("Tree Depth"); - } - - // default material is not available. set material to Elevation - if(options.indexOf(params.Materials) < 0){ - console.error("Default Material '" + params.Material + "' is not available. Using Elevation instead"); - scope.setMaterial("Elevation"); - params.Materials = "Elevation"; - } - - - pMaterial = fAppearance.add(params, 'Materials',options); - pMaterial.onChange(function(value){ - scope.setMaterial(value); - }); - - var qualityOptions = ["Squares", "Circles"]; - if(Potree.Features.SHADER_INTERPOLATION.isSupported()){ - qualityOptions.push("Interpolation"); - } - if(Potree.Features.SHADER_SPLATS.isSupported()){ - qualityOptions.push("Splats"); - } - var pQuality = fAppearance.add(params, 'quality', qualityOptions); - pQuality.onChange(function(value){ - scope.quality = value; - }); - - { // Eye-Dome-Lighting - if(Potree.Features.SHADER_EDL.isSupported()){ - - var edlParams = { - "enable": scope.useEDL, - "strength": scope.edlScale, - "radius": scope.edlRadius - }; - - var fEDL = fAppearance.addFolder('Eye-Dome-Lighting'); - var pEDL = fEDL.add(edlParams, 'enable'); - pEDL.onChange(function(value){ - scope.useEDL = value; - }); - - var pEDLScale = fEDL.add(edlParams, 'strength', 0, 3, 0.01); - pEDLScale.onChange(function(value){ - scope.edlScale = value; - }); - - var pRadius = fEDL.add(edlParams, 'radius', 1, 5); - pRadius.onChange(function(value){ - scope.edlRadius = value; - }); - } - } - - { // Classification - var classificationParams = { - "never classified": true, - "unclassified": true, - "ground": true, - "low vegetation": true, - "medium vegetation": true, - "high vegetation": true, - "building": true, - "low point(noise)": true, - "key-point": true, - "water": true, - "overlap": true - }; - - var setClassificationVisibility = function(key, value){ - if(!scope.pointcloud){ - return; - } - var newClass = scope.pointcloud.material.classification; - newClass[key].w = value ? 1 : 0; - - scope.pointcloud.material.classification = newClass; - }; - - var fClassification = fAppearance.addFolder('Classification'); - - var pNeverClassified = fClassification.add(classificationParams, 'never classified'); - pNeverClassified.onChange(function(value){ - setClassificationVisibility(0, value); - }); - - var pUnclassified = fClassification.add(classificationParams, 'unclassified'); - pUnclassified.onChange(function(value){ - setClassificationVisibility(1, value); - }); - - var pGround = fClassification.add(classificationParams, 'ground'); - pGround.onChange(function(value){ - setClassificationVisibility(2, value); - }); - - var pLowVeg = fClassification.add(classificationParams, 'low vegetation'); - pLowVeg.onChange(function(value){ - setClassificationVisibility(3, value); - }); - - var pMedVeg = fClassification.add(classificationParams, 'medium vegetation'); - pMedVeg.onChange(function(value){ - setClassificationVisibility(4, value); - }); - - var pHighVeg = fClassification.add(classificationParams, 'high vegetation'); - pHighVeg.onChange(function(value){ - setClassificationVisibility(5, value); - }); - - var pBuilding = fClassification.add(classificationParams, 'building'); - pBuilding.onChange(function(value){ - setClassificationVisibility(6, value); - }); - - var pNoise = fClassification.add(classificationParams, 'low point(noise)'); - pNoise.onChange(function(value){ - setClassificationVisibility(7, value); - }); - - var pKeyPoint = fClassification.add(classificationParams, 'key-point'); - pKeyPoint.onChange(function(value){ - setClassificationVisibility(8, value); - }); - - var pWater = fClassification.add(classificationParams, 'water'); - pWater.onChange(function(value){ - setClassificationVisibility(9, value); - }); - - var pOverlap = fClassification.add(classificationParams, 'overlap'); - pOverlap.onChange(function(value){ - setClassificationVisibility(12, value); - }); - - - - } - - var pSykbox = fAppearance.add(params, 'skybox'); - pSykbox.onChange(function(value){ - showSkybox = value; - }); - - var fSettings = gui.addFolder('Settings'); - - var pClipMode = fSettings.add(params, 'Clip Mode', [ "No Clipping", "Clip Outside", "Highlight Inside"]); - pClipMode.onChange(function(value){ - if(value === "No Clipping"){ - scope.clipMode = Potree.ClipMode.DISABLED; - }else if(value === "Clip Outside"){ - scope.clipMode = Potree.ClipMode.CLIP_OUTSIDE; - }else if(value === "Highlight Inside"){ - scope.clipMode = Potree.ClipMode.HIGHLIGHT_INSIDE; - } - }); - - var pDEMCollisions = fSettings.add(params, 'DEM Collisions'); - pDEMCollisions.onChange(function(value){ - scope.useDEMCollisions = value; - }); - - var pMinNodeSize = fSettings.add(params, 'MinNodeSize', 0, 1500); - pMinNodeSize.onChange(function(value){ - scope.minNodeSize = value; - }); - - - - - var fDebug = gui.addFolder('Debug'); - - - var pStats = fDebug.add(params, 'stats'); - pStats.onChange(function(value){ - scope.showStats = value; - }); - - var pShowDebugInfos = fDebug.add(params, "debugInfos"); - pShowDebugInfos.onChange(function(value){ - scope.showDebugInfos = value; - scope.infos.domElement.style.display = scope.showDebugInfos ? "block" : "none"; - }); - - var pBoundingBox = fDebug.add(params, 'BoundingBox'); - pBoundingBox.onChange(function(value){ - scope.showBoundingBox = value; - }); - - var pFreeze = fDebug.add(params, 'freeze'); - pFreeze.onChange(function(value){ - scope.freeze = value; - }); - - // stats - stats = new Stats(); - stats.domElement.style.position = 'absolute'; - stats.domElement.style.top = '0px'; - stats.domElement.style.margin = '5px'; - document.body.appendChild( stats.domElement ); - } - - this.createControls = function(){ - { // create FIRST PERSON CONTROLS - scope.fpControls = new THREE.FirstPersonControls(scope.camera, scope.renderer.domElement); - scope.fpControls.addEventListener("proposeTransform", function(event){ - if(!scope.pointcloud || !scope.useDEMCollisions){ - return; - } - - var demHeight = scope.pointcloud.getDEMHeight(event.newPosition); - if(event.newPosition.y < demHeight){ - event.objections++; - - var counterProposal = event.newPosition.clone(); - counterProposal.y = demHeight; - - event.counterProposals.push(counterProposal); - } - }); - } - - { // create ORBIT CONTROLS - scope.orbitControls = new Potree.OrbitControls(scope.camera, scope.renderer.domElement); - scope.orbitControls.addEventListener("proposeTransform", function(event){ - if(!scope.pointcloud || !scope.useDEMCollisions){ - return; - } - - var demHeight = scope.pointcloud.getDEMHeight(event.newPosition); - if(event.newPosition.y < demHeight){ - event.objections++; - - var counterProposal = event.newPosition.clone(); - counterProposal.y = demHeight; - - event.counterProposals.push(counterProposal); - } - }); - scope.renderArea.addEventListener("dblclick", function(event){ - if(!scope.pointcloud){ - return; - } - - event.preventDefault(); - - var rect = scope.renderArea.getBoundingClientRect(); - - var mouse = { - x: ( (event.clientX - rect.left) / scope.renderArea.clientWidth ) * 2 - 1, - y: - ( (event.clientY - rect.top) / scope.renderArea.clientHeight ) * 2 + 1 - }; - - - var I = getMousePointCloudIntersection(mouse, scope.camera, scope.renderer, [scope.pointcloud]); - if(I != null){ - - var camTargetDistance = scope.camera.position.distanceTo(scope.orbitControls.target); - - var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); - vector.unproject(scope.camera); - - var direction = vector.sub(scope.camera.position).normalize(); - var ray = new THREE.Ray(scope.camera.position, direction); - - var nodes = scope.pointcloud.nodesOnRay(scope.pointcloud.visibleNodes, ray); - var lastNode = nodes[nodes.length - 1]; - var radius = lastNode.boundingSphere.radius; - var targetRadius = Math.min(camTargetDistance, radius); - - var d = scope.camera.getWorldDirection().multiplyScalar(-1); - var cameraTargetPosition = new THREE.Vector3().addVectors(I, d.multiplyScalar(targetRadius)); - var controlsTargetPosition = I; - - var animationDuration = 600; - - var easing = TWEEN.Easing.Quartic.Out; - - scope.controls.enabled = false; - - // animate position - var tween = new TWEEN.Tween(scope.camera.position).to(cameraTargetPosition, animationDuration); - tween.easing(easing); - tween.start(); - - // animate target - var tween = new TWEEN.Tween(scope.orbitControls.target).to(I, animationDuration); - tween.easing(easing); - tween.onComplete(function(){ - scope.controls.enabled = true; - scope.fpControls.moveSpeed = radius / 2; - }); - tween.start(); - } - }); - } - - { // create EARTH CONTROLS - scope.earthControls = new THREE.EarthControls(scope.camera, scope.renderer, scope.scenePointCloud); - scope.earthControls.addEventListener("proposeTransform", function(event){ - if(!scope.pointcloud || !scope.useDEMCollisions){ - return; - } - - var demHeight = scope.pointcloud.getDEMHeight(event.newPosition); - if(event.newPosition.y < demHeight){ - event.objections++; - } - }); - } - }; - - this.initThree = function(){ - var width = renderArea.clientWidth; - var height = renderArea.clientHeight; - var aspect = width / height; - var near = 0.1; - var far = 1000*1000; - - scope.scene = new THREE.Scene(); - scope.scenePointCloud = new THREE.Scene(); - scope.sceneBG = new THREE.Scene(); - - scope.camera = new THREE.PerspectiveCamera(scope.fov, aspect, near, far); - //camera = new THREE.OrthographicCamera(-50, 50, 50, -50, 1, 100000); - scope.cameraBG = new THREE.Camera(); - scope.camera.rotation.order = 'ZYX'; - - referenceFrame = new THREE.Object3D(); - scope.scenePointCloud.add(referenceFrame); - - scope.renderer = new THREE.WebGLRenderer(); - scope.renderer.setSize(width, height); - scope.renderer.autoClear = false; - renderArea.appendChild(scope.renderer.domElement); - - skybox = Potree.utils.loadSkybox("../resources/textures/skybox/"); - - // camera and controls - scope.camera.position.set(-304, 372, 318); - scope.camera.rotation.y = -Math.PI / 4; - scope.camera.rotation.x = -Math.PI / 6; - - this.createControls(); - - scope.useEarthControls(); - - // enable frag_depth extension for the interpolation shader, if available - scope.renderer.context.getExtension("EXT_frag_depth"); - - // load pointcloud - if(!pointcloudPath){ - - }else if(pointcloudPath.indexOf("cloud.js") > 0){ - Potree.POCLoader.load(pointcloudPath, function(geometry){ - scope.pointcloud = new Potree.PointCloudOctree(geometry); - - scope.pointcloud.material.pointSizeType = Potree.PointSizeType.ADAPTIVE; - scope.pointcloud.material.size = scope.pointSize; - scope.pointcloud.visiblePointsTarget = scope.pointCountTarget * 1000 * 1000; - - referenceFrame.add(scope.pointcloud); - - referenceFrame.updateMatrixWorld(true); - var sg = scope.pointcloud.boundingSphere.clone().applyMatrix4(scope.pointcloud.matrixWorld); - - referenceFrame.position.copy(sg.center).multiplyScalar(-1); - referenceFrame.updateMatrixWorld(true); - - if(sg.radius > 50*1000){ - scope.camera.near = 10; - }else if(sg.radius > 10*1000){ - scope.camera.near = 2; - }else if(sg.radius > 1000){ - scope.camera.near = 1; - }else if(sg.radius > 100){ - scope.camera.near = 0.5; - }else{ - scope.camera.near = 0.1; - } - - - scope.flipYZ(); - scope.zoomTo(scope.pointcloud, 1); - - scope.initGUI(); - - scope.earthControls.pointclouds.push(scope.pointcloud); - - - - if(defaultSettings.navigation === "Earth"){ - scope.useEarthControls(); - }else if(defaultSettings.navigation === "Orbit"){ - scope.useOrbitControls(); - }else if(defaultSettings.navigation === "Flight"){ - scope.useFPSControls(); - }else{ - console.warning("No navigation mode specified. Using OrbitControls"); - scope.useOrbitControls(); - } - - if(defaultSettings.cameraPosition != null){ - var cp = new THREE.Vector3(defaultSettings.cameraPosition[0], defaultSettings.cameraPosition[1], defaultSettings.cameraPosition[2]); - scope.camera.position.copy(cp); - } - - if(defaultSettings.cameraTarget != null){ - var ct = new THREE.Vector3(defaultSettings.cameraTarget[0], defaultSettings.cameraTarget[1], defaultSettings.cameraTarget[2]); - scope.camera.lookAt(ct); - - if(defaultSettings.navigation === "Orbit"){ - scope.controls.target.copy(ct); - } - } - - scope.dispatchEvent({ - "type": "pointcloud_loaded", - "pointcloud": scope.pointcloud - }); - - }); - }else if(pointcloudPath.indexOf(".vpc") > 0){ - Potree.PointCloudArena4DGeometry.load(pointcloudPath, function(geometry){ - scope.pointcloud = new Potree.PointCloudArena4D(geometry); - scope.pointcloud.visiblePointsTarget = 500*1000; - - //scope.pointcloud.applyMatrix(new THREE.Matrix4().set( - // 1,0,0,0, - // 0,0,1,0, - // 0,-1,0,0, - // 0,0,0,1 - //)); - - referenceFrame.add(scope.pointcloud); - - flipYZ(); - - referenceFrame.updateMatrixWorld(true); - var sg = scope.pointcloud.boundingSphere.clone().applyMatrix4(scope.pointcloud.matrixWorld); - - referenceFrame.position.sub(sg.center); - referenceFrame.position.y += sg.radius / 2; - referenceFrame.updateMatrixWorld(true); - - scope.zoomTo(scope.pointcloud, 1); - - initGUI(); - scope.pointcloud.material.interpolation = false; - scope.pointcloud.material.pointSizeType = Potree.PointSizeType.ATTENUATED; - scope.earthControls.pointclouds.push(scope.pointcloud); - - - if(defaultSettings.navigation === "Earth"){ - scope.useEarthControls(); - }else if(defaultSettings.navigation === "Orbit"){ - scope.useOrbitControls(); - }else if(defaultSettings.navigation === "Flight"){ - scope.useFPSControls(); - }else{ - console.warning("No navigation mode specivied. Using OrbitControls"); - scope.useOrbitControls(); - } - - if(defaultSettings.cameraPosition != null){ - var cp = new THREE.Vector3(defaultSettings.cameraPosition[0], defaultSettings.cameraPosition[1], defaultSettings.cameraPosition[2]); - scope.camera.position.copy(cp); - } - - if(defaultSettings.cameraTarget != null){ - var ct = new THREE.Vector3(defaultSettings.cameraTarget[0], defaultSettings.cameraTarget[1], defaultSettings.cameraTarget[2]); - scope.camera.lookAt(ct); - } - - }); - } - - var grid = Potree.utils.createGrid(5, 5, 2); - scope.scene.add(grid); - - scope.measuringTool = new Potree.MeasuringTool(scope.scenePointCloud, scope.camera, scope.renderer); - scope.profileTool = new Potree.ProfileTool(scope.scenePointCloud, scope.camera, scope.renderer); - scope.transformationTool = new Potree.TransformationTool(scope.scenePointCloud, scope.camera, scope.renderer); - scope.volumeTool = new Potree.VolumeTool(scope.scenePointCloud, scope.camera, scope.renderer, scope.transformationTool); - - - // background - // var texture = THREE.ImageUtils.loadTexture( '../resources/textures/background.gif' ); - var texture = Potree.utils.createBackgroundTexture(512, 512); - - texture.minFilter = texture.magFilter = THREE.NearestFilter; - texture.minFilter = texture.magFilter = THREE.LinearFilter; - - var bg = new THREE.Mesh( - new THREE.PlaneBufferGeometry(2, 2, 0), - new THREE.MeshBasicMaterial({ - map: texture - }) - ); - //bg.position.z = -1; - bg.material.depthTest = false; - bg.material.depthWrite = false; - scope.sceneBG.add(bg); - - window.addEventListener( 'keydown', onKeyDown, false ); - - scope.directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); - scope.directionalLight.position.set( 10, 10, 10 ); - scope.directionalLight.lookAt( new THREE.Vector3(0, 0, 0)); - scope.scenePointCloud.add( scope.directionalLight ); - - var light = new THREE.AmbientLight( 0x555555 ); // soft white light - scope.scenePointCloud.add( light ); - - } - - this.flipYZ = function(){ - scope.isFlipYZ = !scope.isFlipYZ; - - if(scope.isFlipYZ){ - referenceFrame.matrix.copy(new THREE.Matrix4()); - referenceFrame.applyMatrix(new THREE.Matrix4().set( - 1,0,0,0, - 0,0,1,0, - 0,-1,0,0, - 0,0,0,1 - )); - - }else{ - referenceFrame.matrix.copy(new THREE.Matrix4()); - referenceFrame.applyMatrix(new THREE.Matrix4().set( - 1,0,0,0, - 0,1,0,0, - 0,0,1,0, - 0,0,0,1 - )); - } - - referenceFrame.updateMatrixWorld(true); - scope.pointcloud.updateMatrixWorld(); - var sg = scope.pointcloud.boundingSphere.clone().applyMatrix4(scope.pointcloud.matrixWorld); - referenceFrame.position.copy(sg.center).multiplyScalar(-1); - referenceFrame.updateMatrixWorld(true); - referenceFrame.position.y -= scope.pointcloud.getWorldPosition().y; - referenceFrame.updateMatrixWorld(true); - } - - function onKeyDown(event){ - //console.log(event.keyCode); - - if(event.keyCode === 69){ - // e pressed - - scope.transformationTool.translate(); - }else if(event.keyCode === 82){ - // r pressed - - scope.transformationTool.scale(); - }else if(event.keyCode === 84){ - // r pressed - - scope.transformationTool.rotate(); - } - }; - - var intensityMax = null; - var heightMin = null; - var heightMax = null; - - this.update = function(delta, timestamp){ - Potree.pointLoadLimit = scope.pointCountTarget * 2 * 1000 * 1000; - - scope.directionalLight.position.copy(scope.camera.position); - scope.directionalLight.lookAt(new THREE.Vector3().addVectors(scope.camera.position, scope.camera.getWorldDirection())); - - if(scope.pointcloud){ - - var bbWorld = Potree.utils.computeTransformedBoundingBox(scope.pointcloud.boundingBox, scope.pointcloud.matrixWorld); - - if(!intensityMax){ - var root = scope.pointcloud.pcoGeometry.root; - if(root != null && root.loaded){ - var attributes = scope.pointcloud.pcoGeometry.root.geometry.attributes; - if(attributes.intensity){ - var array = attributes.intensity.array; - var max = 0; - for(var i = 0; i < array.length; i++){ - max = Math.max(array[i]); - } - - if(max <= 1){ - intensityMax = 1; - }else if(max <= 256){ - intensityMax = 255; - }else{ - intensityMax = max; - } - } - } - } - - if(heightMin === null){ - heightMin = bbWorld.min.y; - heightMax = bbWorld.max.y; - } - - scope.pointcloud.material.clipMode = scope.clipMode; - scope.pointcloud.material.heightMin = heightMin; - scope.pointcloud.material.heightMax = heightMax; - scope.pointcloud.material.intensityMin = 0; - scope.pointcloud.material.intensityMax = intensityMax; - scope.pointcloud.showBoundingBox = scope.showBoundingBox; - scope.pointcloud.generateDEM = scope.useDEMCollisions; - scope.pointcloud.minimumNodePixelSize = scope.minNodeSize; - - if(!scope.freeze){ - scope.pointcloud.update(scope.camera, scope.renderer); - } - } - - if(stats && scope.showStats){ - document.getElementById("lblNumVisibleNodes").style.display = ""; - document.getElementById("lblNumVisiblePoints").style.display = ""; - stats.domElement.style.display = ""; - - stats.update(); - - if(scope.pointcloud){ - document.getElementById("lblNumVisibleNodes").innerHTML = "visible nodes: " + scope.pointcloud.numVisibleNodes; - document.getElementById("lblNumVisiblePoints").innerHTML = "visible points: " + Potree.utils.addCommas(scope.pointcloud.numVisiblePoints); - } - }else if(stats){ - document.getElementById("lblNumVisibleNodes").style.display = "none"; - document.getElementById("lblNumVisiblePoints").style.display = "none"; - stats.domElement.style.display = "none"; - } - - scope.camera.fov = scope.fov; - - if(scope.controls){ - scope.controls.update(delta); - } - - // update progress bar - if(scope.pointcloud){ - var progress = scope.pointcloud.progress; - - progressBar.progress = progress; - - var message; - if(progress === 0 || scope.pointcloud instanceof Potree.PointCloudArena4D){ - message = "loading"; - }else{ - message = "loading: " + parseInt(progress*100) + "%"; - } - progressBar.message = message; - - if(progress === 1){ - progressBar.hide(); - }else if(progress < 1){ - progressBar.show(); - } - } - - scope.volumeTool.update(); - scope.transformationTool.update(); - scope.profileTool.update(); - - - var clipBoxes = []; - - for(var i = 0; i < scope.profileTool.profiles.length; i++){ - var profile = scope.profileTool.profiles[i]; - - for(var j = 0; j < profile.boxes.length; j++){ - var box = profile.boxes[j]; - box.updateMatrixWorld(); - var boxInverse = new THREE.Matrix4().getInverse(box.matrixWorld); - clipBoxes.push(boxInverse); - } - } - - for(var i = 0; i < scope.volumeTool.volumes.length; i++){ - var volume = scope.volumeTool.volumes[i]; - - if(volume.clip){ - volume.updateMatrixWorld(); - var boxInverse = new THREE.Matrix4().getInverse(volume.matrixWorld); - - clipBoxes.push(boxInverse); - } - } - - if(scope.pointcloud){ - scope.pointcloud.material.setClipBoxes(clipBoxes); - } - - {// update annotations - var distances = []; - for(var i = 0; i < scope.annotations.length; i++){ - var ann = scope.annotations[i]; - var screenPos = ann.position.clone().project(scope.camera); - - screenPos.x = scope.renderArea.clientWidth * (screenPos.x + 1) / 2; - screenPos.y = scope.renderArea.clientHeight * (1 - (screenPos.y + 1) / 2); - - ann.domElement.style.left = screenPos.x - ann.domElement.clientWidth / 2; - ann.domElement.style.top = screenPos.y; - - distances.push({annotation: ann, distance: screenPos.z}); - - if(-1 > screenPos.z || screenPos.z > 1){ - ann.domElement.style.display = "none"; - }else{ - ann.domElement.style.display = "initial"; - } - } - distances.sort(function(a,b){return b.distance - a.distance}); - for(var i = 0; i < distances.length; i++){ - var ann = distances[i].annotation; - ann.domElement.style.zIndex = "" + i; - } - } - - if(scope.showDebugInfos){ - scope.infos.set("camera.position", "camera.position: " + - viewer.camera.position.x.toFixed(2) - + ", " + viewer.camera.position.y.toFixed(2) - + ", " + viewer.camera.position.z.toFixed(2) - ); - } - - TWEEN.update(timestamp); - } - - this.useEarthControls = function(){ - if(scope.controls){ - scope.controls.enabled = false; - } - - scope.controls = scope.earthControls; - scope.controls.enabled = true; - } - - this.useFPSControls = function(){ - if(scope.controls){ - scope.controls.enabled = false; - } - - scope.controls = scope.fpControls; - scope.controls.enabled = true; - - scope.controls.moveSpeed = scope.pointcloud.boundingSphere.radius / 6; - } - - this.useOrbitControls = function(){ - if(scope.controls){ - scope.controls.enabled = false; - } - - scope.controls = scope.orbitControls; - scope.controls.enabled = true; - - if(scope.pointcloud){ - scope.controls.target.copy(scope.pointcloud.boundingSphere.center.clone().applyMatrix4(scope.pointcloud.matrixWorld)); - } - }; - - this.addAnnotation = function(position, args){ - var cameraPosition = args.cameraPosition; - var cameraTarget = args.cameraTarget || position; - - var annotation = new Potree.Annotation(scope, { - "position": position, - "cameraPosition": cameraPosition, - "cameraTarget": cameraTarget - }); - - scope.annotations.push(annotation); - scope.renderArea.appendChild(annotation.domElement); - } - - var PotreeRenderer = function(){ - - this.render = function(){ - {// resize - var width = renderArea.clientWidth; - var height = renderArea.clientHeight; - var aspect = width / height; - - scope.camera.aspect = aspect; - scope.camera.updateProjectionMatrix(); - - scope.renderer.setSize(width, height); - } - - - // render skybox - if(showSkybox){ - scope.camera.rotation.copy(scope.camera.rotation); - scope.renderer.render(skybox.scene, skybox.camera); - }else{ - scope.renderer.render(scope.sceneBG, scope.cameraBG); - } - - if(scope.pointcloud){ - if(scope.pointcloud.originalMaterial){ - scope.pointcloud.material = scope.pointcloud.originalMaterial; - } - - var bbWorld = Potree.utils.computeTransformedBoundingBox(scope.pointcloud.boundingBox, scope.pointcloud.matrixWorld); - - scope.pointcloud.visiblePointsTarget = scope.pointCountTarget * 1000 * 1000; - scope.pointcloud.material.size = scope.pointSize; - scope.pointcloud.material.opacity = scope.opacity; - scope.pointcloud.material.pointColorType = scope.pointColorType; - scope.pointcloud.material.pointSizeType = scope.pointSizeType; - scope.pointcloud.material.pointShape = (scope.quality === "Circles") ? Potree.PointShape.CIRCLE : Potree.PointShape.SQUARE; - scope.pointcloud.material.interpolate = (scope.quality === "Interpolation"); - scope.pointcloud.material.weighted = false; - } - - // render scene - scope.renderer.render(scope.scene, scope.camera); - scope.renderer.render(scope.scenePointCloud, scope.camera); - - scope.profileTool.render(); - scope.volumeTool.render(); - - scope.renderer.clearDepth(); - scope.measuringTool.render(); - scope.transformationTool.render(); - }; - }; - var potreeRenderer = new PotreeRenderer(); - - // high quality rendering using splats - var highQualityRenderer = null; - var HighQualityRenderer = function(){ - - var depthMaterial = null; - var attributeMaterial = null; - var normalizationMaterial = null; - - var rtDepth; - var rtNormalize; - - var initHQSPlats = function(){ - if(depthMaterial != null){ - return; - } - - depthMaterial = new Potree.PointCloudMaterial(); - attributeMaterial = new Potree.PointCloudMaterial(); - - depthMaterial.pointColorType = Potree.PointColorType.DEPTH; - depthMaterial.pointShape = Potree.PointShape.CIRCLE; - depthMaterial.interpolate = false; - depthMaterial.weighted = false; - depthMaterial.minSize = 2; - - attributeMaterial.pointShape = Potree.PointShape.CIRCLE; - attributeMaterial.interpolate = false; - attributeMaterial.weighted = true; - attributeMaterial.minSize = 2; - - rtDepth = new THREE.WebGLRenderTarget( 1024, 1024, { - minFilter: THREE.NearestFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - type: THREE.FloatType - } ); - - rtNormalize = new THREE.WebGLRenderTarget( 1024, 1024, { - minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - type: THREE.FloatType - } ); - - var uniformsNormalize = { - depthMap: { type: "t", value: rtDepth }, - texture: { type: "t", value: rtNormalize } - }; - - normalizationMaterial = new THREE.ShaderMaterial({ - uniforms: uniformsNormalize, - vertexShader: Potree.Shaders["normalize.vs"], - fragmentShader: Potree.Shaders["normalize.fs"] - }); - } - - var resize = function(width, height){ - if(rtDepth.width == width && rtDepth.height == height){ - return; - } - - rtDepth.dispose(); - rtNormalize.dispose(); - - scope.camera.aspect = width / height; - scope.camera.updateProjectionMatrix(); - - scope.renderer.setSize(width, height); - rtDepth.setSize(width, height); - rtNormalize.setSize(width, height); - }; - - // render with splats - this.render = function(renderer){ - - var width = renderArea.clientWidth; - var height = renderArea.clientHeight; - - initHQSPlats(); - - resize(width, height); - - - scope.renderer.clear(); - if(showSkybox){ - skybox.camera.rotation.copy(scope.camera.rotation); - scope.renderer.render(skybox.scene, skybox.camera); - }else{ - scope.renderer.render(scope.sceneBG, scope.cameraBG); - } - scope.renderer.render(scope.scene, scope.camera); - - if(scope.pointcloud){ - - depthMaterial.uniforms.octreeSize.value = scope.pointcloud.pcoGeometry.boundingBox.size().x; - attributeMaterial.uniforms.octreeSize.value = scope.pointcloud.pcoGeometry.boundingBox.size().x; - - scope.pointcloud.visiblePointsTarget = scope.pointCountTarget * 1000 * 1000; - var originalMaterial = scope.pointcloud.material; - - {// DEPTH PASS - depthMaterial.size = scope.pointSize; - depthMaterial.pointSizeType = scope.pointSizeType; - depthMaterial.screenWidth = width; - depthMaterial.screenHeight = height; - depthMaterial.uniforms.visibleNodes.value = scope.pointcloud.material.visibleNodesTexture; - depthMaterial.uniforms.octreeSize.value = scope.pointcloud.pcoGeometry.boundingBox.size().x; - depthMaterial.fov = scope.camera.fov * (Math.PI / 180); - depthMaterial.spacing = scope.pointcloud.pcoGeometry.spacing; - depthMaterial.near = scope.camera.near; - depthMaterial.far = scope.camera.far; - depthMaterial.heightMin = heightMin; - depthMaterial.heightMax = heightMax; - depthMaterial.uniforms.visibleNodes.value = scope.pointcloud.material.visibleNodesTexture; - depthMaterial.uniforms.octreeSize.value = scope.pointcloud.pcoGeometry.boundingBox.size().x; - depthMaterial.bbSize = scope.pointcloud.material.bbSize; - depthMaterial.treeType = scope.pointcloud.material.treeType; - depthMaterial.uniforms.classificationLUT.value = scope.pointcloud.material.uniforms.classificationLUT.value; - - scope.scenePointCloud.overrideMaterial = depthMaterial; - scope.renderer.clearTarget( rtDepth, true, true, true ); - scope.renderer.render(scope.scenePointCloud, scope.camera, rtDepth); - scope.scenePointCloud.overrideMaterial = null; - } - - {// ATTRIBUTE PASS - attributeMaterial.size = scope.pointSize; - attributeMaterial.pointSizeType = scope.pointSizeType; - attributeMaterial.screenWidth = width; - attributeMaterial.screenHeight = height; - attributeMaterial.pointColorType = scope.pointColorType; - attributeMaterial.depthMap = rtDepth; - attributeMaterial.uniforms.visibleNodes.value = scope.pointcloud.material.visibleNodesTexture; - attributeMaterial.uniforms.octreeSize.value = scope.pointcloud.pcoGeometry.boundingBox.size().x; - attributeMaterial.fov = scope.camera.fov * (Math.PI / 180); - attributeMaterial.spacing = scope.pointcloud.pcoGeometry.spacing; - attributeMaterial.near = scope.camera.near; - attributeMaterial.far = scope.camera.far; - attributeMaterial.heightMin = heightMin; - attributeMaterial.heightMax = heightMax; - attributeMaterial.intensityMin = scope.pointcloud.material.intensityMin; - attributeMaterial.intensityMax = scope.pointcloud.material.intensityMax; - attributeMaterial.setClipBoxes(scope.pointcloud.material.clipBoxes); - attributeMaterial.clipMode = scope.pointcloud.material.clipMode; - attributeMaterial.bbSize = scope.pointcloud.material.bbSize; - attributeMaterial.treeType = scope.pointcloud.material.treeType; - attributeMaterial.uniforms.classificationLUT.value = scope.pointcloud.material.uniforms.classificationLUT.value; - - scope.scenePointCloud.overrideMaterial = attributeMaterial; - scope.renderer.clearTarget( rtNormalize, true, true, true ); - scope.renderer.render(scope.scenePointCloud, scope.camera, rtNormalize); - scope.scenePointCloud.overrideMaterial = null; - } - - {// NORMALIZATION PASS - normalizationMaterial.uniforms.depthMap.value = rtDepth; - normalizationMaterial.uniforms.texture.value = rtNormalize; - Potree.utils.screenPass.render(scope.renderer, normalizationMaterial); - } - - scope.pointcloud.material = originalMaterial; - - scope.volumeTool.render(); - scope.renderer.clearDepth(); - scope.profileTool.render(); - scope.measuringTool.render(); - scope.transformationTool.render(); - } - - - } - }; - - - - var edlRenderer = null; - var EDLRenderer = function(){ - - var edlMaterial = null; - var attributeMaterial = null; - - //var depthTexture = null; - - var rtColor = null; - var gl = scope.renderer.context; - - var initEDL = function(){ - if(edlMaterial != null){ - return; - } - - //var depthTextureExt = gl.getExtension("WEBGL_depth_texture"); - - edlMaterial = new Potree.EyeDomeLightingMaterial(); - attributeMaterial = new Potree.PointCloudMaterial(); - - attributeMaterial.pointShape = Potree.PointShape.CIRCLE; - attributeMaterial.interpolate = false; - attributeMaterial.weighted = false; - attributeMaterial.minSize = 2; - attributeMaterial.useLogarithmicDepthBuffer = false; - attributeMaterial.useEDL = true; - - rtColor = new THREE.WebGLRenderTarget( 1024, 1024, { - minFilter: THREE.LinearFilter, - magFilter: THREE.NearestFilter, - format: THREE.RGBAFormat, - type: THREE.FloatType, - //type: THREE.UnsignedByteType, - //depthBuffer: false, - //stencilBuffer: false - } ); - - }; - - var resize = function(){ - var width = renderArea.clientWidth; - var height = renderArea.clientHeight; - var aspect = width / height; - - var needsResize = (rtColor.width != width || rtColor.height != height); - - // disposal will be unnecessary once this fix made it into three.js master: - // https://github.com/mrdoob/three.js/pull/6355 - if(needsResize){ - rtColor.dispose(); - } - - scope.camera.aspect = aspect; - scope.camera.updateProjectionMatrix(); - - scope.renderer.setSize(width, height); - rtColor.setSize(width, height); - } - - this.render = function(){ - - initEDL(); - - resize(); - - scope.renderer.clear(); - if(showSkybox){ - scope.camera.rotation.copy(scope.camera.rotation); - scope.renderer.render(skybox.scene, skybox.camera); - }else{ - scope.renderer.render(scope.sceneBG, scope.cameraBG); - } - scope.renderer.render(scope.scene, scope.camera); - - if(scope.pointcloud){ - var width = renderArea.clientWidth; - var height = renderArea.clientHeight; - - var octreeSize = scope.pointcloud.pcoGeometry.boundingBox.size().x; - - scope.pointcloud.visiblePointsTarget = scope.pointCountTarget * 1000 * 1000; - var originalMaterial = scope.pointcloud.material; - - {// COLOR & DEPTH PASS - attributeMaterial = scope.pointcloud.material; - attributeMaterial.pointShape = Potree.PointShape.CIRCLE; - attributeMaterial.interpolate = false; - attributeMaterial.weighted = false; - attributeMaterial.minSize = 2; - attributeMaterial.useLogarithmicDepthBuffer = false; - attributeMaterial.useEDL = true; - - attributeMaterial.size = scope.pointSize; - attributeMaterial.pointSizeType = scope.pointSizeType; - attributeMaterial.screenWidth = width; - attributeMaterial.screenHeight = height; - attributeMaterial.pointColorType = scope.pointColorType; - attributeMaterial.uniforms.visibleNodes.value = scope.pointcloud.material.visibleNodesTexture; - attributeMaterial.uniforms.octreeSize.value = octreeSize; - attributeMaterial.fov = scope.camera.fov * (Math.PI / 180); - attributeMaterial.spacing = scope.pointcloud.pcoGeometry.spacing; - attributeMaterial.near = scope.camera.near; - attributeMaterial.far = scope.camera.far; - attributeMaterial.heightMin = heightMin; - attributeMaterial.heightMax = heightMax; - attributeMaterial.intensityMin = scope.pointcloud.material.intensityMin; - attributeMaterial.intensityMax = scope.pointcloud.material.intensityMax; - attributeMaterial.setClipBoxes(scope.pointcloud.material.clipBoxes); - attributeMaterial.clipMode = scope.pointcloud.material.clipMode; - attributeMaterial.bbSize = scope.pointcloud.material.bbSize; - attributeMaterial.treeType = scope.pointcloud.material.treeType; - attributeMaterial.uniforms.classificationLUT.value = scope.pointcloud.material.uniforms.classificationLUT.value; - - scope.pointcloud.material = attributeMaterial; - for(var i = 0; i < scope.pointcloud.visibleNodes.length; i++){ - var node = scope.pointcloud.visibleNodes[i]; - node.sceneNode.material = attributeMaterial; - } - - scope.renderer.clearTarget( rtColor, true, true, true ); - scope.renderer.render(scope.scenePointCloud, scope.camera, rtColor); - - - scope.pointcloud.material = originalMaterial; - for(var i = 0; i < scope.pointcloud.visibleNodes.length; i++){ - var node = scope.pointcloud.visibleNodes[i]; - node.sceneNode.material = originalMaterial; - } - } - - // bit of a hack here. The EDL pass will mess up the text of the volume tool - // so volume tool is rendered again afterwards - scope.volumeTool.render(rtColor); - - { // EDL OCCLUSION PASS - edlMaterial.uniforms.screenWidth.value = width; - edlMaterial.uniforms.screenHeight.value = height; - edlMaterial.uniforms.near.value = scope.camera.near; - edlMaterial.uniforms.far.value = scope.camera.far; - edlMaterial.uniforms.colorMap.value = rtColor; - edlMaterial.uniforms.expScale.value = scope.camera.far; - edlMaterial.uniforms.edlScale.value = scope.edlScale; - edlMaterial.uniforms.radius.value = scope.edlRadius; - edlMaterial.uniforms.opacity.value = scope.opacity; - edlMaterial.depthTest = true; - edlMaterial.depthWrite = true; - edlMaterial.transparent = true; - - Potree.utils.screenPass.render(scope.renderer, edlMaterial); - } - - scope.renderer.render(scope.scene, scope.camera); - - scope.profileTool.render(); - scope.volumeTool.render(); - scope.renderer.clearDepth(); - scope.measuringTool.render(); - scope.transformationTool.render(); - } - - - } - }; - - //var toggleMessage = 0; - - function loop(timestamp) { - requestAnimationFrame(loop); - - //var start = new Date().getTime(); - scope.update(clock.getDelta(), timestamp); - //var end = new Date().getTime(); - //var duration = end - start; - //toggleMessage++; - //if(toggleMessage > 30){ - // document.getElementById("lblMessage").innerHTML = "update: " + duration + "ms"; - // toggleMessage = 0; - //} - - if(scope.useEDL){ - if(!edlRenderer){ - edlRenderer = new EDLRenderer(); - } - edlRenderer.render(scope.renderer); - }else if(scope.quality === "Splats"){ - if(!highQualityRenderer){ - highQualityRenderer = new HighQualityRenderer(); - } - highQualityRenderer.render(scope.renderer); - }else{ - potreeRenderer.render(); - } - }; - - scope.initThree(); - - requestAnimationFrame(loop); -}; - -Potree.Viewer.prototype = Object.create( THREE.EventDispatcher.prototype ); \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/examples/lasmap_template.html b/PotreeConverter/resources/page_template/examples/lasmap_template.html new file mode 100644 index 00000000..52265e66 --- /dev/null +++ b/PotreeConverter/resources/page_template/examples/lasmap_template.html @@ -0,0 +1,405 @@ + + + + + + + + lasmap + + + + + + + + + + + +
+
+
+ + + + + diff --git a/PotreeConverter/resources/page_template/examples/viewer_template.html b/PotreeConverter/resources/page_template/examples/viewer_template.html index 5e9b6508..67cb8e76 100644 --- a/PotreeConverter/resources/page_template/examples/viewer_template.html +++ b/PotreeConverter/resources/page_template/examples/viewer_template.html @@ -1,60 +1,120 @@ - - - - Potree Viewer - - - - + + + + + + + + Potree Viewer + + + + + + + + + - + + + - - - + + + - -
+ - -
- potree.org +
+ +
+ + + + + + + +
-
+ +
+
- - + - - - - - - - - - - - - - diff --git a/PotreeConverter/resources/page_template/libs/d3/LICENSE b/PotreeConverter/resources/page_template/libs/d3/LICENSE new file mode 100644 index 00000000..f27c7e67 --- /dev/null +++ b/PotreeConverter/resources/page_template/libs/d3/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2010-2015, Michael Bostock +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* The name Michael Bostock may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PotreeConverter/resources/page_template/libs/d3/d3.js b/PotreeConverter/resources/page_template/libs/d3/d3.js new file mode 100644 index 00000000..8868e425 --- /dev/null +++ b/PotreeConverter/resources/page_template/libs/d3/d3.js @@ -0,0 +1,9504 @@ +!function() { + var d3 = { + version: "3.5.5" + }; + var d3_arraySlice = [].slice, d3_array = function(list) { + return d3_arraySlice.call(list); + }; + var d3_document = this.document; + function d3_documentElement(node) { + return node && (node.ownerDocument || node.document || node).documentElement; + } + function d3_window(node) { + return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); + } + if (d3_document) { + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = function(list) { + var i = list.length, array = new Array(i); + while (i--) array[i] = list[i]; + return array; + }; + } + } + if (!Date.now) Date.now = function() { + return +new Date(); + }; + if (d3_document) { + try { + d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_element_prototype.setAttribute = function(name, value) { + d3_element_setAttribute.call(this, name, value + ""); + }; + d3_element_prototype.setAttributeNS = function(space, local, value) { + d3_element_setAttributeNS.call(this, space, local, value + ""); + }; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + } + d3.ascending = d3_ascending; + function d3_ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + function d3_number(x) { + return x === null ? NaN : +x; + } + function d3_numeric(x) { + return !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = +array[i])) s += a; + } else { + while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.mean = function(array, f) { + var s = 0, n = array.length, a, i = -1, j = n; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; + } + if (j) return s / j; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.median = function(array, f) { + var numbers = [], n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); + } + if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); + }; + d3.variance = function(array, f) { + var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) { + if (d3_numeric(a = d3_number(array[i]))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } else { + while (++i < n) { + if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } + if (j > 1) return s / (j - 1); + }; + d3.deviation = function() { + var v = d3.variance.apply(this, arguments); + return v ? Math.sqrt(v) : v; + }; + function d3_bisector(compare) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + } + var d3_bisect = d3_bisector(d3_ascending); + d3.bisectLeft = d3_bisect.left; + d3.bisect = d3.bisectRight = d3_bisect.right; + d3.bisector = function(f) { + return d3_bisector(f.length === 1 ? function(d, x) { + return d3_ascending(f(d), x); + } : f); + }; + d3.shuffle = function(array, i0, i1) { + if ((m = arguments.length) < 3) { + i1 = array.length; + if (m < 2) i0 = 0; + } + var m = i1 - i0, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; + } + return array; + }; + d3.permute = function(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; + return pairs; + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + function d3_zipLength(d) { + return d.length; + } + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.merge = function(arrays) { + var n = arrays.length, m, i = -1, j = 0, merged, array; + while (++i < n) j += arrays[i].length; + merged = new Array(j); + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + return merged; + }; + var abs = Math.abs; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_class(ctor, properties) { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } + d3.map = function(object, f) { + var map = new d3_Map(); + if (object instanceof d3_Map) { + object.forEach(function(key, value) { + map.set(key, value); + }); + } else if (Array.isArray(object)) { + var i = -1, n = object.length, o; + if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); + } else { + for (var key in object) map.set(key, object[key]); + } + return map; + }; + function d3_Map() { + this._ = Object.create(null); + } + var d3_map_proto = "__proto__", d3_map_zero = "\x00"; + d3_class(d3_Map, { + has: d3_map_has, + get: function(key) { + return this._[d3_map_escape(key)]; + }, + set: function(key, value) { + return this._[d3_map_escape(key)] = value; + }, + remove: d3_map_remove, + keys: d3_map_keys, + values: function() { + var values = []; + for (var key in this._) values.push(this._[key]); + return values; + }, + entries: function() { + var entries = []; + for (var key in this._) entries.push({ + key: d3_map_unescape(key), + value: this._[key] + }); + return entries; + }, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); + } + }); + function d3_map_escape(key) { + return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; + } + function d3_map_unescape(key) { + return (key += "")[0] === d3_map_zero ? key.slice(1) : key; + } + function d3_map_has(key) { + return d3_map_escape(key) in this._; + } + function d3_map_remove(key) { + return (key = d3_map_escape(key)) in this._ && delete this._[key]; + } + function d3_map_keys() { + var keys = []; + for (var key in this._) keys.push(d3_map_unescape(key)); + return keys; + } + function d3_map_size() { + var size = 0; + for (var key in this._) ++size; + return size; + } + function d3_map_empty() { + for (var key in this._) return false; + return true; + } + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(mapType, array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + if (mapType) { + object = mapType(); + setter = function(keyValue, values) { + object.set(keyValue, map(mapType, values, depth)); + }; + } else { + object = {}; + setter = function(keyValue, values) { + object[keyValue] = map(mapType, values, depth); + }; + } + valuesByKey.forEach(setter); + return object; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var array = [], sortKey = sortKeys[depth++]; + map.forEach(function(key, keyMap) { + array.push({ + key: key, + values: entries(keyMap, depth) + }); + }); + return sortKey ? array.sort(function(a, b) { + return sortKey(a.key, b.key); + }) : array; + } + nest.map = function(array, mapType) { + return map(mapType, array, 0); + }; + nest.entries = function(array) { + return entries(map(d3.map, array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.set = function(array) { + var set = new d3_Set(); + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); + return set; + }; + function d3_Set() { + this._ = Object.create(null); + } + d3_class(d3_Set, { + has: d3_map_has, + add: function(key) { + this._[d3_map_escape(key += "")] = true; + return key; + }, + remove: d3_map_remove, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key)); + } + }); + d3.behavior = {}; + function d3_identity(d) { + return d; + } + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + function d3_vendorSymbol(object, name) { + if (name in object) return name; + name = name.charAt(0).toUpperCase() + name.slice(1); + for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { + var prefixName = d3_vendorPrefixes[i] + name; + if (prefixName in object) return prefixName; + } + } + var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; + function d3_noop() {} + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i >= 0) { + name = type.slice(i + 1); + type = type.slice(0, i); + } + if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + if (arguments.length === 2) { + if (listener == null) for (type in this) { + if (this.hasOwnProperty(type)) this[type].on(name, null); + } + return this; + } + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.event = null; + function d3_eventPreventDefault() { + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + var d3_subclass = {}.__proto__ ? function(object, prototype) { + object.__proto__ = prototype; + } : function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; + }; + function d3_selection(groups) { + d3_subclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectMatches = function(n, s) { + var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; + d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + return d3_selectMatches(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = Sizzle; + d3_selectMatches = Sizzle.matchesSelector; + } + d3.selection = function() { + return d3.select(d3_document.documentElement); + }; + var d3_selectionPrototype = d3.selection.prototype = []; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return typeof selector === "function" ? selector : function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return typeof selector === "function" ? selector : function() { + return d3_selectAll(selector, this); + }; + } + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0) { + prefix = name.slice(0, i); + name = name.slice(i + 1); + } + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.getAttribute("class"); + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classes(name) { + return (name + "").trim().split(/^|\s+/); + } + function d3_selection_classed(name, value) { + name = d3_selection_classes(name).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.getAttribute("class") || ""; + if (value) { + re.lastIndex = 0; + if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); + } else { + node.setAttribute("class", d3_collapse(c.replace(re, " "))); + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) { + var node = this.node(); + return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); + } + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); + }; + function d3_selection_creator(name) { + function create() { + var document = this.ownerDocument, namespace = this.namespaceURI; + return namespace ? document.createElementNS(namespace, name) : document.createElement(name); + } + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); + } + return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; + } + d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); + }; + d3_selectionPrototype.remove = function() { + return this.each(d3_selectionRemove); + }; + function d3_selectionRemove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; + } + for (i = -1; ++i < m; ) { + if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } else if (node !== true) { + updateNodes[i] = node; + node.__data__ = nodeData; + } + nodeByKeyValue.set(keyValue, true); + } + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.get(keyValues[i]) !== true) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3_ascending; + return function(a, b) { + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; + }; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.size = function() { + var n = 0; + d3_selection_each(this, function() { + ++n; + }); + return n; + }; + function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.call = d3_selectionPrototype.call; + d3_selection_enterPrototype.size = d3_selectionPrototype.size; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); + }; + function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, n = group.length, node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n) ; + return node; + }; + } + d3.select = function(node) { + var group; + if (typeof node === "string") { + group = [ d3_select(node, d3_document) ]; + group.parentNode = d3_document.documentElement; + } else { + group = [ node ]; + group.parentNode = d3_documentElement(node); + } + return d3_selection([ group ]); + }; + d3.selectAll = function(nodes) { + var group; + if (typeof nodes === "string") { + group = d3_array(d3_selectAll(nodes, d3_document)); + group.parentNode = d3_document.documentElement; + } else { + group = nodes; + group.parentNode = null; + } + return d3_selection([ group ]); + }; + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; + if (i > 0) type = type.slice(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); + delete this[name]; + } + } + } + return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; + } + var d3_selection_onFilters = d3.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + if (d3_document) { + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); + } + function d3_selection_onListener(listener, argumentz) { + return function(e) { + var o = d3.event; + d3.event = e; + argumentz[0] = this.__data__; + try { + listener.apply(this, argumentz); + } finally { + d3.event = o; + } + }; + } + function d3_selection_onFilter(listener, argumentz) { + var l = d3_selection_onListener(listener, argumentz); + return function(e) { + var target = this, related = e.relatedTarget; + if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { + l.call(target, e); + } + }; + } + var d3_event_dragSelect, d3_event_dragId = 0; + function d3_event_dragSuppress(node) { + var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); + if (d3_event_dragSelect == null) { + d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); + } + if (d3_event_dragSelect) { + var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { + w.on(name, null); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { + var off = function() { + w.on(click, null); + }; + w.on(click, function() { + d3_eventPreventDefault(); + off(); + }, true); + setTimeout(off, 0); + } + }; + } + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + if (e.changedTouches) e = e.changedTouches[0]; + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0) { + var window = d3_window(container); + if (window.scrollX || window.scrollY) { + svg = d3.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + } + if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, + point.y = e.clientY; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } + }; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); + } + function dragstart(id, position, subject, move, end) { + return function() { + var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); + if (origin) { + dragOffset = origin.apply(that, arguments); + dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; + } else { + dragOffset = [ 0, 0 ]; + } + dispatch({ + type: "dragstart" + }); + function moved() { + var position1 = position(parent, dragId), dx, dy; + if (!position1) return; + dx = position1[0] - position0[0]; + dy = position1[1] - position0[1]; + dragged |= dx | dy; + position0 = position1; + dispatch({ + type: "drag", + x: position1[0] + dragOffset[0], + y: position1[1] + dragOffset[1], + dx: dx, + dy: dy + }); + } + function ended() { + if (!position(parent, dragId)) return; + dragSubject.on(move + dragName, null).on(end + dragName, null); + dragRestore(dragged && d3.event.target === target); + dispatch({ + type: "dragend" + }); + } + }; + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + function d3_behavior_dragTouchId() { + return d3.event.changedTouches[0].identifier; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; + function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } + function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + function d3_acos(x) { + return x > 1 ? 0 : x < -1 ? π : Math.acos(x); + } + function d3_asin(x) { + return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); + } + function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; + } + var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; + d3.interpolateZoom = function(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; + var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; + function interpolate(t) { + var s = t * S; + if (dr) { + var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; + } + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; + } + interpolate.duration = S * 1e3; + return interpolate; + }; + d3.behavior.zoom = function() { + var view = { + x: 0, + y: 0, + k: 1 + }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; + if (!d3_behavior_zoomWheel) { + d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + } + function zoom(g) { + g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); + } + zoom.event = function(g) { + g.each(function() { + var dispatch = event.of(this, arguments), view1 = view; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.zoom", function() { + view = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }; + zoomstarted(dispatch); + }).tween("zoom:zoom", function() { + var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = { + x: cx - l[0] * k, + y: cy - l[1] * k, + k: k + }; + zoomed(dispatch); + }; + }).each("interrupt.zoom", function() { + zoomended(dispatch); + }).each("end.zoom", function() { + zoomended(dispatch); + }); + } else { + this.__chart__ = view; + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); + }; + zoom.translate = function(_) { + if (!arguments.length) return [ view.x, view.y ]; + view = { + x: +_[0], + y: +_[1], + k: view.k + }; + rescale(); + return zoom; + }; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = { + x: view.x, + y: view.y, + k: +_ + }; + rescale(); + return zoom; + }; + zoom.scaleExtent = function(_) { + if (!arguments.length) return scaleExtent; + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; + return zoom; + }; + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + function location(p) { + return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; + } + function point(l) { + return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; + } + function scaleTo(s) { + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + view.x += p[0] - l[0]; + view.y += p[1] - l[1]; + } + function zoomTo(that, p, l, k) { + that.__chart__ = { + x: view.x, + y: view.y, + k: view.k + }; + scaleTo(Math.pow(2, k)); + translateTo(center0 = p, l); + that = d3.select(that); + if (duration > 0) that = that.transition().duration(duration); + that.call(zoom.event); + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - view.x) / view.k; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - view.y) / view.k; + }).map(y0.invert)); + } + function zoomstarted(dispatch) { + if (!zooming++) dispatch({ + type: "zoomstart" + }); + } + function zoomed(dispatch) { + rescale(); + dispatch({ + type: "zoom", + scale: view.k, + translate: [ view.x, view.y ] + }); + } + function zoomended(dispatch) { + if (!--zooming) dispatch({ + type: "zoomend" + }); + center0 = null; + } + function mousedowned() { + var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); + } + function ended() { + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged && d3.event.target === target); + zoomended(dispatch); + } + } + function touchstarted() { + var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); + started(); + zoomstarted(dispatch); + subject.on(mousedown, null).on(touchstart, started); + function relocate() { + var touches = d3.touches(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + function started() { + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + locations0[changed[i].identifier] = null; + } + var touches = relocate(), now = Date.now(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); + d3_eventPreventDefault(); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; + } + } + function moved() { + var touches = d3.touches(that), p0, l0, p1, l1; + d3_selection_interrupt.call(that); + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { + p1 = touches[i]; + if (l1 = locations0[p1.identifier]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(scale1 * scale0); + } + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + function ended() { + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + for (var identifier in locations0) { + return void relocate(); + } + } + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); + } + } + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); else translate0 = location(center0 = center || d3.mouse(this)), + d3_selection_interrupt.call(this), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { + mousewheelTimer = null; + zoomended(dispatch); + }, 50); + d3_eventPreventDefault(); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(center0, translate0); + zoomed(dispatch); + } + function dblclicked() { + var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; + zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; + d3.color = d3_color; + function d3_color() {} + d3_color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.hsl = d3_hsl; + function d3_hsl(h, s, l) { + return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); + } + var d3_hslPrototype = d3_hsl.prototype = new d3_color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = d3_hcl; + function d3_hcl(h, c, l) { + return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); + } + var d3_hclPrototype = d3_hcl.prototype = new d3_color(); + d3_hclPrototype.brighter = function(k) { + return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = d3_lab; + function d3_lab(l, a, b) { + return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_lab.prototype = new d3_color(); + d3_labPrototype.brighter = function(k) { + return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + d3.rgb = d3_rgb; + function d3_rgb(r, g, b) { + return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); + } + function d3_rgbNumber(value) { + return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); + } + function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; + } + var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return new d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_rgb(k * this.r, k * this.g, k * this.b); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, color; + m1 = /([a-z]+)\((.*)\)/i.exec(format); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (color = d3_rgb_names.get(format.toLowerCase())) { + return rgb(color.r, color.g, color.b); + } + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { + if (format.length === 4) { + r = (color & 3840) >> 4; + r = r >> 4 | r; + g = color & 240; + g = g >> 4 | g; + b = color & 15; + b = b << 4 | b; + } else if (format.length === 7) { + r = (color & 16711680) >> 16; + g = (color & 65280) >> 8; + b = color & 255; + } + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + h = NaN; + s = l > 0 && l < 1 ? 0 : h; + } + return new d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgbNumber(value)); + }); + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.xhr = d3_xhrType(d3_identity); + function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; + } + function d3_xhr(url, mimeType, response, callback) { + var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; + if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" ? request.response : request.responseText; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.slice(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.slice(j, I - k); + } + return text.slice(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && (a = f(a, n++)) == null) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + f: false, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + }; + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(); + d3_timer_active = d3_timer_queueHead; + while (d3_timer_active) { + if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); + d3_timer_active = d3_timer_active.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.f) { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } else { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { + var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + exponent = false; + + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before, after; + if (i < 0) { + var j = exponent ? value.lastIndexOf("e") : -1; + if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); + } else { + before = value.substring(0, i); + after = locale_decimal + value.substring(i + 1); + } + if (!zfill && comma) before = formatGroup(before, Infinity); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.slice(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.slice(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: [ "AM", "PM" ], + days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], + shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; + } + function d3_geo_clipCircle(radius) { + var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, c0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + } else { + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { + listener.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b, two) { + var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + q = d3_geo_spherical(q); + if (!two) return q; + var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; + if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; + var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; + if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; + if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { + var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); + d3_geo_cartesianAdd(q1, A); + return [ q, d3_geo_spherical(q1) ]; + } + } + function code(λ, φ) { + var r = smallRadius ? radius : π - radius, code = 0; + if (λ < -r) code |= 1; else if (λ > r) code |= 2; + if (φ < -r) code |= 4; else if (φ > r) code |= 8; + return code; + } + } + function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + if (t0 > 0) line.a = { + x: ax + t0 * dx, + y: ay + t0 * dy + }; + if (t1 < 1) line.b = { + x: ax + t1 * dx, + y: ay + t1 * dy + }; + return line; + }; + } + var d3_geo_clipExtentMAX = 1e9; + d3.geo.clipExtent = function() { + var x0, y0, x1, y1, stream, clip, clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; + return stream; + }, + extent: function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); + }; + function d3_geo_clipExtent(x0, y0, x1, y1) { + return function(listener) { + var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + listener = bufferListener; + segments = []; + polygon = []; + clean = true; + }, + polygonEnd: function() { + listener = listener_; + segments = d3.merge(segments); + var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; + if (inside || visible) { + listener.polygonStart(); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } + listener.polygonEnd(); + } + segments = polygon = ring = null; + } + }; + function insidePolygon(p) { + var wn = 0, n = polygon.length, y = p[1]; + for (var i = 0; i < n; ++i) { + for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { + b = v[j]; + if (a[1] <= y) { + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; + } else { + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; + } + a = b; + } + } + return wn !== 0; + } + function interpolate(from, to, direction, listener) { + var a = 0, a1 = 0; + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { + do { + listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + listener.point(to[0], to[1]); + } + } + function pointVisible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + function point(x, y) { + if (pointVisible(x, y)) listener.point(x, y); + } + var x__, y__, v__, x_, y_, v_, first, clean; + function lineStart() { + clip.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferListener.rejoin(); + segments.push(bufferListener.buffer()); + } + clip.point = point; + if (v_) listener.lineEnd(); + } + function linePoint(x, y) { + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); + if (polygon) ring.push([ x, y ]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + listener.lineStart(); + listener.point(x, y); + } + } else { + if (v && v_) listener.point(x, y); else { + var l = { + a: { + x: x_, + y: y_ + }, + b: { + x: x, + y: y + } + }; + if (clipLine(l)) { + if (!v_) { + listener.lineStart(); + listener.point(l.a.x, l.a.y); + } + listener.point(l.b.x, l.b.y); + if (!v) listener.lineEnd(); + clean = false; + } else if (v) { + listener.lineStart(); + listener.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + return clip; + }; + function corner(p, direction) { + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; + } + function compare(a, b) { + return comparePoints(a.x, b.x); + } + function comparePoints(a, b) { + var ca = corner(a, 1), cb = corner(b, 1); + return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; + } + } + function d3_geo_conic(projectAt) { + var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); + }; + return p; + } + function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function forward(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return forward; + } + (d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); + }).raw = d3_geo_conicEqualArea; + d3.geo.albers = function() { + return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); + }; + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); + var point, pointStream = { + point: function(x, y) { + point = [ x, y ]; + } + }, lower48Point, alaskaPoint, hawaiiPoint; + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); + return point; + } + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; + return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); + }; + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .35); + hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; + alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + return albersUsa; + }; + return albersUsa.scale(1070); + }; + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; + var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x + pointRadius, y); + context.arc(x, y, pointRadius, 0, τ); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + function d3_geo_resample(project) { + var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = ringStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; + }; + path.bounds = function(object) { + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return reset(); + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(x, y) { + return project([ x * d3_degrees, y * d3_degrees ]); + }); + return function(stream) { + return d3_geo_projectionRadians(resample(stream)); + }; + } + d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; + }; + function d3_geo_transform(stream) { + this.stream = stream; + } + d3_geo_transform.prototype = { + point: function(x, y) { + this.stream.point(x, y); + }, + sphere: function() { + this.stream.sphere(); + }, + lineStart: function() { + this.stream.lineStart(); + }, + lineEnd: function() { + this.stream.lineEnd(); + }, + polygonStart: function() { + this.stream.polygonStart(); + }, + polygonEnd: function() { + this.stream.polygonEnd(); + } + }; + function d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; + return stream; + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); + }; + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + d3.geo.rotation = function(rotate) { + rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); + function forward(coordinates) { + coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + } + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + }; + return forward; + }; + function d3_geo_identityRotation(λ, φ) { + return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + } + d3_geo_identityRotation.invert = d3_geo_equirectangular; + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; + }; + return rotation; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radius, precision) { + var cr = Math.cos(radius), sr = Math.sin(radius); + return function(from, to, direction, listener) { + var step = direction * precision; + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * τ; + } else { + from = radius + direction * τ; + to = radius - .5 * step; + } + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = d3_acos(-a[1]); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + d3.geo.distance = function(a, b) { + var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; + return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); + }; + d3.geo.graticule = function() { + var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { + return abs(x % DX) > ε; + }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { + return abs(y % DY) > ε; + }).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return graticule.minorExtent(); + return graticule.majorExtent(_).minorExtent(_); + }; + graticule.majorExtent = function(_) { + if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + graticule.minorExtent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return graticule.minorStep(); + return graticule.majorStep(_).minorStep(_); + }; + graticule.majorStep = function(_) { + if (!arguments.length) return [ DX, DY ]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + graticule.minorStep = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, 90); + y = d3_geo_graticuleY(x0, x1, precision); + X = d3_geo_graticuleX(Y0, Y1, 90); + Y = d3_geo_graticuleY(X0, X1, precision); + return graticule; + }; + return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + function d3_source(d) { + return d.source; + } + function d3_target(d) { + return d.target; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_; + function greatArc() { + return { + type: "LineString", + coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] + }; + } + greatArc.distance = function() { + return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.precision = function() { + return arguments.length ? greatArc : 0; + }; + return greatArc; + }; + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); + var interpolate = d ? function(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; + } : function() { + return [ x0 * d3_degrees, y0 * d3_degrees ]; + }; + interpolate.distance = d; + return interpolate; + } + d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; + }; + var d3_geo_lengthSum; + var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } + } + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + function d3_geo_conicConformal(φ0, φ1) { + var cosφ0 = Math.cos(φ0), t = function(φ) { + return Math.tan(π / 4 + φ / 2); + }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; + if (!n) return d3_geo_mercator; + function forward(λ, φ) { + if (F > 0) { + if (φ < -halfπ + ε) φ = -halfπ + ε; + } else { + if (φ > halfπ - ε) φ = halfπ - ε; + } + var ρ = F / Math.pow(t(φ), n); + return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); + return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; + }; + return forward; + } + (d3.geo.conicConformal = function() { + return d3_geo_conic(d3_geo_conicConformal); + }).raw = d3_geo_conicConformal; + function d3_geo_conicEquidistant(φ0, φ1) { + var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; + if (abs(n) < ε) return d3_geo_equirectangular; + function forward(λ, φ) { + var ρ = G - φ; + return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = G - y; + return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; + }; + return forward; + } + (d3.geo.conicEquidistant = function() { + return d3_geo_conic(d3_geo_conicEquidistant); + }).raw = d3_geo_conicEquidistant; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + function d3_geo_mercator(λ, φ) { + return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; + }; + function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = π * scale(), t = translate(); + clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); + } + } else if (clipAuto) { + v = null; + } + return v; + }; + return m.clipExtent(null); + } + (d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_transverseMercator(λ, φ) { + return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; + } + d3_geo_transverseMercator.invert = function(x, y) { + return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; + }; + (d3.geo.transverseMercator = function() { + var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; + projection.center = function(_) { + return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); + }; + projection.rotate = function(_) { + return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), + [ _[0], _[1], _[2] - 90 ]); + }; + return rotate([ 0, 0, 90 ]); + }).raw = d3_geo_transverseMercator; + d3.geom = {}; + function d3_geom_pointX(d) { + return d[0]; + } + function d3_geom_pointY(d) { + return d[1]; + } + d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, y = d3_geom_pointY; + if (arguments.length) return hull(vertices); + function hull(data) { + if (data.length < 3) return []; + var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; + for (i = 0; i < n; i++) { + points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); + } + points.sort(d3_geom_hullOrder); + for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); + var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); + var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + return polygon; + } + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + return hull; + }; + function d3_geom_hullUpper(points) { + var n = points.length, hull = [ 0, 1 ], hs = 2; + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } + return hull.slice(0, hs); + } + function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } + d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; + }; + var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + d3_geom_polygonPrototype.area = function() { + var i = -1, n = this.length, a, b = this[n - 1], area = 0; + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + return area * .5; + }; + d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; + if (!arguments.length) k = -1 / (6 * this.area()); + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + d3_geom_polygonPrototype.clip = function(subject) { + var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + return subject; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); + } + var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; + function d3_geom_voronoiBeach() { + d3_geom_voronoiRedBlackNode(this); + this.edge = this.site = this.circle = null; + } + function d3_geom_voronoiCreateBeach(site) { + var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); + beach.site = site; + return beach; + } + function d3_geom_voronoiDetachBeach(beach) { + d3_geom_voronoiDetachCircle(beach); + d3_geom_voronoiBeaches.remove(beach); + d3_geom_voronoiBeachPool.push(beach); + d3_geom_voronoiRedBlackNode(beach); + } + function d3_geom_voronoiRemoveBeach(beach) { + var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { + x: x, + y: y + }, previous = beach.P, next = beach.N, disappearing = [ beach ]; + d3_geom_voronoiDetachBeach(beach); + var lArc = previous; + while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { + previous = lArc.P; + disappearing.unshift(lArc); + d3_geom_voronoiDetachBeach(lArc); + lArc = previous; + } + disappearing.unshift(lArc); + d3_geom_voronoiDetachCircle(lArc); + var rArc = next; + while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { + next = rArc.N; + disappearing.push(rArc); + d3_geom_voronoiDetachBeach(rArc); + rArc = next; + } + disappearing.push(rArc); + d3_geom_voronoiDetachCircle(rArc); + var nArcs = disappearing.length, iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiAddBeach(site) { + var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; + while (node) { + dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; + if (dxl > ε) node = node.L; else { + dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); + if (dxr > ε) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -ε) { + lArc = node.P; + rArc = node; + } else if (dxr > -ε) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + var newArc = d3_geom_voronoiCreateBeach(site); + d3_geom_voronoiBeaches.insert(lArc, newArc); + if (!lArc && !rArc) return; + if (lArc === rArc) { + d3_geom_voronoiDetachCircle(lArc); + rArc = d3_geom_voronoiCreateBeach(lArc.site); + d3_geom_voronoiBeaches.insert(newArc, rArc); + newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + return; + } + if (!rArc) { + newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + return; + } + d3_geom_voronoiDetachCircle(lArc); + d3_geom_voronoiDetachCircle(rArc); + var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { + x: (cy * hb - by * hc) / d + ax, + y: (bx * hc - cx * hb) / d + ay + }; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); + rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiLeftBreakPoint(arc, directrix) { + var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; + if (!pby2) return rfocx; + var lArc = arc.P; + if (!lArc) return -Infinity; + site = lArc.site; + var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; + if (!plby2) return lfocx; + var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + return (rfocx + lfocx) / 2; + } + function d3_geom_voronoiRightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); + var site = arc.site; + return site.y === directrix ? site.x : Infinity; + } + function d3_geom_voronoiCell(site) { + this.site = site; + this.edges = []; + } + d3_geom_voronoiCell.prototype.prepare = function() { + var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; + while (iHalfEdge--) { + edge = halfEdges[iHalfEdge].edge; + if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); + } + halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); + return halfEdges.length; + }; + function d3_geom_voronoiCloseCells(extent) { + var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; + while (iCell--) { + cell = cells[iCell]; + if (!cell || !cell.prepare()) continue; + halfEdges = cell.edges; + nHalfEdges = halfEdges.length; + iHalfEdge = 0; + while (iHalfEdge < nHalfEdges) { + end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; + start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; + if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { + halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { + x: x0, + y: abs(x2 - x0) < ε ? y2 : y1 + } : abs(y3 - y1) < ε && x1 - x3 > ε ? { + x: abs(y2 - y1) < ε ? x2 : x1, + y: y1 + } : abs(x3 - x1) < ε && y3 - y0 > ε ? { + x: x1, + y: abs(x2 - x1) < ε ? y2 : y0 + } : abs(y3 - y0) < ε && x3 - x0 > ε ? { + x: abs(y2 - y0) < ε ? x2 : x0, + y: y0 + } : null), cell.site, null)); + ++nHalfEdges; + } + } + } + } + function d3_geom_voronoiHalfEdgeOrder(a, b) { + return b.angle - a.angle; + } + function d3_geom_voronoiCircle() { + d3_geom_voronoiRedBlackNode(this); + this.x = this.y = this.arc = this.site = this.cy = null; + } + function d3_geom_voronoiAttachCircle(arc) { + var lArc = arc.P, rArc = arc.N; + if (!lArc || !rArc) return; + var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; + if (lSite === rSite) return; + var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; + var d = 2 * (ax * cy - ay * cx); + if (d >= -ε2) return; + var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; + var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = cy + Math.sqrt(x * x + y * y); + circle.cy = cy; + arc.circle = circle; + var before = null, node = d3_geom_voronoiCircles._; + while (node) { + if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { + if (node.L) node = node.L; else { + before = node.P; + break; + } + } else { + if (node.R) node = node.R; else { + before = node; + break; + } + } + } + d3_geom_voronoiCircles.insert(before, circle); + if (!before) d3_geom_voronoiFirstCircle = circle; + } + function d3_geom_voronoiDetachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; + d3_geom_voronoiCircles.remove(circle); + d3_geom_voronoiCirclePool.push(circle); + d3_geom_voronoiRedBlackNode(circle); + arc.circle = null; + } + } + function d3_geom_voronoiClipEdges(extent) { + var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; + while (i--) { + e = edges[i]; + if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { + e.a = e.b = null; + edges.splice(i, 1); + } + } + } + function d3_geom_voronoiConnectEdge(edge, extent) { + var vb = edge.b; + if (vb) return true; + var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!va) va = { + x: fx, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: fx, + y: y1 + }; + } else { + if (!va) va = { + x: fx, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: fx, + y: y0 + }; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!va) va = { + x: (y0 - fb) / fm, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: (y1 - fb) / fm, + y: y1 + }; + } else { + if (!va) va = { + x: (y1 - fb) / fm, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: (y0 - fb) / fm, + y: y0 + }; + } + } else { + if (ly < ry) { + if (!va) va = { + x: x0, + y: fm * x0 + fb + }; else if (va.x >= x1) return; + vb = { + x: x1, + y: fm * x1 + fb + }; + } else { + if (!va) va = { + x: x1, + y: fm * x1 + fb + }; else if (va.x < x0) return; + vb = { + x: x0, + y: fm * x0 + fb + }; + } + } + } + edge.a = va; + edge.b = vb; + return true; + } + function d3_geom_voronoiEdge(lSite, rSite) { + this.l = lSite; + this.r = rSite; + this.a = this.b = null; + } + function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, rSite); + d3_geom_voronoiEdges.push(edge); + if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); + if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); + d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); + d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); + return edge; + } + function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, null); + edge.a = va; + edge.b = vb; + d3_geom_voronoiEdges.push(edge); + return edge; + } + function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { + if (!edge.a && !edge.b) { + edge.a = vertex; + edge.l = lSite; + edge.r = rSite; + } else if (edge.l === rSite) { + edge.b = vertex; + } else { + edge.a = vertex; + } + } + function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { + var va = edge.a, vb = edge.b; + this.edge = edge; + this.site = lSite; + this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); + } + d3_geom_voronoiHalfEdge.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b; + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a; + } + }; + function d3_geom_voronoiRedBlackTree() { + this._ = null; + } + function d3_geom_voronoiRedBlackNode(node) { + node.U = node.C = node.L = node.R = node.P = node.N = null; + } + d3_geom_voronoiRedBlackTree.prototype = { + insert: function(after, node) { + var parent, grandpa, uncle; + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = d3_geom_voronoiRedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + d3_geom_voronoiRedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + d3_geom_voronoiRedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + var parent = node.U, sibling, left = node.L, right = node.R, next, red; + if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); + if (parent) { + if (parent.L === node) parent.L = next; else parent.R = next; + } else { + this._ = next; + } + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + if (node) node.U = parent; + if (red) return; + if (node && node.C) { + node.C = false; + return; + } + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateRight(this, parent); + sibling = parent.L; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + d3_geom_voronoiRedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + if (node) node.C = false; + } + }; + function d3_geom_voronoiRedBlackRotateLeft(tree, node) { + var p = node, q = node.R, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; + } + function d3_geom_voronoiRedBlackRotateRight(tree, node) { + var p = node, q = node.L, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; + } + function d3_geom_voronoiRedBlackFirst(node) { + while (node.L) node = node.L; + return node; + } + function d3_geom_voronoi(sites, bbox) { + var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; + d3_geom_voronoiEdges = []; + d3_geom_voronoiCells = new Array(sites.length); + d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); + d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); + while (true) { + circle = d3_geom_voronoiFirstCircle; + if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { + if (site.x !== x0 || site.y !== y0) { + d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); + d3_geom_voronoiAddBeach(site); + x0 = site.x, y0 = site.y; + } + site = sites.pop(); + } else if (circle) { + d3_geom_voronoiRemoveBeach(circle.arc); + } else { + break; + } + } + if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); + var diagram = { + cells: d3_geom_voronoiCells, + edges: d3_geom_voronoiEdges + }; + d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; + return diagram; + } + function d3_geom_voronoiVertexOrder(a, b) { + return b.y - a.y || b.x - a.x; + } + d3.geom.voronoi = function(points) { + var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; + if (points) return voronoi(points); + function voronoi(data) { + var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; + d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { + var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { + var s = e.start(); + return [ s.x, s.y ]; + }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; + polygon.point = data[i]; + }); + return polygons; + } + function sites(data) { + return data.map(function(d, i) { + return { + x: Math.round(fx(d, i) / ε) * ε, + y: Math.round(fy(d, i) / ε) * ε, + i: i + }; + }); + } + voronoi.links = function(data) { + return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { + return edge.l && edge.r; + }).map(function(edge) { + return { + source: data[edge.l.i], + target: data[edge.r.i] + }; + }); + }; + voronoi.triangles = function(data) { + var triangles = []; + d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { + var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; + while (++j < m) { + e0 = e1; + s0 = s1; + e1 = edges[j].edge; + s1 = e1.l === site ? e1.r : e1.l; + if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { + triangles.push([ data[i], data[s0.i], data[s1.i] ]); + } + } + }); + return triangles; + }; + voronoi.x = function(_) { + return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; + }; + voronoi.y = function(_) { + return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; + }; + voronoi.clipExtent = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; + clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; + return voronoi; + }; + voronoi.size = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; + return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); + }; + return voronoi; + }; + var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; + function d3_geom_voronoiTriangleArea(a, b, c) { + return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); + } + d3.geom.delaunay = function(vertices) { + return d3.geom.voronoi().triangles(vertices); + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var x = d3_geom_pointX, y = d3_geom_pointY, compat; + if (compat = arguments.length) { + x = d3_geom_quadtreeCompatX; + y = d3_geom_quadtreeCompatY; + if (compat === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } + return quadtree(points); + } + function quadtree(data) { + var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; + if (x1 != null) { + x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; + } else { + x2_ = y2_ = -(x1_ = y1_ = Infinity); + xs = [], ys = []; + n = data.length; + if (compat) for (i = 0; i < n; ++i) { + d = data[i]; + if (d.x < x1_) x1_ = d.x; + if (d.y < y1_) y1_ = d.y; + if (d.x > x2_) x2_ = d.x; + if (d.y > y2_) y2_ = d.y; + xs.push(d.x); + ys.push(d.y); + } else for (i = 0; i < n; ++i) { + var x_ = +fx(d = data[i], i), y_ = +fy(d, i); + if (x_ < x1_) x1_ = x_; + if (y_ < y1_) y1_ = y_; + if (x_ > x2_) x2_ = x_; + if (y_ > y2_) y2_ = y_; + xs.push(x_); + ys.push(y_); + } + } + var dx = x2_ - x1_, dy = y2_ - y1_; + if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; + function insert(n, d, x, y, x1, y1, x2, y2) { + if (isNaN(x) || isNaN(y)) return; + if (n.leaf) { + var nx = n.x, ny = n.y; + if (nx != null) { + if (abs(nx - x) + abs(ny - y) < .01) { + insertChild(n, d, x, y, x1, y1, x2, y2); + } else { + var nPoint = n.point; + n.x = n.y = n.point = null; + insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } else { + n.x = x, n.y = y, n.point = d; + } + } else { + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } + function insertChild(n, d, x, y, x1, y1, x2, y2) { + var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = xm; else x2 = xm; + if (below) y1 = ym; else y2 = ym; + insert(n, d, x, y, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(d) { + insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); + }; + root.find = function(point) { + return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); + }; + i = -1; + if (x1 == null) { + while (++i < n) { + insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); + } + --i; + } else data.forEach(root.add); + xs = ys = data = d = null; + return root; + } + quadtree.x = function(_) { + return arguments.length ? (x = _, quadtree) : x; + }; + quadtree.y = function(_) { + return arguments.length ? (y = _, quadtree) : y; + }; + quadtree.extent = function(_) { + if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], + y2 = +_[1][1]; + return quadtree; + }; + quadtree.size = function(_) { + if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; + return quadtree; + }; + return quadtree; + }; + function d3_geom_quadtreeCompatX(d) { + return d.x; + } + function d3_geom_quadtreeCompatY(d) { + return d.y; + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null, + x: null, + y: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { + var minDistance2 = Infinity, closestPoint; + (function find(node, x1, y1, x2, y2) { + if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; + if (point = node.point) { + var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; + if (distance2 < minDistance2) { + var distance = Math.sqrt(minDistance2 = distance2); + x0 = x - distance, y0 = y - distance; + x3 = x + distance, y3 = y + distance; + closestPoint = point; + } + } + var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; + for (var i = below << 1 | right, j = i + 4; i < j; ++i) { + if (node = children[i & 3]) switch (i & 3) { + case 0: + find(node, x1, y1, xm, ym); + break; + + case 1: + find(node, xm, y1, x2, ym); + break; + + case 2: + find(node, x1, ym, xm, y2); + break; + + case 3: + find(node, xm, ym, x2, y2); + break; + } + } + })(root, x0, y0, x3, y3); + return closestPoint; + } + d3.interpolateRgb = d3_interpolateRgb; + function d3_interpolateRgb(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + } + d3.interpolateObject = d3_interpolateObject; + function d3_interpolateObject(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + } + d3.interpolateNumber = d3_interpolateNumber; + function d3_interpolateNumber(a, b) { + a = +a, b = +b; + return function(t) { + return a * (1 - t) + b * t; + }; + } + d3.interpolateString = d3_interpolateString; + function d3_interpolateString(a, b) { + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; + a = a + "", b = b + ""; + while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { + if (s[i]) s[i] += bm; else s[++i] = bm; + } else { + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + bi = d3_interpolate_numberB.lastIndex; + } + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { + return b(t) + ""; + }) : function() { + return b; + } : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); + } + var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + d3.interpolate = d3_interpolate; + function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + } + d3.interpolators = [ function(a, b) { + var t = typeof b; + return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); + } ]; + d3.interpolateArray = d3_interpolateArray; + function d3_interpolateArray(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * halfπ); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.interpolateHcl = d3_interpolateHcl; + function d3_interpolateHcl(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + } + d3.interpolateHsl = d3_interpolateHsl; + function d3_interpolateHsl(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; + if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; + }; + } + d3.interpolateLab = d3_interpolateLab; + function d3_interpolateLab(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + } + d3.interpolateRound = d3_interpolateRound; + function d3_interpolateRound(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolateTransform = d3_interpolateTransform; + function d3_interpolateTransform(a, b) { + var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; + if (ta[0] != tb[0] || ta[1] != tb[1]) { + s.push("translate(", null, ",", null, ")"); + q.push({ + i: 1, + x: d3_interpolateNumber(ta[0], tb[0]) + }, { + i: 3, + x: d3_interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } else { + s.push(""); + } + if (ra != rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(s.pop() + "rotate(", null, ")") - 2, + x: d3_interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(s.pop() + "rotate(" + rb + ")"); + } + if (wa != wb) { + q.push({ + i: s.push(s.pop() + "skewX(", null, ")") - 2, + x: d3_interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(s.pop() + "skewX(" + wb + ")"); + } + if (ka[0] != kb[0] || ka[1] != kb[1]) { + n = s.push(s.pop() + "scale(", null, ",", null, ")"); + q.push({ + i: n - 4, + x: d3_interpolateNumber(ka[0], kb[0]) + }, { + i: n - 2, + x: d3_interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] != 1 || kb[1] != 1) { + s.push(s.pop() + "scale(" + kb + ")"); + } + n = q.length; + return function(t) { + var i = -1, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + } + function d3_uninterpolateNumber(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return (x - a) / b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return Math.max(0, Math.min(1, (x - a) / b)); + }; + } + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (τ - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; + if (dw * dw / theta2 < dn) { + if (dn < chargeDistance2) { + var k = quad.charge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + return true; + } + if (quad.point && dn && dn < chargeDistance2) { + var k = quad.pointCharge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.chargeDistance = function(x) { + if (!arguments.length) return Math.sqrt(chargeDistance2); + chargeDistance2 = x * x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return Math.sqrt(theta2); + theta2 = x * x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) alpha = x; else alpha = 0; + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + d3.timer(force.tick); + } + return force; + }; + force.start = function() { + var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + if (!neighbors) { + neighbors = new Array(n); + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + var candidates = neighbors[i], j = -1, l = candidates.length, x; + while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; + return Math.random() * size; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function hierarchy(root) { + var stack = [ root ], nodes = [], node; + root.depth = 0; + while ((node = stack.pop()) != null) { + nodes.push(node); + if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { + var n, childs, child; + while (--n >= 0) { + stack.push(child = childs[n]); + child.parent = node; + child.depth = node.depth + 1; + } + if (value) node.value = 0; + node.children = childs; + } else { + if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; + delete node.children; + } + } + d3_layout_hierarchyVisitAfter(root, function(node) { + var childs, parent; + if (sort && (childs = node.children)) childs.sort(sort); + if (value && (parent = node.parent)) parent.value += node.value; + }); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + if (value) { + d3_layout_hierarchyVisitBefore(root, function(node) { + if (node.children) node.value = 0; + }); + d3_layout_hierarchyVisitAfter(root, function(node) { + var parent; + if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; + if (parent = node.parent) parent.value += node.value; + }); + } + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyVisitBefore(node, callback) { + var nodes = [ node ]; + while ((node = nodes.pop()) != null) { + callback(node); + if ((children = node.children) && (n = children.length)) { + var n, children; + while (--n >= 0) nodes.push(children[n]); + } + } + } + function d3_layout_hierarchyVisitAfter(node, callback) { + var nodes = [ node ], nodes2 = []; + while ((node = nodes.pop()) != null) { + nodes2.push(node); + if ((children = node.children) && (n = children.length)) { + var i = -1, n, children; + while (++i < n) nodes.push(children[i]); + } + } + while ((node = nodes2.pop()) != null) { + callback(node); + } + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0; + function pie(data) { + var n = data.length, values = data.map(function(d, i) { + return +value.call(pie, d, i); + }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), k = (da - n * pa) / d3.sum(values), index = d3.range(n), arcs = [], v; + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + index.forEach(function(i) { + arcs[i] = { + data: data[i], + value: v = values[i], + startAngle: a, + endAngle: a += v * k + pa, + padAngle: p + }; + }); + return arcs; + } + pie.value = function(_) { + if (!arguments.length) return value; + value = _; + return pie; + }; + pie.sort = function(_) { + if (!arguments.length) return sort; + sort = _; + return pie; + }; + pie.startAngle = function(_) { + if (!arguments.length) return startAngle; + startAngle = _; + return pie; + }; + pie.endAngle = function(_) { + if (!arguments.length) return endAngle; + endAngle = _; + return pie; + }; + pie.padAngle = function(_) { + if (!arguments.length) return padAngle; + padAngle = _; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + if (!(n = data.length)) return data; + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var m = series[0].length, n, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { + return radius; + }; + root.x = root.y = 0; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r = +r(d.value); + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + if (padding) { + var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r -= dr; + }); + } + d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); + return nodes; + } + pack.size = function(_) { + if (!arguments.length) return size; + size = _; + return pack; + }; + pack.radius = function(_) { + if (!arguments.length) return radius; + radius = _ == null || typeof _ === "function" ? _ : +_; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return .999 * dr * dr > dx * dx + dy * dy; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); + d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; + d3_layout_hierarchyVisitBefore(root1, secondWalk); + if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { + var left = root0, right = root0, bottom = root0; + d3_layout_hierarchyVisitBefore(root0, function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); + d3_layout_hierarchyVisitBefore(root0, function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + return nodes; + } + function wrapTree(root0) { + var root1 = { + A: null, + children: [ root0 ] + }, queue = [ root1 ], node1; + while ((node1 = queue.pop()) != null) { + for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { + queue.push((children[i] = child = { + _: children[i], + parent: node1, + children: (child = children[i].children) && child.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: i + }).a = child); + } + } + return root1.children[0]; + } + function firstWalk(v) { + var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; + if (children.length) { + d3_layout_treeShift(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + function apportion(v, w, ancestor) { + if (w) { + var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !d3_layout_treeRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + function sizeNode(node) { + node.x *= size[0]; + node.y = node.depth * size[1]; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null ? sizeNode : null; + return tree; + }; + tree.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) == null ? null : sizeNode; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(v) { + var children = v.children; + return children.length ? children[0] : v.t; + } + function d3_layout_treeRight(v) { + var children = v.children, n; + return (n = children.length) ? children[n - 1] : v.t; + } + function d3_layout_treeMove(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; + } + function d3_layout_treeShift(v) { + var shift = 0, change = 0, children = v.children, i = children.length, w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } + } + function d3_layout_treeAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_hierarchyVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { + node.x = (node.x - root.x) * size[0]; + node.y = (root.y - node.y) * size[1]; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null; + return cluster; + }; + cluster.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) != null; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + bates: function(m) { + var random = d3.random.irwinHall(m); + return function() { + return random() / m; + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s; + }; + } + }; + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + return domain; + } + function d3_scale_niceStep(step) { + return step ? { + floor: function(x) { + return Math.floor(x / step) * step; + }, + ceil: function(x) { + return Math.ceil(x / step) * step; + } + } : d3_scale_niceIdentity; + } + var d3_scale_niceIdentity = { + floor: d3_identity, + ceil: d3_identity + }; + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3_interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3_interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + d3_scale_linearNice(domain, m); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(domain, m) { + return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + } + function d3_scale_linearTickRange(domain, m) { + if (m == null) m = 10; + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m, format) { + var range = d3_scale_linearTickRange(domain, m); + if (format) { + var match = d3_format_re.exec(format); + match.shift(); + if (match[8] === "s") { + var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); + if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); + match[8] = "f"; + format = d3.format(match.join("")); + return function(d) { + return format(prefix.scale(d)) + prefix.symbol; + }; + } + if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); + format = match.join(""); + } else { + format = ",." + d3_scale_linearPrecision(range[2]) + "f"; + } + return d3.format(format); + } + var d3_scale_linearFormatSignificant = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + function d3_scale_linearPrecision(value) { + return -Math.floor(Math.log(value) / Math.LN10 + .01); + } + function d3_scale_linearFormatPrecision(type, range) { + var p = d3_scale_linearPrecision(range[2]); + return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); + }; + function d3_scale_log(linear, base, positive, domain) { + function log(x) { + return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); + } + function pow(x) { + return positive ? Math.pow(base, x) : -Math.pow(base, -x); + } + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + positive = x[0] >= 0; + linear.domain((domain = x.map(Number)).map(log)); + return scale; + }; + scale.base = function(_) { + if (!arguments.length) return base; + base = +_; + linear.domain(domain.map(log)); + return scale; + }; + scale.nice = function() { + var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); + linear.domain(niced); + domain = niced.map(pow); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; + if (isFinite(j - i)) { + if (positive) { + for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } else { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (!arguments.length) return d3_scale_logFormat; + if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); + var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, + Math.floor), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), base, positive, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { + floor: function(x) { + return -Math.ceil(-x); + }, + ceil: function(x) { + return -Math.floor(-x); + } + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); + }; + function d3_scale_pow(linear, exponent, domain) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + linear.domain((domain = x.map(Number)).map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + return scale.domain(d3_scale_linearNice(domain, m)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + linear.domain(domain.map(powp)); + return scale; + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, + 0) : (stop - start) / (domain.length - 1 + padding); + range = steps(start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeRoundPoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), + 0) : (stop - start) / (domain.length - 1 + padding) | 0; + range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); + rangeBand = 0; + ranger = { + t: "rangeRoundPoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); + range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); + var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); + var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); + var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + y = y < 0 ? NaN : y / kx + x0; + return [ y, y + 1 / kx ]; + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + if (x <= x) return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return [ domain[y - 1], domain[y] ]; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + function d3_zero() { + return 0; + } + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; + function arc() { + var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; + if (r1 < r0) rc = r1, r1 = r0, r0 = rc; + if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; + var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; + if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { + rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); + if (!cw) p1 *= -1; + if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); + if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); + } + if (r1) { + x0 = r1 * Math.cos(a0 + p1); + y0 = r1 * Math.sin(a0 + p1); + x1 = r1 * Math.cos(a1 - p1); + y1 = r1 * Math.sin(a1 - p1); + var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1; + if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { + var h1 = (a0 + a1) / 2; + x0 = r1 * Math.cos(h1); + y0 = r1 * Math.sin(h1); + x1 = y1 = null; + } + } else { + x0 = y0 = 0; + } + if (r0) { + x2 = r0 * Math.cos(a1 - p0); + y2 = r0 * Math.sin(a1 - p0); + x3 = r0 * Math.cos(a0 + p0); + y3 = r0 * Math.sin(a0 + p0); + var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1; + if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { + var h0 = (a0 + a1) / 2; + x2 = r0 * Math.cos(h0); + y2 = r0 * Math.sin(h0); + x3 = y3 = null; + } + } else { + x2 = y2 = 0; + } + if ((rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { + cr = r0 < r1 ^ cw ? 0 : 1; + var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + if (x1 != null) { + var rc1 = Math.min(rc, (r1 - lc) / (kc + 1)), t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); + if (rc === rc1) { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); + } else { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); + } + } else { + path.push("M", x0, ",", y0); + } + if (x3 != null) { + var rc0 = Math.min(rc, (r0 - lc) / (kc - 1)), t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); + if (rc === rc0) { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } else { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } + } else { + path.push("L", x2, ",", y2); + } + } else { + path.push("M", x0, ",", y0); + if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); + path.push("L", x2, ",", y2); + if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); + } + path.push("Z"); + return path.join(""); + } + function circleSegment(r1, cw) { + return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.cornerRadius = function(v) { + if (!arguments.length) return cornerRadius; + cornerRadius = d3_functor(v); + return arc; + }; + arc.padRadius = function(v) { + if (!arguments.length) return padRadius; + padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.padAngle = function(v) { + if (!arguments.length) return padAngle; + padAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcAuto = "auto"; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_arcPadAngle(d) { + return d && d.padAngle; + } + function d3_svg_arcSweep(x0, y0, x1, y1) { + return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; + } + function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { + var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(r * r * d2 - D * D), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; + } + function d3_svg_line(projection) { + var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + step: d3_svg_lineStep, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.join("L"); + } + function d3_svg_lineLinearClosed(points) { + return d3_svg_lineLinear(points) + "Z"; + } + function d3_svg_lineStep(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); + if (n > 1) path.push("H", p[0]); + return path.join(""); + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + points.push(points[n - 1]); + while (++i <= n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + points.pop(); + path.push("L", pi); + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (abs(d) < ε) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] - halfπ; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3_selectionPrototype.transition = function(name) { + var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { + time: Date.now(), + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id); + }; + d3_selectionPrototype.interrupt = function(name) { + return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); + }; + var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); + function d3_selection_interruptNS(ns) { + return function() { + var lock, active; + if ((lock = this[ns]) && (active = lock[lock.active])) { + if (--lock.count) delete lock[lock.active]; else delete this[ns]; + lock.active += .5; + active.event && active.event.interrupt.call(this, this.__data__, active.index); + } + }; + } + function d3_transition(groups, ns, id) { + d3_subclass(groups, d3_transitionPrototype); + groups.namespace = ns; + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3_transitionPrototype.size = d3_selectionPrototype.size; + d3.transition = function(selection, name) { + return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, ns, id, node[ns][id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node[ns][id]; + subnodes = selector.call(node, node.__data__, i, j); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.namespace, this.id); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) return this.node()[ns][id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node[ns][id].tween.remove(name); + } : function(node) { + node[ns][id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id, ns = groups.namespace; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node[ns][id].tween.set(name, value); + })); + } + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + }); + } + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + function styleNull() { + this.style.removeProperty(name); + } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = d3_interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + }); + } + return d3_transition_tween(this, "style." + name, value, styleString); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + function styleTween(d, i) { + var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + } + return this.tween("style." + name, styleTween); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + var ns = this.namespace; + return this.each("end.transition", function() { + var p; + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node[ns][id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].delay; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].delay = +value.call(node, node.__data__, i, j); + } : (value = +value, function(node) { + node[ns][id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].duration; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); + } : (value = Math.max(1, value), function(node) { + node[ns][id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + try { + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node[ns][id]; + type.call(node, node.__data__, i, j); + }); + } finally { + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } + } else { + d3_selection_each(this, function(node) { + var transition = node[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = node[ns][id0]; + d3_transitionNode(node, i, ns, id1, { + time: transition.time, + ease: transition.ease, + delay: transition.delay + transition.duration, + duration: transition.duration + }); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id1); + }; + function d3_transitionNamespace(name) { + return name == null ? "__transition__" : "__transition_" + name + "__"; + } + function d3_transitionNode(node, i, ns, id, inherit) { + var lock = node[ns] || (node[ns] = { + active: 0, + count: 0 + }), transition = lock[id]; + if (!transition) { + var time = inherit.time; + transition = lock[id] = { + tween: new d3_Map(), + time: time, + delay: inherit.delay, + duration: inherit.duration, + ease: inherit.ease, + index: i + }; + inherit = null; + ++lock.count; + d3.timer(function(elapsed) { + var delay = transition.delay, duration, ease, timer = d3_timer_active, tweened = []; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + function start(elapsed) { + if (lock.active > id) return stop(); + var active = lock[lock.active]; + if (active) { + --lock.count; + delete lock[lock.active]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + lock.active = id; + transition.event && transition.event.start.call(node, node.__data__, i); + transition.tween.forEach(function(key, value) { + if (value = value.call(node, node.__data__, i)) { + tweened.push(value); + } + }); + ease = transition.ease; + duration = transition.duration; + d3.timer(function() { + timer.c = tick(elapsed || 1) ? d3_true : tick; + return 1; + }, 0, time); + } + function tick(elapsed) { + if (lock.active !== id) return 1; + var t = elapsed / duration, e = ease(t), n = tweened.length; + while (n > 0) { + tweened[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, node.__data__, i); + return stop(); + } + } + function stop() { + if (--lock.count) delete lock[id]; else delete node[ns]; + return 1; + } + }, 0, time); + } + } + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); + var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; + var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; + if (orient === "bottom" || orient === "top") { + tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; + text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); + } else { + tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; + text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); + pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); + } + lineEnter.attr(y2, sign * innerTickSize); + textEnter.attr(y1, sign * tickSpacing); + lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); + textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); + if (scale1.rangeBand) { + var x = scale1, dx = x.rangeBand() / 2; + scale0 = scale1 = function(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1, scale0); + } + tickEnter.call(tickTransform, scale0, scale1); + tickUpdate.call(tickTransform, scale1, scale1); + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = arguments; + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x) { + var n = arguments.length; + if (!n) return innerTickSize; + innerTickSize = +x; + outerTickSize = +arguments[n - 1]; + return axis; + }; + axis.innerTickSize = function(x) { + if (!arguments.length) return innerTickSize; + innerTickSize = +x; + return axis; + }; + axis.outerTickSize = function(x) { + if (!arguments.length) return outerTickSize; + outerTickSize = +x; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function() { + return arguments.length && axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x0, x1) { + selection.attr("transform", function(d) { + var v0 = x0(d); + return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; + }); + } + function d3_svg_axisY(selection, y0, y1) { + selection.attr("transform", function(d) { + var v0 = y0(d); + return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; + }); + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; + function brush(g) { + g.each(function() { + var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + var background = g.selectAll(".background").data([ 0 ]); + background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var resize = g.selectAll(".resize").data(resizes, d3_identity); + resize.exit().remove(); + resize.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + resize.style("display", brush.empty() ? "none" : null); + var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; + if (x) { + range = d3_scaleRange(x); + backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); + redrawX(gUpdate); + } + if (y) { + range = d3_scaleRange(y); + backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); + redrawY(gUpdate); + } + redraw(gUpdate); + }); + } + brush.event = function(g) { + g.each(function() { + var event_ = event.of(this, arguments), extent1 = { + x: xExtent, + y: yExtent, + i: xExtentDomain, + j: yExtentDomain + }, extent0 = this.__chart__ || extent1; + this.__chart__ = extent1; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.brush", function() { + xExtentDomain = extent0.i; + yExtentDomain = extent0.j; + xExtent = extent0.x; + yExtent = extent0.y; + event_({ + type: "brushstart" + }); + }).tween("brush:brush", function() { + var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); + xExtentDomain = yExtentDomain = null; + return function(t) { + xExtent = extent1.x = xi(t); + yExtent = extent1.y = yi(t); + event_({ + type: "brush", + mode: "resize" + }); + }; + }).each("end.brush", function() { + xExtentDomain = extent1.i; + yExtentDomain = extent1.j; + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + }); + } else { + event_({ + type: "brushstart" + }); + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + } + }); + }; + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", xExtent[0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); + } + function redrawY(g) { + g.select(".extent").attr("y", yExtent[0]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; + var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (d3.event.changedTouches) { + w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); + } else { + w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); + } + g.interrupt().selectAll("*").interrupt(); + if (dragging) { + origin[0] = xExtent[0] - origin[0]; + origin[1] = yExtent[0] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; + origin[0] = xExtent[ex]; + origin[1] = yExtent[ey]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= xExtent[1]; + origin[1] -= yExtent[1]; + dragging = 2; + } + d3_eventPreventDefault(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += xExtent[1]; + origin[1] += yExtent[1]; + dragging = 0; + d3_eventPreventDefault(); + } + } + function brushmove() { + var point = d3.mouse(target), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; + origin[0] = xExtent[+(point[0] < center[0])]; + origin[1] = yExtent[+(point[1] < center[1])]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0] != min || extent[1] != max) { + if (i) yExtentDomain = null; else xExtentDomain = null; + extent[0] = min; + extent[1] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + dragRestore(); + event_({ + type: "brushend" + }); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.clamp = function(z) { + if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; + if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + if (x) { + if (xExtentDomain) { + x0 = xExtentDomain[0], x1 = xExtentDomain[1]; + } else { + x0 = xExtent[0], x1 = xExtent[1]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + if (yExtentDomain) { + y0 = yExtentDomain[0], y1 = yExtentDomain[1]; + } else { + y0 = yExtent[0], y1 = yExtent[1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + xExtentDomain = [ x0, x1 ]; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + yExtentDomain = [ y0, y1 ]; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; + } + return brush; + }; + brush.clear = function() { + if (!brush.empty()) { + xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; + xExtentDomain = yExtentDomain = null; + } + return brush; + }; + brush.empty = function() { + return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; + var d3_time_formatUtc = d3_time_format.utc; + var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3_time.second = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3_time.seconds = d3_time.second.range; + d3_time.seconds.utc = d3_time.second.utc.range; + d3_time.minute = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3_time.minutes = d3_time.minute.range; + d3_time.minutes.utc = d3_time.minute.utc.range; + d3_time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3_time.hours = d3_time.hour.range; + d3_time.hours.utc = d3_time.hour.utc.range; + d3_time.month = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3_time.months = d3_time.month.range; + d3_time.months.utc = d3_time.month.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + function tickMethod(extent, count) { + var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); + return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { + return d / 31536e6; + }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; + } + scale.nice = function(interval, skip) { + var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); + if (method) interval = method[0], skip = method[1]; + function skipped(date) { + return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; + } + return scale.domain(d3_scale_nice(domain, skip > 1 ? { + floor: function(date) { + while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); + return date; + }, + ceil: function(date) { + while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); + return date; + } + } : interval)); + }; + scale.ticks = function(interval, skip) { + var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { + range: interval + }, skip ]; + if (method) interval = method[0], skip = method[1]; + return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_time_scaleDate(t) { + return new Date(t); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; + var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { + return d.getMilliseconds(); + } ], [ ":%S", function(d) { + return d.getSeconds(); + } ], [ "%I:%M", function(d) { + return d.getMinutes(); + } ], [ "%I %p", function(d) { + return d.getHours(); + } ], [ "%a %d", function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ "%b %d", function(d) { + return d.getDate() != 1; + } ], [ "%B", function(d) { + return d.getMonth(); + } ], [ "%Y", d3_true ] ]); + var d3_time_scaleMilliseconds = { + range: function(start, stop, step) { + return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); + }, + floor: d3_identity, + ceil: d3_identity + }; + d3_time_scaleLocalMethods.year = d3_time.year; + d3_time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { + return d.getUTCMilliseconds(); + } ], [ ":%S", function(d) { + return d.getUTCSeconds(); + } ], [ "%I:%M", function(d) { + return d.getUTCMinutes(); + } ], [ "%I %p", function(d) { + return d.getUTCHours(); + } ], [ "%a %d", function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ "%b %d", function(d) { + return d.getUTCDate() != 1; + } ], [ "%B", function(d) { + return d.getUTCMonth(); + } ], [ "%Y", d3_true ] ]); + d3_time_scaleUtcMethods.year = d3_time.year.utc; + d3_time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); + }; + d3.text = d3_xhrType(function(request) { + return request.responseText; + }); + d3.json = function(url, callback) { + return d3_xhr(url, "application/json", d3_json, callback); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3_xhr(url, "text/html", d3_html, callback); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = d3_xhrType(function(request) { + return request.responseXML; + }); + if (typeof define === "function" && define.amd) define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; + this.d3 = d3; +}(); \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/libs/d3/d3.min.js b/PotreeConverter/resources/page_template/libs/d3/d3.min.js new file mode 100644 index 00000000..34d5513e --- /dev/null +++ b/PotreeConverter/resources/page_template/libs/d3/d3.min.js @@ -0,0 +1,5 @@ +!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n.toLowerCase()))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c +},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.5"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},u(Math.pow(2,o)),i(d=e,r),t=ta.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||n({type:"zoomend"}),d=null}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(v=e(d=m||ta.mouse(this)),Dl.call(this),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ac[n]=Ft(function(n){return(n=ac.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t +},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Ll)),e=t*Ll;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}(); \ No newline at end of file diff --git a/PotreeConverter/resources/page_template/libs/jquery-2.1.4/jquery-2.1.4.min.js b/PotreeConverter/resources/page_template/libs/jquery-2.1.4/jquery-2.1.4.min.js new file mode 100644 index 00000000..49990d6e --- /dev/null +++ b/PotreeConverter/resources/page_template/libs/jquery-2.1.4/jquery-2.1.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ +return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("