From e915b78599e4705e81c6d8dcb25019177316c6a1 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 2 Jun 2024 20:47:57 +0530 Subject: [PATCH 01/60] volume Activity --- activities.json | 1 + activities/3DVolume.activity/LICENSE | 202 + activities/3DVolume.activity/NOTICE | 13 + .../activity/activity-icon.svg | 7 + .../3DVolume.activity/activity/activity.info | 6 + activities/3DVolume.activity/css/activity.css | 452 + activities/3DVolume.activity/css/introjs.css | 543 + activities/3DVolume.activity/icons/add.svg | 1 + activities/3DVolume.activity/icons/clear.svg | 6 + activities/3DVolume.activity/icons/cube.svg | 12 + .../3DVolume.activity/icons/cube_solid.svg | 6 + .../3DVolume.activity/icons/dialog-cancel.svg | 6 + .../3DVolume.activity/icons/dialog-ok.svg | 6 + activities/3DVolume.activity/icons/dodeca.svg | 26 + .../3DVolume.activity/icons/down-arrow.svg | 2 + activities/3DVolume.activity/icons/glass.svg | 3 + .../3DVolume.activity/icons/go-left.svg | 8 + .../3DVolume.activity/icons/go-right.svg | 36 + activities/3DVolume.activity/icons/help.svg | 9 + activities/3DVolume.activity/icons/icosa.svg | 25 + activities/3DVolume.activity/icons/image.svg | 4 + .../icons/insert-picture.svg | 40 + .../3DVolume.activity/icons/left-arrow.svg | 2 + activities/3DVolume.activity/icons/minus.svg | 1 + .../3DVolume.activity/icons/number_off.svg | 18 + .../3DVolume.activity/icons/number_on.svg | 18 + activities/3DVolume.activity/icons/octa.svg | 21 + .../3DVolume.activity/icons/restart.svg | 6 + .../3DVolume.activity/icons/right-arrow.svg | 2 + activities/3DVolume.activity/icons/tess.png | Bin 0 -> 799 bytes activities/3DVolume.activity/icons/tetra.svg | 16 + activities/3DVolume.activity/icons/throw.svg | 4 + .../3DVolume.activity/icons/up-arrow.svg | 2 + .../3DVolume.activity/icons/zoom-in.svg | 17 + .../3DVolume.activity/icons/zoom-out.svg | 17 + activities/3DVolume.activity/images/1.png | Bin 0 -> 15332 bytes activities/3DVolume.activity/images/1.svg | 12 + activities/3DVolume.activity/images/2.png | Bin 0 -> 18232 bytes activities/3DVolume.activity/images/2.svg | 12 + activities/3DVolume.activity/images/3.png | Bin 0 -> 17334 bytes activities/3DVolume.activity/images/4.png | Bin 0 -> 15513 bytes activities/3DVolume.activity/images/5.png | Bin 0 -> 16851 bytes activities/3DVolume.activity/images/6.png | Bin 0 -> 18101 bytes activities/3DVolume.activity/images/7.png | Bin 0 -> 16196 bytes activities/3DVolume.activity/images/8.png | Bin 0 -> 19142 bytes .../3DVolume.activity/images/default.png | Bin 0 -> 134909 bytes .../images/grass_background.png | Bin 0 -> 83676 bytes .../3DVolume.activity/images/green-board.jpeg | Bin 0 -> 2141 bytes activities/3DVolume.activity/images/image.png | Bin 0 -> 169571 bytes .../3DVolume.activity/images/red-board.jpeg | Bin 0 -> 1526 bytes activities/3DVolume.activity/images/wood.png | Bin 0 -> 223968 bytes activities/3DVolume.activity/index.html | 109 + activities/3DVolume.activity/js/activity.js | 2047 + activities/3DVolume.activity/js/buffer.js | 934 + activities/3DVolume.activity/js/cannon.js | 13850 ++++++ activities/3DVolume.activity/js/debug.js | 306 + .../3DVolume.activity/js/fonts/robot.json | 1 + activities/3DVolume.activity/js/loader.js | 8 + activities/3DVolume.activity/js/orbit.js | 1555 + .../js/palettes/bgpalette.html | 18 + .../js/palettes/bgpalette.js | 66 + .../js/palettes/colorpalette.html | 11 + .../js/palettes/colorpalette.js | 48 + .../js/palettes/volumepalette.html | 4 + .../js/palettes/volumepalette.js | 35 + .../js/palettes/zoompalette.html | 4 + .../js/palettes/zoompalette.js | 36 + activities/3DVolume.activity/js/three.js | 38464 ++++++++++++++++ activities/3DVolume.activity/lib/domReady.js | 129 + activities/3DVolume.activity/lib/intro.js | 11 + activities/3DVolume.activity/lib/mustache.js | 610 + activities/3DVolume.activity/lib/picoModal.js | 1 + activities/3DVolume.activity/lib/require.js | 2000 + .../3DVolume.activity/lib/sugar-web/LICENSE | 202 + .../3DVolume.activity/lib/sugar-web/README.md | 7 + .../lib/sugar-web/activity/activity.js | 228 + .../lib/sugar-web/activity/shortcut.js | 60 + .../3DVolume.activity/lib/sugar-web/bus.js | 14 + .../lib/sugar-web/bus/sugarizer.js | 82 + .../lib/sugar-web/bus/sugaros.js | 223 + .../lib/sugar-web/datastore.js | 14 + .../lib/sugar-web/datastore/sugarizer.js | 498 + .../lib/sugar-web/datastore/sugaros.js | 224 + .../lib/sugar-web/dictstore.js | 68 + .../3DVolume.activity/lib/sugar-web/env.js | 106 + .../lib/sugar-web/graphics/README.md | 40 + .../sugar-web/graphics/activitypalette.html | 9 + .../lib/sugar-web/graphics/activitypalette.js | 62 + .../sugar-web/graphics/css/sugar-200dpi.css | 457 + .../sugar-web/graphics/css/sugar-200dpi.less | 2 + .../sugar-web/graphics/css/sugar-96dpi.css | 457 + .../sugar-web/graphics/css/sugar-96dpi.less | 2 + .../lib/sugar-web/graphics/css/sugar.less | 587 + .../lib/sugar-web/graphics/grid.js | 57 + .../lib/sugar-web/graphics/icon.js | 89 + .../icons/actions/activity-abecedarium.svg | 172 + .../icons/actions/activity-journal.svg | 11 + .../graphics/icons/actions/activity-stop.svg | 6 + .../actions/checkbox-checked-selected.svg | 27 + .../icons/actions/checkbox-checked.svg | 27 + .../actions/checkbox-unchecked-selected.svg | 22 + .../icons/actions/checkbox-unchecked.svg | 22 + .../icons/actions/dialog-cancel-active.svg | 6 + .../graphics/icons/actions/dialog-cancel.svg | 6 + .../icons/actions/dialog-ok-active.svg | 6 + .../graphics/icons/actions/dialog-ok.svg | 6 + .../icons/actions/entry-cancel-active.svg | 23 + .../icons/actions/entry-cancel-disabled.svg | 21 + .../graphics/icons/actions/entry-cancel.svg | 21 + .../graphics/icons/actions/photo.svg | 51 + .../icons/actions/radio-active-selected.svg | 31 + .../graphics/icons/actions/radio-active.svg | 31 + .../graphics/icons/actions/radio-selected.svg | 26 + .../graphics/icons/actions/radio.svg | 26 + .../graphics/icons/actions/zoom-groups.svg | 6 + .../graphics/icons/actions/zoom-home.svg | 6 + .../icons/actions/zoom-neighborhood.svg | 6 + .../graphics/icons/emblems/arrow-down.svg | 20 + .../graphics/icons/emblems/arrow-up.svg | 20 + .../graphics/icons/emblems/favorite.svg | 6 + .../lib/sugar-web/graphics/journalchooser.js | 687 + .../lib/sugar-web/graphics/menupalette.html | 8 + .../lib/sugar-web/graphics/menupalette.js | 53 + .../lib/sugar-web/graphics/palette.js | 185 + .../lib/sugar-web/graphics/presencepalette.js | 187 + .../sugar-web/graphics/radiobuttonsgroup.js | 62 + .../lib/sugar-web/graphics/xocolor.js | 731 + .../lib/sugar-web/package.json | 10 + .../lib/sugar-web/presence.js | 302 + .../test/functional/datastoreSpec.js | 253 + .../test/functional/toolkitContractSpec.js | 31 + .../lib/sugar-web/test/graphics/iconSpec.js | 59 + .../test/graphics/menupaletteSpec.js | 78 + .../sugar-web/test/graphics/paletteSpec.js | 36 + .../test/graphics/radiobuttonsgroupSpec.js | 97 + .../lib/sugar-web/test/karma-shared.conf.js | 75 + .../lib/sugar-web/test/karma-unit.conf.js | 19 + .../lib/sugar-web/test/karma.conf.js | 16 + .../lib/sugar-web/test/loader.js | 21 + .../lib/sugar-web/test/unit/busSpec.js | 156 + .../lib/sugar-web/test/unit/datastoreSpec.js | 32 + .../lib/sugar-web/test/unit/dictstoreSpec.js | 54 + .../lib/sugar-web/test/unit/envSpec.js | 137 + activities/3DVolume.activity/lib/text.js | 366 + activities/3DVolume.activity/lib/tutorial.js | 111 + activities/3DVolume.activity/lib/webL10n.js | 1029 + .../3DVolume.activity/package-lock.json | 24 + activities/3DVolume.activity/package.json | 10 + activities/3DVolume.activity/setup.py | 5 + 149 files changed, 70736 insertions(+) create mode 100644 activities/3DVolume.activity/LICENSE create mode 100644 activities/3DVolume.activity/NOTICE create mode 100644 activities/3DVolume.activity/activity/activity-icon.svg create mode 100644 activities/3DVolume.activity/activity/activity.info create mode 100644 activities/3DVolume.activity/css/activity.css create mode 100644 activities/3DVolume.activity/css/introjs.css create mode 100644 activities/3DVolume.activity/icons/add.svg create mode 100644 activities/3DVolume.activity/icons/clear.svg create mode 100644 activities/3DVolume.activity/icons/cube.svg create mode 100644 activities/3DVolume.activity/icons/cube_solid.svg create mode 100644 activities/3DVolume.activity/icons/dialog-cancel.svg create mode 100644 activities/3DVolume.activity/icons/dialog-ok.svg create mode 100644 activities/3DVolume.activity/icons/dodeca.svg create mode 100644 activities/3DVolume.activity/icons/down-arrow.svg create mode 100644 activities/3DVolume.activity/icons/glass.svg create mode 100644 activities/3DVolume.activity/icons/go-left.svg create mode 100644 activities/3DVolume.activity/icons/go-right.svg create mode 100644 activities/3DVolume.activity/icons/help.svg create mode 100644 activities/3DVolume.activity/icons/icosa.svg create mode 100644 activities/3DVolume.activity/icons/image.svg create mode 100644 activities/3DVolume.activity/icons/insert-picture.svg create mode 100644 activities/3DVolume.activity/icons/left-arrow.svg create mode 100644 activities/3DVolume.activity/icons/minus.svg create mode 100644 activities/3DVolume.activity/icons/number_off.svg create mode 100644 activities/3DVolume.activity/icons/number_on.svg create mode 100644 activities/3DVolume.activity/icons/octa.svg create mode 100644 activities/3DVolume.activity/icons/restart.svg create mode 100644 activities/3DVolume.activity/icons/right-arrow.svg create mode 100644 activities/3DVolume.activity/icons/tess.png create mode 100644 activities/3DVolume.activity/icons/tetra.svg create mode 100644 activities/3DVolume.activity/icons/throw.svg create mode 100644 activities/3DVolume.activity/icons/up-arrow.svg create mode 100644 activities/3DVolume.activity/icons/zoom-in.svg create mode 100644 activities/3DVolume.activity/icons/zoom-out.svg create mode 100644 activities/3DVolume.activity/images/1.png create mode 100644 activities/3DVolume.activity/images/1.svg create mode 100644 activities/3DVolume.activity/images/2.png create mode 100644 activities/3DVolume.activity/images/2.svg create mode 100644 activities/3DVolume.activity/images/3.png create mode 100644 activities/3DVolume.activity/images/4.png create mode 100644 activities/3DVolume.activity/images/5.png create mode 100644 activities/3DVolume.activity/images/6.png create mode 100644 activities/3DVolume.activity/images/7.png create mode 100644 activities/3DVolume.activity/images/8.png create mode 100644 activities/3DVolume.activity/images/default.png create mode 100644 activities/3DVolume.activity/images/grass_background.png create mode 100644 activities/3DVolume.activity/images/green-board.jpeg create mode 100644 activities/3DVolume.activity/images/image.png create mode 100644 activities/3DVolume.activity/images/red-board.jpeg create mode 100644 activities/3DVolume.activity/images/wood.png create mode 100644 activities/3DVolume.activity/index.html create mode 100644 activities/3DVolume.activity/js/activity.js create mode 100644 activities/3DVolume.activity/js/buffer.js create mode 100644 activities/3DVolume.activity/js/cannon.js create mode 100644 activities/3DVolume.activity/js/debug.js create mode 100644 activities/3DVolume.activity/js/fonts/robot.json create mode 100644 activities/3DVolume.activity/js/loader.js create mode 100644 activities/3DVolume.activity/js/orbit.js create mode 100644 activities/3DVolume.activity/js/palettes/bgpalette.html create mode 100644 activities/3DVolume.activity/js/palettes/bgpalette.js create mode 100644 activities/3DVolume.activity/js/palettes/colorpalette.html create mode 100644 activities/3DVolume.activity/js/palettes/colorpalette.js create mode 100644 activities/3DVolume.activity/js/palettes/volumepalette.html create mode 100644 activities/3DVolume.activity/js/palettes/volumepalette.js create mode 100644 activities/3DVolume.activity/js/palettes/zoompalette.html create mode 100644 activities/3DVolume.activity/js/palettes/zoompalette.js create mode 100644 activities/3DVolume.activity/js/three.js create mode 100644 activities/3DVolume.activity/lib/domReady.js create mode 100644 activities/3DVolume.activity/lib/intro.js create mode 100644 activities/3DVolume.activity/lib/mustache.js create mode 100644 activities/3DVolume.activity/lib/picoModal.js create mode 100644 activities/3DVolume.activity/lib/require.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/LICENSE create mode 100644 activities/3DVolume.activity/lib/sugar-web/README.md create mode 100644 activities/3DVolume.activity/lib/sugar-web/activity/activity.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/activity/shortcut.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/bus.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/bus/sugarizer.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/bus/sugaros.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/datastore.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/datastore/sugarizer.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/datastore/sugaros.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/dictstore.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/env.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/README.md create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/activitypalette.html create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/activitypalette.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/css/sugar-200dpi.css create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/css/sugar-200dpi.less create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/css/sugar-96dpi.css create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/css/sugar-96dpi.less create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/css/sugar.less create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/grid.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icon.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/activity-abecedarium.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/activity-journal.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/activity-stop.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/checkbox-checked-selected.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/checkbox-checked.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/checkbox-unchecked-selected.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/checkbox-unchecked.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/dialog-cancel-active.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/dialog-cancel.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/dialog-ok-active.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/dialog-ok.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/entry-cancel-active.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/entry-cancel-disabled.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/entry-cancel.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/photo.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/radio-active-selected.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/radio-active.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/radio-selected.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/radio.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/zoom-groups.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/zoom-home.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/emblems/arrow-down.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/emblems/arrow-up.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/icons/emblems/favorite.svg create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/journalchooser.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/menupalette.html create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/menupalette.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/palette.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/presencepalette.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/radiobuttonsgroup.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/graphics/xocolor.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/package.json create mode 100644 activities/3DVolume.activity/lib/sugar-web/presence.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/functional/datastoreSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/functional/toolkitContractSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/graphics/iconSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/graphics/menupaletteSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/graphics/paletteSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/graphics/radiobuttonsgroupSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/karma-shared.conf.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/karma-unit.conf.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/karma.conf.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/loader.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/unit/busSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/unit/datastoreSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/unit/dictstoreSpec.js create mode 100644 activities/3DVolume.activity/lib/sugar-web/test/unit/envSpec.js create mode 100644 activities/3DVolume.activity/lib/text.js create mode 100644 activities/3DVolume.activity/lib/tutorial.js create mode 100644 activities/3DVolume.activity/lib/webL10n.js create mode 100644 activities/3DVolume.activity/package-lock.json create mode 100644 activities/3DVolume.activity/package.json create mode 100644 activities/3DVolume.activity/setup.py diff --git a/activities.json b/activities.json index 98d5c0244..e7cf5803e 100644 --- a/activities.json +++ b/activities.json @@ -1,4 +1,5 @@ [ + {"id": "org.sugarlabs.3DVolume", "name": "3D Volume Activity", "version": 1, "directory": "activities/3DVolume.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, {"id": "org.olpcfrance.XmasLights", "name": "Xmas Lights", "version": 1, "directory": "activities/XmasLights.activity", "icon": "activity/activity-icon.svg", "favorite": false, "activityId": null}, {"id": "org.olpcfrance.DollarStreet", "name": "Dollar Street", "version": 1, "directory": "activities/DollarStreet.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, {"id": "org.sugarlabs.Tangram", "name": "Tangram", "version": 1, "directory": "activities/Tangram.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, diff --git a/activities/3DVolume.activity/LICENSE b/activities/3DVolume.activity/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/activities/3DVolume.activity/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/activities/3DVolume.activity/NOTICE b/activities/3DVolume.activity/NOTICE new file mode 100644 index 000000000..004c62e1b --- /dev/null +++ b/activities/3DVolume.activity/NOTICE @@ -0,0 +1,13 @@ +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/activities/3DVolume.activity/activity/activity-icon.svg b/activities/3DVolume.activity/activity/activity-icon.svg new file mode 100644 index 000000000..55e602a3e --- /dev/null +++ b/activities/3DVolume.activity/activity/activity-icon.svg @@ -0,0 +1,7 @@ + + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/activity/activity.info b/activities/3DVolume.activity/activity/activity.info new file mode 100644 index 000000000..688d98e42 --- /dev/null +++ b/activities/3DVolume.activity/activity/activity.info @@ -0,0 +1,6 @@ +[Activity] +name = 3DVolume +activity_version = 1 +bundle_id = org.sugarlabs.3DVolume +exec = sugar-activity-web +icon = activity-icon \ No newline at end of file diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css new file mode 100644 index 000000000..75b0baffb --- /dev/null +++ b/activities/3DVolume.activity/css/activity.css @@ -0,0 +1,452 @@ +#main-toolbar #activity-button { + background-image: url(../activity/activity-icon.svg); +} +#main-toolbar #network-button { + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); +} +.toolbar { + display: flex; + justify-content: space-between; +} +.left-container { + display: flex; +} + +#private-button { + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); + width: 47px; + height: 47px; + margin: 4px 2px; + color: white; + color: transparent; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + background-size: contain; + border: 0; + border-radius: 5.5px; +} + +#shared-button { + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg); + width: 47px; + height: 47px; + margin: 4px 2px; + color: white; + color: transparent; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + background-size: contain; + border: 0; + border-radius: 5.5px; +} + +body { + height: 100vh; +} +#main-toolbar #bg-button { + background-image: url(../icons/insert-picture.svg); + outline: none; +} +#volume-button { + background-image:url(../icons/cube.svg); + outline:none; +} +#main-toolbar #clear-button { + background-image: url(../icons/clear.svg); + outline:none; +} +#clear-button.active { + background-color: #e1e1e1; /* Light white color */ +} + + +#main-toolbar #solid-button { + background-image: url(../icons/cube_solid.svg); + height: 100px; + width: 65px; + background-size: cover; + background-repeat: no-repeat; + outline:none; +} +#number-button { + height: 65px; + width: 65px; + background-size: cover; + background-repeat: no-repeat; + background-image: url(../icons/number_on.svg); + outline:none; +} +#zoom-button { + height: 45px; + width: 45xpx; + background-size: cover; + background-repeat: no-repeat; + background-image: url(../icons/glass.svg); + outline:none; +} +#throw-button { + height: 45px; + width: 45xpx; + background-size: cover; + background-repeat: no-repeat; + background-image: url(../icons/throw.svg); + outline:none; +} +#transparent-button { + margin: 0; + padding: 0; + height: 65px; + width: 65px; + background-size: contain; + background-repeat: no-repeat; + background-image: url(../icons/tess.png); + outline:none; +} + +#colors-button-fill{ + display: none; +} +.button-background { + background-color:#000; +} +#cube-button { + background-image:url(../icons/cube.svg); + outline:none; + background-repeat: no-repeat; + background-size: cover; +} +#tetra-button { + background-image:url(../icons/tetra.svg); + outline:none; + background-repeat: no-repeat; + background-size: cover; +} +#octa-button { + background-image:url(../icons/octa.svg); + outline:none; + background-repeat: no-repeat; + background-size: cover; +} +#dodeca-button { + background-image:url(../icons/dodeca.svg); + outline:none; + background-repeat: no-repeat; + background-size: cover; +} +#icosa-button { + background-image:url(../icons/icosa.svg); + outline:none; + margin-left: 10px; + background-repeat: no-repeat; + background-size: cover; +} +.active { + background-color: #e1e1e1 !important; +} + +#main-toolbar #help-button { + background-image: url(../icons/help.svg); +} +.vertical-line { + width: 5px; + height: 55px; + background-color: white; + transform: translateX(-50%); + margin-left: 5px; + margin-right: 5px; +} + +.selector { + display:flex; +} +.bg-select { + /* padding: 10px; */ + width: fit-content; + text-align: center; + background-color: black; +} +.bg-select button { + border: none; + border-radius: 0; +} +.bg-select-item { + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto; + background: transparent; + width: 90%; +} +.bg-select-item img { + width: 40px; + margin: 10px; +} +.bg-select-item div { + margin: 0 10px; +} +#main-toolbar #reset-button { + background-image: url(../icons/restart.svg); +} +#main-toolbar #image-button { + background-image: url(../icons/image.svg); +} +body { + margin: 0; + color: var(--color-text); + background-color: var(--color-bg); + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +body { + margin: 0; + overflow: hidden; + height: 100vh; + background-color: #f0f0f0; +} + +#game-container { + position: relative; +} + +/* #throw-button { + position: absolute; + top: 20px; + left: 20px; + padding: 10px; + font-size: 16px; + cursor: pointer; + background-color: #3498db; + color: #fff; + border: none; + border-radius: 5px; + outline: none; +} */ + +#score-container { + position: absolute; + height:70vh; + width: 70vh; + top: 70px; + left: 20px; + font-size: 16px; + color: #fff; + border: none; + outline: none; +} + +#sum-display { + position: absolute; + top: 20px; + right: 20px; + font-size: 18px; +} + +#last-roll { + position: fixed; + bottom: 100px; + right: 100px; + font-size: 1.5rem; +} + + + + +#color-grid { + display: grid; + grid-template-columns: repeat(3, 50px); /* Decreased circle size */ + grid-gap: 10px; + margin-left:5px; + margin-bottom:5px; + margin-top:5px; + margin-right:5px; +} + +#zoom-in-button { + background-image: url(../icons/zoom-in.svg); + width: 50px; + height: 50px; + background-color: #000000; + background-size: cover; + background-repeat: no-repeat; +} + +#zoom-out-button { + background-image: url(../icons/zoom-out.svg); + width: 50px; + height: 50px; + background-color: #000000; + background-size: cover; + background-repeat: no-repeat; +} + +#right-button { + background-image: url(../icons/right-arrow.svg); +} +#up-button { + background-image: url(../icons/up-arrow.svg); +} +#down-button { + background-image: url(../icons/down-arrow.svg); +} + +#left-button { + background-image: url(../icons/left-arrow.svg); +} + +.arrow-container { + width:50%; + display: flex; + flex-direction: column; + align-items: center; + /* background-color:white; */ +} + +.arrow-button { + width: 50px; + height: 50px; + border: none; + background-size: contain; + background-repeat: no-repeat; + margin: 5px 0; +} + + +.left-right-buttons { + display: flex; + justify-content: space-between; + /* background-color:pink; */ + width:80%; +} + +.left-right-buttons .arrow-button { + margin: 0 5px; +} + + +.color { + width: 45px; /* Decreased circle size */ + height: 45px; /* Decreased circle size */ + border-radius: 50%; + cursor: pointer; + border: white solid 2px; +} + +/* UI styling for introJs-tutorial */ + +.introjs-overlay { + background-color: #000 !important; + opacity: .8 !important; +} + +.introjs-tooltip { + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif!important; + border-radius: 6px !important; + padding: 2px 1px !important; + font-size: 14px !important; +} + +.introjs-helperLayer{ + background: inherit !important; + opacity: 0.6 !important; +} + +.customTooltip .introjs-tooltip-header { + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color : #ffffff; + text-shadow: none; + /* background-color: #808080; */ + margin: 0; + padding: 0px 10px; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} + +.customTooltip .introjs-tooltip-title { + font-size: 14px; + padding: 0px 8px; + font-weight: 800; +} + +.customTooltip .introjs-tooltiptext { + color: #000000; + line-height: 1.42857143; +} + +.customTooltip .introjs-skipbutton { + float: right; + cursor: pointer; + background-image: url(../icons/dialog-cancel.svg); + width: 25px; + height: 25px; + background-size: 25px 25px; + margin-top: 2px; + padding: 0px; + white-space: nowrap; + color: transparent; + line-height: 27px; +} + +.customTooltip .introjs-nextbutton::before { + content: ""; + margin-right: 6px; + background-image: url(../icons/go-right.svg); + width: 20px; + height: 20px; + background-size: 20px 20px; +} + +.customTooltip .introjs-prevbutton::before { + content: ""; + margin-right: 6px; + background-image: url(../icons/go-left.svg); + width: 20px; + height: 20px; + background-size: 20px 20px; +} + +.customTooltip .introjs-tooltipbuttons { + display: flex; + flex-wrap:wrap; + justify-content: center; + align-items: center; + cursor: pointer; + border-top: 0px; + padding: 0px; + text-align: center; + white-space: normal; +} + +.customTooltip .introjs-button { + text-shadow: none; + border-radius: 22px; + margin: 5px 8px 8px 8px; + width: fit-content; + /* background-color: #808080 !important; */ + display: flex !important; + align-items: center !important; + color: white !important; + padding: 6px 20px 6px 10px; + border: 0px; +} + +.customTooltip .introjs-button:focus { + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + border: 0px; + /* background-color: #808080 !important; */ +} + +.customTooltip .introjs-disabled { + color: black !important; + border: 0px; + opacity: .65; +} + +.customTooltip .introjs-disabled:focus { + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + border: 0px; +} diff --git a/activities/3DVolume.activity/css/introjs.css b/activities/3DVolume.activity/css/introjs.css new file mode 100644 index 000000000..d9d845984 --- /dev/null +++ b/activities/3DVolume.activity/css/introjs.css @@ -0,0 +1,543 @@ +.introjs-overlay { + position: absolute; + -webkit-box-sizing: content-box; + box-sizing: content-box; + z-index: 999999; + opacity: 0; + -webkit-transition: all .3s ease-out; + -o-transition: all .3s ease-out; + transition: all .3s ease-out +} + +.introjs-showElement { + z-index: 9999999 !important +} + +tr.introjs-showElement>td { + z-index: 9999999 !important; + position: relative +} + +tr.introjs-showElement>th { + z-index: 9999999 !important; + position: relative +} + +.introjs-disableInteraction { + z-index: 99999999 !important; + position: absolute; + background-color: #fff; + opacity: 0 +} + +.introjs-relativePosition { + position: relative +} + +.introjs-helperLayer { + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: absolute; + z-index: 9999998; + border-radius: 4px; + -webkit-transition: all .3s ease-out; + -o-transition: all .3s ease-out; + transition: all .3s ease-out +} + +.introjs-helperLayer * { + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +.introjs-helperLayer :before { + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +.introjs-helperLayer :after { + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +.introjs-tooltipReferenceLayer { + font-family: "Helvetica Neue", Inter, ui-sans-serif, "Apple Color Emoji", Helvetica, Arial, sans-serif; + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: absolute; + visibility: hidden; + z-index: 100000000; + background-color: transparent; + -webkit-transition: all .3s ease-out; + -o-transition: all .3s ease-out; + transition: all .3s ease-out +} + +.introjs-tooltipReferenceLayer * { + font-family: "Helvetica Neue", Inter, ui-sans-serif, "Apple Color Emoji", Helvetica, Arial, sans-serif +} + +.introjs-helperNumberLayer { + font-family: "Helvetica Neue", Inter, ui-sans-serif, "Apple Color Emoji", Helvetica, Arial, sans-serif; + color: #9e9e9e; + text-align: center; + padding-top: 10px; + padding-bottom: 10px +} + +.introjs-arrow { + border: 5px solid transparent; + content: ""; + position: absolute +} + +.introjs-arrow.top { + top: -10px; + left: 10px; + border-bottom-color: #fff +} + +.introjs-arrow.top-right { + top: -10px; + right: 10px; + border-bottom-color: #fff +} + +.introjs-arrow.top-middle { + top: -10px; + left: 50%; + margin-left: -5px; + border-bottom-color: #fff +} + +.introjs-arrow.right { + right: -10px; + top: 10px; + border-left-color: #fff +} + +.introjs-arrow.right-bottom { + bottom: 10px; + right: -10px; + border-left-color: #fff +} + +.introjs-arrow.bottom { + bottom: -10px; + left: 10px; + border-top-color: #fff +} + +.introjs-arrow.bottom-right { + bottom: -10px; + right: 10px; + border-top-color: #fff +} + +.introjs-arrow.bottom-middle { + bottom: -10px; + left: 50%; + margin-left: -5px; + border-top-color: #fff +} + +.introjs-arrow.left { + left: -10px; + top: 10px; + border-right-color: #fff +} + +.introjs-arrow.left-bottom { + left: -10px; + bottom: 10px; + border-right-color: #fff +} + +.introjs-tooltip { + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: absolute; + visibility: visible; + background-color: #fff; + min-width: 250px; + max-width: 300px; + border-radius: 5px; + -webkit-box-shadow: 0 3px 30px rgba(33, 33, 33, .3); + box-shadow: 0 3px 30px rgba(33, 33, 33, .3); + -webkit-transition: opacity .1s ease-out; + -o-transition: opacity .1s ease-out; + transition: opacity .1s ease-out +} + +.introjs-tooltiptext { + padding: 20px +} + +.introjs-dontShowAgain { + padding-left: 20px; + padding-right: 20px +} + +.introjs-dontShowAgain input { + padding: 0; + margin: 0; + margin-bottom: 2px; + display: inline; + width: 10px; + height: 10px +} + +.introjs-dontShowAgain label { + font-size: 14px; + display: inline-block; + font-weight: 400; + display: inline-block; + margin: 0 0 0 5px; + padding: 0; + background-color: #fff; + color: #616161; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none +} + +.introjs-tooltip-title { + font-size: 18px; + margin: 0; + padding: 0; + font-weight: 700; + float: left; + line-height: 32px +} + +.introjs-tooltip-header { + padding-left: 20px; + padding-right: 20px; + padding-top: 10px +} + +.introjs-tooltip-header:after { + content: "."; + visibility: hidden; + display: block; + height: 0; + clear: both +} + +.introjs-tooltipbuttons { + border-top: 1px solid #e0e0e0; + padding: 10px; + text-align: right; + white-space: nowrap +} + +.introjs-tooltipbuttons:after { + content: ""; + visibility: hidden; + display: block; + height: 0; + clear: both +} + +.introjs-button { + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: relative; + overflow: visible; + display: inline-block; + padding: .5rem 1rem; + border: 1px solid #bdbdbd; + text-decoration: none; + text-shadow: 1px 1px 0 #fff; + font-size: 14px; + color: #424242; + white-space: nowrap; + cursor: pointer; + outline: 0; + background-color: #f4f4f4; + border-radius: .2em; + zoom: 1; + display: inline +} + +.introjs-button:hover { + outline: 0; + text-decoration: none; + border-color: #9e9e9e; + background-color: #e0e0e0; + color: #212121 +} + +.introjs-button:focus { + outline: 0; + text-decoration: none; + background-color: #eee; + -webkit-box-shadow: 0 0 0 .2rem rgba(158, 158, 158, .5); + box-shadow: 0 0 0 .2rem rgba(158, 158, 158, .5); + border: 1px solid #616161; + color: #212121 +} + +.introjs-button:active { + outline: 0; + text-decoration: none; + background-color: #e0e0e0; + border-color: #9e9e9e; + color: #212121 +} + +.introjs-button::-moz-focus-inner { + padding: 0; + border: 0 +} + +.introjs-skipbutton { + -webkit-box-sizing: content-box; + box-sizing: content-box; + color: #616161; + float: right; + font-size: 20px; + cursor: pointer; + font-weight: 700; + line-height: 1; + text-align: center; + padding: 7px 10px +} + +.introjs-skipbutton:focus, +.introjs-skipbutton:hover { + color: #212121; + outline: 0; + text-decoration: none +} + +.introjs-prevbutton { + float: left +} + +.introjs-nextbutton { + float: right +} + +.introjs-disabled { + color: #9e9e9e; + border-color: #bdbdbd; + -webkit-box-shadow: none; + box-shadow: none; + cursor: default; + background-color: #f4f4f4; + background-image: none; + text-decoration: none +} + +.introjs-disabled:focus, +.introjs-disabled:hover { + color: #9e9e9e; + border-color: #bdbdbd; + -webkit-box-shadow: none; + box-shadow: none; + cursor: default; + background-color: #f4f4f4; + background-image: none; + text-decoration: none +} + +.introjs-hidden { + display: none +} + +.introjs-bullets { + text-align: center; + padding-top: 10px; + padding-bottom: 10px +} + +.introjs-bullets ul { + -webkit-box-sizing: content-box; + box-sizing: content-box; + clear: both; + margin: 0 auto 0; + padding: 0; + display: inline-block +} + +.introjs-bullets ul li { + -webkit-box-sizing: content-box; + box-sizing: content-box; + list-style: none; + float: left; + margin: 0 2px +} + +.introjs-bullets ul li a { + -webkit-transition: width .1s ease-in; + -o-transition: width .1s ease-in; + transition: width .1s ease-in; + -webkit-box-sizing: content-box; + box-sizing: content-box; + display: block; + width: 6px; + height: 6px; + background: #ccc; + border-radius: 10px; + text-decoration: none; + cursor: pointer +} + +.introjs-bullets ul li a:focus, +.introjs-bullets ul li a:hover { + width: 15px; + background: #999; + text-decoration: none; + outline: 0 +} + +.introjs-bullets ul li a.active { + width: 15px; + background: #999 +} + +.introjs-progress { + -webkit-box-sizing: content-box; + box-sizing: content-box; + overflow: hidden; + height: 10px; + margin: 10px; + border-radius: 4px; + background-color: #e0e0e0 +} + +.introjs-progressbar { + -webkit-box-sizing: content-box; + box-sizing: content-box; + float: left; + width: 0%; + height: 100%; + font-size: 10px; + line-height: 10px; + text-align: center; + background-color: #08c +} + +.introjsFloatingElement { + position: absolute; + height: 0; + width: 0; + left: 50%; + top: 50% +} + +.introjs-fixedTooltip { + position: fixed +} + +.introjs-hint { + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: absolute; + background: 0 0; + width: 20px; + height: 15px; + cursor: pointer +} + +.introjs-hint:focus { + border: 0; + outline: 0 +} + +.introjs-hint:hover>.introjs-hint-pulse { + background-color: rgba(60, 60, 60, .57) +} + +.introjs-hidehint { + display: none +} + +.introjs-fixedhint { + position: fixed +} + +@-webkit-keyframes introjspulse { + 0% { + -webkit-transform: scale(.95); + transform: scale(.95); + -webkit-box-shadow: 0 0 0 0 rgba(0, 0, 0, .7); + box-shadow: 0 0 0 0 rgba(0, 0, 0, .7) + } + + 70% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-box-shadow: 0 0 0 10px transparent; + box-shadow: 0 0 0 10px transparent + } + + 100% { + -webkit-transform: scale(.95); + transform: scale(.95); + -webkit-box-shadow: 0 0 0 0 transparent; + box-shadow: 0 0 0 0 transparent + } +} + +@keyframes introjspulse { + 0% { + -webkit-transform: scale(.95); + transform: scale(.95); + -webkit-box-shadow: 0 0 0 0 rgba(0, 0, 0, .7); + box-shadow: 0 0 0 0 rgba(0, 0, 0, .7) + } + + 70% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-box-shadow: 0 0 0 10px transparent; + box-shadow: 0 0 0 10px transparent + } + + 100% { + -webkit-transform: scale(.95); + transform: scale(.95); + -webkit-box-shadow: 0 0 0 0 transparent; + box-shadow: 0 0 0 0 transparent + } +} + +.introjs-hint-pulse { + -webkit-box-sizing: content-box; + box-sizing: content-box; + width: 15px; + height: 15px; + border-radius: 30px; + background-color: rgba(136, 136, 136, .24); + z-index: 10; + position: absolute; + -webkit-transition: all .2s ease-out; + -o-transition: all .2s ease-out; + transition: all .2s ease-out; + -webkit-animation: introjspulse 2s infinite; + animation: introjspulse 2s infinite +} + +.introjs-hint-no-anim .introjs-hint-pulse { + -webkit-animation: none; + animation: none +} + +.introjs-hint-dot { + -webkit-box-sizing: content-box; + box-sizing: content-box; + background: 0 0; + border-radius: 60px; + height: 50px; + width: 50px; + position: absolute; + top: -18px; + left: -18px; + z-index: 1; + opacity: 0 +} + +/*# sourceMappingURL=introjs.css.map */ \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/add.svg b/activities/3DVolume.activity/icons/add.svg new file mode 100644 index 000000000..2aad05f62 --- /dev/null +++ b/activities/3DVolume.activity/icons/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/clear.svg b/activities/3DVolume.activity/icons/clear.svg new file mode 100644 index 000000000..bf2a7ac7d --- /dev/null +++ b/activities/3DVolume.activity/icons/clear.svg @@ -0,0 +1,6 @@ + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/cube.svg b/activities/3DVolume.activity/icons/cube.svg new file mode 100644 index 000000000..0aceb06d7 --- /dev/null +++ b/activities/3DVolume.activity/icons/cube.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/cube_solid.svg b/activities/3DVolume.activity/icons/cube_solid.svg new file mode 100644 index 000000000..873cdf2b9 --- /dev/null +++ b/activities/3DVolume.activity/icons/cube_solid.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/dialog-cancel.svg b/activities/3DVolume.activity/icons/dialog-cancel.svg new file mode 100644 index 000000000..dab4ae2d9 --- /dev/null +++ b/activities/3DVolume.activity/icons/dialog-cancel.svg @@ -0,0 +1,6 @@ + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/dialog-ok.svg b/activities/3DVolume.activity/icons/dialog-ok.svg new file mode 100644 index 000000000..69e5a2a13 --- /dev/null +++ b/activities/3DVolume.activity/icons/dialog-ok.svg @@ -0,0 +1,6 @@ + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/dodeca.svg b/activities/3DVolume.activity/icons/dodeca.svg new file mode 100644 index 000000000..780771ccc --- /dev/null +++ b/activities/3DVolume.activity/icons/dodeca.svg @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/down-arrow.svg b/activities/3DVolume.activity/icons/down-arrow.svg new file mode 100644 index 000000000..49b67a6d3 --- /dev/null +++ b/activities/3DVolume.activity/icons/down-arrow.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/glass.svg b/activities/3DVolume.activity/icons/glass.svg new file mode 100644 index 000000000..6b6cb0679 --- /dev/null +++ b/activities/3DVolume.activity/icons/glass.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/go-left.svg b/activities/3DVolume.activity/icons/go-left.svg new file mode 100644 index 000000000..a66530b8e --- /dev/null +++ b/activities/3DVolume.activity/icons/go-left.svg @@ -0,0 +1,8 @@ + + +]> + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/go-right.svg b/activities/3DVolume.activity/icons/go-right.svg new file mode 100644 index 000000000..90a2d98d3 --- /dev/null +++ b/activities/3DVolume.activity/icons/go-right.svg @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/help.svg b/activities/3DVolume.activity/icons/help.svg new file mode 100644 index 000000000..3ebda0a2a --- /dev/null +++ b/activities/3DVolume.activity/icons/help.svg @@ -0,0 +1,9 @@ + + +]> + + + + + diff --git a/activities/3DVolume.activity/icons/icosa.svg b/activities/3DVolume.activity/icons/icosa.svg new file mode 100644 index 000000000..cb4f85551 --- /dev/null +++ b/activities/3DVolume.activity/icons/icosa.svg @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/image.svg b/activities/3DVolume.activity/icons/image.svg new file mode 100644 index 000000000..3137f7164 --- /dev/null +++ b/activities/3DVolume.activity/icons/image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/insert-picture.svg b/activities/3DVolume.activity/icons/insert-picture.svg new file mode 100644 index 000000000..b8d4cf42f --- /dev/null +++ b/activities/3DVolume.activity/icons/insert-picture.svg @@ -0,0 +1,40 @@ + + + +]> + + + + + + + + + + + + + + + + + + + diff --git a/activities/3DVolume.activity/icons/left-arrow.svg b/activities/3DVolume.activity/icons/left-arrow.svg new file mode 100644 index 000000000..4752e2a0c --- /dev/null +++ b/activities/3DVolume.activity/icons/left-arrow.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/minus.svg b/activities/3DVolume.activity/icons/minus.svg new file mode 100644 index 000000000..0ecba4fb5 --- /dev/null +++ b/activities/3DVolume.activity/icons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/number_off.svg b/activities/3DVolume.activity/icons/number_off.svg new file mode 100644 index 000000000..1e98ce9e8 --- /dev/null +++ b/activities/3DVolume.activity/icons/number_off.svg @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/number_on.svg b/activities/3DVolume.activity/icons/number_on.svg new file mode 100644 index 000000000..7db4931b2 --- /dev/null +++ b/activities/3DVolume.activity/icons/number_on.svg @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/octa.svg b/activities/3DVolume.activity/icons/octa.svg new file mode 100644 index 000000000..ce3f913df --- /dev/null +++ b/activities/3DVolume.activity/icons/octa.svg @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/restart.svg b/activities/3DVolume.activity/icons/restart.svg new file mode 100644 index 000000000..1fba0ec6e --- /dev/null +++ b/activities/3DVolume.activity/icons/restart.svg @@ -0,0 +1,6 @@ + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/right-arrow.svg b/activities/3DVolume.activity/icons/right-arrow.svg new file mode 100644 index 000000000..517724c35 --- /dev/null +++ b/activities/3DVolume.activity/icons/right-arrow.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/tess.png b/activities/3DVolume.activity/icons/tess.png new file mode 100644 index 0000000000000000000000000000000000000000..9977b91516e954381bfe0979792a8bc758b25d0a GIT binary patch literal 799 zcmV+)1K|9LP)RCt{2o6T+$F%ZXnNq7g;idUg(ka!Cofm_S5&%&+c$OC|Y z@^NiTTk#kroPTJe-D=`^KRi25ym9|h)okLO8UJ@?JYL(*Jz+NhUIACYb@kbm0^m9D zo=toNUbuY)FbCcN%euqOfBAV?x0$`3D{a#0EfT_q2LZ!7|1;kG7k*oEo$2uR5l-u5jacX zVgb1+W$Wbvm%_k{L{I6w{!8IqmCX&lX@p=E?*rxprdd5S<<L3}`o6+r_mx>z`&6ywV+cb(-h;0iT4~@?rnV~#$+-4i_Q?&i8>BkykZuHyME~_ePI*O;qds_(j zE(rLf>4zBs+L~bKl-t5K(;Mk&+gH>mh?*7wEw0^!Md-WEP?;nF9my>^S!SOEBZjvI zQN!hAl3)(^JrR((2Ydy-7Va0~n%&`zz-lmOc6`FYoG9Xe*AH3Cb0W&}{`YjMKvw@|#w z+Hlj*C}6EGx23VAlp&4NgwVE@#*!_Lit8*FM=!UqJnkc_ + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/throw.svg b/activities/3DVolume.activity/icons/throw.svg new file mode 100644 index 000000000..673fb179a --- /dev/null +++ b/activities/3DVolume.activity/icons/throw.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/up-arrow.svg b/activities/3DVolume.activity/icons/up-arrow.svg new file mode 100644 index 000000000..acaa8de38 --- /dev/null +++ b/activities/3DVolume.activity/icons/up-arrow.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/zoom-in.svg b/activities/3DVolume.activity/icons/zoom-in.svg new file mode 100644 index 000000000..e9ec68313 --- /dev/null +++ b/activities/3DVolume.activity/icons/zoom-in.svg @@ -0,0 +1,17 @@ + + + + + zoom-in + Created with Sketch Beta. + + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/zoom-out.svg b/activities/3DVolume.activity/icons/zoom-out.svg new file mode 100644 index 000000000..f833de1bf --- /dev/null +++ b/activities/3DVolume.activity/icons/zoom-out.svg @@ -0,0 +1,17 @@ + + + + + zoom-out + Created with Sketch Beta. + + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/images/1.png b/activities/3DVolume.activity/images/1.png new file mode 100644 index 0000000000000000000000000000000000000000..e838f2420fc6e7a4e115b64eb636455354746f30 GIT binary patch literal 15332 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 + + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/images/2.png b/activities/3DVolume.activity/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..4462d6b8fdf9140036b09e05981401dcaad09566 GIT binary patch literal 18232 zcmX_ocT`i&^Y=|c@1aW(2@$0uRXS1>q$!G01R_Y2s`Q>HB{UJFC`gg|hzdj@NDl%c z(m^1CKm-A421Fr9NPd^^Iq&;NPIB(f?#`W^ot>TeOj52`U*cpFVFLhw)7;F&7672& zPbk2`1b+RA`0@|@Vhu8L2nGPI?&A-nFYv!R;KLIkruHG%0zE^*Z{PO-!o$NAeFA)g z-ERkZCYZ?NJ&Xak0+Y}zo|R3k3HVCHm?+@ z?iQ-<#&)_a+~nbppKcx=eD1q4sKtpj8&ijEgm^lm&MTMgj?|vu;8>@=W3-2PuT#C8 zIU)Z-52O7F4+7!?6-T++IOQ1yp=sLm=d%idp)@VnQ^r9C?R8oYBDtYSPK zg{I^@0BQQwVNO76h^H^gYcs6lP9S8PUyhj}6P5`{(@Wz`$pA@WeE|*F-p}kUG_BaQ zUp5c0fn@^Ruq8~svmCF?c}~D&-k+!bOudAh66K$VMD zq9KRSLzGb5QHxS_*ydQ*76C?xCB8~SJ6cnfZ7Fr##LHpXWV)^02WpFHp-?WaFjR? zqwp)wGz(_w4grd=I8MuZZ%kw@U_ifoMpXWBggAH!Oce^V0c>%Fed0YHj{{&ICnd&qxxMC?!OG)0cW9Zs8k$ zrqEbq82`2~?Tm`))%6i60v!9tu-tepz>9Q=fEf73{^ zoGS`wb}NlPK|1x*719Qw1g$q#dZpsE2wJ?}s#(xII6ANS2D0`K zOXdIds}(>iYE_baYXBf4>5=Jtx#Ji*ySWM(AiylU=i8~VQ-{wabS+~?%v})fXv)Hr7Z&VcyRKu&oUB4h)hII`{{RQ*CvdKbLf6>p|;cj?0`8juiY1% zn5=Rl+HRdTY*1oMw(`0iyD2aot3&_Bs^H~<+(p~CWu86_1!uW^7N1UTo=;>+t9RYa zbQhxP;8G&8J6{k^z82%zB@d4o_c^KEf#ju9+KKgveSM7v&7+WMmCfZ3i1E^W&0CxtUB%nwzm8Cb!~ zbX(3NQX&H9r4j_e>%PV&bE50qS{EZFxN)m>-Y)2zZ!Lswii97o3ikfwd5$*_rQR_X zF|KdeE$nLS^PYko!lwj(CEmm#bpE!%C$~X2zm@H&6enk-YavwV#dPGx&>hsPo;QlN zs6!N=OB;7BQH8zmOZ6<3w=j!nJForgi5FK8YMe<(rtBJy(YuYi=BWz25c#rb4jFcY zyUAM8M}MCxV}YL7@D9u}@&-CE#jMi(Fv8BygN_^)}Dt8iMg$Z@*$Vv`D-1_xu+n_k@ zX%o7AJBktTJcQJ9fjhz?ZXc2B6`Ee9@cq;Vs8H#$mT6lf;My1P zFuFu2^)XjN%43Ew-G9rSi2d@T7SmMEYYhwssSU8|3SVn@@L3UOCFYgePpA#oO3cow zqvZUDyXi6Ci%t{9)(WoG1W0VE^O1))i$rm;TqYHii;(zkdfWFRQvznKz_#YD5=x&| zL{tqP+f~c5FG~5v)RrMFYhk2QDnR>Ko=Y`xYmy97qOJ0c3^ImU&px*0EWEaraef9( zesO|^@EBu=fzK9pA5s@{pAoD45$z(E8pHoaGZHmJ4F^vI8L6BsMorm~!I zVZS;bvh{kd1?Gj1=v5Oof8ii4+Nx-sCDX;~08^RV+r*l%`JxG_V9dn0<_oOJ4`hwb zh!51U<(78il{rSWlcsh1Dc2`2UJsY~?lh)b{n-~#?Cw;J`f(qHS{LNcdddmJJ)|Fe zlImn1jpN0!?~oHBz0TpwO0b{UR&9rzt(92DB6v&oVFTM_bfl+ty%+W)U)K27#C59# zmn8o`(gJS{CY=`OCsFM@8D|icjT%8ZwAEL-?-YP}9mNu3k1b=Cz ziWj!e{q=a52@eiyduzijZ7w1l-}aW}yt>&yirM6IriR7%lLK;mn9v6e&-sT9Ns?3@ z2bV}bqSA4@-4^R0fgY20^CX0c(ML-ror!dps&msN65{_u3US--Vq+c;E%>SDlQIf| zRwP@eiw$s3F|+q|<4b8D35;A0zFlC|CZ4_(v^w|V_oqqkdlK;ei}9q()RExG3eNM& zX0K8T&k;2q&lkyL-eOQo^3i~2om9V4#Q4RtG~;2}q(tH9_qmj})}q@xhW^qB+q8F> zN=ga+?Qq1VBO>cSfmU>8Lmv8wZ4!k*u!$2<+y=dI~NI<;P-dBiP1_qdLG$!sRQsaTD58B}>gsC^+~`4%U*?79}Hq45b0 zmq{%;OHbH%NpxYgH5?*{Q^K?5cSY($|6}5VvwDkeiY><$5310i1>h_R!IHEhsR7I0 z8$WpAGBp_(b4rQQyTO|e8uJ)%qRVloJygO_DOmnFd(vs@NTEX|=d9EPHk9!4$$K3P z>18dh!euRuNx}@@|PA)y#4%&aIwqrvI)|_{p^0l=0cLM?8`oLO8A4< zznSVy-&gU%`{$~1qTNU@zZYE27Pg99&?`zD`cD8nCf|2ZnyQj47GW_bH z4~!s!G0q>u*BkyXfrP;C8$KnKd`5@aC#|F3O@{D%ao{z_J|E86qhK{ud+tq4)&(aslZtOsFdAqEbgs_>Q!Fz^r~d|1J6~UMNY}vf zz+d(i6Sck^>YkJ}V4c-ney6ZJgIp}XU3kf)!WJ}~;B$Y%-tEF_U6HfX!6gF^@sF@( z>PIK(yOIBWOUsKVkF8PSwO;%VM4^Z)aQMp#2*I6FGHqO`%m+|Dfq)N&CZ#&jo+?4nek}B}%97)~}!X$S>BmeG$&XxukH)Me!jP;v_ zt+OxiWQV*U`Fob&&sXG(=Aw3D8+>4UEMS|SD6k6SMnzwW*xz^@$w-0YPu#e=a?thOXRa0Zy(GHhmUaUF@M!6Ubes8zv+F*HFHwl{ zbkF?qM-cO6v7560l}Bc9(a^z{q4+~**ThQ8D5q3qi14;D?MNLz$k<`3B?zASa|!YB zR)s?UeahRZUR)shsB#O7x7@C{9>(~qtF!K7u>-LXuFzlLDtkwMIf6gP`e%|%fiy{r zDyr?T4+ja=F5WK8|9K*wXxSqu%)8cKy+bbd*_GY9amA+y6E9Ug)b9Q9?duv=iqhvD zi;7RpCkm;j$L*xHPtlH4{fF7-lugGt+S21;)#h^el!sgk&6H2eQY!1 z(+Bb0RsGj~3MId8U|hBzKu1nlEdJPOARAi^D?DZYveM2&z!ws{4HEJmU4EC* zL!YPjJ#0$tSurCiP!G#&qiIl%^|gVV&lC>Yk+M~>^lyycrZ3GPQCi4ut`DOD(q!vq zLPTHx%e`#+M~cRZxx^fpE7seg-2LKvp$THlP|HQ8CW;30%e+1ry-j}Tb3c$G-QNeH zXu1?o^h z@e9{}_q#Qqt_Z|J=^Zx%SKJ;x{ctEo%diV{sY1R|G9M}!U5~RHG(r^JIvu*fZ26&+ zJXtuB5SU#rlG2JPca1dXJ!GUES?~1AH?)UO_Oz*NnQV$U*iDPnu3Lvw zWkSCkLEFqu4W_iXKwh_cv2Z8lAa>x1syDYxt$Y2~#(8jrkt5zLnCaDgV?$t)7xJO4g(!oTv%N<7gg52Zk*7ze8lMYw^KM)ETXHG zrN5Vj#D%H|m$W^r<0h)Fx9N<%6QHHccfPmvv9=nN9o<^A5s@E9ESQ}yFOT{yoJD?* zRQF{F_|Q2b(>rMU$J5YG4X`1B!}t3V)ti*9KXhJQAA+A~NamlI-X;fL#;G#e^SylF z4*veTLgAPx55U!y?imVZNG3L%nVGuzQX#R(`*fGygZTJYXQsWzg#QxJ5ohS)`4^(i zb~vPe-Hgwn?%u9ov;NpwH~B;Zb)9J|i%^TQE=cY(AnMDDRa+5anJKfjuYRG zqRW0F$Ba}K)8diG-yMz_R|z|*Dpi9gjb|%l>Nr2-?s!avU)lwnnrq&e60%d z%Np2-OZ%74-Iulvdsy%LAl&uUndpiGl20I}FDi%%bz-^rc}vFxpNZ!m<{oo`cr>w4 z8pGkC{~k}%@3AX0)8SXf8_G;kHKH?G@Zm-HH5ob6gX7a^PL*neY1p) zub?bMA8-cMhQqU)TT{Y4KEEF9xzXx`wz}`cA`eRfx^aDOzU4#rTAY|RlN7|Hmj61D zq~=Y(d~YX0y_)~Y(aK9I)(mIkZ(JB;zn1vP*6(2<*)4M|tWR;fQDpw0^WfBWEU9Za zzIuQ4_nuf(mF^(&JqRbn2k=hj09} zkfCz5ca?+A?pek-LiFn{%@?)^RQ?R;=`1-t1ifGUCC-yc0+A_}XXS`mYU#8WUtg%I+mqfAhveTOQ`ug8ZJ9mp)7(~-64BPP9*8AW*&v@ zJ|oI|koP!>+Cis{fA}6{A6MaE_A#8*J*M-YonPM>nYyK8a#aDMoHzTy46l<(U#qk6 zN*(AVrBGf+l?xTAuYkSwS7Fz|tg;jf|tDnAO z5`FQQatu01=q#xmvN6)MWEk%2oHM>d2T$e1-?yoU_Tw>U(vl)-0SS3?N6c*pwFLf; z=P3K{8k!%h(^9EW+E$51C;PmtEwUSP8hnn7lns$emm?%!F7P$he84{}06tV}e#Rdi znQxLFF4($a<@fP@&h%gKam5MIw-9{?Gq$Y zZQsa3Xo=JH^VC+8{LmY0+nDS-l3)E&WP9%+Xh*N-8=$kvf!W@ijM5}}`9zppdjfXvOgxZ z*|{d+ohryi@gVG?$LL9qF%I2y`u=qrnhn#IWbZjFWv_v$Rk3*k!!#Z(fuU2Gt|iaW#=y6 z<=>eLaOT>~0k`vQj?*2cn18zD*T3L~egn7W(~8blw1pu%F3d|2o!MUwZ4gXBBP98$ zLQdxO0pa1iDe}vzdQ2z`4TB11GyBu7#YIO8T}cj%7UFp!;a|p|h{sIe^&S6i=K6Pz&3Qch)`0P-L(I zK@j9@^f)+<#p+?&_26l71kZU>vlZI1v!T)9-#zYsBEV+u1xbf-pp=T2uASQrVDgL< zf;aoNv74D?N)9ODBZ2l|t2KXM)KH>{lg(QusQTDe6Rm4+MhRVkPDcto9=$QoXtom2 z_v=@$+sHiaLLdx1gi(L@@-*N6i)nfdijNHC{YkLq&mLwcq=5|agh+(RR74Vt!QaZf z!qXE5Km7roiMn_!IWPT-moZ3A(cI1pho6Q|Ea`CqHIn$7!_hGuqq9Kiv=b9!Xl6Jh zJ1wJ~-OP}kp0JArW#SE7;lw!yN3~Vuf9E0~Nakb&Qs`XKE%KgXsmaNG#Z#CukVnPh zQ?-dTcROICMz^A!3@mT)sZ_*mfNwy!HqV&z$pi%+L+T>w?a>O$_=NW^SLcdP&X=zH z0QCZ~uf?t#;@%9&|6RMJ8x0IH&u=T1nYcN51STBwatQK*0_r{XJHJi2dcZ-3k3IwnH z7ElT&N|rc2z-|eqe^IJ>QX^Q&7FY$&q!ul7sySs%q(N%#WO&+WQ9rvwV<#gtX5ux6Zo*Pt#}FoE|fb$L+F%XH})#&0SEv3&T_iUg$$_iOuI~-Q*Wuz9BV~0xYei& zp0l!c`FT7RUI!qzv5tlU$_4Ma-aZv^9*-_Of8${xY!FMBR@wp4zv~CC&8?d>A4|TgqO1T24ENt z)NC-%GyZw;sj=73skFZpE7JbU8T}qh<043NC4R9034C>f3d7-g?}gqEv|_VoCXZz@ z(|F}W3aFBw#n9?0s{({Dk{ub*GkL)8t7xdR?L$Uf_TvUO&?rz^P{lJ+2>7DvU>&M} z@X}2Sw}}*AcOF)9BuQu9G-M}sF^WmJ@FBPo;9^1+-DK_!(ND17;{sfE6b;$F>+mn- zwnk&v+tbeC`jU~G{|^grkMI%Px7%f47f-MsYdYpUGjOeq{jt$cz?!Kz`1SYGBxJ}X zS&K{2LXRJVDlagRIpNI=oKt^$$CoSUe3<0$tQ-MOkWZ$QWwrW2lc)y#SNpWqYK#l0 ziV!-Ls3J}jia_?!N6w(zQ#R0UT0AcN%oBPBFo>Hd)xNOElR-hL)sd1S8MIT2rwYk|Fd-Hlt-}ep4q98&O#Qk z+@;n3S}txTtjBhkXCvC5Ar5AF==%1)#hZ?lK`8%qi#(obCFstW1VtEs;nIx0CgXW- z;m51YJl0@v^od%Z=QuBKn)GI;E$1r=J5M)H@7(?Hi{%#AVCi0* z1|LN#2)QGF!AWCj$6x_zW$7enp&`Av!0l_UGfL!mZLHf_Qw{FZt7`;}pUnc^03L#e zY4jCiIM=9$yEw0ZO1Cq+i+j0vckZ3N_lCx-EHK0lZ@rk|v=u+#%2ywzdN0HA@8{ev zzFJ|or}cSzr+}F0R;>0na0~JqSvp9+{dOT#jwg(MSSKn#=ITG|naSK<0WN#SsrTZvQYk8|BxE@pW52pXrD zT4u;0VrPXY7_jXKG#4^CPk#X%0Sru0H?Nj!`1NYQQx@zuzec8oOUmflG+EhI{9bVz z*!+mPBBcR2!?Zj0Mk7nb9!6nS9&de|qj;>|URw18aLbvh1NMu~Xl#WB%lnhWey~;0 zz0fixr0Gj5T7)xw4Q?o(A&b!Sh15d!_7#!nS7T1*GF@BU%o#)W*(wM>nW59BylmyY-4m&0T6&*qaM& z7Q^J;bd>I?}Boi<} zK3B!)VE(qT_Y-bny>c~o_y#g3S1$fxF*F(X{V3gcgo&7!zr1*rL-bVXhfPCs2*Rg8wmeUB+7_XPVdg<1rS&-E~px8tv#nCSeVUCZkAsb+5SBO)j z(4x2W9#JZ9x~%f42+r4zqpSrs$}PO&oM4vnI$7aou;ye3rO7=|yKTi8YZA!0_!U*x znDt(|_4Tqd+KjaU%zC@@D8|_BGd4_hCcr1u?_Q7s9V7wP*IQuwk9n9bgv#Ag&J@JB zeT|H$P@CETJszzL!Y#5aFV-jIv@LQ8{R%yc@qg|sle$B=)SmX(7W4|#n@&7kbv6a@ z45zXOulOk)j=a$HX(`|k} z7eLun#Bg;j^-g>_7;d?H?#Qg@tK}{56cxFN^fg8+x$#k%w5DzE z44^ZC_+y@wsw7^jLJDwWJHJ_e-o8UT!_yUe zGuv|+Z%wkt+2Z~dW;j#(K!8Xt|3u@O9X1%K&kBbiPG~W;ZU-(qn92bncQ226^6aRJ zICUxIg`Q7+nuf`%;8e=S_)sLc8$CB_6En{mUTCb!$jwwVaZ_V>&tsoJ-{JS2skh@X zO)X7)&-A;yB_4gic(!ILePj0(k4#k@!3*s`kuW~DYDFpqVF1~c3fl&XcZYODfw0)d zvsJ5Jv*7WPJ$jD7KteQBn~ID;TVdgmLWprw%jt^n+P}`s0G~M9%Y3|cuDu`Ew{)xR z*}>*K;t{NHQ}b&pPga0ii*$SNn`v-wiax}xQf*+D{^jwupC))=_;8nU5vF_->T5;5 zvD5t_=k%c1m$*tt5%d$Z1jHTG?}su3=FvrcTfcM8PomnUB(U@!362ww_6+hDzp_RE z^rdq7W~dXdL&c{go@r6w@b4eVi$bOK$ggjj0*#Sg4>1yMbyblPLLQ%DNX9805V%3~ zV*DVo|G#2=6WDebB=wd0%&Jo=$*6ESXd`E{kyUf~$bd8MheOf(2XF82*2k9x8dgMTbkprGXLq*(YK$pOW4q%0#vv(S;Ih3t=F$#%)}x=3kOYc2>-=l0TL!#wpaz7Lz<_PC zwrIC8tflhocd^8|s`<&*$Gj1o=NV_2B9F0sU@}87ZQTTw>l?iZ14c~@&FkQ^&15E2 zLZHl;Z?}fg4D|gmtWSPx3%umoO#XJg%jCGz>3r4qS8chZ$qiwqTWRqIipIZA)rOau zj$?h6>P(SOAM#D8jwTvfypgc1NE5H$A8S7h+I+?!mS4YR>NX`|pH9c!^ga1&&G^qB z7W6!AfKi=gHpvc|(v8z}X?S}5NDxd$;UTr)av*J5Z$Cua>wcM7kO%R$Bmlf1$gV;( zZ#U6TH!l-P>Q|{kZk-7xHAuXjyoC90*GK-5-39Z3IiGz28Z(_H5_}Tb(Qkuz(c9oM ze4lrI%@6PtDE;a9j^F96U4^DqPvhO2V=4mYpS*X8=I#HWyjidmYdKYL;&05SLbfXr z*XeB6&x2eTUt0;kf?-_D>Z|_1>Aq-IN`1tW`s#H7Zx8H@( zl8&NsXB4{TS0$KZB=lB=93=6+R?flyxIV=H$VN`AA3V0bYejI~CKuwCnKnNtA;?pJ zWglJGD!WB+K-BCOSxd3ccxvOR-^YGcs!Ryo8-P@gsSx5`Cq>p~R3E6LPrfgyZPYNG<^$ua)v-fY| zK8d6z5y66Q&EHQp%jTPiL8Z*Z^@O8Wo^gR7QOkBtdE47(mf|_i^z-_?q+WNM+40ue zUTu)DFH>WieK_Ar7f^44=uFMEAntS0`tD_a6JIorF9#%$=7Kz^FH%>15FMDiE|Jjl z4YVo&_+|jbh?=>IY(CY)yig5N`B-!9be$G){n$AopfG_B#&u<3D}qI6eIetUf{PC| zVZrR%x3hl~@JQS$t5?{0b2H0h1#mUA*iTIeQK0pOWe-Y|NOTECilO;YAcv6 zFg0Ti(o$A&4o)@G?5_7s-9J~1ygB*1>qZk7fvsthvLKfVAw+|x`(P29*iUB@$T!B# zgspJp32-~S)hed^`j4^QGccWwmG+^*-$E*a3P8ldzUqi2)Y@$JF=D)0bjM)o$(<7? z+SU*KAo}*%@)y~Qa=s?-klkGz&~L(oXX|*gi0Op+>V5r{CqEV%bzzMI`K*n8qRA}>kG1)~3meZ!K~i{SBg+EJJJ56f z0$wgo)jJSy;K^zVVp_3Ug_N){zg}rzWs!+`Nyl6@m(p=PvWcisxh@x8)@q9|onW!- zqEjEM#gCLKM1@|wD1q1zD+WWC0pQItdAtfn5!q>XLE19n6jk;x@*?G$#wol0Xkwmf zUhY4>g)*_ZU5zLyaN7lZr0Zs>?cy~5-2a;oB(4^D5T+c|lI{fXQ4ehjEE({u^Rfuu z&v>Hxhr?T0YB#?eVkYDt@1F59*n6r5(3vS9);61XmJGEmfW~yUvN)KE;#(IvNT#F{ zeyi8swAt&Gbl1Xh2k-jmKJ3b(^f8<92DgF!%eNl&c9PFdev&6$CY^0a^|nh#469%C zgdVLQjrvF<6c}sGcRKe~07?!RXtD=y8x<;3AE$JhS))UC%{SX5m;d@K>h|5rnCrcd z>EE!~Gu!Q&=t%toY#V)!Pfl(e{a}b& z+g3D`^%v7Hrr!Uv^uli#axp^aYBDRK+lR&|Id4icMuU_iWr-NKx1+59+>bLSdQ7+M ztvA!~zG9hme&vx+f-rE)`g!ND2IHe$MWefTLMxDE-3si(+}qi+zRWX&o}P*LjM>h= z1vW`0pbkQ3+a~X9FMwglAs6sIT#;sl+nsj=eZn~r?7qmh&F19fbt8pK{|*i5#cf*< zu`TC>FL%ITA7aigrJc3%Qp!DTF5*}4VlS{>Fv}ewPCt?L_s6}(Thte+o}>j={>tc% zl%6U$=;L-=v#H|qj!nH14$Pnz82^PY`+Jchz|1g4gbp=ay{?Ujs2= zWJHT-n!vK2cFh@^^0}{W+s4$x)UrQ0I*;wuAS2%wy#9Zd^k;kOo96=MTj*<`&Q3=4 zfN$&~#<95ya{5CJB}YcAhe++QlsIT;U8l`3DSMNas7Pr=YSm@_Ig`loR`p$}Xsmm>pI zRX7|mH@L582vGRz6Tvu&F7(@mvx#H%cY_p!)e7Lt7%0HHEKfbemW2#SW1B+JP1|I4 zpZoW>Z`>Q@u z{*c*6CGmN!dgPqjPe2e=H`RL=snNQBl5A9QX~eD0b!*ufdwpgVJq?Da>e(@T?P+Ff z7DNVutq+sShxm|O=`_q6W6gAi35IVE;tj6WJX__lnFW!n@dLPn`^|Gc!~fI?wKM(R z#t+5*_T`-)em7SUCQkfGj{_&jrcJk^*?bw|tlH#gZ?>KNc&=TxBC=uT%U?Gf+yQYx zrBlcFo07AW{c4-c4=5CQTI?zRDqDCmTp_z z06mm)(Wn$_qDfRf2!H&D(D~i?$At!Wl>AgTe)`WZ)yq^LDcb&!`xJMCFg=b3k<1uS z=$*4%w8ain7zxR+99e8Fhi+POwll z@kRCF^s9^g0|aYq^-+DON86#%j65Tr?VHBfffmix1Z(**uPpaH2NR8bHg=$uTc+2F z8Q=Z<(LL90T;uQdLt`ve&2OT|M**clbG?YYb@IJG(jbG7MR3K%%C#M4+dlusw)u#^ zt7M1FEvdrp^vVQ1LZT-od_IIVW&8*{rO~x9O<#;J=O;AZ>pxzKz6 zb=#bq-2ZN;!E$ex7c~=xiF$ z>}vG2{Fu{VJXtN4i7M`@g>TI#^7lt?OXMbgenKd%ACf}Yn!|8uo%@k`*JTf97S_Lk z*hxOwc#dxx2!vh83EJxJm4j$vl}!FUf*ZB7;>v-^|4?{2yE_J%$)9;q@9KxH=^OOW z)7Ww=i8`+ki0nZZqUppj+;4#sg!?rx;k%Od^95ulFrg3ekFjRW8BmvQI#pb@?&>4f z^yUZynydXMU7&%%UlN{mf1UYT8a-0y&b&}VqAW?`7=pOF^glK)8-O5daTU!3|KhvC z5B-}qjzHL=&;QVew}yM@y5cIqxKQH?u96-)JIAvdsFG3zB*K>K@M>)nArB_OK4T;> z!SXGco-eEt2rW$ZL&CE@Wt7M01kW4ayD^(FB}i2AA}guhk-j}Jkcz!NH$wm`j`EE? z&&{lBKCN4#CngPddpD!tFYy?{LrO`e@v5Ap=*xA-dQSef&M%|QY|(>~2wU|Gf;y!n z*?84w)9xaxcevj^lB}c|(x9D#y?*uCjacU_EsGZyLZ+VCYC>;=)+v`Gzp^m(B_^}H%-YAE^htZYv(gtB+oYU74 zh+kn0E2Q|iAw+n2Q}caIk*68-{1Q`7$ZUeIEF7*mdt0tFs^sRoPhu#)_f^?Q>z+>7 zo!k?IJP?;_f($Q&)_srHACj#GsbBV09s_t)IEiQNEmE*Qyg0VmXsctd@0)`iqriOp z;xX{og76;*%0Ohd|Hb6*7{q(#3gM>5D}k_Hm+tLf{0<%}_2%_cApCdO{&Q2a&2$4K zn!#6rwJcqfDRk&M>fLc)-uaJ2Zb9;{N8W*L{Zuxp06n`+7IRop{}YDB@=t_;K(3NA z?*rI#gQhc7ghm{iz@b-B3i=^J=HlOfDtW-L_RO4YN_hqFCapoa-}mEl?Z zd%c3nyZTC=&u;iVmV%#$(Yc<4f+(}Z<3?>UlgRx2t%7$TCi=&c8&$_@%Ez>oB^8^w zgz2S}FuxlfF$PV@!1bQ{$-b>x4w!- z@2;65oj3`JloImqPoe~-3yxOb#7xvL)(l{L(HIb4Zt}zVA1og9UGp!^pkW`>3L_46 zY2a?rWk~0k@IxSg*NmVRA1Ks4mjJOeEF$R6#f6u@KM#0M8p2uK*-^Z-)yB5_9w3Mv zae_q`8^8Kl?j7dnVZ(Fm^~w31cuYxcq_8l5#d1Z^McX%7esV#MkMsDlYP6o*ZChzV zD3#~ZE(cz#Q7?0T^d@Cg$z&Ho3^iliywrFRKY(M^EjLmt#^y|! zk*ujBHzPe+_wY}ze|kw!q3XC6A8_wk+L%CMw+~?_Qffu*H6S5^=;vxBi)BQ1-PJVW$aD^k}P6nN$tKL-Qa1AmF{_gx+t4 z`g{MR`LkI3*>n@>k)NcRP*227g)fG3CqxqW`VqF`XJCQf<7qpe-VsFB3a@;=1FLDg zXt;liG2Lt~6=8d)8!fkOun^&asE=E}kz*^uZrRoOB+5h9Q4g%>+pCx{xL@_+Qt?}n zLP*`xf}I#v2zdWqe_HU4SWdE<5Rf+$-`PkoEJR?p!cZmqUXjnDy~oHgJ@12TzCMJ zhUM2hM0O@8+IPnwFv1HSWcV4Mld6-mz9l+(P0&q_MWYfdIO#W&!k4*Dl5Z*Y-B8z+ z1@FjDmvE;;+I2$VzfaX$wXE=G5%7#BKhUXH5*pk$#IOv(+M}5GRYjvb)VtKfYM(1` zbAMo40VP9QO+-|{C@8QNX07*d3^S#!RPL{4+O`SPO{Pbdp7mkYsry|H9c)9iaA za1DrKeVto%)wIHM0}#5_zBPFG*=_Uc8?zNIqq^yk5i!4PltpToK-T=f&D1&j_I`}Ul!NHjdgHk_a=fE? zgb*rsYMCd~HmjO};hj)Nw9SiEYV5Mz<}lXmwjZ`#a5XlFU+Wsq+;S)sk27=l3mfj) zd$q4`o%Z}D`rh%Qs*A7@Qv73nJtb)MZHxTW>kfQDOfB2f+12R38@`@dF&vP94|G(D z+td0L;d z{DJr$)&walR65y`{R2)yHY}Cq*r*1{wrrp2S0P#aRl}QIea-;Zt0?#8^b-yUB<&fe z3bP~>pQ~%nagA6SF471wHp_loW+mXXa(jct?{Qnd>8&?uTTelsY3VF}kZI56+aiKd zgX@UxKTQkt08VejG}TYlZ?uJlz9jw85{ftbwvK??Ge@w{AIeY3!vYu{83uBh-BW{J zf`Vx$1E1eOeb@)QP|9aR(#UaDoEO`6sNWVNoZB58h+@C>zp}Z@D8siaLMhYDM8|`9 zW|vJcn|o6GM7u2VtM3PwrB*iY?oN7Nv4)@My{yk)kMC~}>*XWGXAMm)5-)p~izMh-w?O=R3+)sy>O4$+y%>U$q%nJgHHk&U5r(8GN|D(CXV27frK z&(u_iM{qbb`wQK$i@E$t;t$w@GLFl3&Wzepek0_X9za%cgag z?R08|<`Kpj1FN4z?0=9ZUZ%M~^|9hgwpWhJMo;bK5xm;i;WL=(`U_o01ZP5Ft$^LM zvLCulsoutmspSR|xCyM@=98@?9G9i05Ka@tATp2o!Lm|aKsR{~np;2Br}$e=k)rcr zcED6ch{CWf1YQ9&j!R$lNVc1T)%+8>BnK2!LpVY#g7*kfW5>St$yW&FixS<;rO}P z2T-R)c)%hEzR!l+9P3=vmXnKTwS^GAqav zSTf%e$g?d@n)rFg@uENR6iQoJv@qyB;$7>qm7j7}C7f8iZzG!{eK}H-L z-{;z9Xcq_}foJ{H17i3i=39AKEy$yJ6DHu_03@Tej_b3ZuYk4VdzXahw&UXH-=d6E z4yS1slL|piw^s*NyT|3(I>7S2HeT}cO9BU2o?St7>BF1rGdWW&Y*eFOfev1`Gi>i8 zx+5Cy@Pd=V7*@T#Fx+Tn4OWvE-~mM_*FIRd{cR4bw^8!L8T-QmhQ6!AR47sZ0xSa0 zZWTh0K+O`21f0@dFP0!`}1# z1GXaEj%aeINEx+btA(Bb+=VvpsXRRbknXkueh3`pU5JPTa|iaE|5@)sq&W0TPr7>^ zI2t$t?eZlOyT`D}zK5WTzlbEzfK3#98rxh(QIt6HJoY}n?(&LAD$T%0ux)9*Pi%^! z^q#5M>3lqzQ` za6a1pTSRi!0(=~J1^*MFqEso9fOD~9P(;K{BQ{y^QT$Jgic;o0j@^B0Mnt4w_62Uh z|3s-MWyvz&Hf)eBB2svJ1J_`yk9nU!6(z>BVoP;ziAV$~?xfYQ;y$)Os{0e)QUnF-L07wxu1MdaS z2988`1*DwXfPVtN2X4Y9$cnhp06>anJ>XPqzQ7L7wTOi90`^@0GGK;lB_dsD03b!S zC-#WoWMFOQT11kV1ze5Y=6~3^7Lgul03b1F0uI6k1IJ@;6o?c}D{v!l1#k;?%U`6h zGyss;3L0!uuL z5{Zol03zXxzy<_cclmP%G%%>w+rZ1%1%5)8KVJcsJJuyq3K{^2WN&?7)6R_R_Sb7T z)>Q;^fl0u`E`P4b>yCAal%56vBEE(JV}K2T(Vc%i7TXqi6n6Ju9qjeRm~>z};>`r! z=*(1LI`)43tDTweRiH@KX#gNnd=1zjU}Wc?BRVs@Gb{djAU2@rkGWpsFixk-Z2dZM! U5@58#N&o-=07*qoM6N<$f*(-5;{X5v literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/2.svg b/activities/3DVolume.activity/images/2.svg new file mode 100644 index 000000000..6c1eeaf5a --- /dev/null +++ b/activities/3DVolume.activity/images/2.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/images/3.png b/activities/3DVolume.activity/images/3.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a747f91cbc4aaca9a4d6c7c0cfba49c98a193a GIT binary patch literal 17334 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 literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/4.png b/activities/3DVolume.activity/images/4.png new file mode 100644 index 0000000000000000000000000000000000000000..7f594fef50263388c9f4e5e468b42d75a62c25cf GIT binary patch literal 15513 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 literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/5.png b/activities/3DVolume.activity/images/5.png new file mode 100644 index 0000000000000000000000000000000000000000..8b84d7cde52b7cf78d61a4eb4fd288ffaa660b73 GIT binary patch literal 16851 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 R0y^M92s2yLnoI7F{||P|6wd$v literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/8.png b/activities/3DVolume.activity/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..845302e342f84973f2904cffd575a521c213b17c GIT binary patch literal 19142 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 z zJ*_k{pk%4&Jv-FJ@L-v=-CwJv-kBv?wOxg8=)&#D{R|nUwYKv+f_RwUI*=siB@L z{tiWOW{M3oN&f|M|8SOam)hV#bMz#*%er0i@i5Nx_d|!?YDvbBGbNeNI|!i!5%SHI zUdel;nA0}|jNYccKyHK?=xT>QRkj^8qb)Z0En*WA$UIi{@my%si>Fo=9pi5*zYuTa zHF7~qi$g8_`mDw7jbH=~X_{*?fn0?;Elar(!ZmT!Qm~$SDu&vRH74P8MWM3Z|7%$r zpw@|_JQWe;H-pu^@HMCiUzomQIIhDw5Au4)g$5!N;|PTnjE$yk~Jw^Ittz%XuQ;Sl;R~37u)LmnKA#v z^eK_gwTFfB=r9Z?2gkKOnfOMpyA2B;i9_N$uPTYtKA8pg;<^-wMc|8TMfbrDq zI0fLcVd+%gdhSIy*XQCwMZc8S;0Co8nDlj-r;bhz%>u_&H=o)6hj_t@;0L0Vh6^hK6H>Vg^z9F2Z?^Y} z`R^R9KHC8uz%%yuMx~AhS|v5%c$3a9xG*s6sX}dgbnGU$dfz?fD9GfqXfaCS5$11z zez+LcE5b_K`(qI=MI5IGulB?T-mA#^iT2MS<0M>>R(h~vW!M9K*fX+Wkr4Sm@8ZjvA9BE@CRl3a$~q9<27QQ^kL$`>5<{5(KmizIyV?Lkn}FlM1Esrw2Qk3T zpPC%F-ZUqqQSSvMH#SwQF1%x65z2q3%nwja!ggL$OIJeV+zh_#iT`k~B6JrJg;e)E zR+=AcqSS3u19F2d3`sWJzvEBdiVM~lNsD|#oq!>C?wfxA%CL9(uzpXEm-N>2ZtChD zp;L379N1y_Dw%LfLdcP=M6u=#+LR}eJH?b7w0dFxhEC1LXGb%9;Jff z`ew5#akhVb4&l!MN^>q}RPU9H9YgS6$?$K2H*j{*LZbIeEFo-AXL~b3!M2ia!rPN7 zFYwKusN;88a4b5a`h6qAf&R(>E6ew4Uc>hfRZR1M+Z#Lh0vvA5zKc}#W7g)%eO-nS zZ~yO#;*{GZWW@#dzlXM@p6yipS@)0NtAajmyNZ9u@gqB!0LPWc=Tpa$wTUa6TQM z=_js#I(leHwOG?A@N*!$Bf58fqdD^!c)z`y2=XES5beRsLjOSn{Fkx55yeWKN`R^& zV;f2ow%4^pnhDD`x-Do*wN5%s<0zRI%Xe}LBaOKdC$Zwn-e-kWuwmP z@`t@y7RBh1hZ#T8IS@1|;9ChY-ARzt`u$)Qz8mxe_}0aNkPmYoDxpkov6yM^Vwl}; zrd8YZ?lC|tUxqMVogz=UY}0$t#_lFsC*%!KE|iy^rrQiq!9E>kC9G?~ETk*`i4lIr z3#kzZQ(H<_#Hg6)!~Q7Bz>6U}XlZBMW29g=rP3hTB27M|^c@e1-E?=^240rWaeKIF z7s;J`HC}t@neFPU)EEVJzU)Gd3Cds`bbokWI!X-jm66osY1OUzHR%i^VLN-Wyv$WsrTj=|4 zPfQ-_I^iGzmmKlp^E)tKO`d?JkE76(xM|s)IU%{Os$UV8f(11}X(B3E5=ALv5Co(L zFc1)Q933es3P`aGDo9l@sH2iZR79i%90@3GjM8CWcR+`6=9zi+`@MU8zp@tmy3YIT zLb}%a)^{M0YrSq}#RP5k_s5w-jYZ%cfn0&a#a^XOu0>4BXTziqF z`?y|(7~)N(>|SI(bVYOGAS5H5x)ji!-rHj~5qTotta{0&d@u)kPB{Su3XjN#AM%=K z0C*)-!G#G9z0{jlF<0_TRX{ERI+L78NNRW)gWE;HwZ}C0(KA~5a7QR#Qv@T;cOZ|C zYlxIP^VC_*&bU<&A_+oRt@Q%{2v~VO%0sQkZ3cr8InUVQ{acA z=x(J&l_AG}uTCvuXuR zv(3=dg%oNxEfhA)^l9YvM1A6n-J@01Pw0_!ZfI(n+gh3X;Z`SGnHS<#bFCo5Q2)*_ zO#H%{{#$=B&orumX%xvGLA;T`R=BHuK~FD5$b0)7xU8v#_8Z7c$xEW6BdjW7Tw=%c z1v5IMM4R>FZAZy%4wh~8M&Z)GjC#LIx;pQEd%IShbB_on=M}TmXa7T}vimvy zlS2O|S* zpKyrbRv*B3hc59=E_)AF8KLNVf7YFOa2>_J?zEnZYFEZ|$FT3whj|QDv%UruI$>>J zgBCrbj7r$J-<10ZY%|?<=)QxIT+c0-_vM!(6p;TIDdvB93Cb_}aY*(Z`;2RD>{-<( zsA&VYNWD5<`}>gjkO`Q=0#|CA&W)(X8>l~i@~ z6;02cb>s!j%-$eSE&j|rHy7U!><}NW_Q#iIcyj5l|8q(JXJ1Z*(jPac*Gz#L`n;Oz z3-lcrm0V&tK$zTuR!=K@Epl~leKfWyV|>4mLkJh^KAn6%^QkMhSI8Yaz*-3175b-) zw?Bv|!*cB1?Tki^O9WoKHS3&zGKWO)_2CP^>H_%-)FNI)H~YO4EYYsZ<@c0MM&{+0 z)&S6TutVBgcej7QWBc`}FB2j-sIL3tL87Ni&OQCszdU{a|C6Un?SNm6>H66Fp=%9H z@QFUt^T6&&FSA3=YP7m*Fw%m2ePv(7!ofCm8%eLJ;?B}rOk%LF>>^<0f+(>U!Ic2uGH-SFW7z+;9m)V{Hk8#!(N~&K|N~ z>bN^<@;(%7&o7$QdssHsik$aYfv;#~pq!>Pw+B%*)00=(KI9YjO&*#Sd+&x+gm=U@ z`9}+hc|_4^QO&q=Sb^>C(6Ek}hI=QVU?+uKc`0PSEVlp|(yhRKC)+rzA_q zv`=?zn|^gDai~Oav>C!po_RQsJNa?2M{~N+!p^kqhl#as#WI(mBtbbW-wm+Fvj!si ze44y%zqItiEL{e#Syu(x@84H>C4g za8C}kX-a?y-)33Fy`lXN-yCZ(ZiJAhf;Hp6Rt2-SE%0P0$-`>o8X`*E-2@VdC+=?h zBteMt>12srz?U z;HD~~H+hgab$s^Qv(JNbAM?(GkTMk|Ka*P+<=8k`ku zumtcy)kC)fw+GIMy7u9k7BK)uAJj&@4&`4u>zm>`KGg%9w;8Na;A(w=MuFU|bF#UM;CE94O#cf{VTD3Vf~X!(d}9 zzVVTuGmAzX>VIE471@x>83)*p`T-IE3mqzZ?#`k@?3qXey`$-O!nEtBj24|+wN*AB z^MV0oJ>P%+|E(8)z!!VFWu)uxbKnI@4QEIkq~8DuUmiGM120~ULLXWrGZnZ>klTBd z$B8Oh2zsLV>f}{jHm%GEyP{mx$V7Pf7ipn_2grx$q0W?c8?dc@5Tr*gXS4I6dbmrP z{6#0LNyYXOXZ-rf-uQAkz^|YW`XN1UNxsmM1%x=A8QvELN^ zpuF@EI-xbrSBK98nf0NtJo?Lu|1e?V;K7tp*5l?ohv|1th-QDKC zEhh5vMlE!}z%}{8BswYL6rL`PxGOgmog&En?IU<7>%(|pJV%goj8~oek|S&D2(yWx zIZj6b`1^U^*2D!e^nF-j%2C^h&Wi`I#GgN!LoNOTD63XQK`Izn*~s)*CUDw?{Ajc4 zzGvHA%HBpPmc=gFWDTuD1fW1WByjpQF`pPUb?KSjMI~NRilh~)9b^X2Dl%@0)6}IM z3moI&JWF}=KAfrSVdG`yCLX4^!TXIE;F*#N>`R^+b{3?*u6qG%{_mt?Ok4ghi-^-j5PSKr&PK0%=5z zzi>v88J+kcKI)60b0n#@wT)|Yn;xM$*rMJx;WY&c!JXXF=G6&iVCSw=Cx)01*Qnld ze@v3I43>TPmk*+~0e>C-b!SFtPiI!r5ZF2UeYW8bfz^5pge?t_+LU<$@`rChOLeMg ziW5Bp7J_Oslo~n$lpd3lZFlQ%dxw-r3LrNGNCvl3{e)aixcTLE%wToW3))IOLTV&J+z6rUp|WU&t2FgeWy+b&86oo2 zvSwxMhbE^Pi|fkcUV$Nvpcec<6L|qT@p^EkifygIP}}{%pG8w?$R2!pfIXD9cXjSg zCN`);nz13F777ZSNdETY3bh{;J1E*1a7G8(09Ix1!Oo$#-RCR4V;$2UzQkm4^~QsO z?eps+k`nKoXnXWJ2KNLF5IY(621g8#L|`%n$fTHt-wYGcv=<4uenaWiwC;okp<$v0 z?e_#+B#$-?XUFNsNw5w`1lqvKS&>miv-W2&>cwL?d+Jp{3y1}^fD(tKvwxJySF1bQ z6FoARx7=>xb1zsrSGI@)mH=(;4ZN#e@EhOxQ}p6eJIzIOM8n+?`72$fWEm;d`nIOqK3@DB_t1a1t^SABlkbW&NL z!y?K(d)45bu#MDnv0d+ZJAFf(5y62v@K$Hc%q0uysp2v#Ul8T&9AKD!1{{Qjl zHH*lDQIoyBCAFi!_1@hv0d6cFh-WJeqLAXPAqJA%eZHn)Bn23fz|!1j-Y7w<5U16{ zX;0h~2*|5)v^Q=F`{q*M!GJSOVSyq4P38lA?^|?PH%77>nJ16mU{vv18vUkZ@fQNZ z1+AY}p|Xkbi^I+1p*{xyNbPPg%1$k_`T%<2(P8O#1baAwmV3kk$gt}jEUStns=Gl* z_um5Hw1fg^P%GALL2;R-0|*CNw}O=%M`Neo&O8-iUYy?h#B}maymI%6GfNIb$ksgo z)8kaV5b6Mhzn70*qmxJ^liGbC_cr(0#b|2k@ktGMy6Rf0IIi0mz{PPbi@D{y*(z5u ze&!bVu*LF%v$z-bu!jbu*VDeZ|7#xn6)#ktidUn0Vl}G8*riO7cZ2&a!ro8N?<$bb zKo}VztV1DKu*b&Yn{H1UUN_f{P@8`|Fm zmTghFPjAD-o3i}oDk0{rQs$ClP`uh*{@WLS2td+lTKRzO#HDDk!&)>GBjamjKDbP& zha4nhE^&v+;4yC2`3*rKi-|M6N6LaBLnM(&YRI38G@0hE3z4^rH1B;aXxkR&^V<7x z(G)+7Dkj#?@%s+$6@mU%O4l}3@nncr<1j|G&;fGT8uYL`bipe|P;+4%_^9@J!8<7% zsXwCNIh85pn}L$tHD88%wg%^wj%{T#Xj z^*q$xV4s<$W=sP-<*V}&`zPmBsJe&o{eue}a7o`<*8lS0u;xN0^kCEAoowjARPf+? z^B%k=;Zv!$;9?+XRzVG(c>GFIwXM}Zw=jOCq)xe-yes=CH>`*xA$qD_^XFrMM_?7{>J;yxvSeF z!5@9udATbbac+&sQst?8)u=?Hkw8U_nfKWgzbaqRHjZLZS_Q*Cyihuj#5e zT++XLZ=xGa&z^8K2J+pv>7#Cpusc*PPTT}v%WW%RHlGy3iBjRs#_vNH)D(@j-FdwL?0#5pVGQRBM0LJE;?SG;Yw*Gi6^2y<$@;SzA_6~X_u|?ljT}lD7{*Sybefeii2=a? z)%5;^Y=_%qfH&9rCYWwMRk39ThR_4Sit{Q3eo9XbG}VQ8*r3G~QQK~-M*y3qx5*W3 zJoF}BoCz4Ro~v9vlBRz|%Luqtmj=#6$s)y#6QyXWN`DDx{vj-tzX@nu+3loTWfi== z3{Sih{%=~^v)#NVx+MZ_sHU2O-nfcvzD zGfP&8zW3L)4yUoo`@MAe*%#J`qo&JJ^e_f2$NdzyZldH|%CZ1SW(6SJNjuZND@az6 z68`E2{xqI3M!%ARZ;+=t#xh-1`KOsX9aQ~c!$l6mN7WcK{oV91i^94^?E49R`X87} z*iV$OTvo5dM^*eNM0Q}ko)WE{HSi-Qx|VOVo*A*BC>Iv#zDxLY|=A5lE_ zM|GRyDQ6Rg;zuNE%;4e82Z*5Uje0{54Wf%W+R&QjAt|ynYWEPxaDFe1UfuS|hqKjV z0Ccf_TB0YCg3kSp@vE!0a%R5l?dG57#-0pq(>o<;B@EPXoQ)04;U>mh7RNBP=)?I^ z8s4G5E7AV5-AhAfVo&1ywo*hKNBEtF`Wp!6HlzJY^uq`W@(3zzbR(oc5UTT>=lI4JF_rie@|5 zQnMXw7?zfxzcC(~R3F6L%yYQ8ob}DvVQ??3)GDfPtp1xqc%U}^p1<30de=Tm7f4^v zAxy0i4xt`w4|r)3UCeme3sop0XS5)vD~!*7{Xr~e*M6vPTUukeB%#UrP;eU9`7Z;= zDA&1V`mDe$^gvfIyXI;ww>~VJ+YdD_-ur(XZDw(`$Twv4BB#?o+KmB1(DbSq7L@os zAcK{Tpooo`CM$tbG`|!P2!Tz`ys}7PDOdsNj_+Sa_H6eF-PjSMG}X#JL(d>E774|7czOou@E{P9iZ#a7GSaB~M+eCL;TrjRgcl{8PhOt>7UM zd@IE7?ml9d?KRORkWQ$UI+_BVfQaM7DI#cS;3*(H%>&#HV%VxXf}A}y9y|wh~a{X-D)C#oZut-a; zwMWbd1xa0YXHISc=-%g^%{_Jhx2GzRF1Y`fPnBT*lqxa+@nH0v0l*QZRM|a7R?yGqT;9!`;Cvk4mL6@DksiMl>&2hw9_(U-Hc9iVD`jbPBMO2eDHohVf3EOq6Y zt)mlfI{*U^4YiU6ouZP~2~b&F>j;2h1sC0nN{uf&_|sA-9eU0%+jb%rOnMqGr`W*x z0Fa^;H0#Iwn{LZ2YUtP7RB;O$@>zTm|I8R1%joTg*xJTc&3q0~;J1mNlK<)t=)u2u zS%izxmOJe)cKB)&;C2zn!?{mi?|zauASIyBncMtExGg{H}-Lh#Y8 zj?eGli(nu|yrVOeOPZG_JoL>{>V-7ljpZkaTn=C%WVm4t<_(75f&v#^GOw!rn~>m% ze^G!}d5mUCQCDfP0cv0lGsRJDE+EAExvSn!@$;l7zv|Q1pUx<8ucdPNT^fn=o)%1yRk3VW;!4(aUcWM<{hS-CIH*kTEHdJiN(I4ZJk{`3)3cj z%3I>pSyA8Kvc(qAK3s%AFrJmM6mJj`hE94v)$VRzzW2dO{Y@3;pb5s^OM#VEZNqOY zT+*WiXQ9O}cc&p6aP4JmWxfqeFO=!S38Yjv(oC(msoo$C03$a)^92HnG%-AI<2|o<_TxS z=~a^BULOJafMwf4$%?We-J`tfD#2iMBo5qcuHB|n$43zup-Ajd~llXm_t&bxNfyc}cG;)VO{hQ2imN*Sh0VY;Q^K`0wGK&t#hW z7FSAI880$X>LPEH0bxKv8unG;^HmjYD$(u|EZ|1w%Vf+q8$r$M7*X?D!@O=GvcKd{ zt!s6}T0;>GUu@SF925fb1zmUw(>0#ClYZePT~i&+yj>wHgMLpdQ~_Tae?SLQe7T^7 zVHs}jcJ9i)N&~QlpBg1?T@?L()Vc*V@AUG>5X`*jiZ@W%vTid{3BAB-Di^fHuA+{c9!=gF8&Y^%o-RV;Y2ilLb78W8x^x zjQ|1d&z64#e-5}nq{sofYw*R`RRlH373$K|RDtneL|X~Uwm z{gX4!`OpMk1W$Vm=)5_k^H(8M!nT>WBY;zMT6-+!2!K0+{98W3qE773pIgH`*~mQa z-#)hK!;2<=IJobgxsFb6F7#W>uCvd`z}&ce1F&I*Z*Zeui2-Om4HF5|Pr>(Y7`3*v z0%XkO?fhyBX2eNC$HeH-VnGXX_j#45p&WMT>e@el^%EIT7eF?)QC-pM5?PRXR;x~i zd;$mX?V!&5R;V+d?`(_rvplvCd~d)T(ChMSCiZFBblp$ju4vLD*uKQU5=APb*ZzcYHe>25H#ooDj<++fo7vET^ho552d`Y*uc3ryY$ zao$}8GI{j}H{g_8((H02Y_+?F)C$dI2rWQKy;Q_c)qQ&77F7W1vv^Q)yj1t`=d{k! z>8QGhG+uSq=MfuQZKLoz(!x}Cf!5xmW9bKU>7g!nezVJ5&kB9h6$kvi`pP?C!RHj1 z;9D}y-0M}MmfRA*bHrh^4B&9|1m=T&{RC9|EeiN1h1q`nljz0A)_t@5zf*<8?^MAYy-tdX0B{{}Y(}a+;J28i;Sy1DOQBb*`J5}nKRps+ zs({CP<5NH2U-*Y$&M8*YJYEawEhB2<@ zHPD)!7sSA77P!zjFwd!}LbP=vx28gWw3AFIbTBP?$zE+wI0iGl=12(K3Bynm)u=Sq zx91tYa(!f@PkjubQl%9`z>U)a!5&TF$invHD=R{^Kii~poKkX18uX%9!fjd=!P%iEAj=33CEip!8? zS9lMK*7J#ZWl_+2{@&>i{C!7cJMy$`Vj7wXw0&b5-p1g3>834lm^UDf{Y9nen7Qk@ zodD%sg#eN`eF;VOr361toIZh~zk`Rpsst->)2J82-gc9+gddM(wii(25Q;_!l$XY; z6+tJ^=XAF}*hRhj(B4-B?sRka87N^bBXoQkb-5*I*`>1~X*5+`c+^5kjxP-G%0eg9 zSFYv)zCvghV-{&VUUb8&E>tYyTCAR3MLegL9js1X*T>Z=@|Cqye}&10sx z#?-m8o(tVL(ZAs@_nj__Wa%8cgi>mF);f2Z*zGJH}HW+?J?{k;kE3 zhccG*uLXrm{=iF4ZAAEod@y-jHU+Ke-+1n;&gF-N2CL*dh9@aaw1(`55;$$%P%k$O z8DgNoy`arSE0{0yZqqE`oJ=<}ncwpQb9$^zZGh+|G80j!#y;fo|*>Fw1C79u*1 zGM1S`QX{g(76Oz(Cq za`Var51C-A%b|iBwLY`nOqG_S^fj=F)9UQ^|MHT?gVSD%)A(>&@BFkKEa!<%>{wrs z*0;==I}wvn;E-`uz(ml?Zvz5fM!_Abm~-$o2jA;2DHwQ?8a&+~`2xZk0F4KfYu;8TdQyVj29H=BDR+pcKw9pr*f1NF5BD5{~DNJ`#dB z-{2@P{Hozwb?$+j{oDKN_6><4x3dBg#F57(k(Ahm14Y^_HRMf6WW5@)Ngm0U`#OdK zi?_2U(I9h~kdg`(p^j4uoSO1y7RbfOjp1J+L1RmQn?5UrEf~$2;|Yau_rv z#MmkeazDTB%SBBeO;3*+kp%o}d;EDemGlTy?&-LkE3x26a1dhBL1n92kzYOfVgkxF zh4oCc-p*ps$I_t;oxD^v{m^a$;3Pd&z8@I29Awxc(ts(>DQLx@CPOK!1F1wW`Ks;~ zFMT6r$gexDX)gq0H!XHKgV)3up3B?l1Wl65oeI-k3{~rYc6uUeixv7@e3cv`at+l< zjp`y#-JwSHmZ$om;Qor#<0xumiHVY{lLBIqR)PyW2L;bxz60h1uUYPd84qVh=PfLX zJ~V1(V>_O$ENH(Uanxz#BeRw>?B&TTH3yf|4jf_3?57W34h#Qjt-$93xAM5J?8Cr7 zTt}aM$Eft2P@sXdaSJ3AV_^LNumlKYs!0Thx$CWI=H+T=DR<}FsnMn;?dbPA0NM;+ zQV`oG3Vcc3pqD-D$n}k`3~&)cE;CM4oM#E@3Q$`I_&OtVKbimF>tIe$T%YIbVwu~; zAxk)n2`+R9_JWR-{9rf> z?vx(Y6F*&ZKOzNaLcw5gr1UPKv7+E&G1si^_3tBKZ72IVJ=B#R+Ts`0!>>Hkk)84Z zRo4?J8wCIWv<8ZVHX;NZ$q>*O#R(IXnpM zV`N+ks5x2jLw{qhb!sypccSYz=ZxyasMnZ*Z)pn)s|t?!){Nw8;$BLqts&M$3?l5&l_lc#T6hxC+balk`~g#y*t04b0$h^FX&GEl$#5P z>L>DA2P1+i#=wHZF<@(wz}8Mb1@N-j(7;=;ea2`5Iqkob*5rRFEr?8DR$53z;H<2$ zf07jzk`+v(TrVgN@Y8D4Gb|I5tMF}%e&4X0?@x7uTJ)e(3xv_#F0+75%2R0;eS_Ln zWxL4f>&7Rvb7R$Qwt&^IfFUiLrWARtI)1h{BlN@}aP8bEi?Z?uWCQ&Ggq8yUZ+eHf zNBW8&Wku@_-$d{gYvKohHSwuKQAwr|qqDPjapgRnDIwv>b2=kI%X?O#>dsKLpz@tr zAg??cvj3KY!3q2Q(H4ns(*<|<1u>%h&2vWAcl_jOHVS;sYt;=y*>oAx%;OF~xwR+W zgD_IHvV+U$WzVjG;2{+9(n=k9bm!}L@`2dT~1g`0sp=X61#YZ(@AkNNzruyYG$bWLIO zvQaZ@t?@Sf3Xh>2B+uqe!Qio~5pSV_VxuJ(9vukYD4QSpx=tmnNQD&Ti(<1c@}f7EundB)F=;`i>5isg!g695A%&2Z9Hv&N+_ z7dBnIEe*i!RX7jr@dHjkL>8%T<0u)fZY^|K1y#z`Thx1213`obKXx1yAAe6I;{;qU zgV`?mZAF1XIRJlvnBW3`I$1B;{JI|goE&|>zGJ9c!~RR6X= zmtFtci8jV(6*3wO3N$<7l*TXpMHs4oFN_V}gfWO;4KBXs0?C{(#3&b1ySCB=JDfC` zB(NH)rg|Ce4x{c?q#lUvdK07XljPS?FiV*RXaXK>GQH(U_$-_cXJRD?wH6F2S0$}u zMUZ(N&0&LO0xcxB^fiMRjv0)DL&7iybCe2bj&f=OOjAn)PjAyp-$;|CcW1>zQN!FS zUvKgrgmT>E-#&4+32gqxUvkbQGoaS-FO!QNP4==TW!Cu5lR%8pXzTyI1Y{6Ovk(_^ zXxs-drdJ$H^J2bvMHqD}R%!*+Emq$nUf(N8zZEW$fGksw!WJRwlU)_sV!HZz3mjHc zx=y*zvimwIehVIc|)Oz*IRJuSnEBU{bW!e zUEM7S5ZTRS`NC)Znvjodj%z%k&D9DDhUfzuU&J5Rt?OB77WnhYR_^-@aPbIkBGTT0 z9kK1orv}qq0%xvV-^Pg^17|{aeMH`Y3V8<$di~T1Q;>fC5h$+ENC~jNagPg;9LcXK zx%eDR#@9IEg?G7%PhC+Fwu2!k1&c%U*S88K)aZ|1#%+AXczuVu+WG!9hx>squz)!t zmI=(mdPTAXwK$(8Ux!E?Aud-dHC7iYSSXG8 z6Al5996b%TPLf2OrD|(%8-nX-Ppx^0`erR=35y26j^N2BPcI#6U-zkj(QmM+;syv1 z{qF#T4}=XT+5&!EJ2?rM(5GO!XyxWV+=ZebsMbe88o3aKTq;UR?e`xxK>Efuw4xIi z{kcF(X!sN}w_6cqKL?oc72#YRvBMCtpK8*+6FqK@MgsLv6e&kh%7GZaR>L|SY^4&V zmH;ti0TlO3gpV%hQ~)cMlVbvh;3TGUHKLd0#{=B7isfr6k~HTy7pk$Gr=7dOXjR5d zfVx8u-AifDzNiNvsG-bU>C`8XdXfdjVCJeEdKv&Yx0`>ua~G<`r2+1XC(woyKZrOY z_f9lG*8m=2$#4JPDnb(-at&)mj-kXPQ=EFNNcK3Q-wH8&ik#j4-?6u$zB{1n%;uzKMFAeHjx4T$p$5uQ?%VHYI63R4_00=9f=>y!A#hlZ;@Ji67=0b z7O?9n(QHF04Mc%*l3wJAIL&RX`&jYWvD)u~ATFwldp@6E4)L7A0`xXLrvS*@Ui_z8 z#V7!dX$ggbY-s0VcqpvAFJ@LCk>!@;CSM&cKdMp_bX%;DkW?p~eG1+Q;bdWr$4{^r7(Z3Qre6TF^JMdz5I05?Z$=4^UDEvQ^ts zXb?0$r13F1)k-$?Kn8I&iXs3r+Q;2c52kGw2n4%dLw(1I6SoxC6_!Z{Wb})s#|Dc= z1>|P!b}LcFd>YWJz`=ToZ^@c67Zt7Ib$ghXyX$K88eGF6!^)#>--HrmagX2Zu2oHLLU*WZ*Ss2;;z&fyhM=!-4 zlC{gyiyVHcF+&c2C%Nc}Y1X<7kRCt_M+NKSYWsKWZG=Q%KCnzFj~vt%r^E*LAr`0x z@xNLJ&5y*Bl{at4nS$DBnfVfDs3$j@E&Y)o00bzi$-13$1P@YF1K0z=u6lu)ViBW{ z=V4h&uzWX-A~EcB82c9`Sed&<|2n#uWuXK>@KvJx`m921a|T%+ek1>S`Xf1T3Jw6l zhdE&tg3#M;(Th5(>jb(wFCaYWmErEv`NPf3rvj9Upp6G@SycK58#s(knmi2M5WNtj z2-4YWe-ROTS%I|^1FIdPba}Y~pEJ|gv2A#WeH<2^T_ z8xTHK;)qzP2~r%s9s5ll*jahVT8f}@;p|_ZEsk?Vw*fGbmKeGVe{2II`VhDew;lHZ zZAr0qqtt=>xagTKn|3DgU{Ir@WpydC**alS)x%*Frz3#^T31Bc(GG`EkU_Tq0M*!& z6Hm2C?GCpxfE7ppE1TJ#mLnMZ%5icUkQ;?dWZAoi;xf(>V%Or3l3OPz-mp05~!SAEPjX@s~l zKoGTrzzi|6SN6ftb)a4KL~a(K+HBk`TXEb+Z@jJB<`=EDc=?LZVckGp=^D$!vUXvO z!zets5)>`Y`%6}{0MTdA==q%hCpf!aW!oQJDhO26SWa-%YITCL?)FwO#Q z_;i2JE%4-#@FyS_keimgc-!*AbA}q%7xbpT`}~WM>ru!pEX`f!brQ5Pf`T2AB1d~f z_`433lBx1b;J40Ur&i*mM?`fR=HVR}62KK{O1%7ID!ELHNZrB)4*2Y(Tz)R@> z0AYb9+C9M8)SM9nM?c7^Hd(XdOpWSzw8~HVfAa+M=ThICAqjGY z&Qw`6!ear|RGw<7MzzOk(8SpJ%oMj6X0xG$7Q57*cg%kHnxkrvlxYTYKi%^#ip@>v z>Xk+u;Ox=JUIDO^r|guc&2m=)Yy_v;PpJbL&j8jVw>m|{TOn?992I^7a1}|PTBt5u zc~yicZ(#M|fW`*!%~Rj3PGE^Hz%3@9uG$9BKNJF%ZhC$fkl)Ewg9C!x_VJYEQ_2Tk zb?w`nzTbzg37A?!qo+g{h+}Xzx9n*_DcD>9oQN8+kV!d6@)- zt>~NpfRgZ$5Tk@B+P-kAV>j3CLkqLxvAgKYYw>I>3I@HZ^1X#e6_j#}BH!;4el*#( z6wIfV1N0=!V3qyguhP{bTaeeN<6mlFeAlssZbsbzE24QI{iOV}OUZXKm-M~`HRB6# zYv2J*UZJFLtRo=|j4O1Nlm12{j2k2rPs3?vp6%-t`&V<2R2-2Op~cM7Tqsg?V!Jk~ zic`8aJD48AsOCGUk}$&nVfZkHSLnzuN(Sao?7%NkyPxK_CyCiv0Du8?0lJ(8Yw^!` zdr&-m{Oo92;b=O67DOMnpE=qrGqr6Uvm*pIBF&1p#_PBqwjI<9ngAoI_u?^~1$DjI zru5M5^icQs_LH&gKPrYdONToXBh>49FICb-Ysm4lowf*V&|}CuKz2W-W02T%5$VGP z1-3{+q;}jV9J3!>mX?9~VdZzDjW2u0Mw+ZU@nMm{tFJg3(H(~dy6$aF3bs$0cvi@} z0Rv*H2rd<;T_Gr}{G)?$Py2dkmJ!qy$VC&1a)oN)$@ZrrmMk!Ou455>%QOO6vOq5$-QAT*0-b5pDB zIBhzL9Z`YhGyo8_ZJa*9X+saJi>q0x2ADR$Vt%i8>z_meHkca=0ruFwc85>Pz{UMc zO7ES@!TPDM5`kz#$TOch>}Sd8xzLH2i0Ka!g@?RCiNCZ3qZXjoq2Xpif zAa%e>A*e>NaW1CLmrS~Lq9sH_m9XV*Qr6UyC~6$b#Mm|R zBmUmo)Gm^PDL6&BWg*S(6J1Z*jiZiYYr9hi{;~1XyeexT2Iek(73=l=u<( znh1C}ajhU*L*R1}RkeNC=gHuE3RF%-&S}Qi=XdT?uAXXIHL>W~xyvasAaC%SPQPC@ zOpFQ+KLjw9x;mYc>R3fk7=@8#!4$W=cXc}>e(FRU3EWdYI@DI)5(ARz#Cb`zgTW6k z{ygY|1N6zS)`Eho$^T(3bo36Vaz?>f;7KqdPl7FACaa;NZZFOWLD$_bj0u&6$FodK zTw{vy58kFp9CpBj$iweg=(0bu=rhl>Q=-0j^9Dhe#TU$T0n~yBiBXQc5n5G!^ZNI| zxJC5PhOWzckx!!0$8m$de$+_}8kb(Fz)x<3vM3`fZ{>iEVA8v6{m=0>!WXp@tX@Xw z%mvfknI`HWk~D3#=-A+MlAYZYI(aXB`}U}T4*+=YQyBo}so>oZ|L?6}_?s1o!5~WM zIX}7p{VPH>0Nu9)5$mp+ie9o5k?XEni1vd~_m-ICh*N_ksV8H)I%V|@D39z_k6TkO z%VA%xM8C;Vp0xoB-Kmdd%#LHYk-9?wZAGBVj<}2ENHK}Uj+`UixJ0n2jjZ_u5BE3( zwVTTFePsa@BUn!K=Cy=_z1+zbXso?*=J$?m%8?@zc}3TwNTN~1RX}_JRTYz=@f~At zEZ)y(KD{41D29TkU$Z&~r4f3wX+&6((&yHY|DjokQPp8iY4*XFF~Ljm=T_*y#lMh4 z#8G8&h_e!~0(}QHY_a_?Sn@ElR}GLdRR#c!0FiDMT}u86!Y$N(*nPRP-G^*pt;b~B zqO^foR55_Ur}=RRx^Y@kZ6E@R zmrztiS-k-JEs2Vx1hC(7N%3EkiR)j1Dgg)4BcwmN@g8)mRjWcz#KYc9st+*TCb>R1R;xeTbD0z7k8 zmH>N&RXV{Z8672qSyk_;HoTf2M5 zD9#?JZD1DB+4cZ)NvSL5|C>5sHP#yzV}X<{P-69YhB&yr<736OiFOM&`;5g>5{t2L zfcLGkbpic#jF?_aTKM|3a0^22gCG2M?EJ8><$Q>XYv!iSzZ7J>{&j!K@0+e6jncB- zO`rYr;%&so>i0U`);&KLwYXIlHl%Pag%50cQ0U`N>lo3GIllaDcI_9Z^50MQs7Keu zrrB7*lXg5jENKYCS}NM}4a07}?}=IzE>VB9mpOTP6&b1OG_ zElFWHxa8-_ZU?%?8qTGM-MH*0NhjUBtnVDxwK1`aN=KJ2;U_ZF=$f~d+)wt)q|5%u zNWOxX@@6IH;ZvVnz2IRVe8Yu;gJ;T7f1W~T4Y0VoFnY~2=Z z1C9x+LvH^a6{Xq_aXG5eHvS8Q#k)SK)YuPJDO8)ms{PG`*VKh+94?K{Ex8#aq)z34dBSDtuV^K>OzD*rx~- zrT%L7EpgQSV=s7Z@oD2$YmsnL!nv!#lB6Z3TA*%`qFHQ#T9~KI^;fRNEj0I2MApk8 zn^2m3IpnZE0DwX$zXFto=v~HFA7#)jGa2Rj=wHd1XC%?mA(=5!Xouex`>jCKTdD96 z)Zp{*a2P`NB0LSTR1JP91hIXDaI_zTX;yUZXx$?;+fC0ssdXw(0ebv3Y^^-bQ7T;! zYTooI|9-DlMla~(>W90Rxao&EaUR{?4F1+^5i0 zO_IWOf1pGoox3Szw#3%wzQFn5N zpOvFR0)oMsbrC0gtjBY{Fmv0GQ%|1=%zk8WmPDO6F0|fP92GqjYUEtY@-ONjPWtR+ z5}gkHAe%(o9hZ~gFFd-HTY8d~(;L?g^LJj6aJ~KEe-et(_Q)w2{Fq=P2TQm zT;HRFisG#gbtnN!LaaFmwoKKmWFHb{jirE9DTDePa>N|Lm0hu;TbjGkZ_O=If*dmN zJXGRNkeqfMNnJ*IfHY5BXr5=i2B3uvvQ%qv-FBGaeKe+H9Fv_%)roOlkI-yWMlaqLgx(ucsd3`GlCrs^(N(IQ+dHZBC`{Hu@X8bX-M*}l4|b7o z)UHITw_$+cn?D{Y(H1cPv_R`FZuaqMyJXV` zagjt5Uge4Vj-#1LL%-3|$|AJiC}3q&>bgY&C@e1_9bey*WxaD0+w)fLm(t40Q6s{@ z$%%=&mp!#=IaAZChHrQ?_6O;NWPPD`mt|X3L~sdv^Kp(KH(RqdK#aCz8R%+VpbsB< z4BDLK>A1^R8c1=tV$6O^%>D`k00%f`iyTI2O(G<|5H@(S1ZPBEQiksN%jK@kaZDGH zk36@-bj6?Ku$8g8Ipd6ThBDs#76kvY6HR_aa8X0}YU77O)K3lm%;g-RMKv50YON8J zCg-;3occm=1Vo#g>K)55gE3DrNkcCqR68zi5nl5FpZ%R)9wd=wSD?I?Fz;B%mp1bZh#~8e#n`Y5X+cE(5RpcVtXc|OYF@e@C z2JE%?Tb`~Nl6Ye;*I*bv%PX$H04@F7P8SIT>-^3K5{P;Q>Jd15eLVQ=B++NTivRf0 z856Py`x=Lc@=2;8J`)gc+uOBTjVovgy&iCmrrAy;NBx8t&o&o?dNYVld7xx%Id^eE zozV<_nN5nBZe6ngmCMk^FueDJZ!ghSn3xpgq`j$fCO)^J|F^8;19-kh_VMK#Cm-$} znCahcJRPiI)n#ra4H>txRaEQM|7hIOwEI%Djaz6uDVk?|Lr`1;*%`ufIc45uy=HWU z^hZ|M4IEDo!}Fv0Imh`u&ZHW}=s(M(TEStAH0q`p<_jd-m=foy#QfPIxH0aL3>vvc z_6)*M4W2WFE*@a1v?fd3=pJ-48`oON<=jfMwU$05JzRQ(N9+)co$WLdT3_r1zp6Ic z3gHd1;p+~DJ6PIiH!H17sJC_k@K%n4CZL-^Ijd9WA#isUyoX|C6>s=NpyMTmAjWaj zAa|xTi2ufn|Keh4`F-f~!=cZg;5m7CNM@0oM{b1P+sNODVGj|ZU>yYd;DUtDuVSiq#79vJr zELA%zhP}F$>IKK%%%TG7lB_HGd&RN!YXI7byCK4+6~C{|8FNRK(s?$u#u=zUslDBk zC}E4Kut(XBm1;ZDAP3frU7m)??q!Ac$0ZFdRpvj@Mg@+nyQM{SpO7qjy!uwgt^ArX zR?u$?Ih0T78??SO`rKRj9(?|LqR;O%sqt*^)Wk&n=bxhDWORU?UwEf-#o%^KTE~e# z!2rhjR5nr(X72Foi*j-E%nMK}BCg>Q#$Yw(pLL$>cL4u)IuO&v&4hGd=kf+4wNeDt zZ@t<}IGX$0&UYU$!(3OrOus7*yi!H1JI42pDvW!7=7MBy=@NoJTd&ay#=C>*VNF&z! z>H40xvGuF0dbTBx0;G)L&&}#Hb?Z@q32FCzGY!tAy^#19Q@Hm}rXUNK-Le3QcKFC5 z-NfBAz}!4Q)87&&G6oA4MiTY^fKX4gnilU>*w;pgEL&N{>{ z80-xWk+^-qc#b(MmyMiK0D-Q7n`=FJbeGU;6|+N+5~V>ak>w8^T?iDx1wdDZY9Ba8GDBR^ma^Dv{bu94SIRT%cSjFDE)VJ+2Ij%vQeuuqKY=#Thu0pg0)f*a@nxjD6Xu7#L@qxIJ$XCh40 z;PffzMYD3%Daptto{yLbGBPhl@Vf-nd z*u>;i+#I*L-@(wCGC>NhOSq-J{ai=O4G`|NnQ)#u+;v)nV4L z^VnUOhk@21CFfZLlASHZW+*u= zrPS?QF1HJYX!t>;89^nVm$Ics$2jQ6uihlSgQ4;!v8#x_&sqZZt~+Mh+nF&q1ybR0 z9`OO4gN$0Xay*r_-f{GWoucm6G!P(K&kHVIU=R!G#Nfo7-{RWE{fB*G2hS}1vtIm! zDbuEVuP!~UqRZ>|#*~HdFo{EgGY(;Icc*AhH!4%CO=@8lKZ62F5-Olj`+IaFmuwi~ zb12%;pF4)}MGVIv%k}oXmL(uPqUp4dBPFc_ANH;OmJ~afS0Ipq(zJhe71VF8f|qd* z!{5J%ojGeB{)o^8h$O28ndsGW)E!z>Z#e9iG^w@lV{!V~OJmc!7SS(w=5;N>x={Q` z+SKcE@G?n)ldY1VHe~g94pBgSm2PX0IGx2OX7GqQ&(!KXYRmo)ZEqeAWxvLc+hekK zlRay0qO$MI*oH_mNTgB-C0iyWqG8ZvMpR0P8Pg_OL@1%iR$}I9&lXyy6*VGRzMp&O zdFq_!obNg3`}_UVOMl+iT+914+pQzjUc6U~-p#NoZh3Q7N&g zMLscqapEGq^8O{0;(jIZ-Jq)I-rQqISfN9Rl_%8k-V`)aTs$b%cY8Y_i1GL|)hx;G zh3I-C(r8w-=Yt0$v+7_gCCO$}()993 zJ6uJi4(#tZu(qwnJ~QcbHot-xO6;&D7g9Ph+~{K=OksECZ~ZpXQo*35YS2h44oszG zUDc!?P*0~tS7;tc8)~y9Yn_%`?;Y%lJM-@D3C55IaR`UCmv&l?iP3e4aihCD$)!10 ztfl*g;->kbCHK8)XR`dz_^M@s&jbW}rQqeuOgIB33dvWDIF%O}-Hy3Gjm8?uwr||m zikBEHd%N7$>p0u**44x-=)QD|(^Y4Di67>bgkt6+|zX#SJE zu5U&7iGN|Q1g^a@XB% zyb&I>Nzq^9*nQ@D5ttP;T^GxUPW*P|^j%4nDBfFAy9wcXN>4$=t@Xp0l~r^|s+>M7 zse&Zqm_sdVWozUkOo8(#l8}His;lW!>1y~+A7zfr;1k}j+MnL$x6E?3HJAS=^&!e~ z|BsGsYr(r;8Vw#9yc8i)D^`Z0NOG1gVVz|r7=;Ne|JjFbG)&zgH^TS_g#I%<6Ng&z z1jNb9h$S6)=`!S%Kj1$Inrz%hDd=FnrY1gE8alsQHVe2{`Q#KESkDk_ zw;Ick8K2sei!iJ|~vETy$#JU?{Vsn~%YF(%YO2` z_-#csY)vbOE|Z&npkz5!6-?dL|MqfZIy1Vb#cU`N@d`ZGD`J22MWH4dW@ZMb4%vx} zOs+~gI!+&sQbKZ$KCKz#oF@bVzva+}U^!+OE-cBLRf`YPR8!cDqL`&QZzcZh$h7!R zw2s*V&gSomvKqWa&-Ep3QVl+-=juymtHY1YQdj%pkW?>@`GKY|r08h`u~H zUpB$|s~5+DXY_b5QrleZ`JIF@V}Y)Gi&`WwtYFtBl+#j7MTjd9v#5ZLs3YlG=osDe zLq5EuYf5)b(Su!{yjO`s>I~l-IZfy6a|v>&DiPEzBk-)U96O~)kG-#eUhHHwr6t_SM7YC^qqhn9YNQiSphlXZYiJm=?Sddfz7T)ff~F>0!f_9fgs^yHOPz9$lvm! z+27{HU~q%AANvsP?#EtvX@y##+dGx*BurQj$U5Ftc#@d=c4Mvf%4!jJC2{QSb^Z> z!+fwaL3mZkgGVc#2j`@Gf9)hXU=^=Y1CZ*C=Sb_;<~Ut4b4WYItIiV?6H0l z3pvpX-4OLMV@|?p+W?gdl7fgrWY)h)3TCj7FxdDZOA?2iv_(`> zOI)=@^iu=ZW(4NYT#kC>ir~uMqhEQ#`SMU7M2^o-w<8l%)q_rxk3;5*;lgUh)-v3r zjX%9>&lo$s`Fg#Q40>J4WnA1Fd%I1NTEsM z1}*mH8s`xg63CO7<@tNht0O4#4-iC7h3-t#>8@c)|Hw zFDbKC^0xAQf?L%e&=017R^`Ee~XC6eBE=xM@P-<^X7Pr7lmOdfP-fo^sRt z+BL=@Dq;??<4QC_A)jSj&p(vUMMroYhMnmMpw|kkQu~G8$VYCR{`d=5^wW^0E*Q2K zCNGsoDyM5k$swBW5pLOd?H$Yqm03zx!-*o~?LOJNbZ^Sw?pEf>JxdMLNXv4qSc0=w zrVd@QSDVyijO1blK5S!$QrSl7#U_Vr>zpcuMrEe@9$T5^;=ZPsKeTvj%8igF zk7App{RzXH2j4GiylQ^gvSZAIPdTMot4&+^{NIMh?f*-72vK%pf@~cO&^X6-T)HH* z$`G;>a5^IDbX-926qRg~N@V4c-BSbCW1D21R*{q~7kDmt6 z$`WaBW$#=v6pc6+BHgrMDDIHr%1DbB31F}h!focZ^#O-SjN7zVcm(?=sFqmHpMe01 z4&t4&%rJ*vSdE{6!ZhsYVui03S-eDbdvVZ#B;=+c$mx~pO&E&I_x{KWQJ%o2I#9VG zR1c9ul|{fsG>E)6YiO$=sm+jl@&}yChq~B|e9qjxai;oFT?J}6^ z^kIz%&;|^Usb4o$z-FIlVuncKx9z&%^KTRsrNBldp>y`#c1Pt|R-tMQM@Bb4rpNc$ zCoLVay5=zQ%p}ol1&117p08$IqPP-ULmO1PwX#pWc6|o@!6SFlXl>t6#vy;IX%VO; zelI~S!9G$xJ|Mla@rTA>somVF!6yN8UU8e&iVPAaDH@sD%IE&3!v1L_&!EM>7)eIq zn#BU@S}>9fzceJZTh|_jR$J=shanG3-J3jQGdJCDJY;9(thFeoG9|}Vq#-|#Kt+-{ ziC82U1E5xra{5{_Ew$vmIh4RFI6Ry++ar~{Dw9#HU($`*f9@XJ$iM13a#q61lJl{{ z9n1tvkPhR`mGSiG#HZ!TUnM=vT2FG(HDwrTV#tHBXiw=u)L=|qi`dedO}&JwagZU_ zW*{AgfmPjk*|~1^XM_ozAA9t#s%+lrsvN-2WSM#FPB$$HsyY1+ID57HYU4;q!8KpO zKh;^WMCt?$833Wem~jgr;{^@BGOY8hwxF^-LlAp_1I8?6wDmVw6gM2HZIx24T8>lB3Hb$T2Ff04zRBizr8tcK&wtZrFWsGyw} zfLTsu-8Qox;7PpuVOcMwX1YSTlqjlrn;Ylj{axUsVx_GYV2G(59AK5UOA&j8&-bVg zt0hq)eTHjEqhX635+Jipy}RQB=GOKmfv8x{g<2>2dLktN%|RHOEA&|At-L2m42>Ny ze=i*IH^^eE;nuKT*f>wmq?(6f>bO7+L(B`G+QCKWTRE*&7{(^%iSAN49vIdu73PIQ z^hzgr;S#-4HXqzyj_0`IjNiL#Etv$<wYvW>R0LaW|O+MCZ@R`i!@8hoz9E;#kP320KP zvjHc5^B7Ox7Ql27Bfpzgsa>o#PQP+D#r$i)$B!>RgD%TV1etK&{dl@@MekfsvFN(# z^UGcuT(_>uYYtbnoNC2m!}$Mokqj=a5d5V`raDJpkt6rX#^=DBz{J)hj}{&@ew(Y4 zFxnu_tf*uQ@l&m9`;IVv5TC_1N-SZ_Ic%7;W$YRc1z*YkmF*w+QUJumT3Rglrfi~JEcOw+BK_B?Rpt{ z;op4bMM;&{KR%T9?%HJiNJ2{O>>nTAWNlnS)w66;01-{K zv{z(S^mvGeTi)bV9@}Fm8fFX|+nx}WQ$X4VH;O)*A;wyup9GC}S^?%j2`~o{ci^SQ zZ(?U3(s~yqoTR9x@yb1xQ0tmIhxguo*b>d$=S|c*`aHv?pBRCp`e)(}5=R zWELyp4(l6(CO(IEDRP{Zi%r|!k0|-XFJ$c>Q1@qF+;Ohp_La6(jSiG0Z#Ys z080t@G^bONQ*OX6I7SQYfARi%R(F?H;+qh5fj-->*n+HP-H)7~qVOqPS6)c()sNtH3t_E0AJ*k~0au)(A7e6) zmhdo_=^*8Tv*y|&aF6PK9f+kT5)HkD$Vu*k%_@-A2RPv~1HBQ$8)uDbrohh2gr)|b zWH)FJGrKR6Z5J=*JlA1#hpC$B>}l~a*Ru|)NgTHusU zp?z+SD|{wZ7+&)P&*{bHRuR=d8beu%_!G7+oW@f5~a{Q@(AXxtW_gBy`r@a`~00N#5TIBn zF$kilyAajaXt&DB1VAWrw*?DWWGz^LybaY-MHMh7$EMmBYtav@2MV@ySjyPyM$ zX?o{ZSDmYh%Dg!;3*BXAZ6#9?t;pxhk8si;DUne2;fqtxPKhEsYz50sk}kuuQZybegA{EifW_b-iN z4;Eby_i{*hJ+deV1PML0jFW9vo?1{mRJj`~#8DT$d~7ulhW=y@wdfOW2sV6Y)Cmgo#~ z%DI5&$#%A10WI{?3uHFD>}6fx!F~hzM63unENPv-DoXvE~B6LK}Y^8vQmm z-8klZuj7DI+XfBGsf;6xBUBcj4w^w zA)##l)sglu$iPh!5jXJ2Iwj$_WvEyp!}trf@%0uZbAnCV3AxN%r<-6qkpnVsj$FrHmOByvWR5o=5$vG zTG4}(>%*#DC)$5{gG=Qy^G57YoRi(K;FLRu(YO>tBFS-T^2OLx!y)!T)F<5;l>TW&aAxjYt zAw@R+SUcDspQO4e<=r0Z?0c_nmkgIJQcfJ!Y+J6(5G1^+_G;TBVcGv-?;ert(@Jk>`a$j*?`&f=1$f~m4OK$m5M4RWn=Kip6rKf7pqc1=y4b6sf;17EjrDmT-*?Uj??TogiNHJHlG=mb-klkN9aa^;Ao^GNme41{rr|JWo2 z7=hvP(^ZlUhUys)A8dZ5cB{i^U^X*);D@cs=h|u6-fR#S7r8~`TID|7Lfd_zwIef) ziU`gc{B&gol~ZDfgvNUew%@#?;N+VA(z+u~AvJUiAPXyc!;Bi8`@u|18pvcE(;5-zn z%m#j`qC|cgTQX-QA(VV&AFG={v0c1}Q&46p1jrWs0)B8XBl`i<$P&{%d7T?4%Ni78 zIaDAjjNB_6@I|u!)$@@V!%fMv{y;iv3w8Cz20XK=&s6-w;;jq}KJON?y5^#|>qa=%VA}>vjfn)kHL-Km0) zyi3T*3N3Y2?;x21Z>^^L-pt_(mq6W!w4PF#C@Fr;H}eq)i!NO>NWH=wL6r>HkoIR% z9*@}BDgq5fV5fA`pu7(I`N`7hJ|Mu$2DG#Tv0pzNnSJjF7X$y6761)W^eGs)hnVmn zPznI8k+@+x)?Wq;_I7X2NqPM``rYYesC{o&zRVzWS!B-pNc`;O=((sALu(+s8Wq0i zMqTljZPNd(ZCd}%HbRuI0+b)~wi)^d+jtoW;T{3ooWbGVYpPu&4*6j1<(v#jfu{sn zms9Oky<#e)NS*tkwSuH4&OmAnq{XI;;z7QdUSOE=r>OvLYJ@$p6KSyT&d^FcC+DRB zDXKTGMJok|=U zm{ZIXv3@&u$t-A3A>-v@3TH(Wt_x9uFpL+pm?B!tK&~V@7meG**2M7P9<}2jR~+u$ z1v!5BeK}s#6V}}huhD^OETzE#E*+>xPv%o1PO{2-lTs=%M49wZK=7#$w3qw&bVO<1 z`+;5g_zcpKIe|8XV~j@YB{{R8UTUolXui%Oe(xYV$2u~ZQ*1qYXI&=fP!sOj z9lnvLw1JQ`PSXH$edBk+muMP5YJC}~kGGs}#Yp_xj{ES=uVUC^g8rc4Ptx$=cg$st zFN!I=6;YT_26<6LpoJvbB7(tdoG%fk)n0SPFUR1|Yk-Hj0J=Krg}<1}w?SN8p7t`N zG0a?bWQh;*LIGy=anmtSi~h7O-^ zeE%Fheq-qvd&lwr{c}rW3TgNVrujN%?TCvo%|#Vn84P;}lNBsvoOoJ}nUD=d$d<|? zXb3D^oGm1K3PLw_>*mSe2$h=8@aRDW2U5iei6u#`DlEETH9wufDz5SS1Zh30SDJ`%AtwLMvn}4^}88Yj^Gl#_(wSJN_J2NMv(`B)NE6yna>Nu@U`@6BtUMaHyO_v&HwXNe`bZm3V+`#|8 zm-GaIm?fCrq@1nCE|EVXO-^ikkpT3DnxM!m3gU8 z(}uPdq&uSy%RuHmc;B_S6UwPgqptP?N5D7d(TjH|PU)?`WLrG>d^^YLz?*C!5afpv z^MlXLB5e{F%byEbK8LgbpyZvt_T;O?c4v-<|0#v0;ku;*9Ttf-s}h7q-m2Z|Gff{2 z%aBw-Vmij5Ro_(+OxAk&5E5B9E^Zy^Vd#kHM8ojR;ES&VsP6qE#}nA1N0j_D7E1nq zTGs9$C4b%rs)G+d#0_AFZI7q(qGQy~4++q2SDZm&`;S~n@ct;Sw*#l-39sIx>4Lu? z4c!nWZx@7KiXw}xTm7UJbsT+g>|NLcCYS?pJgB?cyveJCfLbMEe|`kp$otrS)3M6? z?07!b$O-$#GAE8Gj_r4?I`Q*Mqa6o}whP%T0m9L5FWfLnKj>K{z15Rk9wL>%nG%r$d6VK{__Zs#R?7#CXa<|6dFh zWwoTsW-=^cvNE7$I7Deru$Uu&guBE3klG0PYO6h)CTPR;9+WhdUs>5pd<&~Z#MU!f z4o8LF+bUe2cbI(9-3lLmg!L+NvtJ>3Nve9ALQAed%Q0%Y9NCySouv z(b~7Sn4b&u<8?A$9Joc$$$ohSSooAc6@7xMfWnxHzB6e~pL>I6haRREZ&RH5$Wf5H z^Ddqi_N`G`t0WXnEz$~)O&=`+I#ZU>o``%&@eu*cNM7eF@P|DK7^O$DX~V>#{^b_G z29Mt7xtHggySi3>UJD?=G|>C0J+n&LP&-#+CsQ;z6`t@Yn?Wrim$b_U%_sV zI8XhcLb&y`G=`bh`g1A|1`g}d!w>R68;xV0s0s#e3-@6ePdtasLH*CecmyRcqwGsl zKR~NJ>=na@d*#b8&zpCYT*OgI%>`gX#8JxE;Mn(E!99s{xb#Zz&JUQ1tL`Nlf*mMw zQ8O-jE$*uF0lqIW=($S_=7g<-U0odKc_}c1#*{K_nGOc9hrwv&bsac1shoKJ5~~hJ zkADv)u7(nWZYoWoW8B%ptvHQB%UD^n^RZp0qY)_=um8-B(X#G(Nx-dnS?>g+l_zMz zD==bQu1uHtvpxLq$efM&wI$MBST2IY+&3CJ?l|+Afe)WA0v)R7D;jcI>oK{im;-bM z2#od$=NL^)%vlY!40Q^v_J~)EFfRDL3pg}+7B~9d<+tx$DUwTp6UDKECYZrERdRb9 z7_o>u4}2A!>CB*adpy1kAk7Gx#hV$O7$*mc`iM%Bwmz#)&td*oR%yBqXr#Tu#VuX~ z)A)s*0$Z7HpKqo108jT=jf@~t`}R+qYN{WZL?bhZ1ZMX6|Cirbj#>M;ec_u0bJ29> z)icO9vkn?Kwn&lq+z~uwAj%bo~6C4UKk_b;lS3cyKWV3|B=Hg>*Q2_uT=+)$?FhCPj?*w#7F| z*Si@W1LL);?#%-d0QT*ntAVUw4T?r_=4f1-aXQv4VD_D1hTRu|$aO%+cLzZ|{BdIu z-5J=~m+KHA-+{JH^WEO8ipQzqpeO#%(XPHcc$sv9g=9t{<7-xi?K{dx7%@?$E&5XZ> zGQXn0Jhe3%dzMi)x>17A8g&|Jg{@Pz3KRKcfz-fe1t+qA6O}3;Mb;v!BmQAe52dK* z+E?hDN6rXsFHP`Su*sFwrfmi3E~uk6a7HylcrncGO6B92(&jEz(nj=4xK`n8Or9OB z>|h%O5Qq-uHV$Hmvf6CFhBsK|lLu0K0()x0y9t_EI6W!I zZ@1(Qh-`COj^wTB37fw5F4v`v9`hP22Mh0h(h~|ebg;C#j;Cn}3`Bk9_S=A{@2mvh zOjLn#@;b4p=T-D>$I1h0g|;7@D7;Ce{a6Z^LUfA;?Gz5M85#Afb?j!kcMpK7P?_{e z7);!cUslEmpFb5C5$<5#8<`r3333$9VP}|7(SS|!rx6%0=Us(RH(0Hx9oboHGW;gH z=ZIM;Fb~XilV?`(+s0NU2K*SR0a3F*qwRh|hk;X@55MK-R#&RKr9H3mb+EaO(5;|H zgqY7R{3YH3+Zf!XbwP@&8+hH%8#8QrzSV%1+m{Qf#AarDH#cYqs-7@png7#1x_|JH zZIneEAc{cC^0bm;_G9-wc`v0BmArDVEONSQcrX{z1$O~}aBCNlZ-keYU?VhBoUF=jv6SMF%-nJIePLkHy=9(OH2_j zS&eu}#;1>;GxiOP9T>g)G+=Sf>{Rh+*gxB*dh>3Vhq;^4ZTn6uED|&}eZRma%nQYl z<$k22L~_Y`X01t>rgm7cqVW?!%5));NF1?A9oWU-7JbdbO_c*PP6a=E5q6o-h9e7KwV%;|nE0NR7A60&_b3?WEit|(rtpF2 zyx+r#4_!gm*hVZN(Vpefee-C{Ji0FfH~LPKet|gTg+(r{2nb#+Og92so_v3h*TXzkvs>gjHp{5C{A;U$bZ0~z@tgAYP6aT+0jb3!t>}(OVn!%i`7kZM z9Sk#p=EI5549*$sW1ZWX7ircTH{5bb-Ym4YC?>5{e~sc}U;<&p1P*Zs+HUy1CSi19 z&fI$Z!Lq>$!<;|!Z2JM%MPNj6{ZpRCN$N*F%YFdBU|I!UdtL=MFRSOSxT6$~Rn%2Yl5VQ3xL!7{he$bJq8hab|M zkH&0yJ@)emCf6|ly#{dRAtaaj3+H&C!Azp(>z^};)9(vowFMBmNv&oeS*Q0aRU-X- zK%&hpW$~lmHQOL%e=&BgqPB&pBI|$t(Hk%os+hli^fccR9KnwPesoR;1_Xue9!+oj zT~9c}&(*z*=CUE*u~7(;{g4-C!AE)+1HY2R$=Qmq_u(A}h6mZ9{kyjE6} zu;W>55rk!3&E26HZ9A3xc+Sxg7_Z)PFXd^UtvnBRa?eTfs5=Y`-r} zQT$vxa%Xf^TwHXSCD`sy1wxRB_LdqO#r>ZoW{rCj#+Rxztzch>&|YM${7i=1YT(d8 z43By3$NUBjcgG#@j9la;Nbz6nyqoA;Er?VQGguael=3y*aaEF#)o$H<9`Z`%EaaCj zBs-@Ld3glNkje#GR=YV}N1^t(w*V8!eek}ml*I>ReS{UT>@qB8V2jg04=_RBu7^_A z+^Qi7mei1mc6WIvcf7yod}w?KP=5Ar7OLl$Nj$#Nm^-Pv|5RZX)RIUt@C76ePKgf! z>M6PE)44-EU#Dv3)HdxO7)iHUX_w`XL;?>I2_Zn}*YwV;SN`+9xsifSgo#CB$ab#KPqz3D5c+iidj6eqmE*`| z_vU~@(yThdHULQKN5*8^S!*lasNKqn^aT>}RX=O1ido~t{pj&S$fm{kVDy`QvrMq; zTf6ym>}NSd{ud$(p3XM2=J+Oj0DEYIfbv$)gp@Z@K*(T~Pl+wK%hUhRUEDGZC~uOD zuj!jSSz`P}Uqk>B7jViH<<^q(fg&{aAGv6UF=r4qrD5J^UT_M-hgZ-bZD=E}lhtxo zPM|8S-B)`sujCC68rcHatQDMHtKUpM ze95W+h2yg;vo1Vhyr?;TE4BaZVxWzlUnNj!it+c3a1hQBR|V|Ep)Eh|tr%Q8G8Ju} z{FyIu1L#*;k$$D%6Ho-+`W}#?d+XDOD}dWXV7zYqLtpUdIsJO}K2wz$_eIYM0y(BKO>d{b*X~kND%vZO7!nd9 zGC2Bi9kWiuVWdc)z79}#{;?UO35>x{A*^|zh2O*HFiAm15;Mb%qM@H}WcPsZpc)|I z;S8|2@KTjVd1T|ag#`wj%RpIpm3nUYCXqV91>n1W0&wKiOX|;4FT_?&w+tOxOnArY zbF5oA&edmI6N%0ZS_e{x4$#<#>0xx-q%R{Z@8_>g=c#fYbD;|#^O7nfHu-uG6-Z#w zQ2EFx7JrpU4B9MhtWCRZsnRka1f8srpD{D0k32W$R4O4k;)SDdz)b(p2qCjbgv|p= z#0rWS=+HyuINaa+(=$@^O;euvqO=XW(V`gLl>9GH8DWU|A>C-HR8K# z{fnyrS-K;{ZS3>sB5#)^8(MQZR;4Shnj<79F!6?~EY7gocRB)e`Tp6ifBobCJc_V@ zF8v%4{DkC$u=$*z%zQTDGXLYhQr!qcc2D4wFto)Gih{`K({hKrksser`U<>L2Wq#J zenbdcL5l`rkK00!@(-78iJ%x`|C_ToJkG}r!H;MG=y#|aljlgfCthvedf_0u{SxF1SD#B!O3mJW$mK z4-Mtp=XS2K_m@B=xI5i(hE(t8U2tW^s-mtu#H*?tAdW9*&h-JpE>K~rKrDZM$f!{Z zyYfXiSRxrPc_fLfp2>k}ZPD6Rz+pfBs$4wd2Fw6xZKl2_+N?{V&sp#nGbq)&rF{CD zB8!NAxIEq^a3#SAX*j&CtOmdBTbY?@H*r0B=4|nav@~kocTG?w{QmD%V!_8qsnNQ(3cnn^KQF%?y`Xr|m*UhcNW zRJPA0+lfi5ZLASr?i{mKhf|ImPWd9)1(KvnlVHTHz3k12pH^H+H>>~9ylrH9^X!H* z=5Iatkjx0i9CmUm>)!?qc0^6DP58EG?xB(r;b^C}Mah4WBf#(9Bp|k?XnSV|56>LO z)e++@^k=q^HLey@_#~n*b3;=O!X>aTgz>`r37SW7j#5y-Zry$eCwvd%?g_7Xtr>`a z=?Ne8)2&^P>kN_>(2@1QWq&+Rd@BG2zIBrN4m0LjzcahTj!H>cmvo0cK4(A4iyh4> zT3b+LCjuzzKg{_6n(`|s0{mv3k;BLqQvV=AXy4%CDF)xo8%W*w(%C$tIf; zC$O@L`PGOUXdrIj7;pD=RrBLj`>lGX`&T`vkYeEti(h4xXaEK)Zn!k45@{;9)dSo` z-v1jt@$+yRISkS@*3&ofT=J(EM5H-`M%Z#H<+qQ2zuoPj zoa|i29qScO)}3O6rm~P|Fx@VW>_}KXTmt?R8VuHJ`qvB9e}xj`L)d*~Osk|%JPzi4 z#zOOPaXJ-{TAwx(?joxspyM2j9)6Hky`#50rmj`Yt>+1-qEd*jl%GC*HTM0Zp=HW+ zjUW;d7+4S(Hf-7{=U?Dcs`xk4e{GVL|H>r770zS!be=qVRtYZ4kCDXyC(}w#c%!#* zx0PEA-H^>HH0Fxl!E_4jQtdkQ))5LZ%T)hrS~BseytZ+dahFIbgZT9AEOe-w2?j9|^$bO_tw0@sVVkW>p0W^OmcwI?_B}S!U0Dl!U zzQ#-`^44Ca>E;{YsFb)>UEQr%fFToZ0}Sc>0wJ#E%JpXLRxySHf3d=e->pC$|9dOk z+X@^5v4V_3t&ibRUP{Dbu3HEKw-9C>NTA~cIxvoSxRszf1arMY=I7FKw$|XKMNajG zKyi;hv5R5P7J$Megq+yT=(!%#oc*#4rC@-ZKvy0Aq$D)8Vl6mbJ7)~GPcJUO=h#bw zT-9uYsR=!HB!uJ5oULv$SgL4W@_iI=6dnZ|&%$74xp-qY^ zNygUSdJi3H45v^J$yly=b3_SG2p*>eMJnvsWlCdIpH^ZLHt}0N>A4W|Yp?Y)EqXB| znYC4qTm+2?VxUsCR%z8n5(P=37%9#3>L{Ar!`x^G401FN@c{0= zx}czuBiNm3Qt_&Y=0P2u(M)+ldpLf(3{?Tm;4L+C znQy8O+XEKly1kN~tYjHk8Ug)V|BA^jZ~$a|2+~XhjP;#j#7`>J|B$(<1~h?Xpz}t? z!j#7=`l4&+P0|rH3j+l8w)s1@7zL2z-3K2EW3ftH(|n4$1%3WLK7d-+$gxzB)%kz1 zOWdD!asJINEx*|XIVNZb>*_$~^$u=?>L668tYa+-mE^9j*3yh3ADzh)UUPsTfU3^S zEE$J5DQ$zaks0`1=)7slHzqfXRYtK!JXs%8w7?mQJSCeWqKVZyoNTbh56;OcMO_Ac zC!(`gj8dsu>jlnAy;IK^_}+e`b}tBmEToZguAQt7ZN4&dreph2!}OrKzo#1sQ(KXVt+M9%c<- z83UeUJh*hW%T?M5HJJFy+mROvvXn+{9mB$}Rdsn$-WDSM-2KCRa3$@55dA zcsw14I=cjSd(SXyA|a0xno~>xcjdlWh5%^b)Vhm%w&R6XqH`!1Z#@7nW;o9O-arEy zKwoR56j8qAT}Cs-Y}tzoD*yz1JIXn9CDW|_tHWPkE(9cSV7&3;O$%7B;D>3ct*&aIacXDjhT~HCm92K z9J|FyMVMBkrMx7PXsU<>%6h9*5Th{io`%M|OcBAd4I>+bd*!HoR zV1|zf`cxU(pf7Om>>NYRGqh^6m2(Rez+X>wI9;2<0So&XTr6pDMiyz6YMj>J^oxEd zid8Ioo(~S}Mhv%7{LLb_yl4WAk^s_8Qn2 z#4Ffn2t>cKK=fPfX0JnfbP%4jJk$_>DZ@@KnVV2pWVFv_fI1LonL%2GWJdw`f!C5ItOMF395wbPB&#Qe%AjQ{QL?7LLdN! z2WVL|Ty{II69S1LeDQ6J>BNE8I#p@uO1Q5y7ttw7N&@ z&Qaof$N6)bzz|;31;7v+uYh^p{!p?40*8j3knYH)RVhWm`lJFVhcQX5t62YF3|5^F zcxn~w(kS#1!A;Fx+4b*2?r_s$h(FX6b?eK+QKojW1mF{PRwVCOV)eFHDh=?i<6sSQ z7eEXTMn~VyYW7Zlw#)FJ`<^m9hGxqZ9Jf3tR1yRR5f{!dDv1m9qZr#aiqVSUrBEyv zHjUlEBLJ&0!|Zdfjr%NHbpD)^Kay=L2<Xi~RLTCYpek^v_uOYP4MCXba=N;Z8?T!q94R#%}pS<*R*1~$`j}L?Qw2K3F4b&dkp@^6SSwb7H{WN4VcxT{8 z4(O_8g>y0#ES=w+rIsFGp8({%?mKq*e*>4|^i59kQliqFZ-GE@HK);3Vdq!gojj0} zr8M%K5ZoCIe}-A`9V4evG$A3{9?KQeQ z5wPfJ?6bsp;p-Rw{Q3*Oe?1I^SsJ{#P|Lt+8X|`YF6oJEftn%mPWKX}7E3hm)m<`> z)=F{5ZIzp)J$0(Oved_7oCnhU&e~Z`Kd+{Ddtc!g9V*`8+_)la{6|LPA)ty?MD_$S zfI4K8zm3^g!A-~Rz~S2T(Tv7r)+OL6VsN^^Oqh0R`9rW=(`pF%NuzXd=g1lVXYvH^ zZNZTPl~McQj~^!ka)%17{y-`Sc{2XY=I@V|E}hFWRTfG9$KXh%KHrA4lnTs>erHPB zFEd^)4~L>yxFNwI&GKFQGdT23hL#x5>WeIbWCU8W3d!<M4T^oX<`LU(swx?yehJ^PAsvbHT`u z)6s3o5<|{$T{kq43wiv%4kk$F1E%K(6E19rw)Uh~2_=k+Qa`JQAfZ!S1I%j{fP**l zCA={6=Qh<|VV!=o{ADvr~Jfv5Q>{&%K^%=jx?rUPn87IX+ zZLGsz&jn{Hgh@pvt?w;J-B-nJ_>C&TA@+0H?9*G3!vkhu-R`(Dcvq`t1=yF!Hub4| zT-1EYOyb3b< zf>KpUP_RnJpd)s@27pnyX?fxRPlROV{)L|tnWH~9NdCqjoQ#W>7&ica;B`u+rt_0g zDAGX_vI43lSLv<{NqsFAIsZi>&adT@)bj-qW2|rs+{{lV?s46vloj}Pwd>ZiJauZ& zF=X}b#R*Pmx?OV|M}8+jQDAez86U(GKp)r<^o^?x+;y-hnY*Hjs31I6v-{N9r-O}w zMX({r5?a?HfUu0{q}3l-!q`S|?ZYPvya zDPA~ilc8K0N^DD7Yb>#~=B%?dP42Lpy}e+|L2*Yh(vXWsiwrt<`Eh|F6xsQPrull@ z-D}>WWwhI^-c{k~ZC5==ZM<-T_>puURK=pyz^|tCxd)5XXP&?8zO0-acXF2{5sNJ9 zu4M4fhU3}KLpk6;O;JmMg+2E^=Qwh%CbxheP_I_O3Wz4;mr(h-Hgnzpk+-J4jy_w~ zC<(S|?7v)>9(qTDD6XoK6d3p89I+ohd$TMeeZ^4`P??GPo~ zpqw<)hK|@v-S;#oUqmu>bE*@97Jbk(_srUb9*jMqS?hV&h6ke6@XkcpNWSY9ND2AG zHhLI54xTXBB|qcgZ314!QC@k{B)!?ljUHNz>BfZz#%FKG-h>#u*-*OV3qwB_wb9P^q0IL%e-9` zO#352#yC-5zSH>31Z%STQPv`eUlasO%Ry?oC|L=-Z{g8$j1OgEk%gFL@z4_5;q9&h z4D)liT|v?x;2~ae+8${o9cR-L9!q!WEjpbl(&UGy&~x$2PI5vI*p0(CDwg|vr`?rot+M@biwmT#=yo>(2 z7OS`?tM=fqDOJ%Ta(gcmoJ%|n>W78YoYY~XiT9N(q9&AxUC4J5S!IXx7T=>cVNx%dM5jN@al&rWQ}i&DSQf=Y}j7q3=A%+ln=~lFGO3GJtkEHveuKQ;V#W~J3_N{q0p_H<`nH75G`8kaQV}y=b$yL`{b5H;5CiROT#B4^R ztX4^Ua7KRxs=n+ga!qcn0GnG>RhoVVi^JWuN;NUP?J+BU*cHAjw6aHx5i|Ai^dP0X zLEHKpY3Z1-99cXUu#roY*HZw*!iul4G+n_yq3kRkW_g)c`9s8S!dvcJ4m!qfBq zxmU;z?iMGraL{~&R8^cji^PC+Sef{E`1}obO7#0qzurHQSxAr_d zI}kK2FN!Tb7*f`~Y`^J!c4fe0uHs`Us}fBQT_a^v1|`0c=X~^L%ipU0d14kc8TXOP z&E-xiKv&&0TQ{SOa?x$XWbRdezUPh=tR78&t{HPdx zWj4Fq`bXN_!Bul=?8y@wxM?y~{KnY7Pm^b^1t+X$o(NhkE~8M8FY2vuh3I@mE3Kfl ziY66#=fpak=v8lTn1*J#HaQEnsl-5vAPY-R2*#xA(rUM3G*{3@TfIBO(X6X0 zxw1jLsxr1^Bl$+8`Al-l2qFXv%isTPw9?PHc;+H6(uT3rz|MMB(cXd#Zv9M%cmgJL zN3{}{%7Z9)j*KVzzgyf?e)2|ikdk5tHrDeERR0faZypcz-u{o<#xls#*v6K9Y{@Q^ z8EYcVgd(M~WGci-kz^Ois3>xfnNcEbQivpkv@laDvW1q>hMFRU@AV#aPWOG*`}6z# z(|OeS>wV4jy4L6Qd;*i3Fo2+B$o?Mp5gO6;s~#ZvWBC>D)S8Lcoy{YgzP?vI(DonZ zc^@6Hvja$<~ly=H+&S_O|F?3i? z`d$i7AxFb7%kKpjW@Ty;*@ZKr(T*KUrFGtfeWsXAe#Y{T^;kO~IFi0E2Ysnp%-Y>7 z?_BR?$`d_3w4j|xwLO-_SAQr2$tX%Kk-?b*k9PD*H?4~l8?cw|_u}v0F7=z4;51J^ z^hHhH$exl_?+SML@Hepf+N&xT?r|H_pPdkk?m6tx)^?o}EP+9iqV@N|(vv9&HK(Cm zvWYZ`;_AY$Ls>^Cev;Mb>c5^LSRHWlVT}xi?-v~%4Dr-$0=$|W ztM68~CLLRY3-#8?`a~6MQy(Vvl~d!rw8DQ;MVE|d4CU}#e3Kh1HnGoo!OU9$d1RS) zZryoA^5!mm@DZIC7-chzVWGWj7iFn!RA&*;fo$YdS>^yS-9(kYOZ7RQh+qJwctSFC z#D=ng2(UA`(EW_G57xX2iV6WJDzu&sToZ;l#&T(;x!2`WS;~hp5AW4LLM0GI%CG+W zPEBx^dFx~d;FPj0~}fwBZEoDER|Xc3NRZa*X!U0z8gUEh5B;Ri8Z(| zZyn$?6>n3A%ad~I5wA10FIHgO+8!E4Dx;7KHM!WGZM32f>oaZ|5`nnuiLlsfj-e_k~>c^i>+$m);EB=~>-*>gnJ;)`=Nc(K3z zbgQ}q%ha?gaR-fTrD-1)yGZgC%ga&+4KUB zA-1`ba)&RzVU5Yeu7;2Ow7I-fE|wp}j(knwp|h>h*EYPG61mN+yoC^+EgQ)|c9_Ob zs6o}XAo`-967w1mP;jm}x^XA--KEEHuTmTMIMV}Y&wx90J*dEl{}dnEqF?9Rpw}(` z(h@dXai)2oP$ql_%qFB^ffec3j~wz1Y??dI8E;q5*?jwVJ^@_Bpc>{;;wVcsYzq*7 z;Ds?UIz+w>MW%yE35pWt!f#F4MfFJ|P9$MYR{!-`ds`dZW@mWAopkx9+W{>HI>6SlEZusyUJol%c~wu9pmp<6Q{ zaIO-Cp@j5}hrh5xH{>I-C^zCmQ%!4^qFFiKR?mSLZxKEJa$@LXIATmLn0_@}RQcIlZR73X+aHyG4YfEhaHtb2UxwHeZiLlLXX&mVdz)x!PrjSL{iHSU$~!1 z7Hs(R)CM`jRo@W(B=vU>7=C+lKsab1Vs2RZcFc5&a;lHb6u@1w+X>EXTTzOY&9{Fa zV=p7ebd`m>2!lk(b2i{333J zN!MH|wGoXQe(u4iLoE#;6zEW&1!@eP<4#$snQ*vi6Dp-8R&ud68KyG3m2`OA^_F+A z<7{FNmlPL`!n;@)`}j3LMz12$4=RUZwGM4P3@;-47k035m3DIHXJkU>c9&TfP8lde zff>lqYw60)+W`(X_I@2+)ps~F4Bo_O+JW)osc?`jUcI}o`$|0wsKjBAZRHKO+5=w> zaU`*jUi7U-rooq^P(|b_xJI~|($zw(m*Pgd&r$vVV@@SNISefRXtLh%pF_kwBiV~+ z%4QeJHoSiy6$yx5k{FyB4x?u&X@QFo{4Z-bAE6UZ6WNzNtltpAGo7CmPd!QE0o;#) zcJKoGgx$V6Xm^X{3)pd5u?8>gZ0H#Tz-aX=|Jw+u<27X?RhjZup&@*Ikj}JX(bcq}%Focw-K=^#SskcOYdE20<(q7O*R| zcBAC;U|j;YCv#(Wx$>(}90+Qau9S6nweB_kSO%`Tt7bJ3v)OrbDRutC)d)lCT8$~T zu8Fp8Zam^CDk(hfi-kOv;bOfdJ8-4k=@Eg>Q}Xe){tfB@Ygh_tiEqB)fRB%H{XVN< zOTujaD+il8aDfG}Rq!PGcHj}cK#B7|4lYb~z~AMdP72=oX+vw!p45|H^1SUV>hJ1B zn*)>Zreb;B5tRa{6)cBUtZQe_#|h>RPELfiy{LZ152RRjFspta(A^_sb;d1a{>&mS zTHpHXOq`KH*#GJamw2!dIZ!(uLQ__`{D(yZFajd!qG*RhnTQGj8g(p_PbS?EjVjDU zPQ1|$nl344dD1uKaBc`xHx}C4q$fyW6uxQ@vJ+nkVm6s4*jz5~MSEaihxJGM94w|S`jbvse&K2j3jx+V|11!eNu z%Sc?89I<};bslVE4)$TZlB+U_e>n}_@YyItgFF|)p~-+k!q z%udnOu_HTW3|Pg=P1#8YGs_(Rxp=1>Ws$z4)RFOMG|ndZ1V@%N7;;)hfP zChTZwe#8tC?zNYWh!l}*gj+8~6s+{{ctc=ba6>h6Z#qt3Tx(OZJyWy&o#G;JZNw0|+CQfMZw zZW(KzO;0lTa?q70uRk7ckk5#HmHusY(nY56KzA)PhHLO+FGcD(-+aw>3J5iom_MHP zgOJo-Xq7iQ(e(AL_DzTd-I$APR-9ax*4qc#pW?C1YyP^k&f^bv))@Cq&JGH%Mu?mq zi^X1zCFIMP*FSTj7enV<@jtlwoh~mv6H9$lUvA1~{$fi)aY5tl)kO769>F-E(RQCG zj`v&X)^dU;-D;^+DjH|zEqNBV8I)sBAd*KY%(#~%uQmqoj={Nnj66~e7~zIHd+z>c z>ev@OO_(Y#FZLV_QjRm%o8G9q9>IxUap)BE{}j_0v^w+T7NE+#+=>{WZtwXCq)A$f zz+dM_Oa$@{Qds7tl|z2yMtX$c zcLRGg3+g_2MNRG6w6WoYRl7^R@7sk8rs2isUjMQ-fPAn!aJwE4wlK}{oUL7VmdYw# zAI6gEfbNIS2Az&_ks^pV-{&6Ra6s?mZdejO{M|Ehl6BI(;uBA35GZ~=s1$2WL*Pml zLub8Hk6nW)OyjwbpLyd+_W9)kQ*ztvithX7XFOv9hfya#K$ixW^4HgK)eFn49dn7v zeaR|>SyS9d-7+WhL_#$`wO0ceqTB7J7G1ge>*?MF)q^qQI=8IQ?N_TWeqTrkAq$DM zkQ#a6qUe41Qx1&*97-TGPWKKi-M8(b%YGh9OTaq0{L4BK;{OF#iUYoQAv4XBEaGEy zYFmIX_EnC^KI}}QZ32o(phayV@{w>QvY0?jx>XL^9)&5;JLrkI3?hQAbS$cxCw*_A zBuZQMGj&PNVj&vFS&M$+&4J>vcLAfFN;dMqb^v4)ae6mC;D6;ntrRAQEomaN_PwN=+f`0&hxFd~f?{+lD{ zz3bm=xFM#u645VN8)rVX^239Y+-bgu2$((QuulAKue7}n!|>8^S34K)xG@iVDBe*k zNurz~TNSY4;caP?4t=@LCex@E9v53)6iC)IB?{7st`&>!aG|Z2!kKOc292B)MH6NH zoypKCUR*4c!b_sU>mZ>^7LvMmzhmvQ1msIwr#wX-c=-ZDtasF=nq=at&1oeWKz<9VHdo3)(vy{|JPHxfh@-T(R541`lYR4U{vwARr`9win7 z4_M_gWTn3mss<2KcG;??q7ZroUCRy7pdD8X&pvpU*bCU8_4j{d7AyyN=lY>V>xV$m^>JVTm|DQGsT8z$Bu>^QQ^Ar=cp_f@tZsgM-(^t+1Q~@Wwwte&kV?$%)FH}R$A7X&1ag7lGWLaB^R7w0x#Ruf zoqJyuLyrxL;v+;F4bZxB4pp!vi~17~eiPyLbMNG{XZ0q6`ZJgBSXHG5J+}y+f(&bqSA+nr zwhJk{+hi+|?RTMc{UKxI|H*UKoB1suQ*C0D{BQ!>EBL=qmncH2K*SySWwL&2V;n-y8JZMmNa?LYx9bnlW`H zpyolTl*xfZ&W;1$$Kd~Kw8%`KaE9yM;K(a0mOD^!- z$(>Vg-!Lj4az?oiMWfAe>`X(}#?uTd10=*K1~cle$A=~Z&iX!Ut3FnuY4rUCAmu$4 zYSkC}k@n8gxwb5PS7d1Hm@*up?APj>xn~uA7m71}xYn|o-m^zKZm^qId0OvAg!eyr z$!-*fKm&HbKZ1+gW|Iy+PIwU}$E-Knu&oP?Z4O*=Sql3m2Rj3IjVK9Q+KDX$3^&H> z7o7(fBD+FenzUjKZiTmawGOVp5Wu%Dxms@2s;jQq9E>OIQV=RnjraRNvYlHc;CEoE z-nQtx=4cQw&jKA;vKay)DtXT82{3Vlv|QNA4Xk8o1BQ7^p&&6?C#XCQ$kbYic`CN6 zz$^IQ#aUn9;~C{|uH8#_M^_Am_ktB-5dYXaanb(Tx08oc+8U;HZUpOp`a@k}(f%JU z@~3u?G+JUea0QDE4-AHFu+)Ao%Dt^4{aR@}z~=QzhHV};D+?)S|U z8`a>Fo2_SFH42sa0*I&~N45bBv0snBe>p0`9Z*D1U`Vs_dx>6XKeoMZ;F{CycxN+m z(hfWihk6G<*e4kx=nAXdU-QD=)7qN+JL|ir*3s=>r~G~Ve1it!g8*>`k;`QH>#mwI zYNLlHZ$@jLE$`t*vIjXvnpx^emi5^l!281iPOI8;bIABmz$+uU71xc!#QyT*0I?7`|u{=X^Egx2m+IV)Y|Zo{HgC|(8|n{ zs~mxrIjE!}dSJM+#;)_1k>u2{S|O2IeKc!py9k^%N4UtT0XcIlIU7RiE+ytim>|8uq?qcB>#3zb>^O zF9o-#d_YFn`|gTHYvI_ThRBxd{^hFLo#US?SUE=g2f}KY!$zF5R)gRA z7=uX{P?kWe7IJS1*6fplUgo4%SX-joK`f$u_XA_W9Wg2*w_;$0`6vby|{@&ng zE!F&K*o|C)7iI8pL|9?m9nFaHL!zm+$%hjn4%nlKtwC}b>r+}QPCJ@WD>BL7v~Ws` z(k0LiqQb*zwntmCkfV2+;IVA7J;9k~5$Ft;3V>ffUSOTelg1Sc4qn6r z^}jrA!~0E+VpmkSRSjro8+1Kq&oq{^yiaZee~_s65s7xV$WR zqdqk}xa_DX`JA!roKU)eh#w#I($VZiwlsADO+zc;z%a#D;`U10HDYLwwUV`1;tGGs z=eRCUq99DX+m7O5GIJ5=Oz5r$W6NfPTWOv5770MqoG!e=io8zlfN?j1Fgwn9K2ci{ zo-*6i0d2<-WG_@${dfw2_6xvCiyC;x{NQ_KwQy+u&`(|ed`JmIxM^ET4)>lkYXF5P zB%H9|Kz!e;F?8<0M`jolD9>E3y8WA1^FtV*p8Op1M+eEnUX5=xq*oMDD>?$k-37%l z;(9{j7)=aXK~YE*8;2J>hl(A?RbapuuSZ7}zpt_Q416uU=Oea3Y-s462? z>nuOy*x2erZcX4DnTR&X&XNh}eIphIxNR34?1F!_PCjh=Q?VW}TGje)qjdE*$R->x zV&o6cxV?0eQ9P6YFvRhDk+bqQh+h#C!oJhBW1yR_D#((rNK%6m&C{J=T1k19w z*d;m9CT<19GcDZ7&kg0$O;i_};O9LfJ7HC`4})9{z9AxxkwAPy3zM#JP5p=mF`^G_bQ$*4_-f=~th&*-~`ad0`j3Iuts}yCDnI{sA9ZYnr z)0kHP-|P&k05szBCAb_MW}}|ePBf0+TQUo0q>U**acePpOMKJ%y!1W1MBF(S9`%-X zZrl;K>;t{~5rXfT{kO(GMX|lXF+_%gu4Kdt{2Kgvb&?nYEJ(^YzW|u0h^&o%Fae^< zBgHwC<&sZ*?<1rkSGH(5SDS{8)dE?B$p%2HH-9OA(%ez@@K6K)am#qnTtWt8x96|m zfaLFPn~id4?4%5gPUmE9xX$~?Vf)n9t)<1=7Fj+m`SWPXOO;ro)qKG5u5&$Ziwhf$cTea^~W~NTlQ3aG0ffQZ8uR z4KML5%cr}pA~wq6h^pLeG7szdRSKKGVx?Lds4XJ%tJ+wO3nVGEf)!&K~NH`?CEp z0M|?U-tcEQ^_KSti4N%^`yn`VnJWfu?|qk07O(mQk!*{Akl4}`wBW`?JonJkrcU`e*U+fNM1{+XjJVU+2I?e#qIS1tk(~$^6BD<{OX0MrVV25L zd$swB?5Dpb4%Y2ubjBRBgEPn(juA`{2+mu>t#i60)#%4BS$QGI`Y=^?*EiUku`~6k zkz-Ha!Z-+xH%~W=6jVmgH(%fqfnay2H5F?$Oqe5^q>bNpmv-@i zTH7KdZZv&qJGo%z3_DjQE>*VPB-PpXG1~}sX&Lv4P7m=BS62O1CQ&=(c0iP(l+x;6 zhz*K&^z}AU&?RRW$^Nj#7@ue)zagGhy1C4Pgs*6(lIg^l3iUoBay{s+W8_J!@zXhA zN`2O<{*=`9hA?!AfV!~_&!tRY45|o90B?n5&hEVw_HSJPrYW}oy#*U+g0i;|i!94I7 zkp0T}DnuwFw)sogpg^pF^-R;@=JU$m(3^g3R!0)aVSA^4A0*1$D8_iTi|LN1Z2gb5 zQ%}=$$?BB>Ve1(2)gB^T7%4p|Ei`VAm-s3!l&r4MQYlQTo{$TsfD5JP!XujkDiNW9 zl4H2HpQ!QOj;OmrxKkZ24x7_k+VW<{+QyhMtRU+)JO&19y0y zi&!#Lo|hynUTshVU+@W;{A@hbcf1^^)c1>S${G1T*`ksM8y&brRSFBdsrrfezUkX7wT|9vSd0A-6AUtAHO2N85B7AaL}z~m{P~r z=G?MI@0Y?X>@AC5%8NdqZ^w*Mc6b?LKi4b6YS;}zLKf|umE(1tnf}H>eaD7Mhg|^S zelNZpnHIBp25FJ0Ois;R9SB5ByvSaq`sS~xVb>IUndhMZUv1l44$J1N@)y5Z17QNP zQJLJL76vE85E-l1W;{b6XCSN6Ydiy*Bkgy8^?j+vJZfmOG>a7l`;>o?BOaB7uf_Xc zbfv7ob6!IQyoTJe2~qMbAC7>dYZD(zIHk#Hv5=M&#$Qirw+;^D?Hu-+U|61}>WxCx z7#-Y;`5H|m2A{EgDT1pBU634*-qD*jTRjptvX#*pnw6CKj0w1X`_4q3=vL}dwWx@j zAW2(qobl)@|Dc03h)F3miPq8}1kS(($66~f^Jmw|cQbQBck~U6SpTS=q*ukSM}B)) zDOMjJZonwQWcV zZ#byC_w@$W<3pVX-WbfN_eFEn^X&a=WqpYsY5G>!kl?=IkquvN$uueEDhbx@tKMP-;Q$Tkpf?bNHDWto!0Gf2=?lOh zazmsoDc5AaRPt>Pw{LS-`^O?$j4I(-((27ksW&oJ6(%2(W_Ej$O?hhc<3 zZ!s1nSdoBk9wym;L|XaP({-DKVAxOr^`iOW*=dhp!H%VEh+7=5w=zgR zw7L(yI%ekT_*+mV{QtuiIgN-dj7@KHQ}(*payjr2!f7Hvy~O25bmhiuJ*Pg-riKML zN6~R8T$tg(01N|->v&W)WiFo5wg*U;#4ROK)|=Z*53J6KH5;#dcwl+ueRva*oc>`% zDtetPCjpFIxeDcxKV-@NB00`P`>!3LFY3jsp^1z8cSy=5%5TLj`gp$R z%LjfKQ3?7!_+Mj!QrgS>5 zXi_&XPDzS%j0cS2VZ8&NaKVkPi~=4y)n1LE>?W27Zn$=Wwa@wxyAs4n&De4wd&Hq) z_7Y4Da_3(5F{5X0<*~Gr6HCD#fOTSE3xB<^Fj!%ax~)Z@g;azc;LpD973}=a*cgHE z4Q_eG9(Ay~Spv}!IRW)i0jM5ykP|=}-*CvFe(z0}@AbjO=N9ylGKs%yokq1r4jeda zt9I5+^a>9)CUD6gKVAerzT)?fA5OPD+PbFVOd^kD63!D(D|ba*;m5pwtv*YV4Yns- z-YALJKGKoabm5KD;9c+2Mcm&qfGU1#rJ6E0fSEV@oSl{m3}um7t^bp%Wyw1mks9f- zu_Mf(erurr^Cz!`y@vdu2FFibia{0H7Ox7By)dJ&cE;6WlHos8@yfT4vFYGtx-WyR z3S;Vwf0GpR1I9fVX=k&yI3}&44YdW}A6&witi0RRjWYQ)Ph=;eQ_FTmsH_{ahy-ps znz)|^;dG)ZMI#}#NsM|VpbYmYDN!an%#m z=53vhATMNQ#Q?t_t99tiH8=E+F`uTrG8fGo0u}k+hz)tkaeb{5PJ)`(D z?+2WFuYbMe&Rxt&MWFmU?r{?+jZDm9w( z9k1Co53D)LMGW37sf!B$4i=Gg-FRDy#?e%sBesS@BmnTUTC$=Xx15isZ?pkgLbN=I zs^FN2Lp{tRv5`5;{m=tkfYYn!xsBMymo#0M(eOrknN>O~kB=o6bPvg=CA#d;B}nf$ zuq6+`C}SS}P)kJKK<2Wwp3pcrllK8kO)0eT3nQmcum5RH%PWbwXMM+4?+1V`Tl;ro z49|Tmaaun2YUpLTOlvEM@XuUrGp#v+$DCL+EH6^wMb^MQpa1=Pm(s21D9zo zqG(USc`V50Ta$i99K%L(&9t2K;iqY-IV%I1(aJ3!1aeeB0SDb&|!0qI4bm^j|5tUK3Y$I zoqk{VXu|l-R2!L-u|sVLcrnGG?u)uXQ1o7y(zgCnv^>CrRh{W#X+!m()tup&Z##0h zJ^oa983P}<1Tlt#$sH+<*aixts3#4emcZYRIXNaI%zXp zwXM@49ib0#Vf0d+5n3Km>XMi&#$8>o#EegZO6FI22^f7FoPwO7Lrb?TaN03@KDk+3gmS4X2vft3k|*;pBv zjrWgUY@$%+U}E)mrpQ)cMr@XWU4>&SHw8?g@zH|J3!V&W~&i<4*b7kErp-YVK{HGNK$ zlQIJYZO|N4-Zk4XK7RzS*nfQ6Hza|$8zWc1=Ytfypu?z9?(cIL6+LVoYxPvtb97H* zh_O=0#jT@{oAoV}pI**B?E?Yxx}ROt+iNn-z}?$C015Of0t`ssCALg@-~%&G=H!=- zxMrE&t$+6-Tz|2jmyW0lSYkpYtvTbk8YNn0B)8S%DFz$o%CAmwNpaN4rn#tjF`^c` z7b`Gof`HLh0T^uqoo|;?E&K=p+SD-rGQqAS9t6EEmcAv>d9!>x-PcBHffN16Qddc$ z_9dl7=zC5e!lY2jq22*r4f$Yu`1Z*1CmgIky%fQpokRXKmbni*&{4%^R{zzi=*}T4 z20e0nt6kK>+6hKNa#-#DUdH{6#|FluMydh7c|266&uoPGQ@1hiDPyAz-|E69KMij9 z)?jkx<+B2-3B))xh5h|uV^+(>5nZmKp0PQxn6eN@$R)Xxf00YnolF#Az;nIYy_hCI z$+9$!j%HB(evY1Kiif6MuHz-@#D)^brPT0Fb#bD#I7ab~$2}a_$JvYaj842>;lcNs za8!tN(lDgty;?VyXt`@Fzu71D)yJWik#Q2CGvjeXE+NK$msK=>=xG#U zMVFC}b(Jw6COJw${egs&b((5f`~XnnxQzvY_t!F#FP?WW3y+^zL>NjaJ3+#S-^zm1 zX%z8hg@@o->g`*L)_W2$~b7Oo>ko)1X@w=Vq89Ha3ddd zj?K+LT%bg|IKN#Mx|!N{Fu7Cv>rnF{+?O4Bno3#vC>7m!ch_5K!&G$S|faVuP~ zGwSZelryu#J10<8d;cl;2CQRn4?0o~D4DE$-NhOu6rW9Zl&5lgm9%P5kKPGjJrG<= zb~Tn8G3f$@8Y8I%Tn>Wvq~dtngQpW+xE&N+c*I*&@U${l)Hx;0?Gwpn2m>bjDrrl& zI=XN~gL%B^oR!6BCr{kk5k?UCQWBu!Z*qXYt6;VTLri3n+t0(=cw7$Y;c_pC!ko8$ zgkrYvdRFx=)8;HM+<1A6(~#H*zi0)bn)^3*wMl4uy3Tu7CmOf_o`#7gDQZ zU+NxexO>RgH$V4!!RU)+5{hyF!cnt4iDfd)R{aLTX_r{#vTr%9 zbLYe@Gj#Ye+|*T40vP1vLk*Q2+w=LGrkAx9wVSYeZNc+{L$}yI#o{lmPt5 zqHntK;`ocJ|HdCAe)vNGi^0VM2IvB&d= zskRlj)-X>e+AeTuB8s8iT$`j&=Xuft110-$w?9$GpR8_DMYSd1V%&0*z0uX7%SR`% zancVNL0d0z zcS$WFqZnuxCRGM910(XKK^-vTr|C}ngY(eNS8|pgR}JPoYrYK}PO)0NOnlE|XtUW+ zffWgv@$_*n^Q(P@5!S1z2bLD|kA;z7*zm{h7O7O=vHUR3bbB{VEATq$>Xu;qc<`D7T>Dv(q4T1RH9r3<241ap!pN*GtYGy@c7 z_X)JU6sG6|pI*8T3Ulv{`b|>78v^&GO_H+O_)NTEgk@UQyfXOkWDIsXI3p|8al&|m zQ-f~3V{+JZKPapS4kFB7rqI`O$TPX>ttdcjGVl(T!US`F#r_h9REVcl$_M_CYYv&c zQ;*&`qB(dSxPp<5$RW-~Bp(6>A_Zr*9SG?G{Jc{PIYhSmR@bQZ$q(ugzK9L?4F1sD zj;N>ZhTFs5QYItG(b-1M+T;KR>$!XT6)A?Gp8Lq<_}1+}aEdZH>uto)C9{pwz!GVy zK-EA$r~zlV8MdTUXIc8@Q^Xh@m&CPd=%7I7Bl2msn;XbrlGE_Sz5WUzuH7vOM$=@NnBOSv2tx+Ds>$l|xOWi7F13KHXW+_#hqv{YJ3 zz2pZRmVr zXXwZfND->nqM^DDdSuX6PMIGXte)Jg?@p-Bg2JRQHB6VYF>%P>BUxV^o~@kGt>l+v zF~@T6)`n-ok35qfT+5su_d?tot)a&|Qa_0WTqu=68vZ|jE>4P1fY{{EAIk#A5?lYP z>8+P+i)&pGosJ@)(?kd*j{#W)Vdz#C%a#Bt%*|QEF5o=MOZO4QQ6H7z&ACz9&c9eC|h!1yC4WA;V?3@kQy5C=C4spFVn{6z0Jl^?RgCFb0JP zu}GVkiEoVX#l%dK79q-B75KqA5MhWt2Cu7#X*s=-e0h3*^Pq!c!-!}3EB@u~deM$j z%t|pvC6KJblYuC)DlbHAXxH2}z*;3%yUO*igwVN1sp&jtHy{)8)R{fdx+?b4g&1IY zYdbB_^nM_uTd!bOn-X}zmv?WIjVCuOfRwSi2`-Gbu zq2+9|Z>r~9@nC}H=oof(@p^XMM$XWu(!i;cL8m0cAv#yImK>5&jVVj*rd}_&Dc?xX zZ|BJP_K*{pfNHo?gjj)l>4nZQl6xbvcKKIJEA~$xD^{(tA^Oy|;i< zBl9}gnH>1OGf5rl0g3V$ZAI(93!kMUwcMO?i;N#R<;E{YQNfR#O>dQ_s(5K~j5C55 zC;W$TMv9!Rsh5sWEgBd7W}FHElJ!~Y5)QD}lR!G}I;_wF;42Zh*Uw6P!3RT-L#h+Y z)9)btt{x-q>q|!Go-DR;YEir8^jmXzKu=8fUqJNG@;n?mt?^)-Yj#s+;2bPj02&*%xOXbtQVih_R(pXSDyPlQ$#N9L7%FLnZ`P#+A{X!&cq&$mp%Y0FrkkyTgPz zLL4T{gTwE74~M=3{-?E^-tI@fwTb$Ewc99)EW(Xlx7({8t61xT;M--gVslXu$xqQIQ!O3SPok96ks?xc+=t4!QB{@)_h8u3L<1Y&2`%cz4iC~zbv86=7 zK{jN(n%^dAjimvWw1sUg`M^ILo z$C+bck2LM(cj^z3aYS^hJak>-ZoKSF6PQyBX_iv+)r>)aI6@v)i4%|ES}c^aOuSBh z=tiG33;oc%P`<_3U~W!2E^MsB>Ex{v`1SW7zrNq6b@W?td3)agFHDUV{pS|rFFt!z zt2mYasO`vEH-34_(iF!nh1f!r$>zJ86?9E5=ZWmbPM}-3_`?ab6{M13ClpGmM0FJ^ zMY+Up4KI4FD_sUvfkty6W*_0b*l#h;5uyRJMgx+5g9v zcq?Ek@d_$Nm|OX9R_BzARR_iqoN7J>x=hiZ6XNN7i2g$dDZ{Okvxe56zg0K)cYbpT z#&%ru7l5K!9k3P9bv9`9%?)*O$fiNissS$R41a5@{^FarWsA_z^MUOi1hzL_EIq1( zPYI$by)j2*5}nTNn#hxKfG-8D*_dQ2NWJ7&w&GzD&k+sogH3C=9mGkM$CTfhLGPpCp5J0d8*cYC`)^%YL?M$ zR2mLGFA6?C^6u?{df~`am$4bIn$M5c$r$8HMSoGa@cgu^!MlU3niHG;I>gOC{->pe z9#9zkhXeTC?Xp-*rEM(X2SVkW>NsLB5$Cn!pc2L&ldhZQw~E_gk*35!o`qDhr9`*DiZ{DOF+)VI`61P^YccR$bZM$ zL6C^HafMR|?c8U! zVdlf#Hw}`3DZa~7b@ysNblIo+bNaVLXuaMNoZf}aw@g-YA|{$#lx(x@BA$$!I>xd>{+SY-c!duc7=o< z(k1(vU{|gu7q*P7kW1S5kKz5YH3@Q2?^ zt)6)?m{NCfZfmzyoJD^U#V zvwQnJDTc*4_vXs@1Hp0`0V%C}q$qn+==i*JQ50tJoki(f>3XiJq9_GCq0}{{LvJAFK@W1EP=-HcyQ4H+-8N6nb*w^Tv<1-n87#p_RUO*bh#o zWghzS8{X0lp+VipAX#nr#p7Y)MuOA4YoN(gw!43+0Oh=;=}Rx_G8e^(7z(*!PNH$-3iV8qMLBf|3}ffvL~YIp zXhO7jemg$$plE5VCM+<>m5;`FXD@Uy#{xX3>oPle4xQ9@D1U`n-mDj`DUGzz#o`=% zb@}A70Uqgo>DJea8}mHvXX-RQiV&-%#5~v4oAfRI1P$qIs(yEoerzNrYidIu+?LF zWYv(0wK+(ui>?Pgn?K2uslT?Y43@75zx9|PdTVK90P;|tzs&32`tX!k|D}PZWjsQ5m4JG z1WCDQ6et;`-*yt3F;}eNFi;G!j4iyTt>PO%*RIaX6iPt0tM=1GWCtj(n1>KP{V>2Q zmFwwM4hCXF!q6tWn43dQXm{UFql(gP;rE8h4>BZ|p&m3nIA`$^wiM0v4HFle9vL(! zgvN~JPD%c&m%;q)W$2L4b+vQRxA2jJAC(!dBk)$Z@@r8@DUKoDM#pr?F~)M^Chw)t ze69$&Pl_!+at#Xyj!kfz!r;angpQ#ret2!+EhF?cDa`9>4V5FdLwJl{n{$(P$|GLK zgfBSiTn=w8!Z@)^VgF}-7n`qBbtI>TBu}Je?mRYsn4q$L+3E@J9n8vJuVs@Hg`1gH ztzHPnPaw07(T%_e;+Y{^GGIq6OoNj0kz$&;pIko`Zia*GdV#P6*@Qy395DJ4rcY6& z-sf&rNyxXonA~sO7ydRyH^(pMJ|ugzy4`(R_KPc$v@Ro8cwE) z{h)_6>Eg&Mn$CmOoeAJ$;@5D@^(5(lYIdWuzXy%v;{ZZn6Za7^>2)~CB|@SY6->IE zYocI_Bz~wRE6k20KvdUppD5!-UAV0ND~VW6-TcHrC=nwfKo48$D@oG6tfY$WOg%Vj zu5^|ilxou(&N4Rln$Bw*JZL%HU%oQ#%tYi#X632vFf~|K$vg9RSU>O>6iCvl50LpMyPj(>K z3-2`%$6x_?r2?R6dX8gF_Z*QU(OIKfeZX)BE`+~S?|zg4ZODIBpu|eX$!~|0Qv~8B zZKgqypl<>y&1=Az8dSOvJ7G1Hw=VX8fhgr4icpJRydM5P&b~Yx>h^7aY=dF! ziWy5tCQ5_JPQx%t(a@C2Qez#JtwAVd9Xk;<_8AH-4^oMUEZMRQN`!3LN+F6$`Q4+Q z=lg8m_kEAwKh1L-_55*PpKCd<^E$8l-Uz8G=e-#E-JXL~p@glGO-Hi%wGsXyO^(OZ zu`1-}eK))9unN^XBtLVB@!moiqr^1YQm{QC?EL$~EIDBK1K`s2o9r-qzkPd=(d-Vj zTZOE#;6kXWfPF`AECiVl7Pc*~zfVs7ac99zMJ{kw1vP{7A`598L+t@ z1MawCwcSqqK{kh(RcH;?MqIHb>oL(IBwr533+YI$Ba*}3O`!m#`V2BXfI6xA=gtFQ z@PAd~=R5BA>pCt)zgN%{I;8+2hYLi`)6Q{Fg1tWlv2JsTa&!2xy#~$mzo1l~H>E#I zjsK|w%fA~28-CjoF9p|+Y;w#->VfJ-5)4%lo6ffB}Rivu?;e-+*8A zIrjolZy4b3XYvwQI>p2BuS1(Q0nyF&c-9S!$d-PvBL`jwW&Fb>w%iSD{OkiR+h!yI zXf6SoFjH`&UVXaOV;%j4v) zV5n26H;?v&JMteQ6>>)^Y+H<}CvruW=!*A2>2#1VsA6K=d8Il@TWhSd{4%fo;9qdV zNCP#={yn{`=_TNLux&~#XNXs2y<_AFYrq9$M=c!tLstZdmS6bu)TXVPN&SYb#~J$f zf>%&k^US=BTNIZ*uS7Q2EQr)>J@vno(nN)*pULszA*VdW-aYC-Vu5Hq0a1z%q7jhu z-#=Uhw~e8E*bAh(QU3n^EI}Hd*Y}~^h{6+Jd26K1XP#14{|9LCf3%0`#4(1jgHnDD zw3dF9V5(-!D6&#Yj$nt;x3E@RSQ7RZ`{MHmT&RmLHY9n`>?dlsZm~Wo73w&BAqBmE ztk2@w#iT_ywBXk{FxaFnYB-qH?g#hN)SIzxmJ|OmMXq5nFtyPY9J`nI`?k{`k#F~0)g0{mz3~1LEGzhM*v|{x)&t9op?JOnNDBuP zigs%X!d~BcL3?)T`<}%~>`Wx3Vwo>c_U~Y8KX^b?8&n5rGlA1T%S2(5i6Esnzc+v9l9Ek0~JdF#p6Y^@zLzY-7qikcI zi~gQ8Bt5)_W}>1H_|}B^@9HdaX6yrUzTR~_YcL3)hqHQmakbpe` z@>p-ZYzfu%c)fhv>C$9p+j5uv<=gg`P1RjZs1xnoHwxjE3&ox_ew~Ib<*l0!)2_qD zd!Su^yAqKlWL(cnh3HI|+*!H#ThJTZ7bAsli^n7%Rb5=YsFXKRh zaOSx$Cw8r6^qF-f@Az05+vOr11UKF3%lU979*KD+B;Xbd8CHCErbjMIy>Uz1Hn!AY zrg4;aTaJ+jPf6^fYZ-Gv{ao+zEN$Z-Y56aIVN!*i9wxu26|X68LYntt_D2e8c6gM} z54Z_rU8>LeTK{rUB%1Ne)xle`_lA9Ry=kz#lvRLr9`lH0ggGqxkCTUmJx*ku{C;Dl zx`}Bk?uJTdnvQaijB)~rV9re#a4AL~O8-pQF1s!EA|!#kVTi|wBQlDbNJ8iF(@Cy0 ztyL2y9n+4yEk+iLqc|bqKEjyQ*_hB|XbU?gzRyg-E2?Sqw}_f;D$Bd?#O3grzM+Wa zHs~(DifNOo+o_8Tdo=>B-R=Xd&}pkg0zaM(Zg=GL7rnx5Q-k|j!M|zqeCM1vy2+)B z9tKR~M?}^~wH8O&m#V(K|Fr15lUc5yhlH9x@-8xF*qn@W&v3oFP*+gt@=f}DdriJl zheGa;l*)y+J>!Z(xN9BNqv{3wtArxszM5!*1!)t;Z&^HPV!9lt=Tkm)8OG;0BhO2h z96%A=yM?*7hsD}tp#s>5hz2|18)0u5WqJq1W+2LwM#QfM7@3F|l6**0BI$Lsh!F>z zf)j*eImkyHJ?VL4$|2Od+FFmgw`?|Q$jy;JxJU26;@pEVtN!FIuVFK%?;4+|dNiX$ zFfr6xth|jYNiS+O^N#DJf0{Xfe4d*W;9qR!wn*;=!0QUNJNu!)a5kH-R>>7~m~ElG3(jwFTip*r~g%G?OZ`rCOQZfvWP@vnPS)VnGFLxOa$;eAqLzsH+?HwVl& z*{W0iHPfC$))@uIwq7W8_Z$eG=a^!i-*ZMac&Rynjm&h>*i{if+oNhSmS-rOh|=F~ zMOINVR*dX~z#atJl@>6ijm8|BRYYOLw$l_1JNo&!H9VN4HNYnjO-hUE~YD2T(4vSBVq#w;dKD|rx*PmQB{!=2d> z2bxV4yrb|F9U(vNs4PE_6bzn^Ynk3^=;k-`Kt%CG-0E>@yqgh|rcm*q6|##4DF0yGVt@@XLqyYawPNmJV5j&#-Z(M>T@HABWI6@MnNv|)97~$X) zCqcLo2iX}h)ufClV<)5dC={wIyS@O*?mltY@|?Odagy+gXbJ7lRtm%UZ<;M{WqyNNv%Y6zsv1!X_LK#!swK2 zEToo7OiPkejdPpWd z;xuf8P3(i8Nen%Vt?2$tfa3Nr^P@DIkL*R-0u<9!^IK7Eo-~^RZi0^NRN4K+{HF5r7-8e zXfC_S$Ob3Hbr^)%9*Q45GQMF96f0No9dXnWt;)XhqgjJw z3zCS{LY-@zt?i(z|NGiO{e_|Cjsc)V!R_I)*IWvFMpJ!nET;NabS%35kUY%ii%HRS z=1r`eRszpUu^JB4(7Q?zBSj97MHbDiInzqfC2vd#q0Yt8*88}K0a1Z2L6EeiEz0XM z;3CK&luJWG9oy21fNsl68=XUhFe&nLK#+b` z20AIryo_##i(-xkkk=%p@M4Ig5V&STwHzv}sQ~TOVx70RqZ2tJ(v77R%PI{Mj!D9Q zduD`N7Mocq<6kz|u`SnWRq6n)gzG`P@KD~%3&d}OqYs|}z9Z3AiB(B_l}a?4gmSb& z{`7-Bkh>81D_;IbufW5q(zAXn*>rq3FE~*Hq9t!Mykpb3>AM&{?y&s-`N3#e%X7yZ==i^hJxmjHOS%WesSaas{9{=}K8@McUe=mm8z@54C)L>$^>q@X-Mt<(%KY9+bWmGOe{ylD*73#>{@ccDrfB~3KpcXV-8*7yvmXs zMHMpvrrSfzk7lU^5P#?c@2O7F4Keqksq_%b42XSEF>G5x@R4YVosfhrq}NXXDiNS4 z2*FL*FjzG$$L*oY^`?Tqrq-!McrO~Yi;_6Vl6pi8bp35(KGv6!?of@z<&AUO0f z$`OJ2Paz1S`k_+ea6Y=Q8%=_mN{#?^cL(B;Jed#&HuT_u=9n!7q8!`cS>oN;!T6+_ z&4*j3!FxhIOp5()m+Z$*tHwJFTE&2Z>vot>&#E|aUzZSFA4$G8cQgQ4Kj#bqMsemJ zOO<5erR%?CMM78)uSN(aI|8wRvX z{TMAgvOcfxOS?w@d*fnmB3GQXSyPnI(lHU~$pu>pD&9+I<)Mp+PQygfl&~&4+Qs#i zFNL%@(`<+wMVx{ZmyFo*DA+f&7rz8{!kDII$4{5r+MWOgRS?8kY9L24JKT!oIg~f1 z64LZQTWzSjw9q8nQAX`d&w`E!f|BXG{6IS4Lh&eNyR^Z>7jI7Uo;Xcf&JZ}Iuv*&v zLlXe8t@%~wvKv7Xy%W;(b#U|uvfc}qt_zYT>F@aBpXW91EBDaSn;-HLc)Zdu)gD)p ze%tE@yWo-t$OwF@PLJ;MSydDrj8K3bj6pS~E!DfS|NQPIY$uvo$>2ZVJ^t5sPv@s_ zrN!QkGWVvT`q_xDq5_?;5IBFs&`Bd46^MJ#Y85CZ9dxR(V&uy((Qq4fME`hWh#5z& z2s|#Z@yR}pnZ4wGkL8)ux`;1Qb<+ghAj{jWFNJEF>SrEEJxDh5KK-0kFrvEcsd(^c zo~kVV7%AJ(?IctZFUkEtZf#RrkCg|H;1zqps+0E5-Q%oQni36VU!l$WEuMO+2m0fk|89m6>USfSshfw85O8_9+LKX3xW&EWIh3>ccMn& zlhIzjF?1`Xc4WLfITf~qFX6=8e;Ol~L^V`INIZ+Ky8`V_W-s|@yr0Y5&J4Wr2~%7N#ED>PJh zn}$-eNw3M*u?<9;)&)g+-OkR0K+I>Rr}3sN=^#z&wJXFk}wDF}?EgA^bMw=j%2RFLpr&&@Zo%DuEOzjkxL zkN#cIO|v76g`P-e3*no+Xd0Imr74qD>qxBtUFko^M~dTf!w23BaXqQ=I` zFq27Mf@H7o(_O4ES1BSo7!boi5tPP04W=R4$y*kf{X!;AP`ZyZ4SAd#B8i>oL~Dt4 zhk)MmFik5&kv;|vAB0(Tp|hzcNf^7MvLWyWTi#%cZF03Mt3!Lux?b$a3<9ue&dIkx z;BA+iewE}fZ}bNm@#7{sE$&pVZn^ z^`O7wspqcytsu+kfGjuH*Vq2)^Ly9zbd{-o*$V@pCv^+(erf!7S|T;t*iL8HnQipT znazB|yr{W%fr<)Y<_EG=_7Za-X=>6XYjC={ay?s1~^2c^@z>~i;qRjJW8_DR`{4-s; zI$s_)O&iMuCAzvRta{8GFlFWWbM?A0myjUn&tx~sWqo*M6a#WQC&=yDnh`c;_A2Rr zr=+anhL6upeaz{-7LWR$r52qb^p|(pZ^d!UZG9#Vxg+fQYxDZLDuT2`b``3N+OVF<(P#L79Y6MjeT! z<;uy#s<*>cF-`pRP&XPf9Y`;nE zis|JMrwama7}Ks-2Uwsmn&b=Rash8lUKMBqbxsxQv~~~0!UE@ZzKRA0X_Knop2@h0 z_dcB{SKT#D8}7BbMsUZi?)+2QJ9XDKZrEHL)#;5gSLL7mBd1C4 zf`m(iUz1ebZ}EVQ8=$v?ycjQu`6Q%zPseow`MeUk$oC)CA^w%jm*veJISqoD9(w1- z@F4X&gVZl-J6PT^XXx9XwK{YnV}HpHKAD~e{|W_vrGH^kW_f$Z?%cp-f3-A2h{8mVVG`5!d1H@*QMdq^nUi62JleZ4?#D0)9<^BT>ndX(nj zm2`mayl;bu5CIVp^Rts4J12@xPC|ik2}T*KO5w^mQO-+z|;Jq)zX^!Tm#>%@Yfbj&gjrw_5;wC z?GxYGN$2|R;T`v(c%vn9vH};l41h;)Biz8>yOe>3$R=Na(X%2phjuACt@zivzw4Fy zsYhR%x;mULeF|QB!k3FVFRM$nwE&Uu1VqAIr;}TIchdUZ%C+oE4IGa&A30BP|Al)4 zO>|q#<$Y|JuzF8j;qvYA+=Tv@OXgJeG)d_aeN%!?9OY>ej1kr5LbGvDD3TBqjnoTk z1F3+Yd2*C7CU<}zDU8YBr*QDYHP|p~Xf^rnC!h=*p&_4=(Q$N=WmiH7M#z<3S0A7 zM_u_zJ4T}htV$bF8O3P_{0-*9{H%Vt0k|Kr=>6NwTC|(Pg^r=%_ungCfI`3v3PF5d z-;s5h7mSsQ{r_o^DIUJ9|KbFqa#xJWoRP!&s{iT)v9f8#f@#mtCDnL>j=)mv3PgE{ z^Ys^!^hT0J@aTl>pkoyYIHLy5cb41TQl^ zcnaHd5_L^d=TQBmwto;x4)ZE0*|XEY*=vVsxYeV8lpdfi z7?wC*eZkoeXK$>f1m1=@`kpk=V669-fIlx0HN;uBR45U)eh?rGbs6^XGc zr;~~+tQntJUeI<9BL0=+xrM&?1gBs^Lu!&27MR{5+o+O?h|v1RC#fOO-_Tkq-O&Zu z@a!B39(QazHr&`Ypvx`cA#>KaMkO3C4dCE@ zYPbCZOOO!GsUS}C%r9}eCXiBHm}0nq+;`}ur&diS>bL(VbNFAmI~YGqo;!j=TJ~b{ z>p)6yU12m#F?(gzt~q>HlWBCy+wXeuFQcoC4v;5Z%vb?6H{}xwssSh0MFShvq6-j^ z67w)t40a;3-fl79ny1K_S~+fhqYpQ!-L5hsOq@$c@{si*7?X%fpKz+6ARI+{jm^j6 zm}oC&b8N#Annb5M)4NcKYK#!!V?H@{O{=pfVDQaSZOy7dQB)2N%&d^P-*lHV4rQL= ze7Nn);Hd8=!Tfs{v(?PJRRF!J=f~UxrVw#*>9apiPXRPFvfl5n)JR-;yxM?AlPAXi zHUnPhp3w~e@ixk6z3A=rmCV~E>)bN+|EE(&f>Wc5F7VTh^C!$j)0DA_JKHzB-`tg^ zQcOI>CjJIp62eWWl%2w}BZTxZ_=w8T@FqdQ1eDKf0w{$9nighzcZK*goE$@b(kmJ+ z2an|>^g%4S%l$&Cy*NIR9 zQ80Xyd`Ecb6$Ze<$J%XI!(Rz~AVh7){T&$wc3)N7R@E`IrevM-qsJ;49_}>}!Nt2fALI@+0RubQkSd#*3`CVn0(WF91tVNcJ zCGi#{?Y_1Zr$Ui)Q;a;)NKvO<+)Me5jPeS}Q9GJ$5Ro~ zAyfw1E5Mo_y1yCuT%?;`s01}ZNbq&@=*md=xvvek&76)n@yv%wxlX$vFhQ}4a z35E~qQQo=}R;*63hn}GQlPyj6+ZanqoWcdDzZJ47%Rl*FoPw+Bh5o`?b4O>Dz-VzV z#=;N4Bi{b-P0huP?6w(M)scC$arkkOf@hCJ{uo6?R-Bc0w&qEomu5C|dBP@y z#lC*BtD}Du#!doiKuP&NI?IHN zGt?A26EK)j&^3S&c5W(pre~4*zyvdQgVNyWf{9K{f}V(ij(0@=n0v^4dB$#e2z5mJ z;>yhK8=8|mo{HcGl}%6=RyA1MD2T)RSmAJdRLD}2HD!=)>jaF*%j92-$n9ilDZQ@% z6wtzkPk|P|_WcOVD`S?G1-v(g3eD^Nc0W36$3peQ0UlCPB|W;X!thoT*bK(8EF#1* z;sA^xEatyd@x+3pA7)&eqar~xWG@GKorF4hMu|XSacEB1$X4-$7Z}I^7@D^wb6m0)AJUIWw zgYy1cZDWJKGC)QL?dpB^*%*&G>dT|W=|VP9e7|aeFe8&a5b`(YKMWBsN3*Ok%d!Ti z_!PS2vLHQ0c8bb@5ZA|8Mg&fnal%zduLJV23RW~^Hu-WaeN5#*{3%Su_HO32PNbAd zKEqcR!_S9N-X-~6(i3+g+?}~nlVJ(~(Bp!r?sVZaz=yLO05pmPg0G@+tE~C-6S=H1 z8>Xvlz00=d4OsL%8sr3fm4OKGlU8_tj1hW=H!#48TDQ zg#K!p)EHwsX;t#iKroB&giW%6 zKrm^gv#W=+S!AIO67N6>?tDw;B+j%>bjfo(!Chd<{0l_+3&~TPZpFsL3QxheuwjZD z1341e;ie?d3wb2vkfsuCwL9Gl1t#Gq9$h@++b{vjXSQHMiwq*K5L9?)_|?Hi zF89i6mIbavdiR&(PhXyu$d)wXlL089w;O<>7t{rkli-;8Gg2R4{sEo6uEh3I-PG)G z=U;XKI*ykNOWTCGQ80rA*^pSgKB_hK@!|Rlsr9-2Q%@q1uHSNkD?VW?4sijzbW-p0 zu*-+Xi}i0u((`csr~cv*4&*k#4-0I@Y>&Vb`a2YiwIV-4VBdrCoC&;=*hzeInv${& zN74PrO8LE9!oVjerdMIA^d7*Ke}pK1B{}YmrYFm{!(}ni0c5)vN&_6O%Z8BvBWYze z_(>nfllg3xG1OXL$AIgkGIoUZG>+q56!ooI_dH4Z>%I2yn&!hT)AEKkz0A{iiyHjm zZYjK*>2qiB#oat4vS;5duduxqZd)rzgG_UEjSDXWfa_Gmp!9d#2Oyt zd`rp@&HZ+(a`|oT=$P*AKc^6xT^Mot7Mo5ki=PQ3nrXq%4*>}TB}p`f5-PhzBh5q2 zZL?IuiDCvs-3hz&8+q>-8|L9rUL(DxqeJkqXsruO6g&AOFECF6Fb^U!FrUbds2>M< zCj~op1f(~HEd|;ZOFn8ZfS?ze)E2tX=Uone)QRru#w;J-r~Zk@b7ufg;XNtPL?(fH z>IC1Q5(iweStP>T?xK^N4^f8%QY6JS?g+R{Z%J=eyH^Nu$Zk+wq*eAK>y>b}O>Zu) zUh0S}S${lmZu!lrK}HA29F8DljNS(?&~(=7=QCK$BP(x=H~2aJ#Y`Z7%&&_F` z%hrGRRmtiIM?jLm%b0FOjXgfRy^8j{k#URBr6we0w#dq}tdw{-Zxqm{6TC&7Gl7%1 zQ1iq@n_Kcl_n%f8?{yI-eBq}=UTssQf0UYn>7@B#pYCZ-;Kg79$oF}c@X0&~yZUM? zB{ujz(rW_vx@Fp0HQi|Nb?fvU>DL#}a$@Q=d-LZzP;V&wx#6=jyOW(Yl17h!j>v6= zewVrc8iU*LF)$!FXq;{I8(@nFg}>qby}{TVbc1IdpWPiy91fW4!yyU1m;q4t!UrC# zdoD4())_i09|gVi^cTIeB>)ZjRFvwsaanP}Y6Gj$IU(jD zIW~r3MQ>&TZkUJ0+Jbjmh@05by0mRh-CPJN(vPY%wsesud=aFCvnRkqsBvg730``) zVkgo+6%eBo4so~*43qk_QaBO%h!Z0;C5%&qrz&%5`JbSInyPj83|I6|es__2+j}T0EnMy`=tJ>$CdL&Xqf9 z$y9w}LZ4$SG=T?W=js9$#;;7ZpIFPT{P=3U=KVtB-{Vz&hZW5n3*wb0Qw%STTANy| z4`5ej3v&2Epr_o?EC)OUn7S z5603ah7@&x-Uk`TBg;}9QoY+y;Vh7^zFCYKD*Kyi zgN-8xcFjJk<^`e50YW*%L(e~Bc*Vdu@xt;FDEpUA71w^s^y5~NDHhEK%?`l(`&oE@ zLYM>=8`xo#Nt6ET=v&w^t)$IOq6A(hB{Dp=1){7IR7|iMU?;xztuB)=a>2B#R0$Ic z8mh}=upC$#Qk?9)0M0&*7e*X_z|lUh`}0j2z}at;TVm;T;OrF45Euu6Ss2EBAq$f5 z$U-W3r;=Q*#%h+iS=Zu@%;29b&TT6e4VHbc6JR`mY$|R>Ef`*NbbZ0Na|g`*vpo} zq-&>yzt+CisCeL-ucDZ0lR~{n23V$0p&%%E1U=&)%tqQM8( z1ouix0+e!{cFYf@V)jW#N+U@3Ex&SE=E-y0andR=bN>C`n~eShSX#RadUZa}?n*a^ zlGPma|4{Ey8r(PHMm6t4rU;$<0O0n3MpU0ed2 z@@xhn=2(P{Zc!>HVJwCc8QZpr{;^;p?J19PzEANRYqrmXbijWFaVD^lUiS+b;qWxA zt0qhd(P_L4B7~jn97~xHhuaEc=(Ck~6RFkwm>pB13Q7rd(Y(cTD)}Yd*WXY$a`ggd zbh2H-Sy|7;h&AqXTc(3?#|N4b(vPQ$7 z8==`;zDnA^*5@x-|Ng9RXU#G-aP-T;480FEU3WZ2LXlRz7}k``TW?+uCz;Mxk8{FjA+$TY{Q0U zTe0!-jA0``y`EHb`1Qa{IkH z<;>ljXb1)-u115F)31ros$ ze=T6|c&M)E-@)ErhDZ5nwRl2)m0BXZM(UHLkxc*dF+ zO1#xzXH_U7NEYIu;5ZV5LZAs~tyE?-J2|y`5lZL8=tl-N9Fa%#Ee}PTah!l6q@Kmd zr9yin=|+LAGhL+;AI~Rt#i%b2b@br9#eqSl02{;yhNQEeCVbLhRQhvBxO=awX1gyb zRXSW5$flC>H-Ilxe88ggztoL00=mT5W8_|!RJC_Q z*6P{`Ae^aoDh6DFx3>QbI-B?3K-2Ap$X^+xatq5MZGTu~ro!6v#!rigu`F`Kr+Cg< z_%q>VBE=;;wmJ$Qs;#BT6dGe#DRd?%V!#|73>@V4#xxabdySf#aAf8nm&h~EOeUOk z)Dn9F9(iN%Nii1%j zQtPbxS#y+Kyjg@I><}-i{e=QMSsqsV#ZIX3vW$@n zwo6=4S&@wxv~1FCO;_JM9V?%<7kh(fCN|2W+~Nf^54K*SCxYIInmcYkGIb6yg2`iNx=+4!N@@OUf5TP zP*)3DH;y}j)%$_o092SX@Smv`%WKX)yzrD)ceQDbb)%3@|JG|ZF6Eh$(DPIY@)R80XJg+8LH!Gs&8R` z{;m^|OcSxNrHbEO93@LUz+SXE6X3Ex6lR~L5=Y$1Cf=)E!f8fm1tib~&IC@n2PBD3Mzb(emWzSPE`4{G>UidbTn-3Hsy!<^Y(A5fQeTlgT3*YdL+muGTYg ztkkDhKNJ7cv+dQ%aX=Qck4aRhI1E}_SN@b^V)t$dxV!-r#=F+vJA#F8V0DSr;UQlI z9P#{s^#@^nc{rM^cPe8WkL;OE&V!xyu3ckM9mz=-JwILgQuU*+#8W!t!XKf{nhVSU z<^3N(>FuI1@aDR}U*5bWK}D8m$zHU!accAIMAT&%kDys=Z8PL{hvEb05aZVB)CmZ2{>!XRlx@mT0BktHS%`ebVq<; zDc{|{j08>ueebJVQPuq;$ba9{_ya6&czfg@-hSCqOhi$6kFLXjHQl;!BCUl-`6elE z&06guuuGO{$0n5vCBBknKTm z@i9+=30ZlM#NdJE^^2n`cVvP(#gj|8F8l^Yg_wP;3^yP~1avPj4WNQQ2;FCbW~%Cf zbUGJWg+G6XI{6Stcow|`d?FZ*w5@;&&^JpUz2%Z+Vw#G^)B<~z(dCo26%Zuou2 z?I#~1%=JjuWcB(KKpF)w)^+od-_B2@ooBKr}Z2z$b3=QnzqGu`gl?TB$ z-nfN<4L8d|9wL+3FheuZ43sdO=;OJTM*>1JN{}PZbw()!p8lzB%l-zBLxRB_i{}q* zHfRPzGs7B9-sI%QL%gmd~;p>1C3y z8-E*XZpiVHwchxsfrXbOw}SEzt$sJQ{WYzl*UBb3#h$auT=FgP*uVOO%lB=*lMgOw zHLs68n|k8(vZLJo+@12RL$r*7SpcHj%ij;V`;V+#*7@4HN6ls=pxN%yH?4q~#j$=y zgYPx~r@MY%JNOwgF5qb+%ZP)pLSaxrD$$lC`_Q(~92;{nkVz}^Ocl>S#Py_0_TmZ0 z;G&8&SXoqCd7{k+4l+7=sVRmBv1fd!$e1N8H|s(siU5_>bQ2oUmP)2So1oUWkw>Lu z;T6(|^O@(mU!IAqX`0BJw!RCSIG5xQcM5q1;G;XHtS0MR6Fu?=OL4T|=L~%kkB(Pw zp>GFzoBg1&v@Q|;FE;u9+lkC)BR^~?Yxj1ghi~?zy+8kXtm!$XW9RBgjeFAHKB%v} z4yeoeF@8Is|8bl^vw(Dg2ID`Y)u#a_NAu8Wq4oAU!iL+k@B|)J1@<6$W$vKjXo3+h zo&EYm?54C*h;o2WG1mGnC-KOH9dAJjf01`{;G|&~H5{$Amq}nFuQ7#jaqMs%T~BnL z-1acMinLa2_sIeioV|>f_MHf7=VCW_1B$I;tmFkO*rVCBi{Y3F$nTlCn73WK^_RwH z;-ymoGV*n!v)0<&C58jT)xlay6)U4yL5+eF>*7W6$CQh}>%?~?o=+caFVIbmIlKX= ze?<|)GR^c~qlhf}Xm(s>oh0>5666$bkW(t+H}{AVbeN(p5wYeFafUX?DG^+RR?vi|v?aA$G7-7VNfhX*P&al_3~9TZsA5ZOg#O766KNAX$|F7w zbxCwKxs7Evu%zPqQ=zS`h3KY(LX(y#(Mrbd$8tD4x~T7q8|NYOXUo9r*x@Zm-tXyP z26@H3o01RvBGohHSd6CL;6ar7HWKi-vHv@gz1{V7z+HROhlt^XOoR^FrHH}r6 z_z!2Ek9?!?)}zjEWZpLGaF*Ov-I2KsS$|WZs6`djUw*$^J3M%DrG5gjNKMAM0eGwD zKL0~ygDJx_^7&I4JHeukYw7nygQ?;x(?3@hPR0MsA#^@h$=|E4PYlx}R9QH(>a6?) zYwg2%*sNy0S>7V4>pqbYZRU_PIfIf2Q$lO+QmhN7_)Be@*qdk$-m zZpsOl_whW#kb^QS@~81z5j1&nOOJ54B>WUFnYmvHYKo9j$z%9BU}QKD3^0)0Y;vg_ zJlt8M{3Yw@v@asa1DM7ALPvAAr08K54lBru1Vg4_KNJP9)(DRf;0`->gL*Sige+?P zb#~{nUeYvD@OR;El4im&&~6!LGK2nMfz#{5Q!T7f#rns=0Giqc47H3`y*e54OSRLa zXe=6e%S7zMTW|1`=1q~vaeY^hVbp?P;%-76Azs9*PbrUda~F8wvP*eO{70NR#r=SGmvoo12zm)y{#0nDWAc zfpL&39jiW%I0-mh=*jvLH&0V3`OYt>f#1LzG2J)Hq<+cPo>rKU^9X6t_4d-LAuLBA zWSK=!U>}5uBFlDij4~0d%ib;(5{&rSxB9H4(d6z&3l$6KRo$*PQQiPy@4kjeQ)mI{v}&f zQx(oY5f$^ifj)ySW|@W(n-(6+sz1+}#%W-)x9E-uL9S=f9OMN9=@L6UAuxgRlxD6$ z7b%`d`@w?{A0IkugoDyaCe27kdGcKM612bs<2@TKmq0DJw=hbtr`E6_;~j)C>~ zL}NpxMV-t<2i(3lr#GJ%^zpq~B+k;OF~T?L0|Pt+^4wLJOA+hdh%yrBxx9cNN$8h{ z@8a-4Udc~zi`0~JeW+Na_IP?(@pWE?>pMUw#GDzd1Un1sa+EhtxmwpYb{4=+JnoFd z2F3=IecRb5zgFWXL>Mu~hvi}C{3;;kW5gI^AH&U^+pEn8`v7Gh0oxBrlQ$@dHYE)7 z`b4J0PC=AQeTtG*K) zrPn@2k0n!ElbQXmc7S^kz*H`7)y~4KBzx<}-Ek%E(z^SJ+hjpa%%7OC#PZ34ooD*y-3!;TxNA8QedK`s*oG4_YSSQ!?O zO5J_AeFx3G9;F9=OObHJNVidq#6blp^9Ekg+iDq=?WO-~ccX^?zQ=*Y4SZ0a82oj= z>-?BmdwF)1_Z1KcfTQ@S&I_KYd$FE3f5YZdeAE#M>3D!6!PLq{XQKwN!z3nzfA@-L zA#=9Fp)9XpZg|CBrPx>z;1f1ain6!}i*b}2DdsH8Or%ZmC{F_Vu~!H}KFVCA#`7S| z$A@@u3KkGJ*2mE~kIg(3y30VVv>S3Q1o}kX@$&V$vs*EZ8*^x28F+5omzf7CCM@|0 zuuN2}e*OYKU;aJyqJ&WuUoF85U!KApqWJXf_^nQaMvYTC+Z$nADAs& zN(g^+Mx1q9#IN9(-t>w}%?_UR+feYI8=}-68@wy}@Mgh)Bglg6dLxu48#F9SsP+BZ zdJM}Hv41m#x@VrDm{U*k>z}WAG?NM9p#|6ORMB6ZU7(p()UIUglm_JN|87%d~_c_fq%i ztuYro#!uTsb@D%8wcPj4vdF0i$H49;7~uJ#kSw}&+kjDNermoYXgPJ~E=ZMj&> z?a#1-O>f8(m67EP5@6m0DIZE+A5R!#?Jx|208s-05(@h7-DZ0hH~Mce!8_WzVt|_a zPio*2Q*ToEy$lM;r5fL2j~u_CNv;-L1Q#N7d8pq6mns538RY2wQ+?smNZ5y);v`pi zyk(Sp+4_dUmwO&~L9$8Bw#D7Iac+^v#{eC3Oy}qPTK4&fLO^pwx4kBcrRcSkc|I~9 zuo7j}Byk^H^4Ast)QYwH{DtOFlfPF(?V@K#j94ozzp8^L!vi(<3OP^PSZk!bmmDg{ zaE${^xY}{jGF@fD2nnC)_V`r z30Z$+jX)PgHu3A)R`o@6IB*B0HbwC+HX?b#?pT35yeR8k01nFuhx>Ro(nC3bO(qgB z;PRB&%I+BGc11+}a^nKN!48VxS&HdS!FTX>(*|d{U;0{qtc>V-pudc0k!Rf_^Mava z>;Y(r3`d?5tH3nb@vP+8rcO#thqm%lC3nV`(ak}nNRj5$GuL@bY;`Vd=mesivcm+h z&-#0Euq7jV0%snsdp|H8uz)?hRWN`60bw$@GN-}4>orV)Xwt_T}MFx9$66NiufD z2qBq9DNIE6wX}^Tk}XM%gk;~h>{~s^QkWUh$QDv|g-nDQrR-GF2-)|E`Q4wXXMLad z{r>u^qr-9NzCPD|-PdxS=LHIH&2T$qxhrqU%5uNz?pq7W;iLjEr6Eb!hOfcj<0`bA zD-t1`x;es<0|{XmPSexk@=K;U3Uc9-My!dy2?;Css7x|nFAu+D0C3&NxM`2R^;RwG zXY7!4N?Pc%gtqQh_npo98aY5%npMARl>1(MP}uDHy?bd^&qz zoTveRteGr_S)vA*OZCg>) zH$ZjLi;{nbg#yPx!F>w*ucAfT8<6t$_N?a&vNX>?m&Ac^%Hb}QGXBdYIYD$md&QOC zPZ`d~RDg$rLF|TpIh7p#wPAHr$^F}XTKsks_+L7b+R1&#&fMWrPhqK}s+}nI7oY z%$H_WhEL0rLoRd)D?2i0?MP^{WDGoXbxcEJPT4dvd&=UA6Q^ymipMU3^Ajjk29fij z&eUR*Q9fJdBpB{i0QDHPWd!x{>-YpAkU4HUN)MbkS^iq#XfWDY+D% zUF#Q{d-0rEjMo=BUMif7>T1HSJb&qmJMM#q+AjHQAbaF@uuW|Tt*R_dXNVB~zm89D zLqVEK-s%g8U&lY=05A~p{$c?wd)E7YksgAl%zgg19YlFfE1-|bN{0DM{M7~m^2qBz z>0q)7f0D(6A=OgE=%K(bD**nZYD=|UIBOCkDu1HtWn|<726!w$uj}QW-v;kkYSA=c zve^$8coXvp2#iJGLVYE%8gD++neX6ssN4@o_~e9+vZ0>5DhyIf_pyn1@OJF>NxR*h zbrb^>>SorCweyE>1cdcVC6>R%s(ah*e}_9L_%s@%7T)S0U`T=#)U)6qy{O#o*^h*T z+H6?|6!#kllMX$FeY5r-6LDX8HJjOdIN(;LQGao)Vo`p73s7z=&+-=eNt|5CQ<^){ zuJhmCkWg(KHJf8+bU%R()CeDRMPk7fVM7|K;8Tq3m`t}VTlGJbU?&Vs`Z!z+W%o}P zWWpc>HJNlD2rFUM7=U4_yCYe%7~t_sE$H(hkqT;ZJ>7TGVpv=KTDT2$zOiab(glXZ zU(YJ5OHX1cw-nb0ntHQ2>%S(`uZe=_8Fi@6;>@j8!o-T#fk`hyMm(eXNb)Nq)_72R zdv@9v=GaFUcNk91}tuuWDqopb<_g;pY^XalrklDj=a0t*x7b~K;l zcG%t!Rsj*Y5%Lp0&E~t=N*5gPjAzbqc%dd7@VGKrXo5X(BVfTlo)qdgq5Oa@`teAo zbomM$|5xee@l4X!;G+nyzW4i|S++MC)ECk6mp~Rh`hJyjdY>mctzF`{h*yIfI;}Vi z5-N1SI@|Ahx?Dt7OzCnl@yac?6mY(9rXmI?kLmf(Xk4BNUa|lBwos#$2_YsDE25%% zP4oBa3QD1*!uVJUb@>-2d2lJ)oR}lVw)oiX)oM7n>XXby&{X^jQk(Ea)o&^GsM^98ob=s(6Z^P;-)qsU=Tpv!ygZ1|k8w!vC5}HOY z3_*YtET`kX4RDTRV z!?F_WacOmvDP$e-4*V6$i?hbt zJqz}gH`@_~cmE|WUe&TBu{-Pd0uUr!I|+1Ir~>W)AxkGgI3MlYAI_yHFx}^!R=wKB zRkSrMrQ*^4ufCyc^!g9qK%zCfq_vMn0N>#HrW<-vGveeq{-aXJ2tRx@65apa%^|aW zg#kI|22n*XF~E3LT-LB!R~TT2{6<~?f~2vEE4f{l$r7$K+5OR+F(KLWTEhWkjgpJ6XMfQho@HK52Bn5J7*u3JctS9!c+a!tJSX}uaYI_0Kca~FXWY;( zl`e-^&C*zz$GVD_iW6%G2@NwCI&EU*HuAGa` z7Ej&4#}DIDj95|k7y=SWfZ!0k$@O((mBz=FdF%hY42Jyo7OQ_<29?s5eb|MElvG6) zWRyxth_RfAW}Z1wrk~yP%h=EfO{ONWAjo5AO{@7e1KfJ4uFL?a2B2KXcpLWvBtgEKfP@19lI{~9&*E%iVsB?74x8TC_J={TWpJj(z#wYo zB?UfA$VHVAAFL)*M50nGf;@-+9Ykv=8F>iazzaH2`W(S14m0iWuPDsZ@8l}Ff|3A0 zk0)hYzJJ}jt-)+*qB8Szwt&yQiEfKsC_n)^v;6#J@qx%W=wY1>fw1NBM&urys*c~% zk9s1;9v|{zdn9X|SYxuQ|I?GPgt-5FKm8JeSESqlW%BBvNUhrO6SF6$dOC^rrLqFQy_6Qrur=Y1i|n#-K{-(A7JjyAcZ) zsA51yt3X9}2nlO25y(?MOxnx|{C*=x00u5TTi74N#ty)<34&D^hEuvaR-#I5 ziP5c{j@^Vo9FLC-zO<7)@X#Y2Qr=@^{HU5=!v5rI%-Q089+52c+_4rzhI+GRpTM0! z5J=|fpcqWAf(96c838#sg%lObF(9`Ma$Gf=DGs)9fW(i(HA_ntF0w z0!GLJM$?cntC#r;z!D%lZbur z!@=}+;yd|BT`IZvN<>2>nBI0E2H3VrAaR|U+sA6cS(jIZMm+629ml2@nHuoUb@V@Cw*J8?r)UeB!a%W~}Uip3yfAU!D(bx~0xDa-dHJodFsy9I5x& zkZ8Y>BBP9**m6PXc7(utM$@n{-M0K~>LKlc4}>`}rfk@k5@+KjfTjvoPXAoI28x4) zJeQ)+VS|d6!rRH7)8MSZ-PA>eoK0#4oMF0@F<{VQE-qOCeMRE@(L0GY$!%XULas%M zW?5)09BipDj_R7wTIe7i7muIG2FReBz6N6=qCi&dPKRjvnI;4iOD7m&x&sLDDfnXo7*|yXND1e{Kn;?*I>EuZfDyw6)!=G9ZAjEOa~$> zIhj+gn{ck=jsJNo*dFoQ2!LC`_bIEXej9WvUH~pRmN$>WE}UA;fQ(Zqi8r=@VI}rZ z3>8!77?5jjE~;2NalkA(rW6YVHZ;S*%?QTqiN!J^698n2|BWV-wWu8b+dCI{V7SHH zP#*?ZCp50xyHP=k%a#P1i;C*2@4B7jAZYnf5FMObL2nMsvgz|#G@#NPPpMo)Z%5y? zW~x1*JbBjQN>p>AlI&mlaUEgLz%V~@vi$zN;b+sWdV}WgZrohElzfRgQ(s6s)!(ME z0T-1R!|4F9*aQb?Medq~B*3?^|J^4(K|Ue0d*r~-Uwp#IujC2cCkWE)k9kw=M2zZE z316n7wX|YdcZw(_0!A&Q-WcJb&cR(XEC z6cShxHfe)eP6bffWiQbVCyz=vg#;ybYM3G(ZO3);q7y!U8|MG;~6n$&dkt0$~woiWHzAG}J*RnAduX z&xtXBUN~aCAD*5T5>yJvfm9$Tc|@JKNOn4WO88}RcNQPeO3@X<-03h)ul7Jpv9xi- z2$>|wm$d(;fAiFTxVrdpC9ees-0#&SDzU=Ww|P&{C+`lCRrO2HzW2r4zwqvc=Gvv) zr;BRaJ6^^qb*!faV@iT)8uN34Tz>?^h3Hcmvfm96nD|prmLP;!dM^%EP?<}ycRJLe zbE4Vx;O3VJUzpJ`hGT}psWlA9c$JcLV>=N|hYKW`k36Oe%XM~Ip{yJt5JsL?Q}PVl zb+0kr2$?0^j6uB7Y_?XH+xM1yj2C`RDJMbsavFAa8H{Ik=M4Hz98Jr63o0mp9EHa6 zyS57e1%X~>dU^opJrE?J39IgUX;)0{G@mI*B!N*N)obt`NJAfg9vuZ(gf7!x-e?cD z9>h#p;G8jgK+m9Bd9_k3y;*Ecq+%5}Ruuz+A^?pVdW&i}B?f&yXGRO+71=X}|KkRT zx*Y&J0eDyv=cBpx#ATQw8dP+T<;^|T#Z|}eT;QJ*AecVnd$pfYpa6w`eZa`Ehi5z` z7D#NhsFe7ah>L0J*%D#wC=@gK4o)(XPYUk%w#qJ=HJ1UNw@_si&sxEO`Z&%PE&)rF z%+>N-?E4{kEbyFxbWX+EgD#!Y%7X$B3@#tLRo7gBPU`xMg|zQyo*t{w*}l9gH}c*s z1;*M|6l$?AvHTo)55N;ch8TRjv@TTPsouVIz1->2?gIwH2jb{;rPgpIvcI}4`S4C> z;a~Ojyp{hVXfEaTcB=JezdTdqsJJ)aNz4VH<^625Y>=Kl{!w6~TJ@qXM{#xwk@Lz` zKO2L3s%!=uNCS9uB>X%vuqk<=8I`!M$!glBWRRsKV8$fRxSmS9)Lk zB~=QqaXMe_ZZB=SYe4Z=JvnxKVKBIA;KlSVePN4tH}6n#m%og8sC7#h^^8s%Hfn>!{=v)+IXDE~fkALbEW`}FPyKARDAOxRK4x%RQhf0P z+%^IZ!H^(nT#bo*|L~JR&|~?RgD*$87AYR7jp)_~0g+9;j~UD(dVkI%w%aO#M1n_@ z_Z{}1cOoLuE#`=xoeWPzLyhjGPEBwl*cSO$B*BevkBE#t%bbc-Gyh2F19I*?JQ3$( z+RL&LU)W7ImWQe|o0}QnM?G46w3|UlRPuA#hi5zx7Wr7!2^S$cY}%Xd0^qY(Ksqu9McTcL0$o~8XGE4er*w3s z7cUyEm<#&?$jWi4>AJ*Yw=f#&>3@!j$z>^()fur>K!@cUI zPO&jYIgx+;XHT4)0Y{ZaQuKWC0 zlf{B4GtO@M!?WeJCKEH(NQaaljVfU#`y{s}=%M!MkuRkZ6Rx5Npu1T5Kv3NA;R6O( zMW@GvzxdDnKu7dnb4uLM(yr za|7SMIrka1IFYkeS-r6l{PEL~|6zi)LL$p~_9uq?58hM(ueS}2)l)IQPFT`Km%Vid zyJ2h?QctLr)*;a+Ke*{VV27BtelAzAJeL_@!hSBgxKQBt4^S>BycY0*LRA-409@jO z1&z1tJsCdUf+?a8Y`=VQ=gdiVn5axHPWU);kfh_?j)m%?BS+UMat1{QlUOu*~E z;lGN$EPe&`)RR;Q3J!Pw+hGolzKs&JwD z(G2ioOGD|}I)H1e>^S5D=(fzT3%*0q0JpRszSSOM_gmx}pf0&jpz z-}hFX|9k@+`)Gl66^H9xIDK9zs4MQ%8Y2%+Ai#aIGyxI zvEW-5mmxc2i0}ZJ!2xzlA;gdOl1<163n}*Qbt~+nC#AYlk_j@3rhXCxt8dj322zK^ zVjm_VicK+$saMp@TMLS|lexL2TUp@}tmM}?X|NpBY8!^!%_qqVJI#Z-0?6nO$W%!qjgljdN!&2d2 zhJSJk>2HArL)+=l49P_Kv!L^C9a$1y-jF%Ht50nb+m)i{r8Mm|hMT#VF#7y&&!S`h znGP3wFUj2RMUJ}wl<~!kPE=>c;lM$wz%h!C;6Dv9`|pO(B|LgjstF8%P%;hJMzG3r zb4ZK@m*J$E`Ne`BPAtAi(ok7o1<*|=s>(uJfj6iuLE!LH%&_Brmrvkv2f|p3RWD!2 z{Zz~V3-gSg|E;x?1q-;hpkkJI4N0)KcRS&u1ByR8DT`zsGFq5!syGwX)k7!%k_r$; zI&sgwS*yC_F$5C_oqB$Zj^vTIzS9w#F13t!^Z0c~e1;0wzc~rK(e-byBFtl z`uc+w?+)%!@AFwvz1ahHeFWQXCL=t!JHr3&8vorQ4jfrq% zs>=9mULZpPqo0chR!bs;l}kk=^{eHHg^qv*5F#KA=E)tIC>poCJ)~Y2SeK8evlv-J0jk(v=v2L z7$=n%#ddV07c8Pbm?sZ!t2Gem$tZt46FKan+GAZfPgVR|3SA!;ym;ssV>Hszm^cd%_vqGfkeW-izAn%DQyTn%@tous_~Y&l>x>cYlM|yKW1- z8MOW7G;ClRqMLzKdKD;;tL}8L5Xi`oGoj#$O83{Yc>O(8j;%WXEphfUx1nZ$UP4+& z=wi}+NLa+lY0$^SDRcbgc46Yy7!8_EiEe$_-BGC&Y$powC)obCM~wdp77kW~N90mn zJrFFEOoJo{m-6Nfvv~y!)y!=Q`dHC!AKX~7VyO&pmZc#EtubAO1TjAs432Ss2y3>- z2stG>vP2ca_mpraL@xUPZaPkw`DfJ}`AOsOT(!w|HA-At@4kks@4AKXic?TQQLN*w z51ME`!KBY3dknmTe&)>p@U{EsD&X)#B=zO@`$2bU^0dY8Z|3{8K;h%-%I>5^4hZqsh9SH|b~FG?Xv zD)@&cp<09)Zy;A_}WH3OYaIq^*aL zGE^q2ZB94rny<`E?BcBKhe`<-ks5`ttALeP6FxT4#g*v{>V;F^(2*tu(7Em2L#r6t zH$Yd-Joi7N;<^Q)JCvKSd|2(iw-#J|e z$t-g}UUdIkzVXCZTZYtAYNb6$wC@LZsVu_)1{gD_mT{U)(b+(vJ3a>cQaVcs=(~La zaR2~!WOQ{dTOSE~iypY4zEn@7oB-pu!}}q`&KZMikprMj0v);#Vikj<`mv>1?5IfL zbT|fx4i$>G3yr*sTW1IR+R`HaXb;r=8YXDe43IIF;1L8fl&+De#QV)+KT@jadTE#F zH8P6^mkI~V&-v@ZpI3Z00dZ3bKWCoCR?Lq{MVtV0=O78B@HeY&+%|qK2blyJZ__Oh z^(Lz&PO&J3us=(gxN=4Y~8^E*Lqvk9l@Hx1`m4c3l`qMU$j@*mB_>SiJubi zsBA^y99kgI9S>X@k~MStG1F~MKD7W>!ED#*rk%<_%jQvBB+a9_ByR2 zWafMu01d#K$KR(siR((~n05Y{2K+6;J7Z)ri4e&_bqzOa>5O?=e6+LaW4Pr2q`^+H z-rf;-j;2Jf6DyTQU+$&`OmOr6nk-f-oA*dS`SW7_Mg5~?<@ z|4hQOXZaRv0XI(#f}z1;w#jb5V+a!DxvxI5fycVS3%R4aWULq=oiG@or4 zZjk$Mz)%3Sna4B62Qx85MW=oB1 z4@E*-1se&nLiiV<_-ju&hhbWDnph$iT`&u@jEC_^2;Q#9^hX!R}Xh0j~nt!FHp~C#n zE07+^X3pOrj}|P_xoyT?X~>`dCi>1fDa}xJ0~K>00*4;@8)y`XLSoz*evo1qkmfI@ zr24SHcP_OY(`wdZfJyqf+{WGA9~Oy4T@JyA8pcGH$zOhwt5v!UE=FX^MZ!uue@ysC zjJ^Ydq)PVn%%)x|brrszFG9DkrHE$TH3BfbUBh}R9@|-+BgE^#4Bp5q?lD-l;&uM6 zkdT6hdi&b-a+6CGgncQ0U^4i% zKUiNF&5#-099{dIEhZAS4Ybd3aSj$xMhvArr*&@2u-ghDI6jDjRmvBo*xxzmzJA1u z)qMpXIaSk034%u+->Y*mhF9T6H&Y3xry!=aI)GoODV+uKJ76CHT&`muUJH>Rh{q6& z@u5ZWf-{)D!jQ<<^Dq7!Dc{<&w5M3R;oe;3y`-*fM}1d5_CGYZmTL$VhgsYxs4rk$ zsN)>~Rf|UVcF@IFfGYrSn#KDo`hU3)?M4Re618xz>iItcVz^>LKly{Z@IdTW&-qlb z;w`f^@bH&DJ326wd6jYQ+jr&>uKGe$`g# zCV_}j4t~}tZd|H%F>l1TTe4qFcA)VKclA?5k-ChgHE(^#&xIOZRVkf7qH!M}Zgn67 zY`fp(CuqFezAPp3KCB%u!b8jxV>}KRVGi@LFodYa~+d8lD! zw#=Lg#a*P!YBf8Nz@zO$5cy(dk=b_^yko^x}sJkh$>16vd|kog^1WFO!NvxU`MnhBex7BZT@=k=Zr)SFqm;%}1o0HXHr(P+A#8ze_mJCs%58c99U_y+j z6wm>TjJ(eVk9ivt8q0c63f|RTt(C;u*p7J$II&Mf;2zMPA#2?1tEvfQ^WhUrfWYK? z)VKAcP)cN16TBp>G18*BLU{mV}X-AcxGR=uQqVNrEW>SCxNsh&P zeR$J>!NM~j1EOG&^PmfzE&OX#u(KgLE#jB= zOa0;f>VJAaulT)fCA=(C0TRB9a6jW58BF#^MKpYnUH}(X1Vg-Iyt`fu}C6;VKxL z`Kc^f6&gaI3e!lNsp|_jURFqpWJPE##J^Iw5Y@FsU}gW0cW-6xMGYl)CKrD zw>_wLx?%N6|1*1O7q>GCeBgoXU(XKz`tgtsZUTPYeom+v7ZYlhO((28Ib*cHr3{MaV=89E)<5DjQOnu#6^?AzW#IjOEj6HWF2`>3#w>epdR`Y2Cbn z@{wgqcp0J(ZG%0IFMS1A_zIbdVZ=@da7vsY5AQ+|*<3p1ZofHr+u~%D{Kb@xkaV7R zXtVRe(6r=TVH;q?tD|x0=`?6cH7sc1S z{rPP`md^-J%f7hDb;ZViarf8x=fUBJ2gY&q)KK4}?OpKf&C zokHDDH4n_U@fz)D8#CmFHeA{}TCL~a64V@5NRX*Dz0E|pF;#u}T%JZ)B#WBTW%6qt zEuGkGL03#)48YnY34?`=hMG*4YMX)+kuPJ&3^k`wd#_<%s@x7rIJk7}B3Mfludhy- zKWenS{$BDyFdm*L0z?k=>p(;_)1IvqD)TIUmIzM?vEW8noClhl<3F-NDhNfv->ije z*KU#T$;k}(x#(DS%V%vgDVYu4AO48B@01CksP{)!hW$GmqQoF_qk}t04?e|tR{tyq zc&z2$NXTCM^%@?&WW@GH6Zb@v=kkFsNTVuQN|=VgHWs9~uN4m-tVMP>n1#5#gAkI> z!*YyKz(vhyiH}@$N1sD@2j=uiN!C4Z9|_Ef_AzJ`AupM1mL$mPcx(?(0{)E-lRGQC zZpY>jIIzVozq9d%>b>)o=oB`1@H zI}LJRX0Lb^g0RE9KgfLCjII?MdO1&jexqgCS_E7V=B)STiqdm?umc&W2XoK3pHYwt zxcP4I=U9ODb1bmav;SX?4dZ!&xb1*NUr5_o#(#?s{X$ln5J1bQ3{N|LrV?GHMBI2?y~Ff>B{#7K_SpzYRGi0Y9N63xDW@r+sbJ zB}ITC(6I*ANo=pnZ)X&No40O<;4n4s!l2VOfqE~t-3-WT?`kc2kW?P<+YT@;tuflw z-~FUHAxQ-cfSl}i+j0UKH9bobsQfOL?6jr%yHg~SGJ(?MXMNz}P1}{~q3vS^v{Cbp z_q{YG+lO!Tqp+G$6Y&$j0a!p-{?0mHcCmNd;&Iv+SlJ^yV~(8CGLO;r65`n^*n>dx zoRn_o7RX~mUU9VIc^C$%O(vM%P9+3PRpZYwLEH8m>+b5ru%>#rbl}PiLL>83>H7s4 zVXmGrWp`Rz?si`qxFNcvCYBX&cFes&)UR{D1&CIifT8IA!+CBisHN5~=y3{tr@32P3Sd*!vxH*B;%%>PCRbu48(SpOF47M*f7JTu3_xZXQw~xE;Oz zfzKf)GK2vRUmAL%6%I+Oe`|S>9Lk!ndU*oRb2^5#ss>uJ@cB08w6h}TM)p~-b9BDi zhO6G}{g?ibYX&MRZ+fnRF<8whj|tA5zj*`lri#b4?aRsx3SpuSDSI;lj|hK5?>B+$ z)pju6LRrkg_h`S!6U!c*VMN?Crted$K~^REWMAJafQ*Q@=%#1-_og5g*3U7(oOdT^ zdSo~spMB9;;M0(molyH`(TaFw-AzoW;lA^54{L`-dyoL9U>@8*&As z18tJ*#uCVDj#fesf$V66DM{cGlE`EF0&or<$?XkkjYPQ&0kxPg)_b6(mmD1l>@bJd zI>-aN<+&z#IV!Ss2l>UoL7=iLtwpj9NKDJ^y&~_s|6|78#I7TiFeu7L(a}EwpuM}_ z2;!>BXV9~o8;=zMguv(?Xzw!C>Z$c4l)p}l6af{lMIkov?-72Tp4)`(R(FI%l$2`o zGZ=s)47-8N@7)@kP5>(vOx!)*s`=*?sCxKKzBHtXh|+zH=e`%JZD%dfVcd5ff5*B@ zfC0JP(A01N?D=L)tw5D<8(YLWbGs zkYO<8Od+IvaeEcbZUPpM{eJ$;*3?VLHXhTKWvg$GjKEfn?P_4L1RL1*eo6&wy=Qo+ z8E&F37t*bHFFLYjW9Y>RrY{`7=D!xFQ6XT#*y;Tw235%ga(~|2oFpHwe4K*n&u0xN zgF}>J|#t0zi9M-elt=(;$WERbGZas4WkcpWT8Zc5_h{u=ksw~8 zE}89atFR_7dG0rT4s{gU0Ww4CIgZPybkOH5Q4bE7CTl7)!JY2=O*pCV^yB>OUMUN- zZIVlURrwak7I_jRT~!6Jnc56eyQwL}Z}@Z1o)D?IpS$5NrVuLX@EZZj`+r%-5t013 z4Oj<+#I=3Gp!t2ppqUd4#R=z)h<|?>Ec_92!LX8x)-RdkEA|7j6QySM<6 z4N36B^PAXlVvS3r^%>x7+c39Ji~(G|ECAgp*MQU>+I~~dhC?2!86V-(f@ZI&iZ=fURc_RB`{Q{cAfWSD}P`tsT)$MGQ_Lav#f$lr6o)?4$gL015B zF79gYOPwm<9VdWyGy^(8J(v3Bvp>3Rox_5K>kR%89HX0FSM9xFwD*msc$qAZ%Yikd z(S)pYtQIR$+Sf`M4|b7B9m+z)nC@jFJj{2?OE+|97I0Tn+6F`iwRCQ03u<5lA!+6W z2qkHZ*Fxq9zQ$<2(X<~qR_FFx>l+sA%jXtJ4PJ2{HAr*S7sTqHmA~#imd)rpal_cM z18RTabi7S_en&;cF}KBs3n5?*W%RT+!_@gUNEN3`_SbInit!`tbilcQw?VbXs1W~; zP%&0Lx7-MU1SYQ6=AJ2a)Ccb&+&}5T5w4(#)s9za9nH zxvt%n0GmEM8axk|ttEzx*Usa~)?G5&V+aqv@JPk5n=%VjdX(%l1`2!l$I|U`0u%-$ z#c@bbQ%8#-fyWWB3&x_iCCGgnV$)^1%L_AErwjhD!dF_E?4@9tJGFJ@%(QKj^>7q^ z_0?9b(t?8vl?kPrsvi3bpzo`admf65!$$Oyf9A_x-(LG&WE=-?CA6l7BRx^R0BBrH z&G`#?J$ExQD2`Q8^qL?4vk8_!S$H0FS@g%;BlAHKXTLKh!>bLeYG5m+y!A1+cGZ>F zW$y>wiHY|YiRZk|4sFc8N>1{c&6ys_zF1%HF=@2wEA3Km9XJ%!5p;HSY;lEBO`N8# z$i3OB3U<=)o*3ICP7iMN*7Q_kG2CstpU%3E-C;7V|f2m{ori{G)n{n7HXiqPfv7 z(AFlMDeLcvE)9)-gN>uKwGWixt)SUz>5}`D@AidB_0+A{%?C5zcMe-ltS|ijqhuleNs_f@77w&{LFXBuLbs8r6cOjqrqP$il5IO z_wA^APVJ+fRV`mwn!i5_#1Duyq;De)jsDXaGwwMvK{rNc#VWGZJOh@i0;e-fh&4H` z!3i^C(zQw6)6})Kwi#m9qjPjY*)+1zE-U28s!n3w8bKx0waHCl?P{s_thehpb>+K)dF^OYAZ>o8OuEK) ztTn^ZRb%dp#p3kxOt0nU+KpA^VT$Lh#%N$iL*O&2@}?!VZ|5+@a=|^@zX^CAHK>m2 z>C#8~9yB{cTqzDb9W=D{e1rCNaKdsP%Q=fhn-yN*vwBSvHKP*N zz&2t{Z_w(B{_2OIu1VrBv3M(xYUcOYzoFr|f62(P#kt8%;cMT=Ru-pA{hL&~)m+5< zuP-Yn6{+^u@5-JA>muBz!8Nhp9Q(lcC}|{s=CLYAb+3J{PF>?$30!p=TrDPwsnz)T zZ}!}MKep1?A6!IJ1-2U7BnAI4U0tl{r6#-^o~`yjq|smhp)@A|v%C^Bv-myuUBmZf zjhSGLl|btHmVJ_UBNyS%rUrgv$M5BTZ%CJZbz5SRE)_=Z{7NezW3Mu=S%Zj_AAAk^~;UY z>r~5n*Wkp>>fzGv#(J;mmxH3U-J2?#gVNtiz3ZsULrLE$uXCIG16Gyvsq(Cv!}X$x1=qC zV^fwjfH{UXw>H3G28dMX++vHtogjKnbrZ*f7l&=%Yz-)luFTp_j$GWN?yh}Kso8TN zxM6d1#kExRx_@h@?daCp*HY@t>?@jDA7Igx(jM5*lou$kNmCjl!CjMX)E_}TTYf9V z75DGG@3y>adSm?SXl|u6)%C?`>L&3AX=nXtGA(dDh~~RFxaIymxRmC)BKC;XNDeI7 zRIn*e@+^7qENf=C)Qwg@5qz*wky6|HY*Tr&)NXlp@c9Q{O4z2)YBgor{Smbi9JG~% zs``W!Wg^{SZyj~4S8ccK1JwpUe-J2qslFoAt%#Y%YojH7M3p4ZS<7p+w(qun+;40x zvz*Nl5SuJS%jIKdL49cKmK~l4s*J>b$}CQgL@m0H4o!r!k`O0a$FYZnU|EmQVGe$x%N9S#2rn%&yvQUr*nJueg5i{SZ`4 z6&obo)p$-*e@znF5M9cV3{Lz}w>7p&SxOqTCD9b#XgJZvY=Ak5w0ssI2J`Z25000U(X+uL$P-t&- zZ*ypGa3D!TLm+T+Z)Rz1WdHz3$DNjUR8-d%htIutdZEoQ0#b(FyTAa_dy`&8VVD_U zC<6{NG_fI~0ue<-nj%P0#DLLIBvwSR5EN9f2P6n6F&ITuEN@2Ei>|D^_ww@lRz|vCuzLs)$;-`! zo*{AqUjza0dRV*yaMRE;fKCVhpQKsoe1Yhg01=zBIT!& zC1$=TK@rP|Ibo3vKKm@PqnO#LJhq6%Ij6Hz*<$V$@wQAMN5qJ)hzm2hoGcOF60t^# zFqJFfH{#e-4l@G)6iI9sa9D{VHW4w29}?su;^hF~NC{tY+*d5%WDCTXa!E_i;d2ub z1#}&jF5T4HnnCyEWTkKf0>c0%E1Ah>(_PY1)0w;+02c53Su*0<(nUqKG_|(0G&D0Z z{i;y^b@OjZ+}lNZ8Th$p5Uu}MTtq^NHl z*T1?CO*}7&0ztZsv2j*bmJyf3G7=Z`5B*PvzoDiKdLpOAxi2$L0#SX*@cY_n(^h55xYX z#km%V()bZjV~l{*bt*u9?FT3d5g^g~#a;iSZ@&02Abxq_DwB(I|L-^bXThc7C4-yr zInE_0gw7K3GZ**7&k~>k0Z0NWkO#^@9q0fwx1%qjZ=)yBuQ3=5 z4Wo^*!gyjLF-e%Um=erBOdIALW)L%unZshS@>qSW9o8Sq#0s#5*edK%>{;v(b^`kb zN5rY%%y90wC>#%$kE_5P!JWYk;U;klcqzOl-UjcFXXA75rT9jCH~u<)0>40zCTJ7v z2qAyk54cquI@7b&LHdZ`+zlTss6bJ7%PQ)z$cROu4wBhpu-r)01)S~6}jY?%U? zgEALn#wiFzo#H}aQ8rT=DHkadR18&{>P1bW7E`~Y4p3)hWn`DhhRJ5j*2tcg9i<^O zEt(fCg;q*CP8+7ZTcWhYX$fb^_9d-LhL+6BEtPYWVlfK zTBusSTASKKb%HuWJzl+By+?gkLq)?+BTu761jmyXF)a;mc z^>(B7bo*HQ1NNg1st!zt28YLv>W*y3CdWx9U8f|cqfXDAO`Q48?auQqHZJR2&bcD4 z9Ip>EY~kKEPV6Wm+eXFV)D)_R=tM0@&p?(!V*Qu1PXHG9o^TY0bZ?)4%0 z1p8F`JoeS|<@=<@RE7GY07EYX@lwd>4oW|Yi!o+Su@M`;WuSK8LKk71XR(_ zRKHM1xJ5XYX`fk>`6eqY>qNG6HZQwBM=xi4&Sb88?zd}EYguc1@>KIS<&CX#T35dw zS|7K*XM_5Nf(;WJJvJWRMA($P>8E^?{IdL4o5MGE7bq2MEEwP7v8AO@qL5!WvekBL z-8R%V?zVyL=G&{be=K4bT`e{#t|)$A!YaA?jp;X)-+bB;zhj`(vULAW%ue3U;av{9 z4wp%n<(7@__S@Z2PA@Mif3+uO&y|X06?J#o zSi8M;ejj_^(0<4Lt#wLu#dYrva1Y$6_o(k^&}yhSh&h;f@JVA>W8b%oZ=0JGnu?n~ z9O4}sJsfnnx7n(>`H13?(iXTy*fM=I`sj`CT)*pTHEgYKqqP+u1IL8No_-(u{qS+0 z<2@%BCt82d{Gqm;(q7a7b>wu+b|!X?c13m#p7cK1({0<`{-e>4hfb-UsyQuty7Ua; zOu?B?XLHZaol8GAb3Wnxcu!2v{R_`T4=x`(GvqLI{-*2AOSimkUAw*F_TX^n z@STz9kDQ$NC=!KfXWC z8h`dn#xL(D3Z9UkR7|Q&Hcy#Notk!^zVUSB(}`#4&lYA1f0h2V_PNgUAAWQEt$#LR zcH#y9#i!p(Udq2b^lI6wp1FXzN3T;~FU%Lck$-deE#qz9yYP3D3t8{6?<+s(e(3(_ z^YOu_)K8!O1p}D#{JO;G(*OVf24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV z0GgZ_00007bV*G`2i^u04kaJkz7mfB03ZNKL_t(|+U&jCvMoz)o|k{^!6rpYEir6| z!WRy|i(khN%aQjzxl(1@ayaWOtar!UVi=c zWdk}^v!{_wU+8lU{PA!9-7kOj@~^-Dc^g>+W8HcYtY{h-^_PRue)$9b!~ge>|2MX6 z{9MHPG4(Y|O4CmVZ-HMPfAt6Y+yoJKoH%>7Twli*-idIsVCd=%&zq3H{Pz2w|M~Y{ zwqKYNde| zDA*csxG#p_74~`v=-+Pt@z-Df`A`4y51*}X(lH9qSG>bJhXim>`pfoL^!OkD@h|_W z`<95dRzbh?%(|Jm*4;L(*DvrnGEtJ8eUOEhRfx(@!x)< zfByC5`TD<|T0k|@UY!m0>Re~|cfq&xhkrx=_^3F8%HH_b>nY z_<#S~e+F*)9{lrPeto>qZ!cFX;PW{~&&dA1cV<LPKjHR{L+Kz&nfWJwkzl*3NfY z?@!hjdU>JWU+8@|@VyW81qE^WrakChYn?qjXz=RxpiDF{R_FZ3I02YS-0 zfx*%z0qtMtZx`ZZX9M1&;pt~D^!xAB|2X-;Fk)ZOiyxzR6yQG!vH$2KYA^K53;p&& zfBF0oxQ|*o-Eqo=P1(b}e~a|G9eQamzrW1@6T$6y&r6RYq8Iw@g|^@6w;w>5{jEqn zKmDEg5}et~(e~*O2w&I0Zx=5D@*XmEK5)b*McW#^{NCQT0lhsW)nAjrsjoQVg?&e_k- zg10}ywa<>*fmM&UD+W^x2E;#$0!oB!!ix3-OF_%&PU|w3=baxk8y(G={aSV#o z-tPp#i-3lQOxz#JKOEuwuvf`h&~P zuWx7fPQdK*{6t+e%U=>USa@rD2;N4}+Row(2Gw?TCrzbBL8CJ=et_}x9)P@eZX75N z4jcwM<8nm3NXp?);PO?0@N5Ahx?0-Y0#!Qqu) z>L(j|k>Nm?;aoDLlWV0U7+w$aytQ4y@ahnqzI}Z4-<1n}u&~Im3kc~YSdXyu>2BWh zk~zxC%F4>h%7!>TSkEc*%v1iel<5E=b!^-{JVj;o6`IZ{AoZVASL!Z%VxkRN`-qOD z4-F^`Lb0vnsvw-tr?Dn~w17zS>3P1lAnKgkN3ABUWBGbUKz8TQQx3^2bWjT$#GawL zJkYNe;!B%)L55+a!uJ*SQ)eRKDy5#}f8YKU`>73CCq3wMEDzFWoeszMfKq$CB1H?Q)64Q8osETbzG*kb!h^FE7ET4iSwVxCiGpoFm{N>syhK3@N$_$X zus8>PG_rBkJ_WopN*3zC7w|lp68@WM)8ztxIcFAu#B5* zVB%wG>OE%)hnx&F4(Vq&V^kqS2rC&X#1=|9t)UV1r<}0}EIiFObHHo|^$ZIO?*)Xj zvWXrXqntnH;O~KB99*o#>lW8JCr(+p=@+VC&gh= z`wPNR6b#R2{{2MMLyWnwuvHq;ZXh%j2Iu;SYCDJ)3R-bs8_j(>x=;mu<#l+j80Z9n zqN5_<-;cduO6n_b((wtLUt*%?nvzaqnxM$$JUJqYB}4%qT3lxnIAr^jo}7`sS7Nqv zD82f@WvHPk8D4}CY72%q=X!TPQuK0W1}uC#`;q2ZO!xx`F+&)f=p&jXR?r^>mzM-} zI8av%jGzRfNfaV~0z?dC^Q5mLy~qqX5gnD>M)74I{P9j!qeOp16dXk+XRv}pu21Qx zY-k>87=R2(cxus&CKe18GrL@2VFQ4}yf}vmX;NU}c}|_e!Ve(aDa2(d}<&)uQPg!w>27+OmA{d(12E1zO6x)`@!m=MpWmXUd6)V~=kAjf<2ii)4 z&U)bCJa9QMB?K-+fJ0MD@2NB}{qjs@zvW+wCy!-{+c!1xH~~c(I@$pdz4?&$`&n=( z@hRoVhSD~4$nf+xtPC`RuA~rk+(Iz)@grq~7v2a9zZM<2F&{pJ`!T(U!I>zC!@Zz4 z3TkiGM_n|Nq99YFQ}PR)#es`Jpp*a&JU`)hJ<7@=;Ud2LQR%B_#P{9JRr7$6Doc8EJ#zeMA)s$^)E!Mi&N0ZYUbK_mz(ckPPN-(RB4W z0CuGY!dzwX=rr0<(+tf7!%vUF;Gu>VexygwYG3u?SS-4mWmGa{yT7;2COSQpr~n03e2#+IV+DUCW0R+q zNeY2$5a4+99}UG&#ohOpxlJkMA00G6aR3qB_^{iXRQ31DI*?wn(AS;7Yj!1?MTTvt zUInhPupP!0;$k}|#nJBToi(s9Di$_&fbrXAm<~Re-UACC{EO6sYI%YN=@g0tn}!r9 zh|7CpSxFGg8CZq`O(D>k0MTX~TBH}O=ahE5QFj%jfOtd43lHs&IUI^M)DsWq$cDTK zL%jd%d@`i&9}E>R#MSXK9Bon&&#^#-CKe9YvcJGWGu77AOcr^$uN>1Ls2E+ugLi=j=9vKz{;E08zzgfbdVz?lh=C1ZC^Q!I+S|0^R z0tJmZu#LHfayby!DM;t+X0>x*;`tGO{f*p<0N2AmsL{tHOcT= zFx*bF3Y{E37-6XDN2-%`<{X@IVPRtzvOg$He=96}jMHEmVX#o}X*Ny@(8*EIS`r+0 z4ZXZN!8uSB0za340|ZVXK=@zF;=Qu&t4u~GAf?vGtBrozQ{Y3UP{!dyJ{(Sz4e4T5 z79qnPNc-wG*^5Kx|Ab2{Y$88W%d9htVBx_j4o9~gj*Es#J0B1Nort9(46fuOx@B;6tv^0F-cI31C_SLQ#i2OBUD;M z%Lsr%xNB;ZotOYt#d6UmnI`mt#+!piset|tNe=bk!=7IY;7~i@&~!U`ou)^R3|s3d zAJ;V(SFWVzn>t0o(87;YGrUj`fENi1-_npa;$IY;usyT$t4M{9iyd=k#rZToqPp&; z=vQXeK*89OAec0TMi_T=b>+Yz2vnyGHv){oKUnHCqkF{2Mig~%kkdIS$Q@#rTy%^N zmD^{+p?1I_yskK>ruhgNwo6G$iwvo+K`@Y5vr%OY|}4+k-X zU4zPxg%8nFp_P4`3WV=uU9z&WvaTn(xR7ZMSggxlR#v8mn$^S9vr(JmS0L<&8^o#V z=I<#g2=kMbE9^asbmb{)gIB^J2_|a%=0OY}QKN0Nc%rPmLv^pGk19Cz8I8oP_uPy) zh#}4Yf<*6~LSfbx8J>qC+)GB)COQxJ_@WE+KhK@j!jOKX(WOFCo~APw&4JQ|YJr9A zFedX_+(S>Fv$&7Gb(Pwcn)jxg03FwEIi2v)@fyjJLI!G81TI4s6}QpqZlVwigGMN5 zA1>&?fzbV31PFv?Z}So$(lpw4MlisGfZKf^h5Rlf2fDH>NcbE-2>-R}Q*A=Thmyv7 z;7~II%`#-zP9qG(iD%S3NL%i}!V{K5CzL83ny!cPyQ8K(5^0$am5Qx4=b>3iV8+_E>mQ zh|wGizlHq?M~&0ms)yNTc29v0EL=t^d|ba~W(b3t*+hFutWfYiC|3Y;6*qJj9lG3Pc4k=OqPqGA z^O+gs!_k_DDp@^^;BYxIY>itEZU;h|rYor!I9YR5VB!Aq+Zq~n ztJoi8RO#U6)L8g9d{tNqgNkJa1^H(Bgh5CNM7PF+x+;iMFNq~3!GIj7Ee3XnIFf3! z{t!5RBI-usDAZRN7aScmb^s^1sKAQ&@;@x}N%Fc1^WqOBaL5ce9G49{9ZDyX;n|S+ zuB2izq*LHjj4Q^dgR5E!O5gMMEk!PS1* ziRFytfkG3OmO3#Wu_8?w;~2R)oaI}-W$J8y1{~_hh9eH81NX~?VE7#aS`(3>OfbY% z7EomHBUQn|Bgk123*REA!2N!)ZeZbKZ(eL@FqAO(e)B^MLcCD$vb3Nd3SRD#XL*PA zxQ5cmoGieB=%gKMY?zKEy6lIUqsWM+LEyj7Xi|Sj+SqPZZ7>8JhtZM!SxE24li*NC zHq?DlT96^LYK4Vh*scht^$Uh#KT@BpGq@2J?zD`z>K8v_DZ8*Rxd-XvOOJ0O3|?}G z(soJ$Oa3URt~9oJ-k})$cO7R=G-anyeKD}>Ep7@7GiU@qH(tBn2Sbjc;4LphlH+;1 zM2trG5;(D+eI{`6iJ?=F_m8=cUN+HjWy4Dh##pyC$gs(BliFn^)fEiI1q=n%3p-!& zA4yiTsD&oA>~ZKOa9rdND|wJUYOC>c1`xGxb%Dyt)bH`St;)&_IJ2y*OqZOU_L)>y z=ce{}2ktNlMIAgvPrn&xt)6aA(Q|DuNW@^wa6y&yp~F6+(S^Y{ZiK$iT%6+{B6Ys# zbkZikV;97^xMX}T(zuDfwimJ*VR!rJfBp}vh2DOoN)d)WiD%lmR5%!0XyHLho$cXb zjFZdR2h$0O*!{%VVChgTze7mjLnooAO40dQo}v&{;_?X^j1)0wgo1N@L|@a%(7=pM z;6Nj^bE&%SO@M7ETha=%Q4nf&S;)B)MwRP{-7>D*qhQqtJ$m8~hrC3cO(nu`Dj5o^ z+g}xvXzCuMf;KsUg;Vm)d~|FU7P9dVP|Gy4Khh2m)u>fCn+YGgG#E!1?EZZ}iFpJK zE{uXx!Uff11tH?VrjvU{p94EfrJ)&Mffr{08mC6G87PX&#gJrrzVVv%dXHjPL=3`* zGB_LwGUS}GH!*2QGVH2uFNiG^CL?W1A|$@I7Y7T!hZ8dc_SCRYG1Cltyx1+x-PMye zT&r-lRQNEV!3;j4_@F@=8f>9pZWJ`)!1zv~X&FUL0Q}(LZLAICol7MG&tpV!Zw|6( zo!l4P6(TOqhkQ8nl?~6y5MXRZNv==no2 zk|>xYb1@PX2m0VAA@=a$+v8n#D`o(Pie%Wt*(n2%p}t@UX-XAEKT@2Ea!r_ql#hit zP2ky!g&pT@#&s$9ltrHv={THb8(8T6VtNR7>dA#ca}M#UDgg+4?u>%bCBXr;^L89) zf9m-Jh(xnhb_0&R+QW>u`1PXz`BqkRLPR}0b2__NHtcdJr6IiJkRcgNHQ$xAORADb z7)J0T)x*N3frZ`YZN_yeT7Sx!T~AcAz{2TL;iG%MzJM^O@g5HCBU&ifG+aY{IB;(A z&y5#BWQ;Xa2=JSyz;FyS;bkb+5j2$m0l#_ zT0H`Q(FjA*xw76^*w~(&r@QyOSa`TFF2geS)(=U$ae#V3JxCvYB@M=DaF#HLPSwsw z7%UW2=fE)(R16VR<3Q77CnQp{6A~aw^egN7`a?^MOAi!XD^I|e58q#cu#vMA8XPAs zhMITgjp;^)Qg2eGD=GEv2UG<^NhTZBWTbvr*n3_LETki6V0I?bBWwYi)4Br-xh;6S zk{*yfyiTVtqA&2$9EcewJy{jblH)zuugKMN_9*Mf z2G#NBd(B$VGv!0&@_>R956Fgn4yAN)K{Jx!kYITHNnl7YJi1JfgcmAeVaLg@uy8cr z%n=$jo$3@9nMipav;0WEaSzg2QsG06f=$Cs)KVC{K*4bYz5KU+Ms-EOmIDRN6Nhp{ za%)(f$bn=C4{i`RPJo%{34vQHfLw(e<09fQAFcq0Zn7b+c4vxYNCRUXa3xiYFl;gW zNUuvlG^Oyu9xSv@L#j}nqA?0od~bxSH~M>!n!Q9GeUGQp?U!}_7_HOq- zwTa>&>K-U)DhakPK@;K`*5SYz`+NAD+zd8!T|mPzQ9;^mxp75e{BgJ1@gc}+Ede6p#n7t--$h_cQMk#>LC4C&;<+S6+G94=X|I zsX0i6Ml}pCJxB$RqaRo}xjF?7GtDyRQ&g_7b?JoYj-outL3)&!l(lk>l*Y^kT!a9N`pSHk6OG<(PIU+wH@gEL zk`$}v!(ngw8DZ|3BxI=NL5lXd6(l~mm|$V!`=e@OVb`oq!9qa-(smTMhl4m$HWnI& z6wat$|KJy0h>}oLRT%6GC(M{XCW$IISrWAFo-P>Zih=CHGu99|jQ|m>owX3xZC1$q zfSlKbx)1lRLpXyEA(&NlI3&F*J{Dn!lbRbb$k0zPJYf#?z(Rw3GlhkRsbY_-*z=c- zUr6DM`t=Xadu2*OQDb4SbMN9)ZoW{^83lp2#|i}1C?j&9q>2oG0+)b5g)=Vcyo&VW zgY*E3z(8R^F#si+@ZpaI5A?ljRN&BAHoPLkEA-MJL%Y;Cx`N?1$m__?Io*$RsFr;U zEL130*y%ya&n#!_K{`A8=;Lx(sJMyRRT?yp7?h_Aff*~#;pw0Co zy$==&$`uk$M?@X{9Kpga57L0yM;|QQ8lk%+!k`unPWBP)L_y+g7oAbiSP~rC{A*OM z56gjeVxT7k4xK9<6=jdJ9O*u#y&SZ18a{tSG~&Z%42R~jVQ&%|F&Q4rxEdLbxRO%G zY<+~$9&?n$k2Db$0tZ#*Lt$aB2Wc0TZGFQW@gNPDZ03=a1|tfCbA3dGs^D1w*?mwD zaU?-S4n)u5K~@OVIT1qA;B?-L%$tgZC@I)wU5gl<5%KHkBEzAdY}l2AMk2~xc#Cq$ zuyvONY(|1%J5O8E=SMmeUN{L0&n_1A-HF{;XcSo3P8~R<3ub}`DOYTiJTxA=Z&rUd z(J8{<;V#f2kEmx7(UW7K1Jy$u*etz686%PC!@9fD8BU=eYX*8aU7QIITi*< zLkfH;Xn}5(-qnNDQYw5fUq(@c!K>vvx@nf7U?&)$|1A0_sJJ^#h=SbDitBRV&Q>5Q z&a6^@qL8c1av=?HRkv@8@qmmFkt-hZwu3@A9Fz@x97;zsf4U?lRTK;v#kVAWq`Fwx zIt8&4EF|u8MG*_R-j%9Y*w=Xs-Drj$q+ZEp9uaAouz+?l`G^(@25=4a zcM64e?L*_eV{UZ1Gx$XwJDqLqCzjxg42nU9Vle`+AD%zA3WIPMQ#L#*J25f+Omi0u zAq@ze8eaI>RO6N|JjAfm%Sd+BDX5S1`1xj9M=kretWrPuYkZ7t*n`w7*~}x=TOycW zQ+@F-D=RB2D=RB2OSG3B|CF#~XSV(i6ARhn$7hBkl(&RK-&7N*dNCJwd5g%SDk;H&|sjrL2DXB z&mOTqo2X*9$!?Ya03ZNKL_t*0ASCHe&+>?(A4i4%7|=)b<4J*myfUWnY{Y?IpCcAU zN7m!141L8A*_OkD?X-vEudl^M@g=RoFj{y{xaO?(CX-<75xE&A`B95Z!Wp=NBY8WW z@{vlDk+MpCumR)nvUcX1nVE+4G^%hK79K?v4ull8%PRKm`;O?@v9><}X}Ij8SJ&v# z;>QhcW+>SDvW!adG5QFD{U|7jD_aW%r%QsGdLVc|?5#?FTLKK((aK7*JU32)Mx6Yw zZ#|;uv#QL8%z-Z|8?wmIG9PJ^)x;AEhW!&E4=fCoh7=83Ya4p+J1ZVXf=n}e zzu&21k3(hXC%?V^>}6<{VoHTCFZ2Na)#d9ZYAp<+QyA>dCOQ`dPmWm~1&6pO8wX;% zDY`KPekN2vkd7Ah&0Gnm_sHCHzd z!BCOD5@BI$*nk%h`?fK}!mYK|@G_ifrX5I&==7ES!Eak@G|IhJ*+=D4As)~ATL<8! z6$UX&7@Qj}D9bj9NGNC|37({xU6liGNHv%~F;nLU;nv9_xab0q=Bx?uhB{=((ESWuNt-Ai>6l>XhlP!$>P*AJ_d=8a+6#5%AeCGf9u~eO zARP%TY(3=)w}V!Wq(buZAa#%mAFNl-LNwSl#-pAHH|yrYU~Uwg=^Z+KO*rJphL#ScxP+l`GBij( zGb$MF?VrPj7dl|!{wj!rh1(kzG8^woLy8j);(L(hmI~<+hz6a7!Sk2pjN~!>{3h}d zy(4>7(tQxKrE|M~-m=Tt@J(DWEN4IN={rnj#G5c=JYAY$Ms!~vS)|A;3W zYCDwD$gl-;$4ftRL58}5;guh0lL>~ou#iO%t-XFUq7Z5!zhWUD3-RSC$gI}DPZoMK zI{ms7BT^yS!-xjE-9%eYAw(k-Y>9-yMN!bwJ9HWfYJ~`@av<|sL7$Bqex)P0Le1jAfd$UH5+%&|Kb z4h9xpVBuF!RGgvOkyQAJg`I^!4eL^fq{2str)e+`w6e0Yva+(Wva+%=jjsqcouQf~ zHGBMIAfqfYUBv(=dW!a(RL4`)Eq_e^KM4outPPIHcO$hiRK72`JBXUx3pUSFPf2hz zhz@qsQ0Dsz=NP%3oa|1}m0Eu1a2o1{OfaO-_gbG?=n!Gpm6a6z(>ph|u-Ak1q*0t@ zm67X&t)~dyB%ScJo#$KpSXFzU{SnX4o@qVNeovCnG_E)7_nQ{o%^v!Bj*RZ8P@$#o z=;QjKm@sZo5+ScbkMlf5p;}~22w+)I5F_K)Ksac|94^9vz7RMxT38Hy9_^qNdq$>5 z*tLzZCHQ<3bs?gR5A71tK>5>tM`6ImOX$)V5r#4{yvnR-NQS($t9~A&SAm5p`DV^8 z*(~=_^u*xdZGyY1*bmmFI7>jv=KsUt;4o}0R!E_l?`WIpG8)EHblcQQv6YpTm6es1 zm6ercGTB#vp5pAbKp`H51L?eESOEA6gr13HwzCfa-77%c2qb4*A&X4+q+b177;SK- zk7!O|P;w)vKE$E6K+g}vNGEmQStc*STxQg5N;pz7X?s#}#R1K5Krqx#KLgFFbn_#X zONFGjy=FLqQvb1o9;AvD3!M^>eqKxOG&5bfI?>S32*hC?!_=c+i#zU4CW%kjonvO_ z$FIoqGB7j;-_cKn@V+#Nu5?K%o11R;PTiov*eGa~N_4?cyORn!kFmWU>^t^(EtZL*r7U0(=Jd&5vIdM(rj$M0GBl%@)1$M zhh1+da`v zN|?s=Ae|)@?$;r-ph0XJ8#x_euxD$kB@9OO5nUDqO_PQ|daa{zpdAJ{K;R5N*U)ob zR;<{+aR+_gNrP!(6cACH4-He%=!GqH$cCn5XgXvD-L?LT_Sb@8*nBh5u#i-oNZ+xL zxKL-3Y38dY+*lr@8!yoBjAOezNJC165AI%e0XNZiA5k-e(`(AhN0bT$6*=!a`FI(vR)UwYMh@%iwYQ+~q;)B^6T7`gxesAi5*t^ii-K3xn1w zTreNeiz9-dpk1Kg85Rw+oX#~=F(sEZ1UeERrX5^dmv#y$`2a~;n#e~>6!2j`9Ky)6 zMhOl{d?Gd(0@83nV={CT4ABW}msr?0CDE|3bJR!W8-aIhvB$!5)feZ1g(N)Vs8q;1 z*N9YjJh3~CJ}x%3O9Tx9`@h}~9fDFGQ9)C^!r<)8F$j9f>uvzEW^y*_07Zi?_Y@`XzX7`qoud~H+qq{c-ohvqyRP)zO~Ik`nhb&WPv%19 za8?0BEiC-F{?vCa&G5opSjfybgZE&%5vUu-LbSu>eSmNyHRbo`|EN&m05I!Hg_d;) zgCr`bdq|4PxvEFsyPhI5NYA(yD@^ZAbp1RA0L?Znbx9t^K#TPpO?Z zX|E3HMOoHT;pb1p)gArwkzOTwX`O+EpD*{_xUaUXPT1`<+g+}Zm{GfSnPxtRPx1@F z?XbXi^y+lhzN3#W8tjN0obD-#_mY|Hw!6y%_R>OUHc_?yocgE==uO}$8XX0{2GWe6 zV5nMx@j1{80*4w7h*(8^MU44z(blJQ(3{jB4&##{o_^v=8n%_6fMN*ad}ohc12;UlV?KPCpOW0G#{d$&Z=KGS~oxCBbupL*B*R4pg%b9Gc@S#Y9cEtueTG+a>rM8wr7@S&I z!Y!5P1QZls);1~$hT=f*s}auTzH7H(x zn9lS{Dtt(`=|(hYR(Jl8vTh{|2F@I#kAij_7z70s;{%Uw8`PL!8jd4jV{AdKOSa?vvo|tD;_aLP=lsJyQ1UHFjM{ga9*Yl*pSGE7q zA(gSqOLzti;$Sb|Ck*z|S64xZS;AnUAmar_?~edU%ULAB^V^AfPLvJ;7b3u-zHp1g z@%l!<#i4LQFA32!gNzy+#vnsd@9FonWPVvm#eSqpuBR(t;gK@bi-jYBh4=zB^E^o3 zn6T|BX-zfR3|}hj!9sCSiU;eVf5dDC!eHxi6K%sZ*ad{>Z9JYZ=o2oO0R_Ka&7WV# z){q35Hxg(4=i)%joAadLSEv~uS`m~L<)CLTA$EY=p0}7G)5Rb|uzN}(LVM2aZ#%9L z#_Ph0-ejcHv5?yJKaf&&ig6E8eBl?>tez*0V7J+)ML3>p#z!iY)vtdDX|T`*JO$&_fej$Qe(=i#dhAZ!7wF4t%xTq3p?= zHE<#58S^0*4%ty{IoeoxYB?o}yaXAdWBP$@Q2YG&t-gDrwf#M|4qPFuE$}`2b(# zOGL(jq|4@FI&lBN3Vli~5hA);xUf_h+=H~8c{aALSj2OkuyNJ1rlb>IRIz`)QF|Nd zx&-_Z_V^-_G44U4)_J{IYkno)(ZdGu1qgG{pi10eXU>}(wIw=(22}AdyAK5Mlba!K7i1P20e=n%JN2bP!s!rU_pat zLfPF=P13Pa|hr z!n`ENw=@4wsUDmd%T}2IF#Cx;YU)9k=pcv!@o^V z*A-OsBgJW&*@F`;s}`c+8M}P3@N5dv;X&Fu5|DzknQ)Z~`I$nSPU_JI>oqW5Z!ki! z!JeC^lQYq`cbpV4s3r{hq!RTp{Z=> zLWUz}khqfKY5zRWkF@unl40Qq(cx#)U?)mdOF-J?LCUT)cwMWIK0t%rX4`{UU8(Rm zXRJ&ryi7g%U}0O9purLbyHW7S%eA{tSy2$uajn6DP^!GL5CkqxfEZc>Wut49;`W_n z3&GLRwC_|saQLb`eA(!UiO)+mR3t+>BSUAw@ax`*mdC=b0(vS~c-+WgdPE);>I4?{ zd5{t_43%4}kUA!GF{$wN)Q{*i=ovJaS{RfhkI^}`rjMwvDu}O+BGFLLM-RN3x;sGN zYgUOxI568N9HyN(MX20(?Q*=d+zKR*Q>norlNT97`A~RL&2Sl;}0U% zSh%b9H2aE$r_xgg7S8n`#rMS}^_lG8)!jDRUT?jm!u?-1iB$NRR3;G(nz)HlYnuiQ z<`D+Np`e)PMhd4{?}=i|}ce%uf;# z4pWe!S@RQe&nVVvo8?400}D^YwC=z{XAjagCKW=sgp8!ZDVb&-Rx~(0g-l;n!!D3B zbQ2ASf-R_zr~(S=Zi#YnpwC7>4g@A8K=`#?p^`)IH6FZ?-I5q-n%;8_!=*~0Js)<% zA;)W)vCU%b#5x^H!;+!1VCd&Zx&#)E=9@XLh|m#On9hTg-?4Tu*rp~Gx)r7Pdf?*_ zrsmP0YtUc=c5x7W1HgDDo7T^h(S$)C6dYv8Xebzf1C@Co2nXgO92hr-i$`FY!!^Yf z$dUT`8A$oqWLxlHe5haFzDG73cPI@@hSLN?>c5oBW1&jE8E~1zDzI=g2dP4`nf*kw zBN2cnQlUdp3gX_3dr4)2?4@dVQdY~OtgNi8tgK8z+ht{w)+YtKS=jw~Jbx0ja25@k zQ?y-`o#BijyQe5Rf3F2;k=#Tva*B&?&I+WRlUpZ)}ltLan+K2iJ&H zGR?Sz6sq`+9@QyS_8kqDdbHt5Aq4cR&qs_K{4!GFpeNdzG}zU?)^&dt7y#F^Xx&YTM<;K6eX^67%QjS4AQzBfhuq_`NiH8<&Xz5TYCquk%7qwGj_zQ+| zEac;v5@AlnLTU$4M7?KRQ%}?_taJhagwT5np;rNEl7voZ0i=jZr~&D{hzcR}-aCX| zL_t8h5|CaDMFbS2Doqfiis+mFbMJHC^C2gn_Du4dGqd;FYptW4R_^~UoH%#|aq!R2 z=eM!%tq3r&FP)#h)PrVZ8H_8TebU$osPJV>^8HL24mS@HYRq9Ds$aQy)9V z&3%n8(YmaEZu|)&8IRolpxBdFCL_n8wEkIfsg4dO&s#sRHsf^B+*alSi}8x$UpXKC zQ}HG<;u(uX>o2>lN1%ywKJF|)YJq1x5~rUHo7z&&08I1bS$q4WQ4C}8%j{}=Ld-e< zA=;r|x3YzAF;|o^XX=>|;Ec4)aUB3M9jkw7u_g(eu8&fv1F0G>O}XjXI3_e>Dtjd! zm}eyP(+um6GSR4(Pj`(m(>bhp(9a-{YoPOK%p;9WUkru9b}@{ir_`NbFM%>`$V}*V z7t+O#aE1J(u}vV|dRx|e?#J~<+}Aswnl?W1Zx(tmHpoXWrgxVQ?&gIoLK?vN@C1>b zpFUF<)LM#(eP&3SSGjhe4%jn>QnXoWJ;g$gn`&2xd1^%AmuV$5AjEqe5;U7i5qcN6 z2`+%K1=LB^rbg-7U|(18{Q?nsEA{!B+Q#LkBGO9;wr~g3QQZpkd!0G&1r|u^_tg5fSDAgR^rjqi_~x=}tUU(KMLeKQ|7e{x(2Xr7yByp z)?@lu7NcI$6>B@BiVaoFZjEUUFOxoYTUF6k4v9BY-n}d3^7)ZStC6OArrWoX;1gF; zokiX72;KaavR&0M1w`}Vwy7HGai14cW=4%v7Z~_sOgM=%3)X=O9ujuk99#c+f7f_guJgT|Mx)uxGXw}~KfRZDCF{C@W)hPX*3ei=ok@^q&j zKbMgP9>atDX0CZuo{Z~uQo0_4Pzd_rUt5z=fgcBBh@j6|!w9ry~ zEa7Q^5qJR42bZ;&tTjW`vwRzZZS?iW^Hc(`fX_D9zrCMzww=jA#l8gcdOndhrDNhr zBI(0i#hH~u--ThgP(QDI?B#$<-%>?Ev`JBflFM%2vPMShDyr*PFPWe&{imVQw_a7y z=TOxI+9*NP!EFm4OK9DkLIm)pN=FlVA!}jQ$~@M3jVMeJtT3N)Cj{3I*6#SB{yt@+ zd54dAsXJA}Z`R(MP=@!CXedMraTM`N5hbyYajP?2XsSrdPyulM{j)|Zc0-+9f6?hg z`U=^1DIRE@n7>gtw{>=IiTC(7PX$UkUxhQS<82kr~iv9vWS)D z8{{SBl77R^8-pDHImMlSVRR?mpo&2?H(~LxZiUJRkysa=N@FByPu!|woWm}VuKu9n zho;z^AMdpWX{wvTwzbDkA7yhlA6cd8B8alg3^6H!GfwbRg!vgeE${D%q&vLR|5QrS zNP0fC2V9S^cOX&5=hHv# zahCNRZVD@ESO$0hpFHTri!*53xKZE=rMd848s&Y)P@nynrVPsA4GK$SX8=q2_Dcsg zO-?n@KJk5wHD}STAi#`*?{kA=hSYZT;m5Gqnw12zzNz%_&eoPxySqPdW;bE&hDAF9 z*N(#ZYIk@lha<5@uXM1cB5I3Nz`f{=cG+kt`!++kuwxZ3)+D{O+%P2E6ZI-zfpdZBYPusAB7wmAh5(?E_pxnQVuzBd=U9BmBiYh5EfI|W_S7(m+Z7Tp_ae9YxPTnm0!Tlwg#*Rz`F#oFlTqJ3yAB6u7fn=7E?N@l7c zk2gf@!k!EyL5Fyc8hq-UARY@o>&v2#<$M;NyC(lx;X#R#9D6|^az-|t*KOAL&g}9p zkTrS-YB1-o61!i4ZfsW5qpX7ctYbX9J~2`M9q_REYMcIb1PHi0Ywx2pz1CZE8~7W? zmr=Irkp`cLil(s?l=xGKFcoBdo{{8xZ`kP4)Cxb43yz1z%*oK(ehwoR}$WLow;gT7o!x|JkyG$S4}WRYnHui*_#4>dm2c zzV-8tc%T&XpMA6eC*4t1UHoetS>OBTsIi7z!Oj*oz%*Z`>$iKQyrcQkSKfc<$b6WzztRjTMGd4 z!{Ny75C^HaXN0#1XTd$b8?qYG6!*1Sndrk;8NR<1>V3_>_>e;!Z#tHI?Hgt#n4f@M zruC1)ifsXwNoaO3A}?!SoPG>d(pMbF1YFt$cbI&S0Ppe1vdOOF^hHOws(Jkw zQ^6R&t|G!H#)~My5?>k~?k}l7mrS7CF|H8|NlU2ee|x(n*McH)S@#MCcx4+70tWRI zg#V$26ZmvE(ZuHBQ;Mt+$Zx)6Y3>6z)Kp#$=~xub`P#86yuSX;66TnG`tvqaK1TT*W!rf-}NT|0we z+);+Iy;Q{KK#d&|v=}d~y1r)Z5X}c)j4n-nijc{&M-_^)aiG>(pUA7@l8Ox3?uqT! zHcanV!CamNEbD$Y?hJjZo#2T*4_!1;^oqIP-?j3;?E`0y`)DE6oegoD*BUG@`03X> zjGlfN-o!KbV!~UBhs-U^IJMj`Z(To_rV+vmn4U)2TsEY8cw~8+I`N_&i?P|(VmBI% zvWZEYp0tYaF(32U_ZdupY%xLVrkjZ#e;5o#s@WN4W+h-bhNhs61 zMn5)h$w39u+u^udHB_HF+~@T77CiWb2E=Tq5>!L8iiu6AmXwS{4GcT3XJ9luTc zTD>&HY$t4qa}>oz$#H;+@AnreUOk4>eR-)`?Q=%Ug?`ISM`eBZfMhMDN)EESeXxo5 z8jRKPQNG7rQkXp#Lz_(w|JMh5ETMHik>s$JMOLX>LmI0SR@pz4-M?f(tZz?iz3u=C zx#a1mbof|+DHeHb!OO%J9jDSXBgNqNx8#6_z1k$cWq6^G8>S3H{lF>Zp7GQzUXIs% zxVyZ_5tZntP93d8Rxgd}@C;C%ps(q^1_gYhw*)Y|8)Ca_D4Nl`%$ad_0v*n$YuqER z23)#Q>vJKM^U8p=C54MipI9G>ZAsnqA_`Y(%otXfSa`*sX#QZAiS6s;Ydza`V5;I( zv8|-9Dr5b`LZ^QSqs_XhCOD83#lQGyUBVij|9ox?(@OerNKLP;sz1~Guog>+RkHqo zcmAS#{%zM+{i3Q3k8kFCSBNGppDjyFMdMbtL*ag9l4Mg$I*zzzkV^gjC8>oVf+CjX z>HdJz_KtzO02^IE8@;(n*gGDUu&}f<+>+00cFMY@ALJ|tRTevGI^fRKf*24~DwQL0k37ZS_{@{`I$+k;q;Mi<&N zlqoZZ;(JY+83RT56iWgHvl&BD(dt?Fxo8I8`I_^jSeRpJ)e8!>2Xzq*{0#ySjrKO@ z5_Z)s84Y$0=AV4M8-6HUS_EgVoyxDtlbj~rYye(`wb~H-8X^CKZ!syXI$!jl!hlHc zl;Uu@ww$&^Hh5K2sT@{Nk|Rl%@bx^JJy6S8ISC#!MWm7? zJRZA7Bx5%&UH+)+;W>w#>Y8dG+K%&846cc84IQZ^LfPrFtE(x`Sb%b>f||qe3JZng zt|1M@Ft(oCfS3J|@%#)&Dsg3Eko|6L-f&N*+)3*)J#}6FbFzQ|{`O4?;3Gyxo;{-& z$EdZ5CzT`;3Z&mec`60(=v^AeixOA!OT(Lr8on-#uR_#ac>?m&&b$jemxdsJuZSXp z|9b^JcW$GQnBuHfm~*7(rQC!zbumuaWUXCFeNrlW5g|khqekR*H6CXNNpb1 zZ+Z`#PZibaulVCf6s=&$6_WNxBJ|G78T-)#iPt!y=myCRL(vc?bW=?)b0<2@>&BOW z{u03!!OKQ%a6W z*uz-^%%UT0GeNS_NA~Nqpa783L|KG506Xv9XDB;Y0WM$)8WZZ5z;kcpc|ZoU(%w*i znVhi`6d%796}-_mM;2H5uOdWxYz)WtKwu4rzhu}w^#f<%?t9?E>`^1N{TpFHfLhvf zggk~HJ6+kIZW#bF(X-0l`}`sVw#Oy^xxJ@?-il+Bn{(V8#36Ew!ykpkw=+`Eki*OE z>FC~Pb;@qb_hxJN7n#Pq{>u!P&6$Kfp{iBUy2!-66R&&8TZdj9vsPXRD?-Q%B2l^B z--POXDh<}AvE&?i%I>=wOP1vXJ|gjihI_CKpRQi6Scw-v6EZ2neZ~tZL|m98L33N# zg+6?LT%DJ;Al=N6ryYr~`B>o#27^njk0S&#nfKlAoJvI+%|cKng|^mI+4IY!G|Xyx zg9imVNPf=vZ1YsF7Y0ch2Nf+a0Pmp%M-0WFagtz~j_Tz-(wQW_WmWp>GNp5^+Iy!wKe*dH98jq`ZDmbsvn*2@)UL! zceSmj$IsFGbNhj|hIaS}JL<;A{%JnHnN=TxJjf7fU=^5fl#0ny15`Txj8H-_^vJnGm6$u_riD)l_7Lt>@_LTMaRW-1SF z*VRZ&SaCFb{%zvs(|`kWOMwkhJ*rs1gJY3_ajJoa*wA)f(VgX#H`WWk!ig0zlpYyT z*F-Y_)5j)3*C8L9x~^*V95;z$)#Nt^v55XJqm_84ajQMJh9ExdlUetsO3^E>gl3Ll z9u&WOrB*9~l{^4<<;Ymquj)&i9+!=g3)B$pwD?bEXsd8BmHu8y8YdK2@>0xnA!z`f z!|~ndNo$dl6K+pC6p%F6X)buK(E#PR7aj1CzjiCn&4Y52d(EoI@mpC#>Y!U%sKjlL zX0#8ss65`3@a`u?g(E;;>o7U$!8z}@Gkc>miM+Y*ojYYAadAOUMlf)sn&{+XM?U|g z&G$1RSO%KHpgUIq*0qdCH1ZvfkzHW%vWc~Y=;jqZYOBEb3b8P?K|Sd|@x+cdJEULz z-%Kp|+Ve|J%gd;79`;tK6$jT#L|!fh|3&RCa$bNNP`#-+p&qgJ$hQLg(MS?bbzfr> zkE`~m)s3+sgX^PyQw7reXeJ1ucI@?VHfPgxU=R69*i}eByfqxG{nz7HSy)&~zV2|h z_N!e!9c8Buv4K?9Z@v;ceKvD$1aJ++L-9ODJYF{d(&5Mk0NvkgpZxHnl2{W-M@$F%q-6cFqg{|9jPwBS;{#c#i4 zuwJmI-PkWqHQe@R;GKny+NLc1o|UO;u_SvJRZK>7d5W9fWOGw83S~`?6s+?p3%NGk zH47^3w7Gx2$(|7dl56;I_y}yg7=|J4k1UNl^JVmK%oX)H5scsn_-254OoKUw{gU?^ z($a)F(?wrab~4&urm{c)H|!suoZd9nDQ>$(?>ZXu*9k4i1|#*{)E#|~u#|98WF(_8 zs)Xr<-!|=88IG8Xp)N$l!=CE#`x}Q$_0kVYf|b${3>-zy zdUP%Ed_}I=zY3^=6(Z!5XGb=y`L7(iF9=k0CZi0K8(J(rN@8qM(O3+Uf2tjIo(|9r zyKN@M70lbazxH;Z(NhoSUy!d$!m)X7h``J(1zB;-g(kBCSpeYgniD*R0@Y%#=8i6L zG7q?4`Io5+MEplQB@lp=wt0MP@)Y1xrFlPFh6`g8FlN=SZAGb$%wlQjixj zJ5KaorF}xC3*v(9*O*_c3PTjAE~6xB^;ruGnJb;#ID1YWfs#&Ip<|GtDNw z*>l&_6>bh8i0u|C_hvoO5?xJ8TJ_*zr(Op4`>GccPv*#-A_vSw47!&*C@P8m;;->-U-V%XnD(aAq>W1P|rS~#!8u$9Oc)QkRzDiglBWFtnaMuK)* zTd{VmI0D_1<2qYbQd3JurA@`Le|PirNcI-Fxj!G*%R0A5hMbQu%%Ya1`cQbc#@;wedN3N zvmoX;1J`VyM^CdjL8y)e*%Dh|7lsOoOp3j873Le%-uUPrDkVQ}=9jTnPLX|eWek6I z8$*HTl^cpFilwmpqE+K|+f>_VxWdH@j-*SXi5xxh4m)?Vd}<7hulK2ia1MzK3^p}U zyD!o^plY&+G?82CdUud(4Ie<*G{C9&C*D$RnMTSxE*`&9mOhp9(Js+F*P4=8sfY#e6Cnbg2)2@i^cE8HEETX>|JMbEXkmKryEtHI^^_Yq`rCskqcq+S)Ye#f4 z=~vrppku!VCxGZ?-{!%VYTt&gH1Kti_Ni9tTaTP_FA#7gilX8&PAWO-TSi$q4^=K* ztm~`(cdt_hp|UFSAc2dZ@8%VX``ub&S>U_#(np9sr%GItOru4|vv!%P{)>Z({WRD; zZGaThoAET*F9rEr=3QT8tSJKHaN{TBPKXa(mzaJlBk(M#YB)0}P-(hTqx^B1y$oHE zA_n`&S)5;B@?c1Y{i({NEjnh0Y`}gEOJ}`s^q7}^=Dq=T_n@NjNN&>p*`8D_G9=CA zn@VMq;A9dzlLpy`Dw+%GG`$#ojOt^}LW=IZpimA86hU%v0tud*SLU^v2SZo6AIA+Wov0#qJClo(#HZH5BpJ~Z+?Jr?`_GZx2m z|3$w~2YAn@U%9foh}Pv)(%j+85zO$M)ZT|p026A10v?*6Gfy#nqmVA;GVV7u#uE}W z;{L<=+13~Lj-I0xD?N)wDdn{u4kYgBJUbDQy&V9nSc>7`$Bq$uO;2?7pOlOoMN>Mi z-g|jIBI}<`G;vRKXw~4Sp`!E~9SNeR3j|kBm$~2keYFV!!Z0Wd1wW6V+oX{(NP?{X zC}wS_^Zm9(ypIZ(u>kx&{S2og&QcGh`E`1p9jSaH74b@2hS%=wC|XU#ENItEX_8_$4JUhUCGC0dig*3 zZA5bvAmkmg99q+58bBQu3i_*R0otLnTMvNbj?UBi<4>{AsBNZ!ph@vtzXwpXaz#>X z{jPK8`2NWKirFgI+KaA^RDl14!W#=JH1t8cU+(+Tw4(uu}YRRO;mrP;>* z=v|RsGCJZaEEdAl^h6A>$D+Ukjiod3PfY6SB=-3(F*b<~$cX;o5f|9BO#sYxdim4D zB)Vy1ArFeN^(yaaTIf^=2__=p5`PSvNFd|KGTK^9DZj7-xL-o7w4eHjnna{#_h;EE z7)Myl`#|2Ub)^n|tpDsp(ws<^+?p(d+=Y;t0K1IrYK#Kaidzrb2SzLf<%jJ+0TocD z|KgHJbgv&>g><-dJu z=LBAskg7N-KajB_r;$|CLs<=zNCk?{2k|mnR-$`g*oB(-M>pxdb|;igLi})idwkD80ULc61k){G1fs|ysYoQs1n48|;3;zuFxfa;WV6T&J{-u;K^|EmjTII&| z^liXNLSp!h82iy03|DCr6Q1jjER*6AWmONxKfs%dCfuBe34C!E}`qVl|xMx-quPt-6- z$2HVF{UqS*cyOy=NzwFRM3mqf&3U z6#Lttnmg6y_(}z;Ee#<}XzzRi=>VK>zGPxBKE2g8;Dt6DJ00aHL11sVb`2bmsl@KJ?k5UuDf$FA(*na7eLIG; zz#DkN=49RMYM-=K8MNt<$<6=LU61W`lE}fQ?I>*y?>M#JFM!0-mcYN4L_{}EgP%V< z9CJy;W~%G`GQbLI-N8~{whW{CT`j5Az#cAb5VqTUxAPfzHMj4F$iTMCI_Q%n?~*Ll zbnpYK4IxXN`6Vv_V=AT)HVP8iuvTC8F1b(QCd)=?rve|(BrGvTkW=__w#BE^IYUW> z;eIXy(c|bgS@yNxiBZj0QN3Q%W2%l?BWh+p3!kM9^Xb*uNY}}vT-63^C!`>m z3=j()b-K>C_g>{U1v(|7cuwu_$QGz1icPOVKJ9o0P0S|L&+yryeZm0wd|u_y2rZlu z#>`Acky2nxi1m1xfew{H?Gm6aIvRr-CE!y79Ol{iSvV>s+Xnhgk+{^W?VkP z!{eTflg!Tv%S&0Vb&57c7GF~a0E8`ubn*VD^5)mKFMQ_fVmWSbjC7I?Q#z#4%G=}C z9{c*JMw&^?@n(@S!3qqxxUEM}FtibO$S0N=cPcKh$2@}S0r*SGQo^dln2q_dot&U_ zn#+@fa%gEu>a#)9KPBZtwB^Ca6}Z(RPuwj^7P6dWHagjj9)HT0VP(85QH?p)RspSrVf$@R^G3FB zE)J^e(E`Wg%^&;Nzhkq$$ATZ}Fi!dvRT};b2#CpS`f0?2U0KA~^FRA=V=LV?fR~&^ z24(Kte^bULmYOq{T4~aR{1$*->{R}dKz^WG(_S%ZtLbK8p=L~!*elPAm5n7eN?}mI z(hVI}BJuRli>JTj*8_$~+SA|r6)a+7X#yYgeyN1r23H1mD&JxfoBG^F z4=$u`TOU-*|0y|MYoq*#(2x1>tcBXUDW{&IcgcJpm*Qio?y>^9wXviM0Q0OvpcoJ; zuh5q)XrWc}T0N)MV`jctrnFD?=oPh$ZB3;6D<$@woy7_#0jwV#vUOv9gLL4)P*bcP zSz8hTBCU%&I~qX$&zGJ$P#04}M`| z^4betO3ka%>GY&nyNW%g@>L-1A2|tNiv34!bW=3(ydb8h8sA58FE;p2bw;bs72M`) z|JACZQ0*2A(MwD036*I8=6nWKzPZXk!0mCOa4nGvu@GPY8bbBQMiW|_#_rI6&mur) zxj#|?6QoaU72;6BcWwf!wTY2Q-WAM%Ueu=gY52tHi`zVB8J4lJUci%f7i)~NDw~NF zt!s#h`<4_@-?{79ALI`Hvde6D@Bdva)4Nv{y`BK3msUn7uai~N*VUqdU?=k>1D?6d zsxYjZLu9W)9e|<#cBSfxl z0g`t=H5}YFss|Owp;{!(M!MeH%j5Q4*Zpp>=ldLb^HeblEYBne^$oF%{xMUInIW- zm>U?Tz82Aw+D)u*c%NX1S;|&UppdMc_@aCA62cY+j-b$X3v+iROYX`5vGHy2ms4}u zW}tK=_R&cz!wEX7p%xA(oVHm)%4BX(K|bFWM@#L#_`+{|CX+*2|q6eVP^j?Ys{Hl03+Q|CNis~Wy6N!v{e{sQSWd6_T zW0l0Pw@Ww`$sC1Rdd|nQWz#J8;6(~sCX?-_a*z&+>awu(o!!+uy3s~M8Or*VPX6qe zi4g)bO(85IF5WtC6q9v5o{z*4dkM1DZSUTP%A$0@FJ3dX`Tq~*-rZnk1SKM5{kqU9 zb@R4isayCX&Oc7irax_(ooo$1ku{PHGxd!MJ$4zzJb2fdz&SVO80B*5zx8G{u)Mp+ z%I9;Yw5v$|+KBH57CwT7?xuVupdvg?XNH;-3#zZl=f5|`veit?D-YhBePOmtPxIdh zK*t+_AdvgidkN8vZIoGmdhIGG3%B=UdA0f07<^Iiqr>;p7;Q`)y2+G~dIvy~c>Vgp z5p|aGZIR>@UeQEaVVW=5w9eKGI;4Mutl8u%O_zgD9+(l1xr^CNLa3b%LUmOM)A`h& z?>rM7}#?E@( zJhcAm9&ha4Z8sauhB?T|hgGXy2LEHLikp4*BEZsZLGNctEX5_}RfHa};&?Z06F0cM zwf8~3-^0nUDLGPCQ9#!V3#ucWpPMp&m3qzP0ZksODY%Pz+}8xdp5r#pH`mkGm0+g?Av|8nrOK$dNvmFv`0D zY`*q6%bjMK)~;I4F*=R6pEy;xv2-O@_|;ah^YBRL^VjsjwA-2%Yq2%HNxQSN+{jv` zfn6ldPmNzcpCEBqTW!3+;n{c#F5;o;o88-T{wG&7uz!BZ3d^v#vR*JM_P&-~J{R$e z+qm0Vfc?941<~l?>Xrzcz))`w=+w7V%dftfLufBwe292eRbIiq+veFYoKX7Iz#a9K zX4*`$=$m_GL0GHfRvc37Ejhhs`J%;`>KK<`i1630Dwu;bUN{i;gc3G-C&cmKAm7(% zRTflwLdY-+bTg&jO`!OJCLEBzGQUqk?0X%?MAhG4d&RJ=@YCU4_yF$j?NpN(2`gmF zbrNTglg5%T1g)SR7)w0 zXy_Hkhl+o#gMcZ7)r;=W9dER#P^L>|!P%JZ8J3W^HH!Fwc5aQgZqYDDrh^*Q*Mp` zV}5yUw$|!88))TU2-T0AwCVxTTe>cD2Oi+&NSV_6p@b!yuR_;nRMHmI(@bP6jLC>P zZF?MiWom#upAiYUX5Rx@Y5yqj-8K8G_S}DZ`fm8*I`z5qaBk?Af*iHDNX}L!`~BCc z--1{?-I=PW^p4z2pLHi)YwsChsB|#t7SQ`=W&r?f1)DXvL}jW}bu!v7afo~Vturi! zwSL0M${v+V>1ICG-`G9-{M!f6vaV_oe^qzz!>dg5)Z_0^KmW#Bru9~MA+9vL#HyKO zb6c*lxTV%@do`)GJl%~!rqA$e8MM}k37g@)J85R>`0uy8T+_bBwsLPcYK?%U)xV5n zoJqA1#3p&#^DBHad;D(NYeS;+%2!JYuVlp z4&BbcCK`;f3K zsV#hM@_FG*11`#O1B02yZ1y*(B1}*BV3qH=f3_~!rx8?fnBuqs_M z5+c8=Ql{Xr)+>N|b4P3<*!1-odmBW?wAA!f=@+>em8@xL#(ra=CmLkEnjO#I)e3y# zcRDF!QDg;zanO$0)y*<|9;9|K(%Jb)bb3{5JOia7aT3N;KPNV^raGqfYNk;8&T!I; zh>e2N!RQLaNZb$F7Ytp(tipBDue?v}v+U%wF83s*dp2Cw z-;CnfFJ#Er`+xb~qNQ1e14s?9jqB9IpQXVQBhiocaj8zW$7P?5=*%n)f?dsKc`iQX zTjx=rg2=D2MB8~bd-IwZDcf7Cn<87C)BVkYCfJ(E53rWPW@$&r4t?OCfy;Fzo0lgm zg0IL zH$VG|pxHf5qc_nIac=hf>e6J*JZ?PU+A3O22`C0Xr#@GY4yBs23Rv!cU=cyQr)8tj= zKa_0Qq&$0#5ZMd~(V(`tn;V_5v5ikvG+Ln&rXTYPa}729U=#3{^R5_h*So-~5=!L) z3j69ss4lXpn)bfE1^2}FX7wTej0uVR-y?;R!FylG#{?Joy8)iQ&9|(A{Zo)htYr4d z>Lov5>=99Z0~fhvZ)4*iM=M9qVZFTqzyAJ2*A0+MR_UK71IzALmF0)Y@>k2QVdQVz z4mI8KnsaCAVdx&MEDs1|oL-f1HKXLpSc`%5x(9FmJ@9Cv=AT&ooGBa>5@5Dxr1E>Z#+*tE!U5 zANCWRuN_gOJ^bCCJ=CX5vE#W6dNKK!#4F9O!3M@D=;)6B8`210+16DQ-U87~rtR%)?vJQn+|n-k?n zZVWfk4E|`-J7BVbwW!(laf;>IJWatowSP=GgXJeKG9Sz-qLLPV#1c488|OR4Nb^w9AHYsANTeLQ$T@shke|6(dqoXxzr)_Z%EaNJ`{)!7 z$!1V+fQ>-XKeZdJDZY(V%NwwnYyW9KZLiD`u$;9ZuoVEJHA9puH(5{L0*Q9qQ$xgv z9zxH%KtJ>54+c8xbU#10p!#9)E3A-L8yx+jQNwWITx&n~91 zyIQ?wBt$}~2C7vJ4d$6;r69j9?xrDP^Lsb7OJ^lDV{FLb#HiwW*db{z%XH1()GM!9 zv}I9XaP*YH7ai|qi^1*EcjuT}x(5>UezsGLD%%sa`fffa4&*iXcnBoUiWqC$B%aQ- zQuR8KzZ(B1niU~KSUyQ`Pw z)wCbWsq0+fY}r^fr9r1p}i}kjWq#a}8x`v8KrRmOT zrx;Vv3r{;c197lLekjKMbnNs~^(Y3B6uEhX3l%O_>i^ zQ}TK!fzS?^c8(;P4;mT?M;j<1!{aKh1Sx-HM%;&h35e`k}Z6BvQZd0Wqz#+TQmS}urvVGp99si@7 zD`XmTRUPl0;@Xo^M*MW?bxXf$i%o24aAZ(B*BIiXKXrRm$dQp$MoBWci$$MjQ{K)I z5y(a_68;;Dqf5nr-6v%%N> zu7ipdL>`W7&<-4Z?+wpQ=RqlBG+p%nV_yIFB|Zrl9mei`%Gul;F3bvKQxD+{Ww>k^ z**pRjR|8|hIwES7G#sU6f37Inh-HPPfoG|8!{CbWfM49nw@hmD9f#Ukux=6kWLOBF zN;q3OgTdg(cASyV_rT|q?v8>&Nj(5@G;!%^&|V461{1l*>C+O7bc|v7^N)8r z@>#n1=P!b4=cC3~^5t0Sb8N+U%V$v~c`+uhdxdYbj`@VsC>x8k%{rZi?%BF?1OzY@ zLuidP@oe`!Gl9Xk>=zxJLIfog%x(LKI7?0)iYzGsgL-1b-+A-gZ1Tq4 z$>Pc^YLwFGLj;vHcoTTr)EQPK272_|RCu&*5Z)&rCNrlqycZ6i{S`9FtpeF|v*oZ@ z2sFYGtybGT1Bfw~M+h}4*njF(>(KhEfmI@7>SMmY3ey{h{*{(Qjw1)yD8I*|Ih4Ou z?Cy5+?4O@3!iAg*Iy?cDubz8vF(@d{EO~GQ6!M`5Ar9Y|d#bC+6(@yBbJ~2Y?rJ4p zWjFt%K;o3IYQ>n`t9c~BTo0k9xAwr64N68TT{g=iyJZsHySc^#uf88kO?;8{f#)Jk z&`G%rf#^VSom)cX&4MmcITpYCjqpAWuzx6%5RaTGjdag%-qg zCda>-nso=kri!0%X^I^epg-tw1dn6z34oVBBvfK4zJKIId;{&H=V6?8d{#ho#^Ydr#MC z;*79V=SY@}Ocyx*^=oS%Ho(9O~~CJT7aX2jEaWt}~3GnYj%S(?<2gJ#AaUz;`w;a?-3C^OSwR={!+0dJq2 z#SYXM*7IZiQpCWB!RQY>QBkc0MeETvj1wM>|I2Cq@4lb&1{iUhFe2@a>hOH2rn@8U zEya#vHQ;GI=ztSeD>>`;!m?$oa}ImWk!$#glbz_1YNq;9vNw*XA*L7xYRfk!!i=vu z4}P+r)!jQR@eN=K9(4D8KPAMn{Aezc!R6LpJExd&QTR09`pjxrN%@wb?LDX3|6WSD z>Ya?$d0UZln9dC*i&a+mmC`jY5{-;J`iZEu%s18NncXh-9-21#<;EuX4x-Bw?HEY; zX}D`hH~~z*G{cwlN;7~%t>eC0%SfLb){PM)LGyQ0f++)4Zh@HZO-{uQ4fl&oCOupX z>wIUT3C{XxMsR>ts3#jFCc6fP&$yk`WIs|^run$-%kY#0^~bAZ4~#l_dx(P?XklWyRXU#fi-IJ?cY+vm(%E z1|}9*Scbh$gUmJ8Gc(fAG?>Mr5rq|dJMT*Bu|7|W8=8P-1)aJ`5$Bbt8N(Q{(h&<) z*UKGM7;u8kCP%wQ^oPsg%-X^}R8~a2M2qAtlDVGZ?yh-tY(folMj-UYdC2#@Q9Edv zzpbo*oNQs?0f>6iKGgk*2Nn4J-d?7VB>4*)X_ef{1iKJ0*|#rbk&F{|Pybxl=BBrcn!mRCMdeY{#pfTF)i$bXJy6P5;w(y|c4O@@rIeXW$?3fGc2@#Dq1xgx66 zzWafKh#R3JtZ~w*tg?dQ!8KVwvne8m6p3ci_B7Y3>7}!?}dGd5B%e zxA~L}Ee--^ZZM*C=bp#;Fma=*MJNMX%fWef0N^jSHa3f%jXXF0_pEB;<*GKx4Ld1% zh@j>3$TWWNxBNj(g6W1vO-t=(qpT(K)>iy$xvgM>g;wui8qw(C=5OBe!e7nyen%UB zmPA{!H!aESkTEGrtLSC<1{}WYCXebl-5!4w;N>hmYG5wnLoQV6rF+Y&|=qp^-|S-RaUWz=Q`@RY@T#n@{Fhi?6QO~gN2r>nWH9>N9#(TLXTpAO zp+(gB|8ez}VQqFzyKrfX1c%@SiUxPrLa+cS z?(XhxMH6Uo_d+3PixqcDin|qeX({edXz7>x-p{k&cYplJaggN7nl&?P&75NpIM#P* zBk2BTJjNa$;?Q2$YbE70TsC#yEn32wL!NdE@tnQgUXqF8@itFi=xveg^+xXTP0)0W zjw}HoJBNx100(b*PF*-E;{7|-y?ZNg;o4h#V6+PFEXD9EbRe@ugEGnxV)6YbNAcBM zaKu~ku`-K+;UOX#p9c4Jl!M#S<(yr;#?*7K5$vLcfq(KhAMn43C)X+{3n0D)JO#}K z%2uq_*M$}m$LeZ{2>^-zQ0~1CC#yCKHxUA=a+l`cWuMnGF4h1b6N=hO|fhfN8+&m zuP(yUsymN+m%d(bl(4=9QV(hXt+>^yu>ccwEZb9Zp)${1P}v;+iP893ui(;mJ;h?V z=1T5y_`Mv3r^l5gDe6Tg5LkCO$V_m<6{S^r&@Nv9!@7Mm*_ggNhsCl}!c3=@7PDPn zfZxGDj31Bq*_UkZV+M4w2K5J=&|nQ0ab@7yoHw82X#2FXo=5rn6H{@jPe&%ycLb+# zC&6zB7U{YXWorg7fT__tvz)V!7G0!3Z1IZ++DRHSX`_8DU-N z?;Bu%xTWH5UeGH{6$(zQ&b3lb&U`rhfJcbDvM!q!4LDE;g+#< zjDZd-VyyPhGWl@|bF<9_i8`om;MbC-V}?Ees;f3i>(C$Eq27{8yNt zm|ZMDD96KU02PYgFsvV?$YK^$Ww-uo_U?#B+<725mV(GQK7luDz9`Ch)?$IS28N1} z)w}Ccw@@CT=J+M(IE=%-XPoC>CL%xX<(Oyc+k*=x*^>Z{E}1&wuO?v6=tiC*vq`aD z7px7>4p(#vheI1|SO}=3jHSMR_$Qi&dYTaihCw!jJ$_sIK&$z&09qmDA#V%LSh!^a zBDt~&*`EnpAm!*f$3Ze?QED{z3&A}T%aJQ$*3W&(xTrX(IG<>|XxT2zV5x9R=QJ2v z-JR0NaxJ{{80!kO2FX*rpK^K?m;;!x3hT2Jpg8(T_v=q) z%SPky==)|j(%A9c5!=AG4@XK{Lit-9KlEI!zTYrQ;^CY07|&YR!gE^JI2$nf8NP0= zyrb4CI{!+~0gNPFZIT8yF9PgzntQXLSn!GdYOtr1ouT5tIZOWy5Lm60p%pEUQ3<1b zVF8Qcx~^SXbPgqgbu(H{>({}KQNui@SIz-DLU#4C2K)t6qNfgGiKuXZBSVqCA7#dh zL8@t)|1U=+;u{Va8DExn^PG3P=`RJwTQ1Kll>3`AjbFw2jA1@CE(+djKSN(Ib)!(n z4!jr4`^aBpgW+@0{iWw}mJu*+s_1^L$lI20_9VW}R2L~+)j;uPueTT#zOCQ>+UvIF z!jde*&IyVifSpmbk^|ZE`}Y+WkENFUyVK_LQ=DFO?!FTku~`ZL0F!38d5pk5(&XS>Q z8sR}{#U9AJPC9NDQ0Kr)hMk_#5Bn-nJ$Z*pS$<$0Q*(Num2J*~Yg#Q0b#(q@0Y=~b zX5I*-QEHh`5LCdduD@ zX^jtpmILoOlN)+1?hR!fzAPaMJK)Sp^=I;sbyo*Kl&z}Q&zVm~=IpRw>t~L3ZsV{>fk8`fjDL%!nO@-=FRejMYP2G` zCzh_}#jnZvBRKLCJ=dt^OJrd#AODHop~_$XrhmgOd|-{L*KxQ#Gsy=0X}5olpmRZzYH zYBcRGm_$4;m$D$E%8a#+)hAoyVH@c`7%D->`9BG{92^3eNaHYEU(fRwe=WAW6Us7P za)xQNN=68sOZH#yk+i@BgHcTRC{J04An(|AP2+7q0NH%ix_kJ%b^>Z&I2NPgQa zzCZ;tH%f#eG5hZ)d$N(eTrOd7k?K+8}oE6h*fzAo)@s%blAHSVBD6LfcsDh z317tW zvMJ@igf1&2B+`dmJwc$Lnd!J73?LQXS^E3tcRLlaCbh?+2bpe-sUtS_(K&_843oUi zMM@U}(mqZoL>;;Y61ns#g!jkz4}jm?PFo?*3t=Cj`!bB|_MQNcpwXHB)IJ08_CY>I zB0^85q9^Oq6J)q?PhGHHpXt1^_b}a9P%Ap#y0wHUp#jjyz@cLciiAa3wm1-ZfwK_8A?S(AWRX1@O;AcbMAqg7$nz zk3f%^p7&`~&P}$nyRq+WgI3JGm9+&UP|5}}S@dii_n^kpC|@7775uW}r>A^hRlC#q zSj#ev7G9Z6}TFREdW97;q01N?=p^>Me@ zTW5l`9!_62n4Rl=134*>~Ea-cpcnnD`*J*V`weLq=t?9m<10rV?%7<8ZCR3Ch2a zyjJD~GG$MdW5cIeo`$$u^0=$Y3~G)Z@s|>2{;!m=wK-_`nKYYX`Qn?IAJ0d4a$U&g zjMVZ?u5KW!cX%j8D)zu8TMfBTY!Vh1S$2m43Bo-4O4nMWzKf zAan=?+PN}$`caaPgTPE%ZXEOna~&R!&30;0b;gTNMvR%aYGswYQCBat<&+D*laz8E z;^i(G3n=Hcw9T3}ZL#e6*irsM=%ccMvhB$|zX4Id#re{6X|;#!ZN zaeyovq~Wm>g+K9-0Izu@h8{r^oP!y@mBw*RWV*JN+QXAe1IZH&3Q2_+IgX zj=0H))xZxerVUdA+DZ&mOdJ{_fLV&fp;gfoI%;*?xUP9kj83oZwZ^MwEK}I>|PI z43`LGdoO?B>0J>|EMt}Rynit6BP_l_D=-KyH2cO-`NG?j3fgSUeHO>}$2W!yNHyMO z8A+L2r$D(Nc}=4<4E+#ywmz9{I?(dGpUgw$kj*7Pt4d7ZQ+3*lp{N!QvhkmwTx)yt z=M;DKBtSAC$QqeFT1BnoYmb5O7P1UGtjN;Koc>Rc@86faWxK?l>tny-E$%KgS{Z{l zr~Y4Z-Jcb+{8fm@E|X=h7aJa!iOT{uC2X?V)R~HUrK*$5m~CEzzZ0m?cTNj832Ta( z&7}NMKqwH9;c@r8LF9 z28DvaUj~@rhyK4-ui2Et*4UV`f1hP_@AhVQ)vtX}TzNzZIay5rxrDW4nIX0|{HdGv zk7Uy_+M>*Gnqp9qUzP_REQ9|dTK!iLk$R_Dg{_I;zL1K1Zp-!;B=X?U3yRfHspqfE zT-((>n7q(+`HA^7;gZBOu!dat=FWfPFg8j`G?l0^lwib&)G+TXQS$)z?gIho2QfV{&23KwUmqlVNoz`EQ#Q#0R&hi z39!r#DcK9Vzs#pkXM63JgCzv`)+6yDyl(9KFI_YkLR-^#_o?VCoc9fZ8R{FJ3UkF= zAZYuD+qS54mxH&^ zeQT3S|HPDt7?tiuMOq#=@{aQ-XCe_&vY$%ii)GgiB68ibv<0)>*0IsGDPH^SaaHn4 z0Vf`ITeNtKo(=fOeW$4(B|N>IJ)AR+h5i2zFKjF!PKz3yxjwbL7#TE}<$*kxv0&+L zZ(3TWaJIj0mRjpnN50hozPQ;A=cYM-1wEuYWl^4!=lXaD^A zR@fkzj|XOR&CbSY67Z|)K=j}O?c5J)kOEWDKGsY-`&s1AQ9Hegy|v$^l1j#JC9rKC zw=6ecLg}B3+5bmwQ-b7DPiJiy4j-*W`1&Gcpsi!@xDN%M+}QDfoMqJhswp0~m_-qU zI40#!qrg85gA4nm8LXBbKV*6uVsrZR^!39Gb2Mx(tZcFX2cki$DH^uuR};sn3|l8X z`nZ;cy?K|wW3vuBouHLHN7UruXb*$9Pr5Uj4>@h0cPbutMIoHb#L;ia_<~yDtv^N& zx@@O^7!_t+qDH>eaogHZadL2W9^TFfW zSFV|mOa)W>rfHtMR-ez`8I+;_fFzf7b!IO)q`Jz{fj6gFQR0c*O47{4UVlxEnO3po z$wJh1Xy;npHA-^M+aG{j0drkOepxXAtCK{PZV93QyZG;Z2_BE>DC}aCmcbbtu#j^K zUsPci2$9-(uvrm_h+NmnqSkaT+b5@@CwHH=FwB3k`(10x3%Y+P4Nl!NUb1by41ex# zrI?~o*oA{NI=``7ByOlD;y%(9YG`T6XGsY1d8n~Zr4jtzA9))IMy`~|gTvl({Xh0x zMtAXFZ`KZq8@eDiLF& zt19;}td2>>u@ig6Tw=^}z0W%8EkO=kR&&p?qHb#_5%$=iVG?r~Dq`mF=Z8--na%-voY4jsn}N@-$2XSdNJtz#?ty1E(;q<1-`q#59p{^DyBsS=%Fqt9|ffEZOT3~rvrTka34+t=pV}seM zt|l}gOkFlBMzsX@@1A>?HRUlDpC_YQ_svU@UePy1?^KA*=c?y5LH0L4;YQ&^Sd@(a z^gt1xLqN*1L^8L&H_;FgoFGIPx0Xzh20>Zi+J5cc)JjCB+^i|Qqn9J5Ow=?;?FZBb z|F1uhrl-iI=rcTSUKaoCMqbA1cz!+Y#C9CT!7N2s$2%hf2FsblOyN|GE`+ppkpvf3 zEA7DYLL2swFjv+>)2wr2c-{X3-H*!jWGL1&PSzx(M*dbe*`Sd5Twra^Xmw+CHQ6#t zHegO|t^d3bk-_9XLZ}5(sWB_+4zD{|tvAZI)w_gqGWJL6yZ2gFt3*SVZ{8Fl_k<24 zN!w?i9_xR>HbY==FzdtbpCXrMGc&$U;x&q3aC93-xWSwttOTWgm_ zKSl~ror)H{`ltAoK}_^l!;&U7 z(>sqO|5}EmgQOC3omF9%B*08%dha(Wb(uC)RZWzdDE=IiMhELmP8(UnGvJa!O8qbq z69;bQO^vJ)W zfk=}{@Fn<;nX>3n{s%#(GM6_tOh*QVfW0`b&mAq!%WZ8>aSH!j{!z;l!yr7vNRhvq zPir)N)WOiu_36mL0m<|n)B1ks@4rhDvwK6nCIA-a-;YTyV@ZAwjiyF2gimDIiCDJR zJ+qkHQt#M4^JdpEV4!ZRFT<`uaan|bPoDKkm;jg{Re~a7+5z=)qrpfx<6umIo<3d{ z2^>kmq8c9{-SpGro-o3RKRoZwVKlN{`)&uykK)WCY*nsyOA|%lc}3!7<~15I$)INdNEKwC5Vj4gg4$J0{hzk z)dw&HG2P0{@XnKqu%nH$)nsrWg28fRz7X}3U5exZ+^c~JiD>jfvQ!C~H^28XGpr%r zBC^iMO(S@#u?4A#2>3E2g?9_~VoK++JZ2O7I;!U{8f?iIb2#lE+Gwp##V(G1(?0u^ zZX%Vyk(mT{sh==V$$xIJ#3x@##eFNGLXBs3@>=L@9WxeT7o z1tc^NbW}!J0AM&8tO)~egcClc4;Zqpj|{AA$}(&n^^+XQ49)QqxmFwrsnD&yXAyfY z*!`vfBVtANCeQn`2S*$B0%goohAp#e7ABfdzfQx<8>%dvs;N4cuWy2ti~KC&3F|3AFpz=HuBKjH22V~74j){6$ky_>DsN#QwntCcqx z6pGoRpk5h>Xc215muEVpCngZfO10B4Q}+pZDV_y~CioyP@|Sl7XkCKNCY+LMu@&ji z7deRkwEVqyBQwxzTa3t?Q1CBIyFA+xmiWBG4|j{J#_o(WA{wCIkJDVf`gQ zQ~x>Z5aXk?GasY4lfd8cDWrn833p#;Bqp&4=a(5G!53?o5w#Hmw-O1TV&8uh`xs(& z*74;^s5$6Sw|~#9i2RyHihXpc;+E0UZLhFzR-=aTe#pg1j7y_wNiEl@xy zT0=rTo|3ZA(_=tXryqr7fNf{mF@E3cVH(sF z*DpCotsQz4TcdRuyAx^Lk!hInoeLFEcfNNiJ8}r3EK&JJUyPJ*Sb=FSKI)~cFir(g zNqKpVf6tIISMD}r8tPa0bnXo6QSgor&%oP5+XKH4@LE1@e$+>H-0r3s8;#BZp(J-= zX2-pIKjlb}PC>LB>PHKqnG7YaF>};UscyHa8T`UcH^ZMlW%-Kr5U!y&2%t)s4^Fz^ zE}M2(JtAho;MSnl3_KoO}j;Udkf$K7VX@znO%c0JDHuzsD;a{|S%mta9E*Nx+Vh>Esv zNUUF)A6noN-!qhTu(C!u5RSyRDn-xuMNRgYQ2$)|L$+y`qR9BAYFxWfX`Rs|D=B7| zmM+A|EGOs>jSG?gr^YpDY{H-ah(U6XPH?t9=<4Yhhxe=EY9LmQzTK}irppnO_JBo> zdxL#%l2Pk+JMO1Pt?$2k{&dU5x_El9rSP5N4|Te|7t#!mt*OEc(oZ0QvIe1Q@Bh(t-=SQdNT1WPJL(>H07Kw!uIb0;=GwzN?@EY92fVG zi2i5G3mgT)>x|4Or%k+eH~!@Hb@De>`qL*cjztuE3Ogz~<391G=$k5>Et@p(i-O85 zwr$u4-aENFe$S|U+c!2bf+6Ya{W|s^3H#g7RnX;#-YvnY8+%r|?AjnHb(`CETv!;l zPj3lJQRl$!-=+RXXZ7;+ys;~FdeFAc6r&#sf(mM|Pb@-D?gM!s9^Zy55Kr)c(h1Mq&bX^4y9L<&CE?MF`3MTEERNDyFK zfA>7W|Eb8xKjOrAPKE})O`!RT<^IqZJQC|dVNgr8h5g_T(xXRsbUNmXQjOEt4+%7? z#C_kp_EAqvu|5Yd-5t+33%-1prk|q0WMYZnq~@b4kMAm-W-yG3UBQ@=p4yBMhc$@= zvDZ%T)4CKI*ap{5<9Q{_V2=@(=P;u7wV8$8FFCE7JF@%Aee;YHM6gIS^z2^r5)mQk zsR=*r{Mo1swn}o0n;^*|La~J{{ATZt$@H;TqUPC@v(F9nv~%Mo)5)fgkt`DvaOPhw zc|ZBGakIVu&$aV^Zh~b2S6nP-eMa1=!;*&*Tt=w$pP)8HeGOR@^szHGD=JIvLp450 zn{D6Zm1J(Jo^!;D+>bfH{fjiRghBa#CEs?9nWA4&`R3{xo>P8V<9Vs`qA7{3>`w8+ z^o`6H4!WwFJJYk2&n6UGK@FRj;7C<18jtTrx~Z$x$W*L%7-6N zN4DXFb9$Si97aOkltQ+~4UL%>YOaqLN84koiesZ`#1zewte)~hjSwrLcP^n;m^9$AU^wD~`!HH@t?hH#9Cdq_iMUqO{hW8%cAX#=?TDCi-vTagvF5>{|}r zSSaeaaIs=&NU|n{RBES#PLVNXnMDwThIjQ38dYCmtEE6V#I2Fpor7D)NQh3ywx8$s zUD<-eJA3WWz<4R`Q4OXLY@kYlolD&(5E*f;Xe#w`p4NPWsg$!dt=Y<-(Z*_I;5X3c9#qK<y&0LkbTOpNflfcml@(kZed^J zNM1BgwwbtN?+YFH`5zH08%P~)I-$q~O-Bu6$ReiW|M?(slBQn8Ev=SkZ7w(^S}$t53F!5$w} zYD3frVqz6>8nS-S2)Fha<0~-G8bi5uLf^!~1o^jiL=5|oqrEAU-vr)Z87u9i$4g_1 zVm(K4-WW_c`#j9?^UKw*$S|#<##yC!j2@%sBpci{dRhui_(m_=Kl>8M)@GmGK;b|n zv)}z!RZ+SuGjNFO;AODkRVnKso{+I&(MuaJ`lAO&{jkiC0M*d-i0=@ytm-plNOk}+ z$j&Jzo=l6$q*?hPgpo5NHW^xh!Qe%g@WKs6Al=y75`nZO`4yGvxsY5+9WGXXxohdp z{s5F!K!1XNTj5yP zudfWj)rF>S*hEgq7jBFMK%=!dwR*d!3T?&Yk&((W*{|9&yb^@RLhTFE8Bj>16_1|zw^b*y_cUtpgLNJxA}jBE27ZLJVP6Y*C&b=XXfjDU z5yj0dbd?=l<%UrK-JmqiY%v${`Fu&sv0fXm5!2g+6g;Dqog8x=4O5KCW@Ii6x4(YG z!IYzT&6!N>7U~yf<;bXVly>85luRxtuuNQ;abn&Zdr-O31pjhD#O+aCPg036FF5>B z8jG!|yOsy1TuHFL&HO296k@Cl!iDoGQ*L1rZ>BArDQf>Nc><<#?P7iyS^A~?87;T| z)Z{Z=5z~i`Q&KV#K!RM`&)_5!cqy>;)!%03vT*BmRqS)A$uq$%nQ5(X-onr_AcEl8 z=%tkxIcJ8MK&nQ`7XxxafWL7jferXXK~y6yQMai>w^f7uShbA=5dxZ;lc6{jGWz@o zOE+J?K2)~|#rZ*A88$QPK{=?&zjfWlN;=bex$~Xh^{H5ACuU)wYRzn%{${f~Zxi&M z^;5g#{9#lWXOEtZ!sPQq z5QZ~Q^`}e4yKB?Q48dQsd5ltH?r-g53MoE_F>Zm+d~xVNkonu@gwHCy!UD>-`s?qh z-(Kw8TpW*Bh&3g*<8bJ~ZWW1w5(;RiOlr?dAYi+!B^aRX-3QOgbfwd!~aZ$v+t zEf#EDOLOuL)7(mE6Be9#L<0|FBsrl2%By~pDj=U5r270dYr|nqZ=cn)!b<^^i`eN_ zCL2KVS}016=%yS@^lawDWU}kFIJk+0XDL~UYcAe;kkgK~pJx5}J#0Fm1>C#ZB;hhW+_i(t-@ z>Yo0rz2Le{rqP;Dj-?8{aB*+6QI*R-ULEp{knFm@Wsv==sOy`Tntu zYy%--!jtw^Dj@9R<}(8`i|9`pMxeKB^ef$?|Hn%B=S6D9EGX4ipY8XvE))XrARE2F z$_VJ1x7l1yaFY*QF=bk1D)G30JK{{z?Dej_+-|%kgCH?dzhlF=4;6#N81FP6Z;bBm zRr7-Uhha^RQ~5nR+2TKknH7G^2*)vn3`D!+`5Z=MtF92CD@Vcogn%)S89kG>*W`ui zf-H1`5Z!A9FQc^LoGaJH5{k`&&*pY?f&!pkZ}GnFbJk zPGp2uF=VOc5DR|O4^jp_qbw^r4_vtb<_zoKtLtPOV4fdJPF4w!f|5a3M~GnCqLTab5z zOhj};7t2mdhhS;h9_p26?{R;vuF`bE1zShV!g{k*Ge+k)gCQJ3nbr#dy+xd0(R=xK zuo=$;DOC-KVnXapBEt29?q&9r_0Iso*IKmXHpn`u+N!z5*Za1ZSm|BvI5V5Tn~6YI z)gTeR@MPiUCuY)d(F|KNdCR2rqenZUxh!v$Q%)fBt4HQgg-Esbxxia671-jn6CIvj z=x=PykPJKm*Y|j1qw|Tb^2q2gL5A1&c$ivT`sLErIrF$RregN`8XyY(Evgq+=n;`z zjL*3}fmn_n)>Gz}Fn1A7?!y17hm%+CVqcf3(|Giiul;?TG<&$mhEFSV z=lTKO>8PX(zcl7!{NhHz$~`cerU}L~pxXc-Z={ugRH z-Fbqodn$`i+H~T>MCZWk;2l2-dhBtG@G*4lk2(PBJi=pjyhEBT#L;&6%!>j#v#sd%2W=<1LRm z+bjQAuua0;v)s_Cf#v2YfS1jfr52DC8%hr%9w7%!tehR5+*C2Qa?*uy!?uhDk}LIqzA0JncKHR4rF5>DC^Plgv$x(s=Ci7l!7Bs zfx$Fvoko7tr|dGU`!xi7>!>>Iwx=_OGtaUT5uxL6J*b;r^1}hIob^BXcbyg>VZoO4 z!^x~eZ#E_bMS~p{GBN#)qHyW**IKgnX0+M)Aa}+3%I{Ox&c#%J^CcoOM-`s~Gs8{> z_$+fz_ZzZ33QdKFJ-qSLUB%_{B-{wnG53^JCAJW4oE>Oq$xCMDJ!w}1)vUy1vVW;l z&cqTCXvV}jg5P;rXx`_RchCWeafS&5YgA?|HuDzF(JkLUWs{cHo@Xzt`?(`v**gS4 zq0j&InW&BN8jRfnC;sZfKWeI?=3R`qW6;B~n|)x!S*FFjpH!P~G{zmt`_ z6-mC#&a}zuS1Bn*inC+yRW!mGEC<-biXm_DUWI6|?+Ttrs~md<;ZtA`2KiZ-e$yPm z4SsxLcYW*&M;SH;U0sTd17TLr3D`jH{V@YES+N2|FX*Kwo8Xyv|BO*UUJMA zdYF(4(I|2BPQuRfjr$;0He;^YQ{*&-btw$gGqh3@55G(g%Z@!KidBcB@XK8T_bS*= zz_Z`g4umhRA6`Z)fOP}?^%c{@rw#R)j=ZT!3+J*F4(8&9N5Sm_vp9vsQfb35fuBL| z5+sY@1Lq;Trlf^_!A1KVHnkh^exp`h*OB+iB^}e5D^NOg^hnH%Xm)?YTf3bl>-a%{Vi`90_9x zWW`N+^0I!RKrRYBU*RP?fg8c_CrL?cOu|U`PZUP~e>{1r9L$3h@KfSoRVr5A9q~CW@}_|?|M*AMUz*)s ztLdn3e-s{C!t&4wYmDw0{A13&Uc5h-CdI4*5$1P6aj=S^KQ0?nRA4}H5n-3#<5 zt5Bw>Pr>t2Gg$C!g!oaSOde2(#k!`;sk91bm0VK()0*SW!O*p_Vu&AE_1kd*hKk0F zzxmj(thuxHJI4$k1cL|*q(YnL%~$a)qMDbdu91LQGZz~qmP?!+SOvZ+KI^a=H(4sX zG5J=@&9;i003+e^(QcCPAyGs z2uR)uiJH!GEsHV$P!D>I5UT2BUfgAX(sSUij?gL&a^>wd@wK4@|XiOeWZBdqBQq*G6^E%t5d) za$uf<4eMG6q1+sy?|}A-U*}4k{aFda8`ON4e5aM|LG|%ZJ=x8^V8iQqd#(o(mrOfF zR#JTTTIFcL;>}z^)@^b2lV0>CfITZgSx6LXScW5zi7lVqU1pyrQQ>Z(KZ{vM8ux zmN=#1kM+*REHX#p`q5RhVpNXIl&f9|c7J;>43(;7T&kN*D3DXfPZsTx%0@Bsk?TCB zh|bX?Vyw8qJ+8fw{-7ky)3Kw9FN{Y@?P@$GW>sg_^Xh{on++*3)FJ#}1SK#P*jC6^ zrFUbYeRCGbkrPC~2OWyHd4g0~=EF+ks2*KXKB|EpfyB1XgMJUKm2&^UM*mTe#y7{M zCCXVo2qNDD8U{dEsC+(|as`!^{}Py9G8ff;b_*_$C?15a+BYO$$lh-Ehx;6sG!Izt zx+rq3+ofG}ABnoTRINrv4SVahUU=)Mj$jaew@DA|`0o7N(Jz6?$FXg%UU!uEI|)19 z#Sg)?g#K*a<7u)nm;qk_3UI*XPjC6_U}90y%RU))+YGJ6)fh3?Q5hdcLc86b(1*6g zXF^28vBUA1%18ms-Xtq6DMlX_$v0Us#{UUvNb8GORhMybn6>-ba~zvQW`(j6)mh_rHb`M@0`h zIQRuDBd7)5db}*vxzpkAEbUXU-0c~tRn=y?{%M|-iReXql^;p+UM1wzovI}}sy^fi z>I2o9WmagFulIZEND8N)Kv{EcVYee;Mx?PJps{r9(Rr5qy#f+OQevFj#ih};Famc1 zeH_QS=Xfd+-(GyKeH(h|CNnx+mnE=jDfh>-^=R4E*~tEIWuIAoYFPt8eSd9VCH{hJ zhPZ$HSBI`r!{#f`*q4rqM~@=8WvoWqe8$ooCSBVh_a`j|$C$LY6HS(KSrGA^FxAh2 zg?`5i9xKiEf{B~>#%CDediMi0_S{`U(8(+z^3*p9 zFk4XF^wQ>YppySIKy&a- z)PL@COGH#(k_B@>|5jLl$>%E5eA1zsG#!n$`kXMIjV}W zYo}joy=o$KS+q>*Q0pRoHyff$|&?^XEC%&t-HqNCmBd(=u) z!wn#iO_JGIX-b9zJkZo8!;aqhF5sbY4+OTuvz6)CMd8$*)HEQz>K%u_@X`TOCfN<} zLC9(4By74;sr;3)9LIt+s*v(?vIk^GXFgcFRyGRYUNR^VSHn$ErltoK&k?(IC(!M> zOD;uW?a}{H5UE+Ss38f+myfEe2gy67hpsDo@9Rh9hw==n__v@bglP(3WslBE)aa4- z9wLaoKDL#BifvkyJ(u|HmhzE0(8KAA57?Xq(E#cYra&_y&M+Z;meH%g2fs6&p?(PIiL;&9gD-5+MhI?$`3-NPS8<96#H zTI2fs-^KiXW1k+b)DxH*MSN3^`1zB(hhV~Z;{=SV? zK0u-CD5{S4LXVlOeVhm_`ndrB-5M@CV?kvKBPQCD9o%vXwKttLzv3apfADA*O{Ai8 zlFXhG9w3vQWX|Sg=#3v7iTCBtzrre*grV9~GC(PgrI+Oj>T^*vjeU6mp12oZ56 z9?pJXMQMWW561*DRN?M5qA12R6ax7eath^4o%U<7l<=yvhmeqYb)4eI*6ZwSJb*cx z564%yFqoZcUPnDP)SYB)kKrwPsXzGUeRTN!iQogzUt*NY+iQ9&5-nPdNB(M&3fdYn zqd7RZ!Jr;Cit|P4jY-`D>r4rrP|ksEn2)^h2ml$Hwf1%9o!V zJ$Ps)yRynuj|=;1Z&p3$5ZL{vpBAZ=9>PAzjynSEhF}4iYHz>Ij?uvZnlkM1VN!pW z9Ab}`z&ZwR=>Y-qvRT1~$Vd#7lywWg)qSs^s#_c;?=}6QY?upW*(a^V{NHSA!v{xzd6c-o=!H}Dbb^7_eBFR^ zdLklh{>xkm!XO5al4f0&ddgb-MyWyiWKjx&y0DkQtgPs2o!EG>T^MV^f(B=5=SvT8 z&83-N3X@cSFP6$M<%z#%F#o>wv}iAUuc!D|utT00$;MGR7s{Q^BmbEmXoXj)@rrk{ zA54hj*!ZKE&Z}XlWX9q~i*;$v>P7X~O3{@wS3k@7idnNNNHlTz-f@VVk@l1I&v*p8 z+fmQtTI{D(08Net!{p2JX0l>%KZK=Tj}XWkB5KyVtl3Kr+Tx~g~c|lZ>j&nFTPr| zsrFy0T83ae3W^ZrD7~A$=K^1KK_Cp^wxu}zzO;Cx6C0{>s*-S&Or(7JZDi^lo*$!S z-Ki^>%IBv>(|c46jzixUE<)t6r_Y)NIH`0i(;^yrCkRUa;`yl`h8pYs7S1F`P_c;r+Q7^qPwbR?Hwcm$Ss zf*4A}7LbCx`{x;&9?wW%Xz0wJMU1B`?m~_aIq%bVi`#zMB!=_$dkQ+>yY#~NCt%zh z1I)eg$94xzOC33#I+>k@*iw2_>MeA<THI-n{z z+a%X50VdB?+VCG-E^_Fi9k1o9ZAE69H~hd@RC9RqUE+YyrLS*D6r>;mbsqi45-4n8 zEf>1?C0(j28AIfQ>|omH&P$@`gg^BcbZ#b!|Km#}G}m^8Y)_m#>acb|r<4eQQ`$^| z{`5km89SKosnGW23P}(9fDF_AB!6l?P8aIy&#l6o=&J{?sl^6KTzMCN%FL`Z^hjpc z>GGaD@s6PWnzhme3mYfMYppK zlpE9BK^%J$u^C&jHW1_FXZ~`sUpFB^g(}mVUwps5I=a7ZG`g}7R#*{kY51)mb9pr>rt5c8@`BV4H_Cu zBE#id&jGd3ShU=#QO=44;=;;W_{zU0Huadd_OKLY>ql9?h$I0XjE&y{_9~%^k~CZ& zcWJ7(q+^4$=_^Yisqyr6H7vk48KZ$H?XL)cewa9G?U z0WpGE-?l#o=KA536n!A6ktlp*V3m(*5QwwVw4dIUQuFMeTJF>rAzG7m0DoDwluxW> z__Hu>iLAm7C?0WTlqE>f&8bu5!nVJi6R%xwDnV`=u-?>4>JFjIf;e;1H1d%7<;;8J zrWvYzip#q`0YP=H3D5ra&nL%)Oi&WfRLu8Rbvv&*-9q(HB`VQJI{>@i%OchMMR=$i zFsG$v@#X$BecVZ2bf-l|NI=Cq_*aA`777Ar{A2g3nkV{&VaWY0DxWz;Z;q&odP_KP z(hu{i-+cDN)tU3U>eJ$%BN#w1ec!vX23cnvWcJjL85<-o2xgiepUJSc0Zj#wss;Mp zS{E^$V2Kdn5}b#JK(Lsw3lN(mmb?$esQN~meT&QAWyUjxg80X%5TumE*}?|Tw2U`Gy})sSHk+1>vkp@Q-bH<^REl~VYoka-CoK?zd3GvF5n+FWDEXkZj!A* z`xRD^V~1f2#{T?|4dO$SIb#EnWnibK`S3M{0qxTEKad7m?)5N zgAcY%H0d`9i?sk_f*R3*1AIlJESUV3sG6eml0xk_mP{qQgKq@Es0(|31wr=FXj*|r zUY>ZfjCGNQdvUe-OX_dPX~@Ez(iN(82*Z!0)q;Lul@K*1txBA)=P;fr4=Od4|M7TU zth|E%WOQmOx1km?A&=SEboI1#%mA3y1K4W*u%-~IW#s`yS(TI6fTtwn~gyAoJr9E%Q3P zFi)t1rL{u0HhhT2JR#m87^*|7 zVId)#Ae-v@gF`8|sE8hdSK zTgs$pAvGhSYI~ffkf4}fz zk0`^J#2_rG!&qb^xuziM87;Dmm1D8fh7=lf5kicePSxDcXeT^n*A8_B4HbQ_)a~2| z+(gEn-OsCT*priK+4?H0inIGud>}H8%+{!aQh2)`jlrz`@NT_7;%Ki5$$D^Lt0YX( zDsqW}M@3rl`S(hSroUWZh&y>6A>mS`&*wM4h_i}*YPOUW(d=`G@(b(#DCn?Ze1D^9MP2|8H(lPLdAwbLbq zKOpP>jz%07(V1v!Sk)LNVQ_@!JUpo(5wAF-o)}|h)b#f9UHr7I^*@>_Xw=!|mh4|0 zkC5$!+4XdslT-$DLL6!gYaPNrZojqpku94Q=Her1X_Nt;yQ2-6PJCJ%QH}NhC#PK> z_3m%SIZtM{ni7p!HJ>dXK4OfuG!H*dQ~uQ|+YhP>J{6hgROcD&Z5CX>J%}TropUSt zp-2!rXW8v+wlb<+VYAreqy`xyP~F#a_McAZecdAb&Z8# z+RzC8oJ_m%mX)86-qt1)^M70tedHgdH*u4qZ$}-uE7kSpUGONox$)b})(|qecFkj7 z-KYu^ReeQ{cEiu{gSy&te`@>+b{Opr0tx(H+-)qzYgkh7clp6%Hp`D$qIzY9kxSoz zDgWI>;%s!27*&=~+5Eaffarx3Yn#8e-PH$u^Dx?*nJX~aM=)Ai*&Ipb>;I3ZukdR+ z;JR0&HfpfZIT}Q|yFt1^r9nbex*IkaA)V4Nx>Jynl8^>zNkO_he|z5dd4K=FKHK-R z@4fe&d+s@($dxQ6RQcRxBhtk-sp*LXi{<`^{ds+7%t@ck?ZfC>*i9`9)HZxY03H)n#sY&Z_GCVse>Q zZ>4>0O0?&EQ9n=KMeFHQ+BnKoQtaIq@$29Hk?1Y%L^$z!}49c zX4YGG2$WOS$g{+d&vR}Hk4A^tAu_00xB=gC&P$tl+8+6f zH?LX1y~YAisw74xpw2FTOkzRFLG?L|V5U+rtRZ6m%9w(S89=cTudB_ydzOPsFp|$t z_APeEB>C`N$HxYoy{xTV!`YkDG$$(aUN@`+0N#-x3v;Gvy{WIl{D6PMd`5eyUh_+m zp?z2EwYXKKOR;7)Yi=#Aq4W2czu-;ccJ&9loP9o4)UD@1m4bL_9wm|9w}@t()w6@P;@0WS#eW6a`t zJ+P8wyU4xeE$%#0XQZKcD0mniTZLk;O}MUi55BT@4ZFZuk$0%Be^)+>s%SYjMIL%V zxQ2a9Bq_^3@^HyRtc@1FR(WBTR>!{RXX<*T&Ha{4t*9%WHUX2bgQb^WZmEb-AT@eB z2*e4nX~XpR?A&e3ohPAoc)82^CLi?qadVwU2LVX8S?B}!wrOI#p+O>Wh3Cm|w0knn zXCn|K?kQp7txxX4Af`^%5z1XZi#UGaM9jh;CgKwHC5iQRhQ6kPjMxBX;}8IzuxSpU z{Sao&+#3UhlX*y{U;Jd}AZE6-SUZ+AECY8#)9tXZFW-5^P&M-a5C1ZMz5bll$i%7| z`U{57d7&3rxWv!@R^I`oAO5fC&h@|@(`!n;yH#q07=Y1=fIY-?4RK*aMZhd~Ho9n= z5G=5$qmtf2bZQB?HVp*E>4=xmV7)D`95+((hRW5#chZrcVIq*G4g=X(jxcDR2{C8zB(OZb_il zX$GPOjhhFFi&%M=I{ z1Ik67%O>tO=``L(sS5GtZ5SBcUC6p#5;8;lbw90;l6+yAu9PD!<>o@h8Ev<^b2bBV z`s`gEX%8$w%_3NqBe`8m9PfYE4GibY*y8?TTgbZ`Peeq|>|n=XoxFoKv{-KGF`rw? zQsz1i!z`lAGM*3C+O+GlC$TXBQ&WHrK5<`bYCQGd)jAPMO$I%$NdFNJIvkt zc#G`~i&**b+zbOMJj6kY2(5W4sV1MrSB_T&mR`eiZ#a$y9>uIJRD(cluDgj zgGY|M7L}FaNJ8KEEF&gw1fz6+z_gc&3Yf9IbOUCPgSnV!uOgJpvMej^28Iw9V0FZW4b(W-LRQ)My79eNd4!EUPbBFpZI` zHEr~H(S*ls@oGg|JO;oM0bmLNfL$9)M%m(NkYZNQ!`d3*Z|MBIu?vjSkbL5wopaR$ zbpyqjXsjrwo!Hj@(*pSEY<&})Qg<`0d$#!+XTm(7CF<{ZSMWhzI)`cYRd&Tsn4?k{ zFSLkn?SPxaq8r5vrPaq;evsWg`Dd$zKZ#|r#?e*d7DUI?p4G+muSzZG2b1JdMO26` ztjj$Vv(|CRTr;#@H;fa7*ZWQVS9!`o3S{7+fuI>%*`34cQdM8Yn~&u_kbqPZY7;_Qz`mxribQB&ixHEhGQ5Aexy zgb60~CEFF)1=PTNff~Q8_u_}A<#9&h9W_t!T`FOOQ!|mMxD|Dp;cb`q-cFm_U0ifY zk5O}Fzwfy70D}WCjPzr{6MKGeetqXCg?UEl$QT>o9aK2?l zz|NKdzfnGByHKa7$57_;i5YegG+AB?chs3A^#opH*z4}}8fj(d+ejfH%HgL( zytTKSY9$VM=~{!h3`jSzj9U*ofJe~teF=&5A*loxtfAXSml`EOj*k+*Hl7;^7jE7UPmYnb*9y6RTuN&uEz9!-KOWe1YB@2MaN3pyq5Lbu7dL${cnjrJZ3Qw_wepOmowU3$ zIPlkpZYrL_iEWWWMty233V6KjfEG{0Z+c8sEIiEdT;8=C1AaRVkrv(URZU;GU)vzL z%G+&+i2rI^6blYMKTSjV*#g4kmK}o1-Om3k_x=j?S2e5UOWm$gA7d&IzUl7_EmD7fvgan!1^fO(M3p{>(HM0D4{??JIcaJSz!jLy`9wFSfc|al5%!{`RELQDryjZj+ zr4YY=ZMP=VH)z(Wq=_54ETu1)rB$(aXHW!|%F>!R{g^d=DwlS%)3cg?CZ-F;|oWAFtTSlr*4t zhnk^7*MnOBWF9UhS$&SjG$jL47yYN;df$igMc2MH78Fd2LT`V!ZSW^Ji>?h03S3R! zw-uPY-?L!Xp4;!)UY?J_zg2}#N)httK-+%H=E`v=?VT4JNpr(8Z04TU9ig^;lcJfT zzwbUv1+ncr+xi^>U3lskq5A4H3s_4yx7pF zH}?GDppKL3b6+jU; zQ*v0HPsciwyHyCXLwe<>Iv zf4+%zx9k*8dA4K3+CkYuVcm>H<#V_XNM)3+cCePfs5{#1Msa;2oGcw!VTdk?5KP+F zp&isz3yL4!?lmrU-Vg3YMiHfh!0E>iySi!P2KCErDvXf;?P7ZPErr~~-}=IC8PY)Q zzy$2xKV?P;>@Lt5aTTC-(91q{N7b(M*AIG3&e33*G0ybxkS&eZb7=tkarYy=aLjgb z^fmUkG%uifhpsYetgh*x4_R87B;oqPGZ9ur$$8=J2TMwtzGk}YkPW1fsbH@x6G;^_ z*<)JcUqNeUO2U(1bfcZtuy+MpuI8*j3e(5+Ee3WcKXTjzEA#PO7rDjS;j)QbgiFyb zDkkt|Wa%Wu0hS}i~Ik; zv+PgRi$=WjbIy>?Bob|rnC>xWNSDCgr;e=K3T_P%8!e7(CySMdv{=7(DboS}b4Q(W zG#Vu9#;ou}PXcFlLjgSQJ5}#K#jb(0Wk)D0@O{ipz;DlAq3bLJoZD;*$-0n?!BE{y zg=E&h_4H!zn6IR~?GX;H%x#YolWK_IFQun%>WyQ_vkGpsAdYXprbiVo@5gl~*MPRP zQU;Rv3J0`+{6T)#WXFjZDc=LF%AL&LK#W=JYEbLpp#Jat+Y2(EjxcxC{(Q{l;a@S^_pnD zdv*8xqQaZ76NidM&RQ`Z9&W$&hr&&wkbF9$Q1i(dDmnQa0qo&Yng+lLNII1msUgVwDgb6SF`ffUCCaDeh5#^KJh zF}urVSB%kkNYUJC9i83rQk%_oD8T}LiG34?Bwbkg1LeN`SsENtg9uXY*Yf$s5scz(f>XphQLwvY7YPP5wdDmt!GS+QRbn-BfPs?BR zk=+w9In`fSOP!z`ZI41H>8&v~`!Zp_S3yajN^ZKvo4-RahS`lDN4PyYcEGIyI`;pJ z`5Y8U54&n^kAL0I_U2DumMA2zLScY=TPxPdgQMy);)6TQ((tNZ6bdKR_te|dx%rm1 zTf33xNjhjlkA|aNZVX-8xZQW=vS_i@;;qma`W9J>X#F|{3FnA88jxo5GJ}o60T^Fu zL)K@1eIN`~#lRafBK5P^V*U^sUu#OR zemyP+OBFrHkkJFBOQh-ScKcA;C8` zaqIGtd&japLUWEX2~M z$0TINwanT*8&b=zx6_gk)+HKjd%jkmuC<&vl$H*vEmg|E|H~KySR>a#!1fCQDO| zemLIW*m*%NP0Q^@+<_Z9T&m=(Q_@qDJUbW>qB5^EX0ADvHm?IUP8gwjmC&JNYt)er zG_uos#B>SYiqCsVC3tmXlv@4yh?;1_qobLg>N4-FIx3{mt|o{ZfyD?hiNzdoWpsS~ zUPbDVe5>ovvyoXz5vBj)!`P%BQFOl3D;fSPt;qil5USCMHSi8O&c|H8)_$LFDsvcRF39G-+sug{EiE_Lg3ZZ&QWbTdcM-RlD z%2ghAYG~u&{cWek>FKFDm0NfFBd4>S;{7Z>_8^ z^_%^0FMEQHWuL(#@dh>ne@zfPKW;sj8GK8acXf0?9!B$i#*)Z0HD##V+^$dtK#sq2 z1ujzveQxhE!Y|x#L!17B$7Aizk!zF3y8A{An*mItnRET;?~r7Gs8kwe`8R^XCZY?z zmNVy^FYpapjV{^28+1-XzSdXdky2Fj;c<~6Y1@wl`dBiMt z*w40te>el{4<-8>EaU7gv6oBSmnE|3TmRfxRiHGs_#X5M`LsI-xb6hq^ZGI`9?<3^KtMv193dDeA$b4W%kMcpc z>QJF5JGYfihn~czd)DdL6pQ@(u`D-L7(+&{#q<!h%k)kAS;kmzG z>}v4j98=}qQ1S2Dqn5j-Lcgk-k67t)ItB3cr!z~8|25Y8qU#fh$72Q7z#*~+-o}gm zZu=9tqNcq)p$&WFnL9GO?x*G~I*ehYrc$gnEeRhqSNC$rHV%Zlz9fuhWYn(MfGOM- zO`lpXT+C=5Y>u>A3^6>xm|_f=dy~1tNSVQD%w*2odb{zj2ImisTrYl3TgfUEaGvrC zfKOEEz+pw{!RdgDmzqKjhqWO+JnoiH`oY$G03$MlcXBPLOkJOB&U>H6|7yr0h*nn59~^#OX(yMph}KHA#s0enBGTIWcor%RMZU~n zCE!S>@=!%fF5QePxX&Dz&QC#QU9)MY^)7ro3ZQv;8+-z*;mzA7(VAk!5g`+c*JGB~ z-1Paqnqi?h0z0l!SvmO?ym6roSJo7q62LYK&T{^d4u}7gxE;Xh5F}sj$vb*{DeSA( zSi#e)aJozc!p@-=2nJZqNXwwJ#LC>DSOIGOG{w+eOOyB8V{hQLX*_J1bsM zpQaCKZjNGTzNt-TFn1bYMab4!4Lg|MUW^*lUdjji&;fbF7%-08&sc4DdP~X-o=0AW{oOziqqFDm)qQwdN`+`EUAgK#BAF zLm_aYxWi!h04Z*s3sa?Z*fZpaO$d35{Ci(U`thoO19_er|I~QIM?P(>#TxSNRUUjB zU}#z;9=_mUDyI7vXQWIobMi!hAEH*XW6MxbgZ95i4PQtct5-@Km{fl|qF>)Xr22Y% z3*ZCAucV4u5rCU_C5aI1_`mO8XpxvnR zuRwnQWne5U|3_2U7n4I5kK`vwpMc6nm5UC~N^uQW6H-?a<@3hhELV;pUi^!90MP{& zXQCk#;JLj7gvE%tc{ho(rk}1Bvtl$edTX`R&Qh0r9PB?iTIkyO+Wk*!)9L!=UD`AZ zqr=fuA7c-C0QPG9#U~@V;s?3laa2$AJkJ%*d$+8OyO~C)ZQL!94f?<}KjusBJMG!< zvga?Vu4%j&rUDWR9u-@gb-qfRY-PvKwo4C&AIH9e6@b}DScRk-rYw(F4yUi0F$deW z2Do9$6Z6Jgfw)`VkuhAq3SA>F5Kz&-!wEyo z;u7XkCr$3bh}OGnJnzg}4493Xo=`DqCZAq&S8uni!O28S3;5?MS`n7~cJp2+_9=+> z{3!7LTC#|3D$#2Pwr-G9W3MOBQlU8)Hrf97A2mEMCOy=-LMH}IT0{ ziaXz6O^D>D_19YJgiObur@Q2ki+lmO-V#6eq@Ji*O8s7MtIiKtMA(Wp1QoCx3yIgD zB47hEG`>4GrHIj;6~=#k6-#@yXaEM&f3xw|&Fhx&@4jolbMaQP@Sv5LYa(@_P44V; zd1Wd!M2uQBq#MnBiSMf&mkzT@D$xuZ3+{>Xq+vU`>;c`lUlJF7O!ZXgf)#{lJF!p< zyMYg^+OWpyf?@-o!WsVa`RyO7+2TBz{aOtV)JmwO|HaATCb3SR%b3NWoh0vda;~uu z;|rGVSx%wh>{&qbdvGe1@Fr%7tg-)<5&vF`OS-oHX|ncRr{xGP634n75#mhxik#o; zcq3617Lu)V@p6hT$S?xt(rOy>^EGlJa*-uG=sz40b-yQ}R9|^> z$VJeAnRk^*fC%@F`}TlXB>f3Yw13{RRLPx4$9Lx*p8R{~amMEUbQWk}ux8yQI8`;f2*it9mxFGGw>1l54~1qqho3Nb=ex(L__UH)#i<+KpsH@E&YbwvaI|Z@oU#Qg$xqv(iVR20f)I;0yf8;| zb1EuJxSiZtM`Uv|-l;dg8VUJJ?QK3BSGw#@tZ;`Qnl*ND;nhbycLu4C_ujR$IDj zX_-AFth@kJFZESvGL^$2>+{nUv@f z%&Ei+8_GN1)tMx90bzuDsVT@TmN`Uy>%7iXeocPdMTNMRmhpu2igxE_c3{)Uh6+i& zR0zFwpYx80U8MfV0bcL*KmXlX-Nc*chn6EmG{rV+krmfn9|2~1R{lq+Hc4;p_BD$l zyL_NY*FRv^yCm zC4AGAz5nf(exX}&X3cR+69$ErvYLHBo;-LhVH@k5`wPy7sZnwi5V zeptl0+I;mQ3-%yA@~WdJbW*@xig+QUATxYMcu>$q1CiGknploxkmE1u7>7{w5~ONl zBvA7~yN70Ii=Mq!miQpo3X7BVywc{=$Z3wvkI}PZb()QS1?=lKEv@l|ZSHoZ2Ekbm zl_E>4kjMCsx`M1F!mxY4D5m2)F(!7;XN-z@>#d#Lj%idBu3TG zEp5IPb@BP;H4j>Gs^EN88TsgP^}&<0S$Tl ze9zt?bL9Mu)#?`8TXGCSQlV?f&V#bsX|NqA95t|Bb%v{9r=7-p=*{PY120ZdsJ&m! zKs*nhfVT`nZ^C}>E<%dZ!K1i-?W&sM6Aun~uMTotU|4ANU!e6iX1K(^C@{gwjS~$$ z`_(hzMab>@zxD&Ei-K_1;T#VL8g9E z(q^_5=KNuSVOhM@uJ&{yurNH_x3=a&>M7wDF|#?e?relkm;0PSPA>t^q02&Fe*X{S zxPsgBxr|v$ zBQ7}J2ZExY0|Qq?y~y%tkPk*)Z&SN5{uNn5Z2)1310cdWC>ZN!O38c+1bB%+nR&0wZem zuRZE3F8@yp@EXW0FRQ%9cr_7YawnqA#ZH|6GLtBR2AL)8deH;nkpF!w8;uiIp15|W z`}w;U*%x7#bF@lEMgyxvDe`~Nz&{q*7N-t*13h%(N15~(3k*F6snPK{WSpqfKXL^b zJrYb5YiSUOD6)z`v83vJRvHpMt31L3FdOlyRMuz@+P+7fwVf#>%i7Sck==>b2T2J& zE9er2+(~w0o%MF0DA>{FbZzYAqgQzo$KQN?%gz@Wdf2Yrwn00kT1$%zJO@`aPmB=$ z@m#kiVKp*&8zA}QDil-};9w&wpIi{+sGPTr&SUpS>A zM^>r$Isc#{l~)|+!d~m~*_!l2nCBPHCMt~X$>9b`8C+2|7m=%phJ1)}9B?9asxWp9 z>je%E+sZHoi3$tDjU|*F5Qh3D&o8iD1@lK{a;;@mE5&5qrQK3d)S;p0Bh0ps$q^w! zPcuPdCa^9I3YedFjL?}|Nqfu%KkJ8M%3>M6eo$GHQo#GYFPp5OIGZgGRi6;Kd^S%> zt$54)JJ$6mom`~eT=-O@gfgCyodwdB^L3A`7j~Fy+Hf*%YA$gBi7+)`GMCZ%`pke~ z*0Vi2b*&!88ivPo9|JI^!r$hJ_b?f^?-3}0_ctCqG_Qjbv~*W(#q9s{m1s__nj$y_kz=#v$BOd$z|mmJ8j2R>=?H8s+cbB=_A z32SFA+cz7_@eV6IZrHh|@Uwl0okbmpp5cDcMorkN3ohAvcNG9xe*%eh{q)!*P7!)He>n~ z+*9t7@==Ail=XJYdDpGi%Og5)alH7&LXFo^J z)F3cu#x}6qoC9y@7^ybQwyPdWe+?qU`{T_EMco7ac-60F#!E~I{@xl(gFlpax1GMn zb{pOfj-*1$TEC}lSEfr!u`i+_v1D9{W+0)L*c@JYIm##}>>I-Q&%ozl`~#k9XbXO=*Ll>Neyi$3GX7*@w&5%!-G0{mw)7SpzDah6*fe z5{1^{t>pgi$Z}$XZOoIl9sf%`R2?M;@7*sT?dU=Qv37(*pX^_AXxVlAI(_EpS92kz zp5JQ&<-@`+)j{`Vev)wt8ph0{T0e>VhH-z_YlBE7^K{&pnq4?f{~{Qg&ZrAJ7< zD{4wu-IEUQdr~S{PJC@{7my!8`})0N8mP2XDG!_NK2fly@f`zK@KRl z-7QLZ#=n7OD*AH{4Lz}?OC9-+waLM%S=F+R(KRioL^~Hqf=}f=1%LOc9*vy77t4)B z7&l*j(;;ow@{M3(ftaXcw{_!`;gFaLBwA611B#1wn%;4LStxTq%F7Olzx= zdQ%wWx-E{5=dhym0ksRAx z^X-n#4tFMZMmaB4Lk+oFAu5kH`*)WZzLm+J<}X)M7wJj^mzfx4cDTmBF#z)~bZ2lO z4Z~5r99I4|si*u1F8FaS_B&CqrR>-Al^*^9GRL0l3!(mxJPiFbj~e9Bix_HRx56?397dV7B+RSg*)uu59xw(j zAIKS+=}|(05lPjrg2!R`61&ExB;Pe`AAQ0x9f4u0>omlewy}HQ~IG!mfI&>y7D{&Ho zD6|RG8*aA%m*6g@d8qn?N;wAXI>&w!nde8!`)10`EG~1!|7-8(FuMB7>Ir7%ETg#QmsM+=bt!6KD$pwa-$D zekk9+JGA^O-QUyaGgQWkpoYur05JA}6vlAOC#t&%oVRkNN<2^`h~i57Ir_n7xJ3R` z7qiV*kvH4nNinFNXeqATN=u(-g!quIj8y|-3xRgi;}&4ZNsxCB=J8YD$FCXtJS3Ep zMGX)^dOP(wTbQtjFpBe6+4+TUH_~!tS&B<*cLv^v;XcdXy1xtYM- ze0L?Hp>|_ikJfne63SMn6fm#2@2laIB=qId7`G3~zO|rD^d5l$kZ8D$l5jMeSUfY0 zY3EkTYePQCNBNFngY%uXeCsv<>E_t9p zy(&*PS_z!*l+Qz9YJA76DklMwC$={7j2T@eSZv5d;{LM{W3DDU4wyjO@mt$$6hItU z;uEn(;Ln3NC&E6kn#2%rc`#CL2~ZEW9G$d8TeT3OlXNv9z$v)Aj6l;Xf0f6yPk-c{ zbxqgzk7K8HK1NO#IEq3~U(W8etF1g;N`4A_TIV4Cy4IAsr(ca^N6)!(40`a1jHu`%xll= z@NBM94c5&9*$T*lsPyfw`Lk(^5Xz3Vn0fTwMP9l+ zikenqdq?m|d`$*-VR6Pl@;U2}_7AZOJA>rzmt$LArT_Wiu_u}hsSG697Eln4(RPrj zOE}!h_dYPE-P>sn()rd~3?ro(=@{l}MQwG&I3n{9|XY|n4Gd7E0 zRc*_yut4!*&QEXAqPKm{0}EP~=F~r2m`sP72ba9k;%)={);i9N;f=zx{Gc&$MH_Jc zEhC&hK7w-uHxdS6fA;$C536G%r@K%%S6_zvxIe@L@EOJRv6-$cKhjz0K#3%jor#U+ zoLjUw#BLv^QWT@=cQ*v(g;D0*1!}26 zzOhF5&5t)|Utb*Biy4#~;^Dt>cHi~YOUy<`U92>d0WCdgwVMH)dhBh#3#XKBX?6T_nXa?SiR;0=f?VMy z+0lr;@{xdU^Oc+Mvwm|AVEP{5AqZtYCy6}91BS~2*LEe#yje5Pkdc5jlcaTeM=ZDY z!&5qH4Z%=40v%y=l8&6BR^Bxy_sBaDk)sNtg)bn=VE8v2VQFqXYN1hg4Qs@ky1f3P z!b&4QdhZNXyCuNo2hi%ZBHs%%2N(0Xn*+Jja$ux`94oHJ**`kGn3jk~^zKZonUrej z&WD_b-KBTqiqU)F|HIF{Wm+iSTCV`b5ZXuLImGm|P&}7bd;coeWN;I@py* z+odtE+8*M0B|?Y}cB~qoFsE9MS?ckXRR?GNvdY_8H{;##$o?^v|BL z9ksP=mxoS2<_smDT(K?P3xgRt3bOFi`#C#&IP~sfe!?N$3{$w%dC6kt&_i)$^%E;X z8l4pQ8dsYo`*CAbAPcU;bQ0Oe6(c>U{!dSQsfX0U zr1hX$PWOkrfWnUl_+2kF78y&g&_j!50KTAlOcRfh{=M1mpxTreBjk@$Ibp3R3M_D8KkA_ ztWmtdRxZ|-%G&TRjogFw_h=ju8&(;_GGA}5QNLZ;6sUix4MUAqT+es5725y%9fY;g zBpI&;5$3{6%=(jtT)grY^W4Am-D6b z!oL^femWU@$?;ss0;CUF#6;x`fAwuV0luLM=a{8@QkBb;TqT-Rm*fuOZbcQ-DIdrec^HV-vKhm6JrLsxpj3UTt7Y(u#RW>G(-y|L- z!e7)TGd8mWyIH)|xPU_|$hXDL?>JLY_mEn@p?-+FZ7;xuC+?vOZJ%1{agxR5F&$hm zT}6ST&6wto+jNeR-FPg-p(TOceE*R}|9#KZY-FI%&1;@uxSzV~<-9{YQqM69_o(%K z=Sh5NanfA4+~nF`?rP&>Q#-ftqh~5Fcd1OMW9R)ijZKM8Nrf`cgg;~kSG2hT&+es4 zaA_lO!f@(nA1s}NWskwpD}$SdhjW)to>c>~T7#Q>Dq$rPja&OenU6*u*0mP+xo6#f ztJKfy;loGDZI^X_b_r2yp$M(9Jpwa`MMjaGiQGpEo#8n zJP;jWop>~ZEA2u()^Ks|t*-04ig2^uS%i~`{ zQ?1>;Q6%&RuX?4u^8Q*v6HHzc>v<$Pw1(rxuWChtawd)T#RD7FE$Z{;cKk)cFD2HB&BRq=6JG5|IZ#1@r9*V z;VZcjiiXsXU_bLyat7(ljRuHigA4hP<5)QT;J8bzauyQ$Z=!vjPfG#opp+}RcBmX2 zYsbIGj{$eDXN^&^ciKxz%5L9Z zb!H;i6~n>cF;H;G3{g}d+Ri3l2kH6@nMBQnR4usS z+f++24RJ94gmhA;-gP*!?Q6I#o5y(x0M)+TrrB}3x-U)AO;sxeIbdDhjSg7e0Rz8{ z{Y^=Vj#m0oz_@t>NrJq%~q{zUL`vY=E61-Z`Ot`;nWW|k;E-@ChtYi6!xb0~ld~?I7XJsWCnKTc9~tS9hW$9Xd?iJ z1=}3-w#<`tI(dRSv58pr9_DLAMhLVu`0PQ?LQy>Q-nvIw7!tuE@A&Y^ z2}kIA$>;u;g`Vqf!>QlW_A$IN-Y!t7hf9AO{V+;j^kGZBt5z-_E>krObP||au;iM~B8CUnPvP*N zqev9g>G znQSv9Np=PKFV}! zukNyhE^W$0?*EXD-pr7B&Z2{xs>bipX)8l(i0v{G z`CeGN%@$`T#YOF>pX8~;x2CWcqzW=C9Z9d)v|m~tuLto^Pzm>hKZPph^-OWn z$vrS0At^SlBXLb4+}mEPNP0YSawYDU6HkbnvGxBMIExW*!d)%2BNZFNnwH~|6#hoXT3f#O!&g1bYJLV#k$y~SOM zLval*E$&uain|ndO0nWnZr<I;mY>A+OCRl$vQhAzonM-!t!Zu zBQ_CaqsGhD7Y2{&lnPQ|&K?B5NQ;S#ICo)|TN)3p#W$Qao6zLtU~i70adXsx>p$w% zi6!NzorV=r@qFM*IUro?jt_X`IAn-sU7_3Gk3XVCYn~fdxFx2Ri0Wqvjk^d{xS$qW zar=24?r*Z8d1&!e^nCA8KVzkW<>Spr_s5t)-z+sS3^4qbflQsl!fY#khm^iA<)^`u z!p>&RE&uF;zc@01)PdF_^E&UHgN+gAPcInqYtL=TpW-5B4F~!8zZ`F^;B;08@kjm` zo<7m^yahHJVvsS;Ra3Se_Pl5HMjTOjFsZp{Fl zt;IZ_;M=4c4Y#bAgo67e#$nJ!9k|#0sp#*xg%)b*LVC+88i;r`pq$C}=Tx<*h40O_ zWPd0QUSWO~PilCu0U|S6Yms^yr;I-*??t zuxWvSm~BgvkVd%KSV8e$jj3kdTu3C4R#Wf;(Eu@QDhxy73YBY!Y*sPNGPFp`Ir8n1ZAsIZh(!`?60DOETU6;5Qt`UI7@@V_G!8 zOpFK$p9K;E&1M!x4vq=m?t?ZeG^NXd{)^Mdfl)p&P)`yaM)0%TvEoTYaLr?6T>KAs z?`FIHu>ATghMm3&`YUSWr63(aB@}t|pViS?F~qF)hBT?Q+t?B33nWz#`fs>C3FgvW z8Mos8&+i{Rzmu}btes0=v=oZGf3OIK2~xCyq9UNSd{q7psL1n8ORGh*QAQ9$>z?004B${^SKU{LR{S!4^neLh&+@V(T2bNZLJh`ctQ7P z%ewi8w9H=Cksi)Koe_0u9o7~iS!u)#O!#8f@|L1^++!XuVzJXkVmEQ~hqA~4dm4*e z_hRu<3-8hVQCL66P8l!9Ku{$--+j>`f}e(m#ki&&IZX|vpf?`}5%Jl*uO6?2L#H#W zkP?Gbc*zV5v{=#P#1!xsqxBG95UB81r@`Q`=ZdsKgEX@Dk%8HK48dFOJagUVABV$J zdCPq+es>bm`U|0b^4TTgKWimp8aa4flQ_UZy~hIrV?5wi^TgR=W15eN%&9mQYl29F z!+IJU7dz{lxV)n~2@O5rZfB5-)50PB#BLF3B0beK>>x1y%xpBO*e91=1y&h?jQ=w8O;=Jb+T(AXf}#c!14 zC&A^Shj9t#lsn!S`Q@jiO%zDvtF7RfkYMLB?noX-_)G4+#l8daAMpwfrGf`8_pJ`H z*Cf5|9Ow_10moQ3%B(V;W(8!VH2y5zB^zwEA+gsoXaxv6YY7QNvC&_ zz|kVs>V28oES4sH&AE6G>!ql|#i`7S2myBaxkz+LtLXUVA9-xrBM0lA^M{Ou4)tM9 zVk5^?PZqhnxp;D2&=&+B7a<)aCDZmmOk*keySoufG=6XKb-{@oStT1>V$zE<|QB_>llp2l@L`T+^_*DH#t z(|F}v_4Me%GVBz;{9DboG_ic#8R=w8J!j*?hQf|!Ye0U%TLA`u_#e{ua>2QU7|2d4 zijSJ*=8~>Dy)gD=E)2Y6HE|PZ(X3Zd-Ds^BwdrJ2m?IshaZ35a&Ju${pu{9PO=oY( zQLK=270BniTXXKvg}Fj%U2O23jL-A_5EJU9iQ&cP(D~}Kq23ZsNI+;U zdxFgBD5TQ*RB-BUB{B5Ap2CuZg2uot&ZfDJgI~kWvVtmGeS|43R2vNkp3mT%l-){Y zv+c~*(|m*lIWYEQy@fdtBgW=ikxTithd0IJ)jErK5Q4uiPxsod3?Db1bsvg)n>))D zJzozcy^|y28TY;CjfQ;RCen@kG-8#nG1l+idip~e369Msqi2HsjGR;c@#JpS zG~awU{^~|Gfr-4AVc0nvNMdq+g&I3-_zOpOW+O)CY!WC#{NRuj%WSM%lDj98U_cFR zAwUz3bKt_nfam+LM+iffCW9_xkw#0tY+Pos0v9lPlixOU>p(CmF#L+7Uw=+u1^+?7 zr)`+QH5zERvNh)t^mfYzk&RCZFcKg)KjJy_$RK4OUiX=DVo}nTFu?oJIp23$4*Wsp z>_XPsk>N0!&jcAnqizu}Msl4^A*fXo#RN_o$K}17*piX2%g;~FdCNf$KYF7^tlAK5 zAi`vNQ3M^LQhW1t)49DdN(}-$oZ6SiR*%7p8z~!f;v*m(4np|l!!rY4Rm#zsbj~-aIe)m>!h;AOD6zt(rLLu$VJRLwl z)*fh3PM)V*G>1*rj5?Sg)xURxs_kZ-cc4!NLCyK;i4BF~e4|jp4#D_$jl9vV?pwhg zD6*oMT~QAsw1Yvj8dGc(j#0s6FM6uvp_I5BD4_}|*giAa>&cS?3qst>W9a00Fj$2S z0BI&Jys%wZR)X|q%7c}(xBUBaEbWm(@HqO&`Gz|98?LFWYIyP1Ijs}L{ijPw@&2b1 zZhFj0dhETn8|*8MwX6qYb@n7dfREH>JW;e?X8+G_KF9K3C`;37l2vQ(snBb~xt0|D zGR4_ZLJ@LKh9gai8Q^UJo95>U>`v0nfXVkr9cTZ`Uwv+x{#ew4&u^&3?$kgBBA>kt zRq$9u|JhId7Kwi4!7}xYXj|l49FLP~sv5ve_Hp$`Z?gx4ei6BDA0#y&lny5O*K+HT z$B0P}tc^Cj^ZfYabLrRv`eGX%{?_Sdcag~MtTypmR+k*yY=wuq>RM5FXipG}(VG0# zHYWZY#*2M$rtgsY;mSU7K4q3PhT(`Cf7sNjSA&#H9Ln*euRM2*ozjX5kVW*jp#Z6W zd;Gq|mZHf6C7USu=!>Z4&==BmuK`kn$locvuCZaxw{!#PGAHmKKaWy8O%*Qc4FuhX zi0|(Mj3#9kTEf)7q=na(2xRvm+A)& z7RxCz74N$PeiOO)qTZ5`Bu}llv-DBCV(Dzs;y_&lqxWXog-vvVCt9=MjD8k~AO?EE z4rlBaRElvw zS?oSN>TCzfjMT9QbQk&FM-G?FGTXL7xvAa=;-l9(U6OCU*V0a_@Pzi9&eJdf{a^a( zxI<^OU!MK&Uace^&t9!(ktbunWG^6id~dIRMqwUpHAgIS(u8Oia z1tV`m)Os`mC#!L#>XQnVzc%nutaF5a|EPMwGA?*M$w?={nHUinmm=8M+rO#jtel$? zl}^9vp|mTced21!iPvwecplfaYu_Rh!$8HT*~Sr4A5Vt`iInc$yp^cR*4cku3V+W! zm*y_fF_oYbsa870g+If|L(EQ!_c`Gv*AkD^(0~IUhk%ZJowF24e}>TV&IBj_q;0M$#8Hij&;oWfLc0R^JJHdS$h0S!C~orI4U<-!x@Wsu4}eE z^KG9zpA`91({+7JoMITgo%vA;cEJ^$Uov*$u@tMd*B}My{z29!Y|)zgm98@?%sCLFR&hBBk@Myau~<4Ykw|B;xl`cJtbUFv3l95sRI!xyqqows zsOHEAqlBCY=rmlDABePvq)U3BG@tDg|aToucVsJ@?VZ#J_8_i(oH ze-s{S%Q5EgS}ztvpc$*<)&&ll=TCV|aI+RJ?<*(Dc`#K1_GI07Hl|*V87vUfR_FO) zefBeGO^`HKx3tCrw)h;SHaYp%vM3N75$mE?ZEgI|kL;!`DGm@$4nVLK@;AN2w$q3x zCPMo5%D1Qps3FtB!i%fp9mb`nMs|ezt18MLjw{!z_`&BJWVJw&5`6Ql*LY9=!q_hm zZDQ}wqu#WdeQ;G$jP92veb_^;nZ(}2t?S#Wy|)o<#8L(jOzu$qxN?%3^A6!KbexNL z>EFR&HFt?9+C-C)!BZt|Nf=46$1{iNaPS|~WsP|&X2E`e?JdoK@X&rF4V|tXBO^2W zAmZk&ZTA_D?xEyo?1n{6;GE;FiC~Q&_UOF2m!#AfkStV{T*fpv-34~OdV|udoI9-; zHgr`odCkL0l579_dyQWBhvwUF)3OxM7%8U@Z0;`fVT>IhF6R=57!PO=B@f?jZn;|c z15p_R;hCQj3|sId2s$4Xa2ozQIflxk`kgv?!`FEy7$sx-H<;~D8b&@LCEyiO(l(Xg zu!&DuaHqy=H%+cLUk}6n01$%l>=ph8obyeisEr9ONdEz(O}Dwxa+MvDR&Td04qE3Q zD5*e-cwuFZyLXn}p76}E^iBgF}(=*a)w`CXi$r`2r{=84!V4JWnH^Jb7z zAlKSM$+Xd@k5DvTTLZWI&>eR2c3OqI)KpeB^$7%yd|`qZxHould8LP^lHNgK7F~rJ zi>y~8;?cPrKxk%e{y!WR83hzA6?fW37&!ZK{1?2$=SccK0x0rPhmNm_D;bDhc#sZ= z$mu>?!{!)_aH2LMBX(<=pjVa2HSm(wOV2IOF^NZ-CSp7k^sX0j2DN2oFP3b)LhpVV zdBfqSH${gbDw~HKx?$l$Pr?e{pZMaU9F!o9HpkjQS9Ya>m;!}Z3w;3o;!kqgw`5I5 z!I543jXONG(k{NQH4*27k=a)o@cXfK%daCz#{(l;S&P#FDh*n&61F2MMNXujnth8=#_jf6UAyIXkr zt;p8aJdZc6Oel`v;8X{l2oteLKPkkDx_#2rUC4N6S$39#hu3Kd&8Nk)xO4XPX+~Ai zEf7QBOrSR`o%f2|;Sd3F-CQQET$?}9jL26o_2ISf zH1^R`e(nLQG%{J;1^&U%z|qfcF(y}!J$wAC=u8p+Zuvltf&%%1CR&;*(pj+vQusYG z`1~)y02)kVw|hf^?dKsu=}5uPeB*wJ}>&jaXdov0_zlFovyTQat1-v?n0oWw?$A{e z0LX7F=?r~CDy?k=(p4ghOMLO!=Hp7l(>T}25k_zf%i+1B}{bM!nU%$UVIwE3) z-WUZV5$q8h_gaMsf>;yUySy?bWu8{|b4P77bwMoRa4zhSNet<$L#xGi^q9rcq23Ab zdOlhGo+yvnbBQ0vd-`Tgs-yc5+k>WX$a;m#Dd7=it{r8lFn^$DkRJ?ap=@V1Cob z{*mHqpPJx__@@J9;n-QcjrOAc1hOrLN;OMRrv1zS8cH@WC@GzX_%CD6kNtTmDVN-S zRbQD&{7(X)e~)Z7UXOG2xEoijH~*c=<8YiYq?Ln$8f^n4eb|lSw$ACvX~ERCedOq= zSxa_+P}t2*BG5I=F2i+3-c69x*W2$A#c$g;e`Ui7QoPSInLTGx(Yue)BF!kAM`Lt0 ze;9eTiHB^j9`0A|mv*ElxyYTQ($Je?VLiX36 zalfLQm)*z+x&3dGh1^PB_r2X#^?-Bd*q8|$=<$G)@_o19JxP?&V2&ZP>D$Dj26fNO ze4bvqK|dL{^*cfG#Z-<(nQ zNbV`O_zr!-7vZM;_n;Q-6e~PT`Xzbyj>LtY>O-cLCk-xPgh*p99IZF*>vTleEEIb> z!&XkMGaJ{SOjTK%5QlRHZe;=fj1HA$nyFvzP+;OV-afMm5KWK2TKqCV3WKD*~ii5`a8br(2s|&iBsAe%F5@_ zofPfg|05T?kA%yUKRx<@L7qs1N8!A4A;f)+*y@P6C(Sp=I5{XNKDFm}>n&f8I0*30 zZy7pa?3HGR|1fZIdEA+yB0b;HBfNYeX=hdo;9p@aHS6}+>X!%MGI8HT@;$x?wXreeO;Z)li*2JwZxh9J+@g0TNJtFI%W zt3S93JF2kGcE((ZE*wckzchPUsn)FylZbCRjDNM}`|=0#ar)&2f2AP7`$@xOtuaB8 zJ`pR>lAf4Xi!3S@=lo)qewGrK3dgJFTqHM|JRFwv-C3IL*U|hz*2nT+$382GD2-cC zFX^Pqkyx&B1rIg~xEKI8w$1Qo!h-YRoOdq12s9Ii@zhBbMFM}+`}r1-tSA44MT{PK zLB|M;d%Z3B!-UZC!hlv@%8ZA&>P2M|D2dlVuy#iBA@G&tk_?$Eyj|NQ_@m6N;`jQV(OIFM<=2`f26RJFVBVx9?z?fx@I&HI3`wZx7Y^r0?29RM zrrh)ef2}s5U2%ycBFxvDfoWOp1qVm_*-DK&ch(a3ky*jvTT4*jKETdiGMIiz3JiTCmtl zfixYPdFvQ$oVOHgUssN|T<#vwNXnd7;-z-dJ#FaQl>IBx%L@|C&6+9xYNLO{BgXlx zCxnIhKB3YkBzwuWhG(avBMM2nkW3FpX&hu*f=X(2DG8~Wn()0!{i43U>e@k}BTpa3 zGoEjDU;b9*dBrk26sUR0rBYWjXQM_cflnoE2jLII|B`aZN4btB{))Qrq!altdZ6KZ z)Pl)&4S`3PDyo99`Ta{iC-(yQ{$sgvjQKEamHmmeQ*5>{0aYj-PHoK3ybJZV(%0W zuhH}lll^Ofj<+uas8?&5pk#zlBu8Oc?mWe=%Hgjl;xkV))JH;(7m3}UOBEFR^Y>1GjiJ({@!BUdHum1Yb5!FQECWjeC$f%S8(S#e@ExISx$=x<7Bf+YNs@#ew z6g;FgR;*PjiQvL`N^4ICSZXk-2lFsNXdHIrtd3czPGlENcyM2eS~z(Tsnuk~UEA`T z6q_@-)rVG*AloTAgHPvQJ28WdETwKXsN60esEmpau=soFikYX?NcxGnEi~L|scjgr zX&n;gB7BaL%``45HQxZ~P9A35zwn{LkhBn#L@IO~Smw`v*UVCMN^jVFv4ynN!}q92 zYSgqfWyAL~SO=59Y}vp@X8=^cZ>Szn*VA5tzvo`V<`F&45HwhO=uNQu^EB4`@on5u zUyUKHMkHT&#spd!;^NtPv*Cq~#_-Nhok6^MXL$2h5~VyewgV0_tNRBa49ys&-kpJXDQ+@) z%F83~QVPqh!K*m>zgp@@TUQ2$n_2+b%MCxas$o+JWKvR=;wBGr!-Qzfip-7ISj(#; z8X^bO!=OgbqJu!_{q)hbxW=mpytMXbNzE2uyxjJ&z6g<`Y1T*|+~$D8HOX-4x9DvV zF+WIQmc|+WhNE`tb$xi>-&UMdjRf~YLRHQKQ3$!DH;4`K!a{uZvFco#ncY;!{#*{h zw!9+V(Z(|9{xj&89!7(PITdh;gu%vbVudXf!?}`h!;GF*0DNWch6;K^j^7#S$)`l{ z)yxV0;703KUi%7cK|eVM@3lBtQct{FRH0xHmZaOO>u3!5Ac04`Me3F)m#N9C;kw`lnj7uNr+~iRvHlxBzN?(1100Y|4vNSEW{*3HKH_UagRGlcGcZ;jqGxnsaDmGUC zTpj_D)PXCh4pr%=bb7#7+XaBn<;Y!3qgkW2sV26X_g7)s7u;#LM;}jS!f3LO2(_)W z@Iu3mmaO*}IhnDQ-JRBcCws{Me4# z(%f3k*|z4d=@@GYr|vRdaZqHYQ{jofgM(mAzY*I7xtKgRHoAwlzaa6Lz{RyP(pGB? z7iGBnW7<~(d^o8ieS~zCk7$^YpM@BydA8Y-jZ&sNSBhVK*)T}^Rf^KvTkHrZfQov4 z8*FDO7JS4${0;S(Vk!Ew27PCh!CvL3%_W6X#lT9Oz1=ON+!NgX?&7um)BLXDJ$ph8Aalv}lQtKw<}2t)Ot$6sczbNS3)-A_J1F`;Con z>vymdiD)UHL2ckk;^@QWWI&crQQ5h`->|{Kk~tDQ7{l*Rm}HH}-1uZax)$Kk-^4tzkbY>7p6INb9Ta$$@*f3i5HqyXr??&-ZZ8EmRN?9a=N)29i;YjN=lfL zXtAaGN!`fETlj#TuTOezHL=7ed!EfzC-h+sJ(mF3s^P%SF6xIr74n^t!n~}qz_Zvb z2%2t~dUF8PmYM6DEHe%hpE_*7+zS~9+LN3hZb%{fef|sW5GRNnjA7_P#|9rTQ&Oy9 z_l10UQu+B<>P#St(k50%2X0Me*A_QMN9#Nl`m{6kKW5xUnBa{6vX9Sg~^xU$P1ngd>iOylep z(n&M?A;i;B4iuw1@27(Nek;U+Ei8nc`sm9;{A=TW!ylV2#=P@d!n}4hU%goZa8R^# z`JY2XL=Ac4wDxZkG+l=g>AMJ!34nj8;JshGWaqZ(>t3S<*x@Q#$|w~hd9#E6P6RcW z7WZ;R-zZ^QK?__3Pnr=|K38Z2vG$xl6WP0ts%w76Tea+i`lCiQglc9xk|^q4Ipvv5 z6fRocsIx4ql0Qu-pB)BpuNTY) zjk^4^aqJk9#?Rb3NZVMoEtrl;DBBkd-4Gws$c9J&5qKJpxX(;*^wSS~0h;OL?Yy=$qAx** zCy+=E?g%D#GB!3w%Ri;u*z|Yq!>q`~vzw3akV6*-d$Ffn2c_+q+2>$j4JY}`TF&vM zMDe;*`cTD+7ZZB&)M2i#!xfnd5eZ;>pb`tkG2bGw&~H4w2oGrRn64f@ZSZQ1C*#Js@kRkdKwo$pxE#;t}TfeiumC z<;0F+$R=YnC~d(OI@(#&^KM$5i;o$4`H7LwelZnT;Kd(mEbv4R#{fB)8pX0JH_jE4 zpcqZ?g^9R@Om9E<;h9@rIh@kCx?>(NO9C4KvZ~oL_kszrJ9Z6txWo<69uuz~Em{;n ze0OLw3``pa0Qh@CTt)!=;8~<`AgX~-iwg2|<^6k)@3v@8N6||Mz}>AY z(13?3Q|Whl1HSr0#FaVGn!xlAyRT1baI$mBmuX!7UHv{O$fS%6P%WK#CdO;CBoX(~ zql%1Dg>v&{$OI6}lvWYCdi5an{^)()xh*TFBy~(YW+H{Nld}+K%pU(yFjeP|Rl>&C z))JTZ&d35z=kGZD4F6^ABSgitW8DVVqt3s)&%;Y;ZXLxf>aSL7-CrqR!43_x^SZ2C z7Q^3GsRxbV3WS;P|Jae-)D!~!RSJF&8Ab`bmXS)Ak0ZDR2cGvDv_MtM)i`*2rBATa z=M4bgdnPaJ?Y}Y@&Vd@O~Wbtch0u!rwcvp?)+B`(Xtp z2oHx-seWw39S`G9T0Eq9KCYOvZNm=KmZB4lk!IeJl;@Oc*iE!iWzekfwK!xm9c9tb z*y2Ehbr&`j#fS4n5qi&FX@kA8AIWCJ`$w|eK?NMA5=Hl{S0zP{f5dCu9De7FfXqW? zI`;`{>x>iCg!(luYQl6HnO7L#(S$G>HY>y5Q%&DI=@WX*-Lc8{eEDgDk@&3vi~0Y| zH$iyz8tZaO!Q+q?7u1?xjuOP7w!7*7J#n^uZnr$uu&|nO&5QFTCsa2v(l)eQf+>?^ z7TXM8zW=>~-Uyu_KxE6L=0< z{Qd7Fc_KJ#x-~ARO7(q!E(|11sp)YhycF4`o9^xG0KzC2K|y$!0k5$H@}g@4KqGoN z+=;7>Zr-7vgtREu5)vuQzz7|)l#iyzqXyy?2%OuGP;6v7tu4)iM#+>f?5; zV`+?#6bvn^-Tbi)VJA1y#V}{BUBHZVJQ~)nUkhm>tx-8qs?v;$xY!x9fmo>t`Y7_iw&;QA? zgoKeFz6VH^^k@cyja3xCoW+%ofJ(O1S(H7`@FUVCMXBN=Yus)BUO}psAu5s5F_lo5)Cv+uP>$j zitSfP9cH4v`&`B(nQ@YNc$yg?0xcYcXRLceb;Q{x6uqMYR~l8>sSoU2b{3e~UiQhT zXi#63$LB+%v5Ponn)3H2Cez_Q(S}*<;j@?D!2bO?>-S_s*Km9^B_DGN$5LL*QTc=q z;1EnpE^MXXQHUR8GB6l9RFmcx#-Wu=Gz)yfY$J{@ExoJM><%e{WzYYbu8&9fRRS1~ ziSnTrtaRaUJOHKkDz;`*nZHe;pkL8~R|uTyXMHv|`nmo)m`gD(+xcBi_5of-ERxXj z8aSc}Y@W}KnOY%O@p|}`v5I6H2>X<=5wV^n$>Qq{3|xWUw`!VBVWr-4exb;L6+XlO z1@b^-71NU04Hw=AOdfMp4fKtZK;F?hMMl;T5q_z3sw=an<9_8>b4PB?HA>a7Nc3bt zt!8D2tbr9&;tEnxk1mB4aHn1=$hRB!M#pkIG=NRZ)~UY*HwoLkTEW1lIC^ z+B1P)e`(FltxloiRkHfcz7o0E0!G@nQnz*=GKH}6=BU&3+2+QW^^EC?NFmMic(}KA z9vAjTw(BYXJo!xEFwDEQUFl*qtD*ROASKlz6F1_{8ORmQOX}!&{l%aq4D+Fkcd3zK zyknds7?wLTG?{B`&XOF>YB@w+z`HmJ1?ou!mQ7A_YXDCMgYatAZHWdmHJ@O#b~v04zO%`RW6HkrR1ReBP~*|?lqL1`xQ(a%tvy5!QNn|24&Dn z_a>JvA!RDmcm84sV9&uRws0(#)|yJUDTzJ`UiFn|E&NOZjWrEJ~Y zU5Zmre4IefO7Q%Sf7D_kTm%{3>zb>^J5_JH;w^;NL{NN1{`dQMNU)9PE5xBZgg7LM zI8q5ubJ)1^y|+zWxg!nA;p~YD&%<2Fydk$+Krqx`B)r3iyrk&tCx{s;FBm$wdj6?T zCJ^gXaS5nBmbIhn&nc*A9>oRs%zZ)r1Q6SbmOm__K&}AS+$?EQ*;xK?1>gaRse%#O zOe09W5Kn{19+(1&FAn+ywX2TB8JNg6yXCT-e=F<1E`Xkv5|acT@bqG@i*LmM|24l$3pwT*9Vey z_dL_jYMT{jsFV)@uKz)3|2y^lGD~Di61U3-n#CgX&1Dru7ko%22@qc@Jh0q;*eA4B zM{g&lc`9E(z;?-!_E;&nmDA)h4H&X*gDqdHA7Esk6=x{d_FDlUCB~_} z5Cv;)jh>)b{c#Bkn6g$6wj!5@j8ueZgpP_D+Se!^PEyj4MCqgVkvo9!LmK~ZH2wYA zv#Zz8tFO3;jVw9nct&?MOCE%8T3O3&fb>?WZM(WI_;%X%#KB4XKN>Pj-hw z1y}8aRMSCF7rJalDFNe>OTJuUbW}j0U|y7f`kKSfQ@>;G&9uX(G;vTx!i5h8gPLjfyKrcG z^=pvPyZ`YAaw##h$yA#aCnFTOUxGV!eIN1P$WX~e?KIstj)%LHZRnc$XL-QJXTV8G&=A!eOLfX^XebBqe^qWW zr;+GdOUWxu;iDEyMuf}#JG%}BvM7DR2~ru(h{Z5UT64OV=i4PH|KpTOn53@o$*WEr zQRAYZUudo~X#H((7mzHYwLP=U9B%katra={j<$*qI>tn)_QGN@r+45xOQ?OWi)*`( z{8u5kOa~oyL?6jooJbvC9+1(!c$TY3|FV;N?`{X^oDB@NXhf7FkceJsla36y#nw*v zQbx}xX;E?Q4_<9q|J9|DPS89}_#X!`Eth#*ArY#ssxBV!s)w1a=t!w3zoteop=qq2 zDb36zNy3BSl8a#1GD-w=lL6@pAsTn}sU;B=j6;iEy?cpHoRWRI$+1LDKaVJuTX8J)oiKRyX_CIWsNo)URoUd3zWz-Z>z!dL#I^Oaz~hJGKlc+rDJ1PT2s zP4l1g8y@CZm%tTaZwVXSd%CCJN`btRbSkoB{7C5; zi=tP=8_;wlp_rtLv0N`?&E-oc2jux<@dx~6zw{hHO&u2Js*W3XO*pF2qd$sbje>j5 zU&Z`p);j}h1RU;yWo~KtRY$8x~88 z<^*!YA<2%l^8A&YjD(Uqvh028G2PT%N-XdA+&5nC1N+U}%NS=q#LAk$QxG}TVWuL= zw?v)4soBiX|6W_YIaqilaorcFZ?^28)dC1_elc-%;`YGnlarkY1$G-rB-lfialHbe zDhxE@Z!KdY!&8>&;IaJhr4{B%KjzxgWLLldAzOFTaN@m0XC9ld(%cy| zk?jt$BEz5D=tdU`+6jdy>9=Vx(VY-Ca5SaThE`v9(y~M>o3N9z#RnU+CzF9Vd>$(_j2kZ zCJH8B|2-vsJRd?)?PJ&C*vGAr1S9)BI}O$$A+V(00u${SlDOsBAGT&kCtjt{1>@9r zhbn`~8Z<-xUtn+SCVHFGjz40jf@4mQ4cyDD&Udq^QQF<;y6Ba2LQq*gpa1+)$buRD ziaPYabQ$g|K{@-O{kv9k+|MVn0=~A`2J1b;`GUm1{G`17T>l=~8_<8!;sVQTkK=ACX9Z;r{{EUAflAWr8 zSFXdl=p-vS*62^k&F@vE&9Q|Hs#0ZYKdFV}*}(XmZ*TBz?~j_&sBLjp*7^M6*xPd6 z3P@JtQFg%Phhn7a1>eyCu1&*4f1yunQg-AJB77>H0pLZU2wz>fLgikP8Aq$-k<@B!!%TI5^VxG2$ZFi8-)rZ5+RPLAsBE^wD?@OdGPK(L!3@EB4Q)< zd1gh2E|tb}S~okr?yIUVriw+tj_{N*P`FySG|?I>?zg1Z_s$WN?XSAtx>tAGCMUK@ zJc^_&t)x9U{s@(-bnoOc?qE9duu|+I*a|wI{EEbh6IB1dIPFUrXF`cCfoPSbXv4sE zL^{8;Cae0Bc<{9v%>c~^hh@2xzVco*{P2SYtMWT!?PZNo7d7X|=CZx-zIT`EGZS;5 zB$kmV0Wov*(d7os^*t5J@9t`$M51VK9^S=1K=g3J!;3?8Cnf z!aW}E4qiu`b8+b2Ita)Rhro$OKS^7Z>yBeEclwhFGGFC1S!aI|UTP2j4A@FFZVTknd>&OtTNOepTSeGqRi{O?4fL>NXy(s6;hA}S^B@rREn(56I~z|$B5 z1$k>7Vzj`1g!i-_lm_&69^SD< z#5x@BDew@7=G{N#3Gjp#*aJke5P1phX=-uj9b)iLsqCZxi{2c?ZKz}1>MfbXS}tdd ztg4j{wLmhar1;;{+C{}Iw8Lp~KUM)U)Ni2gtq1iA!HB)a;=I@>NU6k9AK=-pLDhu7 zPaD`phL>)9jDO_Ah50Jzp$6nBy>bG?Jut@fR)f@67vRevBW>Q(44XhVEjWm*xh-?h zL*4D+%gAA9=yFh^)=(F%LhRnKQp!Q%{Qb5E;r%aA*_Nb$R|O6x=~jfpbN0066fS!A ze;4;}66~8G&>LA(92PXQNVCnDgCkTqpB9DMT%(VkUGtRSn!LPUJC!;}! zgzP0FIqF9cL81TB<<>F7PN%jEEk=)q{@jsd)}n=?k%{;(`0r)lxyr+!Z&Eg9^pvi@ z%PhULb$h{s(Gg4IzY=U*-&{+J{OtI<)4WnerA!_Xhen_4o*fGg6RzLrGaPnI3wI3R zAwy-f3~sB=FFlZ)Mpj!W&=`!VmHjUSYI3B0xu>?8;crUs;)ceFGMjG65Zf~UkLxk@ z<<02thW>0B+cyZG$59_@e-xT0kbIw?*6E8g4c11-t}GAIi5x8}a&BfAaD(thv6K_m ze8Y1h1`a?Ufi;Sk;JN+eGnlL0@(TS`%P4GqCqqbCU}lz~n;u2{O#O#$X`y_Wy+iE` zd(93PW<_wd;>R!JH^dlQXKw%u4jLve?;8I5k7@nVCl92oC!*tsW5pu23geMihe2YZ zKv+uSwyjO`i%N-Cnh@23{&$2jbo)jG>kK9H=b+ry4LZ&~xfO-mCtzzXBb@In67D@t zg6UBzg3NmPT?IMFlL}DK?@fnSbR@b8r@PiP&e1r>0aRD<1z5w{j_){a{V%Y+^WuLz zgi9*ovA?@~GnYLuaUBax{{C-GHjqxwwfbN5MB1=(CvEagQ>OXlyXH8huh&(#+5K-? z*M~Bavi*Q}v5Z1od!`TN54HiKi33%;8)S~WLp~@nTHCCJF?H$okr4F_nPImb^R2FY z&Cs6031sy477MT`VfKTA!21vx;wM?V*@Q=l9r=XE_TvV88QJa*ORo`ZkMnD7tAAfH zQp}^APJ-X<{O*`Go>?FCZRfL%u>WZq-#DUprz>`=i}da#nHsx}j~$+1Ri}7oFf-)3 zyh{|NY?!*YV{zY7ayu4(Ra^9-)fur#oXvI(rz%l<{qj~>vHs1+&-XtFi)WJs!*`R! zQyZSVTQH-qzp#?gCaY8gH&yhzQ~p~JOBmb09=VSheZb=Q@3(^&yv*v->-fyWvDnH3 z;_5m`%WDdI2Q z@0szYtHQNl63yl08(Y;&cpOS8>w>rb;`*z({VEf3hBjNb$?!*3&&=ZGo!tHNkGpx3 zioDPd_jh-Q1sIc)v;l}E9#sK~FHftS0Z-4j0Vceez42E&&E5Z=^`F|#cXv#sswkO| zem@MIDI311NNC;sI++S^$)4e)=;__s6Q=~-^|^ABZ9L6Tg7 zo-l#&;>Jwg℘ByHLq)FNVeLmxuUx(}j(I`|eJoj}%Ltc#@HOoOg?P-OsJBe+S(x zQoUQ_2{QHf^?lmaQW#ab?(CS^3^G{e1K*PdyzETAnEbml@aIF$`oq%jjeTfq_hB;c z<^J!^#=?{EU9k0dPGOWw`JA(W+sBKvt3?mRpATvAA3jNVurF7aJgk2Uj$^+Twn5QS jGSK?7dax1jSp9;jfme3wEibNwg!m}Rs>ytpHVghg%gvh3 literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/green-board.jpeg b/activities/3DVolume.activity/images/green-board.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..281ef1dce79be4ded08d3c2c3096bcd8ae67b11f GIT binary patch literal 2141 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 z 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
_b(tkzQIo0HFfo30Z^ zKfWz->$)W0CUHqAsU}mBMy}=x*|D!a!usT?_))9ryXKT1S{}JlzhS9bF=Ho9mmzC;q}4uGv37ST)htHaRlb+E82NvVXU{uy@by$9HXi`pMsI z+q!Z6`nBsetk)+b>Qd8$dINzkLa=y|6lNd}^!LD)L_8T(Jj83^GZm>p1Zp5a0nv-6 z1>&fnyXy=88gzPnaqTUi#0kJsqKHfsjTf*6kyGrfZ|>c9AnVx4>{j+N+Xl8u`t_VzSlvYG$W_Xa0siAPQ4pe zuZvN?D!ttTRIBJ&2hwYc_uAt;wpfoXcFGbxWr?0FiyJRSw4Y~x{t8#kj#dll2|6{T zRzbL`WJZm0ZL$g-2|KrU{cQEx1s7|^MjI=}4&)>I^AJ7R$o8|?x)aobuX!gvSzB9j zsHeHIzoX6F+&t1Za3=F)K~8ROcTZtqF%pXtD%EHvCk#&tAyNZ<=})j^_l6*lVNgKl z{fC6>SNfV~U;ueF0W^D|?|LFBoGR%SnyJW{M*wG6P z?5q<%ZO2d9$rE{+e^-MY7$Lh^$_1}P1A5b}dtlo^n2X*Fu z8&)Ct`SG2v}N1gD)#;V^9zS79MC03Ve=!?~~g<8^FgT`%nbVqX3 zwsgtP&Fi4;S5s3=DX9isqKePMuGa!@@5Gnvq0d&PeBb=&cTHO_*BKTnc^(UPvM_Ee zCvNaO!e<4OT23(XKci)TnK;>e%G+Bv(cd*WGFWc6w6`^ljSjx|?pwQdZr`H zGBPrf(=%i`gGi&58cZyu2Fn*mkm#WxH7THS^&!@$U%})n6xaDm_Z!8KT@1bYt-{qf z%4(l;7>J_=p@~$H3KkUgkA0a9Eq%F#j?cb0(9||i)6ng#Y^|(tRTdwxo%&tfY4&Id zao$E+c2Jfa^aUq#xq`D)!C7#z#*2Z$T+~=0c_5e8ahlkC0$FzyY5p=c_cQ8eukh5o zFu9naQOi>dvWz5Uf>CNP$b2TS7#FwduP+G49IA=Z=qWQC+-k-eKW#&JZK!KZbzuA^ zuurhAG=YB+RkBat0YF1C|NHT^B4wE zWOL5CX`6h;!S!0H!^None0X1OeCI`E>ltjtQO4~KOwCKRV;NCfwGD~ff0WS z=*%htWi5Ll`vce z?=On%&5i2LUNdDudF|*q7t!mK=6}igeZuOeH!+kfIE#o@ip5H)L@k#oWOAuguF>d3 zT=ehO#WWYMpD34t!qO;cn&D!o^ zDAbkry!kKQ`OIBi-y1Ht^WAQE7y2wTyzsY5;5(7^0b=!fVeo@dtM}pqgF^N;YIUV% zh1O8Q_3CvWc!gSJ_^?V@s25GwrGNEj%BE!2_VsJGu1#N`mXwj2kep;lOws9$Vm5ir zi;r+WeFoloPV>*Uojtvesxv0=joLt>IjZ_*~ENcBd(O2<}ehyp2! z&5xyVA_z2)uUN1UCICl-tVQTG01ql!C|2XZX**D?!V+N^0t`d&p9#Q{VyGMlaX^Qbl!3U44tC@I=v(owcWw{dvS08+Oq_UUtxz9PA|*_evFS z-a(tOlZNv#gZZSvJW#5(XW<);#Z?}RFZl{M@MrqgWI)0rsAVFfMwwzzq?r^ZgT!D^ z=yW=RijyFMRi8`rxVTfLk<*qqu%yU}@K}F_)$8^zm|p$434Fg;MbA3WpkTGdd#rFU zY~XHjp3;Izg;aqfjfvI<-Wvk?J9`Y7}a@0u=>&?`g(V_2VA93KXnm z*nxuh{yao)4$6H2*?5X*JHS2pkt*lNo1Jx*?w0zV_V%I9o`$NLgL}X2?r?XxJ3si~ z0~lqOSf+@=l}Xe|uvKA?KJi>j$H1wxd0FT3J39y4>f1+KT&)GK zI}VAuFVZ|E#2GVw&V~hnYR4=%@Uu?Bl!G|wAWzsyBW8SmA-pFqsy{zwq!jM4p(e@@ zvo3mXf%xV1u%|N68;uku5G!I)!A4b?SS^>yr81F7lCgFpDi(J7EAd37Zq!EixCoF` zovS3ySCQtcNPeu|BC86eSMM#ix{|CbE0S)xUcpq*1b$G$^p^MWtJRe(e1Pj4z;@Te z8>o5L!@J&a-WtYzHi!er7mf)itn}Jri$QWIWfZ*;)c#+2F?+>vgnlum?z((Rh@s; z(^)d&uALqm?s2!}=jBXHj5RjaKmGI{Hf`GU$j(O-Q`3^t*U5ASx!xpDY1lFqOQB}S zlw^?{#}^}*+(;5F3=hIJ5CcwB55#~Qfd!z!7Rl8Z;wm&@6$V^`hgiZj0OQEP05yWb zilTF)DJ;573kzQJ>f3*HR5aE#b$zmX@2PW_THM{0bxjpjwm{RrC8O=Gt z?wS?xlUiMIa^(fLT060GPRtxQhY!rKdTsI0tJfYkV~zEeMNbsPj1&{>M>&7oLgJ8P z67*nhRi~Ed)DnYQZqz7^T3wP(h6sh7`)cjDbHkKXFkz+)7NGj`5dC>5Fv)rjSAUXL zv{!KaqqNd9pSbJo-7QW1?HwcCeICgDf+K{p z_%SqYB$*XTqz403$b<;Qk%RD{Cjr_LkTAXG?Sp4RaFlR>wuZ=vBC}$sAW*R^DUL0} zvZPps1kVr=*-|P`&X#ClVKF-%f2O5tsMuVw?+|DLH`LX4*IC`xFKmY-t)~f-#grK{ ze%6AUwSr8A9$cpFgeg0D(m@@w5(bOVJ$bR+*=q)iV#dtyX)AKJoa%ANj=UN1hvd~y ztfNZk@j@0!DG`8uHFAkkA(M(FYPDJ@V4IXt9i=IrO3s9x;4LRYiB&&VZ^)|ttXS#Z zu(}ec(E64r^|h!%uJ6xYZ^^=6aB?MGR<0i)L2r4hm4EAA)_3LT4Hw+YJy%`>ef-~G z0_%QsKh$`*Q=I08Vs{Yjp#=WbhP+LDpHJ}o&d$&`_vWJ*x$O>!1B z^7m=PkDkI+XNnhVGk$2Ogq$*Z7ZK5lUHW z6104i7G!FoUY)2{C+Jiry)4lrlZ)va6XI(w$VSTulV$MfvKWs!)@zCLF%@)xXB`-j zsSgvYv$nVyYxImI%2OIOUJUQdWgYuitP=)lWDJ8w>3bTG_|mJ2M7`RiRVoBrVl2#= zwQ<;NnkeIs6%mFDP(%61zFcJIMO52apgNPC{iXc)C+i)#2Ra*Tx?7rhJ35AY`_E*a zym0#b=)g#OQ`?)bzXgK@awIZ>K!6np(Oe;tErc^bxJFUfk!02y5;KCx2q)0P3E;59 zV1NqoHH1KmATrjFn9)>D9GwSe@Zk)8Je`lE2~kubnkK^0#Q;-G=16@7z+%{{uqU5; zwaGnTcGZ3T?Xmji?#kLWcWY%=#V4f)E#UA%iAbM5u8g#siY!Eswx#SrCcEqiBpqw@lmk- zpQesir%XDC)8&MjN&=KsT`5)l-tP@Hz*ia=R~i_1C{wzjUg>r;h0gz6q5eeHUqH}X zeq<$DR<3W!!UuSA<=?uO^$pa#>*2lJ^X6-Q!|GkA{I3p;KOTm1{lAXY8^{W&)s=!3 za)Ym7wVpQDXmFk1rWXV~{YdKCMB|3!l(ospYg3a_l8uH0l}tjHv*9moqkr*2+)!D@ z<%ZvV-@M~$ed1!Z7;3heE=Eq~AxAD@`%YupGjZ-LPT}X&qi=I7Fa2e@+c7oNHayVT z)L47!)QREY;hw(U7hisf#bKweU6-+8vr=c0sErbxfhm!JWEDy1A_+w#rii3uk(4M9 zW7#|mi;H1#Q4BVU!A83|2gYgRYHYs1^Vk{lcHE+kv}mWzJ7`Pg z%qvyA1t)#76g5$d8OI44Nc^ zCPAk*X;mhzGD*wBM!_!aOYSLVPn5<^m&QR%odtCyYQ~QGNvb-r^G;xeF#T0(b?*Z#{{YbHepXu2Bo-Rt;*oUL^w;8nsSs60^u# zO)>7`U4zBSu@dT75q=1)T|)I{V>&Njn$M6bkMYlcCf)OPn(fjzy-lvMUiUzUyT7yh z=)S}Gm-70%`dS-WKKbZV7;H6>&B1fHI1V4f6=FDI)b&8JMe!^loGE}a1#v#yMpHRa z6m|@a8^_?onSyv( zP^#V)t3S)CJ~*{{JEnf$daZKxmZZG*lQ(n4f93%a^p>~s|Dk(a+?As@TyT%Kg)aM{ zjSVe+!|MHD6;iJEq*?!cYW0UU>3yly8w!JOs1Ux|z`NSOyHd}+QqR3y$GKd`0f#Qt zvzO{v3pI?zTKZCxYP5Xg$1l;gr%QgfW&66slyxa7>r%la>&A7-X~}vqpQ8~1ukEBA zdYd+CUH@b2j;jruFW00jREX#79Q!k~{LG6} z9r@!u)#Jlm9qrATna6s1y7~uuzS;LR28X4yd8XtIDr1^dW8z3vEQyRIkugLPnovv? zh)4nI5o~oBPp&(h zb+N9stFGDo<(|X)4xM&)jaAn)R8^JLSD(x|zSDKu)RD`bD5K5T2y?!y>Y@YesJT+b zS#+_cODWUEq{#yEST=LyBE9bn)}0mGdU8$maqPLTBy08IY6;P#S0x+O$wpA8ChFBj zt;(d)BW7TVq^OMz67BOB7?=7I^?WH}rAyPmMNHs}xYMDW+(5j?brA((%8`MUf zLPbQZd3h&)%)ZTQmyDNEhYGO+`PlwkTu%BT>PJKAoQa;k% z-rL&I)8amJ{LI8o-U#_H!WC$7?4Nh;6!fD)ix=)5O1qhZ9 z$rhnFVkBFHU$VjNwFBl9_8Erl&z6&gcf3r}9i?&=>pb?(yP zW2al1yV`16dK>Ijd2i?KS2Uhw3>Ffn%1FN5;DKvf*n-2B(Ni|ul#MlM}yr~Vq$I6ROWKfP%1BO7tMq&}BUu5w@jF`n9_eEJ&Z|w2U#{K!ePh~UrDVnm z4v3g4LQLc$MlN9o&JlXfkef5<_PwmkcPN+kY#DDoJKbL~J=)RMTyy-`p~@=f==e}& zb=6~!{{a>dPGyRc(l*OACZSBjld4%lIh`jVad<>Fm&DYAv@Zrl_VK;NhWooUYVd%8MP{-R+p&P@JXm`hUn&t@(D9x#)6!+z-O&U z2&-Om{G1K7=){6O9#+r_{yA2EM%H^}suQ?=6EMR%>p((rO}0AV*U1W6!DA)xj%>=2 zzY^AJSBp6D2CV{Ww$UmTI+a?l(it_TWJ7xNYS>55)5aZJrW}g#QtEIK(4U9t$-#7I zW7^Lmn@(X|he)$R5+N7@IE@!i=OY+`8v;capcz6mQ-GoJ=mI5y zE`-6t0zzV*eeq3OWqo^h|G}e~=gwz$xqCYs>bt6ntfyWp-Y0K3Lmw_CPg|~S1%G{8 zcwou~Oxy8b^{wl-L{muTEUbPWa=HT3faGY6Bx38@6iPIWA&CM z@SU0Z>#^zw$?X=ml$a0j8X1%Kke2-YY9a(=R zR(-M+9KEz$&t9x$&et#(YB`r1B;%Fod*0J-FfyLpmbNj)v>_#NeR5((vMDXmkeZmF zS1SY@a*7iF-jlSVZ`8|;PyDO>u`6}keyCr!SSj*afyrX{WMQ1o4Mq=~!*`z{x1FR_ z9b{#H#yS0oZm?$G{E)*l+A=!SU09HJ;>3xbo}PiB!K`!VH*DS!7!nf|idz|%ig+Fi1=6V?S*`dbC*^7-I8}en zLiOoX(s&+mG@Cqpj@p-n?>ZINcmiK~Sp32+1eY0@V3Z^nl*tLY1cN%kpf>42rs{Qx zS{aXzgOwf850=1 zYL!;0(ks;lr7l5ZTx&AK1i?OfMLF)=;C1lF%gDn;xV~I;*9D~e0<~X_V zOXi`s1t&h)Rh6GP*wWnJ+B4WSRFqxxk56{jS623RbhI|soz6PBZQHih0YR|95GI~d zQdfJ|XsM&-voH5st(D#FJ)<2ReO0-ohaa&VkhGp>^ydSU7P3#L68x`IHo_D*N9n{+yVRf|yAQb;4#i`VR87opDcXW60PjKAi%!sDhi+suUW9 z)Q45IMk#grmUlbDy^pP4>g z&$FA+uvLZ03b(6nb*n3l=(}TeGlzQXVUwx1?W?&bQy*gW?o9n>Z|dFgdF#9I2R_8= z|9-6ACt25Pm$n3f;ZX3kmK2a`ZA}4j;HIRd#>Ca;TRsK_|G%Fm2)G_^OGZWdi~kcXKHI}$0sLxdiyUI zl>XrluLlH#Bb+>4+yl_@I4quqCDL(ZCJfqCJR=21Pfnw46$0V0!`UY?I`P$4-z+Y! zt*>t%8JSV*>JA*tJ$U#;OKVqeZ*ONyU8DN4^3-3IN3*($Xw%9Rhj!hk!ftg@i@auu zzh0NPti#yk(Q~z^nM%}jMa*a+ayUP-|01&ELbUlj`HdH%643~;h`{I5#R8_7#}M&Z z0uBiO^4Tl_hZ_}$IQ~xJKn;0X6)~^zU(f_BYJ*^`F6u%z1$BCWdZh<|)$lE31uZul zWc7#0>OHU7HChv{KbQQwMn&g8RrzD|e(ugA|Av*(N? zt0s1=I&!cqq^~fjw+YT%ne3=<$vt?a zIRElMSC@5SqPxBAQhweu&pqqp;el{x3#M`IER)I7I*lBQCB>u? zW3d3G;c+Rx;i#Qn{)n9(m=w~hfBAbwjjs2~WOG~p$&>jfPv*6^cMkTpPxP32G$-Y` zPnypV2Fer0s-h;UgEwiqd4RBrwrUft+Qb=6^jOWVzEbC|Ld0OH$BZI;v?^jkP0ITy z^5sX|UVH@2#)o3jF*H1dO=mzpmBwU%Ig~6KnaQA`k*2VfZ`wq#nPWf!uo-nH ztZuf}Y?c9UbE~(~bo0(btlkQ?yWhDdN4Gt2_fNa^-3_ev@&5TB^?=KOIS+b6&St&R z=HX^xa1ZmR2ZVn{w+aV}x8+&C?IQeKSY2yLhOxTbl(f=HUTM!zo@JBb5WjnN&l9_a z5-v4MD%_n8YJ)Q+LWzLOp%YWk!5Pemx1WpApA=nh`@=V_&)jHx^m>zc#hhx_MA#L9 zc157II%Kjubi5>dxCq&wk7_*|uRW4{;?1a{uQL0MM;C^h7RH9h`v=susuQ`#G#b^| z*vRnku*qW1J#q4tS6?Gjm@Y0p2!sb>hbIEz^?i9G5Z*hT{Rsrt^UuG0^yq1g*3{A7 zdu3q6VyVwLa_k=;eNrQ@y)rb|)6>;ruGJQNRC{!vZjJyV68sYHR_2`+^UTny^E7-Kxk`1rNtm`H?3OwCH?iTEI^D&{eSJit^g zn zV&S3Rbm@z(G&hX(4fVD6mlsqV`Qk`LQAuA%$54MyOLM(it=#|Fr@wyTH%Ztugp)I3 z$4-Q^3u1>eVkhvjKu)gUI;; zcc|0S*N|r{{zLiUw8o3^!&Rt>+VIJmkZF0?oEo_aRQLd6bxxBwt&AC}@ExplA1rge zQtC3J3|g+okIE_MJ_vj15tkPpjUmMQ;S*wcR0b)Pz@jo)G&+MuXV9q}2IxoPvG5Xx zZ%0kWTs>vh7-zS{!a!ZesySxS61!A~UaCW{G{nIp%+{I`)|wKoHYdWm){?ZT`&n57 z!}Zo6@I$wHC+uxw=Uraj`U!Wx^AM}|@ToAcH)(q70r2%FmoNP?SiLP-H?ykWYhb;z zTfN$vveLXs)|Ay|g1tHW@Vm5ZUi2%!+5Jc+KSRLHl1ShRrgRZs#AWeV^fXN99%k6P z&tWvV!sUh+uD3k@P0QYEO&l=oPaAGm1!=FFvv3Z+(C*WEQR&_CSO*;i8|KYaM`!Gj0ODoVS$ zn+FCh9jzq>#o_XkFQ{@QZF!`LO0-Q8dK0DJ$Lg91Gp|D0er-_pBh#3Owtg&c~(9mk(`T{@~dlWjAm0?d*_wG zQH8>E>QuqW6PL6K{cv~NRDXSceQ|xohvmo9wP#6fMW~VLkSTf4bnUKL6|{a_4Ys9j zC#yDQqBd%*I&`?otG8rFU(wDmOIHj~y*2snSiKdcTkkw* ztlk|k@Vh@_8uhjxbwA(3)@SZQ(A&PX^=t3v{1B_Z7*=oF*t&l6XUFQcBrEJzZ;RD! zZLIeU0$*(fq2QIK#FeI`<;KLNhJ>|t-lXM;mml+ab|3M%C$h4|f^3m^w^*`QD%&HG zW{SmPK1ax;P?J!5S*W+3h*IWot-2>}v_APwYxdPf_G(?yye@QJ9qdpA+vGtrl|d7w zfn&vCgN0E&m(Z=}Q;lEa@;{6_@&=P;X%DxbN=+H6S>FoFJ927 zm94GKJw08cqoad^!vh0@SFQ|QxiT~`FxcDM-`UyS+}v7U-&j#mk(-JXJ)Kv2yM_h_2KsyZ`+5fYdk6Y^uMG6{_H;Eh*2`4O|i&Jy7a1Q0g^S?KM#1JXqpm)ueV7QjY#P^rgK%&+JAM;)3XD zscaINNg%Q)6egL>ppZAoDi%rtJP*toV|BGTX|W#bsONT8XUM2K zp58-$dS8Z&%iAMKha}xC2GQV5iBQO9(^F!zDZ5^jc-I~kPZ^(IYkU5BTlS3>+L|Q+ zOt(=-I8@;_dB|*4@N`AcR7J#OMa)Q9{6KMX$K_7k}Jv%K`;$&06to;-Fq_ejpE+!JTMK6(1Y*I%DFej@i+?(w6?j~zL7G$;4y zp^}2LMs>+hkHI$4G}2vbQRY@&{;=TW3m1=)mFMHyi;+W>Ay#E5Jh;xG*+l6&Ru{D4 z3z|rWI>N4un3IRkR7FfwAP0-WdoQ8dFDC0w(Vi6VpyHgR5(1A;mx2g5OU&nq`8*+) z1F*`WOL({lf5hdZl5rD#sM^b_^52G4IPeRTb=eROR|10`8)F0Nt>UEeFl4J%y3o1yLOrku4Wc4Hq!R z^J$m9NX~f=_t~4AQwLtG$v@PnE}HIZT%7Fdtk>3Qp3BWSnRDoL?veASPMkY+ zBLDoEqD%SbPoFxIo12?+C^zTe#nVTXRhQZu6(fD-kseD=W3{<9r{c`Z7r*3{eT6ff zPwgs-8!C?)s}3Kl2_CBno~jL>QG{95(8y(NgiQnDz%Wx`tj_78hARX5OFTRBorWqy z=5(aGGs&O*Zr5v%NA49wCZN1&I4qq&=a6Ya29rf4F~O)pGJ`^8QWz{MF)`Zv_2-i& zO^@2l1e+<|W?nA<-s)BtEwOMA_*Sf5Ye|BE`XI0hv-4Ka{%EFt+nst(rheCh{TT^* z+mE^*)NOs{E(E>pTU)>Oe$MMy{jOi|K=nWqxBu+p{d+R|+g|1t(ZB7zqrT~(-H&c` zQ}18byC|@}>7r~)wQjz=(MjB<>(E-(ja96vus4NKy7de-uqG}yCayIlE!QWkHl?j~ zWa{$Lxv7XWomEg<-4=jxcekPiiWHYZad!{y?(XjH?(V@MIK|y1I23Df3I&S8&Hr#` zzS(o~kjc!++H0@9^rs-h8~92vM_|#h#Jv&H{U!6qcZ1ZgxFH%$QFBzZP0fQnJ|&2| z#h{E!J$vuFEuKkXy_2}tsF&D@W|=R^o|-I z!eI&(7&BvOsi}%2%TPMeSkkb5$9)l!BIkunD0!>1bpmd#)&473e%xq#FUHYE%(W#L zw0~QG7hjJ|iVttC&9hn5!(?L!LeflOB2dBXwa178nWMl)g7JEd(L%FH*cuki|V$uV%uQL?3bDbOs@VNPiXTuB=< zTtEy#swCpN=x#L%uk z-(sbJ@KRmgg$>!cE!jo&A1AvD>jtL=PHv(E%laRS+CRbT9P5jGn>#|gX5{B}r)-~i z0YB7To@07p?}k{_R_N2VP*Pu}aioE`m3TLtax?KL+%!qKsuD^J!^em)@$LT3Fzn}#p3Ii&H>IwT0rb>OMZ!C<)0$$Y~<2LqTOtIMcNU~C8Xi`qi zQLbz7sPm*ORllI2;J4ML>U2I9uN9(@FJ2I@;nY8S=P?j4LP?27;`gaUg@CLFnRVIZ zJzS>mZmel**cdqduz@w8MfTZXzp2_D{)cJw;I~MpXOby)IYZ) zzTBP__1SzNMtB!1{I_ecfL8L;GkNP{o^bn$tFqHyYY_V9({DOAvOQb_07Fz2&8<4iizXMdh-mJzDu z=tUUaJ*K3n8l{*jP?Emg#)E^~7;i!^fW}0Y)Jk1q{fLUk2g%yL9;L7WrLa1hs4<#^ zDd~q=?Acc_kbxMfQki^z{M^U4tGk{_`dzxBk8fqn$MClF$RVv{3ic2Jf<|Uv@(cXaukcz`!VZ zI-24qLOGS4^2_mV(|h{}M|!;Z1cUMAM`CGBnREyh2jeP@qo@W*1)k})!~BN5|7xbZCnPGqhX}D{3^L@TDv=m4Oa50HXUPgc*psfgKOgM zUCu+ZA&r1LGHQSc#oDL3@=N!tM~a;d6oDL?f8aD8-+6axgjuCK0yaNHE8kmrFR3*D z@HIaEP-}v0>ke&jefxd8m9hCB+0~bhTHfxOJjZ)P(+o(-fO8+LSG}Nv!9r~(_kuxM zcMT{r9pci&wxNdVF1fZ&Ba9@j!nBtn;!GVS8R)z z$5U#<%J3`(ix_wbI@-)!g0?)u_tT&2>;!yeS50yz|#GXt@a}BWJp<+P*|$q zgYx;pZ^Dg2!tfQ#=ZVIAF;L^_m>pm=wn`?UqcjHkHHw)zmZW7;d30i9F~fWpw8f{= z9U6WRqI$deC5pvYT*f;0Q#Qf(DybonPmPJ>HL+(*!>dNAWI<6mqBmvq@K&x9`Y472 z#d42i$@0HjI>`{C6r&_?|5o3mL~|C68Z7Qn7M(#h!Up%Q8wo|;Dutmh%Dj0nW8_dE zL!Dy?kk)+zr`sGuhYOyC$V*iFK5tE28~n7nLhj~z3?Bg$zdBYKd&p1`4ul`2@xETy zCQpJ@nW9{_`OFeEx38kjj1#3;mZAC3*Zbe}hc-mJ%Vl zFo6eZZ#7*jX=c|MOG&6Nfg>eER-Yf?QEe(NYyOgjWbeH%9UdJ2oBAg^n&WR+DblG& z?fY50n_is~chQ16S3<;M(dDx}7dQCNQ(g3Qkz2!x0Dt{PpGMXXO;+t@mCS?~3;cFYw!0I?Vo_sA&rV{6)6Nxb9 zUZUnsCgwpVBO)%3DlY5JHtXI#kLG|!1&;|0#cOwmi8cXuU2=XPnh|0xi|;#~3bYQ@ z7K(_Smb6Yr^C7p3IG`rr*U@h=+exY+c|UQN>H=c@?(nb4E#4bqs_oj@3`g>h`S)_P zLh>V3vheHKSR<43)#Cg^(c8HHY6f6z{hdr~Cqhj~)tv~K1fe>7ae+)8CRmzbnKPmg z=eifs>m`E7DqnpSVBfxe#{}r&y5c?0bkHOJjiNvFU{Bynr!`g5oVm(6Gb1btJS^%o zT{E!+1}yV&iQ4II0}=!Hbm%x;NOCN_P`HxPs79t&jet{wm{Xg4OPy##k7!p5Z_$Ws z1F~v`UwjqwARP0+8vz-Owf!D%*N?ZE6X!OFb`+oh1o14n^7rm5nA;h;uT^9hhGxH) zW`~%rWSAZqtDZn!nzhfm1U%BBG9YXr6o3zJ8{o0;X;5u|Nw6=ffNr!P@zuKY6GS)# z>|2I{+O|LBWUa>~%dft)oZ#LOHjcapdXN`3C-oO7rwBULD`E#L3Kr}c? zyN|p4HHZ`GRlM0(dH6YO`)oCeH?3w1V++!gjz1$>SZ+$2YQ)064zx zG!6!XD&f56pVR_UccP1&w)F~_4yvdFX@`Y+r2j@Mv?w!%q1&}*XMNo_m)FiQ_3aKG zv%<9w|7Pz`NlUvIA&oOnk?gT1z-{GYUZLs=ig5u^S%5!R;I*7yNxS)y-!UF|`mN&eG_amfzfqP4;K&Ha-XCsk;U_hL8~HUtA*H7Q`&Pw7Ig% z+pbdqlS+|%1}sZt$qnje1xhHhJ3fV&;HkfZKEXNmKr*E)FB=aC9kOWF#))Fh|Ju@if60dLL4eYOvs zQpVteWA~V4Ml|OS{FLEekh6O~tRe+gG9>^q6ydcxvG>1q7b0vhDvOh^2OPj#+j5-Sw*{cb$`W0NpX64mlE0>=#z!Pf)@CEC;`Jd%_# z%4K460=G{<4eF5Ta^$W$$mV5A^2VIttVF-lG1#snfX_9e+PGWvgq<>p^~g3QoU#5^ z(G4+(v)SV^J1wsBtcPsPaOyyG*JW_?1{;BIliF{UxPDpRu2)CS!jO=J(O`s=H!C2* z3MmcLnRU-nxz<#BFTUl;h1?49fu!br=$a-)C`KU>1{5QZgh=!jI&F|@oQJvea@l5U zYSrlO3tY9ZK*5c6w=<@*YcCqKUs8OB*3={cCr7w@PBa642r_JEn>h-GqJn!M59jrr z^wC_cU}C0GeVZlgS}SB~UKBDq-8AVi;r*boSuP_K2{pDY5WN)~sb@EA2^=mVSc--? zoHYPkpO4%Xzm+f4=-ft0D^TtMNMYMk*MJ@Rlp7vD7 zP&Afj1}J&!6GbcEDHEpd-#60F;4X38c#<$@q6eF7QT8-cI z(`U1`jB%y9pgZY{=V@Tm9m$pfPEuk|Qq(?&Po6*wo155m$lAPAy@+$6j7!HP4e{4bSGgZ)8J^T%r zY;~PU$Kh1_w5&>0As90A!sh=WU(!F1CP)9cj2o)GuFXQ5AT@?9Ya>AtGE}YxiKnEE zLD2-+Fq33R(x7(X@l){&!QA}%nGFlcg%!yM)7o1wO00hAf#!CK4*?lVv1Tt~Su8@N zt)lxeBXT4oQUa-Zl1;Obhu#S#^4tT9+E=^fu1M?djt%M62^;Q5(PjmO#PVzuFJJei zvsK*?vF6EUWdCYxm-P`5tLD|-P${jVI&s3HBSmeBid96L<@ zIckzy(Js?0!C$X9pAG7Nj_h`g>8*NGbD2#o%Y1&V;iot(Ns2gb%W8)K2h`Zq-D|>z z^k;u5inxS(mfqKGqr7W!khHvm*TlGO&}CRsF>pv8Zi)_Ul+ z4FsAi`VNo-!(WNq5fC|FLv3t^qV?q1H)lt~J6^ZNHNodm4!J)8(TX&StH3i%V2*p) z)0=*`Kw31au?utKu+JBGR9Qn-oZrfcYs^2H_$?v(|3w z&Y?|^8lW-C9(o6dD_wHcsp{lLdN*xkFWnSz&46Pzu4?bE>nyghjltpse@~35g~O)I z7N5u{$>Zis7Ie^fh{PmDTZkvoOx=U&skaGl{w~+}AabHdB)LzvMsf|geDY9B6Q~za z)|8G1gkxmEK}bwTNPv(DpOhYvm>HD-2v35bMLo_`O2=php;-J*1Km~=o3lzxFXIc4 z5d}vkD$F>H$5xaAWFBw8F~Y|c!vmOVgMruqyhdXg$K_;Cey`{AloW&!$k!n=N6tLt z!&Q6QJb z&6)-?m4Jw5+hytDLa<|W4)2(l@F(ji}`Xs2Xu}A;_HOOm07vH9->tTNBqF9`iqmQ-rgd*{Gz`z{Z*ua(O|>~Kxg!+ zp=L%`z0xB~Bz~Y>TGoTly43P)R>$aBq{$8?Q#cs-)rL)de;AzE2C}?hl&Pa@S3T6( z#i!98R?2s(Ve3)vUwh*T@Nhu?Qcw~TDEbv}5XLo1yc*2t)*SHD6-cCQB0d!=6et?y zX|0O!6-6#pa+ay=GWJUkj76GsSaWD>G@lRA=U_u1hqn^Gsd`k{68=P#-K(HOJeTxEC1Yn+5gQ)F*TX`d?GCdKF!8TW$k56LQ{#|f^x zV1x*%nL1;A>~oUyXf53sZf;DXF%1)(7;*Je#uJ%(tpJ5QpeO@af5vb?e-M40)Sx!J zc|B?vdvDq_b4`Kf5xM-|VjqC*t>ltlzqDJ7 zA+x`CHx^sQFx2!sw9W>^+t$uPkTXlUWP?W#CQGZXlG5|KjkSU|BCj0j|He?$Kn+wb z;4Z~YvfDT$nX0CC=;g8LrE{!9;MTHfPZ|Uah5_$Ef9hl5DJ`IxlpgxVcLC;&f z+(tW&NEn%Nq$Vnxq{X=76vmIM#eCyWkT6TGVMd7PPp0z}O#f;~oJR|@gzA2;v6a0I~t=8zj-B~AN zfu%N9QZy|d>!{IMs!ZH!u_!&T%0+;T&YJiF^(Y0a5=wSuRqoB|{4?o7E@DMI4HZe& zpZj8W5-gH3ihPgaI=WZ|$fwI5ad7;tZDgo$^QcN=I^EfmXb)N?1llDAGGfAUnpHj@ zp>`EtY0a3)!sqwpn5w1zEPMU*gqVr|o}uEi?HpUHMAQT~+#p!U=~5t7Lje~E+705K z2P6ZoQ>ox|>rtej!)ylh5YC!LK(1-(ht_(b=1Y|SN`l>|~u8t_5t5w}UGSGNTjga&H9lI;V zcF^>i75ZhfahjAv@lNonVHneA%M221=n6J$p7D zU7AvbdayMbN`@p|YmNp<6jl_vw7^%_Mm{PcjfySiA-+$6)y)2#0;X*^e2|T0n2=j7 zstoyYFrC6Qqu!y&|8oIwt?*FP`s4)Ob<@L1a z2@8lx0DQRAaTc6QGzw%OMhG09*Zc4!d)-;!LwRY_(oahsM7QjH|vP+C;VKk*yINYDtsojCb_wa_- zSlcPHj*J$>j3MII5kuBIAg-R`5HW0!?tIQtbJd-K%pHSZfShvP(lY)1j4Z6v(C2J6 zc|?!WM3VN3?9y-2KfEa~Ti4k8{miqn&*G7#KbQ2ycykqvt{*~Kx*vOSd?bssf@82g$V&hBe@IM2uyF|cH34fhANK_?b`QV zg1rj{B`r6djI_zFDvybp8EcT1I&X=I9Zk ztd(#P9E{;&tWyn=J52cE^vE(!Bs98|!ODdkEX5jglnAkb{Td0JB-t`!T-+gphb9=M zAJZqy)48j%^nh?#pY1jn2Y_kiQ8O+#`yqbyBFBm`a&05jMgV)ut(f=PXw&XAB!3Cs;c{85x6j_>4a*W{cZ`4!3OmE+eHnJJ0=i19WzY!rQ=0@GO{d7|s(Nr$4w}Smi_qUqC?@q>-nVy(_9AC>O znxY9Df!vfXEyigaWKa2Uh``$Cd|@*9oF-^h|LC>fj7e)HG~0UzF3Z9YZYk-@!hrpCd?EOz zGp6~hLs_Uvwz z+_u{ZP}TQ<;R}R`%o{O-vq|n)M;IkE*XtYnG`*!pp7BS**P6RcF|>J}Scn4%?hH|V zS)=CMOvG<>zx_a;uwAA{Tg8#XpnZ2tBBWi6NDrODa+V~+$dXHS7Qx2FtS$e=VPlMo zfjTp5%zny6k|0;HWR1t4Fk+0Qjm8|6Gh{_eSNcXB>gC%*-K&rFgFJcxN`hlELhu#c%x%(`@Q2I{ryq4t2Jv4FO>2UAEtwliQ>;a3q#(i(7A-hNYb%_osBDrM zN~y4%I0b<$QBze}ca9J_s)Z8?{;&~KHEjiyFxir@`$G+cFsX{vNeP^^A*|N*x;6>> z@-0Kpy_Aj`^oSOh1pCh1O)WfO?feC!;Ip|cu;&=?soYAuA?jB(@U#i|FS+%3x?pPF zi&1E+XpiivPp_2C(JceW}(Z~n>iH@wGjRD8y`0$_Z03KD_4x!rt}6d#f@VI%6k z`x6N+G9-NsYr2EXcmKFM{P+pA|ACH0ZhD_JaJFiK{gC4D2CW^@<%ih$Ze7>V13v#$ z{@vu>7M0u>gpFyE(X=F@T?;E>z;Q^RXaiBIv}R|q*nZ&9e%nDK-orJ8k@NeyjIq8x zkTSVn(Ni1VLU|a z9HLrZv?h+rZ`V06;*@EyT_&@`vne&I{Uo5MS4go>Fd8}l*}^cy;n4~1vJx)`sbMxp zL@e~ObR}^S)XgfX4EPpPkEd9pv|^ds0|;^BU6f}%_&`%~`H2!)pLA(Xoefhm+6`$5 zm+yBx?OhsdrEn82mcTK7Elu13TVZb^d z-rN8LC#oHR1|0Mf?Z;S?>{|<+!_C&(Guk23g?!OL?n;Acpo$h+vSlPx*g)3id; z{O=?gN9J@fB)gfaZ3Str{jatHdEBIB%Qt48PO<*+IwKu>p166_7$Rok0){fq3XRBQ z1rsc!a4vf}IahsCeLOlkyf73wk*Lq~;RmVI(KTP>NFsAHB?irG;BdkwjMdi3_}6^~ zEEm_^DZjnXn5yYeF3H20CKJ0iQDux8w-6~@qruOYmA5o~u{0gvEIrxw3V&{W*^C`= zfS*((6eCeWAj0tb^6;nb2}$*qUF-)%$s=CLD_Y4jS&2}vNrx{_0m+JJnE5I-Cq8JGRO)AYQ&a~8zU5VZa90ZBH) z()<%usaMohrQCN3`}6o5bCj|8w41-0m%n;VZxVu5UQAO1C>g>)S4`7tw2{L={VJ~J zIdYS@0oQ&OTBl^dW++BavAhmPWrMCWkl-Sh*bmZe)mBb#p?vl{+7s8TguSmdKImUx zxfXd07Ov;zuhgQsDPI9jyGD&nDjW-$@Ei^kscsp%L)nULGzQFMz|>^AE60d_PBNlc zqkl;Dd>Trp8Z^E5#0=Kb@)I6$(w9SQPPiils!L6aXzOAlO@;YnHqOrqIoY)M>=5CS z7=Xud{VbNKFp-HOQmVlS6{kx3>QRI;9tdsGHRCSCl%`zy`RQgE>%|D2=Xf8{c3BF) z*^FD%3{1X^^1xSrT({|=dn!=xUZC1FTO$Ci2`<(Gm!Pkw0?_q2UgLJo#;`^dTO7#; zti``!^lB2KRcO6s#Rtku^-G5RDIN4%-0L&H)?e{sKG`O8zrv{B62QYM1o$XUdL?K` z#xpg4qFveUpe{0|L8|`5ELN)V1?PIip9i%8>o*6nJsm1#(x4@qHW@+I^zVU#ru7c| zaFb^#XuOQ!@+M+)NOZL{ZJi4T5{F-&AY+4x8LQ-6=OV}(Ghy$%?uQZMi5rC*X39oG z2~!G1{2qM9$X_6C)>PxplQC7X_vG!)FLeDtOw|>n2p})fQNjgFn=%UBu)IL2-cXf# z3J*C?NCquTUW1hmO~Ox+hWznN_}a^^ven;Qj-LlW4cyqll*J9;ih5mVrr!{Y(Qz=^fb9TEk=q7?+tV-&hGDWq(U1%3$DCd2Z*a_Q z?NN>{7tt_qa2+yYIjyeM{ZGZ)LH{cwKn`i5cvOIzQXpeCh%2m!_E(iWcf4{$gS+!DcB*Cz0ZO7YHlaa4(>v9* zbbU~9G9sr3s^*627bNBb)e3zv1YT83+;6~y0!UvjK$p_w&N0`n z8{>mc#BJM>kJPV!{cu2nWWyb)Rob%o)InUj^U_^2)j!Y^wfD{^y`T7vqQ<Ve2%|qSTf7LpSu;1 zC|%g|pKiM^bv>oa-!c$p7t~vA>&|zA3Ot;?pKrSC_8s>6tw|PN`FB8Qh`TBv6zFF4 zy*K>ZqlWj*FeV=`GDC_}qARTO{IJ~YYscZS_32y3GZs)dI_u%-I_P2ly^7*P_|1F7 zE1(pyHs~KRc{LQILhkv>#wzFt_UFGAsvTy{O{tBSkdyj(`*B;sw>+gyDM2=iTj%9$ zNB)%rIa(s%+U5jAmh2Rdz`jFrp90m-xwuWU&}-zM(`f^0#q%x}Dq+%V)h?&fb$kvK z9ERelCu`+$Y86T92NYI6&#in`)TDzvD6JGrGpU+^vu`%jn{}tk)xN2)aO9g(yvZ+7 zw1}5WI1vk4CS~7iYmQx^*+fcwNkdt-7`k&0tc)+B!j~BlXpW}G`6~%iB0>4z*t`%K zx}(W({hVkYb+LsTm>9%9)CRWiqtw^PrYTVV(aD&9%ScoIK3xy9)t3>Jj zt&9}RPYbyHjBU%8KW9<=1eDEIi&O?bD=`UNkT~USnrZIa0=fsafJ=}kA;fOG-o0kI zJoo|U<|SWrHjNgzHK>nrIb-kmcTA$r{vV$mhnu8!=qS-Z+jTlanzMsUmBkjz4YX!Z zpM{Bl(^8EVWrX2?4q8~5PX0D>jD)q=K_~D@fBBAuR>V5DbyUc!fZznAwzXxD3Qd8TQ$Z(U%3$}u!Oa; z(5*LO{0#38m$h23M&pQ(s$jQ9aj2g96%~8WBlBZitH9M6ZJUue#pGEo@ zk8AF7GGz(EGx(?DJWKfFFKk0B>~~O9zJm15d14p$Q8)MRZm#_G4QH4|ca%ln)ta|n z`4_&9x!WrPLIGbE-@&ulhpX9#i=HE}A8~a*R;E=@ieFI5xk&StNb|Hvvz}42bWqH^ ziqCbqz-EK8c1tkhb5SDL0g~1?{W5r_%v+OJ^cK0pc-O8>$RpN+|V zXPXKlRdEXNVG{b|Y86(Otan)&v?clR&#xVS;J#hEZ$o!k>c^^>jvaL)ce3Ug643Is zo4GT?qnPgN84h6^^6eQMkPH$i;_Q-n!-VhJ48TP z)QV+;AWoaT_&2_e18K02Z&$-K{FApCt zU+KeHB}ew_>jMlC%6pq48!;2eN$gSM%an_SKr|ncjtUat4_9L@xhH>y54vwup<=2= z-~KV}i5oE`$Aq8b{2kDQ!e){ekv%StJ|QCKbLXYMn4cdr;vca6dX%twV0on4I%U8K zDqQAv)a-i6hmoq8t{d8@bq_l-0l@4K0|$AVYmJ=hpK7%GC@LLKn>S!q6h|BpLYyjf z*O*Q0GMn(#CtBE@C!N#krllx|fD2%=$-N)sxT~GBKz#ym1JHM#yKlaBZmZ;2rH^h^{rkp(+O0 zu(?z+R>`N=)3Z!*QXhrY3JVT$7Gjbz*Gg0*aY}n^OK{9&U%%!`C;lpruu!8Z%qrRS zrP~L7$n{=3>Oav>Y{EjHg|T!GBL!d#Im4L+Lnlz6LJvMDvfMXApMXZd#pJ$ce#j_aZtwJ{^<2-gldUUe6^*k&R`C zwe!n#(#vuP=k-uPEz(Il^mJ}io7s3H&O@zuo9W0ENsm$kY`c0s zkD_}=zSCXj8LQDIw7mgf++#Jkr(Qbm#YVCV{^rD9}$%uk2z2C+XMNZVw#rZfHG z0_K{lkI!o$&aHau{UUcmq3l`OX>jkT(LYXS5kcY2TiU6-Yhw_V16TeFIVQey3TVqsnU$YtU2GF7(mowvy`uOt!O> z$&BWfAF1Udqqbv1TeDbZ=@jtZxahP0XUOiuLcE=o0M#&!@Ic$QmAVTtuQJ^YQFEan z>ES53ip@0KHea76@ex&uejI(oFT`ZU%ZO7imHL0{^k#_}sB#^(C)Up%Gn4nW$Iki+ zHK{d`w7WlL%wc_3=ky_rbLmWcFxT-og@W8zPLA==Z? z!$jlHScaXX6pHx80Q_3vBeVCZ`kQL?X%~sDwdh3k=myU3-{-F`g#K*14OZ~ZgM@$9 z3;(S6)8ZBsGA)vFn_-$Yh_hCr*0HZP-;7ceQyUCn@~k)p5}Qo}i(acm5wm^6ZBw7G z6|+jG!u?x}?6Xi#?X5=4R`D-S?J^fExq;?_;dWqZaG(~j1aXYps-Nj{66}(JR&#wp z=zbu`>D&ld>|-_P={+8iKhqYuG%2CVNrhOci6Ct3c#dwysX z@3y_HV7~1dx4e({fDr}V9*f?#>o=RSg*mSMdRUE&1iXOaJHE#(7roPZ!Z<*O^z-_a z^Vt<(!V1vih#S`h91g1hKRw3Y3QjSrcn^aw6eRTYER=N57i}w&Y70!T=4&q7?^NGe zEXX9IU)EBJ3{&c6Er3`@D@A`a0?x{}`_;RGst$1JpT?EADfj_Fd%DO+ns1n1Pl1@T zgu9XXP}9u|-G?a&^b23*HZ+qR`pOaB??8 zih(#YcH3>0^%C;x$1Iet`02es4BM1--J2N=JopQAnwPcrp5PC>x`y234)r65Jg>$w z*l!S_F*w5l->2z*;>XR`ZrGt}npE<$o_0*)?`a-4m*48H_E#GW%|wv_yi z^i+rx)AktS&UmfN?!)YGB%OL6nmYx{ZSk5lu3?nTjl$rXs2cH*D-*{0raQ(<6N zst>Fh(C)JxsV7M#OmBAMHc`iaFB^3XgO3iGj^2%n-W8DeDJcPI)xsKNrToL~K{Bfl zm1XBxe`~(Gz0lQxe}5_aTt8@L($zfb=~`)h`V8Y6@S*36kW0f`l-oVxcW4V?360*k zFHhe~@BS$L`s1td!Bnw^zr5$_?1DNDR0`z2I?7Ls>5F*JCA74rc?Wr zUFB3j_mV~B)Tr*EW&Inujsr?Z7y3r}RdCJE@mj?{R?9CN_dS&GqJC3iWWL7QRP!Xz zq^p-&u&yp~JO$^P9ll1WGk@?Y3_&CIGX|kmtULLG?jV-GwgwP^b|0>X8HPAD_1O_c z{)RPo+uv#Pbg>feWX3zjA*@M;-Ikh=Y8DWBu*C6a!Pno0u3Cs5vN!%~W`M1)t=0j& z){%U#P{>nDLu7QW&uZGlMTSQ-P5SuM^#LB>Eqw5?DSBDP$x=WF&5PVS#I7@kV@C)< zN2D&ny7(MUIjbYK$v{g@{_D!$rDH8`KH}=b>>|cX4Mn)h@EuR@9lo81UNQ%_aVln& z40-YB@ytzNMmj$?@sG7I^x_?}>7Ib;w|SeR(p&2v(~lNeH)3lwI1e#u4>7JClPC{0 za!EC|^QR<@rzA-^y7M|+YjxXZQm5>|zxjY3J#I%gT@`4ds+)G30Ecd<9-#>nG(9fK zuQ_I!Gwg9=>yl;{p;TjzB7^Fo4rA4PX6y00UTTM&^~kMfrdQzrW5AyaOLq1bv?eorlUF`y9{RHWVE1Y|VSO7yh*t+6OzY09nSTUo)a^ileTA znb0=RFg-PU&o!QTt4eu23Ese6d+2Mu!k4|Tglnn&PZ~cc>K?gt{l3V0h1Y!_t9cDo zze8sI6L2-z!6*Gb^!l2Vjv1)zea<@>6d-h?tXyIg0CRtKxESOQb6;gtVi?5#Z+?EH z_jW5h$w>D7ug7+b*B3$|Q+x`%iKHshxyf9Xo0lobxtN}lYi@@>rAuu26*K?qeGbD- z_E)ze;NPY-@7Xoqd9bfGBy0rSq3oK|>~T!R_iWJQYpQJ$fzyUx&Dhe|)dW4{kIh%! z7cD<;xrZc&I&>TN@R%DCo-IW2-I<_9Uc(0*3BuD5M4oH5?EdpF>Vy}G013@q6V&OJ zQr1O^J96WB0`$C53`PNf8g2-F##|9u4G_4D& zNAqpwgihGc@-Zv9Py9fL31S_^7ud|=0*PEnIS*q)#-QsB_dD-Bcq)trEGP7T75d|T zJ8p8X>A!nA*N>`h(*4ux;sKJUKRWuO=-&XW7x0|itBj3^1#>s>tupHV@XmnO4yd;y z@N=#ero*=^!L=;Gvo8n3?7o)vvX<|vmbl9nMR(;1O7}}&9Y|{2(T(e$W{$>1wfPjBq8Ss(Mso&9 zDHWX|6`L6u2G=$&_g*gN-Vvvk6|a^RzuIlP z$|bAHCBN<=yY3;g$|bkM9=b(65+0!>W zi08u@=C^hm%WkS(Ts=k9Z4Wo+F$&l1XuQ>J`!8%dD--QXf>a zruRJHc^&xn+wS%9FH{tVvFIhHOyjN5%F|ck-mC<}JuSqvmzGh7je{1`8~v8YuWOgO zj!60qn1&6QI<{yAwn#=cvAPb4I(AZx+~f5ScuX`&4l>W#wjr2`-{mtH z>mKt~V1Uj{e=GJYlcWAW7r;dY>zhT_{zt19`JQ6I!@KaVpYg!DHz^v=t{=>X;%3{a z!*W4;2j}U8y{DKM4vv@@0y5S=!*=#vJ-_^~lbQ9u7@YL{)|3D39>0IgzmUsjIz*Nv zZ~3wM{O~E#l9oguCL!u43@3vGz_K@Z1%zE3%RH-g5Z9R2G6}8UqWxkbF;gb$=MNKm8&Q zd}||eEFJjud*7Lt_v(*xAD5ovCtg9N{#ddgY`%X2e@FkM^aV-$6OJHzMs>Z=Il7ad z=vqsea*(<76S^!4zjdquS5|X(3H)~oq5p(a75*ai@@Vr;vH4kb)k?AlKU(Pd$RIJ}kcAQk!QlgC&GgK2N?}`CtnJr{7bE5-#gsszARz1&uP&-1>R z`M1OZe}pygzv+MCQp4r&WpJfmST`4cG?CLLVFaKt>kK=k;P-#}#B= zCDc?YZKfB!A%xsAki%rzr*rysQsT-pI33>-H@Y(1Y6Pz3XbS`UiYvrfiMdOQvsqX7 z#itJrg;|S>-#yv}mV@ViPrRpj&Ocb{x=9-$a8h(Mif9JqN9*Sfk#@gy)MT(G2hw{zH^I6XcMcl9+RX2 zm8=$>2tw}>ulFQ{wjcOC01y}efXfonS-RRijYY3`|J?xl9_g>l}8Vcy54wwFq$?}hc5P5V>#(P1LM zy7d`z4IX0+-u9eK`;(0}-%^upo6EiRAxRWT={U(+BJXGd@2DcrsFCt)bDE0!SC{4b z8%_u0t6awfuXlp%pQEq*J0!w8Bz}O|wo&#a&^YIk*JtKMf75I3rn4qU=xA$bfR^wC z8@hS(>^PJO4a8qsuP~FLGp^T1lmPPPaP5wUb{x;`9VZF+kGudnU6F3w33f+hb&_X~1z}vDSBpIR#JBo3IgA?1 z*R?ymS&v5|dv4@(c)SP?>#@3D<(x6_K87YAmrkUkcpg%NN+8Rc_)wmHT5+)a4w2*KH|$9!b3hr zdJ)PAKeRka;xmqPZ*!R8L0I8XqX@0g18VqN7yY@q(<(jmCB;*R|K8eTq1TRQlDv~o z@JiKMGG~yul0Y1p9|_+x;nBKL%Aqn1eIYa=lacsYhacCU>yyWcxZ4^`r=+Akd82(p z;X5XkMP+j2@JrKq>l;dcXim>y*Wh~kCo6#TX3w`t@urtGt76hH;_xk&^5PAg14 zd@wFhoZI+tX^-}!L|&AQj*wrUlwX5XNWVi&kD{dB2zisHQ-gvVWrF1UD$(~vA}>Mi zQ3Vl2o^cNIh~E5TCbwl!x2PBUcLS_G9ytT&gk>)%#g6X^fG80EAGk#Ku23s}O_;iN zXx?_ggH<@{Mr%_}h2w-H2Sq3BHB*Sa++B>qkzht%li0W=4`|Ct5 zD;82Vn--r{kX9BZBNu8a7fye(i7fx7G>~s9`AP)CJu!m9<0_`G&Bn>PJrc)uqm}n) zhxTe+MmhIIfd_ewG`z-n-MbQgy4_fZfO$B+GOwAdI)T6+mltebU;qF%m zIvky@R(wZzm}=SAlCoK<#26NIXc~0r^Q7TOcmW54wCUl)dYG@uSE7JgkS17`9FjZpRmEY#W{W zgH^MO31^EqkQgx^Ib7 zV6Cg=ZTmKs`X2rlkTD0MUv@VrRd{|_@iKC^Lru6je*Fy~>|&Ew(Cork4Oj+`SsIPz z1Y?;qqt_1WY@_ikpz&<*sFt0xK-srT=%AlZZ%IpU!O`=&P-bysId?wSc9U-wgKU=I zS%OJOPV=THyfum5TakIl`~8Z*+^2T#C-1VZ`Uw( z831;|s8^xsRiPhkwS2knw1uP$rG!;d2S2ZU2jB!BParV{Df~cMbdFQNy<5zCSje-# z;`nD z??NkjVEr1EIa(is;8U}ulD#x-kD+cvHB;|el{wG}w?)q>L9fO?jV7{%w(2XqEH@#v zLa%zDLBfG6-akHpZqI{mr4GZLac2l}AyvGe5(7Sxc0B02p#ax&`j4B{`AE*yObhRN z99WiV(TREj;?D}M@6xRk589kpw7XGtQ^Noo#&y1I%V#e2+6BkpHUL`MG^0|SV3tPJ zhM0BBQm*vdM|JwOz?#Oq^QpZ@O45Hybx2Zah{={He*QZ$h4RUQ?c#6AV z#!c7|7{?!=qi}Ps*$?2DZ<5{RgTMn)rVBdcT|wl2{ohx|L|L<7mK$!Q{tee;G1s0; z-7!mD;M87M6bBCoNDVpeSUh)#jt^P~FZW~R*x;6x+ro_9AxnRrE!}4eUm>qs^aQKo zK6!FsZUm9hyiv*5@qYoaqX~adB8B`53pzFFPuM42K+j)5?{7#X)hiY4{2j)!o7H=<+jmO+NJi7&${n zu+64gf5O-Qs5SzP{$hY`V!`7%QJYrL45ht^XT27o|G;&;RE-4krMI za~^{ifF`(x#|vVMNDu@7RdB1_xWr^w&SF*0BCUoDRF1X|X002L%fd9jqD8QeNp4-H zl&XtJsNTb%%P<#TWYA2(oaNkK!{SG7VjzpGHcgMAZcW5=Z6K&vlE7Lk7v)pqmc^cy zj%1RhWt45vO|i``Y@M3dF)xRwQ?jIRl6PMxXEG{d=;*bo1_c!E79&t}P6^o@)6jof ze~Qk<4U;zYg&+3kSR!O~Q}VmfsF14Pr#9U4O4|J_R;RtzCeU;KsE{*5`nn!DgH#_U zL_6WMb+SwkSkU3Vl+f)uEIXUltlUOa2Za?~OK@Enl~`3!85I=fO+$T5tZ-3Ndx5w> z2}nFHs*S%|DSle&rW8)}rX9&rjXULiqah`kLz!}Wh-LDWx0IC5rdWU2qc}5y@oOgZ z5=rK%!x!%hTsAp8XDV>97OSX?8-7kb5ZOoI*yyvYA23_@D`KRPO*m`MEx)tH#2BW5 zT#2!;K{7GBg^%a#%R|=F74H>|z!fyjxirB;aFRHb7b+0@$FmdD6;;mWR!=tZd&}hm zOh_Zh!2iH1>?73vI@Xs^=nl?u2k(1ab@FduT+Z=E-E{fF+!5}1DG6$m=kwNfdvo(9 z==)+%d-tIJj`};_lYF94c^hOiHaPzO=qCj?9f8G5@nz!aFN5EScRimF#1u3!g{ia}On~ z+7CYACWTQb3qXgr!ZSLmZMiA59nj3A_QjHK$;Y?VtVot><*EKnrv;%XqXrhQ}3C}bMW?*repw%&?fC*WJ_A24L2*u@4N72&DVpPQN z6H)EpK+eg@<%7Q)jGY_OD(DwVlF?gUyS4`#K6*~?N%(n5$W$+lR(YV=gakG)(3}E# zWx*xf>-efc`p!;9r9`mL`KI??v%3-9QMfVu5Vz{vTkz&*HGnTz zZRFEN0$g2w0IjhMrzr@?T!mrpr}?dCBljA;409Bk+<-fzkos)Yyo2%M1x`slUz6p3lT3?&+ctVO$RP!qE@s%;ATuOPdSg5Jo}426FROC{wacc;)G9>NS>&$-gTdX zd)5dQgkMNozePv27I)CBWZKd&^)H1^T-v4?+M4ajvDBR z96+w=N0Qu={1s7XQ$}WUQmC|GG7my;)TA?Z)HOF;rwr%ll?9C-?%tBW{h*m-+VDyt6-25~zgCV?(5 zt)byGAm%jSuJZAd0~Ay%_qx`Z1B9m|12XEqqVh8;NKOoIQgfe91rHGj5J$UE6xUJr694eaK&*cf8E-6 z%=ppvJ|n;GbH0gi2h}3>0USbfOpCE5C9v9|n5Gc2!KrXuMV%NdYoI4N3)A~5DS<;j z@Y>F&2OcR!SpSmUubg=(@dB*{s63vGpvt&fP=a2lA%+#_CyLC+W zweb6fB1`!$o#|MGc-gLYCa!zK)uZ*|0m|-xQ0lW&YKyPWuzC%~dpNlkT=w~6lEl^P z$lg%k{nrh1t{-mu4ZQLrp7Lv`~pc3kM;I_?FOpix3A$KtVng| zz`BtB{Oh>PlSO5_*YZtoG}eV4-6l2_h;?GRFIpG@oFL+GQlt^l>Px}o6IJi$a_-mD zmz>4hZVNOF3-sjUgrs9M)no7o+;S#zp5(l8xep5{O6sgQrJM-$tk^RiTpFQzZC`q2 zU;2jkld~GKO7bwWvY_3A0A^!9?j9z@4Q%?&{mIXvpih>CdIYPDh$dhErmi^lYcQL0 zSgCh~qW%Ah?#Y55Sq6YHe+5PAYWZ&80FlDu&&#X}B2i*W--7U~0e7|&)GVSZopBs`#A^znijl1g+og85E8{iX6}AV9(> zNeRacO*~V)-drh5JnP3k9*w$l;m-}zLDnkwmY6ZnJ;tfr))-pof+#)5_eQ3=c<8xw zxN|OHtP@JQ?s=KgRq}FIE4=^IVJdgn!_^A8y&_wE+ZbH)ShO}WIDzp3!!u5-&2uF5 z?2(aj#{W(XB?znM(gcB5bZNN`YVjduqqH-@N%Ce>TIkFc`*H)Cg@z2&=KguvyEPfcVZR#&D0#sxM~(bbqyvoJ9=R9u zbcsw*GN3U9^CXqQEQK|3^LoJUqLVB>F-v{Ce!o3t-6Rt~pYt9u@tsleT*wmN7dwtG#e+4q7TWbr=lnTln!!M71WfQ;lA7S<|Tzhd{-&g~87awxm_B-`Ku{KB7 zIdnqe_HElktj4tK>{+Wo#r9K~=VYNHK6Op?W~genhnk1F{e1=S$l;bjQAD+ItI@6# zc2S|-Ae=dZ0_Z`|g=$;+ivRsSsvphk z>eU&=v-%WIRk*B2RNcw1EhwgI4UA~=X0APYj!z+!y7!V=>{Q%{rz|^i>+4S;6t0d2 z!tPvbhSUj)a(AKvEy4}sO5JObTegwECsMh0Bncmikzb`_{ZmMoUlEp2CVztm&@+>y zIrz-f*1{*5{9Jhj-P{pQ3RI*s2|D8)Fic`M?%2}X?U?x?;1v>pZgfxs7STDgsTajO zLL1E%wO3`C@KX;N`a%gm#d?&DRMN>-tmfxzYrJ@029(Gm8ot`uS<6o=sI1~x5{ko$ zpVREb`OS(78OGak$`TlGQ8p}z zrQq$tKZ&KM({pupN0PmW+0~;Xm=1Dn+*EUZ=+nW6Y?+9(YR^rpH!b~QY{`2yXOE&$ zHOv~pVjqTECr_mAr;g?FZ+{1>;GVr0g zHT#A)fw*)m`y?v@vA^%(awWm*25FPPLfxMG>thzRA3OX;mV^bP1G@<3VJ?dQ z;3xFQWkO*dhNkOgYB@|wF8n`G`BCtc$*pPY2gDz)8CV;};0Lu(B-Jov09b#C9$k}* zaN$onO2ZsfkZRlK#}+%ggvbD@4M6VLnvhO;G;+!5*JG9%%$2n!WaB!$t{GNM!;cka z^>Xp7u_D5?Iz+w{lTkGz7xFR>v+?)Q@tSYyzRuftD2*Db~6ix{XS`||YDH-zvpyIEg(}1wb zp43K(wE7ofYj{8H;8LZr3yoy8>G(p>PM~1~+=BA!|&EoIcmjL^DxDQa54OVN|%-pCwK%-zE0~cydg5Fml<-uy?W~FvY+7&5t{Bc|KWRn8NY3H1D#yTnZfD>Z`y{n0fG>TT%K=1BR zg-sK(t47r4{{+Ttt6sXnnpUys^pC34#!UD}q8C@=$wNvq=6=czcS|RkVgx>z1d+`2 z8C32|O#l5=x>or=G)ai9E6BVX1Z*3&l$3b@c>Dl7?lm;#`3oW70T&R1dF==L{EiTa zb(B6~Y&2}K>rm^DFc9fEA=p80m5@e42g33c|K7aw&UC?;J!YAS%>9k16`c_}f4xg9 zyzm|}@a#@s6jMRsw6F8KWdCr6a~%Y0Zafe%I4OJ@>4kv!2t{v<7z7s%3k?5H?u_9f2r2@Q5 z1m3CWgAgd~#(m7WRmyr)0};54`&DtD*P)?udaZ1rrs(H#(OJ!YGGX3fVl;Da(f0l6Vyh6l8%bPUxHLRB`-W#%pI$oQL_+E+iQthXU%A zK{Yau|I)TcFI}6X$Isx4!kjT4c5+`M&^He=KGVm^g?HM5WBe&nIEO~3W=iT*Z5o?A z)ew)X{%cyUC?g^UWQuUV*^Evl*aySSPsC_cx>n(yB5!B>#}%$|TT(@KEJOzxOh2mF z?;DyfBAw4MPnRbXB6&RT>D7!-j$&F$k=QAa*eT=OOlSW_J$#@Q)(w$Rb4CQDA?PBg zPq@2fsU0Ec_RR69IzB#HYj#WDOm%jCzryNAmAguh3wA(x}_Qovr=CFA_KT2g7%BR&@t=OJ0fV?p{n7$ z7E9RGN95XI)wZgnf3MwdeP4W@RvmPzpMl>L|A&n;rb-xJREqYXniU@jYPBv(OGr0} zGyqA~43lgd#^5g+8hB1qCL6qt%Dl|Sy>QpctD|)_NE;IvNFE^xdRJs@o6#~E+(Y(9 z2XW#Y;8X~IsM8aZOS9lATb4*?8DSLTi=Ub+Whng{P<_XNl7DOGkv~MQe+J&bADKPGaamug*(ztXi{Z8uzLi$8QR%eO_LEwLB+K zu?e*i;o8Z^6G~&?=nafT!4}C3?}_@#uojYc@$Z=M%lFpfQcEU@d9{WRwC1qQJJAgS z9D^XdWKB92O*$R`kqr+iIRi^G3rjuwPRH~j>OsdukLk3sfX<(cZIx8G_y1ael(L2Y zv`+IY?w~55x{C=ei-o=DQ98_r*oO=~jhQt_Z$zQoAwrp6kqz!vmSKdUt!*H)T~tKK zTto^V`xOETluMpQ=Bw)%a8!F-p#r39s!*6o!!T=1YQdhUsV0V^Ld10=Bz)1x5=n5a zv!&bm>1Egu78wfU=)!3ZR_Ap2v<)Q@%Rm>wE8^spr(+wr|5kE``h5l5L71p|4 zBUamq%wuywjW1a>Z+W37j!%07S>i7GHCD;g%=v-*WsE=XBl9y4a0*WtTn1FsEa5WI zd&;SDmbfqEdze#_08y~qe~bQ%?IAJm-s67e_Q~9#ftyQ5yqcf(?US@LT0M4mRP+t9 z1fo~WD#RUyavo}5JFI4_=C^xOLscWb3-1*2|8#@G!QOT=_ zP~8(XQD85NDgHNT^nGr-%YjFkVr!I2ElN?3mt*wJ_EXM;We$oGal-#ir1tLV9I73r;70tr0PtlaVYpUBRf#>B{sk4ZF!@}1mA2p;={$b~+`4oWr1k*DVm zISWifb9pvfL^g6bM<%ik4&?9Q9|jiAO#rxiT41W7u`8k%T(dLh{!f(S&hl}$m!3I? zY{@+Llm^d?>J)iZPodi8l#R(f&+0BdY|kAn^i#iF;~E)P|MTRTQh`yjqpSOo4Z-dL zk5jeB%GSF&HqCY07&Ac6r}knH8Fn-V%dpz*X><$awrKdC0USX&9X7lZ6&uaR%rbEp zSRA)B%jl{W9jCz;y_Xn&fTMMZ)UPc>M|AFr&<4r4gyDG9&IZWKz9HopG7MZF=@>yGI3eBz#YmRsSTC6zr;~muY@@F|AQ>{&4VT@E3u%qL9q*z;RxsKV;VIr zWS*y%V~0bNA8;=ufw8U@se?gtZ8!PvvKsV@?9txg$V-FkEqH+BZ? z@ZNYzr2r~a72(2^NVO*0^q|d(tf5M*1NwI}3jXw-SkT{U1r(nCxa(t6DZD5Tiz}PW z=ehb;&fd5EvR?kZ`g^V~s@&&lKgeC5JyL*UB z7^wWmE2F^|(}mIBXRh@Lz1Ph*hF>Y5>r?08SSNmp`XI#MmEeP<=2ew)O|{A}6%qbU zVkmuoFB+G1TR0VG>=y#u#BMdSwOOf?xtT|5XH<=Is_qqS_okX4RdrgTy6)xmD)_n< zoC=L5xGYRQ55mWYu(>T8Mw=Mewqh(1b_gXqGVN&aQCgiGCAqnn2m%6n_)?%D19~m= zR-u@5bXIktFeHtDkkY8^oD;fB78D_s{%Az5(I^il+hEQLJD<#`F~%vVi_}-pjA z=y{;MlRv_TXhJ22tU2e6(}0d@ZXzy=?2b_d`BU%ivsad=lYY&3#1SN<>MzQeX<|_y zfui!t(8xo_U@56UD2s+&jZW5ym~KU_bOZO8yzT?^MAd+6!>R}L`+ySXJx!NZvs%0M znbMys2$GWw8`@@5>ZykoSgGmaQ(b^xY`t^zQyp^Hehlr`#0!#B1=VELT9Fx+5AITn zqrg(&jpsGjm_bhz|1)>PC31ab_djMRxO?GWUGYDV4_>J_mx(>I%06Ggu8)HPCEr7I zs6dlZ>|4PUYd73{s5Zq3^bZn^ zPaH|4X;3z2l`$Ka%qfZC&9Uu6&dur6jmQYnXJ?AylqsDSH86FSg(6O>^wm3h6^QqLwXa?MGh&13zHy1zZTyCtv^{D{5l%AmTTwerQJ<^ zyB@g`nLxR~mQ|uA>Vr%9O zv07Q9sybTpXd@@4cM%t+JTI7hs^E84<;HUhKkgQM%wpNB=$St;v62(+E4RQ^4buWg zq@hmm%tu!6=f%3Y)tO?KBgh^>e;14&WbmRub&} z7<}dwFlT-!gVcHdF^unD$Wd0Dv8w4&o9&$as2aF|{gKK&AW+Qseuu@`ikRGq+=W@S zVCGi;)=N-1#ZgP9aC!vgY6`>i*%w7PP`8y359ydX1+in)Dfj0qt_JpU$S1K%R9+9w z;a7o2>vcI-m+SZ1^Dr^lZy#)rH_6IgJpx?J@S$K>sv2#~a1zW@>D)i>CZF}G$${Bq z3(qhS*r7Muv4-23C|hi#W0@Ewza!X6htd}4zDz!* z4lfm*6I@$V{%D#59$)WYE+*=I@ITsgeH23N!=mk#1L8hM3RM2Jvtb}vWhgeouN3}MZILsl0kON0tFzjae%lLD}+Rh4*`dvBw zJU8YDE7P(`h%nxaP(=z!Rp`}DDv=5Y3m*>M?w^iBT{TKsd&|YF)6~XL4I{$eC@_h3 zbXCa{j@2a1_>t{_4o&bzZ%aYeM`#FA$|ebhlftxBh5D7z5fID*5ZU1K16q#yQz90H zXeFe?X%0sh0I>BczR&J<&R+;G?zfLH4uJ{;d}NWCXd`h538PT>qpv&_DG*&1(Jp6v zd+VuV%b$Uvix=xrYNN=+pBF&__JXf(wK71@U#{P5@oF0bpc>`kdzacMe^{mW$oVH_ z#%C#CASj%Rviw|GnARwJ7il0;Ge;?nt54|1d1{eQ%<7`C`%*5MF<4@Km5Hi{M?_(rrEJ>})2MgaawX^GR@2S5&B)Z^mb%_KdAXH&si$Mn z*)lUBygi{+nuhRWgaEb-Hzhgh+udIz_6A^?bIzk1#>_iRN}m|JLNUGwBM0> z(1B;rmOfDY7a4&REE-|c9Z@2x4Nh(ds<1SIp37_sTMP}?oYf~@Ziq4r$G5Xug8s3B z)vz4KHW7TXEhXChC?>=VmvF-2$$K^|d%L!w;|qYmcXr5U1)K53o$||EVtRAsE2;UuIqdv*@3qRAw4UJ~s6S~bHO9gkESuZ)jt6b}gyc-mrN9Be4!b(C`uk3Yp;Z=P+G>pR= zH=p#zjA}WS!hIwt#)*DkNq#v008?i!0X4;3jgnu!f7nc~y${EoE!&Yry&g8LY5{peM}%E%@^*%0L{!rlB}1zWtuA5aC2NTn z8~Q64sisECqPKqiqQ09?JPXz(zZMNjvzg+bT&6#cx|{Uu-diz-&jSASnew_LOCr26 zCL>~utnncTVhm^dXK9qvs;NwPY>wFhaC3aHFqMCu#g%~kdhf(1P$gCcOu_pzhIk= zDTF`EvL~|0M1(Ph#L5xPyNdM^%nI466?l-8PhC9|V`LhYtm=Tyg(6uc=%#Y_jUadO zUtqMLerQH^wK1kNUX;zV*~gU<`2xDuX&k?!J@AUKc1fMcjB7=yEU&S+{&pa}(ct1` z=l8b9^NLlNfmDsRllY_p@m-$Z3XACp0jqk99ES%!FNA#8k49?OlvQUf<(Igt_oI_( z33iicLZMBp+CMr%^XV2l8YfT`o<{X_MQ3|u=g0|boBCC}0^>XPcQNOT>eo^~&k5G$XDi^luei~&khqa_Q&a8U(gNJ(YqZ${jU;?U&P@C2@!3xZ24oJ0ehL}L6@ zA3>QOf#`1!;fJ=+R<-FF0?EaadKXWApco}iL^Bre46SV4`0_@PmAY0-cjef!n(=uB zSVGFW>98TRqfZcX?ipHT(+??#o^!%BmmbpC)^J=|2$k6MuhYB`-YIEUN^(f_WeUZg z6y?l|hvppv@`pELF&gM<=EikUIx6Wjl2OFmBq7pXj3F+PeWeGCl2O{LWy_y0YELV6 ze;eCyhy^zQ8wh>r46)v-N0K3x`^TwC85F~cA?D*?Hhj{pXk>aw4S2XLX$*@YXYco;J_d`c+ZtCm3xM7Z3(MT^`5r-+v?` zL;aGPlG&EW-y`~5?k4VOy4g8gz0JWYFTZj5; znZqX#3s?)=oXgA4e$hEyPP!Wq5Fgv<6fl}HTHMF5HSe`##%a9fxp{pbW7k{<=kOQC`i#DWy1YsNzQKP&n zsxCHVi=>xc-U{~5o9_kNlE1^cUpD}LAN>=fEn-P#RfuB$h<)Azx`}->T|hfX>ZPX+ zWff%r#flS2kv00sy5*;8&ou0K?U6>8igBi@q#24W3S~3$6;BG(K%!J3f%(TF>aQ|& z@9Tegrs0_!v{PvVY*h8paO_Ta)9L+=F77!w)a(f!ShS-u7MT=lSXQynv3?D*_qfv& zcUiP)$yhToQF`-QIC;>UjOGqMD}UDW*^pn{4AZ zXTXbcztNmKGSX|6@Yz$dN!*t3+U;2JDd*avT^!d+|ws`r&eL4)m^d+IQbuA;iYo&Zpr*{gt-=$)uKVo z1LU+^@RQL4tmcK-2bs5seA!Oo9k|?WLu>0#V#CFNq49sQOy0BI$1XR`J6p5Wp6|#? zeqTxlvqX_=354}_&EJc?K6`zootYI(4LUl|^syL)mo$vTut$C3uY`>6Fkme4PXs@1T2E8m zHWyJ=ZkAYy-p}%Rb9E9%xMOL zR?-)a7B_V&pmuaNdj<)F8?g2)7sm|uJ6Mb@ayU4h_MxxnG43a@u;|&eB+rE2%C@3V zij~BKUUOzcTNHy+RmW_y%a(wnX4)Q5;$QwtmSIz|qGSa~*|P8W$ICsX5b4y8COm51 zAzuB9m73qpL|mn0tDg_2#GB*p<~En;a#beaB9$S;G`D=^8T{B2xeEGka}>6%_v-jV zYtTb{*yH#3dsC{PFe5i5cFrl9n(S~)a2p|dy%K5I7SQo*_15D1Ba3RU)6-1#?5&8{ z`+1{t`@Qb)W7wC4Qyf7D$^QGKe1L3rWTlH}hFQ&Hq4YcJSh2YDso+?yF*gF!cvn(u zzG%`ExUz6pYo=ps8ZVM1xSr0wnNaXYI@c5vf-l*?2qh_V4xV~WX8aJ`HGioYoLJ{X zu)TYkHVD8Mw_pzhc$exs%qKsv+5?-_?F}!0^y+%YOe?l}L#k(AhXOab1GIzMD;BFh zCWEco*a-k~+OTz76pZuWOnR}Stfrgung*>7ONoD+OL*H(4ipZoWtUFZApIt>JIBf5 zu}07R!TPZ;>U-vB0;ffFTd(CH{;ZJ8-Og7@lp3MD{D%T3&Cp?jU9#?Y+3);Q4J1od zNIbP%?Q#dWE6;cKZb&?j+zcYss4&cIlgLo;vm)(>wX};umT`QOgGH0oYsz{m%6ekC zg6)i?R5s4q6$|$&gy-X@eIs!p)1X%)LvfU%D7CrgB_~i0=TF(+3@!C*)DkRFZvUDGxe9qnN+7x6KsL} zcln#b4)5ydQ9yG>ezFxQYc<9XSJuWWT#f2%ncQMF^Hao5C@7I^x8k4*Gj}BjAXdeg z6efOn0&amR9#hVUOzB*Lbd@axLpDn4@9@dd`ADTa2C~%@rHcTT2c_zrU5EJ~vcPH( z{{_Nx)asJ8{ibl2qj-r>exXa=k;eR{X#N`}^U|*Tw@E{Djm-88%fZw*9*T^dTOO5N zUWS=PA^fjyxC5U%=oqNq?rE=w$BRUbX15^A)vYA)>%$hZH8LTgNPEx)8?1iUag4)dq`dXa!) z$r_tpU|_Q+f+h$2B&du@C}co2)}Wlp=w{}(rDsfrQ1V(H#Ncpu_Txd*@Scju0(i^) zA#sAuB!2D9`}Md@`Te%Rf}C3AQS@s|WLY*!(%%C&imwtzV0^-v@NLIOtKZ$=cN0J8ebCw0AVZvmb49B3QM<4Mqepb=+!2UuD2%Jb zqGO_4Rp1&`?+BWWVr9DJ%yjdeY89ak7KaT~ijn4rHOdO;RObn%r`B~9hmDG{xka&g zrJSaB^hv-}d86X-Z@`|aoS(d(V#p_tlI}^EQLqJvK-W3Jct&QDO#aS^n(__>lcpz+ zHSrFl^Te`j{lzFNj3GX>%;}SpHW{(Y9Jfs?Lu`nD92yCcSwu%V{cGreSC6riIjkJ5 z85ttSv5WtpIf0mwPj&axeHT%im2{JzvCCfWr7!$u7j>6Rd)F-tN(cw-!(H*Fk+i=S z2D%p)<;y|%rc`8?4RQecEtu&sLjM}QJ0GjN8r5$u&QH+)8b9x;{6=7Fk4da5P6yAm ziQQ*&%^_`)hhi_g9~#?INuFzcDXN*4S)FO*vR{0Qo0NhkBSqO6n+&lSPE|Cr=tIHb zF#Rkw$7>DtRu8l17Y*mzcF*I^)t_3oEZ(_|8@0=sQK3&C_e(xHtxj)J6(3k8_c+~$ zblaP3yW7QP=rng!b&HGzKj&>WDjH}jnYt|VHVT&}2DeZZ`1B3{^{Oq}AM+}}xk>V? zJCMibkx{)iPWjmj`_>VcOJqO89<0)>h2iFItG(5=Wb&J|FmG>>-2ZF^d~k`y*A$~JrU!OX_4w$^xZ>fCs$&L;wWeJomv<^v$ zNi%os>GJ~Z&T>y4hx{=9a*`fp^jzy3!p|3*pqEDNk2vjz)WVbBqr*aJ7(%=h-tICF zeMv7sE2ZJ4Hg|1ZZU)9QiTjdy0>X^i(Z~nOdT=^6W1W~)0jvvOx~G3J?kGPxXJL*m zZJT#yKA1Gm;1R_8bWvN23^cTDQQ0*Ew$tJxCc+?s{N<7c6#r zoH?UvOTZW>N;?dJ@T5QX+3gX`N4qWRf1#jdfAKqIZwgRvG zt6bDAA%-q{G;2}k?cfv8OwU}|i*FgMsJcAu zP0sf2#k>T=hE+wywfB-=cOT2)Rd)Zs7Qj!V`PhC`-|u9y6tt=?cE<-z*yj*5p~a?p z%BH=~2l(M{9h^-}KX9VO?-I-SBDr!jxxhLnACbtvi6ykz;84V25vgXoW1r5YMyjsY z-Iu}}KqzdMaB6n|<@){9+u8fybssBtN7*S_klOfQ3tt6af*!w+%ki>d`-kKfz5Ld! z0hU&(=);?23dyqQmJ=bvOCIBdk4tl-n{4V|BY?PiT92wZmIh7;iD1!wKne>C`!J() z?`HDPF`0wk4vTM@#s>h@gL1`X{qHC~pWnTWdvlpAx!q|WPb%lRUcWAP6Vx)DOuin@ zi7&mMFB_?5nmAe<`M8aVWb&JWFH)or1eDtgbdbj23#w_8b*nD`)LtC$W`4-3t-3U+ z&QYs@EwT*o9bO=#cXb*6Q4v*isGMHVS=pMIGO5_EI>&JMB(>U|FhkVQooc71JT21z zo-(Cw6OQ2XlJ5I41meh|1qc^Bm2`WG0G^l&$g_Xp*}OzF?PJq^)v_x>1{xs2 z`*Bcz0P1d5$O;T%BK>$6zh2u;s1gfCslV#sInus`?S zA5XeroGZayu)s|YCAv1l?OowFmrO8)QS3dd2EbHd)QtP`ZjAk1tAyh)Pv*P{Gb;ac zvKZo7EV!_8IBMc{f|_*yLeyrDN&a1^Q&Q(uG6zdJ4@)&~A7iqOCrOMe*$YXV zS|=%4cWqUFZBtJSmLF#`Pi!@xYdI{m80p7C|2ABAld9NDgvI(cU3bGZ7`Tmu;9H9+ zSdRH=CiU|1@1&S?-X>`859D6!`_T1cKHvMCp3mx)-~~FzQ##DIXw3Br3hdihkael) z$87uK2J+R{+V7t3Ba1k(&&Olj<6d{=egWoXK;ES_?v)+>g&qFC*2l-D$MFpgf$R+Z z*pk#j<(nn=0Burb8pY-WRa&cLnG%Ryw|1GeN(evn?lYovQO47&6oNU^ATT%nH`Rp_ zMKTadVNl}u#kg_Td_jZ|&o-=stn8<=Vx_Y{#S0%3SS``74%s_kR1rC$C)d1#P^r3W z@wdP9ab2_fzaQ=IO|sho|0_xIxxG@m2rUvOwzl+w>((OA7e52ls~9;nYa`VtSysu{ z5f>R{Ts{c0Q4UOJWEyLoG4)Pq&Bu6G(Ml_H$ON!~?JK3OZa3|Os9`PDuNTEH&tDz8 zYgEhMJ725O+}mNT7+nt!xyaWWE4tf#mpiwszXV|i#V}%&9Lwcst^;y*1^nrpR1z#dR33t6Z5HIE50tg+Q>!E;++^V-1e6;jWgex< z^~;+y)`z8NPL-8elf^1ZtT5#(EOq~j@^nydEhD<*_x+lytQERfRKi0e4E%S2EQt|D zr6yVO9((o{J7tOV`{|1C^37SSLsqs|NVJs9jrh6G{TAl%;I_YN??1QQZiD!~Mg6v6bFwWt??)`+#}oLaOy?^~=WDF_ za8J5B8yYNxi}8WuF7qmcckuo9-}fMC|9;R1F;bLo1?IPC)R!mJ=O^>q0rOib=|e5# zDjVd9)8{Gz2)qk%j;91XFY%D>k^!Kik-cU3AD@Gb&x20LrT3tRu%M+H(zimRaVqW8 zQti`YvIYU+1tv)|uhEXDI5$F~)LeqZT>Ml6g5;H$C-#_!rsxM}OD~?mZvJY~vP;aL zG;>DKEJb;+$qQdZR=K(bSKtFs^mdYqlEk(MH)0Sd2LTOP_hCJX@df=?S3Oty&0QLR z#mbXpwzJFR=cfrTjP7n3Xfk)7;w!c1=L(p-P>eI9WKYFHB2}8D$Q=C;(c#@fjz>ZU z!aY~c2~0y{774>Ps8WhH)xb$|%{Zv@{2L

yp2?;!-q$B|p%HG#u)ahNuxS|F!{+ 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 F?w>>v|J?up literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/red-board.jpeg b/activities/3DVolume.activity/images/red-board.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..dd9cc16c72e91a53a8c6f5890f7dd2597ebe06fe GIT binary patch literal 1526 zcmYjRe@qj16u(kwYu8eWz_@OmP$~9WAhvgcRB@uv(t~!iMfu`QCf) z-sipd`MhrZvh^4$#cd87BoaZ82z;QeU!h@WyF{u~N@X&+Oe$AOrG>9brj*L7)s>YM z>K!|(w1(;`jed8<&Kh%#9x*jGHom?S-G|mU8yXr-BDq|yR4Pl9DwPSYfKA{uH-N)m zO#E*Hc;sz@K`_0%HWw^g-$H5`bW`-ESfqoB)FQE3v~>^CK#*7j4)p&K6^S89@iySA zD&;+|xIjPZA~XM%;CR(1f{Kc_NyWfdseaWiqyz^{&HCwZmyVXPNY+EOB?DSPv8AiH@o3H}$Yn3gh=WFk%T$j^xRYKYH$< zjFPgA3FVyokoWd@AmB(Q>jM4BWc-8qD4kFIF{ryp;geA&o%Mq(i>xmm&1^DQhhFO< z@RR!^xOJLB1UP*{k7e*sPsHrkVj2Rs)n}y+12=pyN^1|TIh$s-g{u~nbIQzXXmu`k z5w*B{1Wr;n&>MHI$F3qw{oPIG9b2z%KAH(J^-oO(ZkkP?<)q)`CiCUw;#C9p(}^>V zc2K|+%_ds4Iz$MOovEeR`Lj-=uieemvda&jhlh-IA7>p#gXRGI;h-eYKRrzu2U2~B zMk#C2i;eI`6g25?b2u{^k1ie= z09Bcpx=86?V-ip9U@T5lpD$n6w%y-&a?bKiY%Ly(yU7I=gGaAYifVYz7U6c`2y0kaJeX1?Z^jfX$Wh6e$aft^m{008&Ho+aG7O7KsD zqFQX8g3L||Jx=<-fR5HYhz%WxJ!H&pc=mDL4%w+P#kH#NZ15AX3zl)3qRE~Sz^&EG z>3O3_CY%<;o8=bMZ?= z;@0RhZ}M-eBKHuqAnbUzIaz~Ry6Ydv7Sx{?Pd%;*^h*@ayy#rOw|C~T(Rf%*FX;d literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/images/wood.png b/activities/3DVolume.activity/images/wood.png new file mode 100644 index 0000000000000000000000000000000000000000..adaec39e7d770ffb15ebf7cb2ea9f20ca6f1eaa3 GIT binary patch literal 223968 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%0AoAjt6+^xVgxt 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�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 B?_dA` literal 0 HcmV?d00001 diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html new file mode 100644 index 000000000..954b9dd25 --- /dev/null +++ b/activities/3DVolume.activity/index.html @@ -0,0 +1,109 @@ + + + + + +3D Volume Activity + + + + + + + + + + + + + + + + + + + + + + +

+
+ +
+ +
+ +
+ + +
+ +
+ +
+

: Last Roll

+ + +
+ + + + + + + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js new file mode 100644 index 000000000..b06c2b57d --- /dev/null +++ b/activities/3DVolume.activity/js/activity.js @@ -0,0 +1,2047 @@ +define([ + 'sugar-web/activity/activity', + 'sugar-web/env', + './palettes/bgpalette', + './palettes/volumepalette', + './palettes/colorpalette', + './palettes/zoompalette', + 'sugar-web/graphics/presencepalette', + 'tutorial', + 'sugar-web/graphics/journalchooser', + "sugar-web/datastore", +], function ( + activity, + env, + bgpalette, + volumepalette, + colorpalette, + zoompalette, + presencepalette, + tutorial, + journalchooser, + datastore +) { + // Function to change the background color based on the provided string + requirejs(['domReady!'], function (doc) { + activity.setup() + // Link presence palette + var paletteBg = new bgpalette.BgPalette( + document.getElementById('bg-button'), + undefined, + ) + paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper) + function changeBoardBackgroundHelper(selectedBoard) { + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: 'changeBg', + content: selectedBoard, + }) + } + changeBoardBackground(selectedBoard) + } + var paletteVolume = new volumepalette.VolumePalette( + document.getElementById('volume-button'), + undefined, + ) + var paletteZoom = new zoompalette.ZoomPalette( + document.getElementById('zoom-button'), + undefined, + ) + var paletteColor = new colorpalette.ColorPalette( + document.getElementById('color-button'), + undefined, + ) + let presentScore = 0 + let lastRoll = '' + let diceArray = [] + let showNumbers = false + let showImage = false + let imageData; + let presentColor; + let textColor = '#ffffff' + var currentenv + let removeVolume = false + env.getEnvironment(function (err, environment) { + currentenv = environment + + presentColor = + currentenv.user.colorvalue.fill != null + ? currentenv.user.colorvalue.fill + : presentColor + + scene.background = new THREE.Color(presentColor) + + + textColor = + currentenv.user.colorvalue.stroke != null + ? currentenv.user.colorvalue.stroke + : textColor + document.getElementById('color-button').style.backgroundColor = + presentColor + + + if (environment.sharedId) { + console.log('Shared instance') + presence = activity.getPresenceObject(function (error, network) { + network.onDataReceived(onNetworkDataReceived) + }) + } + }) + + var onNetworkDataReceived = function (msg) { + if (presence.getUserInfo().networkId === msg.user.networkId) { + console.log('returbign') + return + } + if (msg.action == 'throw') { + throwDice() + } + if (msg.action == 'changeBg') { + changeBoardBackground(msg.content) + } + if (msg.action == 'resetScore') { + presentScore = 0 + totalScoreElement.textContent = 0 + lastRoll = '' + lastRollElement.textContent = '' + } + switch (msg.content.shape) { + case 'cube': + createCube( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break + case 'octa': + createOctahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break + case 'tetra': + console.log(msg.content.ifImage) + createTetrahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break + case 'dodeca': + createDodecahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break + case 'icosa': + createIcosahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break + } + } + + // Launch tutorial + document + .getElementById('help-button') + .addEventListener('click', function (e) { + tutorial.start() + }) + + document.addEventListener('color-selected', function (event) { + const selectedColor = event.detail.color + // Update the presentColor variable with the selected color + presentColor = selectedColor + document.getElementById('color-button').style.backgroundColor = + presentColor + console.log('Present color updated:', presentColor) + // changeColors(); + }) + + let transparent = false + let toggleTransparent = false + function updateDice(type, value) { + dices[type] += value + document.getElementById(type).innerHTML = '
' + dices[type] + } + + document.querySelector('#throw-button').addEventListener('click', () => { + throwDice() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: 'throw', + }) + } + }) + + // Toggles the dice's transparency + // document.querySelector('#solid-button').addEventListener('click', () => { + // if (!transparent) { + // document.querySelector('#solid-button').style.backgroundImage = + // 'url(icons/cube.svg)' + // } else { + // document.querySelector('#solid-button').style.backgroundImage = + // 'url(icons/cube_solid.svg)' + // } + // transparent = !transparent + // toggleTransparency() + // }) + + // Toggles showing numbers on dice + + document.querySelector('#number-button').addEventListener('click', () => { + var numberButton = document.getElementById('number-button') + numberButton.classList.toggle('active') + document.getElementById("volume-button").style.backgroundImage = 'url(icons/number_on.svg)' + if (toggleTransparent) { + var transparentButton = document.getElementById('transparent-button') + transparentButton.classList.toggle('active') + toggleTransparent = !toggleTransparent + } + if (showImage) { + var imageButton1 = document.getElementById('image-button') + imageButton1.classList.toggle('active') + showImage = !showImage + } + showNumbers = !showNumbers + // toggleNumbers(); + }) + document.querySelector('#clear-button').addEventListener('click', () => { + var clearButton = document.getElementById('clear-button') + // Toggle the 'active' class on the clear button + clearButton.classList.toggle('active') + removeVolume = !removeVolume + addCube = false + addTetra = false + addOcta = false + addDodeca = false + addIcosa = false + cube.classList.remove('active') + tetra.classList.remove('active') + octa.classList.remove('active') + dodeca.classList.remove('active') + icosa.classList.remove('active') + + + }) + const remove_button = document.querySelector('#clear-button') + document + .querySelector('#transparent-button') + .addEventListener('click', () => { + var transparentButton = document.getElementById('transparent-button') + // Toggle the 'active' class on the clear button + transparentButton.classList.toggle('active') + document.getElementById("volume-button").style.backgroundImage = 'url(icons/tess.png)' + + if (showNumbers) { + var numberButton = document.getElementById('number-button') + numberButton.classList.toggle('active') + showNumbers = !showNumbers + } + if (showImage) { + var imageButton1 = document.getElementById('image-button') + imageButton1.classList.toggle('active') + showImage = !showImage + } + toggleTransparent = !toggleTransparent + }) + + let addCube = false + let addTetra = false + let addOcta = false + let addDodeca = false + let addIcosa = false + var cube = document.getElementById('cube-button') + var tetra = document.getElementById('tetra-button') + var octa = document.getElementById('octa-button') + var dodeca = document.getElementById('dodeca-button') + var icosa = document.getElementById('icosa-button') + + + + // const imageButton = document.getElementById('image-button') + // document + // .getElementById('image-button') + // .addEventListener('click', function (e) { + // if (showImage) { + // showImage = !showImage + // imageButton.classList.toggle('active') + // console.log('doing stuff onw') + // return + // } + // journalchooser.show( + // function (entry) { + // // No selection + // if (!entry) { + // return + // } + // // Get object content + // imageButton.classList.add('active') + // showImage = !showImage + + // if (toggleTransparent) { + // var transparentButton = + // document.getElementById('transparent-button') + // transparentButton.classList.toggle('active') + // toggleTransparent = !toggleTransparent + // } + + // if (showNumbers) { + // var numberButton = document.getElementById('number-button') + // numberButton.classList.toggle('active') + // showNumbers = !showNumbers + // } + + // var dataentry = new datastore.DatastoreObject(entry.objectId) + // dataentry.loadAsText(function (err, metadata, data) { + // imageData = data + // console.log(data); + // // if (addCube) { + // // console.log(data) + // // imageData = data; + // // console.log(imageData) + // // createCube() + // // } + // // if (addTetra) { + // // } + // // if (addOcta) { + // // } + // }) + // }, + // { mimetype: 'image/png' }, + // { mimetype: 'image/jpeg' }, + // ) + // }) + + // Add click event listeners to each div + cube.addEventListener('click', function () { + if (!cube.classList.contains('active')) { + cube.classList.add('active') + addCube = true + addTetra = false + addOcta = false + addDodeca = false + addIcosa = false + removeVolume = false + remove_button.classList.remove('active') + tetra.classList.remove('active') + octa.classList.remove('active') + dodeca.classList.remove('active') + icosa.classList.remove('active') + + + + if (transparent) { + transparent = false + document.querySelector('#solid-button').style.backgroundImage = + 'url(icons/cube_solid.svg)' + toggleTransparency() + } + } else { + cube.classList.remove('active') + addCube = !addCube + } + }) + + tetra.addEventListener('click', function () { + if (!tetra.classList.contains('active')) { + addCube = false + addTetra = true + addOcta = false + addDodeca = false + addIcosa = false + tetra.classList.add('active') + cube.classList.remove('active') + octa.classList.remove('active') + dodeca.classList.remove('active') + icosa.classList.remove('active') + + + removeVolume = false + remove_button.classList.remove('active') + if (transparent) { + transparent = false + document.querySelector('#solid-button').style.backgroundImage = + 'url(icons/cube_solid.svg)' + toggleTransparency() + } + } else { + tetra.classList.remove('active') + addTetra = !addTetra + } + }) + + octa.addEventListener('click', function () { + if (!octa.classList.contains('active')) { + addCube = false + addTetra = false + addOcta = true + addDodeca = false + addIcosa = false + octa.classList.add('active') + cube.classList.remove('active') + tetra.classList.remove('active') + icosa.classList.remove('active') + dodeca.classList.remove('active') + + removeVolume = false + remove_button.classList.remove('active') + if (transparent) { + transparent = false + document.querySelector('#solid-button').style.backgroundImage = + 'url(icons/cube_solid.svg)' + toggleTransparency() + } + } else { + octa.classList.remove('active') + addOcta = !addOcta + } + }) + + dodeca.addEventListener('click', function () { + if (!dodeca.classList.contains('active')) { + addCube = false + addTetra = false + addOcta = false + addIcosa = false + addDodeca = true + octa.classList.remove('active') + cube.classList.remove('active') + tetra.classList.remove('active') + icosa.classList.remove('active') + dodeca.classList.add('active') + + removeVolume = false + remove_button.classList.remove('active') + if (transparent) { + transparent = false + document.querySelector('#solid-button').style.backgroundImage = + 'url(icons/cube_solid.svg)' + toggleTransparency() + } + } else { + dodeca.classList.remove('active') + addDodeca = !addDodeca + } + }) + + icosa.addEventListener('click', function () { + if (!icosa.classList.contains('active')) { + addCube = false + addTetra = false + addOcta = false + addDodeca = false + addIcosa = true + octa.classList.remove('active') + cube.classList.remove('active') + tetra.classList.remove('active') + dodeca.classList.remove('active') + + icosa.classList.add('active') + + removeVolume = false + remove_button.classList.remove('active') + if (transparent) { + transparent = false + document.querySelector('#solid-button').style.backgroundImage = + 'url(icons/cube_solid.svg)' + toggleTransparency() + } + } else { + icosa.classList.remove('active') + addIcosa = !addIcosa + } + }) + + // Event listeners + // document + // .querySelector('.cube .plus-button') + // .addEventListener('click', () => { + // updateDice('cube', 1) + // createCube() + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // content: { + // shape: 'cube', + // color: currentenv.user.colorvalue.fill, + // ifTransparent: toggleTransparent, + // ifNumbers: showNumbers, + // }, + // }) + // } + // }) + + // document + // .querySelector('.tetra .plus-button') + // .addEventListener('click', () => { + // updateDice('tetra', 1) + // createTetrahedron() + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // content: { + // shape: 'tetra', + // color: currentenv.user.colorvalue.fill, + // ifTransparent: toggleTransparent, + // ifNumbers: showNumbers, + // }, + // }) + // } + // }) + + // document + // .querySelector('.octa .plus-button') + // .addEventListener('click', () => { + // updateDice('octa', 1) + // createOctahedron() + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // content: { + // shape: 'octa', + // color: currentenv.user.colorvalue.fill, + // ifTransparent: toggleTransparent, + // ifNumbers: showNumbers, + // }, + // }) + // } + // }) + + // const totalScoreElement = document.getElementById('score') + const lastRollElement = document.getElementById('roll') + + // Function to update the elements + function updateElements() { + // totalScoreElement.textContent = presentScore + lastRollElement.textContent = lastRoll + '=' + presentScore; + } + + const renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true, + }) + renderer.shadowMap.enabled = true + + let xCoordinate, zCoordinate + const raycaster = new THREE.Raycaster() + const mouse = new THREE.Vector2() + document.querySelector('body').addEventListener('click', onRemoveClick) + document.querySelector('body').addEventListener('click', onAddClick) + + function onAddClick(event) { + if (addCube || addTetra || addOcta || addDodeca || addIcosa) { + var rect = renderer.domElement.getBoundingClientRect() + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera) + + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children) + + for (let i = 0; i < intersects.length; i++) { + var intersectedObject = intersects[i]?.object + if (intersectedObject.geometry.type == 'PlaneGeometry') { + console.log(intersects[i].point) + xCoordinate = intersects[i].point.x + zCoordinate = intersects[i].point.z + + if (addCube) { + createCube() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'cube', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } else if (addTetra) { + createTetrahedron() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'tetra', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } else if (addDodeca) { + createDodecahedron() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'dodeca', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } else if (addIcosa) { + createIcosahedron() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'icosa', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } else { + createOctahedron() + if (presence) { + console.log(showImage); + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'octa', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } + } + } + } + } + function onRemoveClick(event) { + if (removeVolume) { + // Calculate mouse position in normalized device coordinates + var rect = renderer.domElement.getBoundingClientRect() + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera) + + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children) + + var intersectedObject = intersects[0]?.object + if (intersectedObject.geometry.type == 'PlaneGeometry') { + return + } + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][0] == intersectedObject) { + world.removeBody(diceArray[i][1]) + scene.remove(diceArray[i][0]) + diceArray.splice(i, 1) + } + } + } + } + + var presence = null + var palette = new presencepalette.PresencePalette( + document.getElementById('network-button'), + undefined, + ) + palette.addEventListener('shared', function () { + palette.popDown() + console.log('Want to share') + presence = activity.getPresenceObject(function (error, network) { + if (error) { + console.log('Sharing error') + return + } + network.createSharedActivity( + 'org.sugarlabs.Volume', + function (groupId) { + console.log('Activity shared') + }, + ) + network.onDataReceived(onNetworkDataReceived) + }) + }) + + // document + // .querySelector('#reset-button') + // .addEventListener('click', function () { + // presentScore = 0 + // totalScoreElement.textContent = 0 + // lastRoll = '' + // lastRollElement.textContent = '' + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // action: 'resetScore', + // }) + // } + // }) + let sleepCounter = 0; + renderer.setSize(window.innerWidth, window.innerHeight) + const canvas = document.getElementById('game-container') + document.getElementById('game-container').appendChild(renderer.domElement) + + const scene = new THREE.Scene() + scene.background = new THREE.Color(presentColor) + const light = new THREE.DirectionalLight(0xffffff, 0.7) + light.castShadow = true + const leftLight = new THREE.DirectionalLight(0xffffff, 0.25) + leftLight.castShadow = true + const rightLight = new THREE.DirectionalLight(0xffffff, 0.2) + rightLight.castShadow = true + const backLight = new THREE.DirectionalLight(0xffffff, 0.2) + const bottomLight = new THREE.DirectionalLight(0xffffff, 0.1) + const topLight = new THREE.DirectionalLight(0xffffff, 0.7) + topLight.castShadow = true + leftLight.position.set(-30, 20, -30) + rightLight.position.set(30, 20, -30) + backLight.position.set(0, 20, 30) + light.position.set(0, 20, -30) + bottomLight.position.set(0, -20, -30) + topLight.position.set(0, 10, 0) + scene.add(backLight) + scene.add(rightLight) + scene.add(leftLight) + scene.add(light) + scene.add(bottomLight) + scene.add(topLight) + + const ambientLight = new THREE.AmbientLight(0x222222) // Soft ambient lighting + scene.add(ambientLight) + + const camera = new THREE.PerspectiveCamera( + 45, + window.innerWidth / window.innerHeight, + 0.1, + 1000, + ) + + const world = new CANNON.World({ + gravity: new CANNON.Vec3(0, -9.81, 0), + }) + world.allowSleep = true + + const groundGeo = new THREE.PlaneGeometry(30, 30) + const groundMat = new THREE.MeshPhongMaterial({ + side: THREE.DoubleSide, + wireframe: false, + }) + groundMat.needsUpdate = true + const groundMesh = new THREE.Mesh(groundGeo, groundMat) + groundMesh.receiveShadow = true + + groundMesh.material.color.setHex(0x425eff) + scene.add(groundMesh) + const groundPhysMat = new CANNON.Material() + const groundWidth = 0 // Desired width of the ground + const groundDepth = 0 // Desired depth of the ground + const groundThickness = 0 + const boxWidth = groundWidth / 2 + const boxDepth = groundDepth / 2 + const boxHeight = 10 // Adjust this for desired box height + + const boxShape = new CANNON.Box( + new CANNON.Vec3(boxWidth, boxHeight / 2, boxDepth), + ) + + const groundBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + }) + groundBody.material.friction = 1 + world.addBody(groundBody) + groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) + + const leftWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }) + world.addBody(leftWallBody) + leftWallBody.position.set(15, 0, 0) + leftWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0) + + const rightWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }) + world.addBody(rightWallBody) + rightWallBody.position.set(-15, 0, 0) + rightWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0) + + const backWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }) + world.addBody(backWallBody) + backWallBody.position.set(0, 0, 15) + backWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2) + + const frontWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }) + world.addBody(frontWallBody) + frontWallBody.position.set(0, 0, -15) + frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2) + + const rollingForceMagnitude = 2 // Adjust for desired intensity + const randomDirection = new CANNON.Vec3( + Math.random() - 0.5, // Random x-axis value between -0.5 and 0.5 + Math.random() * 0.2 - 0.1, // Random y-axis value between -0.1 and 0.1 (slightly tilted) + Math.random() - 0.5, // Random z-axis value between -0.5 and 0.5 + ) + randomDirection.normalize() // Normalize to unit vector + + const rollingForce = randomDirection.scale(rollingForceMagnitude) + + const offset = new CANNON.Vec3(0, 0.1, 0) + + const orbit = new OrbitControls.OrbitControls(camera, renderer.domElement) + camera.position.set(0, 25, -30) + orbit.update() + orbit.listenToKeyEvents(document.querySelector('body')) + + const goRightButton = document.querySelector('#right-button') + const goLeftButton = document.querySelector('#left-button') + const goUpButton = document.querySelector('#up-button') + const goDownButton = document.querySelector('#down-button') + + // Add click event listener to the button + goRightButton.addEventListener('click', function () { + orbit.rotateRight() + }) + + goLeftButton.addEventListener('click', function () { + orbit.rotateLeft() + }) + goUpButton.addEventListener('click', function () { + orbit.rotateUp() + }) + goDownButton.addEventListener('click', function () { + orbit.rotateDown() + }) + // Zoom code + const evt = new Event('wheel', { bubbles: true, cancelable: true }) + + const zoomInButton = document.getElementById('zoom-in-button') + const zoomOutButton = document.getElementById('zoom-out-button') + + const zoomInFunction = e => { + const fov = getFov() + camera.fov = clickZoom(fov, 'zoomIn') + camera.updateProjectionMatrix() + } + + const zoomOutFunction = e => { + const fov = getFov() + camera.fov = clickZoom(fov, 'zoomOut') + camera.updateProjectionMatrix() + } + + const clickZoom = (value, zoomType) => { + if (value >= 20 && zoomType === 'zoomIn') { + return value - 5 + } else if (value <= 75 && zoomType === 'zoomOut') { + return value + 5 + } else { + return value + } + } + + const getFov = () => { + return Math.floor( + (2 * + Math.atan(camera.getFilmHeight() / 2 / camera.getFocalLength()) * + 180) / + Math.PI, + ) + } + + zoomInButton.addEventListener('click', zoomInFunction) + zoomOutButton.addEventListener('click', zoomOutFunction) + + function createTetrahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + let tetrahedron + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5) + let tileSize = 512 + let g = new THREE.TetrahedronGeometry(2) + + let c = document.createElement('canvas') + let div = document.createElement('div') + c.width = tileSize * tileDimension.x + c.height = tileSize * tileDimension.y + let ctx = c.getContext('2d') + ctx.fillStyle = presentColor + ctx.fillRect(0, 0, c.width, c.height) + + let uvs = [] + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map(p => { + return new THREE.Vector2(...p) + }) + let arrOfNums = [ + [2, 1, 3], + [1, 2, 4], + [3, 1, 4], + [2, 3, 4], + ] + for (let i = 0; i < 4; i++) { + let u = i % tileDimension.x + let v = Math.floor(i / tileDimension.x) + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + ) + + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.font = `bold 150px Arial` + ctx.fillStyle = textColor + // ctx.fillText( + // i + 1, + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + let aStep = (Math.PI * 2) / 3 + let yAlign = Math.PI * 0.5 + let tileQuarter = tileSize * 0.25 + for (let j = 0; j < 3; j++) { + ctx.save() + ctx.translate( + (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, + c.height - + (v + 0.5) * tileSize + + Math.sin(j * aStep - yAlign) * tileQuarter, + ) + ctx.rotate((j * Math.PI * 2) / 3) + ctx.fillText(arrOfNums[i][j], 0, 0) + ctx.restore() + } + } + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + + let tex = new THREE.CanvasTexture(c) + tex.colorSpace = THREE.SRGBColorSpace + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }) + + tetrahedron = new THREE.Mesh(g, m) + } else if (tempTransparent) { + const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry(2) // Size of the tetrahedron + const wireframe = new THREE.WireframeGeometry( + tetrahedronTransparentGeometry, + ) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + tetrahedron = line + } else if (tempImage) { + const boxGeo = new THREE.TetrahedronGeometry(2) + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + tetrahedron = new THREE.Mesh(boxGeo, material) + } else { + const tetrahedronGeometry = new THREE.TetrahedronGeometry(2) // Size of the tetrahedron + + const tetraMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + + tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial) + } + + tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y + tetrahedron.castShadow = true + scene.add(tetrahedron) + + const verticesTetra = [ + new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) + new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) + new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) + new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) + ] + const facesTetra = [ + [2, 1, 0], // Triangle 1 (right, top, left) + [0, 3, 2], // Triangle 2 (right, front, top) + [1, 3, 0], // Triangle 3 (top, front, left) + [2, 3, 1], // Triangle 4 (left, right, front) + ] + // Create a ConvexPolyhedron shape from the vertices and faces + const tetrahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesTetra, + faces: facesTetra, + }) + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + + const tetrahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: tetrahedronShape, + position: new CANNON.Vec3(x, 10, z), + friction: -1, + restitution: 5, + }) + if (tempShowNumbers) { + tetrahedronBody.addEventListener('sleep', () => { + sleepCounter++; + getTetraScore(tetrahedron) + }) + } + world.addBody(tetrahedronBody) + tetrahedronBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + tetrahedronBody.applyImpulse(offset, rollingForce) + tetrahedron.position.copy(tetrahedronBody.position) // this merges the physics body to threejs mesh + tetrahedron.quaternion.copy(tetrahedronBody.quaternion) + diceArray.push([tetrahedron, tetrahedronBody, 'tetra']) + } + + function createOctahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + let octahedron + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5) + let tileSize = 512 + let g = new THREE.OctahedronGeometry(2) + + let c = document.createElement('canvas') + c.width = tileSize * tileDimension.x + c.height = tileSize * tileDimension.y + let ctx = c.getContext('2d') + ctx.fillStyle = presentColor + ctx.fillRect(0, 0, c.width, c.height) + + let uvs = [] + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map(p => { + return new THREE.Vector2(...p) + }) + + for (let i = 0; i < 9; i++) { + let u = i % tileDimension.x + let v = Math.floor(i / tileDimension.x) + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + ) + + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.font = `bold 200px Arial` + ctx.fillStyle = textColor + ctx.fillText( + i + 1 + (i == 5 || i == 8 ? '' : ''), + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize, + ) + } + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + + let tex = new THREE.CanvasTexture(c) + tex.colorSpace = THREE.SRGBColorSpace + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }) + + octahedron = new THREE.Mesh(g, m) + } else if (tempTransparent) { + const octahedronTransparentGeometry = new THREE.OctahedronGeometry(2) // Size of the octahedron + const wireframe = new THREE.WireframeGeometry( + octahedronTransparentGeometry, + ) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + octahedron = line + } else if (tempImage) { + const octahedronGeometry = new THREE.OctahedronGeometry(2) + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + octahedron = new THREE.Mesh(octahedronGeometry, material) + } else { + const octahedronGeometry = new THREE.OctahedronGeometry(2) // Size of the octahedron + + const octaMaterial = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial) + } + octahedron.castShadow = true + scene.add(octahedron) + + const verticesOcta = [ + new CANNON.Vec3(2, 0, 0), // Vertex 1 (right) + new CANNON.Vec3(-2, 0, 0), // Vertex 2 (top) + new CANNON.Vec3(0, 2, 0), // Vertex 3 (left) + new CANNON.Vec3(0, -2, 0), // Vertex 4 (front) + new CANNON.Vec3(0, 0, 2), // Vertex 4 (front) + new CANNON.Vec3(0, 0, -2), // Vertex 4 (front) + ] + + // Define the faces of the tetrahedron (counter-clockwise order) + const facesOcta = [ + [0, 2, 4], // Triangle 1 (right, top, left) + [0, 4, 3], // Triangle 2 (right, front, top) + [0, 3, 5], // Triangle 3 (top, front, left) + [0, 5, 2], // Triangle 4 (left, right, front) + [1, 2, 5], // Triangle 1 (right, top, left) + [1, 5, 3], // Triangle 1 (right, top, left) + [1, 3, 4], // Triangle 1 (right, top, left) + [1, 4, 2], // Triangle 1 (right, top, left) + ] + + const octahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesOcta, + faces: facesOcta, + }) + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + + const octahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: octahedronShape, + position: new CANNON.Vec3(x, 10, z), + friction: -1, + restitution: 5, + }) + if (tempShowNumbers) { + octahedronBody.addEventListener('sleep', () => { + sleepCounter++; + getOctaScore(octahedron) + }) + } + world.addBody(octahedronBody) + + octahedronBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + octahedronBody.applyImpulse(offset, rollingForce) + octahedron.position.copy(octahedronBody.position) // this merges the physics body to threejs mesh + octahedron.quaternion.copy(octahedronBody.quaternion) + diceArray.push([octahedron, octahedronBody, 'octa']) + } + + function createCube( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + let boxMesh + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 2) + let tileSize = 512 + let g = new THREE.BoxGeometry(2, 2, 2) + + let c = document.createElement('canvas') + c.width = tileSize * tileDimension.x + c.height = tileSize * tileDimension.y + let ctx = c.getContext('2d') + ctx.fillStyle = sharedColor != null ? sharedColor : presentColor + ctx.fillRect(0, 0, c.width, c.height) + + let baseUVs = [ + [0, 1], + [1, 1], + [0, 0], + [1, 0], + ].map(p => { + return new THREE.Vector2(...p) + }) + let uvs = [] + let vTemp = new THREE.Vector2() + let vCenter = new THREE.Vector2(0.5, 0.5) + for (let i = 0; i < 6; i++) { + let u = i % tileDimension.x + let v = Math.floor(i / tileDimension.x) + baseUVs.forEach(buv => { + uvs.push( + (buv.x + u) / tileDimension.x, + (buv.y + v) / tileDimension.y, + ) + }) + + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.font = `bold 300px Arial` + ctx.fillStyle = textColor + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize, + ) + } + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + + let tex = new THREE.CanvasTexture(c) + tex.colorSpace = THREE.SRGBColorSpace + + let m = new THREE.MeshPhongMaterial({ + map: tex, + // metalness: 0.75, + // roughness: 0.25, + }) + + boxMesh = new THREE.Mesh(g, m) + } else if (tempTransparent) { + const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2) + const wireframe = new THREE.WireframeGeometry(boxTransparentGeometry) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + boxMesh = line + } else if (tempImage) { + const boxGeo = new THREE.BoxGeometry(2, 2, 2) + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + boxMesh = new THREE.Mesh(boxGeo, material) + } else { + const boxGeo = new THREE.BoxGeometry(2, 2, 2) + const boxMat = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + boxMesh = new THREE.Mesh(boxGeo, boxMat) + } + boxMesh.castShadow = true + scene.add(boxMesh) + + const boxPhysmat = new CANNON.Material() + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + const boxBody = new CANNON.Body({ + mass: 1, + shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), + position: new CANNON.Vec3(xCoordinate, 10, zCoordinate), + material: boxPhysmat, + friction: 0.1, + restitution: 5, + }) + boxBody.addEventListener('click', function (boxBody) { + console.log('clicked a box') + }) + + world.addBody(boxBody) + if (tempShowNumbers) { + boxBody.addEventListener('sleep', () => { + sleepCounter++; + getCubeScore(boxMesh) + }) + } + + boxBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + boxBody.applyImpulse(offset, rollingForce) + + // what will happen when the two bodies touch + const groundBoxContactMat = new CANNON.ContactMaterial( + groundPhysMat, + boxPhysmat, + { friction: 0.5 }, + ) + + world.addContactMaterial(groundBoxContactMat) + diceArray.push([boxMesh, boxBody, 'cube']) + } + + const cannonDebugger = new CannonDebugger(scene, world, { + color: 0xadd8e6, + }) + function createDodecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + console.log("creating a dodecahedron") + let dodecahedron + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5) + let tileSize = 512 + let g = new THREE.DodecahedronGeometry(2) + + let c = document.createElement('canvas') + let div = document.createElement('div') + c.width = tileSize * tileDimension.x + c.height = tileSize * tileDimension.y + let ctx = c.getContext('2d') + ctx.fillStyle = presentColor + ctx.fillRect(0, 0, c.width, c.height) + + let uvs = [] + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map(p => { + return new THREE.Vector2(...p) + }) + let arrOfNums = [ + [2, 1, 3], + [1, 2, 4], + [3, 1, 4], + [2, 3, 4], + ] + for (let i = 0; i < 4; i++) { + let u = i % tileDimension.x + let v = Math.floor(i / tileDimension.x) + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + ) + + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.font = `bold 150px Arial` + ctx.fillStyle = textColor + // ctx.fillText( + // i + 1, + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + let aStep = (Math.PI * 2) / 3 + let yAlign = Math.PI * 0.5 + let tileQuarter = tileSize * 0.25 + for (let j = 0; j < 3; j++) { + ctx.save() + ctx.translate( + (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, + c.height - + (v + 0.5) * tileSize + + Math.sin(j * aStep - yAlign) * tileQuarter, + ) + ctx.rotate((j * Math.PI * 2) / 3) + ctx.fillText(arrOfNums[i][j], 0, 0) + ctx.restore() + } + } + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + + let tex = new THREE.CanvasTexture(c) + tex.colorSpace = THREE.SRGBColorSpace + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }) + + dodecahedron = new THREE.Mesh(g, m) + } else if (tempTransparent) { + const dodecahedronTransparentGeometry = new THREE.DodecahedronGeometry(2) // Size of the dodecahedron + const wireframe = new THREE.WireframeGeometry( + dodecahedronTransparentGeometry, + ) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + dodecahedron = line + } else if (tempImage) { + const boxGeo = new THREE.DodecahedronGeometry(2) + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + dodecahedron = new THREE.Mesh(boxGeo, material) + } else { + const dodecahedronGeometry = new THREE.DodecahedronGeometry(2) // Size of the dodecahedron + + const tetraMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + + dodecahedron = new THREE.Mesh(dodecahedronGeometry, tetraMaterial) + } + + dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y + dodecahedron.castShadow = true + scene.add(dodecahedron) + + // Vertices +const r = (1 + Math.sqrt(5)) / 2; +const t = 1 / r; +const verticesDodeca = [ + new CANNON.Vec3(-1, -1, -1), new CANNON.Vec3(-1, -1, 1), + new CANNON.Vec3(-1, 1, -1), new CANNON.Vec3(-1, 1, 1), + new CANNON.Vec3(1, -1, -1), new CANNON.Vec3(1, -1, 1), + new CANNON.Vec3(1, 1, -1), new CANNON.Vec3(1, 1, 1), + new CANNON.Vec3(0, -t, -r), new CANNON.Vec3(0, -t, r), + new CANNON.Vec3(0, t, -r), new CANNON.Vec3(0, t, r), + new CANNON.Vec3(-t, -r, 0), new CANNON.Vec3(-t, r, 0), + new CANNON.Vec3(t, -r, 0), new CANNON.Vec3(t, r, 0), + new CANNON.Vec3(-r, 0, -t), new CANNON.Vec3(r, 0, -t), + new CANNON.Vec3(-r, 0, t), new CANNON.Vec3(r, 0, t) +]; + +// Faces +const facesDodeca = [ + [3, 11, 7], [3, 7, 15], [3, 15, 13], + [7, 19, 17], [7, 17, 6], [7, 6, 15], + [17, 4, 8], [17, 8, 10], [17, 10, 6], + [8, 0, 16], [8, 16, 2], [8, 2, 10], + [0, 12, 1], [0, 1, 18], [0, 18, 16], + [6, 10, 2], [6, 2, 13], [6, 13, 15], + [2, 16, 18], [2, 18, 3], [2, 3, 13], + [18, 1, 9], [18, 9, 11], [18, 11, 3], + [4, 14, 12], [4, 12, 0], [4, 0, 8], + [11, 9, 5], [11, 5, 19], [11, 19, 7], + [19, 5, 14], [19, 14, 4], [19, 4, 17], + [1, 12, 14], [1, 14, 5], [1, 5, 9] +]; + +// Create a ConvexPolyhedron shape from the vertices and faces +const dodecahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesDodeca, + faces: facesDodeca +}); + + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + + const dodecahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: dodecahedronShape, + position: new CANNON.Vec3(x, 10, z), + friction: -1, + restitution: 5, + }) + if (tempShowNumbers) { + dodecahedronBody.addEventListener('sleep', () => { + sleepCounter++; + getTetraScore(dodecahedron) + }) + } + world.addBody(dodecahedronBody) + dodecahedronBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + dodecahedronBody.applyImpulse(offset, rollingForce) + dodecahedron.position.copy(dodecahedronBody.position) // this merges the physics body to threejs mesh + dodecahedron.quaternion.copy(dodecahedronBody.quaternion) + diceArray.push([dodecahedron, dodecahedronBody, 'dodeca']) + } + + function createIcosahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + let icosahedron + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5) + let tileSize = 512 + let g = new THREE.IcosahedronGeometry(2) + + let c = document.createElement('canvas') + let div = document.createElement('div') + c.width = tileSize * tileDimension.x + c.height = tileSize * tileDimension.y + let ctx = c.getContext('2d') + ctx.fillStyle = presentColor + ctx.fillRect(0, 0, c.width, c.height) + + let uvs = [] + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map(p => { + return new THREE.Vector2(...p) + }) + let arrOfNums = [ + [2, 1, 3], + [1, 2, 4], + [3, 1, 4], + [2, 3, 4], + ] + for (let i = 0; i < 4; i++) { + let u = i % tileDimension.x + let v = Math.floor(i / tileDimension.x) + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + ) + + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.font = `bold 150px Arial` + ctx.fillStyle = textColor + // ctx.fillText( + // i + 1, + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + let aStep = (Math.PI * 2) / 3 + let yAlign = Math.PI * 0.5 + let tileQuarter = tileSize * 0.25 + for (let j = 0; j < 3; j++) { + ctx.save() + ctx.translate( + (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, + c.height - + (v + 0.5) * tileSize + + Math.sin(j * aStep - yAlign) * tileQuarter, + ) + ctx.rotate((j * Math.PI * 2) / 3) + ctx.fillText(arrOfNums[i][j], 0, 0) + ctx.restore() + } + } + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + + let tex = new THREE.CanvasTexture(c) + tex.colorSpace = THREE.SRGBColorSpace + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }) + + icosahedron = new THREE.Mesh(g, m) + } else if (tempTransparent) { + const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry(2) // Size of the Icosahedron + const wireframe = new THREE.WireframeGeometry( + icosahedronTransparentGeometry, + ) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + icosahedron = line + } else if (tempImage) { + const boxGeo = new THREE.IcosahedronGeometry(2) + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + icosahedron = new THREE.Mesh(boxGeo, material) + } else { + const icosahedronGeometry = new THREE.IcosahedronGeometry(2) // Size of the icosahedron + + const tetraMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + + icosahedron = new THREE.Mesh(icosahedronGeometry, tetraMaterial) + } + + icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y + icosahedron.castShadow = true + scene.add(icosahedron) + + // Vertices +// Vertices +const t = (1 + Math.sqrt(5)) / 2; +const verticesIcosa = [ + new CANNON.Vec3(-1, t, 0), new CANNON.Vec3(1, t, 0), + new CANNON.Vec3(-1, -t, 0), new CANNON.Vec3(1, -t, 0), + new CANNON.Vec3(0, -1, t), new CANNON.Vec3(0, 1, t), + new CANNON.Vec3(0, -1, -t), new CANNON.Vec3(0, 1, -t), + new CANNON.Vec3(t, 0, -1), new CANNON.Vec3(t, 0, 1), + new CANNON.Vec3(-t, 0, -1), new CANNON.Vec3(-t, 0, 1) +]; + +// Faces +const facesIcosa = [ + [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], + [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], + [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], + [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1] +]; + +// Create a ConvexPolyhedron shape from the vertices and faces +const icosahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesIcosa, + faces: facesIcosa +}); + + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + + const icosahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: icosahedronShape, + position: new CANNON.Vec3(x, 10, z), + friction: -1, + restitution: 5, + }) + if (tempShowNumbers) { + icosahedronBody.addEventListener('sleep', () => { + sleepCounter++; + getTetraScore(icosahedron) + }) + } + world.addBody(icosahedronBody) + icosahedronBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + icosahedronBody.applyImpulse(offset, rollingForce) + icosahedron.position.copy(icosahedronBody.position) // this merges the physics body to threejs mesh + icosahedron.quaternion.copy(icosahedronBody.quaternion) + diceArray.push([icosahedron, icosahedronBody, 'icosa']) + } + + + + + const timeStep = 1 / 20 + + function throwDice() { + for (let i = 0; i < diceArray.length; i++) { + scene.remove(diceArray[i][0]) + world.removeBody(diceArray[i][1]) + } + if (diceArray.length > 0) { + lastRoll = '' + presentScore = 0 + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][1].angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + diceArray[i][1].applyImpulse(offset, rollingForce) + diceArray[i][1].position.set(0, 10, 0) + } + for (let i = 0; i < diceArray.length; i++) { + scene.add(diceArray[i][0]) + world.addBody(diceArray[i][1]) + } + } else { + for (let i = 0; i < dices.cube; i++) { + createCube() + } + for (let i = 0; i < dices.tetra; i++) { + createTetrahedron() + } + for (let i = 0; i < dices.octa; i++) { + createOctahedron() + } + for (let i = 0; i < dices.dodeca; i++) { + createDodecahedron() + } + for (let i = 0; i < dices.icosa; i++) { + createIcosahedron() + } + lastRoll = '' + // if (showNumbers) { + // getScore(); + // } + } + } + function getOctaScore(body) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis + face: 4, + }, + { + vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis + face: 5, + }, + { + vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis + face: 6, + }, + { + vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis + face: 3, + }, + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) + face: 8, + }, + { + vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) + face: 2, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 7, + }, + ] + + let minValue = 1000000 + let minInd + for (let i = 0; i < faceVectors.length; i++) { + let faceVector = faceVectors[i] + faceVector.vector.applyEuler(body.rotation) + console.log(Math.abs(faceVector.vector.y)) + if (minValue > Math.abs(1 - faceVector.vector.y)) { + minValue = Math.abs(1 - faceVector.vector.y) + minInd = i + } + // if (Math.abs(faceVector.vector.y).toString().substring(0, 1) == '1') { + // lastRoll += faceVectors[i].face + ' +' + // presentScore += faceVectors[i].face + // updateElements() + // break + // } + } + lastRoll += faceVectors[minInd].face + ' +' + presentScore += faceVectors[minInd].face + updateElements() + } + function getCubeScore(body) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), + face: 1, + }, + { + vector: new THREE.Vector3(-1, 0, 0), + face: 2, + }, + { + vector: new THREE.Vector3(0, 1, 0), + face: 3, + }, + { + vector: new THREE.Vector3(0, -1, 0), + face: 4, + }, + { + vector: new THREE.Vector3(0, 0, 1), + face: 5, + }, + { + vector: new THREE.Vector3(0, 0, -1), + face: 6, + }, + ] + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation) + + if (Math.round(faceVector.vector.y) == 1) { + lastRoll += faceVector.face + ' +' + presentScore += faceVector.face + updateElements() + break + } + } + } + function getTetraScore(body) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 3, + }, + { + vector: new THREE.Vector3(-1, 1, -1).normalize(), // Towards a corner (negative x, positive y, negative z) + face: 2, + }, + { + vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) + face: 4, + }, + ] + + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation) + console.log(faceVector.vector.y) + if (Math.round(faceVector.vector.y) == 1) { + lastRoll += faceVector.face + ' +' + presentScore += faceVector.face + updateElements() + break + } + } + } + function toggleTransparency() { + for (let i = 0; i < diceArray.length; i++) { + if (transparent) { + leftLight.intensity = 5 + rightLight.intensity = 5 + backLight.intensity = 5 + bottomLight.intensity = 5 + } else { + leftLight.intensity = 0.1 + rightLight.intensity = 0.1 + backLight.intensity = 0.5 + bottomLight.intensity = 0.1 + } + diceArray[i][0].material.wireframe = !diceArray[i][0].material.wireframe + diceArray[i][0].material.transparent = + !diceArray[i][0].material.transparent + diceArray[i][0].material.needsUpdate = true + } + groundMesh.material.wireframe = !groundMesh.material.wireframe + } + // function changeColors() { + // for (let i = 0; i < diceArray.length; i++) { + // diceArray[i][0].material.color?.set(presentColor); + // diceArray[i][0].material.needsUpdate = true; + // } + // } + + function changeBoardBackground(selectedBoard) { + let textureLoader = new THREE.TextureLoader() + switch (selectedBoard) { + case 'green-board': + console.log('now changing bg to green') + textureLoader.load( + 'images/grass_background.png', + function (groundTexture) { + groundMesh.material.wireframe = false + groundMesh.material.map = groundTexture + groundMesh.material.needsUpdate = true + groundBody.material.friction = 5 + }, + ) + break + case 'wood': + console.log('wood changing') + textureLoader.load('images/wood.png', function (groundTexture) { + groundMesh.material.wireframe = false + groundMesh.material.color.setHex(0xf0c592) + groundMesh.material.map = groundTexture + groundMesh.material.needsUpdate = true + groundBody.material.friction = 3 + }) + break + case 'default': + groundMesh.material.needsUpdate = true + groundMesh.material.color.setHex(0x425eff) + groundMesh.material.wireframe = false + groundMesh.material.map = null + groundBody.material.friction = 1 + break + } + } + animate() + + function animate() { + world.step(timeStep) + // cannonDebugger.update(); + + groundMesh.position.copy(groundBody.position) + groundMesh.quaternion.copy(groundBody.quaternion) + + // Loop to merge the cannon bodies to the threejs meshes + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][0]?.position?.copy(diceArray[i][1].position) + diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion) + } + + renderer.render(scene, camera) + } + + renderer.setAnimationLoop(animate) + + window.addEventListener('resize', function () { + camera.aspect = window.innerWidth / window.innerHeight + camera.updateProjectionMatrix() + renderer.setSize(window.innerWidth, window.innerHeight) + }) + }) +}) diff --git a/activities/3DVolume.activity/js/buffer.js b/activities/3DVolume.activity/js/buffer.js new file mode 100644 index 000000000..98c39975c --- /dev/null +++ b/activities/3DVolume.activity/js/buffer.js @@ -0,0 +1,934 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) : + typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BufferGeometryUtils = {}, global.THREE)); +})(this, (function (exports, three) { 'use strict'; + + function computeTangents( geometry ) { + + geometry.computeTangents(); + console.warn( 'THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.' ); + + } + + /** + * @param {Array} geometries + * @param {Boolean} useGroups + * @return {BufferGeometry} + */ + function mergeBufferGeometries( geometries, useGroups = false ) { + + const isIndexed = geometries[ 0 ].index !== null; + + const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) ); + const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) ); + + const attributes = {}; + const morphAttributes = {}; + + const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative; + + const mergedGeometry = new THREE.BufferGeometry(); + + let offset = 0; + + for ( let i = 0; i < geometries.length; ++ i ) { + + const geometry = geometries[ i ]; + let attributesCount = 0; + + // ensure that all geometries are indexed, or none + + if ( isIndexed !== ( geometry.index !== null ) ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' ); + return null; + + } + + // gather attributes, exit early if they're different + + for ( const name in geometry.attributes ) { + + if ( ! attributesUsed.has( name ) ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' ); + return null; + + } + + if ( attributes[ name ] === undefined ) attributes[ name ] = []; + + attributes[ name ].push( geometry.attributes[ name ] ); + + attributesCount ++; + + } + + // ensure geometries have the same number of attributes + + if ( attributesCount !== attributesUsed.size ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' ); + return null; + + } + + // gather morph attributes, exit early if they're different + + if ( morphTargetsRelative !== geometry.morphTargetsRelative ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' ); + return null; + + } + + for ( const name in geometry.morphAttributes ) { + + if ( ! morphAttributesUsed.has( name ) ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' ); + return null; + + } + + if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = []; + + morphAttributes[ name ].push( geometry.morphAttributes[ name ] ); + + } + + // gather .userData + + mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || []; + mergedGeometry.userData.mergedUserData.push( geometry.userData ); + + if ( useGroups ) { + + let count; + + if ( isIndexed ) { + + count = geometry.index.count; + + } else if ( geometry.attributes.position !== undefined ) { + + count = geometry.attributes.position.count; + + } else { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' ); + return null; + + } + + mergedGeometry.addGroup( offset, count, i ); + + offset += count; + + } + + } + + // merge indices + + if ( isIndexed ) { + + let indexOffset = 0; + const mergedIndex = []; + + for ( let i = 0; i < geometries.length; ++ i ) { + + const index = geometries[ i ].index; + + for ( let j = 0; j < index.count; ++ j ) { + + mergedIndex.push( index.getX( j ) + indexOffset ); + + } + + indexOffset += geometries[ i ].attributes.position.count; + + } + + mergedGeometry.setIndex( mergedIndex ); + + } + + // merge attributes + + for ( const name in attributes ) { + + const mergedAttribute = mergeBufferAttributes( attributes[ name ] ); + + if ( ! mergedAttribute ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' attribute.' ); + return null; + + } + + mergedGeometry.setAttribute( name, mergedAttribute ); + + } + + // merge morph attributes + + for ( const name in morphAttributes ) { + + const numMorphTargets = morphAttributes[ name ][ 0 ].length; + + if ( numMorphTargets === 0 ) break; + + mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; + mergedGeometry.morphAttributes[ name ] = []; + + for ( let i = 0; i < numMorphTargets; ++ i ) { + + const morphAttributesToMerge = []; + + for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) { + + morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] ); + + } + + const mergedMorphAttribute = mergeBufferAttributes( morphAttributesToMerge ); + + if ( ! mergedMorphAttribute ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' morphAttribute.' ); + return null; + + } + + mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute ); + + } + + } + + return mergedGeometry; + + } + + /** + * @param {Array} attributes + * @return {BufferAttribute} + */ + function mergeBufferAttributes( attributes ) { + + let TypedArray; + let itemSize; + let normalized; + let arrayLength = 0; + + for ( let i = 0; i < attributes.length; ++ i ) { + + const attribute = attributes[ i ]; + + if ( attribute.isInterleavedBufferAttribute ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.' ); + return null; + + } + + if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; + if ( TypedArray !== attribute.array.constructor ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' ); + return null; + + } + + if ( itemSize === undefined ) itemSize = attribute.itemSize; + if ( itemSize !== attribute.itemSize ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' ); + return null; + + } + + if ( normalized === undefined ) normalized = attribute.normalized; + if ( normalized !== attribute.normalized ) { + + console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' ); + return null; + + } + + arrayLength += attribute.array.length; + + } + + const array = new TypedArray( arrayLength ); + let offset = 0; + + for ( let i = 0; i < attributes.length; ++ i ) { + + array.set( attributes[ i ].array, offset ); + + offset += attributes[ i ].array.length; + + } + + return new THREE.BufferAttribute( array, itemSize, normalized ); + + } + + /** + * @param {Array} attributes + * @return {Array} + */ + function interleaveAttributes( attributes ) { + + // Interleaves the provided attributes into an InterleavedBuffer and returns + // a set of InterleavedBufferAttributes for each attribute + let TypedArray; + let arrayLength = 0; + let stride = 0; + + // calculate the the length and type of the interleavedBuffer + for ( let i = 0, l = attributes.length; i < l; ++ i ) { + + const attribute = attributes[ i ]; + + if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; + if ( TypedArray !== attribute.array.constructor ) { + + console.error( 'AttributeBuffers of different types cannot be interleaved' ); + return null; + + } + + arrayLength += attribute.array.length; + stride += attribute.itemSize; + + } + + // Create the set of buffer attributes + const interleavedBuffer = new THREE.InterleavedBuffer( new TypedArray( arrayLength ), stride ); + let offset = 0; + const res = []; + const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; + const setters = [ 'setX', 'setY', 'setZ', 'setW' ]; + + for ( let j = 0, l = attributes.length; j < l; j ++ ) { + + const attribute = attributes[ j ]; + const itemSize = attribute.itemSize; + const count = attribute.count; + const iba = new THREE.InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized ); + res.push( iba ); + + offset += itemSize; + + // Move the data for each attribute into the new interleavedBuffer + // at the appropriate offset + for ( let c = 0; c < count; c ++ ) { + + for ( let k = 0; k < itemSize; k ++ ) { + + iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) ); + + } + + } + + } + + return res; + + } + + /** + * @param {Array} geometry + * @return {number} + */ + function estimateBytesUsed( geometry ) { + + // Return the estimated memory used by this geometry in bytes + // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account + // for InterleavedBufferAttributes. + let mem = 0; + for ( const name in geometry.attributes ) { + + const attr = geometry.getAttribute( name ); + mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT; + + } + + const indices = geometry.getIndex(); + mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0; + return mem; + + } + + /** + * @param {BufferGeometry} geometry + * @param {number} tolerance + * @return {BufferGeometry>} + */ + function mergeVertices( geometry, tolerance = 1e-4 ) { + + tolerance = Math.max( tolerance, Number.EPSILON ); + + // Generate an index buffer if the geometry doesn't have one, or optimize it + // if it's already available. + const hashToIndex = {}; + const indices = geometry.getIndex(); + const positions = geometry.getAttribute( 'position' ); + const vertexCount = indices ? indices.count : positions.count; + + // next value for triangle indices + let nextIndex = 0; + + // attributes and new attribute arrays + const attributeNames = Object.keys( geometry.attributes ); + const attrArrays = {}; + const morphAttrsArrays = {}; + const newIndices = []; + const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; + + // initialize the arrays + for ( let i = 0, l = attributeNames.length; i < l; i ++ ) { + + const name = attributeNames[ i ]; + + attrArrays[ name ] = []; + + const morphAttr = geometry.morphAttributes[ name ]; + if ( morphAttr ) { + + morphAttrsArrays[ name ] = new Array( morphAttr.length ).fill().map( () => [] ); + + } + + } + + // convert the error tolerance to an amount of decimal places to truncate to + const decimalShift = Math.log10( 1 / tolerance ); + const shiftMultiplier = Math.pow( 10, decimalShift ); + for ( let i = 0; i < vertexCount; i ++ ) { + + const index = indices ? indices.getX( i ) : i; + + // Generate a hash for the vertex attributes at the current index 'i' + let hash = ''; + for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { + + const name = attributeNames[ j ]; + const attribute = geometry.getAttribute( name ); + const itemSize = attribute.itemSize; + + for ( let k = 0; k < itemSize; k ++ ) { + + // double tilde truncates the decimal value + hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * shiftMultiplier ) },`; + + } + + } + + // Add another reference to the vertex if it's already + // used by another index + if ( hash in hashToIndex ) { + + newIndices.push( hashToIndex[ hash ] ); + + } else { + + // copy data to the new index in the attribute arrays + for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { + + const name = attributeNames[ j ]; + const attribute = geometry.getAttribute( name ); + const morphAttr = geometry.morphAttributes[ name ]; + const itemSize = attribute.itemSize; + const newarray = attrArrays[ name ]; + const newMorphArrays = morphAttrsArrays[ name ]; + + for ( let k = 0; k < itemSize; k ++ ) { + + const getterFunc = getters[ k ]; + newarray.push( attribute[ getterFunc ]( index ) ); + + if ( morphAttr ) { + + for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) { + + newMorphArrays[ m ].push( morphAttr[ m ][ getterFunc ]( index ) ); + + } + + } + + } + + } + + hashToIndex[ hash ] = nextIndex; + newIndices.push( nextIndex ); + nextIndex ++; + + } + + } + + // Generate typed arrays from new attribute arrays and update + // the attributeBuffers + const result = geometry.clone(); + for ( let i = 0, l = attributeNames.length; i < l; i ++ ) { + + const name = attributeNames[ i ]; + const oldAttribute = geometry.getAttribute( name ); + + const buffer = new oldAttribute.array.constructor( attrArrays[ name ] ); + const attribute = new THREE.BufferAttribute( buffer, oldAttribute.itemSize, oldAttribute.normalized ); + + result.setAttribute( name, attribute ); + + // Update the attribute arrays + if ( name in morphAttrsArrays ) { + + for ( let j = 0; j < morphAttrsArrays[ name ].length; j ++ ) { + + const oldMorphAttribute = geometry.morphAttributes[ name ][ j ]; + + const buffer = new oldMorphAttribute.array.constructor( morphAttrsArrays[ name ][ j ] ); + const morphAttribute = new THREE.BufferAttribute( buffer, oldMorphAttribute.itemSize, oldMorphAttribute.normalized ); + result.morphAttributes[ name ][ j ] = morphAttribute; + + } + + } + + } + + // indices + + result.setIndex( newIndices ); + + return result; + + } + + /** + * @param {BufferGeometry} geometry + * @param {number} drawMode + * @return {BufferGeometry>} + */ + function toTrianglesDrawMode( geometry, drawMode ) { + + if ( drawMode === THREE.TrianglesDrawMode ) { + + console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' ); + return geometry; + + } + + if ( drawMode === THREE.TriangleFanDrawMode || drawMode === THREE.TriangleStripDrawMode ) { + + let index = geometry.getIndex(); + + // generate index if not present + + if ( index === null ) { + + const indices = []; + + const position = geometry.getAttribute( 'position' ); + + if ( position !== undefined ) { + + for ( let i = 0; i < position.count; i ++ ) { + + indices.push( i ); + + } + + geometry.setIndex( indices ); + index = geometry.getIndex(); + + } else { + + console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); + return geometry; + + } + + } + + // + + const numberOfTriangles = index.count - 2; + const newIndices = []; + + if ( drawMode === THREE.TriangleFanDrawMode ) { + + // gl.TRIANGLE_FAN + + for ( let i = 1; i <= numberOfTriangles; i ++ ) { + + newIndices.push( index.getX( 0 ) ); + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + + } + + } else { + + // gl.TRIANGLE_STRIP + + for ( let i = 0; i < numberOfTriangles; i ++ ) { + + if ( i % 2 === 0 ) { + + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i + 2 ) ); + + } else { + + newIndices.push( index.getX( i + 2 ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i ) ); + + } + + } + + } + + if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { + + console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); + + } + + // build final geometry + + const newGeometry = geometry.clone(); + newGeometry.setIndex( newIndices ); + newGeometry.clearGroups(); + + return newGeometry; + + } else { + + console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode ); + return geometry; + + } + + } + + /** + * Calculates the morphed attributes of a morphed/skinned BufferGeometry. + * Helpful for Raytracing or Decals. + * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points. + * @return {Object} An Object with original position/normal attributes and morphed ones. + */ + function computeMorphedAttributes( object ) { + + if ( object.geometry.isBufferGeometry !== true ) { + + console.error( 'THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.' ); + return null; + + } + + const _vA = new THREE.Vector3(); + const _vB = new THREE.Vector3(); + const _vC = new THREE.Vector3(); + + const _tempA = new THREE.Vector3(); + const _tempB = new THREE.Vector3(); + const _tempC = new THREE.Vector3(); + + const _morphA = new THREE.Vector3(); + const _morphB = new THREE.Vector3(); + const _morphC = new THREE.Vector3(); + + function _calculateMorphedAttributeData( + object, + material, + attribute, + morphAttribute, + morphTargetsRelative, + a, + b, + c, + modifiedAttributeArray + ) { + + _vA.fromBufferAttribute( attribute, a ); + _vB.fromBufferAttribute( attribute, b ); + _vC.fromBufferAttribute( attribute, c ); + + const morphInfluences = object.morphTargetInfluences; + + if ( material.morphTargets && morphAttribute && morphInfluences ) { + + _morphA.set( 0, 0, 0 ); + _morphB.set( 0, 0, 0 ); + _morphC.set( 0, 0, 0 ); + + for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { + + const influence = morphInfluences[ i ]; + const morph = morphAttribute[ i ]; + + if ( influence === 0 ) continue; + + _tempA.fromBufferAttribute( morph, a ); + _tempB.fromBufferAttribute( morph, b ); + _tempC.fromBufferAttribute( morph, c ); + + if ( morphTargetsRelative ) { + + _morphA.addScaledVector( _tempA, influence ); + _morphB.addScaledVector( _tempB, influence ); + _morphC.addScaledVector( _tempC, influence ); + + } else { + + _morphA.addScaledVector( _tempA.sub( _vA ), influence ); + _morphB.addScaledVector( _tempB.sub( _vB ), influence ); + _morphC.addScaledVector( _tempC.sub( _vC ), influence ); + + } + + } + + _vA.add( _morphA ); + _vB.add( _morphB ); + _vC.add( _morphC ); + + } + + if ( object.isSkinnedMesh ) { + + object.boneTransform( a, _vA ); + object.boneTransform( b, _vB ); + object.boneTransform( c, _vC ); + + } + + modifiedAttributeArray[ a * 3 + 0 ] = _vA.x; + modifiedAttributeArray[ a * 3 + 1 ] = _vA.y; + modifiedAttributeArray[ a * 3 + 2 ] = _vA.z; + modifiedAttributeArray[ b * 3 + 0 ] = _vB.x; + modifiedAttributeArray[ b * 3 + 1 ] = _vB.y; + modifiedAttributeArray[ b * 3 + 2 ] = _vB.z; + modifiedAttributeArray[ c * 3 + 0 ] = _vC.x; + modifiedAttributeArray[ c * 3 + 1 ] = _vC.y; + modifiedAttributeArray[ c * 3 + 2 ] = _vC.z; + + } + + const geometry = object.geometry; + const material = object.material; + + let a, b, c; + const index = geometry.index; + const positionAttribute = geometry.attributes.position; + const morphPosition = geometry.morphAttributes.position; + const morphTargetsRelative = geometry.morphTargetsRelative; + const normalAttribute = geometry.attributes.normal; + const morphNormal = geometry.morphAttributes.position; + + const groups = geometry.groups; + const drawRange = geometry.drawRange; + let i, j, il, jl; + let group, groupMaterial; + let start, end; + + const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize ); + const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize ); + + if ( index !== null ) { + + // indexed buffer geometry + + if ( Array.isArray( material ) ) { + + for ( i = 0, il = groups.length; i < il; i ++ ) { + + group = groups[ i ]; + groupMaterial = material[ group.materialIndex ]; + + start = Math.max( group.start, drawRange.start ); + end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + + for ( j = start, jl = end; j < jl; j += 3 ) { + + a = index.getX( j ); + b = index.getX( j + 1 ); + c = index.getX( j + 2 ); + + _calculateMorphedAttributeData( + object, + groupMaterial, + positionAttribute, + morphPosition, + morphTargetsRelative, + a, b, c, + modifiedPosition + ); + + _calculateMorphedAttributeData( + object, + groupMaterial, + normalAttribute, + morphNormal, + morphTargetsRelative, + a, b, c, + modifiedNormal + ); + + } + + } + + } else { + + start = Math.max( 0, drawRange.start ); + end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + + for ( i = start, il = end; i < il; i += 3 ) { + + a = index.getX( i ); + b = index.getX( i + 1 ); + c = index.getX( i + 2 ); + + _calculateMorphedAttributeData( + object, + material, + positionAttribute, + morphPosition, + morphTargetsRelative, + a, b, c, + modifiedPosition + ); + + _calculateMorphedAttributeData( + object, + material, + normalAttribute, + morphNormal, + morphTargetsRelative, + a, b, c, + modifiedNormal + ); + + } + + } + + } else { + + // non-indexed buffer geometry + + if ( Array.isArray( material ) ) { + + for ( i = 0, il = groups.length; i < il; i ++ ) { + + group = groups[ i ]; + groupMaterial = material[ group.materialIndex ]; + + start = Math.max( group.start, drawRange.start ); + end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + + for ( j = start, jl = end; j < jl; j += 3 ) { + + a = j; + b = j + 1; + c = j + 2; + + _calculateMorphedAttributeData( + object, + groupMaterial, + positionAttribute, + morphPosition, + morphTargetsRelative, + a, b, c, + modifiedPosition + ); + + _calculateMorphedAttributeData( + object, + groupMaterial, + normalAttribute, + morphNormal, + morphTargetsRelative, + a, b, c, + modifiedNormal + ); + + } + + } + + } else { + + start = Math.max( 0, drawRange.start ); + end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); + + for ( i = start, il = end; i < il; i += 3 ) { + + a = i; + b = i + 1; + c = i + 2; + + _calculateMorphedAttributeData( + object, + material, + positionAttribute, + morphPosition, + morphTargetsRelative, + a, b, c, + modifiedPosition + ); + + _calculateMorphedAttributeData( + object, + material, + normalAttribute, + morphNormal, + morphTargetsRelative, + a, b, c, + modifiedNormal + ); + + } + + } + + } + + const morphedPositionAttribute = new THREE.Float32BufferAttribute( modifiedPosition, 3 ); + const morphedNormalAttribute = new THREE.Float32BufferAttribute( modifiedNormal, 3 ); + + return { + + positionAttribute: positionAttribute, + normalAttribute: normalAttribute, + morphedPositionAttribute: morphedPositionAttribute, + morphedNormalAttribute: morphedNormalAttribute + + }; + + } + + exports.computeMorphedAttributes = computeMorphedAttributes; + exports.computeTangents = computeTangents; + exports.estimateBytesUsed = estimateBytesUsed; + exports.interleaveAttributes = interleaveAttributes; + exports.mergeBufferAttributes = mergeBufferAttributes; + exports.mergeBufferGeometries = mergeBufferGeometries; + exports.mergeVertices = mergeVertices; + exports.toTrianglesDrawMode = toTrianglesDrawMode; + +})); diff --git a/activities/3DVolume.activity/js/cannon.js b/activities/3DVolume.activity/js/cannon.js new file mode 100644 index 000000000..224e09eac --- /dev/null +++ b/activities/3DVolume.activity/js/cannon.js @@ -0,0 +1,13850 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('perf_hooks')) : + typeof define === 'function' && define.amd ? define(['exports', 'perf_hooks'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.CANNON = {}, global.require$$0)); +})(this, (function (exports, require$$0) { 'use strict'; + + var cannon = {}; + + + /** + * Records what objects are colliding with each other + */ + class ObjectCollisionMatrix { + /** + * The matrix storage. + */ + + /** + * @todo Remove useless constructor + */ + constructor() { + this.matrix = {}; + } + /** + * get + */ + + get(bi, bj) { + let { id: i } = bi; + let { id: j } = bj; + + if (j > i) { + const temp = j; + j = i; + i = temp; + } + + return `${i}-${j}` in this.matrix; + } + /** + * set + */ + + set(bi, bj, value) { + let { id: i } = bi; + let { id: j } = bj; + + if (j > i) { + const temp = j; + j = i; + i = temp; + } + + if (value) { + this.matrix[`${i}-${j}`] = true; + } else { + delete this.matrix[`${i}-${j}`]; + } + } + /** + * Empty the matrix + */ + + reset() { + this.matrix = {}; + } + /** + * Set max number of objects + */ + + setNumObjects(n) {} + } + + /** + * A 3x3 matrix. + * Authored by {@link http://github.com/schteppe/ schteppe} + */ + class Mat3 { + /** + * A vector of length 9, containing all matrix elements. + */ + + /** + * @param elements A vector of length 9, containing all matrix elements. + */ + constructor(elements) { + if (elements === void 0) { + elements = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + + this.elements = elements; + } + /** + * Sets the matrix to identity + * @todo Should perhaps be renamed to `setIdentity()` to be more clear. + * @todo Create another function that immediately creates an identity matrix eg. `eye()` + */ + + identity() { + const e = this.elements; + e[0] = 1; + e[1] = 0; + e[2] = 0; + e[3] = 0; + e[4] = 1; + e[5] = 0; + e[6] = 0; + e[7] = 0; + e[8] = 1; + } + /** + * Set all elements to zero + */ + + setZero() { + const e = this.elements; + e[0] = 0; + e[1] = 0; + e[2] = 0; + e[3] = 0; + e[4] = 0; + e[5] = 0; + e[6] = 0; + e[7] = 0; + e[8] = 0; + } + /** + * Sets the matrix diagonal elements from a Vec3 + */ + + setTrace(vector) { + const e = this.elements; + e[0] = vector.x; + e[4] = vector.y; + e[8] = vector.z; + } + /** + * Gets the matrix diagonal elements + */ + + getTrace(target) { + if (target === void 0) { + target = new Vec3(); + } + + const e = this.elements; + target.x = e[0]; + target.y = e[4]; + target.z = e[8]; + return target; + } + /** + * Matrix-Vector multiplication + * @param v The vector to multiply with + * @param target Optional, target to save the result in. + */ + + vmult(v, target) { + if (target === void 0) { + target = new Vec3(); + } + + const e = this.elements; + const x = v.x; + const y = v.y; + const z = v.z; + target.x = e[0] * x + e[1] * y + e[2] * z; + target.y = e[3] * x + e[4] * y + e[5] * z; + target.z = e[6] * x + e[7] * y + e[8] * z; + return target; + } + /** + * Matrix-scalar multiplication + */ + + smult(s) { + for (let i = 0; i < this.elements.length; i++) { + this.elements[i] *= s; + } + } + /** + * Matrix multiplication + * @param matrix Matrix to multiply with from left side. + */ + + mmult(matrix, target) { + if (target === void 0) { + target = new Mat3(); + } + + const A = this.elements; + const B = matrix.elements; + const T = target.elements; + const a11 = A[0], + a12 = A[1], + a13 = A[2], + a21 = A[3], + a22 = A[4], + a23 = A[5], + a31 = A[6], + a32 = A[7], + a33 = A[8]; + const b11 = B[0], + b12 = B[1], + b13 = B[2], + b21 = B[3], + b22 = B[4], + b23 = B[5], + b31 = B[6], + b32 = B[7], + b33 = B[8]; + T[0] = a11 * b11 + a12 * b21 + a13 * b31; + T[1] = a11 * b12 + a12 * b22 + a13 * b32; + T[2] = a11 * b13 + a12 * b23 + a13 * b33; + T[3] = a21 * b11 + a22 * b21 + a23 * b31; + T[4] = a21 * b12 + a22 * b22 + a23 * b32; + T[5] = a21 * b13 + a22 * b23 + a23 * b33; + T[6] = a31 * b11 + a32 * b21 + a33 * b31; + T[7] = a31 * b12 + a32 * b22 + a33 * b32; + T[8] = a31 * b13 + a32 * b23 + a33 * b33; + return target; + } + /** + * Scale each column of the matrix + */ + + scale(vector, target) { + if (target === void 0) { + target = new Mat3(); + } + + const e = this.elements; + const t = target.elements; + + for (let i = 0; i !== 3; i++) { + t[3 * i + 0] = vector.x * e[3 * i + 0]; + t[3 * i + 1] = vector.y * e[3 * i + 1]; + t[3 * i + 2] = vector.z * e[3 * i + 2]; + } + + return target; + } + /** + * Solve Ax=b + * @param b The right hand side + * @param target Optional. Target vector to save in. + * @return The solution x + * @todo should reuse arrays + */ + + solve(b, target) { + if (target === void 0) { + target = new Vec3(); + } + + // Construct equations + const nr = 3; // num rows + + const nc = 4; // num cols + + const eqns = []; + let i; + let j; + + for (i = 0; i < nr * nc; i++) { + eqns.push(0); + } + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + eqns[i + nc * j] = this.elements[i + 3 * j]; + } + } + + eqns[3 + 4 * 0] = b.x; + eqns[3 + 4 * 1] = b.y; + eqns[3 + 4 * 2] = b.z; // Compute right upper triangular version of the matrix - Gauss elimination + + let n = 3; + const k = n; + let np; + const kp = 4; // num rows + + let p; + + do { + i = k - n; + + if (eqns[i + nc * i] === 0) { + // the pivot is null, swap lines + for (j = i + 1; j < k; j++) { + if (eqns[i + nc * j] !== 0) { + np = kp; + + do { + // do ligne( i ) = ligne( i ) + ligne( k ) + p = kp - np; + eqns[p + nc * i] += eqns[p + nc * j]; + } while (--np); + + break; + } + } + } + + if (eqns[i + nc * i] !== 0) { + for (j = i + 1; j < k; j++) { + const multiplier = eqns[i + nc * j] / eqns[i + nc * i]; + np = kp; + + do { + // do ligne( k ) = ligne( k ) - multiplier * ligne( i ) + p = kp - np; + eqns[p + nc * j] = + p <= i ? 0 : eqns[p + nc * j] - eqns[p + nc * i] * multiplier; + } while (--np); + } + } + } while (--n); // Get the solution + + target.z = eqns[2 * nc + 3] / eqns[2 * nc + 2]; + target.y = + (eqns[1 * nc + 3] - eqns[1 * nc + 2] * target.z) / eqns[1 * nc + 1]; + target.x = + (eqns[0 * nc + 3] - + eqns[0 * nc + 2] * target.z - + eqns[0 * nc + 1] * target.y) / + eqns[0 * nc + 0]; + + if ( + isNaN(target.x) || + isNaN(target.y) || + isNaN(target.z) || + target.x === Infinity || + target.y === Infinity || + target.z === Infinity + ) { + throw `Could not solve equation! Got x=[${target.toString()}], b=[${b.toString()}], A=[${this.toString()}]`; + } + + return target; + } + /** + * Get an element in the matrix by index. Index starts at 0, not 1!!! + * @param value If provided, the matrix element will be set to this value. + */ + + e(row, column, value) { + if (value === undefined) { + return this.elements[column + 3 * row]; + } else { + // Set value + this.elements[column + 3 * row] = value; + } + } + /** + * Copy another matrix into this matrix object. + */ + + copy(matrix) { + for (let i = 0; i < matrix.elements.length; i++) { + this.elements[i] = matrix.elements[i]; + } + + return this; + } + /** + * Returns a string representation of the matrix. + */ + + toString() { + let r = ""; + const sep = ","; + + for (let i = 0; i < 9; i++) { + r += this.elements[i] + sep; + } + + return r; + } + /** + * reverse the matrix + * @param target Target matrix to save in. + * @return The solution x + */ + + reverse(target) { + if (target === void 0) { + target = new Mat3(); + } + + // Construct equations + const nr = 3; // num rows + + const nc = 6; // num cols + + const eqns = reverse_eqns; + let i; + let j; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + eqns[i + nc * j] = this.elements[i + 3 * j]; + } + } + + eqns[3 + 6 * 0] = 1; + eqns[3 + 6 * 1] = 0; + eqns[3 + 6 * 2] = 0; + eqns[4 + 6 * 0] = 0; + eqns[4 + 6 * 1] = 1; + eqns[4 + 6 * 2] = 0; + eqns[5 + 6 * 0] = 0; + eqns[5 + 6 * 1] = 0; + eqns[5 + 6 * 2] = 1; // Compute right upper triangular version of the matrix - Gauss elimination + + let n = 3; + const k = n; + let np; + const kp = nc; // num rows + + let p; + + do { + i = k - n; + + if (eqns[i + nc * i] === 0) { + // the pivot is null, swap lines + for (j = i + 1; j < k; j++) { + if (eqns[i + nc * j] !== 0) { + np = kp; + + do { + // do line( i ) = line( i ) + line( k ) + p = kp - np; + eqns[p + nc * i] += eqns[p + nc * j]; + } while (--np); + + break; + } + } + } + + if (eqns[i + nc * i] !== 0) { + for (j = i + 1; j < k; j++) { + const multiplier = eqns[i + nc * j] / eqns[i + nc * i]; + np = kp; + + do { + // do line( k ) = line( k ) - multiplier * line( i ) + p = kp - np; + eqns[p + nc * j] = + p <= i ? 0 : eqns[p + nc * j] - eqns[p + nc * i] * multiplier; + } while (--np); + } + } + } while (--n); // eliminate the upper left triangle of the matrix + + i = 2; + + do { + j = i - 1; + + do { + const multiplier = eqns[i + nc * j] / eqns[i + nc * i]; + np = nc; + + do { + p = nc - np; + eqns[p + nc * j] = eqns[p + nc * j] - eqns[p + nc * i] * multiplier; + } while (--np); + } while (j--); + } while (--i); // operations on the diagonal + + i = 2; + + do { + const multiplier = 1 / eqns[i + nc * i]; + np = nc; + + do { + p = nc - np; + eqns[p + nc * i] = eqns[p + nc * i] * multiplier; + } while (--np); + } while (i--); + + i = 2; + + do { + j = 2; + + do { + p = eqns[nr + j + nc * i]; + + if (isNaN(p) || p === Infinity) { + throw `Could not reverse! A=[${this.toString()}]`; + } + + target.e(i, j, p); + } while (j--); + } while (i--); + + return target; + } + /** + * Set the matrix from a quaterion + */ + + setRotationFromQuaternion(q) { + const x = q.x; + const y = q.y; + const z = q.z; + const w = q.w; + const x2 = x + x; + const y2 = y + y; + const z2 = z + z; + const xx = x * x2; + const xy = x * y2; + const xz = x * z2; + const yy = y * y2; + const yz = y * z2; + const zz = z * z2; + const wx = w * x2; + const wy = w * y2; + const wz = w * z2; + const e = this.elements; + e[3 * 0 + 0] = 1 - (yy + zz); + e[3 * 0 + 1] = xy - wz; + e[3 * 0 + 2] = xz + wy; + e[3 * 1 + 0] = xy + wz; + e[3 * 1 + 1] = 1 - (xx + zz); + e[3 * 1 + 2] = yz - wx; + e[3 * 2 + 0] = xz - wy; + e[3 * 2 + 1] = yz + wx; + e[3 * 2 + 2] = 1 - (xx + yy); + return this; + } + /** + * Transpose the matrix + * @param target Optional. Where to store the result. + * @return The target Mat3, or a new Mat3 if target was omitted. + */ + + transpose(target) { + if (target === void 0) { + target = new Mat3(); + } + + const M = this.elements; + const T = target.elements; + let tmp; //Set diagonals + + T[0] = M[0]; + T[4] = M[4]; + T[8] = M[8]; + tmp = M[1]; + T[1] = M[3]; + T[3] = tmp; + tmp = M[2]; + T[2] = M[6]; + T[6] = tmp; + tmp = M[5]; + T[5] = M[7]; + T[7] = tmp; + return target; + } + } + const reverse_eqns = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /** + * 3-dimensional vector + * @example + * const v = new Vec3(1, 2, 3) + * console.log('x=' + v.x) // x=1 + */ + + class Vec3 { + constructor(x, y, z) { + if (x === void 0) { + x = 0.0; + } + + if (y === void 0) { + y = 0.0; + } + + if (z === void 0) { + z = 0.0; + } + + this.x = x; + this.y = y; + this.z = z; + } + /** + * Vector cross product + * @param target Optional target to save in. + */ + + cross(vector, target) { + if (target === void 0) { + target = new Vec3(); + } + + const vx = vector.x; + const vy = vector.y; + const vz = vector.z; + const x = this.x; + const y = this.y; + const z = this.z; + target.x = y * vz - z * vy; + target.y = z * vx - x * vz; + target.z = x * vy - y * vx; + return target; + } + /** + * Set the vectors' 3 elements + */ + + set(x, y, z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + /** + * Set all components of the vector to zero. + */ + + setZero() { + this.x = this.y = this.z = 0; + } + /** + * Vector addition + */ + + vadd(vector, target) { + if (target) { + target.x = vector.x + this.x; + target.y = vector.y + this.y; + target.z = vector.z + this.z; + } else { + return new Vec3(this.x + vector.x, this.y + vector.y, this.z + vector.z); + } + } + /** + * Vector subtraction + * @param target Optional target to save in. + */ + + vsub(vector, target) { + if (target) { + target.x = this.x - vector.x; + target.y = this.y - vector.y; + target.z = this.z - vector.z; + } else { + return new Vec3(this.x - vector.x, this.y - vector.y, this.z - vector.z); + } + } + /** + * Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c + * + * See {@link https://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf Umeå University Lecture} + */ + + crossmat() { + return new Mat3([ + 0, + -this.z, + this.y, + this.z, + 0, + -this.x, + -this.y, + this.x, + 0, + ]); + } + /** + * Normalize the vector. Note that this changes the values in the vector. + * @return Returns the norm of the vector + */ + + normalize() { + const x = this.x; + const y = this.y; + const z = this.z; + const n = Math.sqrt(x * x + y * y + z * z); + + if (n > 0.0) { + const invN = 1 / n; + this.x *= invN; + this.y *= invN; + this.z *= invN; + } else { + // Make something up + this.x = 0; + this.y = 0; + this.z = 0; + } + + return n; + } + /** + * Get the version of this vector that is of length 1. + * @param target Optional target to save in + * @return Returns the unit vector + */ + + unit(target) { + if (target === void 0) { + target = new Vec3(); + } + + const x = this.x; + const y = this.y; + const z = this.z; + let ninv = Math.sqrt(x * x + y * y + z * z); + + if (ninv > 0.0) { + ninv = 1.0 / ninv; + target.x = x * ninv; + target.y = y * ninv; + target.z = z * ninv; + } else { + target.x = 1; + target.y = 0; + target.z = 0; + } + + return target; + } + /** + * Get the length of the vector + */ + + length() { + const x = this.x; + const y = this.y; + const z = this.z; + return Math.sqrt(x * x + y * y + z * z); + } + /** + * Get the squared length of the vector. + */ + + lengthSquared() { + return this.dot(this); + } + /** + * Get distance from this point to another point + */ + + distanceTo(p) { + const x = this.x; + const y = this.y; + const z = this.z; + const px = p.x; + const py = p.y; + const pz = p.z; + return Math.sqrt( + (px - x) * (px - x) + (py - y) * (py - y) + (pz - z) * (pz - z) + ); + } + /** + * Get squared distance from this point to another point + */ + + distanceSquared(p) { + const x = this.x; + const y = this.y; + const z = this.z; + const px = p.x; + const py = p.y; + const pz = p.z; + return (px - x) * (px - x) + (py - y) * (py - y) + (pz - z) * (pz - z); + } + /** + * Multiply all the components of the vector with a scalar. + * @param target The vector to save the result in. + */ + + scale(scalar, target) { + if (target === void 0) { + target = new Vec3(); + } + + const x = this.x; + const y = this.y; + const z = this.z; + target.x = scalar * x; + target.y = scalar * y; + target.z = scalar * z; + return target; + } + /** + * Multiply the vector with an other vector, component-wise. + * @param target The vector to save the result in. + */ + + vmul(vector, target) { + if (target === void 0) { + target = new Vec3(); + } + + target.x = vector.x * this.x; + target.y = vector.y * this.y; + target.z = vector.z * this.z; + return target; + } + /** + * Scale a vector and add it to this vector. Save the result in "target". (target = this + vector * scalar) + * @param target The vector to save the result in. + */ + + addScaledVector(scalar, vector, target) { + if (target === void 0) { + target = new Vec3(); + } + + target.x = this.x + scalar * vector.x; + target.y = this.y + scalar * vector.y; + target.z = this.z + scalar * vector.z; + return target; + } + /** + * Calculate dot product + * @param vector + */ + + dot(vector) { + return this.x * vector.x + this.y * vector.y + this.z * vector.z; + } + + isZero() { + return this.x === 0 && this.y === 0 && this.z === 0; + } + /** + * Make the vector point in the opposite direction. + * @param target Optional target to save in + */ + + negate(target) { + if (target === void 0) { + target = new Vec3(); + } + + target.x = -this.x; + target.y = -this.y; + target.z = -this.z; + return target; + } + /** + * Compute two artificial tangents to the vector + * @param t1 Vector object to save the first tangent in + * @param t2 Vector object to save the second tangent in + */ + + tangents(t1, t2) { + const norm = this.length(); + + if (norm > 0.0) { + const n = Vec3_tangents_n; + const inorm = 1 / norm; + n.set(this.x * inorm, this.y * inorm, this.z * inorm); + const randVec = Vec3_tangents_randVec; + + if (Math.abs(n.x) < 0.9) { + randVec.set(1, 0, 0); + n.cross(randVec, t1); + } else { + randVec.set(0, 1, 0); + n.cross(randVec, t1); + } + + n.cross(t1, t2); + } else { + // The normal length is zero, make something up + t1.set(1, 0, 0); + t2.set(0, 1, 0); + } + } + /** + * Converts to a more readable format + */ + + toString() { + return `${this.x},${this.y},${this.z}`; + } + /** + * Converts to an array + */ + + toArray() { + return [this.x, this.y, this.z]; + } + /** + * Copies value of source to this vector. + */ + + copy(vector) { + this.x = vector.x; + this.y = vector.y; + this.z = vector.z; + return this; + } + /** + * Do a linear interpolation between two vectors + * @param t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them. + */ + + lerp(vector, t, target) { + const x = this.x; + const y = this.y; + const z = this.z; + target.x = x + (vector.x - x) * t; + target.y = y + (vector.y - y) * t; + target.z = z + (vector.z - z) * t; + } + /** + * Check if a vector equals is almost equal to another one. + */ + + almostEquals(vector, precision) { + if (precision === void 0) { + precision = 1e-6; + } + + if ( + Math.abs(this.x - vector.x) > precision || + Math.abs(this.y - vector.y) > precision || + Math.abs(this.z - vector.z) > precision + ) { + return false; + } + + return true; + } + /** + * Check if a vector is almost zero + */ + + almostZero(precision) { + if (precision === void 0) { + precision = 1e-6; + } + + if ( + Math.abs(this.x) > precision || + Math.abs(this.y) > precision || + Math.abs(this.z) > precision + ) { + return false; + } + + return true; + } + /** + * Check if the vector is anti-parallel to another vector. + * @param precision Set to zero for exact comparisons + */ + + isAntiparallelTo(vector, precision) { + this.negate(antip_neg); + return antip_neg.almostEquals(vector, precision); + } + /** + * Clone the vector + */ + + clone() { + return new Vec3(this.x, this.y, this.z); + } + } + Vec3.ZERO = new Vec3(0, 0, 0); + Vec3.UNIT_X = new Vec3(1, 0, 0); + Vec3.UNIT_Y = new Vec3(0, 1, 0); + Vec3.UNIT_Z = new Vec3(0, 0, 1); + const Vec3_tangents_n = new Vec3(); + const Vec3_tangents_randVec = new Vec3(); + const antip_neg = new Vec3(); + + /** + * Axis aligned bounding box class. + */ + class AABB { + /** + * The lower bound of the bounding box + */ + + /** + * The upper bound of the bounding box + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + this.lowerBound = new Vec3(); + this.upperBound = new Vec3(); + + if (options.lowerBound) { + this.lowerBound.copy(options.lowerBound); + } + + if (options.upperBound) { + this.upperBound.copy(options.upperBound); + } + } + /** + * Set the AABB bounds from a set of points. + * @param points An array of Vec3's. + * @return The self object + */ + + setFromPoints(points, position, quaternion, skinSize) { + const l = this.lowerBound; + const u = this.upperBound; + const q = quaternion; // Set to the first point + + l.copy(points[0]); + + if (q) { + q.vmult(l, l); + } + + u.copy(l); + + for (let i = 1; i < points.length; i++) { + let p = points[i]; + + if (q) { + q.vmult(p, tmp$1); + p = tmp$1; + } + + if (p.x > u.x) { + u.x = p.x; + } + + if (p.x < l.x) { + l.x = p.x; + } + + if (p.y > u.y) { + u.y = p.y; + } + + if (p.y < l.y) { + l.y = p.y; + } + + if (p.z > u.z) { + u.z = p.z; + } + + if (p.z < l.z) { + l.z = p.z; + } + } // Add offset + + if (position) { + position.vadd(l, l); + position.vadd(u, u); + } + + if (skinSize) { + l.x -= skinSize; + l.y -= skinSize; + l.z -= skinSize; + u.x += skinSize; + u.y += skinSize; + u.z += skinSize; + } + + return this; + } + /** + * Copy bounds from an AABB to this AABB + * @param aabb Source to copy from + * @return The this object, for chainability + */ + + copy(aabb) { + this.lowerBound.copy(aabb.lowerBound); + this.upperBound.copy(aabb.upperBound); + return this; + } + /** + * Clone an AABB + */ + + clone() { + return new AABB().copy(this); + } + /** + * Extend this AABB so that it covers the given AABB too. + */ + + extend(aabb) { + this.lowerBound.x = Math.min(this.lowerBound.x, aabb.lowerBound.x); + this.upperBound.x = Math.max(this.upperBound.x, aabb.upperBound.x); + this.lowerBound.y = Math.min(this.lowerBound.y, aabb.lowerBound.y); + this.upperBound.y = Math.max(this.upperBound.y, aabb.upperBound.y); + this.lowerBound.z = Math.min(this.lowerBound.z, aabb.lowerBound.z); + this.upperBound.z = Math.max(this.upperBound.z, aabb.upperBound.z); + } + /** + * Returns true if the given AABB overlaps this AABB. + */ + + overlaps(aabb) { + const l1 = this.lowerBound; + const u1 = this.upperBound; + const l2 = aabb.lowerBound; + const u2 = aabb.upperBound; // l2 u2 + // |---------| + // |--------| + // l1 u1 + + const overlapsX = + (l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x); + const overlapsY = + (l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y); + const overlapsZ = + (l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z); + return overlapsX && overlapsY && overlapsZ; + } // Mostly for debugging + + volume() { + const l = this.lowerBound; + const u = this.upperBound; + return (u.x - l.x) * (u.y - l.y) * (u.z - l.z); + } + /** + * Returns true if the given AABB is fully contained in this AABB. + */ + + contains(aabb) { + const l1 = this.lowerBound; + const u1 = this.upperBound; + const l2 = aabb.lowerBound; + const u2 = aabb.upperBound; // l2 u2 + // |---------| + // |---------------| + // l1 u1 + + return ( + l1.x <= l2.x && + u1.x >= u2.x && + l1.y <= l2.y && + u1.y >= u2.y && + l1.z <= l2.z && + u1.z >= u2.z + ); + } + + getCorners(a, b, c, d, e, f, g, h) { + const l = this.lowerBound; + const u = this.upperBound; + a.copy(l); + b.set(u.x, l.y, l.z); + c.set(u.x, u.y, l.z); + d.set(l.x, u.y, u.z); + e.set(u.x, l.y, u.z); + f.set(l.x, u.y, l.z); + g.set(l.x, l.y, u.z); + h.copy(u); + } + /** + * Get the representation of an AABB in another frame. + * @return The "target" AABB object. + */ + + toLocalFrame(frame, target) { + const corners = transformIntoFrame_corners; + const a = corners[0]; + const b = corners[1]; + const c = corners[2]; + const d = corners[3]; + const e = corners[4]; + const f = corners[5]; + const g = corners[6]; + const h = corners[7]; // Get corners in current frame + + this.getCorners(a, b, c, d, e, f, g, h); // Transform them to new local frame + + for (let i = 0; i !== 8; i++) { + const corner = corners[i]; + frame.pointToLocal(corner, corner); + } + + return target.setFromPoints(corners); + } + /** + * Get the representation of an AABB in the global frame. + * @return The "target" AABB object. + */ + + toWorldFrame(frame, target) { + const corners = transformIntoFrame_corners; + const a = corners[0]; + const b = corners[1]; + const c = corners[2]; + const d = corners[3]; + const e = corners[4]; + const f = corners[5]; + const g = corners[6]; + const h = corners[7]; // Get corners in current frame + + this.getCorners(a, b, c, d, e, f, g, h); // Transform them to new local frame + + for (let i = 0; i !== 8; i++) { + const corner = corners[i]; + frame.pointToWorld(corner, corner); + } + + return target.setFromPoints(corners); + } + /** + * Check if the AABB is hit by a ray. + */ + + overlapsRay(ray) { + const { direction, from } = ray; // const t = 0 + // ray.direction is unit direction vector of ray + + const dirFracX = 1 / direction.x; + const dirFracY = 1 / direction.y; + const dirFracZ = 1 / direction.z; // this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner + + const t1 = (this.lowerBound.x - from.x) * dirFracX; + const t2 = (this.upperBound.x - from.x) * dirFracX; + const t3 = (this.lowerBound.y - from.y) * dirFracY; + const t4 = (this.upperBound.y - from.y) * dirFracY; + const t5 = (this.lowerBound.z - from.z) * dirFracZ; + const t6 = (this.upperBound.z - from.z) * dirFracZ; // const tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4))); + // const tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4))); + + const tmin = Math.max( + Math.max(Math.min(t1, t2), Math.min(t3, t4)), + Math.min(t5, t6) + ); + const tmax = Math.min( + Math.min(Math.max(t1, t2), Math.max(t3, t4)), + Math.max(t5, t6) + ); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us + + if (tmax < 0) { + //t = tmax; + return false; + } // if tmin > tmax, ray doesn't intersect AABB + + if (tmin > tmax) { + //t = tmax; + return false; + } + + return true; + } + } + const tmp$1 = new Vec3(); + const transformIntoFrame_corners = [ + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + ]; + + /** + * Collision "matrix". + * It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step + */ + class ArrayCollisionMatrix { + /** + * The matrix storage. + */ + constructor() { + this.matrix = []; + } + /** + * Get an element + */ + + get(bi, bj) { + let { index: i } = bi; + let { index: j } = bj; + + if (j > i) { + const temp = j; + j = i; + i = temp; + } + + return this.matrix[((i * (i + 1)) >> 1) + j - 1]; + } + /** + * Set an element + */ + + set(bi, bj, value) { + let { index: i } = bi; + let { index: j } = bj; + + if (j > i) { + const temp = j; + j = i; + i = temp; + } + + this.matrix[((i * (i + 1)) >> 1) + j - 1] = value ? 1 : 0; + } + /** + * Sets all elements to zero + */ + + reset() { + for (let i = 0, l = this.matrix.length; i !== l; i++) { + this.matrix[i] = 0; + } + } + /** + * Sets the max number of objects + */ + + setNumObjects(n) { + this.matrix.length = (n * (n - 1)) >> 1; + } + } + + /** + * Base class for objects that dispatches events. + */ + class EventTarget { + /** + * Add an event listener + * @return The self object, for chainability. + */ + addEventListener(type, listener) { + if (this._listeners === undefined) { + this._listeners = {}; + } + + const listeners = this._listeners; + + if (listeners[type] === undefined) { + listeners[type] = []; + } + + if (!listeners[type].includes(listener)) { + listeners[type].push(listener); + } + + return this; + } + /** + * Check if an event listener is added + */ + + hasEventListener(type, listener) { + if (this._listeners === undefined) { + return false; + } + + const listeners = this._listeners; + + if (listeners[type] !== undefined && listeners[type].includes(listener)) { + return true; + } + + return false; + } + /** + * Check if any event listener of the given type is added + */ + + hasAnyEventListener(type) { + if (this._listeners === undefined) { + return false; + } + + const listeners = this._listeners; + return listeners[type] !== undefined; + } + /** + * Remove an event listener + * @return The self object, for chainability. + */ + + removeEventListener(type, listener) { + if (this._listeners === undefined) { + return this; + } + + const listeners = this._listeners; + + if (listeners[type] === undefined) { + return this; + } + + const index = listeners[type].indexOf(listener); + + if (index !== -1) { + listeners[type].splice(index, 1); + } + + return this; + } + /** + * Emit an event. + * @return The self object, for chainability. + */ + + dispatchEvent(event) { + if (this._listeners === undefined) { + return this; + } + + const listeners = this._listeners; + const listenerArray = listeners[event.type]; + + if (listenerArray !== undefined) { + event.target = this; + + for (let i = 0, l = listenerArray.length; i < l; i++) { + listenerArray[i].call(this, event); + } + } + + return this; + } + } + + /** + * A Quaternion describes a rotation in 3D space. The Quaternion is mathematically defined as Q = x*i + y*j + z*k + w, where (i,j,k) are imaginary basis vectors. (x,y,z) can be seen as a vector related to the axis of rotation, while the real multiplier, w, is related to the amount of rotation. + * @param x Multiplier of the imaginary basis vector i. + * @param y Multiplier of the imaginary basis vector j. + * @param z Multiplier of the imaginary basis vector k. + * @param w Multiplier of the real part. + * @see http://en.wikipedia.org/wiki/Quaternion + */ + + class Quaternion { + constructor(x, y, z, w) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + if (z === void 0) { + z = 0; + } + + if (w === void 0) { + w = 1; + } + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + /** + * Set the value of the quaternion. + */ + + set(x, y, z, w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + /** + * Convert to a readable format + * @return "x,y,z,w" + */ + + toString() { + return `${this.x},${this.y},${this.z},${this.w}`; + } + /** + * Convert to an Array + * @return [x, y, z, w] + */ + + toArray() { + return [this.x, this.y, this.z, this.w]; + } + /** + * Set the quaternion components given an axis and an angle in radians. + */ + + setFromAxisAngle(vector, angle) { + const s = Math.sin(angle * 0.5); + this.x = vector.x * s; + this.y = vector.y * s; + this.z = vector.z * s; + this.w = Math.cos(angle * 0.5); + return this; + } + /** + * Converts the quaternion to [ axis, angle ] representation. + * @param targetAxis A vector object to reuse for storing the axis. + * @return An array, first element is the axis and the second is the angle in radians. + */ + + toAxisAngle(targetAxis) { + if (targetAxis === void 0) { + targetAxis = new Vec3(); + } + + this.normalize(); // if w>1 acos and sqrt will produce errors, this cant happen if quaternion is normalised + + const angle = 2 * Math.acos(this.w); + const s = Math.sqrt(1 - this.w * this.w); // assuming quaternion normalised then w is less than 1, so term always positive. + + if (s < 0.001) { + // test to avoid divide by zero, s is always positive due to sqrt + // if s close to zero then direction of axis not important + targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0; + + targetAxis.y = this.y; + targetAxis.z = this.z; + } else { + targetAxis.x = this.x / s; // normalise axis + + targetAxis.y = this.y / s; + targetAxis.z = this.z / s; + } + + return [targetAxis, angle]; + } + /** + * Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v. + */ + + setFromVectors(u, v) { + if (u.isAntiparallelTo(v)) { + const t1 = sfv_t1; + const t2 = sfv_t2; + u.tangents(t1, t2); + this.setFromAxisAngle(t1, Math.PI); + } else { + const a = u.cross(v); + this.x = a.x; + this.y = a.y; + this.z = a.z; + this.w = Math.sqrt(u.length() ** 2 * v.length() ** 2) + u.dot(v); + this.normalize(); + } + + return this; + } + /** + * Multiply the quaternion with an other quaternion. + */ + + mult(quat, target) { + if (target === void 0) { + target = new Quaternion(); + } + + const ax = this.x; + const ay = this.y; + const az = this.z; + const aw = this.w; + const bx = quat.x; + const by = quat.y; + const bz = quat.z; + const bw = quat.w; + target.x = ax * bw + aw * bx + ay * bz - az * by; + target.y = ay * bw + aw * by + az * bx - ax * bz; + target.z = az * bw + aw * bz + ax * by - ay * bx; + target.w = aw * bw - ax * bx - ay * by - az * bz; + return target; + } + /** + * Get the inverse quaternion rotation. + */ + + inverse(target) { + if (target === void 0) { + target = new Quaternion(); + } + + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + this.conjugate(target); + const inorm2 = 1 / (x * x + y * y + z * z + w * w); + target.x *= inorm2; + target.y *= inorm2; + target.z *= inorm2; + target.w *= inorm2; + return target; + } + /** + * Get the quaternion conjugate + */ + + conjugate(target) { + if (target === void 0) { + target = new Quaternion(); + } + + target.x = -this.x; + target.y = -this.y; + target.z = -this.z; + target.w = this.w; + return target; + } + /** + * Normalize the quaternion. Note that this changes the values of the quaternion. + */ + + normalize() { + let l = Math.sqrt( + this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w + ); + + if (l === 0) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } else { + l = 1 / l; + this.x *= l; + this.y *= l; + this.z *= l; + this.w *= l; + } + + return this; + } + /** + * Approximation of quaternion normalization. Works best when quat is already almost-normalized. + * @author unphased, https://github.com/unphased + */ + + normalizeFast() { + const f = + (3.0 - + (this.x * this.x + + this.y * this.y + + this.z * this.z + + this.w * this.w)) / + 2.0; + + if (f === 0) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + } else { + this.x *= f; + this.y *= f; + this.z *= f; + this.w *= f; + } + + return this; + } + /** + * Multiply the quaternion by a vector + */ + + vmult(v, target) { + if (target === void 0) { + target = new Vec3(); + } + + const x = v.x; + const y = v.y; + const z = v.z; + const qx = this.x; + const qy = this.y; + const qz = this.z; + const qw = this.w; // q*v + + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = -qx * x - qy * y - qz * z; + target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return target; + } + /** + * Copies value of source to this quaternion. + * @return this + */ + + copy(quat) { + this.x = quat.x; + this.y = quat.y; + this.z = quat.z; + this.w = quat.w; + return this; + } + /** + * Convert the quaternion to euler angle representation. Order: YZX, as this page describes: https://www.euclideanspace.com/maths/standards/index.htm + * @param order Three-character string, defaults to "YZX" + */ + + toEuler(target, order) { + if (order === void 0) { + order = "YZX"; + } + + let heading; + let attitude; + let bank; + const x = this.x; + const y = this.y; + const z = this.z; + const w = this.w; + + switch (order) { + case "YZX": + const test = x * y + z * w; + + if (test > 0.499) { + // singularity at north pole + heading = 2 * Math.atan2(x, w); + attitude = Math.PI / 2; + bank = 0; + } + + if (test < -0.499) { + // singularity at south pole + heading = -2 * Math.atan2(x, w); + attitude = -Math.PI / 2; + bank = 0; + } + + if (heading === undefined) { + const sqx = x * x; + const sqy = y * y; + const sqz = z * z; + heading = Math.atan2(2 * y * w - 2 * x * z, 1 - 2 * sqy - 2 * sqz); // Heading + + attitude = Math.asin(2 * test); // attitude + + bank = Math.atan2(2 * x * w - 2 * y * z, 1 - 2 * sqx - 2 * sqz); // bank + } + + break; + + default: + throw new Error(`Euler order ${order} not supported yet.`); + } + + target.y = heading; + target.z = attitude; + target.x = bank; + } + /** + * Set the quaternion components given Euler angle representation. + * + * @param order The order to apply angles: 'XYZ' or 'YXZ' or any other combination. + * + * See {@link https://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors MathWorks} reference + */ + + setFromEuler(x, y, z, order) { + if (order === void 0) { + order = "XYZ"; + } + + const c1 = Math.cos(x / 2); + const c2 = Math.cos(y / 2); + const c3 = Math.cos(z / 2); + const s1 = Math.sin(x / 2); + const s2 = Math.sin(y / 2); + const s3 = Math.sin(z / 2); + + if (order === "XYZ") { + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + } else if (order === "YXZ") { + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + } else if (order === "ZXY") { + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + } else if (order === "ZYX") { + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + } else if (order === "YZX") { + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + } else if (order === "XZY") { + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + } + + return this; + } + + clone() { + return new Quaternion(this.x, this.y, this.z, this.w); + } + /** + * Performs a spherical linear interpolation between two quat + * + * @param toQuat second operand + * @param t interpolation amount between the self quaternion and toQuat + * @param target A quaternion to store the result in. If not provided, a new one will be created. + * @returns {Quaternion} The "target" object + */ + + slerp(toQuat, t, target) { + if (target === void 0) { + target = new Quaternion(); + } + + const ax = this.x; + const ay = this.y; + const az = this.z; + const aw = this.w; + let bx = toQuat.x; + let by = toQuat.y; + let bz = toQuat.z; + let bw = toQuat.w; + let omega; + let cosom; + let sinom; + let scale0; + let scale1; // calc cosine + + cosom = ax * bx + ay * by + az * bz + aw * bw; // adjust signs (if necessary) + + if (cosom < 0.0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } // calculate coefficients + + if (1.0 - cosom > 0.000001) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } // calculate final values + + target.x = scale0 * ax + scale1 * bx; + target.y = scale0 * ay + scale1 * by; + target.z = scale0 * az + scale1 * bz; + target.w = scale0 * aw + scale1 * bw; + return target; + } + /** + * Rotate an absolute orientation quaternion given an angular velocity and a time step. + */ + + integrate(angularVelocity, dt, angularFactor, target) { + if (target === void 0) { + target = new Quaternion(); + } + + const ax = angularVelocity.x * angularFactor.x, + ay = angularVelocity.y * angularFactor.y, + az = angularVelocity.z * angularFactor.z, + bx = this.x, + by = this.y, + bz = this.z, + bw = this.w; + const half_dt = dt * 0.5; + target.x += half_dt * (ax * bw + ay * bz - az * by); + target.y += half_dt * (ay * bw + az * bx - ax * bz); + target.z += half_dt * (az * bw + ax * by - ay * bx); + target.w += half_dt * (-ax * bx - ay * by - az * bz); + return target; + } + } + const sfv_t1 = new Vec3(); + const sfv_t2 = new Vec3(); + + /** + * The available shape types. + */ + const SHAPE_TYPES = { + /** SPHERE */ + SPHERE: 1, + + /** PLANE */ + PLANE: 2, + + /** BOX */ + BOX: 4, + + /** COMPOUND */ + COMPOUND: 8, + + /** CONVEXPOLYHEDRON */ + CONVEXPOLYHEDRON: 16, + + /** HEIGHTFIELD */ + HEIGHTFIELD: 32, + + /** PARTICLE */ + PARTICLE: 64, + + /** CYLINDER */ + CYLINDER: 128, + + /** TRIMESH */ + TRIMESH: 256, + }; + /** + * ShapeType + */ + + /** + * Base class for shapes + */ + class Shape { + /** + * Identifier of the Shape. + */ + + /** + * The type of this shape. Must be set to an int > 0 by subclasses. + */ + + /** + * The local bounding sphere radius of this shape. + */ + + /** + * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. + * @default true + */ + + /** + * @default 1 + */ + + /** + * @default -1 + */ + + /** + * Optional material of the shape that regulates contact properties. + */ + + /** + * The body to which the shape is added to. + */ + + /** + * All the Shape types. + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + this.id = Shape.idCounter++; + this.type = options.type || 0; + this.boundingSphereRadius = 0; + this.collisionResponse = options.collisionResponse + ? options.collisionResponse + : true; + this.collisionFilterGroup = + options.collisionFilterGroup !== undefined + ? options.collisionFilterGroup + : 1; + this.collisionFilterMask = + options.collisionFilterMask !== undefined + ? options.collisionFilterMask + : -1; + this.material = options.material ? options.material : null; + this.body = null; + } + /** + * Computes the bounding sphere radius. + * The result is stored in the property `.boundingSphereRadius` + */ + + updateBoundingSphereRadius() { + throw `computeBoundingSphereRadius() not implemented for shape type ${this.type}`; + } + /** + * Get the volume of this shape + */ + + volume() { + throw `volume() not implemented for shape type ${this.type}`; + } + /** + * Calculates the inertia in the local frame for this shape. + * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia + */ + + calculateLocalInertia(mass, target) { + throw `calculateLocalInertia() not implemented for shape type ${this.type}`; + } + /** + * @todo use abstract for these kind of methods + */ + + calculateWorldAABB(pos, quat, min, max) { + throw `calculateWorldAABB() not implemented for shape type ${this.type}`; + } + } + Shape.idCounter = 0; + Shape.types = SHAPE_TYPES; + + /** + * Transformation utilities. + */ + class Transform { + /** + * position + */ + + /** + * quaternion + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + this.position = new Vec3(); + this.quaternion = new Quaternion(); + + if (options.position) { + this.position.copy(options.position); + } + + if (options.quaternion) { + this.quaternion.copy(options.quaternion); + } + } + /** + * Get a global point in local transform coordinates. + */ + + pointToLocal(worldPoint, result) { + return Transform.pointToLocalFrame( + this.position, + this.quaternion, + worldPoint, + result + ); + } + /** + * Get a local point in global transform coordinates. + */ + + pointToWorld(localPoint, result) { + return Transform.pointToWorldFrame( + this.position, + this.quaternion, + localPoint, + result + ); + } + /** + * vectorToWorldFrame + */ + + vectorToWorldFrame(localVector, result) { + if (result === void 0) { + result = new Vec3(); + } + + this.quaternion.vmult(localVector, result); + return result; + } + /** + * pointToLocalFrame + */ + + static pointToLocalFrame(position, quaternion, worldPoint, result) { + if (result === void 0) { + result = new Vec3(); + } + + worldPoint.vsub(position, result); + quaternion.conjugate(tmpQuat$1); + tmpQuat$1.vmult(result, result); + return result; + } + /** + * pointToWorldFrame + */ + + static pointToWorldFrame(position, quaternion, localPoint, result) { + if (result === void 0) { + result = new Vec3(); + } + + quaternion.vmult(localPoint, result); + result.vadd(position, result); + return result; + } + /** + * vectorToWorldFrame + */ + + static vectorToWorldFrame(quaternion, localVector, result) { + if (result === void 0) { + result = new Vec3(); + } + + quaternion.vmult(localVector, result); + return result; + } + /** + * vectorToLocalFrame + */ + + static vectorToLocalFrame(position, quaternion, worldVector, result) { + if (result === void 0) { + result = new Vec3(); + } + + quaternion.w *= -1; + quaternion.vmult(worldVector, result); + quaternion.w *= -1; + return result; + } + } + const tmpQuat$1 = new Quaternion(); + + /** + * A set of polygons describing a convex shape. + * + * The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained + * in the same 3D plane), instead these should be merged into one polygon. + * + * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f) + * @author schteppe / https://github.com/schteppe + * @see https://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/ + * + * @todo Move the clipping functions to ContactGenerator? + * @todo Automatically merge coplanar polygons in constructor. + * @example + * const convexShape = new CANNON.ConvexPolyhedron({ vertices, faces }) + * const convexBody = new CANNON.Body({ mass: 1, shape: convexShape }) + * world.addBody(convexBody) + */ + class ConvexPolyhedron extends Shape { + /** vertices */ + + /** + * Array of integer arrays, indicating which vertices each face consists of + */ + + /** faceNormals */ + + /** worldVertices */ + + /** worldVerticesNeedsUpdate */ + + /** worldFaceNormals */ + + /** worldFaceNormalsNeedsUpdate */ + + /** + * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check. + */ + + /** uniqueEdges */ + + /** + * @param vertices An array of Vec3's + * @param faces Array of integer arrays, describing which vertices that is included in each face. + */ + constructor(props) { + if (props === void 0) { + props = {}; + } + + const { + vertices = [], + faces = [], + normals = [], + axes, + boundingSphereRadius, + } = props; + super({ + type: Shape.types.CONVEXPOLYHEDRON, + }); + this.vertices = vertices; + this.faces = faces; + this.faceNormals = normals; + + if (this.faceNormals.length === 0) { + this.computeNormals(); + } + + if (!boundingSphereRadius) { + this.updateBoundingSphereRadius(); + } else { + this.boundingSphereRadius = boundingSphereRadius; + } + + this.worldVertices = []; // World transformed version of .vertices + + this.worldVerticesNeedsUpdate = true; + this.worldFaceNormals = []; // World transformed version of .faceNormals + + this.worldFaceNormalsNeedsUpdate = true; + this.uniqueAxes = axes ? axes.slice() : null; + this.uniqueEdges = []; + this.computeEdges(); + } + /** + * Computes uniqueEdges + */ + + computeEdges() { + const faces = this.faces; + const vertices = this.vertices; + const edges = this.uniqueEdges; + edges.length = 0; + const edge = new Vec3(); + + for (let i = 0; i !== faces.length; i++) { + const face = faces[i]; + const numVertices = face.length; + + for (let j = 0; j !== numVertices; j++) { + const k = (j + 1) % numVertices; + vertices[face[j]].vsub(vertices[face[k]], edge); + edge.normalize(); + let found = false; + + for (let p = 0; p !== edges.length; p++) { + if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)) { + found = true; + break; + } + } + + if (!found) { + edges.push(edge.clone()); + } + } + } + } + /** + * Compute the normals of the faces. + * Will reuse existing Vec3 objects in the `faceNormals` array if they exist. + */ + + computeNormals() { + this.faceNormals.length = this.faces.length; // Generate normals + + for (let i = 0; i < this.faces.length; i++) { + // Check so all vertices exists for this face + for (let j = 0; j < this.faces[i].length; j++) { + if (!this.vertices[this.faces[i][j]]) { + throw new Error(`Vertex ${this.faces[i][j]} not found!`); + } + } + + const n = this.faceNormals[i] || new Vec3(); + this.getFaceNormal(i, n); + n.negate(n); + this.faceNormals[i] = n; + const vertex = this.vertices[this.faces[i][0]]; + + if (n.dot(vertex) < 0) { + console.error( + `.faceNormals[${i}] = Vec3(${n.toString()}) looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.` + ); + + for (let j = 0; j < this.faces[i].length; j++) { + console.warn( + `.vertices[${this.faces[i][j]}] = Vec3(${this.vertices[ + this.faces[i][j] + ].toString()})` + ); + } + } + } + } + /** + * Compute the normal of a face from its vertices + */ + + getFaceNormal(i, target) { + const f = this.faces[i]; + const va = this.vertices[f[0]]; + const vb = this.vertices[f[1]]; + const vc = this.vertices[f[2]]; + ConvexPolyhedron.computeNormal(va, vb, vc, target); + } + /** + * Get face normal given 3 vertices + */ + + static computeNormal(va, vb, vc, target) { + const cb = new Vec3(); + const ab = new Vec3(); + vb.vsub(va, ab); + vc.vsub(vb, cb); + cb.cross(ab, target); + + if (!target.isZero()) { + target.normalize(); + } + } + /** + * @param minDist Clamp distance + * @param result The an array of contact point objects, see clipFaceAgainstHull + */ + + clipAgainstHull( + posA, + quatA, + hullB, + posB, + quatB, + separatingNormal, + minDist, + maxDist, + result + ) { + const WorldNormal = new Vec3(); + let closestFaceB = -1; + let dmax = -Number.MAX_VALUE; + + for (let face = 0; face < hullB.faces.length; face++) { + WorldNormal.copy(hullB.faceNormals[face]); + quatB.vmult(WorldNormal, WorldNormal); + const d = WorldNormal.dot(separatingNormal); + + if (d > dmax) { + dmax = d; + closestFaceB = face; + } + } + + const worldVertsB1 = []; + + for (let i = 0; i < hullB.faces[closestFaceB].length; i++) { + const b = hullB.vertices[hullB.faces[closestFaceB][i]]; + const worldb = new Vec3(); + worldb.copy(b); + quatB.vmult(worldb, worldb); + posB.vadd(worldb, worldb); + worldVertsB1.push(worldb); + } + + if (closestFaceB >= 0) { + this.clipFaceAgainstHull( + separatingNormal, + posA, + quatA, + worldVertsB1, + minDist, + maxDist, + result + ); + } + } + /** + * Find the separating axis between this hull and another + * @param target The target vector to save the axis in + * @return Returns false if a separation is found, else true + */ + + findSeparatingAxis( + hullB, + posA, + quatA, + posB, + quatB, + target, + faceListA, + faceListB + ) { + const faceANormalWS3 = new Vec3(); + const Worldnormal1 = new Vec3(); + const deltaC = new Vec3(); + const worldEdge0 = new Vec3(); + const worldEdge1 = new Vec3(); + const Cross = new Vec3(); + let dmin = Number.MAX_VALUE; + const hullA = this; + + if (!hullA.uniqueAxes) { + const numFacesA = faceListA ? faceListA.length : hullA.faces.length; // Test face normals from hullA + + for (let i = 0; i < numFacesA; i++) { + const fi = faceListA ? faceListA[i] : i; // Get world face normal + + faceANormalWS3.copy(hullA.faceNormals[fi]); + quatA.vmult(faceANormalWS3, faceANormalWS3); + const d = hullA.testSepAxis( + faceANormalWS3, + hullB, + posA, + quatA, + posB, + quatB + ); + + if (d === false) { + return false; + } + + if (d < dmin) { + dmin = d; + target.copy(faceANormalWS3); + } + } + } else { + // Test unique axes + for (let i = 0; i !== hullA.uniqueAxes.length; i++) { + // Get world axis + quatA.vmult(hullA.uniqueAxes[i], faceANormalWS3); + const d = hullA.testSepAxis( + faceANormalWS3, + hullB, + posA, + quatA, + posB, + quatB + ); + + if (d === false) { + return false; + } + + if (d < dmin) { + dmin = d; + target.copy(faceANormalWS3); + } + } + } + + if (!hullB.uniqueAxes) { + // Test face normals from hullB + const numFacesB = faceListB ? faceListB.length : hullB.faces.length; + + for (let i = 0; i < numFacesB; i++) { + const fi = faceListB ? faceListB[i] : i; + Worldnormal1.copy(hullB.faceNormals[fi]); + quatB.vmult(Worldnormal1, Worldnormal1); + const d = hullA.testSepAxis( + Worldnormal1, + hullB, + posA, + quatA, + posB, + quatB + ); + + if (d === false) { + return false; + } + + if (d < dmin) { + dmin = d; + target.copy(Worldnormal1); + } + } + } else { + // Test unique axes in B + for (let i = 0; i !== hullB.uniqueAxes.length; i++) { + quatB.vmult(hullB.uniqueAxes[i], Worldnormal1); + const d = hullA.testSepAxis( + Worldnormal1, + hullB, + posA, + quatA, + posB, + quatB + ); + + if (d === false) { + return false; + } + + if (d < dmin) { + dmin = d; + target.copy(Worldnormal1); + } + } + } // Test edges + + for (let e0 = 0; e0 !== hullA.uniqueEdges.length; e0++) { + // Get world edge + quatA.vmult(hullA.uniqueEdges[e0], worldEdge0); + + for (let e1 = 0; e1 !== hullB.uniqueEdges.length; e1++) { + // Get world edge 2 + quatB.vmult(hullB.uniqueEdges[e1], worldEdge1); + worldEdge0.cross(worldEdge1, Cross); + + if (!Cross.almostZero()) { + Cross.normalize(); + const dist = hullA.testSepAxis( + Cross, + hullB, + posA, + quatA, + posB, + quatB + ); + + if (dist === false) { + return false; + } + + if (dist < dmin) { + dmin = dist; + target.copy(Cross); + } + } + } + } + + posB.vsub(posA, deltaC); + + if (deltaC.dot(target) > 0.0) { + target.negate(target); + } + + return true; + } + /** + * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one. + * @return The overlap depth, or FALSE if no penetration. + */ + + testSepAxis(axis, hullB, posA, quatA, posB, quatB) { + const hullA = this; + ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA); + ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB); + const maxA = maxminA[0]; + const minA = maxminA[1]; + const maxB = maxminB[0]; + const minB = maxminB[1]; + + if (maxA < minB || maxB < minA) { + return false; // Separated + } + + const d0 = maxA - minB; + const d1 = maxB - minA; + const depth = d0 < d1 ? d0 : d1; + return depth; + } + /** + * calculateLocalInertia + */ + + calculateLocalInertia(mass, target) { + // Approximate with box inertia + // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it + const aabbmax = new Vec3(); + const aabbmin = new Vec3(); + this.computeLocalAABB(aabbmin, aabbmax); + const x = aabbmax.x - aabbmin.x; + const y = aabbmax.y - aabbmin.y; + const z = aabbmax.z - aabbmin.z; + target.x = (1.0 / 12.0) * mass * (2 * y * 2 * y + 2 * z * 2 * z); + target.y = (1.0 / 12.0) * mass * (2 * x * 2 * x + 2 * z * 2 * z); + target.z = (1.0 / 12.0) * mass * (2 * y * 2 * y + 2 * x * 2 * x); + } + /** + * @param face_i Index of the face + */ + + getPlaneConstantOfFace(face_i) { + const f = this.faces[face_i]; + const n = this.faceNormals[face_i]; + const v = this.vertices[f[0]]; + const c = -n.dot(v); + return c; + } + /** + * Clip a face against a hull. + * @param worldVertsB1 An array of Vec3 with vertices in the world frame. + * @param minDist Distance clamping + * @param Array result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates. + */ + + clipFaceAgainstHull( + separatingNormal, + posA, + quatA, + worldVertsB1, + minDist, + maxDist, + result + ) { + const faceANormalWS = new Vec3(); + const edge0 = new Vec3(); + const WorldEdge0 = new Vec3(); + const worldPlaneAnormal1 = new Vec3(); + const planeNormalWS1 = new Vec3(); + const worldA1 = new Vec3(); + const localPlaneNormal = new Vec3(); + const planeNormalWS = new Vec3(); + const hullA = this; + const worldVertsB2 = []; + const pVtxIn = worldVertsB1; + const pVtxOut = worldVertsB2; + let closestFaceA = -1; + let dmin = Number.MAX_VALUE; // Find the face with normal closest to the separating axis + + for (let face = 0; face < hullA.faces.length; face++) { + faceANormalWS.copy(hullA.faceNormals[face]); + quatA.vmult(faceANormalWS, faceANormalWS); + const d = faceANormalWS.dot(separatingNormal); + + if (d < dmin) { + dmin = d; + closestFaceA = face; + } + } + + if (closestFaceA < 0) { + return; + } // Get the face and construct connected faces + + const polyA = hullA.faces[closestFaceA]; + polyA.connectedFaces = []; + + for (let i = 0; i < hullA.faces.length; i++) { + for (let j = 0; j < hullA.faces[i].length; j++) { + if ( + /* Sharing a vertex*/ + polyA.indexOf(hullA.faces[i][j]) !== -1 && + /* Not the one we are looking for connections from */ + i !== closestFaceA && + /* Not already added */ + polyA.connectedFaces.indexOf(i) === -1 + ) { + polyA.connectedFaces.push(i); + } + } + } // Clip the polygon to the back of the planes of all faces of hull A, + // that are adjacent to the witness face + + const numVerticesA = polyA.length; + + for (let i = 0; i < numVerticesA; i++) { + const a = hullA.vertices[polyA[i]]; + const b = hullA.vertices[polyA[(i + 1) % numVerticesA]]; + a.vsub(b, edge0); + WorldEdge0.copy(edge0); + quatA.vmult(WorldEdge0, WorldEdge0); + posA.vadd(WorldEdge0, WorldEdge0); + worldPlaneAnormal1.copy(this.faceNormals[closestFaceA]); + quatA.vmult(worldPlaneAnormal1, worldPlaneAnormal1); + posA.vadd(worldPlaneAnormal1, worldPlaneAnormal1); + WorldEdge0.cross(worldPlaneAnormal1, planeNormalWS1); + planeNormalWS1.negate(planeNormalWS1); + worldA1.copy(a); + quatA.vmult(worldA1, worldA1); + posA.vadd(worldA1, worldA1); + const otherFace = polyA.connectedFaces[i]; + localPlaneNormal.copy(this.faceNormals[otherFace]); + const localPlaneEq = this.getPlaneConstantOfFace(otherFace); + planeNormalWS.copy(localPlaneNormal); + quatA.vmult(planeNormalWS, planeNormalWS); + const planeEqWS = localPlaneEq - planeNormalWS.dot(posA); // Clip face against our constructed plane + + this.clipFaceAgainstPlane(pVtxIn, pVtxOut, planeNormalWS, planeEqWS); // Throw away all clipped points, but save the remaining until next clip + + while (pVtxIn.length) { + pVtxIn.shift(); + } + + while (pVtxOut.length) { + pVtxIn.push(pVtxOut.shift()); + } + } // only keep contact points that are behind the witness face + + localPlaneNormal.copy(this.faceNormals[closestFaceA]); + const localPlaneEq = this.getPlaneConstantOfFace(closestFaceA); + planeNormalWS.copy(localPlaneNormal); + quatA.vmult(planeNormalWS, planeNormalWS); + const planeEqWS = localPlaneEq - planeNormalWS.dot(posA); + + for (let i = 0; i < pVtxIn.length; i++) { + let depth = planeNormalWS.dot(pVtxIn[i]) + planeEqWS; // ??? + + if (depth <= minDist) { + console.log(`clamped: depth=${depth} to minDist=${minDist}`); + depth = minDist; + } + + if (depth <= maxDist) { + const point = pVtxIn[i]; + + if (depth <= 1e-6) { + const p = { + point, + normal: planeNormalWS, + depth, + }; + result.push(p); + } + } + } + } + /** + * Clip a face in a hull against the back of a plane. + * @param planeConstant The constant in the mathematical plane equation + */ + + clipFaceAgainstPlane(inVertices, outVertices, planeNormal, planeConstant) { + let n_dot_first; + let n_dot_last; + const numVerts = inVertices.length; + + if (numVerts < 2) { + return outVertices; + } + + let firstVertex = inVertices[inVertices.length - 1]; + let lastVertex = inVertices[0]; + n_dot_first = planeNormal.dot(firstVertex) + planeConstant; + + for (let vi = 0; vi < numVerts; vi++) { + lastVertex = inVertices[vi]; + n_dot_last = planeNormal.dot(lastVertex) + planeConstant; + + if (n_dot_first < 0) { + if (n_dot_last < 0) { + // Start < 0, end < 0, so output lastVertex + const newv = new Vec3(); + newv.copy(lastVertex); + outVertices.push(newv); + } else { + // Start < 0, end >= 0, so output intersection + const newv = new Vec3(); + firstVertex.lerp( + lastVertex, + n_dot_first / (n_dot_first - n_dot_last), + newv + ); + outVertices.push(newv); + } + } else { + if (n_dot_last < 0) { + // Start >= 0, end < 0 so output intersection and end + const newv = new Vec3(); + firstVertex.lerp( + lastVertex, + n_dot_first / (n_dot_first - n_dot_last), + newv + ); + outVertices.push(newv); + outVertices.push(lastVertex); + } + } + + firstVertex = lastVertex; + n_dot_first = n_dot_last; + } + + return outVertices; + } + /** + * Updates `.worldVertices` and sets `.worldVerticesNeedsUpdate` to false. + */ + + computeWorldVertices(position, quat) { + while (this.worldVertices.length < this.vertices.length) { + this.worldVertices.push(new Vec3()); + } + + const verts = this.vertices; + const worldVerts = this.worldVertices; + + for (let i = 0; i !== this.vertices.length; i++) { + quat.vmult(verts[i], worldVerts[i]); + position.vadd(worldVerts[i], worldVerts[i]); + } + + this.worldVerticesNeedsUpdate = false; + } + + computeLocalAABB(aabbmin, aabbmax) { + const vertices = this.vertices; + aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + + for (let i = 0; i < this.vertices.length; i++) { + const v = vertices[i]; + + if (v.x < aabbmin.x) { + aabbmin.x = v.x; + } else if (v.x > aabbmax.x) { + aabbmax.x = v.x; + } + + if (v.y < aabbmin.y) { + aabbmin.y = v.y; + } else if (v.y > aabbmax.y) { + aabbmax.y = v.y; + } + + if (v.z < aabbmin.z) { + aabbmin.z = v.z; + } else if (v.z > aabbmax.z) { + aabbmax.z = v.z; + } + } + } + /** + * Updates `worldVertices` and sets `worldVerticesNeedsUpdate` to false. + */ + + computeWorldFaceNormals(quat) { + const N = this.faceNormals.length; + + while (this.worldFaceNormals.length < N) { + this.worldFaceNormals.push(new Vec3()); + } + + const normals = this.faceNormals; + const worldNormals = this.worldFaceNormals; + + for (let i = 0; i !== N; i++) { + quat.vmult(normals[i], worldNormals[i]); + } + + this.worldFaceNormalsNeedsUpdate = false; + } + /** + * updateBoundingSphereRadius + */ + + updateBoundingSphereRadius() { + // Assume points are distributed with local (0,0,0) as center + let max2 = 0; + const verts = this.vertices; + + for (let i = 0; i !== verts.length; i++) { + const norm2 = verts[i].lengthSquared(); + + if (norm2 > max2) { + max2 = norm2; + } + } + + this.boundingSphereRadius = Math.sqrt(max2); + } + /** + * calculateWorldAABB + */ + + calculateWorldAABB(pos, quat, min, max) { + const verts = this.vertices; + let minx; + let miny; + let minz; + let maxx; + let maxy; + let maxz; + let tempWorldVertex = new Vec3(); + + for (let i = 0; i < verts.length; i++) { + tempWorldVertex.copy(verts[i]); + quat.vmult(tempWorldVertex, tempWorldVertex); + pos.vadd(tempWorldVertex, tempWorldVertex); + const v = tempWorldVertex; + + if (minx === undefined || v.x < minx) { + minx = v.x; + } + + if (maxx === undefined || v.x > maxx) { + maxx = v.x; + } + + if (miny === undefined || v.y < miny) { + miny = v.y; + } + + if (maxy === undefined || v.y > maxy) { + maxy = v.y; + } + + if (minz === undefined || v.z < minz) { + minz = v.z; + } + + if (maxz === undefined || v.z > maxz) { + maxz = v.z; + } + } + + min.set(minx, miny, minz); + max.set(maxx, maxy, maxz); + } + /** + * Get approximate convex volume + */ + + volume() { + return (4.0 * Math.PI * this.boundingSphereRadius) / 3.0; + } + /** + * Get an average of all the vertices positions + */ + + getAveragePointLocal(target) { + if (target === void 0) { + target = new Vec3(); + } + + const verts = this.vertices; + + for (let i = 0; i < verts.length; i++) { + target.vadd(verts[i], target); + } + + target.scale(1 / verts.length, target); + return target; + } + /** + * Transform all local points. Will change the .vertices + */ + + transformAllPoints(offset, quat) { + const n = this.vertices.length; + const verts = this.vertices; // Apply rotation + + if (quat) { + // Rotate vertices + for (let i = 0; i < n; i++) { + const v = verts[i]; + quat.vmult(v, v); + } // Rotate face normals + + for (let i = 0; i < this.faceNormals.length; i++) { + const v = this.faceNormals[i]; + quat.vmult(v, v); + } + /* + // Rotate edges + for(let i=0; i 0) || (r1 > 0 && r2 < 0)) { + return false; // Encountered some other sign. Exit. + } + } // If we got here, all dot products were of the same sign. + + return positiveResult ? 1 : -1; + } + /** + * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. + * Results are saved in the array maxmin. + * @param result result[0] and result[1] will be set to maximum and minimum, respectively. + */ + + static project(shape, axis, pos, quat, result) { + const n = shape.vertices.length; + const localAxis = project_localAxis; + let max = 0; + let min = 0; + const localOrigin = project_localOrigin; + const vs = shape.vertices; + localOrigin.setZero(); // Transform the axis to local + + Transform.vectorToLocalFrame(pos, quat, axis, localAxis); + Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin); + const add = localOrigin.dot(localAxis); + min = max = vs[0].dot(localAxis); + + for (let i = 1; i < n; i++) { + const val = vs[i].dot(localAxis); + + if (val > max) { + max = val; + } + + if (val < min) { + min = val; + } + } + + min -= add; + max -= add; + + if (min > max) { + // Inconsistent - swap + const temp = min; + min = max; + max = temp; + } // Output + + result[0] = max; + result[1] = min; + } + } + const maxminA = []; + const maxminB = []; + new Vec3(); + const project_localAxis = new Vec3(); + const project_localOrigin = new Vec3(); + + /** + * A 3d box shape. + * @example + * const size = 1 + * const halfExtents = new CANNON.Vec3(size, size, size) + * const boxShape = new CANNON.Box(halfExtents) + * const boxBody = new CANNON.Body({ mass: 1, shape: boxShape }) + * world.addBody(boxBody) + */ + class Box extends Shape { + /** + * The half extents of the box. + */ + + /** + * Used by the contact generator to make contacts with other convex polyhedra for example. + */ + constructor(halfExtents) { + super({ + type: Shape.types.BOX, + }); + this.halfExtents = halfExtents; + this.convexPolyhedronRepresentation = null; + this.updateConvexPolyhedronRepresentation(); + this.updateBoundingSphereRadius(); + } + /** + * Updates the local convex polyhedron representation used for some collisions. + */ + + updateConvexPolyhedronRepresentation() { + const sx = this.halfExtents.x; + const sy = this.halfExtents.y; + const sz = this.halfExtents.z; + const V = Vec3; + const vertices = [ + new V(-sx, -sy, -sz), + new V(sx, -sy, -sz), + new V(sx, sy, -sz), + new V(-sx, sy, -sz), + new V(-sx, -sy, sz), + new V(sx, -sy, sz), + new V(sx, sy, sz), + new V(-sx, sy, sz), + ]; + const faces = [ + [3, 2, 1, 0], // -z + [4, 5, 6, 7], // +z + [5, 4, 0, 1], // -y + [2, 3, 7, 6], // +y + [0, 4, 7, 3], // -x + [1, 2, 6, 5], // +x + ]; + const axes = [new V(0, 0, 1), new V(0, 1, 0), new V(1, 0, 0)]; + const h = new ConvexPolyhedron({ + vertices, + faces, + axes, + }); + this.convexPolyhedronRepresentation = h; + h.material = this.material; + } + /** + * Calculate the inertia of the box. + */ + + calculateLocalInertia(mass, target) { + if (target === void 0) { + target = new Vec3(); + } + + Box.calculateInertia(this.halfExtents, mass, target); + return target; + } + + static calculateInertia(halfExtents, mass, target) { + const e = halfExtents; + target.x = (1.0 / 12.0) * mass * (2 * e.y * 2 * e.y + 2 * e.z * 2 * e.z); + target.y = (1.0 / 12.0) * mass * (2 * e.x * 2 * e.x + 2 * e.z * 2 * e.z); + target.z = (1.0 / 12.0) * mass * (2 * e.y * 2 * e.y + 2 * e.x * 2 * e.x); + } + /** + * Get the box 6 side normals + * @param sixTargetVectors An array of 6 vectors, to store the resulting side normals in. + * @param quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. + */ + + getSideNormals(sixTargetVectors, quat) { + const sides = sixTargetVectors; + const ex = this.halfExtents; + sides[0].set(ex.x, 0, 0); + sides[1].set(0, ex.y, 0); + sides[2].set(0, 0, ex.z); + sides[3].set(-ex.x, 0, 0); + sides[4].set(0, -ex.y, 0); + sides[5].set(0, 0, -ex.z); + + if (quat !== undefined) { + for (let i = 0; i !== sides.length; i++) { + quat.vmult(sides[i], sides[i]); + } + } + + return sides; + } + /** + * Returns the volume of the box. + */ + + volume() { + return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z; + } + /** + * updateBoundingSphereRadius + */ + + updateBoundingSphereRadius() { + this.boundingSphereRadius = this.halfExtents.length(); + } + /** + * forEachWorldCorner + */ + + forEachWorldCorner(pos, quat, callback) { + const e = this.halfExtents; + const corners = [ + [e.x, e.y, e.z], + [-e.x, e.y, e.z], + [-e.x, -e.y, e.z], + [-e.x, -e.y, -e.z], + [e.x, -e.y, -e.z], + [e.x, e.y, -e.z], + [-e.x, e.y, -e.z], + [e.x, -e.y, e.z], + ]; + + for (let i = 0; i < corners.length; i++) { + worldCornerTempPos.set(corners[i][0], corners[i][1], corners[i][2]); + quat.vmult(worldCornerTempPos, worldCornerTempPos); + pos.vadd(worldCornerTempPos, worldCornerTempPos); + callback( + worldCornerTempPos.x, + worldCornerTempPos.y, + worldCornerTempPos.z + ); + } + } + /** + * calculateWorldAABB + */ + + calculateWorldAABB(pos, quat, min, max) { + const e = this.halfExtents; + worldCornersTemp[0].set(e.x, e.y, e.z); + worldCornersTemp[1].set(-e.x, e.y, e.z); + worldCornersTemp[2].set(-e.x, -e.y, e.z); + worldCornersTemp[3].set(-e.x, -e.y, -e.z); + worldCornersTemp[4].set(e.x, -e.y, -e.z); + worldCornersTemp[5].set(e.x, e.y, -e.z); + worldCornersTemp[6].set(-e.x, e.y, -e.z); + worldCornersTemp[7].set(e.x, -e.y, e.z); + const wc = worldCornersTemp[0]; + quat.vmult(wc, wc); + pos.vadd(wc, wc); + max.copy(wc); + min.copy(wc); + + for (let i = 1; i < 8; i++) { + const wc = worldCornersTemp[i]; + quat.vmult(wc, wc); + pos.vadd(wc, wc); + const x = wc.x; + const y = wc.y; + const z = wc.z; + + if (x > max.x) { + max.x = x; + } + + if (y > max.y) { + max.y = y; + } + + if (z > max.z) { + max.z = z; + } + + if (x < min.x) { + min.x = x; + } + + if (y < min.y) { + min.y = y; + } + + if (z < min.z) { + min.z = z; + } + } // Get each axis max + // min.set(Infinity,Infinity,Infinity); + // max.set(-Infinity,-Infinity,-Infinity); + // this.forEachWorldCorner(pos,quat,function(x,y,z){ + // if(x > max.x){ + // max.x = x; + // } + // if(y > max.y){ + // max.y = y; + // } + // if(z > max.z){ + // max.z = z; + // } + // if(x < min.x){ + // min.x = x; + // } + // if(y < min.y){ + // min.y = y; + // } + // if(z < min.z){ + // min.z = z; + // } + // }); + } + } + const worldCornerTempPos = new Vec3(); + const worldCornersTemp = [ + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + ]; + + /** + * BODY_TYPES + */ + const BODY_TYPES = { + /** DYNAMIC */ + DYNAMIC: 1, + + /** STATIC */ + STATIC: 2, + + /** KINEMATIC */ + KINEMATIC: 4, + }; + /** + * BodyType + */ + + /** + * BODY_SLEEP_STATES + */ + const BODY_SLEEP_STATES = { + /** AWAKE */ + AWAKE: 0, + + /** SLEEPY */ + SLEEPY: 1, + + /** SLEEPING */ + SLEEPING: 2, + }; + /** + * BodySleepState + */ + + /** + * Base class for all body types. + * @example + * const shape = new CANNON.Sphere(1) + * const body = new CANNON.Body({ + * mass: 1, + * shape, + * }) + * world.addBody(body) + */ + class Body extends EventTarget { + /** + * Dispatched after two bodies collide. This event is dispatched on each + * of the two bodies involved in the collision. + * @event collide + * @param body The body that was involved in the collision. + * @param contact The details of the collision. + */ + + /** + * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. + */ + + /** + * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies. + */ + + /** + * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies. + */ + + /** + * AWAKE + */ + + /** + * SLEEPY + */ + + /** + * SLEEPING + */ + + /** + * Dispatched after a sleeping body has woken up. + * @event wakeup + */ + + /** + * Dispatched after a body has gone in to the sleepy state. + * @event sleepy + */ + + /** + * Dispatched after a body has fallen asleep. + * @event sleep + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + super(); + this.id = Body.idCounter++; + this.index = -1; + this.world = null; + this.vlambda = new Vec3(); + this.collisionFilterGroup = + typeof options.collisionFilterGroup === "number" + ? options.collisionFilterGroup + : 1; + this.collisionFilterMask = + typeof options.collisionFilterMask === "number" + ? options.collisionFilterMask + : -1; + this.collisionResponse = + typeof options.collisionResponse === "boolean" + ? options.collisionResponse + : true; + this.position = new Vec3(); + this.previousPosition = new Vec3(); + this.interpolatedPosition = new Vec3(); + this.initPosition = new Vec3(); + + if (options.position) { + this.position.copy(options.position); + this.previousPosition.copy(options.position); + this.interpolatedPosition.copy(options.position); + this.initPosition.copy(options.position); + } + + this.velocity = new Vec3(); + + if (options.velocity) { + this.velocity.copy(options.velocity); + } + + this.initVelocity = new Vec3(); + this.force = new Vec3(); + const mass = typeof options.mass === "number" ? options.mass : 0; + this.mass = mass; + this.invMass = mass > 0 ? 1.0 / mass : 0; + this.material = options.material || null; + this.linearDamping = + typeof options.linearDamping === "number" ? options.linearDamping : 0.01; + this.type = mass <= 0.0 ? Body.STATIC : Body.DYNAMIC; + + if (typeof options.type === typeof Body.STATIC) { + this.type = options.type; + } + + this.allowSleep = + typeof options.allowSleep !== "undefined" ? options.allowSleep : true; + this.sleepState = Body.AWAKE; + this.sleepSpeedLimit = + typeof options.sleepSpeedLimit !== "undefined" + ? options.sleepSpeedLimit + : 0.1; + this.sleepTimeLimit = + typeof options.sleepTimeLimit !== "undefined" + ? options.sleepTimeLimit + : 1; + this.timeLastSleepy = 0; + this.wakeUpAfterNarrowphase = false; + this.torque = new Vec3(); + this.quaternion = new Quaternion(); + this.initQuaternion = new Quaternion(); + this.previousQuaternion = new Quaternion(); + this.interpolatedQuaternion = new Quaternion(); + + if (options.quaternion) { + this.quaternion.copy(options.quaternion); + this.initQuaternion.copy(options.quaternion); + this.previousQuaternion.copy(options.quaternion); + this.interpolatedQuaternion.copy(options.quaternion); + } + + this.angularVelocity = new Vec3(); + + if (options.angularVelocity) { + this.angularVelocity.copy(options.angularVelocity); + } + + this.initAngularVelocity = new Vec3(); + this.shapes = []; + this.shapeOffsets = []; + this.shapeOrientations = []; + this.inertia = new Vec3(); + this.invInertia = new Vec3(); + this.invInertiaWorld = new Mat3(); + this.invMassSolve = 0; + this.invInertiaSolve = new Vec3(); + this.invInertiaWorldSolve = new Mat3(); + this.fixedRotation = + typeof options.fixedRotation !== "undefined" + ? options.fixedRotation + : false; + this.angularDamping = + typeof options.angularDamping !== "undefined" + ? options.angularDamping + : 0.01; + this.linearFactor = new Vec3(1, 1, 1); + + if (options.linearFactor) { + this.linearFactor.copy(options.linearFactor); + } + + this.angularFactor = new Vec3(1, 1, 1); + + if (options.angularFactor) { + this.angularFactor.copy(options.angularFactor); + } + + this.aabb = new AABB(); + this.aabbNeedsUpdate = true; + this.boundingRadius = 0; + this.wlambda = new Vec3(); + this.isTrigger = Boolean(options.isTrigger); + + if (options.shape) { + this.addShape(options.shape); + } + + this.updateMassProperties(); + } + /** + * Wake the body up. + */ + + wakeUp() { + const prevState = this.sleepState; + this.sleepState = Body.AWAKE; + this.wakeUpAfterNarrowphase = false; + + if (prevState === Body.SLEEPING) { + this.dispatchEvent(Body.wakeupEvent); + } + } + /** + * Force body sleep + */ + + sleep() { + this.sleepState = Body.SLEEPING; + this.velocity.set(0, 0, 0); + this.angularVelocity.set(0, 0, 0); + this.wakeUpAfterNarrowphase = false; + } + /** + * Called every timestep to update internal sleep timer and change sleep state if needed. + * @param time The world time in seconds + */ + + sleepTick(time) { + if (this.allowSleep) { + const sleepState = this.sleepState; + const speedSquared = + this.velocity.lengthSquared() + this.angularVelocity.lengthSquared(); + const speedLimitSquared = this.sleepSpeedLimit ** 2; + + if (sleepState === Body.AWAKE && speedSquared < speedLimitSquared) { + this.sleepState = Body.SLEEPY; // Sleepy + + this.timeLastSleepy = time; + this.dispatchEvent(Body.sleepyEvent); + } else if ( + sleepState === Body.SLEEPY && + speedSquared > speedLimitSquared + ) { + this.wakeUp(); // Wake up + } else if ( + sleepState === Body.SLEEPY && + time - this.timeLastSleepy > this.sleepTimeLimit + ) { + this.sleep(); // Sleeping + + this.dispatchEvent(Body.sleepEvent); + } + } + } + /** + * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass". + */ + + updateSolveMassProperties() { + if (this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC) { + this.invMassSolve = 0; + this.invInertiaSolve.setZero(); + this.invInertiaWorldSolve.setZero(); + } else { + this.invMassSolve = this.invMass; + this.invInertiaSolve.copy(this.invInertia); + this.invInertiaWorldSolve.copy(this.invInertiaWorld); + } + } + /** + * Convert a world point to local body frame. + */ + + pointToLocalFrame(worldPoint, result) { + if (result === void 0) { + result = new Vec3(); + } + + worldPoint.vsub(this.position, result); + this.quaternion.conjugate().vmult(result, result); + return result; + } + /** + * Convert a world vector to local body frame. + */ + + vectorToLocalFrame(worldVector, result) { + if (result === void 0) { + result = new Vec3(); + } + + this.quaternion.conjugate().vmult(worldVector, result); + return result; + } + /** + * Convert a local body point to world frame. + */ + + pointToWorldFrame(localPoint, result) { + if (result === void 0) { + result = new Vec3(); + } + + this.quaternion.vmult(localPoint, result); + result.vadd(this.position, result); + return result; + } + /** + * Convert a local body point to world frame. + */ + + vectorToWorldFrame(localVector, result) { + if (result === void 0) { + result = new Vec3(); + } + + this.quaternion.vmult(localVector, result); + return result; + } + /** + * Add a shape to the body with a local offset and orientation. + * @return The body object, for chainability. + */ + + addShape(shape, _offset, _orientation) { + const offset = new Vec3(); + const orientation = new Quaternion(); + + if (_offset) { + offset.copy(_offset); + } + + if (_orientation) { + orientation.copy(_orientation); + } + + this.shapes.push(shape); + this.shapeOffsets.push(offset); + this.shapeOrientations.push(orientation); + this.updateMassProperties(); + this.updateBoundingRadius(); + this.aabbNeedsUpdate = true; + shape.body = this; + return this; + } + /** + * Remove a shape from the body. + * @return The body object, for chainability. + */ + + removeShape(shape) { + const index = this.shapes.indexOf(shape); + + if (index === -1) { + console.warn("Shape does not belong to the body"); + return this; + } + + this.shapes.splice(index, 1); + this.shapeOffsets.splice(index, 1); + this.shapeOrientations.splice(index, 1); + this.updateMassProperties(); + this.updateBoundingRadius(); + this.aabbNeedsUpdate = true; + shape.body = null; + return this; + } + /** + * Update the bounding radius of the body. Should be done if any of the shapes are changed. + */ + + updateBoundingRadius() { + const shapes = this.shapes; + const shapeOffsets = this.shapeOffsets; + const N = shapes.length; + let radius = 0; + + for (let i = 0; i !== N; i++) { + const shape = shapes[i]; + shape.updateBoundingSphereRadius(); + const offset = shapeOffsets[i].length(); + const r = shape.boundingSphereRadius; + + if (offset + r > radius) { + radius = offset + r; + } + } + + this.boundingRadius = radius; + } + /** + * Updates the .aabb + */ + + updateAABB() { + const shapes = this.shapes; + const shapeOffsets = this.shapeOffsets; + const shapeOrientations = this.shapeOrientations; + const N = shapes.length; + const offset = tmpVec; + const orientation = tmpQuat; + const bodyQuat = this.quaternion; + const aabb = this.aabb; + const shapeAABB = updateAABB_shapeAABB; + + for (let i = 0; i !== N; i++) { + const shape = shapes[i]; // Get shape world position + + bodyQuat.vmult(shapeOffsets[i], offset); + offset.vadd(this.position, offset); // Get shape world quaternion + + bodyQuat.mult(shapeOrientations[i], orientation); // Get shape AABB + + shape.calculateWorldAABB( + offset, + orientation, + shapeAABB.lowerBound, + shapeAABB.upperBound + ); + + if (i === 0) { + aabb.copy(shapeAABB); + } else { + aabb.extend(shapeAABB); + } + } + + this.aabbNeedsUpdate = false; + } + /** + * Update `.inertiaWorld` and `.invInertiaWorld` + */ + + updateInertiaWorld(force) { + const I = this.invInertia; + + if (I.x === I.y && I.y === I.z && !force); + else { + const m1 = uiw_m1; + const m2 = uiw_m2; + m1.setRotationFromQuaternion(this.quaternion); + m1.transpose(m2); + m1.scale(I, m1); + m1.mmult(m2, this.invInertiaWorld); + } + } + /** + * Apply force to a point of the body. This could for example be a point on the Body surface. + * Applying force this way will add to Body.force and Body.torque. + * @param force The amount of force to add. + * @param relativePoint A point relative to the center of mass to apply the force on. + */ + + applyForce(force, relativePoint) { + if (relativePoint === void 0) { + relativePoint = new Vec3(); + } + + // Needed? + if (this.type !== Body.DYNAMIC) { + return; + } + + if (this.sleepState === Body.SLEEPING) { + this.wakeUp(); + } // Compute produced rotational force + + const rotForce = Body_applyForce_rotForce; + relativePoint.cross(force, rotForce); // Add linear force + + this.force.vadd(force, this.force); // Add rotational force + + this.torque.vadd(rotForce, this.torque); + } + /** + * Apply force to a local point in the body. + * @param force The force vector to apply, defined locally in the body frame. + * @param localPoint A local point in the body to apply the force on. + */ + + applyLocalForce(localForce, localPoint) { + if (localPoint === void 0) { + localPoint = new Vec3(); + } + + if (this.type !== Body.DYNAMIC) { + return; + } + + const worldForce = Body_applyLocalForce_worldForce; + const relativePointWorld = Body_applyLocalForce_relativePointWorld; // Transform the force vector to world space + + this.vectorToWorldFrame(localForce, worldForce); + this.vectorToWorldFrame(localPoint, relativePointWorld); + this.applyForce(worldForce, relativePointWorld); + } + /** + * Apply torque to the body. + * @param torque The amount of torque to add. + */ + + applyTorque(torque) { + if (this.type !== Body.DYNAMIC) { + return; + } + + if (this.sleepState === Body.SLEEPING) { + this.wakeUp(); + } // Add rotational force + + this.torque.vadd(torque, this.torque); + } + /** + * Apply impulse to a point of the body. This could for example be a point on the Body surface. + * An impulse is a force added to a body during a short period of time (impulse = force * time). + * Impulses will be added to Body.velocity and Body.angularVelocity. + * @param impulse The amount of impulse to add. + * @param relativePoint A point relative to the center of mass to apply the force on. + */ + + applyImpulse(impulse, relativePoint) { + if (relativePoint === void 0) { + relativePoint = new Vec3(); + } + + if (this.type !== Body.DYNAMIC) { + return; + } + + if (this.sleepState === Body.SLEEPING) { + this.wakeUp(); + } // Compute point position relative to the body center + + const r = relativePoint; // Compute produced central impulse velocity + + const velo = Body_applyImpulse_velo; + velo.copy(impulse); + velo.scale(this.invMass, velo); // Add linear impulse + + this.velocity.vadd(velo, this.velocity); // Compute produced rotational impulse velocity + + const rotVelo = Body_applyImpulse_rotVelo; + r.cross(impulse, rotVelo); + /* + rotVelo.x *= this.invInertia.x; + rotVelo.y *= this.invInertia.y; + rotVelo.z *= this.invInertia.z; + */ + + this.invInertiaWorld.vmult(rotVelo, rotVelo); // Add rotational Impulse + + this.angularVelocity.vadd(rotVelo, this.angularVelocity); + } + /** + * Apply locally-defined impulse to a local point in the body. + * @param force The force vector to apply, defined locally in the body frame. + * @param localPoint A local point in the body to apply the force on. + */ + + applyLocalImpulse(localImpulse, localPoint) { + if (localPoint === void 0) { + localPoint = new Vec3(); + } + + if (this.type !== Body.DYNAMIC) { + return; + } + + const worldImpulse = Body_applyLocalImpulse_worldImpulse; + const relativePointWorld = Body_applyLocalImpulse_relativePoint; // Transform the force vector to world space + + this.vectorToWorldFrame(localImpulse, worldImpulse); + this.vectorToWorldFrame(localPoint, relativePointWorld); + this.applyImpulse(worldImpulse, relativePointWorld); + } + /** + * Should be called whenever you change the body shape or mass. + */ + + updateMassProperties() { + const halfExtents = Body_updateMassProperties_halfExtents; + this.invMass = this.mass > 0 ? 1.0 / this.mass : 0; + const I = this.inertia; + const fixed = this.fixedRotation; // Approximate with AABB box + + this.updateAABB(); + halfExtents.set( + (this.aabb.upperBound.x - this.aabb.lowerBound.x) / 2, + (this.aabb.upperBound.y - this.aabb.lowerBound.y) / 2, + (this.aabb.upperBound.z - this.aabb.lowerBound.z) / 2 + ); + Box.calculateInertia(halfExtents, this.mass, I); + this.invInertia.set( + I.x > 0 && !fixed ? 1.0 / I.x : 0, + I.y > 0 && !fixed ? 1.0 / I.y : 0, + I.z > 0 && !fixed ? 1.0 / I.z : 0 + ); + this.updateInertiaWorld(true); + } + /** + * Get world velocity of a point in the body. + * @param worldPoint + * @param result + * @return The result vector. + */ + + getVelocityAtWorldPoint(worldPoint, result) { + const r = new Vec3(); + worldPoint.vsub(this.position, r); + this.angularVelocity.cross(r, result); + this.velocity.vadd(result, result); + return result; + } + /** + * Move the body forward in time. + * @param dt Time step + * @param quatNormalize Set to true to normalize the body quaternion + * @param quatNormalizeFast If the quaternion should be normalized using "fast" quaternion normalization + */ + + integrate(dt, quatNormalize, quatNormalizeFast) { + // Save previous position + this.previousPosition.copy(this.position); + this.previousQuaternion.copy(this.quaternion); + + if ( + !(this.type === Body.DYNAMIC || this.type === Body.KINEMATIC) || + this.sleepState === Body.SLEEPING + ) { + // Only for dynamic + return; + } + + const velo = this.velocity; + const angularVelo = this.angularVelocity; + const pos = this.position; + const force = this.force; + const torque = this.torque; + const quat = this.quaternion; + const invMass = this.invMass; + const invInertia = this.invInertiaWorld; + const linearFactor = this.linearFactor; + const iMdt = invMass * dt; + velo.x += force.x * iMdt * linearFactor.x; + velo.y += force.y * iMdt * linearFactor.y; + velo.z += force.z * iMdt * linearFactor.z; + const e = invInertia.elements; + const angularFactor = this.angularFactor; + const tx = torque.x * angularFactor.x; + const ty = torque.y * angularFactor.y; + const tz = torque.z * angularFactor.z; + angularVelo.x += dt * (e[0] * tx + e[1] * ty + e[2] * tz); + angularVelo.y += dt * (e[3] * tx + e[4] * ty + e[5] * tz); + angularVelo.z += dt * (e[6] * tx + e[7] * ty + e[8] * tz); // Use new velocity - leap frog + + pos.x += velo.x * dt; + pos.y += velo.y * dt; + pos.z += velo.z * dt; + quat.integrate(this.angularVelocity, dt, this.angularFactor, quat); + + if (quatNormalize) { + if (quatNormalizeFast) { + quat.normalizeFast(); + } else { + quat.normalize(); + } + } + + this.aabbNeedsUpdate = true; // Update world inertia + + this.updateInertiaWorld(); + } + } + Body.idCounter = 0; + Body.COLLIDE_EVENT_NAME = "collide"; + Body.DYNAMIC = BODY_TYPES.DYNAMIC; + Body.STATIC = BODY_TYPES.STATIC; + Body.KINEMATIC = BODY_TYPES.KINEMATIC; + Body.AWAKE = BODY_SLEEP_STATES.AWAKE; + Body.SLEEPY = BODY_SLEEP_STATES.SLEEPY; + Body.SLEEPING = BODY_SLEEP_STATES.SLEEPING; + Body.wakeupEvent = { + type: "wakeup", + }; + Body.sleepyEvent = { + type: "sleepy", + }; + Body.sleepEvent = { + type: "sleep", + }; + const tmpVec = new Vec3(); + const tmpQuat = new Quaternion(); + const updateAABB_shapeAABB = new AABB(); + const uiw_m1 = new Mat3(); + const uiw_m2 = new Mat3(); + new Mat3(); + const Body_applyForce_rotForce = new Vec3(); + const Body_applyLocalForce_worldForce = new Vec3(); + const Body_applyLocalForce_relativePointWorld = new Vec3(); + const Body_applyImpulse_velo = new Vec3(); + const Body_applyImpulse_rotVelo = new Vec3(); + const Body_applyLocalImpulse_worldImpulse = new Vec3(); + const Body_applyLocalImpulse_relativePoint = new Vec3(); + const Body_updateMassProperties_halfExtents = new Vec3(); + + /** + * Base class for broadphase implementations + * @author schteppe + */ + class Broadphase { + /** + * The world to search for collisions in. + */ + + /** + * If set to true, the broadphase uses bounding boxes for intersection tests, else it uses bounding spheres. + */ + + /** + * Set to true if the objects in the world moved. + */ + constructor() { + this.world = null; + this.useBoundingBoxes = false; + this.dirty = true; + } + /** + * Get the collision pairs from the world + * @param world The world to search in + * @param p1 Empty array to be filled with body objects + * @param p2 Empty array to be filled with body objects + */ + + collisionPairs(world, p1, p2) { + throw new Error( + "collisionPairs not implemented for this BroadPhase class!" + ); + } + /** + * Check if a body pair needs to be intersection tested at all. + */ + + needBroadphaseCollision(bodyA, bodyB) { + // Check collision filter masks + if ( + (bodyA.collisionFilterGroup & bodyB.collisionFilterMask) === 0 || + (bodyB.collisionFilterGroup & bodyA.collisionFilterMask) === 0 + ) { + return false; + } // Check types + + if ( + ((bodyA.type & Body.STATIC) !== 0 || + bodyA.sleepState === Body.SLEEPING) && + ((bodyB.type & Body.STATIC) !== 0 || bodyB.sleepState === Body.SLEEPING) + ) { + // Both bodies are static or sleeping. Skip. + return false; + } + + return true; + } + /** + * Check if the bounding volumes of two bodies intersect. + */ + + intersectionTest(bodyA, bodyB, pairs1, pairs2) { + if (this.useBoundingBoxes) { + this.doBoundingBoxBroadphase(bodyA, bodyB, pairs1, pairs2); + } else { + this.doBoundingSphereBroadphase(bodyA, bodyB, pairs1, pairs2); + } + } + /** + * Check if the bounding spheres of two bodies are intersecting. + * @param pairs1 bodyA is appended to this array if intersection + * @param pairs2 bodyB is appended to this array if intersection + */ + + doBoundingSphereBroadphase(bodyA, bodyB, pairs1, pairs2) { + const r = Broadphase_collisionPairs_r; + bodyB.position.vsub(bodyA.position, r); + const boundingRadiusSum2 = + (bodyA.boundingRadius + bodyB.boundingRadius) ** 2; + const norm2 = r.lengthSquared(); + + if (norm2 < boundingRadiusSum2) { + pairs1.push(bodyA); + pairs2.push(bodyB); + } + } + /** + * Check if the bounding boxes of two bodies are intersecting. + */ + + doBoundingBoxBroadphase(bodyA, bodyB, pairs1, pairs2) { + if (bodyA.aabbNeedsUpdate) { + bodyA.updateAABB(); + } + + if (bodyB.aabbNeedsUpdate) { + bodyB.updateAABB(); + } // Check AABB / AABB + + if (bodyA.aabb.overlaps(bodyB.aabb)) { + pairs1.push(bodyA); + pairs2.push(bodyB); + } + } + /** + * Removes duplicate pairs from the pair arrays. + */ + + makePairsUnique(pairs1, pairs2) { + const t = Broadphase_makePairsUnique_temp; + const p1 = Broadphase_makePairsUnique_p1; + const p2 = Broadphase_makePairsUnique_p2; + const N = pairs1.length; + + for (let i = 0; i !== N; i++) { + p1[i] = pairs1[i]; + p2[i] = pairs2[i]; + } + + pairs1.length = 0; + pairs2.length = 0; + + for (let i = 0; i !== N; i++) { + const id1 = p1[i].id; + const id2 = p2[i].id; + const key = id1 < id2 ? `${id1},${id2}` : `${id2},${id1}`; + t[key] = i; + t.keys.push(key); + } + + for (let i = 0; i !== t.keys.length; i++) { + const key = t.keys.pop(); + const pairIndex = t[key]; + pairs1.push(p1[pairIndex]); + pairs2.push(p2[pairIndex]); + delete t[key]; + } + } + /** + * To be implemented by subcasses + */ + + setWorld(world) {} + /** + * Check if the bounding spheres of two bodies overlap. + */ + + static boundingSphereCheck(bodyA, bodyB) { + const dist = new Vec3(); // bsc_dist; + + bodyA.position.vsub(bodyB.position, dist); + const sa = bodyA.shapes[0]; + const sb = bodyB.shapes[0]; + return ( + Math.pow(sa.boundingSphereRadius + sb.boundingSphereRadius, 2) > + dist.lengthSquared() + ); + } + /** + * Returns all the bodies within the AABB. + */ + + aabbQuery(world, aabb, result) { + console.warn(".aabbQuery is not implemented in this Broadphase subclass."); + return []; + } + } // Temp objects + + const Broadphase_collisionPairs_r = new Vec3(); + new Vec3(); + new Quaternion(); + new Vec3(); + const Broadphase_makePairsUnique_temp = { + keys: [], + }; + const Broadphase_makePairsUnique_p1 = []; + const Broadphase_makePairsUnique_p2 = []; + new Vec3(); + + /** + * Axis aligned uniform grid broadphase. + * @todo Needs support for more than just planes and spheres. + */ + class GridBroadphase extends Broadphase { + /** + * Number of boxes along x + */ + + /** + * Number of boxes along y + */ + + /** + * Number of boxes along z + */ + + /** + * aabbMin + */ + + /** + * aabbMax + */ + + /** + * bins + */ + + /** + * binLengths + */ + + /** + * @param nx Number of boxes along x. + * @param ny Number of boxes along y. + * @param nz Number of boxes along z. + */ + constructor(aabbMin, aabbMax, nx, ny, nz) { + if (aabbMin === void 0) { + aabbMin = new Vec3(100, 100, 100); + } + + if (aabbMax === void 0) { + aabbMax = new Vec3(-100, -100, -100); + } + + if (nx === void 0) { + nx = 10; + } + + if (ny === void 0) { + ny = 10; + } + + if (nz === void 0) { + nz = 10; + } + + super(); + this.nx = nx; + this.ny = ny; + this.nz = nz; + this.aabbMin = aabbMin; + this.aabbMax = aabbMax; + const nbins = this.nx * this.ny * this.nz; + + if (nbins <= 0) { + throw "GridBroadphase: Each dimension's n must be >0"; + } + + this.bins = []; + this.binLengths = []; // Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow + + this.bins.length = nbins; + this.binLengths.length = nbins; + + for (let i = 0; i < nbins; i++) { + this.bins[i] = []; + this.binLengths[i] = 0; + } + } + /** + * Get all the collision pairs in the physics world + */ + + collisionPairs(world, pairs1, pairs2) { + const N = world.bodies.length; + const bodies = world.bodies; + const max = this.aabbMax; + const min = this.aabbMin; + const nx = this.nx; + const ny = this.ny; + const nz = this.nz; + const xstep = ny * nz; + const ystep = nz; + const zstep = 1; + const xmax = max.x; + const ymax = max.y; + const zmax = max.z; + const xmin = min.x; + const ymin = min.y; + const zmin = min.z; + const xmult = nx / (xmax - xmin); + const ymult = ny / (ymax - ymin); + const zmult = nz / (zmax - zmin); + const binsizeX = (xmax - xmin) / nx; + const binsizeY = (ymax - ymin) / ny; + const binsizeZ = (zmax - zmin) / nz; + const binRadius = + Math.sqrt( + binsizeX * binsizeX + binsizeY * binsizeY + binsizeZ * binsizeZ + ) * 0.5; + const types = Shape.types; + const SPHERE = types.SPHERE; + const PLANE = types.PLANE; + types.BOX; + types.COMPOUND; + types.CONVEXPOLYHEDRON; + const bins = this.bins; + const binLengths = this.binLengths; + const Nbins = this.bins.length; // Reset bins + + for (let i = 0; i !== Nbins; i++) { + binLengths[i] = 0; + } + + const ceil = Math.ceil; + + function addBoxToBins(x0, y0, z0, x1, y1, z1, bi) { + let xoff0 = ((x0 - xmin) * xmult) | 0; + let yoff0 = ((y0 - ymin) * ymult) | 0; + let zoff0 = ((z0 - zmin) * zmult) | 0; + let xoff1 = ceil((x1 - xmin) * xmult); + let yoff1 = ceil((y1 - ymin) * ymult); + let zoff1 = ceil((z1 - zmin) * zmult); + + if (xoff0 < 0) { + xoff0 = 0; + } else if (xoff0 >= nx) { + xoff0 = nx - 1; + } + + if (yoff0 < 0) { + yoff0 = 0; + } else if (yoff0 >= ny) { + yoff0 = ny - 1; + } + + if (zoff0 < 0) { + zoff0 = 0; + } else if (zoff0 >= nz) { + zoff0 = nz - 1; + } + + if (xoff1 < 0) { + xoff1 = 0; + } else if (xoff1 >= nx) { + xoff1 = nx - 1; + } + + if (yoff1 < 0) { + yoff1 = 0; + } else if (yoff1 >= ny) { + yoff1 = ny - 1; + } + + if (zoff1 < 0) { + zoff1 = 0; + } else if (zoff1 >= nz) { + zoff1 = nz - 1; + } + + xoff0 *= xstep; + yoff0 *= ystep; + zoff0 *= zstep; + xoff1 *= xstep; + yoff1 *= ystep; + zoff1 *= zstep; + + for (let xoff = xoff0; xoff <= xoff1; xoff += xstep) { + for (let yoff = yoff0; yoff <= yoff1; yoff += ystep) { + for (let zoff = zoff0; zoff <= zoff1; zoff += zstep) { + const idx = xoff + yoff + zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } // Put all bodies into the bins + + for (let i = 0; i !== N; i++) { + const bi = bodies[i]; + const si = bi.shapes[0]; + + switch (si.type) { + case SPHERE: { + const shape = si; // Put in bin + // check if overlap with other bins + + const x = bi.position.x; + const y = bi.position.y; + const z = bi.position.z; + const r = shape.radius; + addBoxToBins(x - r, y - r, z - r, x + r, y + r, z + r, bi); + break; + } + + case PLANE: { + const shape = si; + + if (shape.worldNormalNeedsUpdate) { + shape.computeWorldNormal(bi.quaternion); + } + + const planeNormal = shape.worldNormal; //Relative position from origin of plane object to the first bin + //Incremented as we iterate through the bins + + const xreset = xmin + binsizeX * 0.5 - bi.position.x; + const yreset = ymin + binsizeY * 0.5 - bi.position.y; + const zreset = zmin + binsizeZ * 0.5 - bi.position.z; + const d = GridBroadphase_collisionPairs_d; + d.set(xreset, yreset, zreset); + + for ( + let xi = 0, xoff = 0; + xi !== nx; + xi++, xoff += xstep, d.y = yreset, d.x += binsizeX + ) { + for ( + let yi = 0, yoff = 0; + yi !== ny; + yi++, yoff += ystep, d.z = zreset, d.y += binsizeY + ) { + for ( + let zi = 0, zoff = 0; + zi !== nz; + zi++, zoff += zstep, d.z += binsizeZ + ) { + if (d.dot(planeNormal) < binRadius) { + const idx = xoff + yoff + zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } + + break; + } + + default: { + if (bi.aabbNeedsUpdate) { + bi.updateAABB(); + } + + addBoxToBins( + bi.aabb.lowerBound.x, + bi.aabb.lowerBound.y, + bi.aabb.lowerBound.z, + bi.aabb.upperBound.x, + bi.aabb.upperBound.y, + bi.aabb.upperBound.z, + bi + ); + break; + } + } + } // Check each bin + + for (let i = 0; i !== Nbins; i++) { + const binLength = binLengths[i]; //Skip bins with no potential collisions + + if (binLength > 1) { + const bin = bins[i]; // Do N^2 broadphase inside + + for (let xi = 0; xi !== binLength; xi++) { + const bi = bin[xi]; + + for (let yi = 0; yi !== xi; yi++) { + const bj = bin[yi]; + + if (this.needBroadphaseCollision(bi, bj)) { + this.intersectionTest(bi, bj, pairs1, pairs2); + } + } + } + } + } // for (let zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { + // console.log("layer "+zi); + // for (let yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { + // const row = ''; + // for (let xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { + // const idx = xoff + yoff + zoff; + // row += ' ' + binLengths[idx]; + // } + // console.log(row); + // } + // } + + this.makePairsUnique(pairs1, pairs2); + } + } + const GridBroadphase_collisionPairs_d = new Vec3(); + new Vec3(); + + /** + * Naive broadphase implementation, used in lack of better ones. + * + * The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 _(which is bad)_ + */ + class NaiveBroadphase extends Broadphase { + /** + * @todo Remove useless constructor + */ + constructor() { + super(); + } + /** + * Get all the collision pairs in the physics world + */ + + collisionPairs(world, pairs1, pairs2) { + const bodies = world.bodies; + const n = bodies.length; + let bi; + let bj; // Naive N^2 ftw! + + for (let i = 0; i !== n; i++) { + for (let j = 0; j !== i; j++) { + bi = bodies[i]; + bj = bodies[j]; + + if (!this.needBroadphaseCollision(bi, bj)) { + continue; + } + + this.intersectionTest(bi, bj, pairs1, pairs2); + } + } + } + /** + * Returns all the bodies within an AABB. + * @param result An array to store resulting bodies in. + */ + + aabbQuery(world, aabb, result) { + if (result === void 0) { + result = []; + } + + for (let i = 0; i < world.bodies.length; i++) { + const b = world.bodies[i]; + + if (b.aabbNeedsUpdate) { + b.updateAABB(); + } // Ugly hack until Body gets aabb + + if (b.aabb.overlaps(aabb)) { + result.push(b); + } + } + + return result; + } + } + + /** + * Storage for Ray casting data + */ + class RaycastResult { + /** + * rayFromWorld + */ + + /** + * rayToWorld + */ + + /** + * hitNormalWorld + */ + + /** + * hitPointWorld + */ + + /** + * hasHit + */ + + /** + * shape + */ + + /** + * body + */ + + /** + * The index of the hit triangle, if the hit shape was a trimesh + */ + + /** + * Distance to the hit. Will be set to -1 if there was no hit + */ + + /** + * If the ray should stop traversing the bodies + */ + constructor() { + this.rayFromWorld = new Vec3(); + this.rayToWorld = new Vec3(); + this.hitNormalWorld = new Vec3(); + this.hitPointWorld = new Vec3(); + this.hasHit = false; + this.shape = null; + this.body = null; + this.hitFaceIndex = -1; + this.distance = -1; + this.shouldStop = false; + } + /** + * Reset all result data. + */ + + reset() { + this.rayFromWorld.setZero(); + this.rayToWorld.setZero(); + this.hitNormalWorld.setZero(); + this.hitPointWorld.setZero(); + this.hasHit = false; + this.shape = null; + this.body = null; + this.hitFaceIndex = -1; + this.distance = -1; + this.shouldStop = false; + } + /** + * abort + */ + + abort() { + this.shouldStop = true; + } + /** + * Set result data. + */ + + set( + rayFromWorld, + rayToWorld, + hitNormalWorld, + hitPointWorld, + shape, + body, + distance + ) { + this.rayFromWorld.copy(rayFromWorld); + this.rayToWorld.copy(rayToWorld); + this.hitNormalWorld.copy(hitNormalWorld); + this.hitPointWorld.copy(hitPointWorld); + this.shape = shape; + this.body = body; + this.distance = distance; + } + } + + let _Shape$types$SPHERE, + _Shape$types$PLANE, + _Shape$types$BOX, + _Shape$types$CYLINDER, + _Shape$types$CONVEXPO, + _Shape$types$HEIGHTFI, + _Shape$types$TRIMESH; + + /** + * RAY_MODES + */ + const RAY_MODES = { + /** CLOSEST */ + CLOSEST: 1, + + /** ANY */ + ANY: 2, + + /** ALL */ + ALL: 4, + }; + /** + * RayMode + */ + + _Shape$types$SPHERE = Shape.types.SPHERE; + _Shape$types$PLANE = Shape.types.PLANE; + _Shape$types$BOX = Shape.types.BOX; + _Shape$types$CYLINDER = Shape.types.CYLINDER; + _Shape$types$CONVEXPO = Shape.types.CONVEXPOLYHEDRON; + _Shape$types$HEIGHTFI = Shape.types.HEIGHTFIELD; + _Shape$types$TRIMESH = Shape.types.TRIMESH; + + /** + * A line in 3D space that intersects bodies and return points. + */ + class Ray { + /** + * from + */ + + /** + * to + */ + + /** + * direction + */ + + /** + * The precision of the ray. Used when checking parallelity etc. + * @default 0.0001 + */ + + /** + * Set to `false` if you don't want the Ray to take `collisionResponse` flags into account on bodies and shapes. + * @default true + */ + + /** + * If set to `true`, the ray skips any hits with normal.dot(rayDirection) < 0. + * @default false + */ + + /** + * collisionFilterMask + * @default -1 + */ + + /** + * collisionFilterGroup + * @default -1 + */ + + /** + * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST. + * @default RAY.ANY + */ + + /** + * Current result object. + */ + + /** + * Will be set to `true` during intersectWorld() if the ray hit anything. + */ + + /** + * User-provided result callback. Will be used if mode is Ray.ALL. + */ + + /** + * CLOSEST + */ + + /** + * ANY + */ + + /** + * ALL + */ + get [_Shape$types$SPHERE]() { + return this._intersectSphere; + } + + get [_Shape$types$PLANE]() { + return this._intersectPlane; + } + + get [_Shape$types$BOX]() { + return this._intersectBox; + } + + get [_Shape$types$CYLINDER]() { + return this._intersectConvex; + } + + get [_Shape$types$CONVEXPO]() { + return this._intersectConvex; + } + + get [_Shape$types$HEIGHTFI]() { + return this._intersectHeightfield; + } + + get [_Shape$types$TRIMESH]() { + return this._intersectTrimesh; + } + + constructor(from, to) { + if (from === void 0) { + from = new Vec3(); + } + + if (to === void 0) { + to = new Vec3(); + } + + this.from = from.clone(); + this.to = to.clone(); + this.direction = new Vec3(); + this.precision = 0.0001; + this.checkCollisionResponse = true; + this.skipBackfaces = false; + this.collisionFilterMask = -1; + this.collisionFilterGroup = -1; + this.mode = Ray.ANY; + this.result = new RaycastResult(); + this.hasHit = false; + + this.callback = (result) => {}; + } + /** + * Do itersection against all bodies in the given World. + * @return True if the ray hit anything, otherwise false. + */ + + intersectWorld(world, options) { + this.mode = options.mode || Ray.ANY; + this.result = options.result || new RaycastResult(); + this.skipBackfaces = !!options.skipBackfaces; + this.collisionFilterMask = + typeof options.collisionFilterMask !== "undefined" + ? options.collisionFilterMask + : -1; + this.collisionFilterGroup = + typeof options.collisionFilterGroup !== "undefined" + ? options.collisionFilterGroup + : -1; + this.checkCollisionResponse = + typeof options.checkCollisionResponse !== "undefined" + ? options.checkCollisionResponse + : true; + + if (options.from) { + this.from.copy(options.from); + } + + if (options.to) { + this.to.copy(options.to); + } + + this.callback = options.callback || (() => {}); + + this.hasHit = false; + this.result.reset(); + this.updateDirection(); + this.getAABB(tmpAABB$1); + tmpArray.length = 0; + world.broadphase.aabbQuery(world, tmpAABB$1, tmpArray); + this.intersectBodies(tmpArray); + return this.hasHit; + } + /** + * Shoot a ray at a body, get back information about the hit. + * @deprecated @param result set the result property of the Ray instead. + */ + + intersectBody(body, result) { + if (result) { + this.result = result; + this.updateDirection(); + } + + const checkCollisionResponse = this.checkCollisionResponse; + + if (checkCollisionResponse && !body.collisionResponse) { + return; + } + + if ( + (this.collisionFilterGroup & body.collisionFilterMask) === 0 || + (body.collisionFilterGroup & this.collisionFilterMask) === 0 + ) { + return; + } + + const xi = intersectBody_xi; + const qi = intersectBody_qi; + + for (let i = 0, N = body.shapes.length; i < N; i++) { + const shape = body.shapes[i]; + + if (checkCollisionResponse && !shape.collisionResponse) { + continue; // Skip + } + + body.quaternion.mult(body.shapeOrientations[i], qi); + body.quaternion.vmult(body.shapeOffsets[i], xi); + xi.vadd(body.position, xi); + this.intersectShape(shape, qi, xi, body); + + if (this.result.shouldStop) { + break; + } + } + } + /** + * Shoot a ray at an array bodies, get back information about the hit. + * @param bodies An array of Body objects. + * @deprecated @param result set the result property of the Ray instead. + * + */ + + intersectBodies(bodies, result) { + if (result) { + this.result = result; + this.updateDirection(); + } + + for (let i = 0, l = bodies.length; !this.result.shouldStop && i < l; i++) { + this.intersectBody(bodies[i]); + } + } + /** + * Updates the direction vector. + */ + + updateDirection() { + this.to.vsub(this.from, this.direction); + this.direction.normalize(); + } + + intersectShape(shape, quat, position, body) { + const from = this.from; // Checking boundingSphere + + const distance = distanceFromIntersection(from, this.direction, position); + + if (distance > shape.boundingSphereRadius) { + return; + } + + const intersectMethod = this[shape.type]; + + if (intersectMethod) { + intersectMethod.call(this, shape, quat, position, body, shape); + } + } + + _intersectBox(box, quat, position, body, reportedShape) { + return this._intersectConvex( + box.convexPolyhedronRepresentation, + quat, + position, + body, + reportedShape + ); + } + + _intersectPlane(shape, quat, position, body, reportedShape) { + const from = this.from; + const to = this.to; + const direction = this.direction; // Get plane normal + + const worldNormal = new Vec3(0, 0, 1); + quat.vmult(worldNormal, worldNormal); + const len = new Vec3(); + from.vsub(position, len); + const planeToFrom = len.dot(worldNormal); + to.vsub(position, len); + const planeToTo = len.dot(worldNormal); + + if (planeToFrom * planeToTo > 0) { + // "from" and "to" are on the same side of the plane... bail out + return; + } + + if (from.distanceTo(to) < planeToFrom) { + return; + } + + const n_dot_dir = worldNormal.dot(direction); + + if (Math.abs(n_dot_dir) < this.precision) { + // No intersection + return; + } + + const planePointToFrom = new Vec3(); + const dir_scaled_with_t = new Vec3(); + const hitPointWorld = new Vec3(); + from.vsub(position, planePointToFrom); + const t = -worldNormal.dot(planePointToFrom) / n_dot_dir; + direction.scale(t, dir_scaled_with_t); + from.vadd(dir_scaled_with_t, hitPointWorld); + this.reportIntersection( + worldNormal, + hitPointWorld, + reportedShape, + body, + -1 + ); + } + /** + * Get the world AABB of the ray. + */ + + getAABB(aabb) { + const { lowerBound, upperBound } = aabb; + const to = this.to; + const from = this.from; + lowerBound.x = Math.min(to.x, from.x); + lowerBound.y = Math.min(to.y, from.y); + lowerBound.z = Math.min(to.z, from.z); + upperBound.x = Math.max(to.x, from.x); + upperBound.y = Math.max(to.y, from.y); + upperBound.z = Math.max(to.z, from.z); + } + + _intersectHeightfield(shape, quat, position, body, reportedShape) { + shape.data; + shape.elementSize; // Convert the ray to local heightfield coordinates + + const localRay = intersectHeightfield_localRay; //new Ray(this.from, this.to); + + localRay.from.copy(this.from); + localRay.to.copy(this.to); + Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from); + Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to); + localRay.updateDirection(); // Get the index of the data points to test against + + const index = intersectHeightfield_index; + let iMinX; + let iMinY; + let iMaxX; + let iMaxY; // Set to max + + iMinX = iMinY = 0; + iMaxX = iMaxY = shape.data.length - 1; + const aabb = new AABB(); + localRay.getAABB(aabb); + shape.getIndexOfPosition(aabb.lowerBound.x, aabb.lowerBound.y, index, true); + iMinX = Math.max(iMinX, index[0]); + iMinY = Math.max(iMinY, index[1]); + shape.getIndexOfPosition(aabb.upperBound.x, aabb.upperBound.y, index, true); + iMaxX = Math.min(iMaxX, index[0] + 1); + iMaxY = Math.min(iMaxY, index[1] + 1); + + for (let i = iMinX; i < iMaxX; i++) { + for (let j = iMinY; j < iMaxY; j++) { + if (this.result.shouldStop) { + return; + } + + shape.getAabbAtIndex(i, j, aabb); + + if (!aabb.overlapsRay(localRay)) { + continue; + } // Lower triangle + + shape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame( + position, + quat, + shape.pillarOffset, + worldPillarOffset + ); + + this._intersectConvex( + shape.pillarConvex, + quat, + worldPillarOffset, + body, + reportedShape, + intersectConvexOptions + ); + + if (this.result.shouldStop) { + return; + } // Upper triangle + + shape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame( + position, + quat, + shape.pillarOffset, + worldPillarOffset + ); + + this._intersectConvex( + shape.pillarConvex, + quat, + worldPillarOffset, + body, + reportedShape, + intersectConvexOptions + ); + } + } + } + + _intersectSphere(sphere, quat, position, body, reportedShape) { + const from = this.from; + const to = this.to; + const r = sphere.radius; + const a = + (to.x - from.x) ** 2 + (to.y - from.y) ** 2 + (to.z - from.z) ** 2; + const b = + 2 * + ((to.x - from.x) * (from.x - position.x) + + (to.y - from.y) * (from.y - position.y) + + (to.z - from.z) * (from.z - position.z)); + const c = + (from.x - position.x) ** 2 + + (from.y - position.y) ** 2 + + (from.z - position.z) ** 2 - + r ** 2; + const delta = b ** 2 - 4 * a * c; + const intersectionPoint = Ray_intersectSphere_intersectionPoint; + const normal = Ray_intersectSphere_normal; + + if (delta < 0) { + // No intersection + return; + } else if (delta === 0) { + // single intersection point + from.lerp(to, delta, intersectionPoint); + intersectionPoint.vsub(position, normal); + normal.normalize(); + this.reportIntersection( + normal, + intersectionPoint, + reportedShape, + body, + -1 + ); + } else { + const d1 = (-b - Math.sqrt(delta)) / (2 * a); + const d2 = (-b + Math.sqrt(delta)) / (2 * a); + + if (d1 >= 0 && d1 <= 1) { + from.lerp(to, d1, intersectionPoint); + intersectionPoint.vsub(position, normal); + normal.normalize(); + this.reportIntersection( + normal, + intersectionPoint, + reportedShape, + body, + -1 + ); + } + + if (this.result.shouldStop) { + return; + } + + if (d2 >= 0 && d2 <= 1) { + from.lerp(to, d2, intersectionPoint); + intersectionPoint.vsub(position, normal); + normal.normalize(); + this.reportIntersection( + normal, + intersectionPoint, + reportedShape, + body, + -1 + ); + } + } + } + + _intersectConvex(shape, quat, position, body, reportedShape, options) { + const normal = intersectConvex_normal; + const vector = intersectConvex_vector; + const faceList = (options && options.faceList) || null; // Checking faces + + const faces = shape.faces; + const vertices = shape.vertices; + const normals = shape.faceNormals; + const direction = this.direction; + const from = this.from; + const to = this.to; + const fromToDistance = from.distanceTo(to); + const Nfaces = faceList ? faceList.length : faces.length; + const result = this.result; + + for (let j = 0; !result.shouldStop && j < Nfaces; j++) { + const fi = faceList ? faceList[j] : j; + const face = faces[fi]; + const faceNormal = normals[fi]; + const q = quat; + const x = position; // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + // Get plane point in world coordinates... + + vector.copy(vertices[face[0]]); + q.vmult(vector, vector); + vector.vadd(x, vector); // ...but make it relative to the ray from. We'll fix this later. + + vector.vsub(from, vector); // Get plane normal + + q.vmult(faceNormal, normal); // If this dot product is negative, we have something interesting + + const dot = direction.dot(normal); // Bail out if ray and plane are parallel + + if (Math.abs(dot) < this.precision) { + continue; + } // calc distance to plane + + const scalar = normal.dot(vector) / dot; // if negative distance, then plane is behind ray + + if (scalar < 0) { + continue; + } // if (dot < 0) { + // Intersection point is from + direction * scalar + + direction.scale(scalar, intersectPoint); + intersectPoint.vadd(from, intersectPoint); // a is the point we compare points b and c with. + + a.copy(vertices[face[0]]); + q.vmult(a, a); + x.vadd(a, a); + + for (let i = 1; !result.shouldStop && i < face.length - 1; i++) { + // Transform 3 vertices to world coords + b.copy(vertices[face[i]]); + c.copy(vertices[face[i + 1]]); + q.vmult(b, b); + q.vmult(c, c); + x.vadd(b, b); + x.vadd(c, c); + const distance = intersectPoint.distanceTo(from); + + if ( + !( + Ray.pointInTriangle(intersectPoint, a, b, c) || + Ray.pointInTriangle(intersectPoint, b, a, c) + ) || + distance > fromToDistance + ) { + continue; + } + + this.reportIntersection( + normal, + intersectPoint, + reportedShape, + body, + fi + ); + } // } + } + } + /** + * @todo Optimize by transforming the world to local space first. + * @todo Use Octree lookup + */ + + _intersectTrimesh(mesh, quat, position, body, reportedShape, options) { + const normal = intersectTrimesh_normal; + const triangles = intersectTrimesh_triangles; + const treeTransform = intersectTrimesh_treeTransform; + const vector = intersectConvex_vector; + const localDirection = intersectTrimesh_localDirection; + const localFrom = intersectTrimesh_localFrom; + const localTo = intersectTrimesh_localTo; + const worldIntersectPoint = intersectTrimesh_worldIntersectPoint; + const worldNormal = intersectTrimesh_worldNormal; // Checking faces + + const indices = mesh.indices; + mesh.vertices; // const normals = mesh.faceNormals + + const from = this.from; + const to = this.to; + const direction = this.direction; + treeTransform.position.copy(position); + treeTransform.quaternion.copy(quat); // Transform ray to local space! + + Transform.vectorToLocalFrame(position, quat, direction, localDirection); + Transform.pointToLocalFrame(position, quat, from, localFrom); + Transform.pointToLocalFrame(position, quat, to, localTo); + localTo.x *= mesh.scale.x; + localTo.y *= mesh.scale.y; + localTo.z *= mesh.scale.z; + localFrom.x *= mesh.scale.x; + localFrom.y *= mesh.scale.y; + localFrom.z *= mesh.scale.z; + localTo.vsub(localFrom, localDirection); + localDirection.normalize(); + const fromToDistanceSquared = localFrom.distanceSquared(localTo); + mesh.tree.rayQuery(this, treeTransform, triangles); + + for ( + let i = 0, N = triangles.length; + !this.result.shouldStop && i !== N; + i++ + ) { + const trianglesIndex = triangles[i]; + mesh.getNormal(trianglesIndex, normal); // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + // Get plane point in world coordinates... + + mesh.getVertex(indices[trianglesIndex * 3], a); // ...but make it relative to the ray from. We'll fix this later. + + a.vsub(localFrom, vector); // If this dot product is negative, we have something interesting + + const dot = localDirection.dot(normal); // Bail out if ray and plane are parallel + // if (Math.abs( dot ) < this.precision){ + // continue; + // } + // calc distance to plane + + const scalar = normal.dot(vector) / dot; // if negative distance, then plane is behind ray + + if (scalar < 0) { + continue; + } // Intersection point is from + direction * scalar + + localDirection.scale(scalar, intersectPoint); + intersectPoint.vadd(localFrom, intersectPoint); // Get triangle vertices + + mesh.getVertex(indices[trianglesIndex * 3 + 1], b); + mesh.getVertex(indices[trianglesIndex * 3 + 2], c); + const squaredDistance = intersectPoint.distanceSquared(localFrom); + + if ( + !( + Ray.pointInTriangle(intersectPoint, b, a, c) || + Ray.pointInTriangle(intersectPoint, a, b, c) + ) || + squaredDistance > fromToDistanceSquared + ) { + continue; + } // transform intersectpoint and normal to world + + Transform.vectorToWorldFrame(quat, normal, worldNormal); + Transform.pointToWorldFrame( + position, + quat, + intersectPoint, + worldIntersectPoint + ); + this.reportIntersection( + worldNormal, + worldIntersectPoint, + reportedShape, + body, + trianglesIndex + ); + } + + triangles.length = 0; + } + /** + * @return True if the intersections should continue + */ + + reportIntersection(normal, hitPointWorld, shape, body, hitFaceIndex) { + const from = this.from; + const to = this.to; + const distance = from.distanceTo(hitPointWorld); + const result = this.result; // Skip back faces? + + if (this.skipBackfaces && normal.dot(this.direction) > 0) { + return; + } + + result.hitFaceIndex = + typeof hitFaceIndex !== "undefined" ? hitFaceIndex : -1; + + switch (this.mode) { + case Ray.ALL: + this.hasHit = true; + result.set(from, to, normal, hitPointWorld, shape, body, distance); + result.hasHit = true; + this.callback(result); + break; + + case Ray.CLOSEST: + // Store if closer than current closest + if (distance < result.distance || !result.hasHit) { + this.hasHit = true; + result.hasHit = true; + result.set(from, to, normal, hitPointWorld, shape, body, distance); + } + + break; + + case Ray.ANY: + // Report and stop. + this.hasHit = true; + result.hasHit = true; + result.set(from, to, normal, hitPointWorld, shape, body, distance); + result.shouldStop = true; + break; + } + } + /** + * As per "Barycentric Technique" as named + * {@link https://www.blackpawn.com/texts/pointinpoly/default.html here} but without the division + */ + + static pointInTriangle(p, a, b, c) { + c.vsub(a, v0); + b.vsub(a, v1); + p.vsub(a, v2); + const dot00 = v0.dot(v0); + const dot01 = v0.dot(v1); + const dot02 = v0.dot(v2); + const dot11 = v1.dot(v1); + const dot12 = v1.dot(v2); + let u; + let v; + return ( + (u = dot11 * dot02 - dot01 * dot12) >= 0 && + (v = dot00 * dot12 - dot01 * dot02) >= 0 && + u + v < dot00 * dot11 - dot01 * dot01 + ); + } + } + Ray.CLOSEST = RAY_MODES.CLOSEST; + Ray.ANY = RAY_MODES.ANY; + Ray.ALL = RAY_MODES.ALL; + const tmpAABB$1 = new AABB(); + const tmpArray = []; + const v1 = new Vec3(); + const v2 = new Vec3(); + const intersectBody_xi = new Vec3(); + const intersectBody_qi = new Quaternion(); + const intersectPoint = new Vec3(); + const a = new Vec3(); + const b = new Vec3(); + const c = new Vec3(); + new Vec3(); + new RaycastResult(); + const intersectConvexOptions = { + faceList: [0], + }; + const worldPillarOffset = new Vec3(); + const intersectHeightfield_localRay = new Ray(); + const intersectHeightfield_index = []; + const Ray_intersectSphere_intersectionPoint = new Vec3(); + const Ray_intersectSphere_normal = new Vec3(); + const intersectConvex_normal = new Vec3(); + new Vec3(); + new Vec3(); + const intersectConvex_vector = new Vec3(); + const intersectTrimesh_normal = new Vec3(); + const intersectTrimesh_localDirection = new Vec3(); + const intersectTrimesh_localFrom = new Vec3(); + const intersectTrimesh_localTo = new Vec3(); + const intersectTrimesh_worldNormal = new Vec3(); + const intersectTrimesh_worldIntersectPoint = new Vec3(); + new AABB(); + const intersectTrimesh_triangles = []; + const intersectTrimesh_treeTransform = new Transform(); + const v0 = new Vec3(); + const intersect = new Vec3(); + + function distanceFromIntersection(from, direction, position) { + // v0 is vector from from to position + position.vsub(from, v0); + const dot = v0.dot(direction); // intersect = direction*dot + from + + direction.scale(dot, intersect); + intersect.vadd(from, intersect); + const distance = position.distanceTo(intersect); + return distance; + } + + /** + * Sweep and prune broadphase along one axis. + */ + class SAPBroadphase extends Broadphase { + /** + * List of bodies currently in the broadphase. + */ + + /** + * The world to search in. + */ + + /** + * Axis to sort the bodies along. + * Set to 0 for x axis, and 1 for y axis. + * For best performance, pick the axis where bodies are most distributed. + */ + + /** + * Check if the bounds of two bodies overlap, along the given SAP axis. + */ + static checkBounds(bi, bj, axisIndex) { + let biPos; + let bjPos; + + if (axisIndex === 0) { + biPos = bi.position.x; + bjPos = bj.position.x; + } else if (axisIndex === 1) { + biPos = bi.position.y; + bjPos = bj.position.y; + } else if (axisIndex === 2) { + biPos = bi.position.z; + bjPos = bj.position.z; + } + + const ri = bi.boundingRadius, + rj = bj.boundingRadius, + boundA2 = biPos + ri, + boundB1 = bjPos - rj; + return boundB1 < boundA2; + } // Note: these are identical, save for x/y/z lowerbound + + /** + * insertionSortX + */ + + static insertionSortX(a) { + for (let i = 1, l = a.length; i < l; i++) { + const v = a[i]; + let j; + + for (j = i - 1; j >= 0; j--) { + if (a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x) { + break; + } + + a[j + 1] = a[j]; + } + + a[j + 1] = v; + } + + return a; + } + /** + * insertionSortY + */ + + static insertionSortY(a) { + for (let i = 1, l = a.length; i < l; i++) { + const v = a[i]; + let j; + + for (j = i - 1; j >= 0; j--) { + if (a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y) { + break; + } + + a[j + 1] = a[j]; + } + + a[j + 1] = v; + } + + return a; + } + /** + * insertionSortZ + */ + + static insertionSortZ(a) { + for (let i = 1, l = a.length; i < l; i++) { + const v = a[i]; + let j; + + for (j = i - 1; j >= 0; j--) { + if (a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z) { + break; + } + + a[j + 1] = a[j]; + } + + a[j + 1] = v; + } + + return a; + } + + constructor(world) { + super(); + this.axisList = []; + this.world = null; + this.axisIndex = 0; + const axisList = this.axisList; + + this._addBodyHandler = (event) => { + axisList.push(event.body); + }; + + this._removeBodyHandler = (event) => { + const idx = axisList.indexOf(event.body); + + if (idx !== -1) { + axisList.splice(idx, 1); + } + }; + + if (world) { + this.setWorld(world); + } + } + /** + * Change the world + */ + + setWorld(world) { + // Clear the old axis array + this.axisList.length = 0; // Add all bodies from the new world + + for (let i = 0; i < world.bodies.length; i++) { + this.axisList.push(world.bodies[i]); + } // Remove old handlers, if any + + world.removeEventListener("addBody", this._addBodyHandler); + world.removeEventListener("removeBody", this._removeBodyHandler); // Add handlers to update the list of bodies. + + world.addEventListener("addBody", this._addBodyHandler); + world.addEventListener("removeBody", this._removeBodyHandler); + this.world = world; + this.dirty = true; + } + /** + * Collect all collision pairs + */ + + collisionPairs(world, p1, p2) { + const bodies = this.axisList; + const N = bodies.length; + const axisIndex = this.axisIndex; + let i; + let j; + + if (this.dirty) { + this.sortList(); + this.dirty = false; + } // Look through the list + + for (i = 0; i !== N; i++) { + const bi = bodies[i]; + + for (j = i + 1; j < N; j++) { + const bj = bodies[j]; + + if (!this.needBroadphaseCollision(bi, bj)) { + continue; + } + + if (!SAPBroadphase.checkBounds(bi, bj, axisIndex)) { + break; + } + + this.intersectionTest(bi, bj, p1, p2); + } + } + } + + sortList() { + const axisList = this.axisList; + const axisIndex = this.axisIndex; + const N = axisList.length; // Update AABBs + + for (let i = 0; i !== N; i++) { + const bi = axisList[i]; + + if (bi.aabbNeedsUpdate) { + bi.updateAABB(); + } + } // Sort the list + + if (axisIndex === 0) { + SAPBroadphase.insertionSortX(axisList); + } else if (axisIndex === 1) { + SAPBroadphase.insertionSortY(axisList); + } else if (axisIndex === 2) { + SAPBroadphase.insertionSortZ(axisList); + } + } + /** + * Computes the variance of the body positions and estimates the best axis to use. + * Will automatically set property `axisIndex`. + */ + + autoDetectAxis() { + let sumX = 0; + let sumX2 = 0; + let sumY = 0; + let sumY2 = 0; + let sumZ = 0; + let sumZ2 = 0; + const bodies = this.axisList; + const N = bodies.length; + const invN = 1 / N; + + for (let i = 0; i !== N; i++) { + const b = bodies[i]; + const centerX = b.position.x; + sumX += centerX; + sumX2 += centerX * centerX; + const centerY = b.position.y; + sumY += centerY; + sumY2 += centerY * centerY; + const centerZ = b.position.z; + sumZ += centerZ; + sumZ2 += centerZ * centerZ; + } + + const varianceX = sumX2 - sumX * sumX * invN; + const varianceY = sumY2 - sumY * sumY * invN; + const varianceZ = sumZ2 - sumZ * sumZ * invN; + + if (varianceX > varianceY) { + if (varianceX > varianceZ) { + this.axisIndex = 0; + } else { + this.axisIndex = 2; + } + } else if (varianceY > varianceZ) { + this.axisIndex = 1; + } else { + this.axisIndex = 2; + } + } + /** + * Returns all the bodies within an AABB. + * @param result An array to store resulting bodies in. + */ + + aabbQuery(world, aabb, result) { + if (result === void 0) { + result = []; + } + + if (this.dirty) { + this.sortList(); + this.dirty = false; + } + + const axisIndex = this.axisIndex; + let axis = "x"; + + if (axisIndex === 1) { + axis = "y"; + } + + if (axisIndex === 2) { + axis = "z"; + } + + const axisList = this.axisList; + aabb.lowerBound[axis]; + aabb.upperBound[axis]; + + for (let i = 0; i < axisList.length; i++) { + const b = axisList[i]; + + if (b.aabbNeedsUpdate) { + b.updateAABB(); + } + + if (b.aabb.overlaps(aabb)) { + result.push(b); + } + } + + return result; + } + } + + class Utils { + /** + * Extend an options object with default values. + * @param options The options object. May be falsy: in this case, a new object is created and returned. + * @param defaults An object containing default values. + * @return The modified options object. + */ + static defaults(options, defaults) { + if (options === void 0) { + options = {}; + } + + for (let key in defaults) { + if (!(key in options)) { + options[key] = defaults[key]; + } + } + + return options; + } + } + + /** + * Constraint base class + */ + class Constraint { + /** + * Equations to be solved in this constraint. + */ + + /** + * Body A. + */ + + /** + * Body B. + */ + + /** + * Set to false if you don't want the bodies to collide when they are connected. + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + options = Utils.defaults(options, { + collideConnected: true, + wakeUpBodies: true, + }); + this.equations = []; + this.bodyA = bodyA; + this.bodyB = bodyB; + this.id = Constraint.idCounter++; + this.collideConnected = options.collideConnected; + + if (options.wakeUpBodies) { + if (bodyA) { + bodyA.wakeUp(); + } + + if (bodyB) { + bodyB.wakeUp(); + } + } + } + /** + * Update all the equations with data. + */ + + update() { + throw new Error( + "method update() not implmemented in this Constraint subclass!" + ); + } + /** + * Enables all equations in the constraint. + */ + + enable() { + const eqs = this.equations; + + for (let i = 0; i < eqs.length; i++) { + eqs[i].enabled = true; + } + } + /** + * Disables all equations in the constraint. + */ + + disable() { + const eqs = this.equations; + + for (let i = 0; i < eqs.length; i++) { + eqs[i].enabled = false; + } + } + } + Constraint.idCounter = 0; + + /** + * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom. + */ + + class JacobianElement { + /** + * spatial + */ + + /** + * rotational + */ + constructor() { + this.spatial = new Vec3(); + this.rotational = new Vec3(); + } + /** + * Multiply with other JacobianElement + */ + + multiplyElement(element) { + return ( + element.spatial.dot(this.spatial) + + element.rotational.dot(this.rotational) + ); + } + /** + * Multiply with two vectors + */ + + multiplyVectors(spatial, rotational) { + return spatial.dot(this.spatial) + rotational.dot(this.rotational); + } + } + + /** + * Equation base class. + * + * `a`, `b` and `eps` are {@link https://www8.cs.umu.se/kurser/5DV058/VT15/lectures/SPOOKlabnotes.pdf SPOOK} parameters that default to `0.0`. See {@link https://github.com/schteppe/cannon.js/issues/238#issuecomment-147172327 this exchange} for more details on Cannon's physics implementation. + */ + class Equation { + /** + * Minimum (read: negative max) force to be applied by the constraint. + */ + + /** + * Maximum (read: positive max) force to be applied by the constraint. + */ + + /** + * SPOOK parameter + */ + + /** + * SPOOK parameter + */ + + /** + * SPOOK parameter + */ + + /** + * A number, proportional to the force added to the bodies. + */ + constructor(bi, bj, minForce, maxForce) { + if (minForce === void 0) { + minForce = -1e6; + } + + if (maxForce === void 0) { + maxForce = 1e6; + } + + this.id = Equation.idCounter++; + this.minForce = minForce; + this.maxForce = maxForce; + this.bi = bi; + this.bj = bj; + this.a = 0.0; // SPOOK parameter + + this.b = 0.0; // SPOOK parameter + + this.eps = 0.0; // SPOOK parameter + + this.jacobianElementA = new JacobianElement(); + this.jacobianElementB = new JacobianElement(); + this.enabled = true; + this.multiplier = 0; + this.setSpookParams(1e7, 4, 1 / 60); // Set typical spook params + } + /** + * Recalculates a, b, and eps. + * + * The Equation constructor sets typical SPOOK parameters as such: + * * `stiffness` = 1e7 + * * `relaxation` = 4 + * * `timeStep`= 1 / 60, _note the hardcoded refresh rate._ + */ + + setSpookParams(stiffness, relaxation, timeStep) { + const d = relaxation; + const k = stiffness; + const h = timeStep; + this.a = 4.0 / (h * (1 + 4 * d)); + this.b = (4.0 * d) / (1 + 4 * d); + this.eps = 4.0 / (h * h * k * (1 + 4 * d)); + } + /** + * Computes the right hand side of the SPOOK equation + */ + + computeB(a, b, h) { + const GW = this.computeGW(); + const Gq = this.computeGq(); + const GiMf = this.computeGiMf(); + return -Gq * a - GW * b - GiMf * h; + } + /** + * Computes G*q, where q are the generalized body coordinates + */ + + computeGq() { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const xi = bi.position; + const xj = bj.position; + return GA.spatial.dot(xi) + GB.spatial.dot(xj); + } + /** + * Computes G*W, where W are the body velocities + */ + + computeGW() { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const vi = bi.velocity; + const vj = bj.velocity; + const wi = bi.angularVelocity; + const wj = bj.angularVelocity; + return GA.multiplyVectors(vi, wi) + GB.multiplyVectors(vj, wj); + } + /** + * Computes G*Wlambda, where W are the body velocities + */ + + computeGWlambda() { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const vi = bi.vlambda; + const vj = bj.vlambda; + const wi = bi.wlambda; + const wj = bj.wlambda; + return GA.multiplyVectors(vi, wi) + GB.multiplyVectors(vj, wj); + } + /** + * Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies. + */ + + computeGiMf() { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const fi = bi.force; + const ti = bi.torque; + const fj = bj.force; + const tj = bj.torque; + const invMassi = bi.invMassSolve; + const invMassj = bj.invMassSolve; + fi.scale(invMassi, iMfi); + fj.scale(invMassj, iMfj); + bi.invInertiaWorldSolve.vmult(ti, invIi_vmult_taui); + bj.invInertiaWorldSolve.vmult(tj, invIj_vmult_tauj); + return ( + GA.multiplyVectors(iMfi, invIi_vmult_taui) + + GB.multiplyVectors(iMfj, invIj_vmult_tauj) + ); + } + /** + * Computes G*inv(M)*G' + */ + + computeGiMGt() { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const invMassi = bi.invMassSolve; + const invMassj = bj.invMassSolve; + const invIi = bi.invInertiaWorldSolve; + const invIj = bj.invInertiaWorldSolve; + let result = invMassi + invMassj; + invIi.vmult(GA.rotational, tmp); + result += tmp.dot(GA.rotational); + invIj.vmult(GB.rotational, tmp); + result += tmp.dot(GB.rotational); + return result; + } + /** + * Add constraint velocity to the bodies. + */ + + addToWlambda(deltalambda) { + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const bi = this.bi; + const bj = this.bj; + const temp = addToWlambda_temp; // Add to linear velocity + // v_lambda += inv(M) * delta_lamba * G + + bi.vlambda.addScaledVector( + bi.invMassSolve * deltalambda, + GA.spatial, + bi.vlambda + ); + bj.vlambda.addScaledVector( + bj.invMassSolve * deltalambda, + GB.spatial, + bj.vlambda + ); // Add to angular velocity + + bi.invInertiaWorldSolve.vmult(GA.rotational, temp); + bi.wlambda.addScaledVector(deltalambda, temp, bi.wlambda); + bj.invInertiaWorldSolve.vmult(GB.rotational, temp); + bj.wlambda.addScaledVector(deltalambda, temp, bj.wlambda); + } + /** + * Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps + */ + + computeC() { + return this.computeGiMGt() + this.eps; + } + } + Equation.idCounter = 0; + const iMfi = new Vec3(); + const iMfj = new Vec3(); + const invIi_vmult_taui = new Vec3(); + const invIj_vmult_tauj = new Vec3(); + const tmp = new Vec3(); + const addToWlambda_temp = new Vec3(); + + /** + * Contact/non-penetration constraint equation + */ + class ContactEquation extends Equation { + /** + * "bounciness": u1 = -e*u0 + */ + + /** + * World-oriented vector that goes from the center of bi to the contact point. + */ + + /** + * World-oriented vector that starts in body j position and goes to the contact point. + */ + + /** + * Contact normal, pointing out of body i. + */ + constructor(bodyA, bodyB, maxForce) { + if (maxForce === void 0) { + maxForce = 1e6; + } + + super(bodyA, bodyB, 0, maxForce); + this.restitution = 0.0; + this.ri = new Vec3(); + this.rj = new Vec3(); + this.ni = new Vec3(); + } + + computeB(h) { + const a = this.a; + const b = this.b; + const bi = this.bi; + const bj = this.bj; + const ri = this.ri; + const rj = this.rj; + const rixn = ContactEquation_computeB_temp1; + const rjxn = ContactEquation_computeB_temp2; + const vi = bi.velocity; + const wi = bi.angularVelocity; + bi.force; + bi.torque; + const vj = bj.velocity; + const wj = bj.angularVelocity; + bj.force; + bj.torque; + const penetrationVec = ContactEquation_computeB_temp3; + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + const n = this.ni; // Caluclate cross products + + ri.cross(n, rixn); + rj.cross(n, rjxn); // g = xj+rj -(xi+ri) + // G = [ -ni -rixn ni rjxn ] + + n.negate(GA.spatial); + rixn.negate(GA.rotational); + GB.spatial.copy(n); + GB.rotational.copy(rjxn); // Calculate the penetration vector + + penetrationVec.copy(bj.position); + penetrationVec.vadd(rj, penetrationVec); + penetrationVec.vsub(bi.position, penetrationVec); + penetrationVec.vsub(ri, penetrationVec); + const g = n.dot(penetrationVec); // Compute iteration + + const ePlusOne = this.restitution + 1; + const GW = + ePlusOne * vj.dot(n) - ePlusOne * vi.dot(n) + wj.dot(rjxn) - wi.dot(rixn); + const GiMf = this.computeGiMf(); + const B = -g * a - GW * b - h * GiMf; + return B; + } + /** + * Get the current relative velocity in the contact point. + */ + + getImpactVelocityAlongNormal() { + const vi = ContactEquation_getImpactVelocityAlongNormal_vi; + const vj = ContactEquation_getImpactVelocityAlongNormal_vj; + const xi = ContactEquation_getImpactVelocityAlongNormal_xi; + const xj = ContactEquation_getImpactVelocityAlongNormal_xj; + const relVel = ContactEquation_getImpactVelocityAlongNormal_relVel; + this.bi.position.vadd(this.ri, xi); + this.bj.position.vadd(this.rj, xj); + this.bi.getVelocityAtWorldPoint(xi, vi); + this.bj.getVelocityAtWorldPoint(xj, vj); + vi.vsub(vj, relVel); + return this.ni.dot(relVel); + } + } + const ContactEquation_computeB_temp1 = new Vec3(); // Temp vectors + + const ContactEquation_computeB_temp2 = new Vec3(); + const ContactEquation_computeB_temp3 = new Vec3(); + const ContactEquation_getImpactVelocityAlongNormal_vi = new Vec3(); + const ContactEquation_getImpactVelocityAlongNormal_vj = new Vec3(); + const ContactEquation_getImpactVelocityAlongNormal_xi = new Vec3(); + const ContactEquation_getImpactVelocityAlongNormal_xj = new Vec3(); + const ContactEquation_getImpactVelocityAlongNormal_relVel = new Vec3(); + + /** + * Connects two bodies at given offset points. + * @example + * const bodyA = new Body({ mass: 1 }) + * const bodyB = new Body({ mass: 1 }) + * bodyA.position.set(-1, 0, 0) + * bodyB.position.set(1, 0, 0) + * bodyA.addShape(shapeA) + * bodyB.addShape(shapeB) + * world.addBody(bodyA) + * world.addBody(bodyB) + * const localPivotA = new Vec3(1, 0, 0) + * const localPivotB = new Vec3(-1, 0, 0) + * const constraint = new PointToPointConstraint(bodyA, localPivotA, bodyB, localPivotB) + * world.addConstraint(constraint) + */ + class PointToPointConstraint extends Constraint { + /** + * Pivot, defined locally in bodyA. + */ + + /** + * Pivot, defined locally in bodyB. + */ + + /** + * @param pivotA The point relative to the center of mass of bodyA which bodyA is constrained to. + * @param bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point. + * @param pivotB The point relative to the center of mass of bodyB which bodyB is constrained to. + * @param maxForce The maximum force that should be applied to constrain the bodies. + */ + constructor(bodyA, pivotA, bodyB, pivotB, maxForce) { + if (pivotA === void 0) { + pivotA = new Vec3(); + } + + if (pivotB === void 0) { + pivotB = new Vec3(); + } + + if (maxForce === void 0) { + maxForce = 1e6; + } + + super(bodyA, bodyB); + this.pivotA = pivotA.clone(); + this.pivotB = pivotB.clone(); + const x = (this.equationX = new ContactEquation(bodyA, bodyB)); + const y = (this.equationY = new ContactEquation(bodyA, bodyB)); + const z = (this.equationZ = new ContactEquation(bodyA, bodyB)); // Equations to be fed to the solver + + this.equations.push(x, y, z); // Make the equations bidirectional + + x.minForce = y.minForce = z.minForce = -maxForce; + x.maxForce = y.maxForce = z.maxForce = maxForce; + x.ni.set(1, 0, 0); + y.ni.set(0, 1, 0); + z.ni.set(0, 0, 1); + } + + update() { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const x = this.equationX; + const y = this.equationY; + const z = this.equationZ; // Rotate the pivots to world space + + bodyA.quaternion.vmult(this.pivotA, x.ri); + bodyB.quaternion.vmult(this.pivotB, x.rj); + y.ri.copy(x.ri); + y.rj.copy(x.rj); + z.ri.copy(x.ri); + z.rj.copy(x.rj); + } + } + + /** + * Cone equation. Works to keep the given body world vectors aligned, or tilted within a given angle from each other. + */ + class ConeEquation extends Equation { + /** + * Local axis in A + */ + + /** + * Local axis in B + */ + + /** + * The "cone angle" to keep + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + const maxForce = + typeof options.maxForce !== "undefined" ? options.maxForce : 1e6; + super(bodyA, bodyB, -maxForce, maxForce); + this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0); + this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0); + this.angle = typeof options.angle !== "undefined" ? options.angle : 0; + } + + computeB(h) { + const a = this.a; + const b = this.b; + const ni = this.axisA; + const nj = this.axisB; + const nixnj = tmpVec1$2; + const njxni = tmpVec2$2; + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; // Caluclate cross products + + ni.cross(nj, nixnj); + nj.cross(ni, njxni); // The angle between two vector is: + // cos(theta) = a * b / (length(a) * length(b) = { len(a) = len(b) = 1 } = a * b + // g = a * b + // gdot = (b x a) * wi + (a x b) * wj + // G = [0 bxa 0 axb] + // W = [vi wi vj wj] + + GA.rotational.copy(njxni); + GB.rotational.copy(nixnj); + const g = Math.cos(this.angle) - ni.dot(nj); + const GW = this.computeGW(); + const GiMf = this.computeGiMf(); + const B = -g * a - GW * b - h * GiMf; + return B; + } + } + const tmpVec1$2 = new Vec3(); + const tmpVec2$2 = new Vec3(); + + /** + * Rotational constraint. Works to keep the local vectors orthogonal to each other in world space. + */ + class RotationalEquation extends Equation { + /** + * World oriented rotational axis. + */ + + /** + * World oriented rotational axis. + */ + + /** + * maxAngle + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + const maxForce = + typeof options.maxForce !== "undefined" ? options.maxForce : 1e6; + super(bodyA, bodyB, -maxForce, maxForce); + this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0); + this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0); + this.maxAngle = Math.PI / 2; + } + + computeB(h) { + const a = this.a; + const b = this.b; + const ni = this.axisA; + const nj = this.axisB; + const nixnj = tmpVec1$1; + const njxni = tmpVec2$1; + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; // Caluclate cross products + + ni.cross(nj, nixnj); + nj.cross(ni, njxni); // g = ni * nj + // gdot = (nj x ni) * wi + (ni x nj) * wj + // G = [0 njxni 0 nixnj] + // W = [vi wi vj wj] + + GA.rotational.copy(njxni); + GB.rotational.copy(nixnj); + const g = Math.cos(this.maxAngle) - ni.dot(nj); + const GW = this.computeGW(); + const GiMf = this.computeGiMf(); + const B = -g * a - GW * b - h * GiMf; + return B; + } + } + const tmpVec1$1 = new Vec3(); + const tmpVec2$1 = new Vec3(); + + /** + * A Cone Twist constraint, useful for ragdolls. + */ + class ConeTwistConstraint extends PointToPointConstraint { + /** + * The axis direction for the constraint of the body A. + */ + + /** + * The axis direction for the constraint of the body B. + */ + + /** + * The aperture angle of the cone. + */ + + /** + * The twist angle of the joint. + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + const maxForce = + typeof options.maxForce !== "undefined" ? options.maxForce : 1e6; // Set pivot point in between + + const pivotA = options.pivotA ? options.pivotA.clone() : new Vec3(); + const pivotB = options.pivotB ? options.pivotB.clone() : new Vec3(); + super(bodyA, pivotA, bodyB, pivotB, maxForce); + this.axisA = options.axisA ? options.axisA.clone() : new Vec3(); + this.axisB = options.axisB ? options.axisB.clone() : new Vec3(); + this.collideConnected = !!options.collideConnected; + this.angle = typeof options.angle !== "undefined" ? options.angle : 0; + const c = (this.coneEquation = new ConeEquation(bodyA, bodyB, options)); + const t = (this.twistEquation = new RotationalEquation( + bodyA, + bodyB, + options + )); + this.twistAngle = + typeof options.twistAngle !== "undefined" ? options.twistAngle : 0; // Make the cone equation push the bodies toward the cone axis, not outward + + c.maxForce = 0; + c.minForce = -maxForce; // Make the twist equation add torque toward the initial position + + t.maxForce = 0; + t.minForce = -maxForce; + this.equations.push(c, t); + } + + update() { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const cone = this.coneEquation; + const twist = this.twistEquation; + super.update(); // Update the axes to the cone constraint + + bodyA.vectorToWorldFrame(this.axisA, cone.axisA); + bodyB.vectorToWorldFrame(this.axisB, cone.axisB); // Update the world axes in the twist constraint + + this.axisA.tangents(twist.axisA, twist.axisA); + bodyA.vectorToWorldFrame(twist.axisA, twist.axisA); + this.axisB.tangents(twist.axisB, twist.axisB); + bodyB.vectorToWorldFrame(twist.axisB, twist.axisB); + cone.angle = this.angle; + twist.maxAngle = this.twistAngle; + } + } + new Vec3(); + new Vec3(); + + /** + * Constrains two bodies to be at a constant distance from each others center of mass. + */ + class DistanceConstraint extends Constraint { + /** + * The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB + */ + + /** + * @param distance The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB. + * @param maxForce The maximum force that should be applied to constrain the bodies. + */ + constructor(bodyA, bodyB, distance, maxForce) { + if (maxForce === void 0) { + maxForce = 1e6; + } + + super(bodyA, bodyB); + + if (typeof distance === "undefined") { + distance = bodyA.position.distanceTo(bodyB.position); + } + + this.distance = distance; + const eq = (this.distanceEquation = new ContactEquation(bodyA, bodyB)); + this.equations.push(eq); // Make it bidirectional + + eq.minForce = -maxForce; + eq.maxForce = maxForce; + } + /** + * update + */ + + update() { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const eq = this.distanceEquation; + const halfDist = this.distance * 0.5; + const normal = eq.ni; + bodyB.position.vsub(bodyA.position, normal); + normal.normalize(); + normal.scale(halfDist, eq.ri); + normal.scale(-halfDist, eq.rj); + } + } + + /** + * Lock constraint. Will remove all degrees of freedom between the bodies. + */ + class LockConstraint extends PointToPointConstraint { + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + const maxForce = + typeof options.maxForce !== "undefined" ? options.maxForce : 1e6; // Set pivot point in between + + const pivotA = new Vec3(); + const pivotB = new Vec3(); + const halfWay = new Vec3(); + bodyA.position.vadd(bodyB.position, halfWay); + halfWay.scale(0.5, halfWay); + bodyB.pointToLocalFrame(halfWay, pivotB); + bodyA.pointToLocalFrame(halfWay, pivotA); // The point-to-point constraint will keep a point shared between the bodies + + super(bodyA, pivotA, bodyB, pivotB, maxForce); // Store initial rotation of the bodies as unit vectors in the local body spaces + + this.xA = bodyA.vectorToLocalFrame(Vec3.UNIT_X); + this.xB = bodyB.vectorToLocalFrame(Vec3.UNIT_X); + this.yA = bodyA.vectorToLocalFrame(Vec3.UNIT_Y); + this.yB = bodyB.vectorToLocalFrame(Vec3.UNIT_Y); + this.zA = bodyA.vectorToLocalFrame(Vec3.UNIT_Z); + this.zB = bodyB.vectorToLocalFrame(Vec3.UNIT_Z); // ...and the following rotational equations will keep all rotational DOF's in place + + const r1 = (this.rotationalEquation1 = new RotationalEquation( + bodyA, + bodyB, + options + )); + const r2 = (this.rotationalEquation2 = new RotationalEquation( + bodyA, + bodyB, + options + )); + const r3 = (this.rotationalEquation3 = new RotationalEquation( + bodyA, + bodyB, + options + )); + this.equations.push(r1, r2, r3); + } + /** + * update + */ + + update() { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + this.motorEquation; + const r1 = this.rotationalEquation1; + const r2 = this.rotationalEquation2; + const r3 = this.rotationalEquation3; + super.update(); // These vector pairs must be orthogonal + + bodyA.vectorToWorldFrame(this.xA, r1.axisA); + bodyB.vectorToWorldFrame(this.yB, r1.axisB); + bodyA.vectorToWorldFrame(this.yA, r2.axisA); + bodyB.vectorToWorldFrame(this.zB, r2.axisB); + bodyA.vectorToWorldFrame(this.zA, r3.axisA); + bodyB.vectorToWorldFrame(this.xB, r3.axisB); + } + } + new Vec3(); + new Vec3(); + + /** + * Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value. + */ + class RotationalMotorEquation extends Equation { + /** + * World oriented rotational axis. + */ + + /** + * World oriented rotational axis. + */ + + /** + * Motor velocity. + */ + constructor(bodyA, bodyB, maxForce) { + if (maxForce === void 0) { + maxForce = 1e6; + } + + super(bodyA, bodyB, -maxForce, maxForce); + this.axisA = new Vec3(); + this.axisB = new Vec3(); + this.targetVelocity = 0; + } + + computeB(h) { + this.a; + const b = this.b; + this.bi; + this.bj; + const axisA = this.axisA; + const axisB = this.axisB; + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; // g = 0 + // gdot = axisA * wi - axisB * wj + // gdot = G * W = G * [vi wi vj wj] + // => + // G = [0 axisA 0 -axisB] + + GA.rotational.copy(axisA); + axisB.negate(GB.rotational); + const GW = this.computeGW() - this.targetVelocity; + const GiMf = this.computeGiMf(); + const B = -GW * b - h * GiMf; + return B; + } + } + + /** + * Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation. + */ + class HingeConstraint extends PointToPointConstraint { + /** + * Rotation axis, defined locally in bodyA. + */ + + /** + * Rotation axis, defined locally in bodyB. + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + const maxForce = + typeof options.maxForce !== "undefined" ? options.maxForce : 1e6; + const pivotA = options.pivotA ? options.pivotA.clone() : new Vec3(); + const pivotB = options.pivotB ? options.pivotB.clone() : new Vec3(); + super(bodyA, pivotA, bodyB, pivotB, maxForce); + const axisA = (this.axisA = options.axisA + ? options.axisA.clone() + : new Vec3(1, 0, 0)); + axisA.normalize(); + const axisB = (this.axisB = options.axisB + ? options.axisB.clone() + : new Vec3(1, 0, 0)); + axisB.normalize(); + this.collideConnected = !!options.collideConnected; + const rotational1 = (this.rotationalEquation1 = new RotationalEquation( + bodyA, + bodyB, + options + )); + const rotational2 = (this.rotationalEquation2 = new RotationalEquation( + bodyA, + bodyB, + options + )); + const motor = (this.motorEquation = new RotationalMotorEquation( + bodyA, + bodyB, + maxForce + )); + motor.enabled = false; // Not enabled by default + // Equations to be fed to the solver + + this.equations.push(rotational1, rotational2, motor); + } + /** + * enableMotor + */ + + enableMotor() { + this.motorEquation.enabled = true; + } + /** + * disableMotor + */ + + disableMotor() { + this.motorEquation.enabled = false; + } + /** + * setMotorSpeed + */ + + setMotorSpeed(speed) { + this.motorEquation.targetVelocity = speed; + } + /** + * setMotorMaxForce + */ + + setMotorMaxForce(maxForce) { + this.motorEquation.maxForce = maxForce; + this.motorEquation.minForce = -maxForce; + } + /** + * update + */ + + update() { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const motor = this.motorEquation; + const r1 = this.rotationalEquation1; + const r2 = this.rotationalEquation2; + const worldAxisA = HingeConstraint_update_tmpVec1; + const worldAxisB = HingeConstraint_update_tmpVec2; + const axisA = this.axisA; + const axisB = this.axisB; + super.update(); // Get world axes + + bodyA.quaternion.vmult(axisA, worldAxisA); + bodyB.quaternion.vmult(axisB, worldAxisB); + worldAxisA.tangents(r1.axisA, r2.axisA); + r1.axisB.copy(worldAxisB); + r2.axisB.copy(worldAxisB); + + if (this.motorEquation.enabled) { + bodyA.quaternion.vmult(this.axisA, motor.axisA); + bodyB.quaternion.vmult(this.axisB, motor.axisB); + } + } + } + const HingeConstraint_update_tmpVec1 = new Vec3(); + const HingeConstraint_update_tmpVec2 = new Vec3(); + + /** + * Constrains the slipping in a contact along a tangent + */ + class FrictionEquation extends Equation { + // Tangent + + /** + * @param slipForce should be +-F_friction = +-mu * F_normal = +-mu * m * g + */ + constructor(bodyA, bodyB, slipForce) { + super(bodyA, bodyB, -slipForce, slipForce); + this.ri = new Vec3(); + this.rj = new Vec3(); + this.t = new Vec3(); + } + + computeB(h) { + this.a; + const b = this.b; + this.bi; + this.bj; + const ri = this.ri; + const rj = this.rj; + const rixt = FrictionEquation_computeB_temp1; + const rjxt = FrictionEquation_computeB_temp2; + const t = this.t; // Caluclate cross products + + ri.cross(t, rixt); + rj.cross(t, rjxt); // G = [-t -rixt t rjxt] + // And remember, this is a pure velocity constraint, g is always zero! + + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + t.negate(GA.spatial); + rixt.negate(GA.rotational); + GB.spatial.copy(t); + GB.rotational.copy(rjxt); + const GW = this.computeGW(); + const GiMf = this.computeGiMf(); + const B = -GW * b - h * GiMf; + return B; + } + } + const FrictionEquation_computeB_temp1 = new Vec3(); + const FrictionEquation_computeB_temp2 = new Vec3(); + + /** + * Defines what happens when two materials meet. + * @todo Refactor materials to materialA and materialB + */ + class ContactMaterial { + /** + * Identifier of this material. + */ + + /** + * Participating materials. + */ + + /** + * Friction coefficient. + * @default 0.3 + */ + + /** + * Restitution coefficient. + * @default 0.3 + */ + + /** + * Stiffness of the produced contact equations. + * @default 1e7 + */ + + /** + * Relaxation time of the produced contact equations. + * @default 3 + */ + + /** + * Stiffness of the produced friction equations. + * @default 1e7 + */ + + /** + * Relaxation time of the produced friction equations + * @default 3 + */ + constructor(m1, m2, options) { + options = Utils.defaults(options, { + friction: 0.3, + restitution: 0.3, + contactEquationStiffness: 1e7, + contactEquationRelaxation: 3, + frictionEquationStiffness: 1e7, + frictionEquationRelaxation: 3, + }); + this.id = ContactMaterial.idCounter++; + this.materials = [m1, m2]; + this.friction = options.friction; + this.restitution = options.restitution; + this.contactEquationStiffness = options.contactEquationStiffness; + this.contactEquationRelaxation = options.contactEquationRelaxation; + this.frictionEquationStiffness = options.frictionEquationStiffness; + this.frictionEquationRelaxation = options.frictionEquationRelaxation; + } + } + ContactMaterial.idCounter = 0; + + /** + * Defines a physics material. + */ + class Material { + /** + * Material name. + * If options is a string, name will be set to that string. + * @todo Deprecate this + */ + + /** Material id. */ + + /** + * Friction for this material. + * If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from `defaultContactMaterial` in the World will be used. + */ + + /** + * Restitution for this material. + * If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from `defaultContactMaterial` in the World will be used. + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + let name = ""; // Backwards compatibility fix + + if (typeof options === "string") { + //console.warn(`Passing a string to MaterialOptions is deprecated, and has no effect`) + name = options; + options = {}; + } + + this.name = name; + this.id = Material.idCounter++; + this.friction = + typeof options.friction !== "undefined" ? options.friction : -1; + this.restitution = + typeof options.restitution !== "undefined" ? options.restitution : -1; + } + } + Material.idCounter = 0; + + /** + * A spring, connecting two bodies. + * @example + * const spring = new Spring(boxBody, sphereBody, { + * restLength: 0, + * stiffness: 50, + * damping: 1, + * }) + * + * // Compute the force after each step + * world.addEventListener('postStep', (event) => { + * spring.applyForce() + * }) + */ + class Spring { + /** + * Rest length of the spring. A number > 0. + * @default 1 + */ + + /** + * Stiffness of the spring. A number >= 0. + * @default 100 + */ + + /** + * Damping of the spring. A number >= 0. + * @default 1 + */ + + /** + * First connected body. + */ + + /** + * Second connected body. + */ + + /** + * Anchor for bodyA in local bodyA coordinates. + * Where to hook the spring to body A, in local body coordinates. + * @default new Vec3() + */ + + /** + * Anchor for bodyB in local bodyB coordinates. + * Where to hook the spring to body B, in local body coordinates. + * @default new Vec3() + */ + constructor(bodyA, bodyB, options) { + if (options === void 0) { + options = {}; + } + + this.restLength = + typeof options.restLength === "number" ? options.restLength : 1; + this.stiffness = options.stiffness || 100; + this.damping = options.damping || 1; + this.bodyA = bodyA; + this.bodyB = bodyB; + this.localAnchorA = new Vec3(); + this.localAnchorB = new Vec3(); + + if (options.localAnchorA) { + this.localAnchorA.copy(options.localAnchorA); + } + + if (options.localAnchorB) { + this.localAnchorB.copy(options.localAnchorB); + } + + if (options.worldAnchorA) { + this.setWorldAnchorA(options.worldAnchorA); + } + + if (options.worldAnchorB) { + this.setWorldAnchorB(options.worldAnchorB); + } + } + /** + * Set the anchor point on body A, using world coordinates. + */ + + setWorldAnchorA(worldAnchorA) { + this.bodyA.pointToLocalFrame(worldAnchorA, this.localAnchorA); + } + /** + * Set the anchor point on body B, using world coordinates. + */ + + setWorldAnchorB(worldAnchorB) { + this.bodyB.pointToLocalFrame(worldAnchorB, this.localAnchorB); + } + /** + * Get the anchor point on body A, in world coordinates. + * @param result The vector to store the result in. + */ + + getWorldAnchorA(result) { + this.bodyA.pointToWorldFrame(this.localAnchorA, result); + } + /** + * Get the anchor point on body B, in world coordinates. + * @param result The vector to store the result in. + */ + + getWorldAnchorB(result) { + this.bodyB.pointToWorldFrame(this.localAnchorB, result); + } + /** + * Apply the spring force to the connected bodies. + */ + + applyForce() { + const k = this.stiffness; + const d = this.damping; + const l = this.restLength; + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const r = applyForce_r; + const r_unit = applyForce_r_unit; + const u = applyForce_u; + const f = applyForce_f; + const tmp = applyForce_tmp; + const worldAnchorA = applyForce_worldAnchorA; + const worldAnchorB = applyForce_worldAnchorB; + const ri = applyForce_ri; + const rj = applyForce_rj; + const ri_x_f = applyForce_ri_x_f; + const rj_x_f = applyForce_rj_x_f; // Get world anchors + + this.getWorldAnchorA(worldAnchorA); + this.getWorldAnchorB(worldAnchorB); // Get offset points + + worldAnchorA.vsub(bodyA.position, ri); + worldAnchorB.vsub(bodyB.position, rj); // Compute distance vector between world anchor points + + worldAnchorB.vsub(worldAnchorA, r); + const rlen = r.length(); + r_unit.copy(r); + r_unit.normalize(); // Compute relative velocity of the anchor points, u + + bodyB.velocity.vsub(bodyA.velocity, u); // Add rotational velocity + + bodyB.angularVelocity.cross(rj, tmp); + u.vadd(tmp, u); + bodyA.angularVelocity.cross(ri, tmp); + u.vsub(tmp, u); // F = - k * ( x - L ) - D * ( u ) + + r_unit.scale(-k * (rlen - l) - d * u.dot(r_unit), f); // Add forces to bodies + + bodyA.force.vsub(f, bodyA.force); + bodyB.force.vadd(f, bodyB.force); // Angular force + + ri.cross(f, ri_x_f); + rj.cross(f, rj_x_f); + bodyA.torque.vsub(ri_x_f, bodyA.torque); + bodyB.torque.vadd(rj_x_f, bodyB.torque); + } + } + const applyForce_r = new Vec3(); + const applyForce_r_unit = new Vec3(); + const applyForce_u = new Vec3(); + const applyForce_f = new Vec3(); + const applyForce_worldAnchorA = new Vec3(); + const applyForce_worldAnchorB = new Vec3(); + const applyForce_ri = new Vec3(); + const applyForce_rj = new Vec3(); + const applyForce_ri_x_f = new Vec3(); + const applyForce_rj_x_f = new Vec3(); + const applyForce_tmp = new Vec3(); + + /** + * WheelInfo + */ + class WheelInfo { + /** + * Max travel distance of the suspension, in meters. + * @default 1 + */ + + /** + * Speed to apply to the wheel rotation when the wheel is sliding. + * @default -0.1 + */ + + /** + * If the customSlidingRotationalSpeed should be used. + * @default false + */ + + /** + * sliding + */ + + /** + * Connection point, defined locally in the chassis body frame. + */ + + /** + * chassisConnectionPointWorld + */ + + /** + * directionLocal + */ + + /** + * directionWorld + */ + + /** + * axleLocal + */ + + /** + * axleWorld + */ + + /** + * suspensionRestLength + * @default 1 + */ + + /** + * suspensionMaxLength + * @default 2 + */ + + /** + * radius + * @default 1 + */ + + /** + * suspensionStiffness + * @default 100 + */ + + /** + * dampingCompression + * @default 10 + */ + + /** + * dampingRelaxation + * @default 10 + */ + + /** + * frictionSlip + * @default 10.5 + */ + + /** forwardAcceleration */ + + /** sideAcceleration */ + + /** + * steering + * @default 0 + */ + + /** + * Rotation value, in radians. + * @default 0 + */ + + /** + * deltaRotation + * @default 0 + */ + + /** + * rollInfluence + * @default 0.01 + */ + + /** + * maxSuspensionForce + */ + + /** + * engineForce + */ + + /** + * brake + */ + + /** + * isFrontWheel + * @default true + */ + + /** + * clippedInvContactDotSuspension + * @default 1 + */ + + /** + * suspensionRelativeVelocity + * @default 0 + */ + + /** + * suspensionForce + * @default 0 + */ + + /** + * slipInfo + */ + + /** + * skidInfo + * @default 0 + */ + + /** + * suspensionLength + * @default 0 + */ + + /** + * sideImpulse + */ + + /** + * forwardImpulse + */ + + /** + * The result from raycasting. + */ + + /** + * Wheel world transform. + */ + + /** + * isInContact + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + options = Utils.defaults(options, { + chassisConnectionPointLocal: new Vec3(), + chassisConnectionPointWorld: new Vec3(), + directionLocal: new Vec3(), + directionWorld: new Vec3(), + axleLocal: new Vec3(), + axleWorld: new Vec3(), + suspensionRestLength: 1, + suspensionMaxLength: 2, + radius: 1, + suspensionStiffness: 100, + dampingCompression: 10, + dampingRelaxation: 10, + frictionSlip: 10.5, + forwardAcceleration: 1, + sideAcceleration: 1, + steering: 0, + rotation: 0, + deltaRotation: 0, + rollInfluence: 0.01, + maxSuspensionForce: Number.MAX_VALUE, + isFrontWheel: true, + clippedInvContactDotSuspension: 1, + suspensionRelativeVelocity: 0, + suspensionForce: 0, + slipInfo: 0, + skidInfo: 0, + suspensionLength: 0, + maxSuspensionTravel: 1, + useCustomSlidingRotationalSpeed: false, + customSlidingRotationalSpeed: -0.1, + }); + this.maxSuspensionTravel = options.maxSuspensionTravel; + this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed; + this.useCustomSlidingRotationalSpeed = + options.useCustomSlidingRotationalSpeed; + this.sliding = false; + this.chassisConnectionPointLocal = + options.chassisConnectionPointLocal.clone(); + this.chassisConnectionPointWorld = + options.chassisConnectionPointWorld.clone(); + this.directionLocal = options.directionLocal.clone(); + this.directionWorld = options.directionWorld.clone(); + this.axleLocal = options.axleLocal.clone(); + this.axleWorld = options.axleWorld.clone(); + this.suspensionRestLength = options.suspensionRestLength; + this.suspensionMaxLength = options.suspensionMaxLength; + this.radius = options.radius; + this.suspensionStiffness = options.suspensionStiffness; + this.dampingCompression = options.dampingCompression; + this.dampingRelaxation = options.dampingRelaxation; + this.frictionSlip = options.frictionSlip; + this.forwardAcceleration = options.forwardAcceleration; + this.sideAcceleration = options.sideAcceleration; + this.steering = 0; + this.rotation = 0; + this.deltaRotation = 0; + this.rollInfluence = options.rollInfluence; + this.maxSuspensionForce = options.maxSuspensionForce; + this.engineForce = 0; + this.brake = 0; + this.isFrontWheel = options.isFrontWheel; + this.clippedInvContactDotSuspension = 1; + this.suspensionRelativeVelocity = 0; + this.suspensionForce = 0; + this.slipInfo = 0; + this.skidInfo = 0; + this.suspensionLength = 0; + this.sideImpulse = 0; + this.forwardImpulse = 0; + this.raycastResult = new RaycastResult(); + this.worldTransform = new Transform(); + this.isInContact = false; + } + + updateWheel(chassis) { + const raycastResult = this.raycastResult; + + if (this.isInContact) { + const project = raycastResult.hitNormalWorld.dot( + raycastResult.directionWorld + ); + raycastResult.hitPointWorld.vsub(chassis.position, relpos); + chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint); + const projVel = raycastResult.hitNormalWorld.dot( + chassis_velocity_at_contactPoint + ); + + if (project >= -0.1) { + this.suspensionRelativeVelocity = 0.0; + this.clippedInvContactDotSuspension = 1.0 / 0.1; + } else { + const inv = -1 / project; + this.suspensionRelativeVelocity = projVel * inv; + this.clippedInvContactDotSuspension = inv; + } + } else { + // Not in contact : position wheel in a nice (rest length) position + raycastResult.suspensionLength = this.suspensionRestLength; + this.suspensionRelativeVelocity = 0.0; + raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld); + this.clippedInvContactDotSuspension = 1.0; + } + } + } + const chassis_velocity_at_contactPoint = new Vec3(); + const relpos = new Vec3(); + + /** + * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces. + */ + class RaycastVehicle { + /** The car chassis body. */ + + /** The wheels. */ + + /** Will be set to true if the car is sliding. */ + + /** Index of the right axis. x=0, y=1, z=2 */ + + /** Index of the forward axis. x=0, y=1, z=2 */ + + /** Index of the up axis. x=0, y=1, z=2 */ + + /** The constraints. */ + + /** Optional pre-step callback. */ + + /** Number of wheels on the ground. */ + constructor(options) { + this.chassisBody = options.chassisBody; + this.wheelInfos = []; + this.sliding = false; + this.world = null; + this.indexRightAxis = + typeof options.indexRightAxis !== "undefined" + ? options.indexRightAxis + : 2; + this.indexForwardAxis = + typeof options.indexForwardAxis !== "undefined" + ? options.indexForwardAxis + : 0; + this.indexUpAxis = + typeof options.indexUpAxis !== "undefined" ? options.indexUpAxis : 1; + this.constraints = []; + + this.preStepCallback = () => {}; + + this.currentVehicleSpeedKmHour = 0; + this.numWheelsOnGround = 0; + } + /** + * Add a wheel. For information about the options, see `WheelInfo`. + */ + + addWheel(options) { + if (options === void 0) { + options = {}; + } + + const info = new WheelInfo(options); + const index = this.wheelInfos.length; + this.wheelInfos.push(info); + return index; + } + /** + * Set the steering value of a wheel. + */ + + setSteeringValue(value, wheelIndex) { + const wheel = this.wheelInfos[wheelIndex]; + wheel.steering = value; + } + /** + * Set the wheel force to apply on one of the wheels each time step + */ + + applyEngineForce(value, wheelIndex) { + this.wheelInfos[wheelIndex].engineForce = value; + } + /** + * Set the braking force of a wheel + */ + + setBrake(brake, wheelIndex) { + this.wheelInfos[wheelIndex].brake = brake; + } + /** + * Add the vehicle including its constraints to the world. + */ + + addToWorld(world) { + world.addBody(this.chassisBody); + const that = this; + + this.preStepCallback = () => { + that.updateVehicle(world.dt); + }; + + world.addEventListener("preStep", this.preStepCallback); + this.world = world; + } + /** + * Get one of the wheel axles, world-oriented. + */ + + getVehicleAxisWorld(axisIndex, result) { + result.set( + axisIndex === 0 ? 1 : 0, + axisIndex === 1 ? 1 : 0, + axisIndex === 2 ? 1 : 0 + ); + this.chassisBody.vectorToWorldFrame(result, result); + } + + updateVehicle(timeStep) { + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + const chassisBody = this.chassisBody; + + for (let i = 0; i < numWheels; i++) { + this.updateWheelTransform(i); + } + + this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.length(); + const forwardWorld = new Vec3(); + this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld); + + if (forwardWorld.dot(chassisBody.velocity) < 0) { + this.currentVehicleSpeedKmHour *= -1; + } // simulate suspension + + for (let i = 0; i < numWheels; i++) { + this.castRay(wheelInfos[i]); + } + + this.updateSuspension(timeStep); + const impulse = new Vec3(); + const relpos = new Vec3(); + + for (let i = 0; i < numWheels; i++) { + //apply suspension force + const wheel = wheelInfos[i]; + let suspensionForce = wheel.suspensionForce; + + if (suspensionForce > wheel.maxSuspensionForce) { + suspensionForce = wheel.maxSuspensionForce; + } + + wheel.raycastResult.hitNormalWorld.scale( + suspensionForce * timeStep, + impulse + ); + wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos); + chassisBody.applyImpulse(impulse, relpos); + } + + this.updateFriction(timeStep); + const hitNormalWorldScaledWithProj = new Vec3(); + const fwd = new Vec3(); + const vel = new Vec3(); + + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; //const relpos = new Vec3(); + //wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos); + + chassisBody.getVelocityAtWorldPoint( + wheel.chassisConnectionPointWorld, + vel + ); // Hack to get the rotation in the correct direction + + let m = 1; + + switch (this.indexUpAxis) { + case 1: + m = -1; + break; + } + + if (wheel.isInContact) { + this.getVehicleAxisWorld(this.indexForwardAxis, fwd); + const proj = fwd.dot(wheel.raycastResult.hitNormalWorld); + wheel.raycastResult.hitNormalWorld.scale( + proj, + hitNormalWorldScaledWithProj + ); + fwd.vsub(hitNormalWorldScaledWithProj, fwd); + const proj2 = fwd.dot(vel); + wheel.deltaRotation = (m * proj2 * timeStep) / wheel.radius; + } + + if ( + (wheel.sliding || !wheel.isInContact) && + wheel.engineForce !== 0 && + wheel.useCustomSlidingRotationalSpeed + ) { + // Apply custom rotation when accelerating and sliding + wheel.deltaRotation = + (wheel.engineForce > 0 ? 1 : -1) * + wheel.customSlidingRotationalSpeed * + timeStep; + } // Lock wheels + + if (Math.abs(wheel.brake) > Math.abs(wheel.engineForce)) { + wheel.deltaRotation = 0; + } + + wheel.rotation += wheel.deltaRotation; // Use the old value + + wheel.deltaRotation *= 0.99; // damping of rotation when not in contact + } + } + + updateSuspension(deltaTime) { + const chassisBody = this.chassisBody; + const chassisMass = chassisBody.mass; + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + + for (let w_it = 0; w_it < numWheels; w_it++) { + const wheel = wheelInfos[w_it]; + + if (wheel.isInContact) { + let force; // Spring + + const susp_length = wheel.suspensionRestLength; + const current_length = wheel.suspensionLength; + const length_diff = susp_length - current_length; + force = + wheel.suspensionStiffness * + length_diff * + wheel.clippedInvContactDotSuspension; // Damper + + const projected_rel_vel = wheel.suspensionRelativeVelocity; + let susp_damping; + + if (projected_rel_vel < 0) { + susp_damping = wheel.dampingCompression; + } else { + susp_damping = wheel.dampingRelaxation; + } + + force -= susp_damping * projected_rel_vel; + wheel.suspensionForce = force * chassisMass; + + if (wheel.suspensionForce < 0) { + wheel.suspensionForce = 0; + } + } else { + wheel.suspensionForce = 0; + } + } + } + /** + * Remove the vehicle including its constraints from the world. + */ + + removeFromWorld(world) { + this.constraints; + world.removeBody(this.chassisBody); + world.removeEventListener("preStep", this.preStepCallback); + this.world = null; + } + + castRay(wheel) { + const rayvector = castRay_rayvector; + const target = castRay_target; + this.updateWheelTransformWorld(wheel); + const chassisBody = this.chassisBody; + let depth = -1; + const raylen = wheel.suspensionRestLength + wheel.radius; + wheel.directionWorld.scale(raylen, rayvector); + const source = wheel.chassisConnectionPointWorld; + source.vadd(rayvector, target); + const raycastResult = wheel.raycastResult; + raycastResult.reset(); // Turn off ray collision with the chassis temporarily + + const oldState = chassisBody.collisionResponse; + chassisBody.collisionResponse = false; // Cast ray against world + + this.world.rayTest(source, target, raycastResult); + chassisBody.collisionResponse = oldState; + const object = raycastResult.body; + wheel.raycastResult.groundObject = 0; + + if (object) { + depth = raycastResult.distance; + wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld; + wheel.isInContact = true; + const hitDistance = raycastResult.distance; + wheel.suspensionLength = hitDistance - wheel.radius; // clamp on max suspension travel + + const minSuspensionLength = + wheel.suspensionRestLength - wheel.maxSuspensionTravel; + const maxSuspensionLength = + wheel.suspensionRestLength + wheel.maxSuspensionTravel; + + if (wheel.suspensionLength < minSuspensionLength) { + wheel.suspensionLength = minSuspensionLength; + } + + if (wheel.suspensionLength > maxSuspensionLength) { + wheel.suspensionLength = maxSuspensionLength; + wheel.raycastResult.reset(); + } + + const denominator = wheel.raycastResult.hitNormalWorld.dot( + wheel.directionWorld + ); + const chassis_velocity_at_contactPoint = new Vec3(); + chassisBody.getVelocityAtWorldPoint( + wheel.raycastResult.hitPointWorld, + chassis_velocity_at_contactPoint + ); + const projVel = wheel.raycastResult.hitNormalWorld.dot( + chassis_velocity_at_contactPoint + ); + + if (denominator >= -0.1) { + wheel.suspensionRelativeVelocity = 0; + wheel.clippedInvContactDotSuspension = 1 / 0.1; + } else { + const inv = -1 / denominator; + wheel.suspensionRelativeVelocity = projVel * inv; + wheel.clippedInvContactDotSuspension = inv; + } + } else { + //put wheel info as in rest position + wheel.suspensionLength = + wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel; + wheel.suspensionRelativeVelocity = 0.0; + wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld); + wheel.clippedInvContactDotSuspension = 1.0; + } + + return depth; + } + + updateWheelTransformWorld(wheel) { + wheel.isInContact = false; + const chassisBody = this.chassisBody; + chassisBody.pointToWorldFrame( + wheel.chassisConnectionPointLocal, + wheel.chassisConnectionPointWorld + ); + chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld); + chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld); + } + /** + * Update one of the wheel transform. + * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example. + * @param wheelIndex The wheel index to update. + */ + + updateWheelTransform(wheelIndex) { + const up = tmpVec4; + const right = tmpVec5; + const fwd = tmpVec6; + const wheel = this.wheelInfos[wheelIndex]; + this.updateWheelTransformWorld(wheel); + wheel.directionLocal.scale(-1, up); + right.copy(wheel.axleLocal); + up.cross(right, fwd); + fwd.normalize(); + right.normalize(); // Rotate around steering over the wheelAxle + + const steering = wheel.steering; + const steeringOrn = new Quaternion(); + steeringOrn.setFromAxisAngle(up, steering); + const rotatingOrn = new Quaternion(); + rotatingOrn.setFromAxisAngle(right, wheel.rotation); // World rotation of the wheel + + const q = wheel.worldTransform.quaternion; + this.chassisBody.quaternion.mult(steeringOrn, q); + q.mult(rotatingOrn, q); + q.normalize(); // world position of the wheel + + const p = wheel.worldTransform.position; + p.copy(wheel.directionWorld); + p.scale(wheel.suspensionLength, p); + p.vadd(wheel.chassisConnectionPointWorld, p); + } + /** + * Get the world transform of one of the wheels + */ + + getWheelTransformWorld(wheelIndex) { + return this.wheelInfos[wheelIndex].worldTransform; + } + + updateFriction(timeStep) { + const surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj; //calculate the impulse, so that the wheels don't move sidewards + + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + const chassisBody = this.chassisBody; + const forwardWS = updateFriction_forwardWS; + const axle = updateFriction_axle; + this.numWheelsOnGround = 0; + + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; + const groundObject = wheel.raycastResult.body; + + if (groundObject) { + this.numWheelsOnGround++; + } + + wheel.sideImpulse = 0; + wheel.forwardImpulse = 0; + + if (!forwardWS[i]) { + forwardWS[i] = new Vec3(); + } + + if (!axle[i]) { + axle[i] = new Vec3(); + } + } + + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; + const groundObject = wheel.raycastResult.body; + + if (groundObject) { + const axlei = axle[i]; + const wheelTrans = this.getWheelTransformWorld(i); // Get world axle + + wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei); + const surfNormalWS = wheel.raycastResult.hitNormalWorld; + const proj = axlei.dot(surfNormalWS); + surfNormalWS.scale(proj, surfNormalWS_scaled_proj); + axlei.vsub(surfNormalWS_scaled_proj, axlei); + axlei.normalize(); + surfNormalWS.cross(axlei, forwardWS[i]); + forwardWS[i].normalize(); + wheel.sideImpulse = resolveSingleBilateral( + chassisBody, + wheel.raycastResult.hitPointWorld, + groundObject, + wheel.raycastResult.hitPointWorld, + axlei + ); + wheel.sideImpulse *= sideFrictionStiffness2; + } + } + + const sideFactor = 1; + const fwdFactor = 0.5; + this.sliding = false; + + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; + const groundObject = wheel.raycastResult.body; + let rollingFriction = 0; + wheel.slipInfo = 1; + + if (groundObject) { + const defaultRollingFrictionImpulse = 0; + const maxImpulse = wheel.brake + ? wheel.brake + : defaultRollingFrictionImpulse; // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse); + // rollingFriction = calcRollingFriction(contactPt); + + rollingFriction = calcRollingFriction( + chassisBody, + groundObject, + wheel.raycastResult.hitPointWorld, + forwardWS[i], + maxImpulse + ); + rollingFriction += wheel.engineForce * timeStep; // rollingFriction = 0; + + const factor = maxImpulse / rollingFriction; + wheel.slipInfo *= factor; + } //switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break) + + wheel.forwardImpulse = 0; + wheel.skidInfo = 1; + + if (groundObject) { + wheel.skidInfo = 1; + const maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip; + const maximpSide = maximp; + const maximpSquared = maximp * maximpSide; + wheel.forwardImpulse = rollingFriction; //wheelInfo.engineForce* timeStep; + + const x = + (wheel.forwardImpulse * fwdFactor) / wheel.forwardAcceleration; + const y = (wheel.sideImpulse * sideFactor) / wheel.sideAcceleration; + const impulseSquared = x * x + y * y; + wheel.sliding = false; + + if (impulseSquared > maximpSquared) { + this.sliding = true; + wheel.sliding = true; + const factor = maximp / Math.sqrt(impulseSquared); + wheel.skidInfo *= factor; + } + } + } + + if (this.sliding) { + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; + + if (wheel.sideImpulse !== 0) { + if (wheel.skidInfo < 1) { + wheel.forwardImpulse *= wheel.skidInfo; + wheel.sideImpulse *= wheel.skidInfo; + } + } + } + } // apply the impulses + + for (let i = 0; i < numWheels; i++) { + const wheel = wheelInfos[i]; + const rel_pos = new Vec3(); + wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos); // cannons applyimpulse is using world coord for the position + //rel_pos.copy(wheel.raycastResult.hitPointWorld); + + if (wheel.forwardImpulse !== 0) { + const impulse = new Vec3(); + forwardWS[i].scale(wheel.forwardImpulse, impulse); + chassisBody.applyImpulse(impulse, rel_pos); + } + + if (wheel.sideImpulse !== 0) { + const groundObject = wheel.raycastResult.body; + const rel_pos2 = new Vec3(); + wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2); //rel_pos2.copy(wheel.raycastResult.hitPointWorld); + + const sideImp = new Vec3(); + axle[i].scale(wheel.sideImpulse, sideImp); // Scale the relative position in the up direction with rollInfluence. + // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over). + + chassisBody.vectorToLocalFrame(rel_pos, rel_pos); + rel_pos["xyz"[this.indexUpAxis]] *= wheel.rollInfluence; + chassisBody.vectorToWorldFrame(rel_pos, rel_pos); + chassisBody.applyImpulse(sideImp, rel_pos); //apply friction impulse on the ground + + sideImp.scale(-1, sideImp); + groundObject.applyImpulse(sideImp, rel_pos2); + } + } + } + } + new Vec3(); + new Vec3(); + new Vec3(); + const tmpVec4 = new Vec3(); + const tmpVec5 = new Vec3(); + const tmpVec6 = new Vec3(); + new Ray(); + new Vec3(); + const castRay_rayvector = new Vec3(); + const castRay_target = new Vec3(); + const directions = [new Vec3(1, 0, 0), new Vec3(0, 1, 0), new Vec3(0, 0, 1)]; + const updateFriction_surfNormalWS_scaled_proj = new Vec3(); + const updateFriction_axle = []; + const updateFriction_forwardWS = []; + const sideFrictionStiffness2 = 1; + const calcRollingFriction_vel1 = new Vec3(); + const calcRollingFriction_vel2 = new Vec3(); + const calcRollingFriction_vel = new Vec3(); + + function calcRollingFriction( + body0, + body1, + frictionPosWorld, + frictionDirectionWorld, + maxImpulse + ) { + let j1 = 0; + const contactPosWorld = frictionPosWorld; // const rel_pos1 = new Vec3(); + // const rel_pos2 = new Vec3(); + + const vel1 = calcRollingFriction_vel1; + const vel2 = calcRollingFriction_vel2; + const vel = calcRollingFriction_vel; // contactPosWorld.vsub(body0.position, rel_pos1); + // contactPosWorld.vsub(body1.position, rel_pos2); + + body0.getVelocityAtWorldPoint(contactPosWorld, vel1); + body1.getVelocityAtWorldPoint(contactPosWorld, vel2); + vel1.vsub(vel2, vel); + const vrel = frictionDirectionWorld.dot(vel); + const denom0 = computeImpulseDenominator( + body0, + frictionPosWorld, + frictionDirectionWorld + ); + const denom1 = computeImpulseDenominator( + body1, + frictionPosWorld, + frictionDirectionWorld + ); + const relaxation = 1; + const jacDiagABInv = relaxation / (denom0 + denom1); // calculate j that moves us to zero relative velocity + + j1 = -vrel * jacDiagABInv; + + if (maxImpulse < j1) { + j1 = maxImpulse; + } + + if (j1 < -maxImpulse) { + j1 = -maxImpulse; + } + + return j1; + } + + const computeImpulseDenominator_r0 = new Vec3(); + const computeImpulseDenominator_c0 = new Vec3(); + const computeImpulseDenominator_vec = new Vec3(); + const computeImpulseDenominator_m = new Vec3(); + + function computeImpulseDenominator(body, pos, normal) { + const r0 = computeImpulseDenominator_r0; + const c0 = computeImpulseDenominator_c0; + const vec = computeImpulseDenominator_vec; + const m = computeImpulseDenominator_m; + pos.vsub(body.position, r0); + r0.cross(normal, c0); + body.invInertiaWorld.vmult(c0, m); + m.cross(r0, vec); + return body.invMass + normal.dot(vec); + } + + const resolveSingleBilateral_vel1 = new Vec3(); + const resolveSingleBilateral_vel2 = new Vec3(); + const resolveSingleBilateral_vel = new Vec3(); // bilateral constraint between two dynamic objects + + function resolveSingleBilateral(body1, pos1, body2, pos2, normal) { + const normalLenSqr = normal.lengthSquared(); + + if (normalLenSqr > 1.1) { + return 0; // no impulse + } // const rel_pos1 = new Vec3(); + // const rel_pos2 = new Vec3(); + // pos1.vsub(body1.position, rel_pos1); + // pos2.vsub(body2.position, rel_pos2); + + const vel1 = resolveSingleBilateral_vel1; + const vel2 = resolveSingleBilateral_vel2; + const vel = resolveSingleBilateral_vel; + body1.getVelocityAtWorldPoint(pos1, vel1); + body2.getVelocityAtWorldPoint(pos2, vel2); + vel1.vsub(vel2, vel); + const rel_vel = normal.dot(vel); + const contactDamping = 0.2; + const massTerm = 1 / (body1.invMass + body2.invMass); + const impulse = -contactDamping * rel_vel * massTerm; + return impulse; + } + + /** + * Spherical shape + * @example + * const radius = 1 + * const sphereShape = new CANNON.Sphere(radius) + * const sphereBody = new CANNON.Body({ mass: 1, shape: sphereShape }) + * world.addBody(sphereBody) + */ + class Sphere extends Shape { + /** + * The radius of the sphere. + */ + + /** + * + * @param radius The radius of the sphere, a non-negative number. + */ + constructor(radius) { + super({ + type: Shape.types.SPHERE, + }); + this.radius = radius !== undefined ? radius : 1.0; + + if (this.radius < 0) { + throw new Error("The sphere radius cannot be negative."); + } + + this.updateBoundingSphereRadius(); + } + /** calculateLocalInertia */ + + calculateLocalInertia(mass, target) { + if (target === void 0) { + target = new Vec3(); + } + + const I = (2.0 * mass * this.radius * this.radius) / 5.0; + target.x = I; + target.y = I; + target.z = I; + return target; + } + /** volume */ + + volume() { + return (4.0 * Math.PI * Math.pow(this.radius, 3)) / 3.0; + } + + updateBoundingSphereRadius() { + this.boundingSphereRadius = this.radius; + } + + calculateWorldAABB(pos, quat, min, max) { + const r = this.radius; + const axes = ["x", "y", "z"]; + + for (let i = 0; i < axes.length; i++) { + const ax = axes[i]; + min[ax] = pos[ax] - r; + max[ax] = pos[ax] + r; + } + } + } + + /** + * Simple vehicle helper class with spherical rigid body wheels. + */ + class RigidVehicle { + /** + * The bodies of the wheels. + */ + + /** + * The chassis body. + */ + + /** + * The constraints. + */ + + /** + * The wheel axes. + */ + + /** + * The wheel forces. + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + this.wheelBodies = []; + this.coordinateSystem = + typeof options.coordinateSystem !== "undefined" + ? options.coordinateSystem.clone() + : new Vec3(1, 2, 3); + + if (options.chassisBody) { + this.chassisBody = options.chassisBody; + } else { + // No chassis body given. Create it! + this.chassisBody = new Body({ + mass: 1, + shape: new Box(new Vec3(5, 0.5, 2)), + }); + } + + this.constraints = []; + this.wheelAxes = []; + this.wheelForces = []; + } + /** + * Add a wheel + */ + + addWheel(options) { + if (options === void 0) { + options = {}; + } + + let wheelBody; + + if (options.body) { + wheelBody = options.body; + } else { + // No wheel body given. Create it! + wheelBody = new Body({ + mass: 1, + shape: new Sphere(1.2), + }); + } + + this.wheelBodies.push(wheelBody); + this.wheelForces.push(0); // Position constrain wheels + + const position = + typeof options.position !== "undefined" + ? options.position.clone() + : new Vec3(); // Set position locally to the chassis + + const worldPosition = new Vec3(); + this.chassisBody.pointToWorldFrame(position, worldPosition); + wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z); // Constrain wheel + + const axis = + typeof options.axis !== "undefined" + ? options.axis.clone() + : new Vec3(0, 0, 1); + this.wheelAxes.push(axis); + const hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, { + pivotA: position, + axisA: axis, + pivotB: Vec3.ZERO, + axisB: axis, + collideConnected: false, + }); + this.constraints.push(hingeConstraint); + return this.wheelBodies.length - 1; + } + /** + * Set the steering value of a wheel. + * @todo check coordinateSystem + */ + + setSteeringValue(value, wheelIndex) { + // Set angle of the hinge axis + const axis = this.wheelAxes[wheelIndex]; + const c = Math.cos(value); + const s = Math.sin(value); + const x = axis.x; + const z = axis.z; + this.constraints[wheelIndex].axisA.set(-c * x + s * z, 0, s * x + c * z); + } + /** + * Set the target rotational speed of the hinge constraint. + */ + + setMotorSpeed(value, wheelIndex) { + const hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.enableMotor(); + hingeConstraint.motorTargetVelocity = value; + } + /** + * Set the target rotational speed of the hinge constraint. + */ + + disableMotor(wheelIndex) { + const hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.disableMotor(); + } + /** + * Set the wheel force to apply on one of the wheels each time step + */ + + setWheelForce(value, wheelIndex) { + this.wheelForces[wheelIndex] = value; + } + /** + * Apply a torque on one of the wheels. + */ + + applyWheelForce(value, wheelIndex) { + const axis = this.wheelAxes[wheelIndex]; + const wheelBody = this.wheelBodies[wheelIndex]; + const bodyTorque = wheelBody.torque; + axis.scale(value, torque); + wheelBody.vectorToWorldFrame(torque, torque); + bodyTorque.vadd(torque, bodyTorque); + } + /** + * Add the vehicle including its constraints to the world. + */ + + addToWorld(world) { + const constraints = this.constraints; + const bodies = this.wheelBodies.concat([this.chassisBody]); + + for (let i = 0; i < bodies.length; i++) { + world.addBody(bodies[i]); + } + + for (let i = 0; i < constraints.length; i++) { + world.addConstraint(constraints[i]); + } + + world.addEventListener("preStep", this._update.bind(this)); + } + + _update() { + const wheelForces = this.wheelForces; + + for (let i = 0; i < wheelForces.length; i++) { + this.applyWheelForce(wheelForces[i], i); + } + } + /** + * Remove the vehicle including its constraints from the world. + */ + + removeFromWorld(world) { + const constraints = this.constraints; + const bodies = this.wheelBodies.concat([this.chassisBody]); + + for (let i = 0; i < bodies.length; i++) { + world.removeBody(bodies[i]); + } + + for (let i = 0; i < constraints.length; i++) { + world.removeConstraint(constraints[i]); + } + } + /** + * Get current rotational velocity of a wheel + */ + + getWheelSpeed(wheelIndex) { + const axis = this.wheelAxes[wheelIndex]; + const wheelBody = this.wheelBodies[wheelIndex]; + const w = wheelBody.angularVelocity; + this.chassisBody.vectorToWorldFrame(axis, worldAxis); + return w.dot(worldAxis); + } + } + const torque = new Vec3(); + const worldAxis = new Vec3(); + + /** + * Smoothed-particle hydrodynamics system + * @todo Make parameters customizable in the constructor + */ + class SPHSystem { + /** + * The particles array. + */ + + /** + * Density of the system (kg/m3). + * @default 1 + */ + + /** + * Distance below which two particles are considered to be neighbors. + * It should be adjusted so there are about 15-20 neighbor particles within this radius. + * @default 1 + */ + + /** + * @default 1 + */ + + /** + * Viscosity of the system. + * @default 0.01 + */ + + /** + * @default 0.000001 + */ + constructor() { + this.particles = []; + this.density = 1; + this.smoothingRadius = 1; + this.speedOfSound = 1; + this.viscosity = 0.01; + this.eps = 0.000001; // Stuff Computed per particle + + this.pressures = []; + this.densities = []; + this.neighbors = []; + } + /** + * Add a particle to the system. + */ + + add(particle) { + this.particles.push(particle); + + if (this.neighbors.length < this.particles.length) { + this.neighbors.push([]); + } + } + /** + * Remove a particle from the system. + */ + + remove(particle) { + const idx = this.particles.indexOf(particle); + + if (idx !== -1) { + this.particles.splice(idx, 1); + + if (this.neighbors.length > this.particles.length) { + this.neighbors.pop(); + } + } + } + /** + * Get neighbors within smoothing volume, save in the array neighbors + */ + + getNeighbors(particle, neighbors) { + const N = this.particles.length; + const id = particle.id; + const R2 = this.smoothingRadius * this.smoothingRadius; + const dist = SPHSystem_getNeighbors_dist; + + for (let i = 0; i !== N; i++) { + const p = this.particles[i]; + p.position.vsub(particle.position, dist); + + if (id !== p.id && dist.lengthSquared() < R2) { + neighbors.push(p); + } + } + } + + update() { + const N = this.particles.length; + const dist = SPHSystem_update_dist; + const cs = this.speedOfSound; + const eps = this.eps; + + for (let i = 0; i !== N; i++) { + const p = this.particles[i]; // Current particle + + const neighbors = this.neighbors[i]; // Get neighbors + + neighbors.length = 0; + this.getNeighbors(p, neighbors); + neighbors.push(this.particles[i]); // Add current too + + const numNeighbors = neighbors.length; // Accumulate density for the particle + + let sum = 0.0; + + for (let j = 0; j !== numNeighbors; j++) { + //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); + p.position.vsub(neighbors[j].position, dist); + const len = dist.length(); + const weight = this.w(len); + sum += neighbors[j].mass * weight; + } // Save + + this.densities[i] = sum; + this.pressures[i] = cs * cs * (this.densities[i] - this.density); + } // Add forces + // Sum to these accelerations + + const a_pressure = SPHSystem_update_a_pressure; + const a_visc = SPHSystem_update_a_visc; + const gradW = SPHSystem_update_gradW; + const r_vec = SPHSystem_update_r_vec; + const u = SPHSystem_update_u; + + for (let i = 0; i !== N; i++) { + const particle = this.particles[i]; + a_pressure.set(0, 0, 0); + a_visc.set(0, 0, 0); // Init vars + + let Pij; + let nabla; + + const neighbors = this.neighbors[i]; + const numNeighbors = neighbors.length; //printf("Neighbors: "); + + for (let j = 0; j !== numNeighbors; j++) { + const neighbor = neighbors[j]; //printf("%d ",nj); + // Get r once for all.. + + particle.position.vsub(neighbor.position, r_vec); + const r = r_vec.length(); // Pressure contribution + + Pij = + -neighbor.mass * + (this.pressures[i] / (this.densities[i] * this.densities[i] + eps) + + this.pressures[j] / (this.densities[j] * this.densities[j] + eps)); + this.gradw(r_vec, gradW); // Add to pressure acceleration + + gradW.scale(Pij, gradW); + a_pressure.vadd(gradW, a_pressure); // Viscosity contribution + + neighbor.velocity.vsub(particle.velocity, u); + u.scale( + (1.0 / (0.0001 + this.densities[i] * this.densities[j])) * + this.viscosity * + neighbor.mass, + u + ); + nabla = this.nablaw(r); + u.scale(nabla, u); // Add to viscosity acceleration + + a_visc.vadd(u, a_visc); + } // Calculate force + + a_visc.scale(particle.mass, a_visc); + a_pressure.scale(particle.mass, a_pressure); // Add force to particles + + particle.force.vadd(a_visc, particle.force); + particle.force.vadd(a_pressure, particle.force); + } + } // Calculate the weight using the W(r) weightfunction + + w(r) { + // 315 + const h = this.smoothingRadius; + return (315.0 / (64.0 * Math.PI * h ** 9)) * (h * h - r * r) ** 3; + } // calculate gradient of the weight function + + gradw(rVec, resultVec) { + const r = rVec.length(); + const h = this.smoothingRadius; + rVec.scale( + (945.0 / (32.0 * Math.PI * h ** 9)) * (h * h - r * r) ** 2, + resultVec + ); + } // Calculate nabla(W) + + nablaw(r) { + const h = this.smoothingRadius; + const nabla = + (945.0 / (32.0 * Math.PI * h ** 9)) * + (h * h - r * r) * + (7 * r * r - 3 * h * h); + return nabla; + } + } + const SPHSystem_getNeighbors_dist = new Vec3(); // Temp vectors for calculation + + const SPHSystem_update_dist = new Vec3(); // Relative velocity + + const SPHSystem_update_a_pressure = new Vec3(); + const SPHSystem_update_a_visc = new Vec3(); + const SPHSystem_update_gradW = new Vec3(); + const SPHSystem_update_r_vec = new Vec3(); + const SPHSystem_update_u = new Vec3(); + + /** + * Cylinder class. + * @example + * const radiusTop = 0.5 + * const radiusBottom = 0.5 + * const height = 2 + * const numSegments = 12 + * const cylinderShape = new CANNON.Cylinder(radiusTop, radiusBottom, height, numSegments) + * const cylinderBody = new CANNON.Body({ mass: 1, shape: cylinderShape }) + * world.addBody(cylinderBody) + */ + + class Cylinder extends ConvexPolyhedron { + /** The radius of the top of the Cylinder. */ + + /** The radius of the bottom of the Cylinder. */ + + /** The height of the Cylinder. */ + + /** The number of segments to build the cylinder out of. */ + + /** + * @param radiusTop The radius of the top of the Cylinder. + * @param radiusBottom The radius of the bottom of the Cylinder. + * @param height The height of the Cylinder. + * @param numSegments The number of segments to build the cylinder out of. + */ + constructor(radiusTop, radiusBottom, height, numSegments) { + if (radiusTop === void 0) { + radiusTop = 1; + } + + if (radiusBottom === void 0) { + radiusBottom = 1; + } + + if (height === void 0) { + height = 1; + } + + if (numSegments === void 0) { + numSegments = 8; + } + + if (radiusTop < 0) { + throw new Error("The cylinder radiusTop cannot be negative."); + } + + if (radiusBottom < 0) { + throw new Error("The cylinder radiusBottom cannot be negative."); + } + + const N = numSegments; + const vertices = []; + const axes = []; + const faces = []; + const bottomface = []; + const topface = []; + const cos = Math.cos; + const sin = Math.sin; // First bottom point + + vertices.push( + new Vec3(-radiusBottom * sin(0), -height * 0.5, radiusBottom * cos(0)) + ); + bottomface.push(0); // First top point + + vertices.push( + new Vec3(-radiusTop * sin(0), height * 0.5, radiusTop * cos(0)) + ); + topface.push(1); + + for (let i = 0; i < N; i++) { + const theta = ((2 * Math.PI) / N) * (i + 1); + const thetaN = ((2 * Math.PI) / N) * (i + 0.5); + + if (i < N - 1) { + // Bottom + vertices.push( + new Vec3( + -radiusBottom * sin(theta), + -height * 0.5, + radiusBottom * cos(theta) + ) + ); + bottomface.push(2 * i + 2); // Top + + vertices.push( + new Vec3( + -radiusTop * sin(theta), + height * 0.5, + radiusTop * cos(theta) + ) + ); + topface.push(2 * i + 3); // Face + + faces.push([2 * i, 2 * i + 1, 2 * i + 3, 2 * i + 2]); + } else { + faces.push([2 * i, 2 * i + 1, 1, 0]); // Connect + } // Axis: we can cut off half of them if we have even number of segments + + if (N % 2 === 1 || i < N / 2) { + axes.push(new Vec3(-sin(thetaN), 0, cos(thetaN))); + } + } + + faces.push(bottomface); + axes.push(new Vec3(0, 1, 0)); // Reorder top face + + const temp = []; + + for (let i = 0; i < topface.length; i++) { + temp.push(topface[topface.length - i - 1]); + } + + faces.push(temp); + super({ + vertices, + faces, + axes, + }); + this.type = Shape.types.CYLINDER; + this.radiusTop = radiusTop; + this.radiusBottom = radiusBottom; + this.height = height; + this.numSegments = numSegments; + } + } + + /** + * Particle shape. + * @example + * const particleShape = new CANNON.Particle() + * const particleBody = new CANNON.Body({ mass: 1, shape: particleShape }) + * world.addBody(particleBody) + */ + class Particle extends Shape { + constructor() { + super({ + type: Shape.types.PARTICLE, + }); + } + /** + * calculateLocalInertia + */ + + calculateLocalInertia(mass, target) { + if (target === void 0) { + target = new Vec3(); + } + + target.set(0, 0, 0); + return target; + } + + volume() { + return 0; + } + + updateBoundingSphereRadius() { + this.boundingSphereRadius = 0; + } + + calculateWorldAABB(pos, quat, min, max) { + // Get each axis max + min.copy(pos); + max.copy(pos); + } + } + + /** + * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a Body and rotate that body. See the demos. + * @example + * const planeShape = new CANNON.Plane() + * const planeBody = new CANNON.Body({ mass: 0, shape: planeShape }) + * planeBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) // make it face up + * world.addBody(planeBody) + */ + class Plane extends Shape { + /** worldNormal */ + + /** worldNormalNeedsUpdate */ + constructor() { + super({ + type: Shape.types.PLANE, + }); // World oriented normal + + this.worldNormal = new Vec3(); + this.worldNormalNeedsUpdate = true; + this.boundingSphereRadius = Number.MAX_VALUE; + } + /** computeWorldNormal */ + + computeWorldNormal(quat) { + const n = this.worldNormal; + n.set(0, 0, 1); + quat.vmult(n, n); + this.worldNormalNeedsUpdate = false; + } + + calculateLocalInertia(mass, target) { + if (target === void 0) { + target = new Vec3(); + } + + return target; + } + + volume() { + return ( + // The plane is infinite... + Number.MAX_VALUE + ); + } + + calculateWorldAABB(pos, quat, min, max) { + // The plane AABB is infinite, except if the normal is pointing along any axis + tempNormal.set(0, 0, 1); // Default plane normal is z + + quat.vmult(tempNormal, tempNormal); + const maxVal = Number.MAX_VALUE; + min.set(-maxVal, -maxVal, -maxVal); + max.set(maxVal, maxVal, maxVal); + + if (tempNormal.x === 1) { + max.x = pos.x; + } else if (tempNormal.x === -1) { + min.x = pos.x; + } + + if (tempNormal.y === 1) { + max.y = pos.y; + } else if (tempNormal.y === -1) { + min.y = pos.y; + } + + if (tempNormal.z === 1) { + max.z = pos.z; + } else if (tempNormal.z === -1) { + min.z = pos.z; + } + } + + updateBoundingSphereRadius() { + this.boundingSphereRadius = Number.MAX_VALUE; + } + } + const tempNormal = new Vec3(); + + /** + * Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a given distance. + * @todo Should be possible to use along all axes, not just y + * @todo should be possible to scale along all axes + * @todo Refactor elementSize to elementSizeX and elementSizeY + * + * @example + * // Generate some height data (y-values). + * const data = [] + * for (let i = 0; i < 1000; i++) { + * const y = 0.5 * Math.cos(0.2 * i) + * data.push(y) + * } + * + * // Create the heightfield shape + * const heightfieldShape = new CANNON.Heightfield(data, { + * elementSize: 1 // Distance between the data points in X and Y directions + * }) + * const heightfieldBody = new CANNON.Body({ shape: heightfieldShape }) + * world.addBody(heightfieldBody) + */ + class Heightfield extends Shape { + /** + * An array of numbers, or height values, that are spread out along the x axis. + */ + + /** + * Max value of the data points in the data array. + */ + + /** + * Minimum value of the data points in the data array. + */ + + /** + * World spacing between the data points in X and Y direction. + * @todo elementSizeX and Y + * @default 1 + */ + + /** + * @default true + */ + + /** + * @param data An array of numbers, or height values, that are spread out along the x axis. + */ + constructor(data, options) { + if (options === void 0) { + options = {}; + } + + options = Utils.defaults(options, { + maxValue: null, + minValue: null, + elementSize: 1, + }); + super({ + type: Shape.types.HEIGHTFIELD, + }); + this.data = data; + this.maxValue = options.maxValue; + this.minValue = options.minValue; + this.elementSize = options.elementSize; + + if (options.minValue === null) { + this.updateMinValue(); + } + + if (options.maxValue === null) { + this.updateMaxValue(); + } + + this.cacheEnabled = true; + this.pillarConvex = new ConvexPolyhedron(); + this.pillarOffset = new Vec3(); + this.updateBoundingSphereRadius(); // "i_j_isUpper" => { convex: ..., offset: ... } + // for example: + // _cachedPillars["0_2_1"] + + this._cachedPillars = {}; + } + /** + * Call whenever you change the data array. + */ + + update() { + this._cachedPillars = {}; + } + /** + * Update the `minValue` property + */ + + updateMinValue() { + const data = this.data; + let minValue = data[0][0]; + + for (let i = 0; i !== data.length; i++) { + for (let j = 0; j !== data[i].length; j++) { + const v = data[i][j]; + + if (v < minValue) { + minValue = v; + } + } + } + + this.minValue = minValue; + } + /** + * Update the `maxValue` property + */ + + updateMaxValue() { + const data = this.data; + let maxValue = data[0][0]; + + for (let i = 0; i !== data.length; i++) { + for (let j = 0; j !== data[i].length; j++) { + const v = data[i][j]; + + if (v > maxValue) { + maxValue = v; + } + } + } + + this.maxValue = maxValue; + } + /** + * Set the height value at an index. Don't forget to update maxValue and minValue after you're done. + */ + + setHeightValueAtIndex(xi, yi, value) { + const data = this.data; + data[xi][yi] = value; // Invalidate cache + + this.clearCachedConvexTrianglePillar(xi, yi, false); + + if (xi > 0) { + this.clearCachedConvexTrianglePillar(xi - 1, yi, true); + this.clearCachedConvexTrianglePillar(xi - 1, yi, false); + } + + if (yi > 0) { + this.clearCachedConvexTrianglePillar(xi, yi - 1, true); + this.clearCachedConvexTrianglePillar(xi, yi - 1, false); + } + + if (yi > 0 && xi > 0) { + this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true); + } + } + /** + * Get max/min in a rectangle in the matrix data + * @param result An array to store the results in. + * @return The result array, if it was passed in. Minimum will be at position 0 and max at 1. + */ + + getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, result) { + if (result === void 0) { + result = []; + } + + // Get max and min of the data + const data = this.data; // Set first value + + let max = this.minValue; + + for (let i = iMinX; i <= iMaxX; i++) { + for (let j = iMinY; j <= iMaxY; j++) { + const height = data[i][j]; + + if (height > max) { + max = height; + } + } + } + + result[0] = this.minValue; + result[1] = max; + } + /** + * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1. + * @param result Two-element array + * @param clamp If the position should be clamped to the heightfield edge. + */ + + getIndexOfPosition(x, y, result, clamp) { + // Get the index of the data points to test against + const w = this.elementSize; + const data = this.data; + let xi = Math.floor(x / w); + let yi = Math.floor(y / w); + result[0] = xi; + result[1] = yi; + + if (clamp) { + // Clamp index to edges + if (xi < 0) { + xi = 0; + } + + if (yi < 0) { + yi = 0; + } + + if (xi >= data.length - 1) { + xi = data.length - 1; + } + + if (yi >= data[0].length - 1) { + yi = data[0].length - 1; + } + } // Bail out if we are out of the terrain + + if (xi < 0 || yi < 0 || xi >= data.length - 1 || yi >= data[0].length - 1) { + return false; + } + + return true; + } + + getTriangleAt(x, y, edgeClamp, a, b, c) { + const idx = getHeightAt_idx; + this.getIndexOfPosition(x, y, idx, edgeClamp); + let xi = idx[0]; + let yi = idx[1]; + const data = this.data; + + if (edgeClamp) { + xi = Math.min(data.length - 2, Math.max(0, xi)); + yi = Math.min(data[0].length - 2, Math.max(0, yi)); + } + + const elementSize = this.elementSize; + const lowerDist2 = + (x / elementSize - xi) ** 2 + (y / elementSize - yi) ** 2; + const upperDist2 = + (x / elementSize - (xi + 1)) ** 2 + (y / elementSize - (yi + 1)) ** 2; + const upper = lowerDist2 > upperDist2; + this.getTriangle(xi, yi, upper, a, b, c); + return upper; + } + + getNormalAt(x, y, edgeClamp, result) { + const a = getNormalAt_a; + const b = getNormalAt_b; + const c = getNormalAt_c; + const e0 = getNormalAt_e0; + const e1 = getNormalAt_e1; + this.getTriangleAt(x, y, edgeClamp, a, b, c); + b.vsub(a, e0); + c.vsub(a, e1); + e0.cross(e1, result); + result.normalize(); + } + /** + * Get an AABB of a square in the heightfield + * @param xi + * @param yi + * @param result + */ + + getAabbAtIndex(xi, yi, _ref) { + let { lowerBound, upperBound } = _ref; + const data = this.data; + const elementSize = this.elementSize; + lowerBound.set(xi * elementSize, yi * elementSize, data[xi][yi]); + upperBound.set( + (xi + 1) * elementSize, + (yi + 1) * elementSize, + data[xi + 1][yi + 1] + ); + } + /** + * Get the height in the heightfield at a given position + */ + + getHeightAt(x, y, edgeClamp) { + const data = this.data; + const a = getHeightAt_a; + const b = getHeightAt_b; + const c = getHeightAt_c; + const idx = getHeightAt_idx; + this.getIndexOfPosition(x, y, idx, edgeClamp); + let xi = idx[0]; + let yi = idx[1]; + + if (edgeClamp) { + xi = Math.min(data.length - 2, Math.max(0, xi)); + yi = Math.min(data[0].length - 2, Math.max(0, yi)); + } + + const upper = this.getTriangleAt(x, y, edgeClamp, a, b, c); + barycentricWeights(x, y, a.x, a.y, b.x, b.y, c.x, c.y, getHeightAt_weights); + const w = getHeightAt_weights; + + if (upper) { + // Top triangle verts + return ( + data[xi + 1][yi + 1] * w.x + + data[xi][yi + 1] * w.y + + data[xi + 1][yi] * w.z + ); + } else { + // Top triangle verts + return ( + data[xi][yi] * w.x + data[xi + 1][yi] * w.y + data[xi][yi + 1] * w.z + ); + } + } + + getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle) { + return `${xi}_${yi}_${getUpperTriangle ? 1 : 0}`; + } + + getCachedConvexTrianglePillar(xi, yi, getUpperTriangle) { + return this._cachedPillars[ + this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle) + ]; + } + + setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, convex, offset) { + this._cachedPillars[ + this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle) + ] = { + convex, + offset, + }; + } + + clearCachedConvexTrianglePillar(xi, yi, getUpperTriangle) { + delete this._cachedPillars[ + this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle) + ]; + } + /** + * Get a triangle from the heightfield + */ + + getTriangle(xi, yi, upper, a, b, c) { + const data = this.data; + const elementSize = this.elementSize; + + if (upper) { + // Top triangle verts + a.set( + (xi + 1) * elementSize, + (yi + 1) * elementSize, + data[xi + 1][yi + 1] + ); + b.set(xi * elementSize, (yi + 1) * elementSize, data[xi][yi + 1]); + c.set((xi + 1) * elementSize, yi * elementSize, data[xi + 1][yi]); + } else { + // Top triangle verts + a.set(xi * elementSize, yi * elementSize, data[xi][yi]); + b.set((xi + 1) * elementSize, yi * elementSize, data[xi + 1][yi]); + c.set(xi * elementSize, (yi + 1) * elementSize, data[xi][yi + 1]); + } + } + /** + * Get a triangle in the terrain in the form of a triangular convex shape. + */ + + getConvexTrianglePillar(xi, yi, getUpperTriangle) { + let result = this.pillarConvex; + let offsetResult = this.pillarOffset; + + if (this.cacheEnabled) { + const data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle); + + if (data) { + this.pillarConvex = data.convex; + this.pillarOffset = data.offset; + return; + } + + result = new ConvexPolyhedron(); + offsetResult = new Vec3(); + this.pillarConvex = result; + this.pillarOffset = offsetResult; + } + + const data = this.data; + const elementSize = this.elementSize; + const faces = result.faces; // Reuse verts if possible + + result.vertices.length = 6; + + for (let i = 0; i < 6; i++) { + if (!result.vertices[i]) { + result.vertices[i] = new Vec3(); + } + } // Reuse faces if possible + + faces.length = 5; + + for (let i = 0; i < 5; i++) { + if (!faces[i]) { + faces[i] = []; + } + } + + const verts = result.vertices; + const h = + (Math.min( + data[xi][yi], + data[xi + 1][yi], + data[xi][yi + 1], + data[xi + 1][yi + 1] + ) - + this.minValue) / + 2 + + this.minValue; + + if (!getUpperTriangle) { + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.25) * elementSize, // sort of center of a triangle + (yi + 0.25) * elementSize, + h // vertical center + ); // Top triangle verts + + verts[0].set(-0.25 * elementSize, -0.25 * elementSize, data[xi][yi] - h); + verts[1].set( + 0.75 * elementSize, + -0.25 * elementSize, + data[xi + 1][yi] - h + ); + verts[2].set( + -0.25 * elementSize, + 0.75 * elementSize, + data[xi][yi + 1] - h + ); // bottom triangle verts + + verts[3].set(-0.25 * elementSize, -0.25 * elementSize, -Math.abs(h) - 1); + verts[4].set(0.75 * elementSize, -0.25 * elementSize, -Math.abs(h) - 1); + verts[5].set(-0.25 * elementSize, 0.75 * elementSize, -Math.abs(h) - 1); // top triangle + + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; // bottom triangle + + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; // -x facing quad + + faces[2][0] = 0; + faces[2][1] = 2; + faces[2][2] = 5; + faces[2][3] = 3; // -y facing quad + + faces[3][0] = 1; + faces[3][1] = 0; + faces[3][2] = 3; + faces[3][3] = 4; // +xy facing quad + + faces[4][0] = 4; + faces[4][1] = 5; + faces[4][2] = 2; + faces[4][3] = 1; + } else { + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.75) * elementSize, // sort of center of a triangle + (yi + 0.75) * elementSize, + h // vertical center + ); // Top triangle verts + + verts[0].set( + 0.25 * elementSize, + 0.25 * elementSize, + data[xi + 1][yi + 1] - h + ); + verts[1].set( + -0.75 * elementSize, + 0.25 * elementSize, + data[xi][yi + 1] - h + ); + verts[2].set( + 0.25 * elementSize, + -0.75 * elementSize, + data[xi + 1][yi] - h + ); // bottom triangle verts + + verts[3].set(0.25 * elementSize, 0.25 * elementSize, -Math.abs(h) - 1); + verts[4].set(-0.75 * elementSize, 0.25 * elementSize, -Math.abs(h) - 1); + verts[5].set(0.25 * elementSize, -0.75 * elementSize, -Math.abs(h) - 1); // Top triangle + + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; // bottom triangle + + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; // +x facing quad + + faces[2][0] = 2; + faces[2][1] = 5; + faces[2][2] = 3; + faces[2][3] = 0; // +y facing quad + + faces[3][0] = 3; + faces[3][1] = 4; + faces[3][2] = 1; + faces[3][3] = 0; // -xy facing quad + + faces[4][0] = 1; + faces[4][1] = 4; + faces[4][2] = 5; + faces[4][3] = 2; + } + + result.computeNormals(); + result.computeEdges(); + result.updateBoundingSphereRadius(); + this.setCachedConvexTrianglePillar( + xi, + yi, + getUpperTriangle, + result, + offsetResult + ); + } + + calculateLocalInertia(mass, target) { + if (target === void 0) { + target = new Vec3(); + } + + target.set(0, 0, 0); + return target; + } + + volume() { + return ( + // The terrain is infinite + Number.MAX_VALUE + ); + } + + calculateWorldAABB(pos, quat, min, max) { + /** @TODO do it properly */ + min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + } + + updateBoundingSphereRadius() { + // Use the bounding box of the min/max values + const data = this.data; + const s = this.elementSize; + this.boundingSphereRadius = new Vec3( + data.length * s, + data[0].length * s, + Math.max(Math.abs(this.maxValue), Math.abs(this.minValue)) + ).length(); + } + /** + * Sets the height values from an image. Currently only supported in browser. + */ + + setHeightsFromImage(image, scale) { + const { x, z, y } = scale; + const canvas = document.createElement("canvas"); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext("2d"); + context.drawImage(image, 0, 0); + const imageData = context.getImageData(0, 0, image.width, image.height); + const matrix = this.data; + matrix.length = 0; + this.elementSize = Math.abs(x) / imageData.width; + + for (let i = 0; i < imageData.height; i++) { + const row = []; + + for (let j = 0; j < imageData.width; j++) { + const a = imageData.data[(i * imageData.height + j) * 4]; + const b = imageData.data[(i * imageData.height + j) * 4 + 1]; + const c = imageData.data[(i * imageData.height + j) * 4 + 2]; + const height = ((a + b + c) / 4 / 255) * z; + + if (x < 0) { + row.push(height); + } else { + row.unshift(height); + } + } + + if (y < 0) { + matrix.unshift(row); + } else { + matrix.push(row); + } + } + + this.updateMaxValue(); + this.updateMinValue(); + this.update(); + } + } + const getHeightAt_idx = []; + const getHeightAt_weights = new Vec3(); + const getHeightAt_a = new Vec3(); + const getHeightAt_b = new Vec3(); + const getHeightAt_c = new Vec3(); + const getNormalAt_a = new Vec3(); + const getNormalAt_b = new Vec3(); + const getNormalAt_c = new Vec3(); + const getNormalAt_e0 = new Vec3(); + const getNormalAt_e1 = new Vec3(); // from https://en.wikipedia.org/wiki/Barycentric_coordinate_system + + function barycentricWeights(x, y, ax, ay, bx, by, cx, cy, result) { + result.x = + ((by - cy) * (x - cx) + (cx - bx) * (y - cy)) / + ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); + result.y = + ((cy - ay) * (x - cx) + (ax - cx) * (y - cy)) / + ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); + result.z = 1 - result.x - result.y; + } + + /** + * OctreeNode + */ + class OctreeNode { + /** The root node */ + + /** Boundary of this node */ + + /** Contained data at the current node level */ + + /** Children to this node */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + this.root = options.root || null; + this.aabb = options.aabb ? options.aabb.clone() : new AABB(); + this.data = []; + this.children = []; + } + /** + * reset + */ + + reset() { + this.children.length = this.data.length = 0; + } + /** + * Insert data into this node + * @return True if successful, otherwise false + */ + + insert(aabb, elementData, level) { + if (level === void 0) { + level = 0; + } + + const nodeData = this.data; // Ignore objects that do not belong in this node + + if (!this.aabb.contains(aabb)) { + return false; // object cannot be added + } + + const children = this.children; + const maxDepth = this.maxDepth || this.root.maxDepth; + + if (level < maxDepth) { + // Subdivide if there are no children yet + let subdivided = false; + + if (!children.length) { + this.subdivide(); + subdivided = true; + } // add to whichever node will accept it + + for (let i = 0; i !== 8; i++) { + if (children[i].insert(aabb, elementData, level + 1)) { + return true; + } + } + + if (subdivided) { + // No children accepted! Might as well just remove em since they contain none + children.length = 0; + } + } // Too deep, or children didnt want it. add it in current node + + nodeData.push(elementData); + return true; + } + /** + * Create 8 equally sized children nodes and put them in the `children` array. + */ + + subdivide() { + const aabb = this.aabb; + const l = aabb.lowerBound; + const u = aabb.upperBound; + const children = this.children; + children.push( + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(0, 0, 0), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(1, 0, 0), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(1, 1, 0), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(1, 1, 1), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(0, 1, 1), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(0, 0, 1), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(1, 0, 1), + }), + }), + new OctreeNode({ + aabb: new AABB({ + lowerBound: new Vec3(0, 1, 0), + }), + }) + ); + u.vsub(l, halfDiagonal); + halfDiagonal.scale(0.5, halfDiagonal); + const root = this.root || this; + + for (let i = 0; i !== 8; i++) { + const child = children[i]; // Set current node as root + + child.root = root; // Compute bounds + + const lowerBound = child.aabb.lowerBound; + lowerBound.x *= halfDiagonal.x; + lowerBound.y *= halfDiagonal.y; + lowerBound.z *= halfDiagonal.z; + lowerBound.vadd(l, lowerBound); // Upper bound is always lower bound + halfDiagonal + + lowerBound.vadd(halfDiagonal, child.aabb.upperBound); + } + } + /** + * Get all data, potentially within an AABB + * @return The "result" object + */ + + aabbQuery(aabb, result) { + this.data; // abort if the range does not intersect this node + // if (!this.aabb.overlaps(aabb)){ + // return result; + // } + // Add objects at this level + // Array.prototype.push.apply(result, nodeData); + // Add child data + // @todo unwrap recursion into a queue / loop, that's faster in JS + + this.children; // for (let i = 0, N = this.children.length; i !== N; i++) { + // children[i].aabbQuery(aabb, result); + // } + + const queue = [this]; + + while (queue.length) { + const node = queue.pop(); + + if (node.aabb.overlaps(aabb)) { + Array.prototype.push.apply(result, node.data); + } + + Array.prototype.push.apply(queue, node.children); + } + + return result; + } + /** + * Get all data, potentially intersected by a ray. + * @return The "result" object + */ + + rayQuery(ray, treeTransform, result) { + // Use aabb query for now. + + /** @todo implement real ray query which needs less lookups */ + ray.getAABB(tmpAABB); + tmpAABB.toLocalFrame(treeTransform, tmpAABB); + this.aabbQuery(tmpAABB, result); + return result; + } + /** + * removeEmptyNodes + */ + + removeEmptyNodes() { + for (let i = this.children.length - 1; i >= 0; i--) { + this.children[i].removeEmptyNodes(); + + if (!this.children[i].children.length && !this.children[i].data.length) { + this.children.splice(i, 1); + } + } + } + } + /** + * Octree + */ + + class Octree extends OctreeNode { + /** + * Maximum subdivision depth + * @default 8 + */ + + /** + * @param aabb The total AABB of the tree + */ + constructor(aabb, options) { + if (options === void 0) { + options = {}; + } + + super({ + root: null, + aabb, + }); + this.maxDepth = + typeof options.maxDepth !== "undefined" ? options.maxDepth : 8; + } + } + const halfDiagonal = new Vec3(); + const tmpAABB = new AABB(); + + /** + * Trimesh. + * @example + * // How to make a mesh with a single triangle + * const vertices = [ + * 0, 0, 0, // vertex 0 + * 1, 0, 0, // vertex 1 + * 0, 1, 0 // vertex 2 + * ] + * const indices = [ + * 0, 1, 2 // triangle 0 + * ] + * const trimeshShape = new CANNON.Trimesh(vertices, indices) + */ + class Trimesh extends Shape { + /** + * vertices + */ + + /** + * Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles. + */ + + /** + * The normals data. + */ + + /** + * The local AABB of the mesh. + */ + + /** + * References to vertex pairs, making up all unique edges in the trimesh. + */ + + /** + * Local scaling of the mesh. Use .setScale() to set it. + */ + + /** + * The indexed triangles. Use .updateTree() to update it. + */ + constructor(vertices, indices) { + super({ + type: Shape.types.TRIMESH, + }); + this.vertices = new Float32Array(vertices); + this.indices = new Int16Array(indices); + this.normals = new Float32Array(indices.length); + this.aabb = new AABB(); + this.edges = null; + this.scale = new Vec3(1, 1, 1); + this.tree = new Octree(); + this.updateEdges(); + this.updateNormals(); + this.updateAABB(); + this.updateBoundingSphereRadius(); + this.updateTree(); + } + /** + * updateTree + */ + + updateTree() { + const tree = this.tree; + tree.reset(); + tree.aabb.copy(this.aabb); + const scale = this.scale; // The local mesh AABB is scaled, but the octree AABB should be unscaled + + tree.aabb.lowerBound.x *= 1 / scale.x; + tree.aabb.lowerBound.y *= 1 / scale.y; + tree.aabb.lowerBound.z *= 1 / scale.z; + tree.aabb.upperBound.x *= 1 / scale.x; + tree.aabb.upperBound.y *= 1 / scale.y; + tree.aabb.upperBound.z *= 1 / scale.z; // Insert all triangles + + const triangleAABB = new AABB(); + const a = new Vec3(); + const b = new Vec3(); + const c = new Vec3(); + const points = [a, b, c]; + + for (let i = 0; i < this.indices.length / 3; i++) { + //this.getTriangleVertices(i, a, b, c); + // Get unscaled triangle verts + const i3 = i * 3; + + this._getUnscaledVertex(this.indices[i3], a); + + this._getUnscaledVertex(this.indices[i3 + 1], b); + + this._getUnscaledVertex(this.indices[i3 + 2], c); + + triangleAABB.setFromPoints(points); + tree.insert(triangleAABB, i); + } + + tree.removeEmptyNodes(); + } + /** + * Get triangles in a local AABB from the trimesh. + * @param result An array of integers, referencing the queried triangles. + */ + + getTrianglesInAABB(aabb, result) { + unscaledAABB.copy(aabb); // Scale it to local + + const scale = this.scale; + const isx = scale.x; + const isy = scale.y; + const isz = scale.z; + const l = unscaledAABB.lowerBound; + const u = unscaledAABB.upperBound; + l.x /= isx; + l.y /= isy; + l.z /= isz; + u.x /= isx; + u.y /= isy; + u.z /= isz; + return this.tree.aabbQuery(unscaledAABB, result); + } + /** + * setScale + */ + + setScale(scale) { + const wasUniform = + this.scale.x === this.scale.y && this.scale.y === this.scale.z; + const isUniform = scale.x === scale.y && scale.y === scale.z; + + if (!(wasUniform && isUniform)) { + // Non-uniform scaling. Need to update normals. + this.updateNormals(); + } + + this.scale.copy(scale); + this.updateAABB(); + this.updateBoundingSphereRadius(); + } + /** + * Compute the normals of the faces. Will save in the `.normals` array. + */ + + updateNormals() { + const n = computeNormals_n; // Generate normals + + const normals = this.normals; + + for (let i = 0; i < this.indices.length / 3; i++) { + const i3 = i * 3; + const a = this.indices[i3]; + const b = this.indices[i3 + 1]; + const c = this.indices[i3 + 2]; + this.getVertex(a, va); + this.getVertex(b, vb); + this.getVertex(c, vc); + Trimesh.computeNormal(vb, va, vc, n); + normals[i3] = n.x; + normals[i3 + 1] = n.y; + normals[i3 + 2] = n.z; + } + } + /** + * Update the `.edges` property + */ + + updateEdges() { + const edges = {}; + + const add = (a, b) => { + const key = a < b ? `${a}_${b}` : `${b}_${a}`; + edges[key] = true; + }; + + for (let i = 0; i < this.indices.length / 3; i++) { + const i3 = i * 3; + const a = this.indices[i3]; + const b = this.indices[i3 + 1]; + const c = this.indices[i3 + 2]; + add(a, b); + add(b, c); + add(c, a); + } + + const keys = Object.keys(edges); + this.edges = new Int16Array(keys.length * 2); + + for (let i = 0; i < keys.length; i++) { + const indices = keys[i].split("_"); + this.edges[2 * i] = parseInt(indices[0], 10); + this.edges[2 * i + 1] = parseInt(indices[1], 10); + } + } + /** + * Get an edge vertex + * @param firstOrSecond 0 or 1, depending on which one of the vertices you need. + * @param vertexStore Where to store the result + */ + + getEdgeVertex(edgeIndex, firstOrSecond, vertexStore) { + const vertexIndex = this.edges[edgeIndex * 2 + (firstOrSecond ? 1 : 0)]; + this.getVertex(vertexIndex, vertexStore); + } + /** + * Get a vector along an edge. + */ + + getEdgeVector(edgeIndex, vectorStore) { + const va = getEdgeVector_va; + const vb = getEdgeVector_vb; + this.getEdgeVertex(edgeIndex, 0, va); + this.getEdgeVertex(edgeIndex, 1, vb); + vb.vsub(va, vectorStore); + } + /** + * Get face normal given 3 vertices + */ + + static computeNormal(va, vb, vc, target) { + vb.vsub(va, ab); + vc.vsub(vb, cb); + cb.cross(ab, target); + + if (!target.isZero()) { + target.normalize(); + } + } + /** + * Get vertex i. + * @return The "out" vector object + */ + + getVertex(i, out) { + const scale = this.scale; + + this._getUnscaledVertex(i, out); + + out.x *= scale.x; + out.y *= scale.y; + out.z *= scale.z; + return out; + } + /** + * Get raw vertex i + * @return The "out" vector object + */ + + _getUnscaledVertex(i, out) { + const i3 = i * 3; + const vertices = this.vertices; + return out.set(vertices[i3], vertices[i3 + 1], vertices[i3 + 2]); + } + /** + * Get a vertex from the trimesh,transformed by the given position and quaternion. + * @return The "out" vector object + */ + + getWorldVertex(i, pos, quat, out) { + this.getVertex(i, out); + Transform.pointToWorldFrame(pos, quat, out, out); + return out; + } + /** + * Get the three vertices for triangle i. + */ + + getTriangleVertices(i, a, b, c) { + const i3 = i * 3; + this.getVertex(this.indices[i3], a); + this.getVertex(this.indices[i3 + 1], b); + this.getVertex(this.indices[i3 + 2], c); + } + /** + * Compute the normal of triangle i. + * @return The "target" vector object + */ + + getNormal(i, target) { + const i3 = i * 3; + return target.set( + this.normals[i3], + this.normals[i3 + 1], + this.normals[i3 + 2] + ); + } + /** + * @return The "target" vector object + */ + + calculateLocalInertia(mass, target) { + // Approximate with box inertia + // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it + this.computeLocalAABB(cli_aabb); + const x = cli_aabb.upperBound.x - cli_aabb.lowerBound.x; + const y = cli_aabb.upperBound.y - cli_aabb.lowerBound.y; + const z = cli_aabb.upperBound.z - cli_aabb.lowerBound.z; + return target.set( + (1.0 / 12.0) * mass * (2 * y * 2 * y + 2 * z * 2 * z), + (1.0 / 12.0) * mass * (2 * x * 2 * x + 2 * z * 2 * z), + (1.0 / 12.0) * mass * (2 * y * 2 * y + 2 * x * 2 * x) + ); + } + /** + * Compute the local AABB for the trimesh + */ + + computeLocalAABB(aabb) { + const l = aabb.lowerBound; + const u = aabb.upperBound; + const n = this.vertices.length; + this.vertices; + const v = computeLocalAABB_worldVert; + this.getVertex(0, v); + l.copy(v); + u.copy(v); + + for (let i = 0; i !== n; i++) { + this.getVertex(i, v); + + if (v.x < l.x) { + l.x = v.x; + } else if (v.x > u.x) { + u.x = v.x; + } + + if (v.y < l.y) { + l.y = v.y; + } else if (v.y > u.y) { + u.y = v.y; + } + + if (v.z < l.z) { + l.z = v.z; + } else if (v.z > u.z) { + u.z = v.z; + } + } + } + /** + * Update the `.aabb` property + */ + + updateAABB() { + this.computeLocalAABB(this.aabb); + } + /** + * Will update the `.boundingSphereRadius` property + */ + + updateBoundingSphereRadius() { + // Assume points are distributed with local (0,0,0) as center + let max2 = 0; + const vertices = this.vertices; + const v = new Vec3(); + + for (let i = 0, N = vertices.length / 3; i !== N; i++) { + this.getVertex(i, v); + const norm2 = v.lengthSquared(); + + if (norm2 > max2) { + max2 = norm2; + } + } + + this.boundingSphereRadius = Math.sqrt(max2); + } + /** + * calculateWorldAABB + */ + + calculateWorldAABB(pos, quat, min, max) { + /* + const n = this.vertices.length / 3, + verts = this.vertices; + const minx,miny,minz,maxx,maxy,maxz; + const v = tempWorldVertex; + for(let i=0; i maxx || maxx===undefined){ + maxx = v.x; + } + if (v.y < miny || miny===undefined){ + miny = v.y; + } else if(v.y > maxy || maxy===undefined){ + maxy = v.y; + } + if (v.z < minz || minz===undefined){ + minz = v.z; + } else if(v.z > maxz || maxz===undefined){ + maxz = v.z; + } + } + min.set(minx,miny,minz); + max.set(maxx,maxy,maxz); + */ + // Faster approximation using local AABB + const frame = calculateWorldAABB_frame; + const result = calculateWorldAABB_aabb; + frame.position = pos; + frame.quaternion = quat; + this.aabb.toWorldFrame(frame, result); + min.copy(result.lowerBound); + max.copy(result.upperBound); + } + /** + * Get approximate volume + */ + + volume() { + return (4.0 * Math.PI * this.boundingSphereRadius) / 3.0; + } + /** + * Create a Trimesh instance, shaped as a torus. + */ + + static createTorus(radius, tube, radialSegments, tubularSegments, arc) { + if (radius === void 0) { + radius = 1; + } + + if (tube === void 0) { + tube = 0.5; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (tubularSegments === void 0) { + tubularSegments = 6; + } + + if (arc === void 0) { + arc = Math.PI * 2; + } + + const vertices = []; + const indices = []; + + for (let j = 0; j <= radialSegments; j++) { + for (let i = 0; i <= tubularSegments; i++) { + const u = (i / tubularSegments) * arc; + const v = (j / radialSegments) * Math.PI * 2; + const x = (radius + tube * Math.cos(v)) * Math.cos(u); + const y = (radius + tube * Math.cos(v)) * Math.sin(u); + const z = tube * Math.sin(v); + vertices.push(x, y, z); + } + } + + for (let j = 1; j <= radialSegments; j++) { + for (let i = 1; i <= tubularSegments; i++) { + const a = (tubularSegments + 1) * j + i - 1; + const b = (tubularSegments + 1) * (j - 1) + i - 1; + const c = (tubularSegments + 1) * (j - 1) + i; + const d = (tubularSegments + 1) * j + i; + indices.push(a, b, d); + indices.push(b, c, d); + } + } + + return new Trimesh(vertices, indices); + } + } + const computeNormals_n = new Vec3(); + const unscaledAABB = new AABB(); + const getEdgeVector_va = new Vec3(); + const getEdgeVector_vb = new Vec3(); + const cb = new Vec3(); + const ab = new Vec3(); + const va = new Vec3(); + const vb = new Vec3(); + const vc = new Vec3(); + const cli_aabb = new AABB(); + const computeLocalAABB_worldVert = new Vec3(); + const calculateWorldAABB_frame = new Transform(); + const calculateWorldAABB_aabb = new AABB(); + + /** + * Constraint equation solver base class. + */ + class Solver { + /** + * All equations to be solved + */ + + /** + * @todo remove useless constructor + */ + constructor() { + this.equations = []; + } + /** + * Should be implemented in subclasses! + * @todo use abstract + * @return number of iterations performed + */ + + solve(dt, world) { + return ( + // Should return the number of iterations done! + 0 + ); + } + /** + * Add an equation + */ + + addEquation(eq) { + if (eq.enabled && !eq.bi.isTrigger && !eq.bj.isTrigger) { + this.equations.push(eq); + } + } + /** + * Remove an equation + */ + + removeEquation(eq) { + const eqs = this.equations; + const i = eqs.indexOf(eq); + + if (i !== -1) { + eqs.splice(i, 1); + } + } + /** + * Add all equations + */ + + removeAllEquations() { + this.equations.length = 0; + } + } + + /** + * Constraint equation Gauss-Seidel solver. + * @todo The spook parameters should be specified for each constraint, not globally. + * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf + */ + class GSSolver extends Solver { + /** + * The number of solver iterations determines quality of the constraints in the world. + * The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. + */ + + /** + * When tolerance is reached, the system is assumed to be converged. + */ + + /** + * @todo remove useless constructor + */ + constructor() { + super(); + this.iterations = 10; + this.tolerance = 1e-7; + } + /** + * Solve + * @return number of iterations performed + */ + + solve(dt, world) { + let iter = 0; + const maxIter = this.iterations; + const tolSquared = this.tolerance * this.tolerance; + const equations = this.equations; + const Neq = equations.length; + const bodies = world.bodies; + const Nbodies = bodies.length; + const h = dt; + let B; + let invC; + let deltalambda; + let deltalambdaTot; + let GWlambda; + let lambdaj; // Update solve mass + + if (Neq !== 0) { + for (let i = 0; i !== Nbodies; i++) { + bodies[i].updateSolveMassProperties(); + } + } // Things that do not change during iteration can be computed once + + const invCs = GSSolver_solve_invCs; + const Bs = GSSolver_solve_Bs; + const lambda = GSSolver_solve_lambda; + invCs.length = Neq; + Bs.length = Neq; + lambda.length = Neq; + + for (let i = 0; i !== Neq; i++) { + const c = equations[i]; + lambda[i] = 0.0; + Bs[i] = c.computeB(h); + invCs[i] = 1.0 / c.computeC(); + } + + if (Neq !== 0) { + // Reset vlambda + for (let i = 0; i !== Nbodies; i++) { + const b = bodies[i]; + const vlambda = b.vlambda; + const wlambda = b.wlambda; + vlambda.set(0, 0, 0); + wlambda.set(0, 0, 0); + } // Iterate over equations + + for (iter = 0; iter !== maxIter; iter++) { + // Accumulate the total error for each iteration. + deltalambdaTot = 0.0; + + for (let j = 0; j !== Neq; j++) { + const c = equations[j]; // Compute iteration + + B = Bs[j]; + invC = invCs[j]; + lambdaj = lambda[j]; + GWlambda = c.computeGWlambda(); + deltalambda = invC * (B - GWlambda - c.eps * lambdaj); // Clamp if we are not within the min/max interval + + if (lambdaj + deltalambda < c.minForce) { + deltalambda = c.minForce - lambdaj; + } else if (lambdaj + deltalambda > c.maxForce) { + deltalambda = c.maxForce - lambdaj; + } + + lambda[j] += deltalambda; + deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda) + + c.addToWlambda(deltalambda); + } // If the total error is small enough - stop iterate + + if (deltalambdaTot * deltalambdaTot < tolSquared) { + break; + } + } // Add result to velocity + + for (let i = 0; i !== Nbodies; i++) { + const b = bodies[i]; + const v = b.velocity; + const w = b.angularVelocity; + b.vlambda.vmul(b.linearFactor, b.vlambda); + v.vadd(b.vlambda, v); + b.wlambda.vmul(b.angularFactor, b.wlambda); + w.vadd(b.wlambda, w); + } // Set the `.multiplier` property of each equation + + let l = equations.length; + const invDt = 1 / h; + + while (l--) { + equations[l].multiplier = lambda[l] * invDt; + } + } + + return iter; + } + } // Just temporary number holders that we want to reuse each iteration. + + const GSSolver_solve_lambda = []; + const GSSolver_solve_invCs = []; + const GSSolver_solve_Bs = []; + + /** + * Splits the equations into islands and solves them independently. Can improve performance. + */ + class SplitSolver extends Solver { + /** + * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. + */ + + /** + * When tolerance is reached, the system is assumed to be converged. + */ + + /** subsolver */ + constructor(subsolver) { + super(); + this.iterations = 10; + this.tolerance = 1e-7; + this.subsolver = subsolver; + this.nodes = []; + this.nodePool = []; // Create needed nodes, reuse if possible + + while (this.nodePool.length < 128) { + this.nodePool.push(this.createNode()); + } + } + /** + * createNode + */ + + createNode() { + return { + body: null, + children: [], + eqs: [], + visited: false, + }; + } + /** + * Solve the subsystems + * @return number of iterations performed + */ + + solve(dt, world) { + const nodes = SplitSolver_solve_nodes; + const nodePool = this.nodePool; + const bodies = world.bodies; + const equations = this.equations; + const Neq = equations.length; + const Nbodies = bodies.length; + const subsolver = this.subsolver; // Create needed nodes, reuse if possible + + while (nodePool.length < Nbodies) { + nodePool.push(this.createNode()); + } + + nodes.length = Nbodies; + + for (let i = 0; i < Nbodies; i++) { + nodes[i] = nodePool[i]; + } // Reset node values + + for (let i = 0; i !== Nbodies; i++) { + const node = nodes[i]; + node.body = bodies[i]; + node.children.length = 0; + node.eqs.length = 0; + node.visited = false; + } + + for (let k = 0; k !== Neq; k++) { + const eq = equations[k]; + const i = bodies.indexOf(eq.bi); + const j = bodies.indexOf(eq.bj); + const ni = nodes[i]; + const nj = nodes[j]; + ni.children.push(nj); + ni.eqs.push(eq); + nj.children.push(ni); + nj.eqs.push(eq); + } + + let child; + let n = 0; + let eqs = SplitSolver_solve_eqs; + subsolver.tolerance = this.tolerance; + subsolver.iterations = this.iterations; + const dummyWorld = SplitSolver_solve_dummyWorld; + + while ((child = getUnvisitedNode(nodes))) { + eqs.length = 0; + dummyWorld.bodies.length = 0; + bfs(child, visitFunc, dummyWorld.bodies, eqs); + const Neqs = eqs.length; + eqs = eqs.sort(sortById); + + for (let i = 0; i !== Neqs; i++) { + subsolver.addEquation(eqs[i]); + } + + subsolver.solve(dt, dummyWorld); + subsolver.removeAllEquations(); + n++; + } + + return n; + } + } // Returns the number of subsystems + + const SplitSolver_solve_nodes = []; // All allocated node objects + + const SplitSolver_solve_eqs = []; // Temp array + + const SplitSolver_solve_dummyWorld = { + bodies: [], + }; // Temp object + + const STATIC = Body.STATIC; + + function getUnvisitedNode(nodes) { + const Nnodes = nodes.length; + + for (let i = 0; i !== Nnodes; i++) { + const node = nodes[i]; + + if (!node.visited && !(node.body.type & STATIC)) { + return node; + } + } + + return false; + } + + const queue = []; + + function bfs(root, visitFunc, bds, eqs) { + queue.push(root); + root.visited = true; + visitFunc(root, bds, eqs); + + while (queue.length) { + const node = queue.pop(); // Loop over unvisited child nodes + + let child; + + while ((child = getUnvisitedNode(node.children))) { + child.visited = true; + visitFunc(child, bds, eqs); + queue.push(child); + } + } + } + + function visitFunc(node, bds, eqs) { + bds.push(node.body); + const Neqs = node.eqs.length; + + for (let i = 0; i !== Neqs; i++) { + const eq = node.eqs[i]; + + if (!eqs.includes(eq)) { + eqs.push(eq); + } + } + } + + function sortById(a, b) { + return b.id - a.id; + } + + /** + * For pooling objects that can be reused. + */ + class Pool { + constructor() { + this.objects = []; + this.type = Object; + } + + /** + * Release an object after use + */ + release() { + const Nargs = arguments.length; + + for (let i = 0; i !== Nargs; i++) { + this.objects.push( + i < 0 || arguments.length <= i ? undefined : arguments[i] + ); + } + + return this; + } + /** + * Get an object + */ + + get() { + if (this.objects.length === 0) { + return this.constructObject(); + } else { + return this.objects.pop(); + } + } + /** + * Construct an object. Should be implemented in each subclass. + */ + + constructObject() { + throw new Error( + "constructObject() not implemented in this Pool subclass yet!" + ); + } + /** + * @return Self, for chaining + */ + + resize(size) { + const objects = this.objects; + + while (objects.length > size) { + objects.pop(); + } + + while (objects.length < size) { + objects.push(this.constructObject()); + } + + return this; + } + } + + /** + * Vec3Pool + */ + + class Vec3Pool extends Pool { + constructor() { + super(...arguments); + this.type = Vec3; + } + + /** + * Construct a vector + */ + constructObject() { + return new Vec3(); + } + } + + // Naming rule: based of the order in SHAPE_TYPES, + // the first part of the method is formed by the + // shape type that comes before, in the second part + // there is the shape type that comes after in the SHAPE_TYPES list + const COLLISION_TYPES = { + sphereSphere: Shape.types.SPHERE, + spherePlane: Shape.types.SPHERE | Shape.types.PLANE, + boxBox: Shape.types.BOX | Shape.types.BOX, + sphereBox: Shape.types.SPHERE | Shape.types.BOX, + planeBox: Shape.types.PLANE | Shape.types.BOX, + convexConvex: Shape.types.CONVEXPOLYHEDRON, + sphereConvex: Shape.types.SPHERE | Shape.types.CONVEXPOLYHEDRON, + planeConvex: Shape.types.PLANE | Shape.types.CONVEXPOLYHEDRON, + boxConvex: Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON, + sphereHeightfield: Shape.types.SPHERE | Shape.types.HEIGHTFIELD, + boxHeightfield: Shape.types.BOX | Shape.types.HEIGHTFIELD, + convexHeightfield: Shape.types.CONVEXPOLYHEDRON | Shape.types.HEIGHTFIELD, + sphereParticle: Shape.types.PARTICLE | Shape.types.SPHERE, + planeParticle: Shape.types.PLANE | Shape.types.PARTICLE, + boxParticle: Shape.types.BOX | Shape.types.PARTICLE, + convexParticle: Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON, + cylinderCylinder: Shape.types.CYLINDER, + sphereCylinder: Shape.types.SPHERE | Shape.types.CYLINDER, + planeCylinder: Shape.types.PLANE | Shape.types.CYLINDER, + boxCylinder: Shape.types.BOX | Shape.types.CYLINDER, + convexCylinder: Shape.types.CONVEXPOLYHEDRON | Shape.types.CYLINDER, + heightfieldCylinder: Shape.types.HEIGHTFIELD | Shape.types.CYLINDER, + particleCylinder: Shape.types.PARTICLE | Shape.types.CYLINDER, + sphereTrimesh: Shape.types.SPHERE | Shape.types.TRIMESH, + planeTrimesh: Shape.types.PLANE | Shape.types.TRIMESH, + }; + + /** + * Helper class for the World. Generates ContactEquations. + * @todo Sphere-ConvexPolyhedron contacts + * @todo Contact reduction + * @todo should move methods to prototype + */ + class Narrowphase { + /** + * Internal storage of pooled contact points. + */ + + /** + * Pooled vectors. + */ + get [COLLISION_TYPES.sphereSphere]() { + return this.sphereSphere; + } + + get [COLLISION_TYPES.spherePlane]() { + return this.spherePlane; + } + + get [COLLISION_TYPES.boxBox]() { + return this.boxBox; + } + + get [COLLISION_TYPES.sphereBox]() { + return this.sphereBox; + } + + get [COLLISION_TYPES.planeBox]() { + return this.planeBox; + } + + get [COLLISION_TYPES.convexConvex]() { + return this.convexConvex; + } + + get [COLLISION_TYPES.sphereConvex]() { + return this.sphereConvex; + } + + get [COLLISION_TYPES.planeConvex]() { + return this.planeConvex; + } + + get [COLLISION_TYPES.boxConvex]() { + return this.boxConvex; + } + + get [COLLISION_TYPES.sphereHeightfield]() { + return this.sphereHeightfield; + } + + get [COLLISION_TYPES.boxHeightfield]() { + return this.boxHeightfield; + } + + get [COLLISION_TYPES.convexHeightfield]() { + return this.convexHeightfield; + } + + get [COLLISION_TYPES.sphereParticle]() { + return this.sphereParticle; + } + + get [COLLISION_TYPES.planeParticle]() { + return this.planeParticle; + } + + get [COLLISION_TYPES.boxParticle]() { + return this.boxParticle; + } + + get [COLLISION_TYPES.convexParticle]() { + return this.convexParticle; + } + + get [COLLISION_TYPES.cylinderCylinder]() { + return this.convexConvex; + } + + get [COLLISION_TYPES.sphereCylinder]() { + return this.sphereConvex; + } + + get [COLLISION_TYPES.planeCylinder]() { + return this.planeConvex; + } + + get [COLLISION_TYPES.boxCylinder]() { + return this.boxConvex; + } + + get [COLLISION_TYPES.convexCylinder]() { + return this.convexConvex; + } + + get [COLLISION_TYPES.heightfieldCylinder]() { + return this.heightfieldCylinder; + } + + get [COLLISION_TYPES.particleCylinder]() { + return this.particleCylinder; + } + + get [COLLISION_TYPES.sphereTrimesh]() { + return this.sphereTrimesh; + } + + get [COLLISION_TYPES.planeTrimesh]() { + return this.planeTrimesh; + } // get [COLLISION_TYPES.convexTrimesh]() { + // return this.convexTrimesh + // } + + constructor(world) { + this.contactPointPool = []; + this.frictionEquationPool = []; + this.result = []; + this.frictionResult = []; + this.v3pool = new Vec3Pool(); + this.world = world; + this.currentContactMaterial = world.defaultContactMaterial; + this.enableFrictionReduction = false; + } + /** + * Make a contact object, by using the internal pool or creating a new one. + */ + + createContactEquation(bi, bj, si, sj, overrideShapeA, overrideShapeB) { + let c; + + if (this.contactPointPool.length) { + c = this.contactPointPool.pop(); + c.bi = bi; + c.bj = bj; + } else { + c = new ContactEquation(bi, bj); + } + + c.enabled = + bi.collisionResponse && + bj.collisionResponse && + si.collisionResponse && + sj.collisionResponse; + const cm = this.currentContactMaterial; + c.restitution = cm.restitution; + c.setSpookParams( + cm.contactEquationStiffness, + cm.contactEquationRelaxation, + this.world.dt + ); + const matA = si.material || bi.material; + const matB = sj.material || bj.material; + + if (matA && matB && matA.restitution >= 0 && matB.restitution >= 0) { + c.restitution = matA.restitution * matB.restitution; + } + + c.si = overrideShapeA || si; + c.sj = overrideShapeB || sj; + return c; + } + + createFrictionEquationsFromContact(contactEquation, outArray) { + const bodyA = contactEquation.bi; + const bodyB = contactEquation.bj; + const shapeA = contactEquation.si; + const shapeB = contactEquation.sj; + const world = this.world; + const cm = this.currentContactMaterial; // If friction or restitution were specified in the material, use them + + let friction = cm.friction; + const matA = shapeA.material || bodyA.material; + const matB = shapeB.material || bodyB.material; + + if (matA && matB && matA.friction >= 0 && matB.friction >= 0) { + friction = matA.friction * matB.friction; + } + + if (friction > 0) { + // Create 2 tangent equations + // Users may provide a force different from global gravity to use when computing contact friction. + const mug = friction * (world.frictionGravity || world.gravity).length(); + let reducedMass = bodyA.invMass + bodyB.invMass; + + if (reducedMass > 0) { + reducedMass = 1 / reducedMass; + } + + const pool = this.frictionEquationPool; + const c1 = pool.length + ? pool.pop() + : new FrictionEquation(bodyA, bodyB, mug * reducedMass); + const c2 = pool.length + ? pool.pop() + : new FrictionEquation(bodyA, bodyB, mug * reducedMass); + c1.bi = c2.bi = bodyA; + c1.bj = c2.bj = bodyB; + c1.minForce = c2.minForce = -mug * reducedMass; + c1.maxForce = c2.maxForce = mug * reducedMass; // Copy over the relative vectors + + c1.ri.copy(contactEquation.ri); + c1.rj.copy(contactEquation.rj); + c2.ri.copy(contactEquation.ri); + c2.rj.copy(contactEquation.rj); // Construct tangents + + contactEquation.ni.tangents(c1.t, c2.t); // Set spook params + + c1.setSpookParams( + cm.frictionEquationStiffness, + cm.frictionEquationRelaxation, + world.dt + ); + c2.setSpookParams( + cm.frictionEquationStiffness, + cm.frictionEquationRelaxation, + world.dt + ); + c1.enabled = c2.enabled = contactEquation.enabled; + outArray.push(c1, c2); + return true; + } + + return false; + } + /** + * Take the average N latest contact point on the plane. + */ + + createFrictionFromAverage(numContacts) { + // The last contactEquation + let c = this.result[this.result.length - 1]; // Create the result: two "average" friction equations + + if ( + !this.createFrictionEquationsFromContact(c, this.frictionResult) || + numContacts === 1 + ) { + return; + } + + const f1 = this.frictionResult[this.frictionResult.length - 2]; + const f2 = this.frictionResult[this.frictionResult.length - 1]; + averageNormal.setZero(); + averageContactPointA.setZero(); + averageContactPointB.setZero(); + const bodyA = c.bi; + c.bj; + + for (let i = 0; i !== numContacts; i++) { + c = this.result[this.result.length - 1 - i]; + + if (c.bi !== bodyA) { + averageNormal.vadd(c.ni, averageNormal); + averageContactPointA.vadd(c.ri, averageContactPointA); + averageContactPointB.vadd(c.rj, averageContactPointB); + } else { + averageNormal.vsub(c.ni, averageNormal); + averageContactPointA.vadd(c.rj, averageContactPointA); + averageContactPointB.vadd(c.ri, averageContactPointB); + } + } + + const invNumContacts = 1 / numContacts; + averageContactPointA.scale(invNumContacts, f1.ri); + averageContactPointB.scale(invNumContacts, f1.rj); + f2.ri.copy(f1.ri); // Should be the same + + f2.rj.copy(f1.rj); + averageNormal.normalize(); + averageNormal.tangents(f1.t, f2.t); // return eq; + } + /** + * Generate all contacts between a list of body pairs + * @param p1 Array of body indices + * @param p2 Array of body indices + * @param result Array to store generated contacts + * @param oldcontacts Optional. Array of reusable contact objects + */ + + getContacts( + p1, + p2, + world, + result, + oldcontacts, + frictionResult, + frictionPool + ) { + // Save old contact objects + this.contactPointPool = oldcontacts; + this.frictionEquationPool = frictionPool; + this.result = result; + this.frictionResult = frictionResult; + const qi = tmpQuat1; + const qj = tmpQuat2; + const xi = tmpVec1; + const xj = tmpVec2; + + for (let k = 0, N = p1.length; k !== N; k++) { + // Get current collision bodies + const bi = p1[k]; + const bj = p2[k]; // Get contact material + + let bodyContactMaterial = null; + + if (bi.material && bj.material) { + bodyContactMaterial = + world.getContactMaterial(bi.material, bj.material) || null; + } + + const justTest = + (bi.type & Body.KINEMATIC && bj.type & Body.STATIC) || + (bi.type & Body.STATIC && bj.type & Body.KINEMATIC) || + (bi.type & Body.KINEMATIC && bj.type & Body.KINEMATIC); + + for (let i = 0; i < bi.shapes.length; i++) { + bi.quaternion.mult(bi.shapeOrientations[i], qi); + bi.quaternion.vmult(bi.shapeOffsets[i], xi); + xi.vadd(bi.position, xi); + const si = bi.shapes[i]; + + for (let j = 0; j < bj.shapes.length; j++) { + // Compute world transform of shapes + bj.quaternion.mult(bj.shapeOrientations[j], qj); + bj.quaternion.vmult(bj.shapeOffsets[j], xj); + xj.vadd(bj.position, xj); + const sj = bj.shapes[j]; + + if ( + !( + si.collisionFilterMask & sj.collisionFilterGroup && + sj.collisionFilterMask & si.collisionFilterGroup + ) + ) { + continue; + } + + if ( + xi.distanceTo(xj) > + si.boundingSphereRadius + sj.boundingSphereRadius + ) { + continue; + } // Get collision material + + let shapeContactMaterial = null; + + if (si.material && sj.material) { + shapeContactMaterial = + world.getContactMaterial(si.material, sj.material) || null; + } + + this.currentContactMaterial = + shapeContactMaterial || + bodyContactMaterial || + world.defaultContactMaterial; // Get contacts + + const resolverIndex = si.type | sj.type; + const resolver = this[resolverIndex]; + + if (resolver) { + let retval = false; // TO DO: investigate why sphereParticle and convexParticle + // resolvers expect si and sj shapes to be in reverse order + // (i.e. larger integer value type first instead of smaller first) + + if (si.type < sj.type) { + retval = resolver.call( + this, + si, + sj, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } else { + retval = resolver.call( + this, + sj, + si, + xj, + xi, + qj, + qi, + bj, + bi, + si, + sj, + justTest + ); + } + + if (retval && justTest) { + // Register overlap + world.shapeOverlapKeeper.set(si.id, sj.id); + world.bodyOverlapKeeper.set(bi.id, bj.id); + } + } + } + } + } + } + + sphereSphere(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + if (justTest) { + return xi.distanceSquared(xj) < (si.radius + sj.radius) ** 2; + } // We will have only one contact in this case + + const contactEq = this.createContactEquation(bi, bj, si, sj, rsi, rsj); // Contact normal + + xj.vsub(xi, contactEq.ni); + contactEq.ni.normalize(); // Contact point locations + + contactEq.ri.copy(contactEq.ni); + contactEq.rj.copy(contactEq.ni); + contactEq.ri.scale(si.radius, contactEq.ri); + contactEq.rj.scale(-sj.radius, contactEq.rj); + contactEq.ri.vadd(xi, contactEq.ri); + contactEq.ri.vsub(bi.position, contactEq.ri); + contactEq.rj.vadd(xj, contactEq.rj); + contactEq.rj.vsub(bj.position, contactEq.rj); + this.result.push(contactEq); + this.createFrictionEquationsFromContact(contactEq, this.frictionResult); + } + + spherePlane(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + // We will have one contact in this case + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); // Contact normal + + r.ni.set(0, 0, 1); + qj.vmult(r.ni, r.ni); + r.ni.negate(r.ni); // body i is the sphere, flip normal + + r.ni.normalize(); // Needed? + // Vector from sphere center to contact point + + r.ni.scale(si.radius, r.ri); // Project down sphere on plane + + xi.vsub(xj, point_on_plane_to_sphere); + r.ni.scale(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho); + point_on_plane_to_sphere.vsub(plane_to_sphere_ortho, r.rj); // The sphere position projected to plane + + if (-point_on_plane_to_sphere.dot(r.ni) <= si.radius) { + if (justTest) { + return true; + } // Make it relative to the body + + const ri = r.ri; + const rj = r.rj; + ri.vadd(xi, ri); + ri.vsub(bi.position, ri); + rj.vadd(xj, rj); + rj.vsub(bj.position, rj); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + boxBox(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + si.convexPolyhedronRepresentation.material = si.material; + sj.convexPolyhedronRepresentation.material = sj.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; + return this.convexConvex( + si.convexPolyhedronRepresentation, + sj.convexPolyhedronRepresentation, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } + + sphereBox(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + const v3pool = this.v3pool; // we refer to the box as body j + + const sides = sphereBox_sides; + xi.vsub(xj, box_to_sphere); + sj.getSideNormals(sides, qj); + const R = si.radius; + + let found = false; // Store the resulting side penetration info + + const side_ns = sphereBox_side_ns; + const side_ns1 = sphereBox_side_ns1; + const side_ns2 = sphereBox_side_ns2; + let side_h = null; + let side_penetrations = 0; + let side_dot1 = 0; + let side_dot2 = 0; + let side_distance = null; + + for ( + let idx = 0, nsides = sides.length; + idx !== nsides && found === false; + idx++ + ) { + // Get the plane side normal (ns) + const ns = sphereBox_ns; + ns.copy(sides[idx]); + const h = ns.length(); + ns.normalize(); // The normal/distance dot product tells which side of the plane we are + + const dot = box_to_sphere.dot(ns); + + if (dot < h + R && dot > 0) { + // Intersects plane. Now check the other two dimensions + const ns1 = sphereBox_ns1; + const ns2 = sphereBox_ns2; + ns1.copy(sides[(idx + 1) % 3]); + ns2.copy(sides[(idx + 2) % 3]); + const h1 = ns1.length(); + const h2 = ns2.length(); + ns1.normalize(); + ns2.normalize(); + const dot1 = box_to_sphere.dot(ns1); + const dot2 = box_to_sphere.dot(ns2); + + if (dot1 < h1 && dot1 > -h1 && dot2 < h2 && dot2 > -h2) { + const dist = Math.abs(dot - h - R); + + if (side_distance === null || dist < side_distance) { + side_distance = dist; + side_dot1 = dot1; + side_dot2 = dot2; + side_h = h; + side_ns.copy(ns); + side_ns1.copy(ns1); + side_ns2.copy(ns2); + side_penetrations++; + + if (justTest) { + return true; + } + } + } + } + } + + if (side_penetrations) { + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + side_ns.scale(-R, r.ri); // Sphere r + + r.ni.copy(side_ns); + r.ni.negate(r.ni); // Normal should be out of sphere + + side_ns.scale(side_h, side_ns); + side_ns1.scale(side_dot1, side_ns1); + side_ns.vadd(side_ns1, side_ns); + side_ns2.scale(side_dot2, side_ns2); + side_ns.vadd(side_ns2, r.rj); // Make relative to bodies + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } // Check corners + + let rj = v3pool.get(); + const sphere_to_corner = sphereBox_sphere_to_corner; + + for (let j = 0; j !== 2 && !found; j++) { + for (let k = 0; k !== 2 && !found; k++) { + for (let l = 0; l !== 2 && !found; l++) { + rj.set(0, 0, 0); + + if (j) { + rj.vadd(sides[0], rj); + } else { + rj.vsub(sides[0], rj); + } + + if (k) { + rj.vadd(sides[1], rj); + } else { + rj.vsub(sides[1], rj); + } + + if (l) { + rj.vadd(sides[2], rj); + } else { + rj.vsub(sides[2], rj); + } // World position of corner + + xj.vadd(rj, sphere_to_corner); + sphere_to_corner.vsub(xi, sphere_to_corner); + + if (sphere_to_corner.lengthSquared() < R * R) { + if (justTest) { + return true; + } + + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ri.copy(sphere_to_corner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.scale(R, r.ri); + r.rj.copy(rj); // Make relative to bodies + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } + + v3pool.release(rj); + rj = null; // Check edges + + const edgeTangent = v3pool.get(); + const edgeCenter = v3pool.get(); + const r = v3pool.get(); // r = edge center to sphere center + + const orthogonal = v3pool.get(); + const dist = v3pool.get(); + const Nsides = sides.length; + + for (let j = 0; j !== Nsides && !found; j++) { + for (let k = 0; k !== Nsides && !found; k++) { + if (j % 3 !== k % 3) { + // Get edge tangent + sides[k].cross(sides[j], edgeTangent); + edgeTangent.normalize(); + sides[j].vadd(sides[k], edgeCenter); + r.copy(xi); + r.vsub(edgeCenter, r); + r.vsub(xj, r); + const orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction + + edgeTangent.scale(orthonorm, orthogonal); // Vector from edge center to sphere center in the tangent direction + // Find the third side orthogonal to this one + + let l = 0; + + while (l === j % 3 || l === k % 3) { + l++; + } // vec from edge center to sphere projected to the plane orthogonal to the edge tangent + + dist.copy(xi); + dist.vsub(orthogonal, dist); + dist.vsub(edgeCenter, dist); + dist.vsub(xj, dist); // Distances in tangent direction and distance in the plane orthogonal to it + + const tdist = Math.abs(orthonorm); + const ndist = dist.length(); + + if (tdist < sides[l].length() && ndist < R) { + if (justTest) { + return true; + } + + found = true; + const res = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + edgeCenter.vadd(orthogonal, res.rj); // box rj + + res.rj.copy(res.rj); + dist.negate(res.ni); + res.ni.normalize(); + res.ri.copy(res.rj); + res.ri.vadd(xj, res.ri); + res.ri.vsub(xi, res.ri); + res.ri.normalize(); + res.ri.scale(R, res.ri); // Make relative to bodies + + res.ri.vadd(xi, res.ri); + res.ri.vsub(bi.position, res.ri); + res.rj.vadd(xj, res.rj); + res.rj.vsub(bj.position, res.rj); + this.result.push(res); + this.createFrictionEquationsFromContact(res, this.frictionResult); + } + } + } + } + + v3pool.release(edgeTangent, edgeCenter, r, orthogonal, dist); + } + + planeBox(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + sj.convexPolyhedronRepresentation.material = sj.material; + sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; + sj.convexPolyhedronRepresentation.id = sj.id; + return this.planeConvex( + si, + sj.convexPolyhedronRepresentation, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } + + convexConvex( + si, + sj, + xi, + xj, + qi, + qj, + bi, + bj, + rsi, + rsj, + justTest, + faceListA, + faceListB + ) { + const sepAxis = convexConvex_sepAxis; + + if (xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius) { + return; + } + + if ( + si.findSeparatingAxis(sj, xi, qi, xj, qj, sepAxis, faceListA, faceListB) + ) { + const res = []; + const q = convexConvex_q; + si.clipAgainstHull(xi, qi, sj, xj, qj, sepAxis, -100, 100, res); + let numContacts = 0; + + for (let j = 0; j !== res.length; j++) { + if (justTest) { + return true; + } + + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + const ri = r.ri; + const rj = r.rj; + sepAxis.negate(r.ni); + res[j].normal.negate(q); + q.scale(res[j].depth, q); + res[j].point.vadd(q, ri); + rj.copy(res[j].point); // Contact points are in world coordinates. Transform back to relative + + ri.vsub(xi, ri); + rj.vsub(xj, rj); // Make relative to bodies + + ri.vadd(xi, ri); + ri.vsub(bi.position, ri); + rj.vadd(xj, rj); + rj.vsub(bj.position, rj); + this.result.push(r); + numContacts++; + + if (!this.enableFrictionReduction) { + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + if (this.enableFrictionReduction && numContacts) { + this.createFrictionFromAverage(numContacts); + } + } + } + + sphereConvex(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + const v3pool = this.v3pool; + xi.vsub(xj, convex_to_sphere); + const normals = sj.faceNormals; + const faces = sj.faces; + const verts = sj.vertices; + const R = si.radius; + // return; + // } + + let found = false; // Check corners + + for (let i = 0; i !== verts.length; i++) { + const v = verts[i]; // World position of corner + + const worldCorner = sphereConvex_worldCorner; + qj.vmult(v, worldCorner); + xj.vadd(worldCorner, worldCorner); + const sphere_to_corner = sphereConvex_sphereToCorner; + worldCorner.vsub(xi, sphere_to_corner); + + if (sphere_to_corner.lengthSquared() < R * R) { + if (justTest) { + return true; + } + + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ri.copy(sphere_to_corner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.scale(R, r.ri); + worldCorner.vsub(xj, r.rj); // Should be relative to the body. + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); // Should be relative to the body. + + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + return; + } + } // Check side (plane) intersections + + for ( + let i = 0, nfaces = faces.length; + i !== nfaces && found === false; + i++ + ) { + const normal = normals[i]; + const face = faces[i]; // Get world-transformed normal of the face + + const worldNormal = sphereConvex_worldNormal; + qj.vmult(normal, worldNormal); // Get a world vertex from the face + + const worldPoint = sphereConvex_worldPoint; + qj.vmult(verts[face[0]], worldPoint); + worldPoint.vadd(xj, worldPoint); // Get a point on the sphere, closest to the face normal + + const worldSpherePointClosestToPlane = + sphereConvex_worldSpherePointClosestToPlane; + worldNormal.scale(-R, worldSpherePointClosestToPlane); + xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane); // Vector from a face point to the closest point on the sphere + + const penetrationVec = sphereConvex_penetrationVec; + worldSpherePointClosestToPlane.vsub(worldPoint, penetrationVec); // The penetration. Negative value means overlap. + + const penetration = penetrationVec.dot(worldNormal); + const worldPointToSphere = sphereConvex_sphereToWorldPoint; + xi.vsub(worldPoint, worldPointToSphere); + + if (penetration < 0 && worldPointToSphere.dot(worldNormal) > 0) { + // Intersects plane. Now check if the sphere is inside the face polygon + const faceVerts = []; // Face vertices, in world coords + + for (let j = 0, Nverts = face.length; j !== Nverts; j++) { + const worldVertex = v3pool.get(); + qj.vmult(verts[face[j]], worldVertex); + xj.vadd(worldVertex, worldVertex); + faceVerts.push(worldVertex); + } + + if (pointInPolygon(faceVerts, worldNormal, xi)) { + // Is the sphere center in the face polygon? + if (justTest) { + return true; + } + + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + worldNormal.scale(-R, r.ri); // Contact offset, from sphere center to contact + + worldNormal.negate(r.ni); // Normal pointing out of sphere + + const penetrationVec2 = v3pool.get(); + worldNormal.scale(-penetration, penetrationVec2); + const penetrationSpherePoint = v3pool.get(); + worldNormal.scale(-R, penetrationSpherePoint); //xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj); + + xi.vsub(xj, r.rj); + r.rj.vadd(penetrationSpherePoint, r.rj); + r.rj.vadd(penetrationVec2, r.rj); // Should be relative to the body. + + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); // Should be relative to the body. + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + v3pool.release(penetrationVec2); + v3pool.release(penetrationSpherePoint); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); // Release world vertices + + for ( + let j = 0, Nfaceverts = faceVerts.length; + j !== Nfaceverts; + j++ + ) { + v3pool.release(faceVerts[j]); + } + + return; // We only expect *one* face contact + } else { + // Edge? + for (let j = 0; j !== face.length; j++) { + // Get two world transformed vertices + const v1 = v3pool.get(); + const v2 = v3pool.get(); + qj.vmult(verts[face[(j + 1) % face.length]], v1); + qj.vmult(verts[face[(j + 2) % face.length]], v2); + xj.vadd(v1, v1); + xj.vadd(v2, v2); // Construct edge vector + + const edge = sphereConvex_edge; + v2.vsub(v1, edge); // Construct the same vector, but normalized + + const edgeUnit = sphereConvex_edgeUnit; + edge.unit(edgeUnit); // p is xi projected onto the edge + + const p = v3pool.get(); + const v1_to_xi = v3pool.get(); + xi.vsub(v1, v1_to_xi); + const dot = v1_to_xi.dot(edgeUnit); + edgeUnit.scale(dot, p); + p.vadd(v1, p); // Compute a vector from p to the center of the sphere + + const xi_to_p = v3pool.get(); + p.vsub(xi, xi_to_p); // Collision if the edge-sphere distance is less than the radius + // AND if p is in between v1 and v2 + + if ( + dot > 0 && + dot * dot < edge.lengthSquared() && + xi_to_p.lengthSquared() < R * R + ) { + // Collision if the edge-sphere distance is less than the radius + // Edge contact! + if (justTest) { + return true; + } + + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + p.vsub(xj, r.rj); + p.vsub(xi, r.ni); + r.ni.normalize(); + r.ni.scale(R, r.ri); // Should be relative to the body. + + r.rj.vadd(xj, r.rj); + r.rj.vsub(bj.position, r.rj); // Should be relative to the body. + + r.ri.vadd(xi, r.ri); + r.ri.vsub(bi.position, r.ri); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); // Release world vertices + + for ( + let j = 0, Nfaceverts = faceVerts.length; + j !== Nfaceverts; + j++ + ) { + v3pool.release(faceVerts[j]); + } + + v3pool.release(v1); + v3pool.release(v2); + v3pool.release(p); + v3pool.release(xi_to_p); + v3pool.release(v1_to_xi); + return; + } + + v3pool.release(v1); + v3pool.release(v2); + v3pool.release(p); + v3pool.release(xi_to_p); + v3pool.release(v1_to_xi); + } + } // Release world vertices + + for (let j = 0, Nfaceverts = faceVerts.length; j !== Nfaceverts; j++) { + v3pool.release(faceVerts[j]); + } + } + } + } + + planeConvex( + planeShape, + convexShape, + planePosition, + convexPosition, + planeQuat, + convexQuat, + planeBody, + convexBody, + si, + sj, + justTest + ) { + // Simply return the points behind the plane. + const worldVertex = planeConvex_v; + const worldNormal = planeConvex_normal; + worldNormal.set(0, 0, 1); + planeQuat.vmult(worldNormal, worldNormal); // Turn normal according to plane orientation + + let numContacts = 0; + const relpos = planeConvex_relpos; + + for (let i = 0; i !== convexShape.vertices.length; i++) { + // Get world convex vertex + worldVertex.copy(convexShape.vertices[i]); + convexQuat.vmult(worldVertex, worldVertex); + convexPosition.vadd(worldVertex, worldVertex); + worldVertex.vsub(planePosition, relpos); + const dot = worldNormal.dot(relpos); + + if (dot <= 0.0) { + if (justTest) { + return true; + } + + const r = this.createContactEquation( + planeBody, + convexBody, + planeShape, + convexShape, + si, + sj + ); // Get vertex position projected on plane + + const projected = planeConvex_projected; + worldNormal.scale(worldNormal.dot(relpos), projected); + worldVertex.vsub(projected, projected); + projected.vsub(planePosition, r.ri); // From plane to vertex projected on plane + + r.ni.copy(worldNormal); // Contact normal is the plane normal out from plane + // rj is now just the vector from the convex center to the vertex + + worldVertex.vsub(convexPosition, r.rj); // Make it relative to the body + + r.ri.vadd(planePosition, r.ri); + r.ri.vsub(planeBody.position, r.ri); + r.rj.vadd(convexPosition, r.rj); + r.rj.vsub(convexBody.position, r.rj); + this.result.push(r); + numContacts++; + + if (!this.enableFrictionReduction) { + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + + if (this.enableFrictionReduction && numContacts) { + this.createFrictionFromAverage(numContacts); + } + } + + boxConvex(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + return this.convexConvex( + si.convexPolyhedronRepresentation, + sj, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } + + sphereHeightfield( + sphereShape, + hfShape, + spherePos, + hfPos, + sphereQuat, + hfQuat, + sphereBody, + hfBody, + rsi, + rsj, + justTest + ) { + const data = hfShape.data; + const radius = sphereShape.radius; + const w = hfShape.elementSize; + const worldPillarOffset = sphereHeightfield_tmp2; // Get sphere position to heightfield local! + + const localSpherePos = sphereHeightfield_tmp1; + Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos); // Get the index of the data points to test against + + let iMinX = Math.floor((localSpherePos.x - radius) / w) - 1; + let iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1; + let iMinY = Math.floor((localSpherePos.y - radius) / w) - 1; + let iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1; // Bail out if we are out of the terrain + + if ( + iMaxX < 0 || + iMaxY < 0 || + iMinX > data.length || + iMinY > data[0].length + ) { + return; + } // Clamp index to edges + + if (iMinX < 0) { + iMinX = 0; + } + + if (iMaxX < 0) { + iMaxX = 0; + } + + if (iMinY < 0) { + iMinY = 0; + } + + if (iMaxY < 0) { + iMaxY = 0; + } + + if (iMinX >= data.length) { + iMinX = data.length - 1; + } + + if (iMaxX >= data.length) { + iMaxX = data.length - 1; + } + + if (iMaxY >= data[0].length) { + iMaxY = data[0].length - 1; + } + + if (iMinY >= data[0].length) { + iMinY = data[0].length - 1; + } + + const minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + const min = minMax[0]; + const max = minMax[1]; // Bail out if we can't touch the bounding height box + + if (localSpherePos.z - radius > max || localSpherePos.z + radius < min) { + return; + } + + const result = this.result; + + for (let i = iMinX; i < iMaxX; i++) { + for (let j = iMinY; j < iMaxY; j++) { + const numContactsBefore = result.length; + let intersecting = false; // Lower triangle + + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame( + hfPos, + hfQuat, + hfShape.pillarOffset, + worldPillarOffset + ); + + if ( + spherePos.distanceTo(worldPillarOffset) < + hfShape.pillarConvex.boundingSphereRadius + + sphereShape.boundingSphereRadius + ) { + intersecting = this.sphereConvex( + sphereShape, + hfShape.pillarConvex, + spherePos, + worldPillarOffset, + sphereQuat, + hfQuat, + sphereBody, + hfBody, + sphereShape, + hfShape, + justTest + ); + } + + if (justTest && intersecting) { + return true; + } // Upper triangle + + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame( + hfPos, + hfQuat, + hfShape.pillarOffset, + worldPillarOffset + ); + + if ( + spherePos.distanceTo(worldPillarOffset) < + hfShape.pillarConvex.boundingSphereRadius + + sphereShape.boundingSphereRadius + ) { + intersecting = this.sphereConvex( + sphereShape, + hfShape.pillarConvex, + spherePos, + worldPillarOffset, + sphereQuat, + hfQuat, + sphereBody, + hfBody, + sphereShape, + hfShape, + justTest + ); + } + + if (justTest && intersecting) { + return true; + } + + const numContacts = result.length - numContactsBefore; + + if (numContacts > 2) { + return; + } + /* + // Skip all but 1 + for (let k = 0; k < numContacts - 1; k++) { + result.pop(); + } + */ + } + } + } + + boxHeightfield(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + return this.convexHeightfield( + si.convexPolyhedronRepresentation, + sj, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } + + convexHeightfield( + convexShape, + hfShape, + convexPos, + hfPos, + convexQuat, + hfQuat, + convexBody, + hfBody, + rsi, + rsj, + justTest + ) { + const data = hfShape.data; + const w = hfShape.elementSize; + const radius = convexShape.boundingSphereRadius; + const worldPillarOffset = convexHeightfield_tmp2; + const faceList = convexHeightfield_faceList; // Get sphere position to heightfield local! + + const localConvexPos = convexHeightfield_tmp1; + Transform.pointToLocalFrame(hfPos, hfQuat, convexPos, localConvexPos); // Get the index of the data points to test against + + let iMinX = Math.floor((localConvexPos.x - radius) / w) - 1; + let iMaxX = Math.ceil((localConvexPos.x + radius) / w) + 1; + let iMinY = Math.floor((localConvexPos.y - radius) / w) - 1; + let iMaxY = Math.ceil((localConvexPos.y + radius) / w) + 1; // Bail out if we are out of the terrain + + if ( + iMaxX < 0 || + iMaxY < 0 || + iMinX > data.length || + iMinY > data[0].length + ) { + return; + } // Clamp index to edges + + if (iMinX < 0) { + iMinX = 0; + } + + if (iMaxX < 0) { + iMaxX = 0; + } + + if (iMinY < 0) { + iMinY = 0; + } + + if (iMaxY < 0) { + iMaxY = 0; + } + + if (iMinX >= data.length) { + iMinX = data.length - 1; + } + + if (iMaxX >= data.length) { + iMaxX = data.length - 1; + } + + if (iMaxY >= data[0].length) { + iMaxY = data[0].length - 1; + } + + if (iMinY >= data[0].length) { + iMinY = data[0].length - 1; + } + + const minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + const min = minMax[0]; + const max = minMax[1]; // Bail out if we're cant touch the bounding height box + + if (localConvexPos.z - radius > max || localConvexPos.z + radius < min) { + return; + } + + for (let i = iMinX; i < iMaxX; i++) { + for (let j = iMinY; j < iMaxY; j++) { + let intersecting = false; // Lower triangle + + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame( + hfPos, + hfQuat, + hfShape.pillarOffset, + worldPillarOffset + ); + + if ( + convexPos.distanceTo(worldPillarOffset) < + hfShape.pillarConvex.boundingSphereRadius + + convexShape.boundingSphereRadius + ) { + intersecting = this.convexConvex( + convexShape, + hfShape.pillarConvex, + convexPos, + worldPillarOffset, + convexQuat, + hfQuat, + convexBody, + hfBody, + null, + null, + justTest, + faceList, + null + ); + } + + if (justTest && intersecting) { + return true; + } // Upper triangle + + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame( + hfPos, + hfQuat, + hfShape.pillarOffset, + worldPillarOffset + ); + + if ( + convexPos.distanceTo(worldPillarOffset) < + hfShape.pillarConvex.boundingSphereRadius + + convexShape.boundingSphereRadius + ) { + intersecting = this.convexConvex( + convexShape, + hfShape.pillarConvex, + convexPos, + worldPillarOffset, + convexQuat, + hfQuat, + convexBody, + hfBody, + null, + null, + justTest, + faceList, + null + ); + } + + if (justTest && intersecting) { + return true; + } + } + } + } + + sphereParticle(sj, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest) { + // The normal is the unit vector from sphere center to particle center + const normal = particleSphere_normal; + normal.set(0, 0, 1); + xi.vsub(xj, normal); + const lengthSquared = normal.lengthSquared(); + + if (lengthSquared <= sj.radius * sj.radius) { + if (justTest) { + return true; + } + + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + normal.normalize(); + r.rj.copy(normal); + r.rj.scale(sj.radius, r.rj); + r.ni.copy(normal); // Contact normal + + r.ni.negate(r.ni); + r.ri.set(0, 0, 0); // Center of particle + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + planeParticle(sj, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest) { + const normal = particlePlane_normal; + normal.set(0, 0, 1); + bj.quaternion.vmult(normal, normal); // Turn normal according to plane orientation + + const relpos = particlePlane_relpos; + xi.vsub(bj.position, relpos); + const dot = normal.dot(relpos); + + if (dot <= 0.0) { + if (justTest) { + return true; + } + + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ni.copy(normal); // Contact normal is the plane normal + + r.ni.negate(r.ni); + r.ri.set(0, 0, 0); // Center of particle + // Get particle position projected on plane + + const projected = particlePlane_projected; + normal.scale(normal.dot(xi), projected); + xi.vsub(projected, projected); //projected.vadd(bj.position,projected); + // rj is now the projected world position minus plane position + + r.rj.copy(projected); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + boxParticle(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + return this.convexParticle( + si.convexPolyhedronRepresentation, + sj, + xi, + xj, + qi, + qj, + bi, + bj, + si, + sj, + justTest + ); + } + + convexParticle(sj, si, xj, xi, qj, qi, bj, bi, rsi, rsj, justTest) { + let penetratedFaceIndex = -1; + const penetratedFaceNormal = convexParticle_penetratedFaceNormal; + const worldPenetrationVec = convexParticle_worldPenetrationVec; + let minPenetration = null; + + const local = convexParticle_local; + local.copy(xi); + local.vsub(xj, local); // Convert position to relative the convex origin + + qj.conjugate(cqj); + cqj.vmult(local, local); + + if (sj.pointIsInside(local)) { + if (sj.worldVerticesNeedsUpdate) { + sj.computeWorldVertices(xj, qj); + } + + if (sj.worldFaceNormalsNeedsUpdate) { + sj.computeWorldFaceNormals(qj); + } // For each world polygon in the polyhedra + + for (let i = 0, nfaces = sj.faces.length; i !== nfaces; i++) { + // Construct world face vertices + const verts = [sj.worldVertices[sj.faces[i][0]]]; + const normal = sj.worldFaceNormals[i]; // Check how much the particle penetrates the polygon plane. + + xi.vsub(verts[0], convexParticle_vertexToParticle); + const penetration = -normal.dot(convexParticle_vertexToParticle); + + if ( + minPenetration === null || + Math.abs(penetration) < Math.abs(minPenetration) + ) { + if (justTest) { + return true; + } + + minPenetration = penetration; + penetratedFaceIndex = i; + penetratedFaceNormal.copy(normal); + } + } + + if (penetratedFaceIndex !== -1) { + // Setup contact + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + penetratedFaceNormal.scale(minPenetration, worldPenetrationVec); // rj is the particle position projected to the face + + worldPenetrationVec.vadd(xi, worldPenetrationVec); + worldPenetrationVec.vsub(xj, worldPenetrationVec); + r.rj.copy(worldPenetrationVec); //const projectedToFace = xi.vsub(xj).vadd(worldPenetrationVec); + //projectedToFace.copy(r.rj); + //qj.vmult(r.rj,r.rj); + + penetratedFaceNormal.negate(r.ni); // Contact normal + + r.ri.set(0, 0, 0); // Center of particle + + const ri = r.ri; + const rj = r.rj; // Make relative to bodies + + ri.vadd(xi, ri); + ri.vsub(bi.position, ri); + rj.vadd(xj, rj); + rj.vsub(bj.position, rj); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } else { + console.warn( + "Point found inside convex, but did not find penetrating face!" + ); + } + } + } + + heightfieldCylinder( + hfShape, + convexShape, + hfPos, + convexPos, + hfQuat, + convexQuat, + hfBody, + convexBody, + rsi, + rsj, + justTest + ) { + return this.convexHeightfield( + convexShape, + hfShape, + convexPos, + hfPos, + convexQuat, + hfQuat, + convexBody, + hfBody, + rsi, + rsj, + justTest + ); + } + + particleCylinder(si, sj, xi, xj, qi, qj, bi, bj, rsi, rsj, justTest) { + return this.convexParticle( + sj, + si, + xj, + xi, + qj, + qi, + bj, + bi, + rsi, + rsj, + justTest + ); + } + + sphereTrimesh( + sphereShape, + trimeshShape, + spherePos, + trimeshPos, + sphereQuat, + trimeshQuat, + sphereBody, + trimeshBody, + rsi, + rsj, + justTest + ) { + const edgeVertexA = sphereTrimesh_edgeVertexA; + const edgeVertexB = sphereTrimesh_edgeVertexB; + const edgeVector = sphereTrimesh_edgeVector; + const edgeVectorUnit = sphereTrimesh_edgeVectorUnit; + const localSpherePos = sphereTrimesh_localSpherePos; + const tmp = sphereTrimesh_tmp; + const localSphereAABB = sphereTrimesh_localSphereAABB; + const v2 = sphereTrimesh_v2; + const relpos = sphereTrimesh_relpos; + const triangles = sphereTrimesh_triangles; // Convert sphere position to local in the trimesh + + Transform.pointToLocalFrame( + trimeshPos, + trimeshQuat, + spherePos, + localSpherePos + ); // Get the aabb of the sphere locally in the trimesh + + const sphereRadius = sphereShape.radius; + localSphereAABB.lowerBound.set( + localSpherePos.x - sphereRadius, + localSpherePos.y - sphereRadius, + localSpherePos.z - sphereRadius + ); + localSphereAABB.upperBound.set( + localSpherePos.x + sphereRadius, + localSpherePos.y + sphereRadius, + localSpherePos.z + sphereRadius + ); + trimeshShape.getTrianglesInAABB(localSphereAABB, triangles); //for (let i = 0; i < trimeshShape.indices.length / 3; i++) triangles.push(i); // All + // Vertices + + const v = sphereTrimesh_v; + const radiusSquared = sphereShape.radius * sphereShape.radius; + + for (let i = 0; i < triangles.length; i++) { + for (let j = 0; j < 3; j++) { + trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], v); // Check vertex overlap in sphere + + v.vsub(localSpherePos, relpos); + + if (relpos.lengthSquared() <= radiusSquared) { + // Safe up + v2.copy(v); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v); + v.vsub(spherePos, relpos); + + if (justTest) { + return true; + } + + let r = this.createContactEquation( + sphereBody, + trimeshBody, + sphereShape, + trimeshShape, + rsi, + rsj + ); + r.ni.copy(relpos); + r.ni.normalize(); // ri is the vector from sphere center to the sphere surface + + r.ri.copy(r.ni); + r.ri.scale(sphereShape.radius, r.ri); + r.ri.vadd(spherePos, r.ri); + r.ri.vsub(sphereBody.position, r.ri); + r.rj.copy(v); + r.rj.vsub(trimeshBody.position, r.rj); // Store result + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } // Check all edges + + for (let i = 0; i < triangles.length; i++) { + for (let j = 0; j < 3; j++) { + trimeshShape.getVertex( + trimeshShape.indices[triangles[i] * 3 + j], + edgeVertexA + ); + trimeshShape.getVertex( + trimeshShape.indices[triangles[i] * 3 + ((j + 1) % 3)], + edgeVertexB + ); + edgeVertexB.vsub(edgeVertexA, edgeVector); // Project sphere position to the edge + + localSpherePos.vsub(edgeVertexB, tmp); + const positionAlongEdgeB = tmp.dot(edgeVector); + localSpherePos.vsub(edgeVertexA, tmp); + let positionAlongEdgeA = tmp.dot(edgeVector); + + if (positionAlongEdgeA > 0 && positionAlongEdgeB < 0) { + // Now check the orthogonal distance from edge to sphere center + localSpherePos.vsub(edgeVertexA, tmp); + edgeVectorUnit.copy(edgeVector); + edgeVectorUnit.normalize(); + positionAlongEdgeA = tmp.dot(edgeVectorUnit); + edgeVectorUnit.scale(positionAlongEdgeA, tmp); + tmp.vadd(edgeVertexA, tmp); // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame + + const dist = tmp.distanceTo(localSpherePos); + + if (dist < sphereShape.radius) { + if (justTest) { + return true; + } + + const r = this.createContactEquation( + sphereBody, + trimeshBody, + sphereShape, + trimeshShape, + rsi, + rsj + ); + tmp.vsub(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scale(sphereShape.radius, r.ri); + r.ri.vadd(spherePos, r.ri); + r.ri.vsub(sphereBody.position, r.ri); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.vsub(trimeshBody.position, r.rj); + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } // Triangle faces + + const va = sphereTrimesh_va; + const vb = sphereTrimesh_vb; + const vc = sphereTrimesh_vc; + const normal = sphereTrimesh_normal; + + for (let i = 0, N = triangles.length; i !== N; i++) { + trimeshShape.getTriangleVertices(triangles[i], va, vb, vc); + trimeshShape.getNormal(triangles[i], normal); + localSpherePos.vsub(va, tmp); + let dist = tmp.dot(normal); + normal.scale(dist, tmp); + localSpherePos.vsub(tmp, tmp); // tmp is now the sphere position projected to the triangle plane + + dist = tmp.distanceTo(localSpherePos); + + if (Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius) { + if (justTest) { + return true; + } + + let r = this.createContactEquation( + sphereBody, + trimeshBody, + sphereShape, + trimeshShape, + rsi, + rsj + ); + tmp.vsub(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scale(sphereShape.radius, r.ri); + r.ri.vadd(spherePos, r.ri); + r.ri.vsub(sphereBody.position, r.ri); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.vsub(trimeshBody.position, r.rj); + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + triangles.length = 0; + } + + planeTrimesh( + planeShape, + trimeshShape, + planePos, + trimeshPos, + planeQuat, + trimeshQuat, + planeBody, + trimeshBody, + rsi, + rsj, + justTest + ) { + // Make contacts! + const v = new Vec3(); + const normal = planeTrimesh_normal; + normal.set(0, 0, 1); + planeQuat.vmult(normal, normal); // Turn normal according to plane + + for (let i = 0; i < trimeshShape.vertices.length / 3; i++) { + // Get world vertex from trimesh + trimeshShape.getVertex(i, v); // Safe up + + const v2 = new Vec3(); + v2.copy(v); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v); // Check plane side + + const relpos = planeTrimesh_relpos; + v.vsub(planePos, relpos); + const dot = normal.dot(relpos); + + if (dot <= 0.0) { + if (justTest) { + return true; + } + + const r = this.createContactEquation( + planeBody, + trimeshBody, + planeShape, + trimeshShape, + rsi, + rsj + ); + r.ni.copy(normal); // Contact normal is the plane normal + // Get vertex position projected on plane + + const projected = planeTrimesh_projected; + normal.scale(relpos.dot(normal), projected); + v.vsub(projected, projected); // ri is the projected world position minus plane position + + r.ri.copy(projected); + r.ri.vsub(planeBody.position, r.ri); + r.rj.copy(v); + r.rj.vsub(trimeshBody.position, r.rj); // Store result + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } // convexTrimesh( + // si: ConvexPolyhedron, sj: Trimesh, xi: Vec3, xj: Vec3, qi: Quaternion, qj: Quaternion, + // bi: Body, bj: Body, rsi?: Shape | null, rsj?: Shape | null, + // faceListA?: number[] | null, faceListB?: number[] | null, + // ) { + // const sepAxis = convexConvex_sepAxis; + // if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ + // return; + // } + // // Construct a temp hull for each triangle + // const hullB = new ConvexPolyhedron(); + // hullB.faces = [[0,1,2]]; + // const va = new Vec3(); + // const vb = new Vec3(); + // const vc = new Vec3(); + // hullB.vertices = [ + // va, + // vb, + // vc + // ]; + // for (let i = 0; i < sj.indices.length / 3; i++) { + // const triangleNormal = new Vec3(); + // sj.getNormal(i, triangleNormal); + // hullB.faceNormals = [triangleNormal]; + // sj.getTriangleVertices(i, va, vb, vc); + // let d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); + // if(!d){ + // triangleNormal.scale(-1, triangleNormal); + // d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); + // if(!d){ + // continue; + // } + // } + // const res: ConvexPolyhedronContactPoint[] = []; + // const q = convexConvex_q; + // si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res); + // for(let j = 0; j !== res.length; j++){ + // const r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), + // ri = r.ri, + // rj = r.rj; + // r.ni.copy(triangleNormal); + // r.ni.negate(r.ni); + // res[j].normal.negate(q); + // q.mult(res[j].depth, q); + // res[j].point.vadd(q, ri); + // rj.copy(res[j].point); + // // Contact points are in world coordinates. Transform back to relative + // ri.vsub(xi,ri); + // rj.vsub(xj,rj); + // // Make relative to bodies + // ri.vadd(xi, ri); + // ri.vsub(bi.position, ri); + // rj.vadd(xj, rj); + // rj.vsub(bj.position, rj); + // result.push(r); + // } + // } + // } + } + const averageNormal = new Vec3(); + const averageContactPointA = new Vec3(); + const averageContactPointB = new Vec3(); + const tmpVec1 = new Vec3(); + const tmpVec2 = new Vec3(); + const tmpQuat1 = new Quaternion(); + const tmpQuat2 = new Quaternion(); + + const planeTrimesh_normal = new Vec3(); + const planeTrimesh_relpos = new Vec3(); + const planeTrimesh_projected = new Vec3(); + const sphereTrimesh_normal = new Vec3(); + const sphereTrimesh_relpos = new Vec3(); + new Vec3(); + const sphereTrimesh_v = new Vec3(); + const sphereTrimesh_v2 = new Vec3(); + const sphereTrimesh_edgeVertexA = new Vec3(); + const sphereTrimesh_edgeVertexB = new Vec3(); + const sphereTrimesh_edgeVector = new Vec3(); + const sphereTrimesh_edgeVectorUnit = new Vec3(); + const sphereTrimesh_localSpherePos = new Vec3(); + const sphereTrimesh_tmp = new Vec3(); + const sphereTrimesh_va = new Vec3(); + const sphereTrimesh_vb = new Vec3(); + const sphereTrimesh_vc = new Vec3(); + const sphereTrimesh_localSphereAABB = new AABB(); + const sphereTrimesh_triangles = []; + const point_on_plane_to_sphere = new Vec3(); + const plane_to_sphere_ortho = new Vec3(); // See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html + + const pointInPolygon_edge = new Vec3(); + const pointInPolygon_edge_x_normal = new Vec3(); + const pointInPolygon_vtp = new Vec3(); + + function pointInPolygon(verts, normal, p) { + let positiveResult = null; + const N = verts.length; + + for (let i = 0; i !== N; i++) { + const v = verts[i]; // Get edge to the next vertex + + const edge = pointInPolygon_edge; + verts[(i + 1) % N].vsub(v, edge); // Get cross product between polygon normal and the edge + + const edge_x_normal = pointInPolygon_edge_x_normal; //const edge_x_normal = new Vec3(); + + edge.cross(normal, edge_x_normal); // Get vector between point and current vertex + + const vertex_to_p = pointInPolygon_vtp; + p.vsub(v, vertex_to_p); // This dot product determines which side of the edge the point is + + const r = edge_x_normal.dot(vertex_to_p); // If all such dot products have same sign, we are inside the polygon. + + if ( + positiveResult === null || + (r > 0 && positiveResult === true) || + (r <= 0 && positiveResult === false) + ) { + if (positiveResult === null) { + positiveResult = r > 0; + } + + continue; + } else { + return false; // Encountered some other sign. Exit. + } + } // If we got here, all dot products were of the same sign. + + return true; + } + + const box_to_sphere = new Vec3(); + const sphereBox_ns = new Vec3(); + const sphereBox_ns1 = new Vec3(); + const sphereBox_ns2 = new Vec3(); + const sphereBox_sides = [ + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + new Vec3(), + ]; + const sphereBox_sphere_to_corner = new Vec3(); + const sphereBox_side_ns = new Vec3(); + const sphereBox_side_ns1 = new Vec3(); + const sphereBox_side_ns2 = new Vec3(); + const convex_to_sphere = new Vec3(); + const sphereConvex_edge = new Vec3(); + const sphereConvex_edgeUnit = new Vec3(); + const sphereConvex_sphereToCorner = new Vec3(); + const sphereConvex_worldCorner = new Vec3(); + const sphereConvex_worldNormal = new Vec3(); + const sphereConvex_worldPoint = new Vec3(); + const sphereConvex_worldSpherePointClosestToPlane = new Vec3(); + const sphereConvex_penetrationVec = new Vec3(); + const sphereConvex_sphereToWorldPoint = new Vec3(); + new Vec3(); + new Vec3(); + const planeConvex_v = new Vec3(); + const planeConvex_normal = new Vec3(); + const planeConvex_relpos = new Vec3(); + const planeConvex_projected = new Vec3(); + const convexConvex_sepAxis = new Vec3(); + const convexConvex_q = new Vec3(); + const particlePlane_normal = new Vec3(); + const particlePlane_relpos = new Vec3(); + const particlePlane_projected = new Vec3(); + const particleSphere_normal = new Vec3(); // WIP + + const cqj = new Quaternion(); + const convexParticle_local = new Vec3(); + new Vec3(); + const convexParticle_penetratedFaceNormal = new Vec3(); + const convexParticle_vertexToParticle = new Vec3(); + const convexParticle_worldPenetrationVec = new Vec3(); + const convexHeightfield_tmp1 = new Vec3(); + const convexHeightfield_tmp2 = new Vec3(); + const convexHeightfield_faceList = [0]; + const sphereHeightfield_tmp1 = new Vec3(); + const sphereHeightfield_tmp2 = new Vec3(); + + class OverlapKeeper { + /** + * @todo Remove useless constructor + */ + constructor() { + this.current = []; + this.previous = []; + } + /** + * getKey + */ + + getKey(i, j) { + if (j < i) { + const temp = j; + j = i; + i = temp; + } + + return (i << 16) | j; + } + /** + * set + */ + + set(i, j) { + // Insertion sort. This way the diff will have linear complexity. + const key = this.getKey(i, j); + const current = this.current; + let index = 0; + + while (key > current[index]) { + index++; + } + + if (key === current[index]) { + return; // Pair was already added + } + + for (let j = current.length - 1; j >= index; j--) { + current[j + 1] = current[j]; + } + + current[index] = key; + } + /** + * tick + */ + + tick() { + const tmp = this.current; + this.current = this.previous; + this.previous = tmp; + this.current.length = 0; + } + /** + * getDiff + */ + + getDiff(additions, removals) { + const a = this.current; + const b = this.previous; + const al = a.length; + const bl = b.length; + let j = 0; + + for (let i = 0; i < al; i++) { + let found = false; + const keyA = a[i]; + + while (keyA > b[j]) { + j++; + } + + found = keyA === b[j]; + + if (!found) { + unpackAndPush(additions, keyA); + } + } + + j = 0; + + for (let i = 0; i < bl; i++) { + let found = false; + const keyB = b[i]; + + while (keyB > a[j]) { + j++; + } + + found = a[j] === keyB; + + if (!found) { + unpackAndPush(removals, keyB); + } + } + } + } + + function unpackAndPush(array, key) { + array.push((key & 0xffff0000) >> 16, key & 0x0000ffff); + } + + const getKey = (i, j) => (i < j ? `${i}-${j}` : `${j}-${i}`); + /** + * TupleDictionary + */ + + class TupleDictionary { + constructor() { + this.data = { + keys: [], + }; + } + + /** get */ + get(i, j) { + const key = getKey(i, j); + return this.data[key]; + } + /** set */ + + set(i, j, value) { + const key = getKey(i, j); // Check if key already exists + + if (!this.get(i, j)) { + this.data.keys.push(key); + } + + this.data[key] = value; + } + /** delete */ + + delete(i, j) { + const key = getKey(i, j); + const index = this.data.keys.indexOf(key); + + if (index !== -1) { + this.data.keys.splice(index, 1); + } + + delete this.data[key]; + } + /** reset */ + + reset() { + const data = this.data; + const keys = data.keys; + + while (keys.length > 0) { + const key = keys.pop(); + delete data[key]; + } + } + } + + /** + * The physics world + */ + class World extends EventTarget { + /** + * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks. + */ + + /** + * Makes bodies go to sleep when they've been inactive. + * @default false + */ + + /** + * All the current contacts (instances of ContactEquation) in the world. + */ + + /** + * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong). + * @default 0 + */ + + /** + * Set to true to use fast quaternion normalization. It is often enough accurate to use. + * If bodies tend to explode, set to false. + * @default false + */ + + /** + * The wall-clock time since simulation start. + */ + + /** + * Number of timesteps taken since start. + */ + + /** + * Default and last timestep sizes. + */ + + /** + * The gravity of the world. + */ + + /** + * Gravity to use when approximating the friction max force (mu*mass*gravity). + * If undefined, global gravity will be used. + * Use to enable friction in a World with a null gravity vector (no gravity). + */ + + /** + * The broadphase algorithm to use. + * @default NaiveBroadphase + */ + + /** + * All bodies in this world + */ + + /** + * True if any bodies are not sleeping, false if every body is sleeping. + */ + + /** + * The solver algorithm to use. + * @default GSSolver + */ + + /** + * collisionMatrix + */ + + /** + * CollisionMatrix from the previous step. + */ + + /** + * All added contactmaterials. + */ + + /** + * Used to look up a ContactMaterial given two instances of Material. + */ + + /** + * The default material of the bodies. + */ + + /** + * This contact material is used if no suitable contactmaterial is found for a contact. + */ + + /** + * Time accumulator for interpolation. + * @see https://gafferongames.com/game-physics/fix-your-timestep/ + */ + + /** + * Dispatched after a body has been added to the world. + */ + + /** + * Dispatched after a body has been removed from the world. + */ + constructor(options) { + if (options === void 0) { + options = {}; + } + + super(); + this.dt = -1; + this.allowSleep = !!options.allowSleep; + this.contacts = []; + this.frictionEquations = []; + this.quatNormalizeSkip = + options.quatNormalizeSkip !== undefined ? options.quatNormalizeSkip : 0; + this.quatNormalizeFast = + options.quatNormalizeFast !== undefined + ? options.quatNormalizeFast + : false; + this.time = 0.0; + this.stepnumber = 0; + this.default_dt = 1 / 60; + this.nextId = 0; + this.gravity = new Vec3(); + + if (options.gravity) { + this.gravity.copy(options.gravity); + } + + if (options.frictionGravity) { + this.frictionGravity = new Vec3(); + this.frictionGravity.copy(options.frictionGravity); + } + + this.broadphase = + options.broadphase !== undefined + ? options.broadphase + : new NaiveBroadphase(); + this.bodies = []; + this.hasActiveBodies = false; + this.solver = + options.solver !== undefined ? options.solver : new GSSolver(); + this.constraints = []; + this.narrowphase = new Narrowphase(this); + this.collisionMatrix = new ArrayCollisionMatrix(); + this.collisionMatrixPrevious = new ArrayCollisionMatrix(); + this.bodyOverlapKeeper = new OverlapKeeper(); + this.shapeOverlapKeeper = new OverlapKeeper(); + this.contactmaterials = []; + this.contactMaterialTable = new TupleDictionary(); + this.defaultMaterial = new Material("default"); + this.defaultContactMaterial = new ContactMaterial( + this.defaultMaterial, + this.defaultMaterial, + { + friction: 0.3, + restitution: 0.0, + } + ); + this.doProfiling = false; + this.profile = { + solve: 0, + makeContactConstraints: 0, + broadphase: 0, + integrate: 0, + narrowphase: 0, + }; + this.accumulator = 0; + this.subsystems = []; + this.addBodyEvent = { + type: "addBody", + body: null, + }; + this.removeBodyEvent = { + type: "removeBody", + body: null, + }; + this.idToBodyMap = {}; + this.broadphase.setWorld(this); + } + /** + * Get the contact material between materials m1 and m2 + * @return The contact material if it was found. + */ + + getContactMaterial(m1, m2) { + return this.contactMaterialTable.get(m1.id, m2.id); + } + /** + * Store old collision state info + */ + + collisionMatrixTick() { + const temp = this.collisionMatrixPrevious; + this.collisionMatrixPrevious = this.collisionMatrix; + this.collisionMatrix = temp; + this.collisionMatrix.reset(); + this.bodyOverlapKeeper.tick(); + this.shapeOverlapKeeper.tick(); + } + /** + * Add a constraint to the simulation. + */ + + addConstraint(c) { + this.constraints.push(c); + } + /** + * Removes a constraint + */ + + removeConstraint(c) { + const idx = this.constraints.indexOf(c); + + if (idx !== -1) { + this.constraints.splice(idx, 1); + } + } + /** + * Raycast test + * @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead. + */ + + rayTest(from, to, result) { + if (result instanceof RaycastResult) { + // Do raycastClosest + this.raycastClosest( + from, + to, + { + skipBackfaces: true, + }, + result + ); + } else { + // Do raycastAll + this.raycastAll( + from, + to, + { + skipBackfaces: true, + }, + result + ); + } + } + /** + * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument. + * @return True if any body was hit. + */ + + raycastAll(from, to, options, callback) { + if (options === void 0) { + options = {}; + } + + options.mode = Ray.ALL; + options.from = from; + options.to = to; + options.callback = callback; + return tmpRay.intersectWorld(this, options); + } + /** + * Ray cast, and stop at the first result. Note that the order is random - but the method is fast. + * @return True if any body was hit. + */ + + raycastAny(from, to, options, result) { + if (options === void 0) { + options = {}; + } + + options.mode = Ray.ANY; + options.from = from; + options.to = to; + options.result = result; + return tmpRay.intersectWorld(this, options); + } + /** + * Ray cast, and return information of the closest hit. + * @return True if any body was hit. + */ + + raycastClosest(from, to, options, result) { + if (options === void 0) { + options = {}; + } + + options.mode = Ray.CLOSEST; + options.from = from; + options.to = to; + options.result = result; + return tmpRay.intersectWorld(this, options); + } + /** + * Add a rigid body to the simulation. + * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case. + * @todo Adding an array of bodies should be possible. This would save some loops too + */ + + addBody(body) { + if (this.bodies.includes(body)) { + return; + } + + body.index = this.bodies.length; + this.bodies.push(body); + body.world = this; + body.initPosition.copy(body.position); + body.initVelocity.copy(body.velocity); + body.timeLastSleepy = this.time; + + if (body instanceof Body) { + body.initAngularVelocity.copy(body.angularVelocity); + body.initQuaternion.copy(body.quaternion); + } + + this.collisionMatrix.setNumObjects(this.bodies.length); + this.addBodyEvent.body = body; + this.idToBodyMap[body.id] = body; + this.dispatchEvent(this.addBodyEvent); + } + /** + * Remove a rigid body from the simulation. + */ + + removeBody(body) { + body.world = null; + const n = this.bodies.length - 1; + const bodies = this.bodies; + const idx = bodies.indexOf(body); + + if (idx !== -1) { + bodies.splice(idx, 1); // Todo: should use a garbage free method + // Recompute index + + for (let i = 0; i !== bodies.length; i++) { + bodies[i].index = i; + } + + this.collisionMatrix.setNumObjects(n); + this.removeBodyEvent.body = body; + delete this.idToBodyMap[body.id]; + this.dispatchEvent(this.removeBodyEvent); + } + } + + getBodyById(id) { + return this.idToBodyMap[id]; + } + /** + * @todo Make a faster map + */ + + getShapeById(id) { + const bodies = this.bodies; + + for (let i = 0; i < bodies.length; i++) { + const shapes = bodies[i].shapes; + + for (let j = 0; j < shapes.length; j++) { + const shape = shapes[j]; + + if (shape.id === id) { + return shape; + } + } + } + + return null; + } + /** + * Adds a contact material to the World + */ + + addContactMaterial(cmat) { + // Add contact material + this.contactmaterials.push(cmat); // Add current contact material to the material table + + this.contactMaterialTable.set( + cmat.materials[0].id, + cmat.materials[1].id, + cmat + ); + } + /** + * Removes a contact material from the World. + */ + + removeContactMaterial(cmat) { + const idx = this.contactmaterials.indexOf(cmat); + + if (idx === -1) { + return; + } + + this.contactmaterials.splice(idx, 1); + this.contactMaterialTable.delete( + cmat.materials[0].id, + cmat.materials[1].id + ); + } + /** + * Step the simulation forward keeping track of last called time + * to be able to step the world at a fixed rate, independently of framerate. + * + * @param dt The fixed time step size to use (default: 1 / 60). + * @param maxSubSteps Maximum number of fixed steps to take per function call (default: 10). + * @see https://gafferongames.com/post/fix_your_timestep/ + * @example + * // Run the simulation independently of framerate every 1 / 60 ms + * world.fixedStep() + */ + + fixedStep(dt, maxSubSteps) { + if (dt === void 0) { + dt = 1 / 60; + } + + if (maxSubSteps === void 0) { + maxSubSteps = 10; + } + + const time = performance.now() / 1000; // seconds + + if (!this.lastCallTime) { + this.step(dt, undefined, maxSubSteps); + } else { + const timeSinceLastCalled = time - this.lastCallTime; + this.step(dt, timeSinceLastCalled, maxSubSteps); + } + + this.lastCallTime = time; + } + /** + * Step the physics world forward in time. + * + * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take. + * + * @param dt The fixed time step size to use. + * @param timeSinceLastCalled The time elapsed since the function was last called. + * @param maxSubSteps Maximum number of fixed steps to take per function call (default: 10). + * @see https://web.archive.org/web/20180426154531/http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World#What_do_the_parameters_to_btDynamicsWorld::stepSimulation_mean.3F + * @example + * // fixed timestepping without interpolation + * world.step(1 / 60) + */ + + step(dt, timeSinceLastCalled, maxSubSteps) { + if (maxSubSteps === void 0) { + maxSubSteps = 10; + } + + if (timeSinceLastCalled === undefined) { + // Fixed, simple stepping + this.internalStep(dt); // Increment time + + this.time += dt; + } else { + this.accumulator += timeSinceLastCalled; + const t0 = performance.now(); + let substeps = 0; + + while (this.accumulator >= dt && substeps < maxSubSteps) { + // Do fixed steps to catch up + this.internalStep(dt); + this.accumulator -= dt; + substeps++; + + if (performance.now() - t0 > dt * 1000) { + // The framerate is not interactive anymore. + // We are below the target framerate. + // Better bail out. + break; + } + } // Remove the excess accumulator, since we may not + // have had enough substeps available to catch up + + this.accumulator = this.accumulator % dt; + const t = this.accumulator / dt; + + for (let j = 0; j !== this.bodies.length; j++) { + const b = this.bodies[j]; + b.previousPosition.lerp(b.position, t, b.interpolatedPosition); + b.previousQuaternion.slerp(b.quaternion, t, b.interpolatedQuaternion); + b.previousQuaternion.normalize(); + } + + this.time += timeSinceLastCalled; + } + } + + internalStep(dt) { + this.dt = dt; + const contacts = this.contacts; + const p1 = World_step_p1; + const p2 = World_step_p2; + const N = this.bodies.length; + const bodies = this.bodies; + const solver = this.solver; + const gravity = this.gravity; + const doProfiling = this.doProfiling; + const profile = this.profile; + const DYNAMIC = Body.DYNAMIC; + let profilingStart = -Infinity; + const constraints = this.constraints; + const frictionEquationPool = World_step_frictionEquationPool; + gravity.length(); + const gx = gravity.x; + const gy = gravity.y; + const gz = gravity.z; + let i = 0; + + if (doProfiling) { + profilingStart = performance.now(); + } // Add gravity to all objects + + for (i = 0; i !== N; i++) { + const bi = bodies[i]; + + if (bi.type === DYNAMIC) { + // Only for dynamic bodies + const f = bi.force; + const m = bi.mass; + f.x += m * gx; + f.y += m * gy; + f.z += m * gz; + } + } // Update subsystems + + for ( + let i = 0, Nsubsystems = this.subsystems.length; + i !== Nsubsystems; + i++ + ) { + this.subsystems[i].update(); + } // Collision detection + + if (doProfiling) { + profilingStart = performance.now(); + } + + p1.length = 0; // Clean up pair arrays from last step + + p2.length = 0; + this.broadphase.collisionPairs(this, p1, p2); + + if (doProfiling) { + profile.broadphase = performance.now() - profilingStart; + } // Remove constrained pairs with collideConnected == false + + let Nconstraints = constraints.length; + + for (i = 0; i !== Nconstraints; i++) { + const c = constraints[i]; + + if (!c.collideConnected) { + for (let j = p1.length - 1; j >= 0; j -= 1) { + if ( + (c.bodyA === p1[j] && c.bodyB === p2[j]) || + (c.bodyB === p1[j] && c.bodyA === p2[j]) + ) { + p1.splice(j, 1); + p2.splice(j, 1); + } + } + } + } + + this.collisionMatrixTick(); // Generate contacts + + if (doProfiling) { + profilingStart = performance.now(); + } + + const oldcontacts = World_step_oldContacts; + const NoldContacts = contacts.length; + + for (i = 0; i !== NoldContacts; i++) { + oldcontacts.push(contacts[i]); + } + + contacts.length = 0; // Transfer FrictionEquation from current list to the pool for reuse + + const NoldFrictionEquations = this.frictionEquations.length; + + for (i = 0; i !== NoldFrictionEquations; i++) { + frictionEquationPool.push(this.frictionEquations[i]); + } + + this.frictionEquations.length = 0; + this.narrowphase.getContacts( + p1, + p2, + this, + contacts, + oldcontacts, // To be reused + this.frictionEquations, + frictionEquationPool + ); + + if (doProfiling) { + profile.narrowphase = performance.now() - profilingStart; + } // Loop over all collisions + + if (doProfiling) { + profilingStart = performance.now(); + } // Add all friction eqs + + for (i = 0; i < this.frictionEquations.length; i++) { + solver.addEquation(this.frictionEquations[i]); + } + + const ncontacts = contacts.length; + + for (let k = 0; k !== ncontacts; k++) { + // Current contact + const c = contacts[k]; // Get current collision indeces + + const bi = c.bi; + const bj = c.bj; + const si = c.si; + const sj = c.sj; // Get collision properties + + let cm; + + if (bi.material && bj.material) { + cm = + this.getContactMaterial(bi.material, bj.material) || + this.defaultContactMaterial; + } else { + cm = this.defaultContactMaterial; + } // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; + + cm.friction; // c.restitution = cm.restitution; + // If friction or restitution were specified in the material, use them + + if (bi.material && bj.material) { + if (bi.material.friction >= 0 && bj.material.friction >= 0) { + bi.material.friction * bj.material.friction; + } + + if (bi.material.restitution >= 0 && bj.material.restitution >= 0) { + c.restitution = bi.material.restitution * bj.material.restitution; + } + } // c.setSpookParams( + // cm.contactEquationStiffness, + // cm.contactEquationRelaxation, + // dt + // ); + + solver.addEquation(c); // // Add friction constraint equation + // if(mu > 0){ + // // Create 2 tangent equations + // const mug = mu * gnorm; + // const reducedMass = (bi.invMass + bj.invMass); + // if(reducedMass > 0){ + // reducedMass = 1/reducedMass; + // } + // const pool = frictionEquationPool; + // const c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // const c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // this.frictionEquations.push(c1, c2); + // c1.bi = c2.bi = bi; + // c1.bj = c2.bj = bj; + // c1.minForce = c2.minForce = -mug*reducedMass; + // c1.maxForce = c2.maxForce = mug*reducedMass; + // // Copy over the relative vectors + // c1.ri.copy(c.ri); + // c1.rj.copy(c.rj); + // c2.ri.copy(c.ri); + // c2.rj.copy(c.rj); + // // Construct tangents + // c.ni.tangents(c1.t, c2.t); + // // Set spook params + // c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + // c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + // c1.enabled = c2.enabled = c.enabled; + // // Add equations to solver + // solver.addEquation(c1); + // solver.addEquation(c2); + // } + + if ( + bi.allowSleep && + bi.type === Body.DYNAMIC && + bi.sleepState === Body.SLEEPING && + bj.sleepState === Body.AWAKE && + bj.type !== Body.STATIC + ) { + const speedSquaredB = + bj.velocity.lengthSquared() + bj.angularVelocity.lengthSquared(); + const speedLimitSquaredB = bj.sleepSpeedLimit ** 2; + + if (speedSquaredB >= speedLimitSquaredB * 2) { + bi.wakeUpAfterNarrowphase = true; + } + } + + if ( + bj.allowSleep && + bj.type === Body.DYNAMIC && + bj.sleepState === Body.SLEEPING && + bi.sleepState === Body.AWAKE && + bi.type !== Body.STATIC + ) { + const speedSquaredA = + bi.velocity.lengthSquared() + bi.angularVelocity.lengthSquared(); + const speedLimitSquaredA = bi.sleepSpeedLimit ** 2; + + if (speedSquaredA >= speedLimitSquaredA * 2) { + bj.wakeUpAfterNarrowphase = true; + } + } // Now we know that i and j are in contact. Set collision matrix state + + this.collisionMatrix.set(bi, bj, true); + + if (!this.collisionMatrixPrevious.get(bi, bj)) { + // First contact! + // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached. + World_step_collideEvent.body = bj; + World_step_collideEvent.contact = c; + bi.dispatchEvent(World_step_collideEvent); + World_step_collideEvent.body = bi; + bj.dispatchEvent(World_step_collideEvent); + } + + this.bodyOverlapKeeper.set(bi.id, bj.id); + this.shapeOverlapKeeper.set(si.id, sj.id); + } + + this.emitContactEvents(); + + if (doProfiling) { + profile.makeContactConstraints = performance.now() - profilingStart; + profilingStart = performance.now(); + } // Wake up bodies + + for (i = 0; i !== N; i++) { + const bi = bodies[i]; + + if (bi.wakeUpAfterNarrowphase) { + bi.wakeUp(); + bi.wakeUpAfterNarrowphase = false; + } + } // Add user-added constraints + + Nconstraints = constraints.length; + + for (i = 0; i !== Nconstraints; i++) { + const c = constraints[i]; + c.update(); + + for (let j = 0, Neq = c.equations.length; j !== Neq; j++) { + const eq = c.equations[j]; + solver.addEquation(eq); + } + } // Solve the constrained system + + solver.solve(dt, this); + + if (doProfiling) { + profile.solve = performance.now() - profilingStart; + } // Remove all contacts from solver + + solver.removeAllEquations(); // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details + + const pow = Math.pow; + + for (i = 0; i !== N; i++) { + const bi = bodies[i]; + + if (bi.type & DYNAMIC) { + // Only for dynamic bodies + const ld = pow(1.0 - bi.linearDamping, dt); + const v = bi.velocity; + v.scale(ld, v); + const av = bi.angularVelocity; + + if (av) { + const ad = pow(1.0 - bi.angularDamping, dt); + av.scale(ad, av); + } + } + } + + this.dispatchEvent(World_step_preStepEvent); // Leap frog + // vnew = v + h*f/m + // xnew = x + h*vnew + + if (doProfiling) { + profilingStart = performance.now(); + } + + const stepnumber = this.stepnumber; + const quatNormalize = stepnumber % (this.quatNormalizeSkip + 1) === 0; + const quatNormalizeFast = this.quatNormalizeFast; + + for (i = 0; i !== N; i++) { + bodies[i].integrate(dt, quatNormalize, quatNormalizeFast); + } + + this.clearForces(); + this.broadphase.dirty = true; + + if (doProfiling) { + profile.integrate = performance.now() - profilingStart; + } // Update step number + + this.stepnumber += 1; + this.dispatchEvent(World_step_postStepEvent); // Sleeping update + + let hasActiveBodies = true; + + if (this.allowSleep) { + hasActiveBodies = false; + + for (i = 0; i !== N; i++) { + const bi = bodies[i]; + bi.sleepTick(this.time); + + if (bi.sleepState !== Body.SLEEPING) { + hasActiveBodies = true; + } + } + } + + this.hasActiveBodies = hasActiveBodies; + } + + emitContactEvents() { + const hasBeginContact = this.hasAnyEventListener("beginContact"); + const hasEndContact = this.hasAnyEventListener("endContact"); + + if (hasBeginContact || hasEndContact) { + this.bodyOverlapKeeper.getDiff(additions, removals); + } + + if (hasBeginContact) { + for (let i = 0, l = additions.length; i < l; i += 2) { + beginContactEvent.bodyA = this.getBodyById(additions[i]); + beginContactEvent.bodyB = this.getBodyById(additions[i + 1]); + this.dispatchEvent(beginContactEvent); + } + + beginContactEvent.bodyA = beginContactEvent.bodyB = null; + } + + if (hasEndContact) { + for (let i = 0, l = removals.length; i < l; i += 2) { + endContactEvent.bodyA = this.getBodyById(removals[i]); + endContactEvent.bodyB = this.getBodyById(removals[i + 1]); + this.dispatchEvent(endContactEvent); + } + + endContactEvent.bodyA = endContactEvent.bodyB = null; + } + + additions.length = removals.length = 0; + const hasBeginShapeContact = this.hasAnyEventListener("beginShapeContact"); + const hasEndShapeContact = this.hasAnyEventListener("endShapeContact"); + + if (hasBeginShapeContact || hasEndShapeContact) { + this.shapeOverlapKeeper.getDiff(additions, removals); + } + + if (hasBeginShapeContact) { + for (let i = 0, l = additions.length; i < l; i += 2) { + const shapeA = this.getShapeById(additions[i]); + const shapeB = this.getShapeById(additions[i + 1]); + beginShapeContactEvent.shapeA = shapeA; + beginShapeContactEvent.shapeB = shapeB; + if (shapeA) beginShapeContactEvent.bodyA = shapeA.body; + if (shapeB) beginShapeContactEvent.bodyB = shapeB.body; + this.dispatchEvent(beginShapeContactEvent); + } + + beginShapeContactEvent.bodyA = + beginShapeContactEvent.bodyB = + beginShapeContactEvent.shapeA = + beginShapeContactEvent.shapeB = + null; + } + + if (hasEndShapeContact) { + for (let i = 0, l = removals.length; i < l; i += 2) { + const shapeA = this.getShapeById(removals[i]); + const shapeB = this.getShapeById(removals[i + 1]); + endShapeContactEvent.shapeA = shapeA; + endShapeContactEvent.shapeB = shapeB; + if (shapeA) endShapeContactEvent.bodyA = shapeA.body; + if (shapeB) endShapeContactEvent.bodyB = shapeB.body; + this.dispatchEvent(endShapeContactEvent); + } + + endShapeContactEvent.bodyA = + endShapeContactEvent.bodyB = + endShapeContactEvent.shapeA = + endShapeContactEvent.shapeB = + null; + } + } + /** + * Sets all body forces in the world to zero. + */ + + clearForces() { + const bodies = this.bodies; + const N = bodies.length; + + for (let i = 0; i !== N; i++) { + const b = bodies[i]; + b.force; + b.torque; + b.force.set(0, 0, 0); + b.torque.set(0, 0, 0); + } + } + } // Temp stuff + + new AABB(); + const tmpRay = new Ray(); // performance.now() fallback on Date.now() + + const performance = + (require$$0 && require$$0.performance) || {}; + + if (!performance.now) { + let nowOffset = Date.now(); + + if (performance.timing && performance.timing.navigationStart) { + nowOffset = performance.timing.navigationStart; + } + + performance.now = () => Date.now() - nowOffset; + } + + new Vec3(); // Dispatched after the world has stepped forward in time. + // Reusable event objects to save memory. + + const World_step_postStepEvent = { + type: "postStep", + }; // Dispatched before the world steps forward in time. + + const World_step_preStepEvent = { + type: "preStep", + }; + const World_step_collideEvent = { + type: Body.COLLIDE_EVENT_NAME, + body: null, + contact: null, + }; // Pools for unused objects + + const World_step_oldContacts = []; + const World_step_frictionEquationPool = []; // Reusable arrays for collision pairs + + const World_step_p1 = []; + const World_step_p2 = []; // Stuff for emitContactEvents + + const additions = []; + const removals = []; + const beginContactEvent = { + type: "beginContact", + bodyA: null, + bodyB: null, + }; + const endContactEvent = { + type: "endContact", + bodyA: null, + bodyB: null, + }; + const beginShapeContactEvent = { + type: "beginShapeContact", + bodyA: null, + bodyB: null, + shapeA: null, + shapeB: null, + }; + const endShapeContactEvent = { + type: "endShapeContact", + bodyA: null, + bodyB: null, + shapeA: null, + shapeB: null, + }; + + var AABB_1 = cannon.AABB = AABB; + var ArrayCollisionMatrix_1 = cannon.ArrayCollisionMatrix = ArrayCollisionMatrix; + var BODY_SLEEP_STATES_1 = cannon.BODY_SLEEP_STATES = BODY_SLEEP_STATES; + var BODY_TYPES_1 = cannon.BODY_TYPES = BODY_TYPES; + var Body_1 = cannon.Body = Body; + var Box_1 = cannon.Box = Box; + var Broadphase_1 = cannon.Broadphase = Broadphase; + var COLLISION_TYPES_1 = cannon.COLLISION_TYPES = COLLISION_TYPES; + var ConeTwistConstraint_1 = cannon.ConeTwistConstraint = ConeTwistConstraint; + var Constraint_1 = cannon.Constraint = Constraint; + var ContactEquation_1 = cannon.ContactEquation = ContactEquation; + var ContactMaterial_1 = cannon.ContactMaterial = ContactMaterial; + var ConvexPolyhedron_1 = cannon.ConvexPolyhedron = ConvexPolyhedron; + var Cylinder_1 = cannon.Cylinder = Cylinder; + var DistanceConstraint_1 = cannon.DistanceConstraint = DistanceConstraint; + var Equation_1 = cannon.Equation = Equation; + var EventTarget_1 = cannon.EventTarget = EventTarget; + var FrictionEquation_1 = cannon.FrictionEquation = FrictionEquation; + var GSSolver_1 = cannon.GSSolver = GSSolver; + var GridBroadphase_1 = cannon.GridBroadphase = GridBroadphase; + var Heightfield_1 = cannon.Heightfield = Heightfield; + var HingeConstraint_1 = cannon.HingeConstraint = HingeConstraint; + var JacobianElement_1 = cannon.JacobianElement = JacobianElement; + var LockConstraint_1 = cannon.LockConstraint = LockConstraint; + var Mat3_1 = cannon.Mat3 = Mat3; + var Material_1 = cannon.Material = Material; + var NaiveBroadphase_1 = cannon.NaiveBroadphase = NaiveBroadphase; + var Narrowphase_1 = cannon.Narrowphase = Narrowphase; + var ObjectCollisionMatrix_1 = cannon.ObjectCollisionMatrix = ObjectCollisionMatrix; + var Particle_1 = cannon.Particle = Particle; + var Plane_1 = cannon.Plane = Plane; + var PointToPointConstraint_1 = cannon.PointToPointConstraint = PointToPointConstraint; + var Pool_1 = cannon.Pool = Pool; + var Quaternion_1 = cannon.Quaternion = Quaternion; + var RAY_MODES_1 = cannon.RAY_MODES = RAY_MODES; + var Ray_1 = cannon.Ray = Ray; + var RaycastResult_1 = cannon.RaycastResult = RaycastResult; + var RaycastVehicle_1 = cannon.RaycastVehicle = RaycastVehicle; + var RigidVehicle_1 = cannon.RigidVehicle = RigidVehicle; + var RotationalEquation_1 = cannon.RotationalEquation = RotationalEquation; + var RotationalMotorEquation_1 = cannon.RotationalMotorEquation = RotationalMotorEquation; + var SAPBroadphase_1 = cannon.SAPBroadphase = SAPBroadphase; + var SHAPE_TYPES_1 = cannon.SHAPE_TYPES = SHAPE_TYPES; + var SPHSystem_1 = cannon.SPHSystem = SPHSystem; + var Shape_1 = cannon.Shape = Shape; + var Solver_1 = cannon.Solver = Solver; + var Sphere_1 = cannon.Sphere = Sphere; + var SplitSolver_1 = cannon.SplitSolver = SplitSolver; + var Spring_1 = cannon.Spring = Spring; + var Transform_1 = cannon.Transform = Transform; + var Trimesh_1 = cannon.Trimesh = Trimesh; + var Vec3_1 = cannon.Vec3 = Vec3; + var Vec3Pool_1 = cannon.Vec3Pool = Vec3Pool; + var WheelInfo_1 = cannon.WheelInfo = WheelInfo; + var World_1 = cannon.World = World; + + exports.AABB = AABB_1; + exports.ArrayCollisionMatrix = ArrayCollisionMatrix_1; + exports.BODY_SLEEP_STATES = BODY_SLEEP_STATES_1; + exports.BODY_TYPES = BODY_TYPES_1; + exports.Body = Body_1; + exports.Box = Box_1; + exports.Broadphase = Broadphase_1; + exports.COLLISION_TYPES = COLLISION_TYPES_1; + exports.ConeTwistConstraint = ConeTwistConstraint_1; + exports.Constraint = Constraint_1; + exports.ContactEquation = ContactEquation_1; + exports.ContactMaterial = ContactMaterial_1; + exports.ConvexPolyhedron = ConvexPolyhedron_1; + exports.Cylinder = Cylinder_1; + exports.DistanceConstraint = DistanceConstraint_1; + exports.Equation = Equation_1; + exports.EventTarget = EventTarget_1; + exports.FrictionEquation = FrictionEquation_1; + exports.GSSolver = GSSolver_1; + exports.GridBroadphase = GridBroadphase_1; + exports.Heightfield = Heightfield_1; + exports.HingeConstraint = HingeConstraint_1; + exports.JacobianElement = JacobianElement_1; + exports.LockConstraint = LockConstraint_1; + exports.Mat3 = Mat3_1; + exports.Material = Material_1; + exports.NaiveBroadphase = NaiveBroadphase_1; + exports.Narrowphase = Narrowphase_1; + exports.ObjectCollisionMatrix = ObjectCollisionMatrix_1; + exports.Particle = Particle_1; + exports.Plane = Plane_1; + exports.PointToPointConstraint = PointToPointConstraint_1; + exports.Pool = Pool_1; + exports.Quaternion = Quaternion_1; + exports.RAY_MODES = RAY_MODES_1; + exports.Ray = Ray_1; + exports.RaycastResult = RaycastResult_1; + exports.RaycastVehicle = RaycastVehicle_1; + exports.RigidVehicle = RigidVehicle_1; + exports.RotationalEquation = RotationalEquation_1; + exports.RotationalMotorEquation = RotationalMotorEquation_1; + exports.SAPBroadphase = SAPBroadphase_1; + exports.SHAPE_TYPES = SHAPE_TYPES_1; + exports.SPHSystem = SPHSystem_1; + exports.Shape = Shape_1; + exports.Solver = Solver_1; + exports.Sphere = Sphere_1; + exports.SplitSolver = SplitSolver_1; + exports.Spring = Spring_1; + exports.Transform = Transform_1; + exports.Trimesh = Trimesh_1; + exports.Vec3 = Vec3_1; + exports.Vec3Pool = Vec3Pool_1; + exports.WheelInfo = WheelInfo_1; + exports.World = World_1; + exports.default = cannon; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/activities/3DVolume.activity/js/debug.js b/activities/3DVolume.activity/js/debug.js new file mode 100644 index 000000000..4766e9f6d --- /dev/null +++ b/activities/3DVolume.activity/js/debug.js @@ -0,0 +1,306 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('cannon'), require('three')) : + typeof define === 'function' && define.amd ? define(['cannon', 'three'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.CannonDebugger = factory(global.CANNON, global.THREE)); + })(this, (function (cannon, three) { 'use strict'; + + function CannonDebugger(scene, world, _temp) { + let { + color = 0x00ff00, + scale = 1, + onInit, + onUpdate + } = _temp === void 0 ? {} : _temp; + const _meshes = []; + + const _material = new THREE.MeshBasicMaterial({ + color: color != null ? color : 0x00ff00, + wireframe: true + }); + + const _tempVec0 = new CANNON.Vec3(); + + const _tempVec1 = new CANNON.Vec3(); + + const _tempVec2 = new CANNON.Vec3(); + + const _tempQuat0 = new CANNON.Quaternion(); + + const _sphereGeometry = new THREE.SphereGeometry(1); + + const _boxGeometry = new THREE.BoxGeometry(1, 1, 1); + + const _planeGeometry = new THREE.PlaneGeometry(10, 10, 10, 10); // Move the planeGeometry forward a little bit to prevent z-fighting + + + _planeGeometry.translate(0, 0, 0.0001); + + function createConvexPolyhedronGeometry(shape) { + const geometry = new THREE.BufferGeometry(); // Add vertices + + const positions = []; + + for (let i = 0; i < shape.vertices.length; i++) { + const vertex = shape.vertices[i]; + positions.push(vertex.x, vertex.y, vertex.z); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); // Add faces + + const indices = []; + + for (let i = 0; i < shape.faces.length; i++) { + const face = shape.faces[i]; + const a = face[0]; + + for (let j = 1; j < face.length - 1; j++) { + const b = face[j]; + const c = face[j + 1]; + indices.push(a, b, c); + } + } + + geometry.setIndex(indices); + geometry.computeBoundingSphere(); + geometry.computeVertexNormals(); + return geometry; + } + + function createTrimeshGeometry(shape) { + const geometry = new THREE.BufferGeometry(); + const positions = []; + const v0 = _tempVec0; + const v1 = _tempVec1; + const v2 = _tempVec2; + + for (let i = 0; i < shape.indices.length / 3; i++) { + shape.getTriangleVertices(i, v0, v1, v2); + positions.push(v0.x, v0.y, v0.z); + positions.push(v1.x, v1.y, v1.z); + positions.push(v2.x, v2.y, v2.z); + } + + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.computeBoundingSphere(); + geometry.computeVertexNormals(); + return geometry; + } + + function createHeightfieldGeometry(shape) { + const geometry = new THREE.BufferGeometry(); + const s = shape.elementSize || 1; // assumes square heightfield, else i*x, j*y + + const positions = shape.data.flatMap((row, i) => row.flatMap((z, j) => [i * s, j * s, z])); + const indices = []; + + for (let xi = 0; xi < shape.data.length - 1; xi++) { + for (let yi = 0; yi < shape.data[xi].length - 1; yi++) { + const stride = shape.data[xi].length; + const index = xi * stride + yi; + indices.push(index + 1, index + stride, index + stride + 1); + indices.push(index + stride, index + 1, index); + } + } + + geometry.setIndex(indices); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.computeBoundingSphere(); + geometry.computeVertexNormals(); + return geometry; + } + + function createMesh(shape) { + let mesh = new THREE.Mesh(); + const { + SPHERE, + BOX, + PLANE, + CYLINDER, + CONVEXPOLYHEDRON, + TRIMESH, + HEIGHTFIELD + } = CANNON.Shape.types; + + switch (shape.type) { + case SPHERE: + { + mesh = new THREE.Mesh(_sphereGeometry, _material); + break; + } + + case BOX: + { + mesh = new THREE.Mesh(_boxGeometry, _material); + break; + } + + case PLANE: + { + mesh = new THREE.Mesh(_planeGeometry, _material); + break; + } + + case CYLINDER: + { + const geometry = new THREE.CylinderGeometry(shape.radiusTop, shape.radiusBottom, shape.height, shape.numSegments); + mesh = new THREE.Mesh(geometry, _material); + shape.geometryId = geometry.id; + break; + } + + case CONVEXPOLYHEDRON: + { + const geometry = createConvexPolyhedronGeometry(shape); + mesh = new THREE.Mesh(geometry, _material); + shape.geometryId = geometry.id; + break; + } + + case TRIMESH: + { + const geometry = createTrimeshGeometry(shape); + mesh = new THREE.Mesh(geometry, _material); + shape.geometryId = geometry.id; + break; + } + + case HEIGHTFIELD: + { + const geometry = createHeightfieldGeometry(shape); + mesh = new THREE.Mesh(geometry, _material); + shape.geometryId = geometry.id; + break; + } + } + + scene.add(mesh); + return mesh; + } + + function scaleMesh(mesh, shape) { + const { + SPHERE, + BOX, + PLANE, + CYLINDER, + CONVEXPOLYHEDRON, + TRIMESH, + HEIGHTFIELD + } = CANNON.Shape.types; + + switch (shape.type) { + case SPHERE: + { + const { + radius + } = shape; + mesh.scale.set(radius * scale, radius * scale, radius * scale); + break; + } + + case BOX: + { + mesh.scale.copy(shape.halfExtents); + mesh.scale.multiplyScalar(2 * scale); + break; + } + + case PLANE: + { + break; + } + + case CYLINDER: + { + mesh.scale.set(1 * scale, 1 * scale, 1 * scale); + break; + } + + case CONVEXPOLYHEDRON: + { + mesh.scale.set(1 * scale, 1 * scale, 1 * scale); + break; + } + + case TRIMESH: + { + mesh.scale.copy(shape.scale).multiplyScalar(scale); + break; + } + + case HEIGHTFIELD: + { + mesh.scale.set(1 * scale, 1 * scale, 1 * scale); + break; + } + } + } + + function typeMatch(mesh, shape) { + if (!mesh) return false; + const { + geometry + } = mesh; + return geometry instanceof THREE.SphereGeometry && shape.type === CANNON.Shape.types.SPHERE || geometry instanceof THREE.BoxGeometry && shape.type === CANNON.Shape.types.BOX || geometry instanceof THREE.PlaneGeometry && shape.type === CANNON.Shape.types.PLANE || geometry.id === shape.geometryId && shape.type === CANNON.Shape.types.CYLINDER || geometry.id === shape.geometryId && shape.type === CANNON.Shape.types.CONVEXPOLYHEDRON || geometry.id === shape.geometryId && shape.type === CANNON.Shape.types.TRIMESH || geometry.id === shape.geometryId && shape.type === CANNON.Shape.types.HEIGHTFIELD; + } + + function updateMesh(index, shape) { + let mesh = _meshes[index]; + let didCreateNewMesh = false; + + if (!typeMatch(mesh, shape)) { + if (mesh) scene.remove(mesh); + _meshes[index] = mesh = createMesh(shape); + didCreateNewMesh = true; + } + + scaleMesh(mesh, shape); + return didCreateNewMesh; + } + + function update() { + const meshes = _meshes; + const shapeWorldPosition = _tempVec0; + const shapeWorldQuaternion = _tempQuat0; + let meshIndex = 0; + + for (const body of world.bodies) { + for (let i = 0; i !== body.shapes.length; i++) { + const shape = body.shapes[i]; + const didCreateNewMesh = updateMesh(meshIndex, shape); + const mesh = meshes[meshIndex]; + + if (mesh) { + // Get world position + body.quaternion.vmult(body.shapeOffsets[i], shapeWorldPosition); + body.position.vadd(shapeWorldPosition, shapeWorldPosition); // Get world quaternion + + body.quaternion.mult(body.shapeOrientations[i], shapeWorldQuaternion); // Copy to meshes + + mesh.position.copy(shapeWorldPosition); + mesh.quaternion.copy(shapeWorldQuaternion); + if (didCreateNewMesh && onInit instanceof Function) onInit(body, mesh, shape); + if (!didCreateNewMesh && onUpdate instanceof Function) onUpdate(body, mesh, shape); + } + + meshIndex++; + } + } + + for (let i = meshIndex; i < meshes.length; i++) { + const mesh = meshes[i]; + if (mesh) scene.remove(mesh); + } + + meshes.length = meshIndex; + } + + return { + update + }; + } + + return CannonDebugger; + + })); + \ No newline at end of file diff --git a/activities/3DVolume.activity/js/fonts/robot.json b/activities/3DVolume.activity/js/fonts/robot.json new file mode 100644 index 000000000..4749dd566 --- /dev/null +++ b/activities/3DVolume.activity/js/fonts/robot.json @@ -0,0 +1 @@ +{"glyphs":{"0":{"ha":783,"x_min":77,"x_max":705,"o":"m 705 376 q 622 86 705 187 q 392 -14 538 -14 q 161 87 246 -14 q 77 376 77 188 l 77 610 q 161 900 77 799 q 391 1002 245 1002 q 621 900 536 1002 q 705 610 705 799 l 705 376 m 572 639 q 525 832 572 767 q 391 897 479 897 q 256 832 302 897 q 210 639 210 767 l 210 349 q 257 156 210 221 q 392 90 304 90 q 526 155 480 90 q 572 349 572 220 l 572 639 z "},"1":{"ha":782,"x_min":126,"x_max":462,"o":"m 462 0 l 328 0 l 328 857 l 126 854 l 126 951 l 462 987 l 462 0 z "},"2":{"ha":782,"x_min":62,"x_max":720,"o":"m 720 0 l 80 0 l 80 92 l 404 451 q 521 605 490 548 q 552 722 552 662 q 509 846 552 795 q 395 897 466 897 q 244 844 292 897 q 195 696 195 791 l 67 696 l 66 701 q 151 915 62 828 q 395 1002 239 1002 q 607 924 528 1002 q 686 726 686 846 q 638 565 686 646 q 504 391 590 484 l 244 108 l 245 104 l 720 104 l 720 0 z "},"3":{"ha":782,"x_min":64,"x_max":691,"o":"m 263 555 l 380 555 q 506 600 469 555 q 543 724 543 644 q 500 852 543 808 q 376 897 458 897 q 251 851 298 897 q 204 729 204 805 l 76 729 l 75 733 q 156 923 71 845 q 376 1002 240 1002 q 595 929 513 1002 q 676 721 676 856 q 639 599 676 660 q 528 507 602 538 q 654 415 617 477 q 691 275 691 353 q 603 63 691 140 q 376 -14 514 -14 q 153 59 241 -14 q 68 256 64 132 l 70 260 l 197 260 q 245 136 197 182 q 376 90 293 90 q 509 136 461 90 q 557 273 557 182 q 515 408 557 364 q 380 451 472 451 l 263 451 l 263 555 z "},"4":{"ha":782,"x_min":37,"x_max":750,"o":"m 614 332 l 750 332 l 750 228 l 614 228 l 614 0 l 481 0 l 481 228 l 37 228 l 37 303 l 473 987 l 614 987 l 614 332 m 180 332 l 481 332 l 481 795 l 477 796 l 464 762 l 180 332 z "},"5":{"ha":782,"x_min":103,"x_max":707,"o":"m 119 446 l 176 987 l 670 987 l 670 869 l 289 869 l 256 591 q 326 630 288 614 q 414 646 364 645 q 629 557 551 648 q 707 315 707 467 q 628 76 707 166 q 395 -14 548 -14 q 186 54 270 -14 q 106 256 103 123 l 108 260 l 229 260 q 275 135 229 179 q 395 90 321 90 q 527 150 480 90 q 574 313 574 210 q 526 469 574 408 q 395 530 479 530 q 281 507 317 530 q 230 434 246 483 l 119 446 z "},"6":{"ha":782,"x_min":90,"x_max":732,"o":"m 458 1002 q 558 990 509 1002 q 641 961 608 979 l 612 859 q 543 886 579 876 q 458 897 507 897 q 287 812 352 897 q 222 591 222 727 l 222 575 q 322 634 266 613 q 442 656 378 656 q 653 564 574 656 q 732 332 732 473 q 648 82 732 179 q 425 -14 564 -14 q 184 91 279 -14 q 90 387 90 196 l 90 576 q 195 884 90 766 q 458 1002 301 1002 m 412 556 q 296 528 344 556 q 222 454 248 500 l 222 374 q 280 164 222 238 q 425 90 338 90 q 552 160 507 90 q 598 332 598 229 q 549 493 598 430 q 412 556 500 556 z "},"7":{"ha":782,"x_min":52,"x_max":720,"o":"m 720 882 q 478 505 541 669 q 388 106 416 340 l 377 0 l 243 0 l 254 106 q 365 524 283 340 q 589 882 447 707 l 52 882 l 52 987 l 720 987 l 720 882 z "},"8":{"ha":782,"x_min":69,"x_max":712,"o":"m 685 730 q 642 594 685 652 q 524 508 598 536 q 661 414 610 478 q 712 267 712 351 q 623 58 712 130 q 391 -14 534 -14 q 158 58 246 -14 q 69 267 69 130 q 119 414 69 351 q 255 508 170 478 q 139 594 181 536 q 97 730 97 652 q 177 931 97 860 q 390 1002 258 1002 q 603 931 520 1002 q 685 730 685 860 m 579 270 q 526 404 579 351 q 390 456 472 456 q 255 404 307 456 q 203 270 203 351 q 254 138 203 186 q 391 90 306 90 q 527 138 475 90 q 579 270 579 186 m 551 727 q 506 848 551 800 q 390 897 460 897 q 275 850 319 897 q 231 727 231 804 q 275 606 231 652 q 391 561 319 561 q 507 606 462 561 q 551 727 551 652 z "},"9":{"ha":782,"x_min":56,"x_max":695,"o":"m 347 90 q 503 164 444 90 q 562 384 562 238 l 562 429 q 478 356 528 380 q 368 331 429 331 q 140 419 224 331 q 56 663 56 508 q 145 907 56 812 q 362 1002 235 1002 q 606 905 517 1002 q 695 621 695 808 l 695 385 q 598 89 695 192 q 347 -14 502 -14 q 240 -4 294 -14 q 144 26 186 5 l 164 128 q 247 99 204 107 q 347 90 290 90 m 368 435 q 491 467 441 435 q 562 549 540 499 l 562 634 q 512 830 562 764 q 366 897 462 897 q 241 831 293 897 q 190 663 190 766 q 238 498 190 562 q 368 435 286 435 z "},"\r":{"ha":345,"x_min":0,"x_max":0,"o":""}," ":{"ha":345,"x_min":0,"x_max":0,"o":""},"!":{"ha":366,"x_min":116,"x_max":250,"o":"m 250 324 l 116 324 l 116 987 l 250 987 l 250 324 m 250 0 l 116 0 l 116 138 l 250 138 l 250 0 z "},"\"":{"ha":453,"x_min":54,"x_max":398,"o":"m 189 875 l 120 705 l 54 705 l 55 868 l 55 1058 l 189 1058 l 189 875 m 398 875 l 330 705 l 264 705 l 264 873 l 264 1058 l 398 1058 l 398 875 z "},"#":{"ha":865,"x_min":47,"x_max":804,"o":"m 483 278 l 310 278 l 256 0 l 153 0 l 208 278 l 47 278 l 47 373 l 227 373 l 273 609 l 99 609 l 99 705 l 292 705 l 347 987 l 450 987 l 394 705 l 567 705 l 623 987 l 725 987 l 669 705 l 804 705 l 804 609 l 651 609 l 605 373 l 753 373 l 753 278 l 586 278 l 532 0 l 429 0 l 483 278 m 329 373 l 502 373 l 548 609 l 375 609 l 329 373 z "},"$":{"ha":782,"x_min":75,"x_max":709,"o":"m 575 255 q 532 359 575 315 q 389 436 488 402 q 182 547 252 477 q 113 731 113 616 q 177 913 113 843 q 353 998 241 984 l 353 1148 l 460 1148 l 460 998 q 637 900 574 981 q 700 683 700 819 l 567 683 q 524 832 567 775 q 406 889 481 889 q 286 847 326 889 q 246 733 246 806 q 287 627 246 667 q 435 549 328 586 q 642 438 574 505 q 709 256 709 371 q 639 71 709 140 q 447 -11 569 2 l 447 -141 l 340 -141 l 340 -12 q 149 74 224 1 q 77 288 75 146 l 79 291 l 208 291 q 260 142 208 186 q 389 98 313 98 q 526 139 477 98 q 575 255 575 181 z "},"%":{"ha":1016,"x_min":71,"x_max":957,"o":"m 71 798 q 126 943 71 884 q 275 1002 182 1002 q 423 943 368 1002 q 479 798 479 884 l 479 745 q 424 601 479 659 q 276 543 368 543 q 126 601 182 543 q 71 745 71 659 l 71 798 m 170 745 q 197 660 170 695 q 276 625 224 625 q 353 660 326 625 q 380 745 380 694 l 380 798 q 352 883 380 848 q 275 919 325 919 q 197 883 224 919 q 170 798 170 848 l 170 745 m 549 242 q 604 387 549 328 q 753 446 660 446 q 901 387 845 446 q 957 242 957 328 l 957 189 q 901 44 957 102 q 754 -14 846 -14 q 605 44 661 -14 q 549 189 549 102 l 549 242 m 648 189 q 675 103 648 138 q 754 68 703 68 q 831 103 804 68 q 858 189 858 138 l 858 242 q 830 328 858 292 q 753 363 802 363 q 675 328 703 363 q 648 242 648 292 l 648 189 m 311 75 l 237 120 l 719 892 l 793 846 l 311 75 z "},"&":{"ha":865,"x_min":43,"x_max":836,"o":"m 43 266 q 91 411 43 349 q 234 535 139 473 q 155 655 181 602 q 130 763 130 707 q 196 940 130 878 q 378 1002 262 1002 q 552 940 485 1002 q 619 791 619 878 q 584 676 619 725 q 478 577 548 628 l 404 523 l 635 245 q 678 343 663 290 q 694 456 694 397 l 813 456 q 787 291 813 367 q 710 155 760 215 l 836 3 l 834 0 l 679 0 l 621 69 q 501 7 567 28 q 365 -14 435 -14 q 131 64 218 -14 q 43 266 43 142 m 365 90 q 461 107 414 90 q 551 155 509 123 l 306 450 l 279 430 q 197 342 217 384 q 177 266 177 300 q 225 140 177 189 q 365 90 273 90 m 263 764 q 281 689 263 728 q 336 604 300 650 l 430 669 q 483 725 469 694 q 496 791 496 755 q 464 865 496 833 q 378 897 431 897 q 293 858 323 897 q 263 764 263 820 z "},"'":{"ha":243,"x_min":54,"x_max":189,"o":"m 189 907 l 120 715 l 54 715 l 55 895 l 55 1058 l 189 1058 l 189 907 z "},"(":{"ha":460,"x_min":90,"x_max":454,"o":"m 90 401 q 197 857 90 668 q 424 1109 305 1046 l 428 1109 l 454 1030 q 292 798 361 958 q 223 402 223 637 l 223 393 q 292 -2 223 158 q 454 -241 360 -162 l 428 -314 l 424 -314 q 197 -62 305 -251 q 90 395 90 127 l 90 401 z "},")":{"ha":466,"x_min":4,"x_max":368,"o":"m 368 395 q 260 -62 368 127 q 34 -314 152 -251 l 30 -314 l 4 -241 q 165 -6 96 -170 q 235 393 235 157 l 235 402 q 163 798 235 634 q 4 1036 92 962 l 30 1109 l 34 1109 q 260 857 152 1046 q 368 401 368 668 l 368 395 z "},"*":{"ha":600,"x_min":19,"x_max":580,"o":"m 220 666 l 19 726 l 52 831 l 253 755 l 246 987 l 355 987 l 349 751 l 547 826 l 580 720 l 375 660 l 506 477 l 417 412 l 294 606 l 175 417 l 85 480 l 220 666 z "},"+":{"ha":788,"x_min":53,"x_max":730,"o":"m 459 531 l 730 531 l 730 410 l 459 410 l 459 99 l 326 99 l 326 410 l 53 410 l 53 531 l 326 531 l 326 818 l 459 818 l 459 531 z "},",":{"ha":274,"x_min":33,"x_max":214,"o":"m 214 33 l 112 -175 l 33 -175 l 80 39 l 80 150 l 214 150 l 214 33 z "},"-":{"ha":380,"x_min":24,"x_max":356,"o":"m 356 365 l 24 365 l 24 469 l 356 469 l 356 365 z "},".":{"ha":372,"x_min":109,"x_max":243,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 z "},"/":{"ha":576,"x_min":11,"x_max":536,"o":"m 125 -85 l 11 -85 l 423 987 l 536 987 l 125 -85 z "},":":{"ha":351,"x_min":109,"x_max":243,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 243 594 l 109 594 l 109 731 l 243 731 l 243 594 z "},";":{"ha":356,"x_min":67,"x_max":248,"o":"m 243 594 l 110 594 l 110 731 l 243 731 l 243 594 m 248 33 l 146 -175 l 67 -175 l 115 39 l 115 150 l 248 150 l 248 33 z "},"<":{"ha":705,"x_min":48,"x_max":602,"o":"m 222 379 l 165 367 l 165 363 l 222 350 l 602 195 l 602 59 l 48 316 l 48 417 l 602 673 l 602 537 l 222 379 z "},"=":{"ha":782,"x_min":103,"x_max":669,"o":"m 669 558 l 103 558 l 103 669 l 669 669 l 669 558 m 669 276 l 103 276 l 103 387 l 669 387 l 669 276 z "},">":{"ha":727,"x_min":92,"x_max":673,"o":"m 92 541 l 92 673 l 673 417 l 673 316 l 92 59 l 92 192 l 498 353 l 555 365 l 555 369 l 498 382 l 92 541 z "},"?":{"ha":661,"x_min":39,"x_max":601,"o":"m 241 278 q 258 414 241 376 q 343 515 274 452 q 439 637 410 594 q 467 740 467 680 q 429 850 467 812 q 319 889 391 889 q 214 856 258 889 q 171 757 171 822 l 43 757 l 41 761 q 118 936 39 870 q 319 1002 197 1002 q 527 933 453 1002 q 601 743 601 865 q 553 583 601 656 q 427 435 505 511 q 382 369 390 398 q 374 278 374 340 l 241 278 m 379 0 l 239 0 l 239 141 l 379 141 l 379 0 z "},"@":{"ha":1243,"x_min":65,"x_max":1186,"o":"m 1175 340 q 1093 91 1168 195 q 868 -14 1018 -14 q 783 14 819 -14 q 731 94 747 42 q 648 13 697 39 q 534 -14 600 -14 q 403 67 450 -14 q 368 282 356 149 q 461 564 384 458 q 651 670 539 670 q 766 652 722 670 q 860 598 809 635 l 857 595 l 861 595 l 827 199 q 841 96 821 124 q 897 68 862 68 q 1030 145 980 68 q 1086 340 1080 222 q 988 744 1097 600 q 652 889 880 889 q 316 732 443 889 q 178 324 189 576 q 280 -79 166 69 q 607 -227 394 -227 q 728 -213 667 -227 q 831 -174 789 -198 l 857 -247 q 742 -291 812 -275 q 604 -307 671 -307 q 206 -138 347 -307 q 77 324 65 31 q 246 791 90 611 q 654 970 402 970 q 1049 801 911 970 q 1175 340 1186 631 m 488 282 q 503 136 481 186 q 575 85 525 85 q 654 102 618 85 q 720 161 690 119 q 720 179 720 170 q 722 199 720 188 l 753 564 q 716 576 736 572 q 676 581 697 581 q 547 507 591 581 q 488 282 502 433 z "},"A":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"B":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"C":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 z "},"D":{"ha":915,"x_min":122,"x_max":854,"o":"m 122 0 l 122 987 l 425 987 q 736 868 619 987 q 854 561 854 749 l 854 426 q 736 118 854 237 q 425 0 619 0 l 122 0 m 256 882 l 256 104 l 425 104 q 641 195 562 104 q 720 426 720 285 l 720 562 q 641 792 720 702 q 425 882 562 882 l 256 882 z "},"E":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"F":{"ha":809,"x_min":122,"x_max":775,"o":"m 706 437 l 256 437 l 256 0 l 122 0 l 122 987 l 775 987 l 775 882 l 256 882 l 256 542 l 706 542 l 706 437 z "},"G":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 z "},"H":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"I":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"J":{"ha":766,"x_min":41,"x_max":653,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 z "},"K":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"L":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 z "},"M":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"N":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 z "},"O":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"P":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"Q":{"ha":947,"x_min":77,"x_max":908,"o":"m 869 406 q 836 227 869 309 q 741 88 802 145 l 908 -70 l 817 -157 l 629 17 q 550 -6 591 1 q 466 -14 509 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"R":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 z "},"S":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 z "},"T":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"U":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 z "},"V":{"ha":878,"x_min":15,"x_max":863,"o":"m 415 245 l 437 168 l 441 168 l 464 245 l 719 987 l 863 987 l 496 0 l 382 0 l 15 987 l 160 987 l 415 245 z "},"W":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 z "},"X":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Y":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"Z":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 z "},"[":{"ha":374,"x_min":97,"x_max":358,"o":"m 358 1023 l 231 1023 l 231 -106 l 358 -106 l 358 -212 l 97 -212 l 97 1128 l 358 1128 l 358 1023 z "},"\\":{"ha":574,"x_min":26,"x_max":565,"o":"m 26 987 l 153 987 l 565 -85 l 439 -85 l 26 987 z "},"]":{"ha":374,"x_min":7,"x_max":269,"o":"m 7 1128 l 269 1128 l 269 -212 l 7 -212 l 7 -106 l 136 -106 l 136 1023 l 7 1023 l 7 1128 z "},"^":{"ha":581,"x_min":41,"x_max":537,"o":"m 165 494 l 41 494 l 244 987 l 335 987 l 537 494 l 414 494 l 302 779 l 291 826 l 287 826 l 276 779 l 165 494 z "},"_":{"ha":631,"x_min":3,"x_max":629,"o":"m 629 -104 l 3 -104 l 3 0 l 629 0 l 629 -104 z "},"`":{"ha":435,"x_min":56,"x_max":332,"o":"m 332 821 l 225 821 l 56 998 l 58 1002 l 214 1002 l 332 821 z "},"a":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 z "},"b":{"ha":789,"x_min":97,"x_max":724,"o":"m 724 339 q 647 83 724 180 q 437 -14 571 -14 q 311 14 365 -14 q 222 97 258 42 l 206 0 l 97 0 l 97 1058 l 231 1058 l 231 647 q 316 722 265 696 q 436 747 368 747 q 648 639 572 747 q 724 353 724 530 l 724 339 m 590 353 q 544 559 590 479 q 402 639 497 639 q 298 609 340 639 q 231 528 256 578 l 231 210 q 298 125 256 155 q 404 94 340 94 q 544 161 498 94 q 590 339 590 229 l 590 353 z "},"c":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 z "},"d":{"ha":789,"x_min":66,"x_max":687,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"e":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 z "},"f":{"ha":479,"x_min":38,"x_max":484,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 z "},"g":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 z "},"h":{"ha":789,"x_min":97,"x_max":694,"o":"m 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 1058 l 231 1058 l 231 635 z "},"i":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 z "},"j":{"ha":359,"x_min":-45,"x_max":251,"o":"m 251 734 l 251 -60 q 193 -235 251 -174 q 31 -296 134 -296 q -8 -293 9 -296 q -45 -284 -25 -290 l -35 -179 q -8 -185 -26 -182 q 21 -187 9 -187 q 91 -157 65 -187 q 117 -60 117 -127 l 117 734 l 251 734 m 247 924 l 113 924 l 113 1058 l 247 1058 l 247 924 z "},"k":{"ha":712,"x_min":98,"x_max":713,"o":"m 318 338 l 231 338 l 231 0 l 98 0 l 98 1058 l 231 1058 l 231 445 l 317 445 l 517 734 l 677 734 l 427 400 l 713 0 l 556 0 l 318 338 z "},"l":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 z "},"m":{"ha":1214,"x_min":97,"x_max":1117,"o":"m 216 734 l 226 637 q 317 719 262 690 q 446 747 372 747 q 571 713 519 747 q 650 612 624 680 q 742 711 685 674 q 875 747 799 747 q 1052 670 987 747 q 1117 439 1117 593 l 1117 0 l 983 0 l 983 440 q 946 594 983 549 q 835 639 909 639 q 724 591 766 639 q 674 471 682 544 l 674 466 l 674 0 l 540 0 l 540 440 q 502 591 540 543 q 391 639 463 639 q 291 614 330 639 q 231 543 252 589 l 231 0 l 97 0 l 97 734 l 216 734 z "},"n":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 z "},"o":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"p":{"ha":789,"x_min":97,"x_max":722,"o":"m 722 339 q 646 83 722 180 q 437 -14 570 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 199 734 l 220 639 q 310 720 256 692 q 435 747 363 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 540 160 491 90 q 589 339 589 229 l 589 353 z "},"q":{"ha":789,"x_min":66,"x_max":680,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 472 722 421 747 q 558 648 523 696 l 578 734 l 680 734 l 680 -282 l 546 -282 l 546 69 q 463 7 511 28 q 353 -14 414 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 115 441 90 q 546 183 519 139 l 546 553 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 z "},"r":{"ha":476,"x_min":97,"x_max":463,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 z "},"s":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 z "},"t":{"ha":480,"x_min":23,"x_max":419,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 z "},"u":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 z "},"v":{"ha":699,"x_min":31,"x_max":675,"o":"m 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 406 0 l 304 0 l 31 734 l 168 734 l 342 216 z "},"w":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 z "},"x":{"ha":699,"x_min":31,"x_max":665,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 z "},"y":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 z "},"z":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 z "},"{":{"ha":472,"x_min":43,"x_max":455,"o":"m 428 -247 q 240 -128 296 -210 q 184 68 184 -47 l 184 208 q 150 326 184 283 q 43 368 115 368 l 43 468 q 150 510 115 468 q 184 628 184 551 l 184 768 q 240 965 184 884 q 428 1083 296 1046 l 455 1004 q 349 919 380 980 q 318 768 318 857 l 318 628 q 289 503 318 557 q 203 418 260 448 q 289 332 260 387 q 318 208 318 277 l 318 68 q 349 -82 318 -21 q 455 -167 380 -142 l 428 -247 z "},"|":{"ha":344,"x_min":119,"x_max":226,"o":"m 226 -183 l 119 -183 l 119 987 l 226 987 l 226 -183 z "},"}":{"ha":472,"x_min":14,"x_max":427,"o":"m 14 -167 q 120 -82 88 -142 q 152 68 152 -21 l 152 208 q 182 334 152 280 q 277 418 213 389 q 182 500 213 446 q 152 628 152 554 l 152 768 q 120 919 152 857 q 14 1004 88 980 l 42 1083 q 230 965 174 1046 q 286 768 286 884 l 286 628 q 320 510 286 551 q 427 468 354 468 l 427 368 q 320 326 354 368 q 286 208 286 283 l 286 68 q 230 -128 286 -47 q 42 -247 174 -210 l 14 -167 z "},"~":{"ha":943,"x_min":87,"x_max":857,"o":"m 857 502 q 798 340 857 409 q 651 272 739 272 q 540 294 590 272 q 433 367 490 317 q 359 416 393 399 q 292 432 326 432 q 215 396 247 432 q 182 309 182 360 l 87 321 q 145 479 87 414 q 292 545 203 545 q 403 521 352 545 q 511 450 454 498 q 583 400 551 416 q 651 385 616 385 q 729 423 696 385 q 762 514 762 462 l 857 502 z "}," ":{"ha":345,"x_min":0,"x_max":0,"o":""},"¡":{"ha":344,"x_min":98,"x_max":231,"o":"m 231 -254 l 98 -254 l 98 410 l 231 410 l 231 -254 m 231 594 l 98 594 l 98 734 l 231 734 l 231 594 z "},"¢":{"ha":761,"x_min":73,"x_max":694,"o":"m 402 90 q 519 131 469 90 q 570 232 570 172 l 690 232 l 692 228 q 626 77 694 145 q 458 -9 557 8 l 458 -166 l 324 -166 l 324 -6 q 137 117 201 18 q 73 353 73 216 l 73 381 q 137 615 73 516 q 324 739 201 713 l 324 894 l 458 894 l 458 743 q 630 654 565 726 q 692 484 694 582 l 691 481 l 570 481 q 522 595 570 548 q 402 642 475 642 q 252 567 297 642 q 207 381 207 491 l 207 353 q 252 165 207 240 q 402 90 296 90 z "},"£":{"ha":810,"x_min":47,"x_max":753,"o":"m 292 417 l 296 316 q 285 200 296 255 q 255 104 275 144 l 753 104 l 753 0 l 91 0 l 91 104 l 98 104 q 146 180 130 113 q 163 316 163 246 l 159 417 l 47 417 l 47 522 l 155 522 l 148 705 q 224 923 148 844 q 427 1002 300 1002 q 637 931 563 1002 q 709 743 712 860 l 708 739 l 579 739 q 536 858 579 819 q 427 897 494 897 q 321 846 360 897 q 281 705 281 795 l 288 522 l 572 522 l 572 417 l 292 417 z "},"¤":{"ha":987,"x_min":71,"x_max":929,"o":"m 744 73 q 629 9 692 31 q 498 -14 566 -14 q 367 8 429 -14 q 253 72 304 31 l 165 -18 l 71 76 l 164 171 q 112 284 130 222 q 94 412 94 345 q 114 545 94 481 q 170 662 133 609 l 71 763 l 165 857 l 262 758 q 372 816 312 795 q 498 837 433 837 q 624 816 564 837 q 735 757 684 795 l 834 858 l 929 763 l 827 659 q 882 544 863 606 q 901 412 901 481 q 883 285 901 346 q 833 173 865 224 l 929 76 l 834 -18 l 744 73 m 218 412 q 299 195 218 285 q 498 105 381 105 q 695 195 613 105 q 777 412 777 285 q 695 628 777 538 q 498 718 613 718 q 299 628 381 718 q 218 412 218 538 z "},"¥":{"ha":843,"x_min":20,"x_max":813,"o":"m 417 538 l 661 987 l 813 987 l 530 500 l 741 500 l 741 395 l 481 395 l 481 304 l 741 304 l 741 199 l 481 199 l 481 0 l 348 0 l 348 199 l 94 199 l 94 304 l 348 304 l 348 395 l 94 395 l 94 500 l 304 500 l 20 987 l 174 987 l 417 538 z "},"¦":{"ha":338,"x_min":98,"x_max":232,"o":"m 98 -183 l 98 354 l 232 354 l 232 -183 l 98 -183 m 232 473 l 98 473 l 98 987 l 232 987 l 232 473 z "},"§":{"ha":854,"x_min":61,"x_max":779,"o":"m 779 292 q 746 181 779 229 q 654 106 713 134 q 725 26 701 72 q 749 -87 749 -20 q 659 -270 749 -203 q 416 -336 568 -336 q 163 -266 266 -336 q 64 -47 61 -197 l 66 -43 l 193 -42 q 259 -185 193 -139 q 416 -231 324 -231 q 562 -190 509 -231 q 615 -88 615 -150 q 566 8 615 -26 q 379 85 517 42 q 140 191 217 127 q 64 374 64 254 q 96 483 64 435 q 186 559 127 531 q 117 640 140 593 q 94 753 94 687 q 185 933 94 865 q 428 1002 276 1002 q 674 926 586 1002 q 760 713 762 850 l 758 709 l 630 709 q 576 843 630 789 q 428 897 523 897 q 279 856 330 897 q 228 754 228 816 q 273 653 228 687 q 462 581 319 620 q 703 472 627 534 q 779 292 779 411 m 409 467 q 349 484 377 475 q 296 503 321 492 q 222 456 247 490 q 197 375 197 422 q 243 272 197 307 q 432 197 290 237 q 495 178 470 186 q 543 163 520 171 q 618 210 591 176 q 646 290 646 243 q 596 388 646 352 q 409 467 546 425 z "},"¨":{"ha":692,"x_min":115,"x_max":583,"o":"m 583 852 l 434 852 l 434 987 l 583 987 l 583 852 m 264 852 l 115 852 l 115 987 l 264 987 l 264 852 z "},"©":{"ha":1088,"x_min":60,"x_max":1022,"o":"m 753 404 l 755 400 q 698 244 758 298 q 532 191 638 191 q 359 263 423 191 q 296 454 296 336 l 296 534 q 359 724 296 652 q 532 797 423 797 q 698 743 638 797 q 755 589 758 690 l 754 585 l 655 585 q 624 679 655 649 q 532 708 593 708 q 433 660 468 708 q 399 535 399 612 l 399 454 q 433 327 399 374 q 532 279 468 279 q 624 309 593 279 q 654 404 654 338 l 753 404 m 142 494 q 258 191 142 315 q 541 68 374 68 q 824 191 707 68 q 941 494 941 315 q 824 795 941 673 q 541 918 707 918 q 258 795 374 918 q 142 494 142 673 m 60 494 q 200 854 60 708 q 541 1001 340 1001 q 882 854 741 1001 q 1022 494 1022 708 q 881 133 1022 280 q 541 -14 741 -14 q 200 133 340 -14 q 60 494 60 280 z "},"ª":{"ha":622,"x_min":81,"x_max":534,"o":"m 416 479 q 407 509 410 493 q 401 543 404 526 q 341 490 379 511 q 250 469 302 469 q 125 511 170 469 q 81 624 81 552 q 139 739 81 699 q 305 780 196 780 l 399 780 l 399 815 q 379 881 399 858 q 319 904 359 904 q 249 886 274 904 q 224 834 224 867 l 115 843 l 114 847 q 167 958 110 913 q 319 1002 224 1002 q 463 954 410 1002 q 516 814 516 905 l 516 602 q 520 538 516 568 q 534 479 524 508 l 416 479 m 279 564 q 350 584 313 564 q 399 628 387 604 l 399 703 l 306 703 q 227 680 255 703 q 199 627 199 658 q 218 580 199 596 q 279 564 238 564 z "},"«":{"ha":655,"x_min":66,"x_max":593,"o":"m 194 350 l 367 80 l 267 80 l 66 344 l 66 357 l 267 621 l 367 621 l 194 350 m 420 350 l 593 80 l 493 80 l 293 344 l 293 357 l 493 621 l 593 621 l 420 350 z "},"¬":{"ha":771,"x_min":86,"x_max":652,"o":"m 652 254 l 518 254 l 518 432 l 86 432 l 86 544 l 652 544 l 652 254 z "},"­":{"ha":380,"x_min":24,"x_max":356,"o":"m 356 365 l 24 365 l 24 469 l 356 469 l 356 365 z "},"®":{"ha":1088,"x_min":60,"x_max":1022,"o":"m 60 494 q 200 854 60 708 q 541 1001 340 1001 q 882 854 741 1001 q 1022 494 1022 708 q 881 133 1022 280 q 541 -14 741 -14 q 200 133 340 -14 q 60 494 60 280 m 142 494 q 258 191 142 314 q 541 68 374 68 q 823 191 707 68 q 940 494 940 315 q 824 796 940 673 q 541 918 707 918 q 258 796 374 918 q 142 494 142 673 m 443 444 l 443 214 l 342 214 l 342 791 l 532 791 q 693 746 635 791 q 752 616 752 702 q 730 543 752 574 q 665 490 707 511 q 729 436 709 472 q 749 349 749 400 l 749 311 q 752 261 749 283 q 761 225 754 239 l 761 214 l 657 214 q 650 256 651 229 q 648 312 648 283 l 648 349 q 626 420 648 397 q 551 444 603 444 l 443 444 m 443 532 l 546 532 q 621 554 590 532 q 652 613 652 575 q 625 683 652 663 q 532 703 598 703 l 443 703 l 443 532 z "},"¯":{"ha":644,"x_min":83,"x_max":572,"o":"m 572 888 l 83 888 l 83 987 l 572 987 l 572 888 z "},"°":{"ha":517,"x_min":87,"x_max":432,"o":"m 87 825 q 138 949 87 897 q 261 1002 190 1002 q 381 949 331 1002 q 432 825 432 897 q 382 701 432 751 q 261 650 332 650 q 138 701 189 650 q 87 825 87 751 m 176 825 q 200 763 176 787 q 261 739 225 739 q 320 763 296 739 q 345 825 345 787 q 320 887 345 862 q 261 913 296 913 q 200 887 225 913 q 176 825 176 862 z "},"±":{"ha":744,"x_min":67,"x_max":688,"o":"m 446 581 l 688 581 l 688 476 l 446 476 l 446 196 l 326 196 l 326 476 l 67 476 l 67 581 l 326 581 l 326 859 l 446 859 l 446 581 m 659 3 l 92 3 l 92 108 l 659 108 l 659 3 z "},"²":{"ha":589,"x_min":77,"x_max":484,"o":"m 484 452 l 83 452 l 83 541 l 288 718 q 349 784 334 759 q 364 838 364 810 q 345 893 364 872 q 286 914 326 914 q 216 892 241 914 q 191 837 191 871 l 82 837 l 81 841 q 134 956 77 908 q 286 1003 191 1003 q 429 960 378 1003 q 481 838 481 916 q 451 745 481 783 q 342 636 420 707 l 238 545 l 239 541 l 484 541 l 484 452 z "},"³":{"ha":594,"x_min":72,"x_max":502,"o":"m 288 771 q 355 790 333 771 q 377 845 377 810 q 352 894 377 874 q 281 913 328 913 q 219 897 243 913 q 196 854 196 881 l 86 854 l 85 858 q 138 962 81 922 q 281 1002 195 1002 q 436 962 379 1002 q 493 847 493 922 q 469 779 493 810 q 403 730 445 748 q 476 682 450 715 q 502 604 502 650 q 441 486 502 528 q 281 444 380 444 q 133 484 195 444 q 76 598 72 523 l 77 602 l 187 602 q 213 552 187 571 q 281 532 238 532 q 358 552 330 532 q 387 605 387 572 q 362 666 387 647 q 288 686 337 686 l 198 686 l 198 771 l 288 771 z "},"´":{"ha":444,"x_min":89,"x_max":372,"o":"m 214 1002 l 370 1002 l 372 998 l 189 821 l 89 821 l 214 1002 z "},"µ":{"ha":789,"x_min":104,"x_max":685,"o":"m 237 734 l 237 298 q 276 134 238 178 q 381 90 313 90 q 489 115 448 90 q 551 186 530 139 l 551 734 l 685 734 l 685 0 l 565 0 l 559 73 q 486 8 529 31 q 387 -14 443 -14 q 301 -3 338 -14 q 237 32 264 8 l 237 -282 l 104 -282 l 104 734 l 237 734 z "},"¶":{"ha":682,"x_min":43,"x_max":567,"o":"m 433 0 l 433 353 l 376 353 q 131 440 218 353 q 43 670 43 528 q 131 899 43 810 q 376 987 218 987 l 567 987 l 567 0 l 433 0 z "},"·":{"ha":366,"x_min":109,"x_max":243,"o":"m 243 423 l 109 423 l 109 567 l 243 567 l 243 423 z "},"¸":{"ha":345,"x_min":81,"x_max":292,"o":"m 198 0 l 190 -35 q 263 -71 234 -43 q 292 -153 292 -98 q 239 -256 292 -218 q 85 -295 185 -295 l 81 -221 q 159 -204 130 -221 q 189 -155 189 -188 q 165 -109 189 -122 q 81 -92 140 -96 l 103 0 l 198 0 z "},"¹":{"ha":378,"x_min":64,"x_max":269,"o":"m 269 451 l 151 451 l 151 895 l 64 895 l 64 986 l 269 1002 l 269 451 z "},"º":{"ha":633,"x_min":81,"x_max":550,"o":"m 81 774 q 145 938 81 875 q 315 1002 209 1002 q 486 938 422 1002 q 550 774 550 875 l 550 695 q 487 531 550 594 q 317 469 423 469 q 145 531 210 469 q 81 695 81 594 l 81 774 m 199 695 q 229 600 199 635 q 317 564 258 564 q 403 600 373 564 q 433 695 433 636 l 433 774 q 403 868 433 831 q 315 904 372 904 q 229 868 258 904 q 199 774 199 831 l 199 695 z "},"»":{"ha":655,"x_min":75,"x_max":610,"o":"m 175 644 l 375 380 l 375 367 l 175 103 l 75 103 l 248 373 l 75 644 l 175 644 m 410 644 l 610 380 l 610 367 l 410 103 l 309 103 l 482 373 l 309 644 l 410 644 z "},"¼":{"ha":1082,"x_min":125,"x_max":1021,"o":"m 329 450 l 211 450 l 211 895 l 125 895 l 125 985 l 329 1001 l 329 450 m 304 80 l 230 125 l 712 897 l 786 852 l 304 80 m 936 191 l 1021 191 l 1021 103 l 936 103 l 936 0 l 821 0 l 821 103 l 538 103 l 532 172 l 819 543 l 936 543 l 936 191 m 656 191 l 821 191 l 821 401 l 817 402 l 808 387 l 656 191 z "},"½":{"ha":1158,"x_min":125,"x_max":1034,"o":"m 313 80 l 239 125 l 721 897 l 795 852 l 313 80 m 329 450 l 211 450 l 211 895 l 125 895 l 125 985 l 329 1001 l 329 450 m 1034 0 l 633 0 l 633 88 l 838 266 q 899 332 884 307 q 914 386 914 357 q 895 441 914 420 q 836 462 876 462 q 766 440 791 462 q 741 385 741 418 l 632 385 l 631 389 q 684 503 627 456 q 836 551 741 551 q 979 507 928 551 q 1031 385 1031 464 q 1001 293 1031 331 q 892 184 970 255 l 788 92 l 789 88 l 1034 88 l 1034 0 z "},"¾":{"ha":1208,"x_min":83,"x_max":1149,"o":"m 428 80 l 354 125 l 836 897 l 910 852 l 428 80 m 1064 191 l 1149 191 l 1149 103 l 1064 103 l 1064 0 l 949 0 l 949 103 l 666 103 l 661 172 l 947 543 l 1064 543 l 1064 191 m 784 191 l 949 191 l 949 401 l 945 402 l 936 387 l 784 191 m 298 772 q 366 791 344 772 q 388 846 388 810 q 363 895 388 875 q 292 914 338 914 q 230 898 254 914 q 207 854 207 882 l 97 854 l 96 859 q 149 963 92 922 q 292 1003 206 1003 q 447 963 390 1003 q 504 848 504 922 q 480 780 504 810 q 414 731 456 749 q 487 683 461 715 q 513 604 513 650 q 452 487 513 529 q 292 445 391 445 q 144 484 205 445 q 87 599 83 524 l 87 603 l 198 603 q 223 552 198 572 q 292 533 249 533 q 369 553 340 533 q 397 606 397 573 q 373 667 397 648 q 298 686 348 686 l 209 686 l 209 772 l 298 772 z "},"¿":{"ha":687,"x_min":77,"x_max":638,"o":"m 437 456 q 420 319 436 357 q 335 219 404 281 q 239 96 267 139 q 211 -5 211 54 q 249 -116 211 -77 q 359 -155 287 -155 q 464 -121 420 -155 q 507 -22 507 -87 l 635 -22 l 637 -26 q 559 -201 638 -136 q 359 -267 480 -267 q 151 -199 224 -267 q 77 -9 77 -131 q 125 150 77 77 q 252 299 172 222 q 296 364 288 335 q 304 456 304 393 l 437 456 m 299 734 l 439 734 l 439 593 l 299 593 l 299 734 z "},"À":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 533 1058 l 426 1058 l 256 1234 l 258 1238 l 414 1238 l 533 1058 z "},"Á":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 541 1236 l 697 1236 l 699 1232 l 515 1055 l 416 1055 l 541 1236 z "},"Â":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 658 1103 l 658 1086 l 549 1086 l 449 1186 l 350 1086 l 241 1086 l 241 1103 l 408 1264 l 490 1264 l 658 1103 z "},"Ã":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 685 1251 q 645 1143 685 1187 q 543 1099 604 1099 q 443 1130 495 1099 q 356 1162 391 1162 q 307 1140 327 1162 q 288 1086 288 1118 l 214 1104 q 254 1214 214 1167 q 356 1260 294 1260 q 451 1228 394 1260 q 543 1196 508 1196 q 591 1218 571 1196 q 612 1272 612 1240 l 685 1251 z "},"Ä":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 684 1088 l 535 1088 l 535 1224 l 684 1224 l 684 1088 m 365 1088 l 216 1088 l 216 1224 l 365 1224 l 365 1088 z "},"Å":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 310 1176 q 351 1272 310 1233 q 451 1310 392 1310 q 549 1272 509 1310 q 590 1176 590 1234 q 550 1081 590 1118 q 451 1044 509 1044 q 351 1081 392 1044 q 310 1176 310 1118 m 380 1176 q 401 1126 380 1147 q 451 1105 422 1105 q 500 1125 479 1105 q 520 1176 520 1145 q 500 1227 520 1206 q 451 1249 479 1249 q 401 1227 422 1249 q 380 1176 380 1206 z "},"Æ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 z "},"Ç":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 511 -5 l 503 -41 q 576 -76 547 -48 q 606 -158 606 -104 q 552 -262 606 -223 q 399 -300 498 -300 l 394 -227 q 473 -210 443 -227 q 503 -160 503 -193 q 478 -115 503 -127 q 395 -97 454 -102 l 416 -5 l 511 -5 z "},"È":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 482 1058 l 375 1058 l 205 1234 l 208 1238 l 363 1238 l 482 1058 z "},"É":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 490 1236 l 646 1236 l 648 1232 l 465 1055 l 365 1055 l 490 1236 z "},"Ê":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 635 1103 l 635 1086 l 526 1086 l 426 1186 l 327 1086 l 218 1086 l 218 1103 l 385 1264 l 467 1264 l 635 1103 z "},"Ë":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 660 1088 l 511 1088 l 511 1224 l 660 1224 l 660 1088 m 341 1088 l 193 1088 l 193 1224 l 341 1224 l 341 1088 z "},"Ì":{"ha":393,"x_min":-23,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 254 1058 l 146 1058 l -23 1234 l -21 1238 l 135 1238 l 254 1058 z "},"Í":{"ha":393,"x_min":129,"x_max":418,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 260 1236 l 416 1236 l 418 1232 l 235 1055 l 135 1055 l 260 1236 z "},"Î":{"ha":393,"x_min":-10,"x_max":406,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 406 1103 l 406 1086 l 297 1086 l 197 1186 l 98 1086 l -10 1086 l -10 1103 l 157 1264 l 238 1264 l 406 1103 z "},"Ï":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ð":{"ha":935,"x_min":28,"x_max":874,"o":"m 142 0 l 142 450 l 28 450 l 28 555 l 142 555 l 142 987 l 446 987 q 757 868 640 987 q 874 561 874 749 l 874 426 q 757 118 874 237 q 446 0 640 0 l 142 0 m 463 450 l 276 450 l 276 104 l 446 104 q 662 195 583 104 q 741 426 741 285 l 741 562 q 662 792 741 702 q 446 882 583 882 l 276 882 l 276 555 l 463 555 l 463 450 z "},"Ñ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 729 1251 q 689 1143 729 1187 q 587 1099 648 1099 q 487 1130 539 1099 q 400 1162 435 1162 q 351 1140 371 1162 q 332 1086 332 1118 l 258 1104 q 298 1214 258 1167 q 400 1260 338 1260 q 495 1228 438 1260 q 587 1196 552 1196 q 635 1218 615 1196 q 656 1272 656 1240 l 729 1251 z "},"Ò":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 530 1072 l 422 1072 l 253 1249 l 255 1253 l 411 1253 l 530 1072 z "},"Ó":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 538 1250 l 694 1250 l 695 1246 l 512 1069 l 412 1069 l 538 1250 z "},"Ô":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 682 1117 l 682 1100 l 573 1100 l 473 1200 l 374 1100 l 266 1100 l 266 1118 l 433 1278 l 514 1278 l 682 1117 z "},"Õ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 709 1265 q 668 1157 709 1201 q 567 1113 628 1113 q 467 1144 519 1113 q 380 1176 414 1176 q 331 1154 351 1176 q 311 1101 311 1132 l 238 1118 q 278 1228 238 1181 q 380 1274 318 1274 q 475 1242 418 1274 q 567 1211 532 1211 q 615 1233 595 1211 q 635 1286 635 1255 l 709 1265 z "},"Ö":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 707 1103 l 559 1103 l 559 1238 l 707 1238 l 707 1103 m 389 1103 l 240 1103 l 240 1238 l 389 1238 l 389 1103 z "},"×":{"ha":743,"x_min":60,"x_max":673,"o":"m 60 238 l 281 465 l 60 691 l 145 777 l 366 551 l 588 777 l 673 691 l 451 465 l 673 238 l 588 153 l 366 378 l 145 153 l 60 238 z "},"Ø":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 357 0 408 -14 q 262 41 306 14 l 201 -64 l 100 -64 l 194 95 q 107 232 137 152 q 77 406 77 312 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 587 984 530 1002 q 693 936 644 967 l 748 1029 l 849 1029 l 760 879 q 841 745 812 822 q 869 581 869 669 l 869 406 m 210 406 q 223 298 210 348 q 260 214 236 249 l 264 213 l 633 834 q 557 877 599 862 q 466 892 515 892 q 280 805 349 892 q 210 583 210 718 l 210 406 m 736 583 q 725 679 736 633 q 693 758 713 724 l 689 759 l 322 141 q 388 106 352 118 q 466 95 424 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Ù":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 529 1058 l 422 1058 l 252 1234 l 254 1238 l 410 1238 l 529 1058 z "},"Ú":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 537 1236 l 693 1236 l 694 1232 l 511 1055 l 412 1055 l 537 1236 z "},"Û":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 682 1103 l 682 1086 l 572 1086 l 473 1186 l 374 1086 l 265 1086 l 265 1103 l 432 1264 l 513 1264 l 682 1103 z "},"Ü":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 707 1088 l 558 1088 l 558 1224 l 707 1224 l 707 1088 m 388 1088 l 239 1088 l 239 1224 l 388 1224 l 388 1088 z "},"Ý":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 488 1235 l 644 1235 l 646 1231 l 463 1055 l 363 1055 l 488 1235 z "},"Þ":{"ha":820,"x_min":111,"x_max":760,"o":"m 244 987 l 244 789 l 427 789 q 672 709 584 789 q 760 500 760 629 q 672 292 760 372 q 427 212 584 212 l 244 212 l 244 0 l 111 0 l 111 987 l 244 987 m 244 684 l 244 317 l 427 317 q 576 370 526 317 q 626 499 626 422 q 576 630 626 576 q 427 684 526 684 l 244 684 z "},"ß":{"ha":828,"x_min":93,"x_max":770,"o":"m 226 0 l 93 0 l 93 734 q 173 970 93 885 q 376 1055 252 1055 q 554 996 486 1055 q 623 825 623 938 q 579 670 623 745 q 535 557 535 595 q 653 405 535 501 q 770 214 770 309 q 700 43 770 101 q 508 -14 629 -14 q 391 0 451 -14 q 306 34 331 14 l 336 142 q 409 106 365 123 q 494 90 453 90 q 602 122 568 90 q 637 208 637 155 q 519 362 637 264 q 401 558 401 459 q 449 687 401 612 q 497 814 497 762 q 463 913 497 877 q 383 950 428 950 q 269 893 313 950 q 226 734 226 837 l 226 0 z "},"à":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 433 840 l 326 840 l 156 1016 l 158 1020 l 314 1020 l 433 840 z "},"á":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 441 1017 l 597 1017 l 598 1013 l 415 837 l 315 837 l 441 1017 z "},"â":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 585 884 l 585 867 l 476 867 l 376 968 l 277 867 l 169 867 l 169 885 l 336 1046 l 417 1046 l 585 884 z "},"ã":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 612 1032 q 571 924 612 968 q 470 880 531 880 q 370 912 422 880 q 283 943 317 943 q 234 921 254 943 q 214 868 214 899 l 141 886 q 181 995 141 949 q 283 1042 221 1042 q 378 1010 321 1042 q 470 978 435 978 q 518 1000 498 978 q 538 1054 538 1022 l 612 1032 z "},"ä":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 610 870 l 462 870 l 462 1006 l 610 1006 l 610 870 m 292 870 l 143 870 l 143 1006 l 292 1006 l 292 870 z "},"å":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 235 958 q 276 1053 235 1015 q 376 1092 317 1092 q 474 1054 433 1092 q 515 958 515 1015 q 474 863 515 899 q 376 826 434 826 q 276 863 317 826 q 235 958 235 899 m 304 958 q 326 908 304 928 q 376 887 347 887 q 425 907 404 887 q 445 958 445 927 q 425 1009 445 988 q 376 1030 404 1030 q 326 1009 347 1030 q 304 958 304 988 z "},"æ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 z "},"ç":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 416 -5 l 408 -41 q 481 -76 452 -48 q 510 -158 510 -104 q 456 -262 510 -223 q 303 -300 403 -300 l 298 -227 q 377 -210 347 -227 q 407 -160 407 -193 q 382 -115 407 -127 q 299 -97 358 -102 l 321 -5 l 416 -5 z "},"è":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 435 840 l 328 840 l 158 1017 l 160 1021 l 316 1021 l 435 840 z "},"é":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 443 1018 l 599 1018 l 600 1014 l 417 838 l 317 838 l 443 1018 z "},"ê":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 587 885 l 587 868 l 478 868 l 378 968 l 279 868 l 171 868 l 171 886 l 338 1046 l 419 1046 l 587 885 z "},"ë":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 612 871 l 464 871 l 464 1006 l 612 1006 l 612 871 m 294 871 l 145 871 l 145 1006 l 294 1006 l 294 871 z "},"ì":{"ha":349,"x_min":-48,"x_max":237,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 229 825 l 121 825 l -48 1002 l -46 1006 l 110 1006 l 229 825 z "},"í":{"ha":349,"x_min":104,"x_max":393,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 235 1003 l 391 1003 l 393 999 l 210 823 l 110 823 l 235 1003 z "},"î":{"ha":349,"x_min":-35,"x_max":381,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 381 870 l 381 853 l 272 853 l 172 954 l 73 853 l -35 853 l -35 871 l 132 1031 l 213 1031 l 381 870 z "},"ï":{"ha":349,"x_min":-61,"x_max":406,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 406 856 l 258 856 l 258 991 l 406 991 l 406 856 m 87 856 l -61 856 l -61 991 l 87 991 l 87 856 z "},"ð":{"ha":815,"x_min":49,"x_max":727,"o":"m 592 880 q 692 716 656 809 q 727 512 727 623 l 727 363 q 629 91 727 197 q 386 -14 532 -14 q 143 81 238 -14 q 49 316 49 176 q 142 567 49 471 q 380 663 236 663 q 495 638 441 663 q 585 572 549 613 l 587 576 q 552 709 581 650 q 477 814 524 769 l 281 702 l 229 771 l 402 870 q 348 897 376 885 q 290 918 319 908 l 330 1029 q 433 994 384 1016 q 524 940 481 971 l 672 1025 l 724 956 l 592 880 m 389 90 q 536 168 479 90 q 593 363 593 245 l 593 463 q 515 531 570 503 q 380 558 460 558 q 237 488 292 558 q 182 316 182 417 q 238 158 182 227 q 389 90 294 90 z "},"ñ":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 627 1032 q 587 924 627 968 q 486 880 547 880 q 385 912 437 880 q 298 943 333 943 q 250 921 269 943 q 230 868 230 899 l 157 886 q 197 995 157 949 q 298 1042 237 1042 q 393 1010 336 1042 q 486 978 450 978 q 534 1000 513 978 q 554 1054 554 1022 l 627 1032 z "},"ò":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 451 840 l 344 840 l 174 1016 l 176 1020 l 332 1020 l 451 840 z "},"ó":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 459 1017 l 615 1017 l 616 1013 l 433 837 l 334 837 l 459 1017 z "},"ô":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 604 884 l 604 867 l 494 867 l 395 968 l 296 867 l 187 867 l 187 885 l 354 1046 l 435 1046 l 604 884 z "},"õ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 630 1032 q 590 924 630 968 q 488 880 549 880 q 388 912 440 880 q 301 943 336 943 q 252 921 272 943 q 233 868 233 899 l 159 886 q 199 995 159 949 q 301 1042 239 1042 q 396 1010 339 1042 q 488 978 453 978 q 536 1000 516 978 q 557 1054 557 1022 l 630 1032 z "},"ö":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 629 870 l 480 870 l 480 1006 l 629 1006 l 629 870 m 310 870 l 161 870 l 161 1006 l 310 1006 l 310 870 z "},"÷":{"ha":793,"x_min":48,"x_max":725,"o":"m 725 405 l 48 405 l 48 532 l 725 532 l 725 405 m 454 677 l 320 677 l 320 815 l 454 815 l 454 677 m 454 122 l 320 122 l 320 260 l 454 260 l 454 122 z "},"ø":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 466 740 431 747 q 532 719 501 732 l 583 820 l 670 820 l 600 677 q 691 548 659 627 q 723 374 723 469 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 329 -8 360 -14 q 269 8 298 -3 l 220 -92 l 133 -92 l 201 47 q 101 176 136 95 q 66 359 66 258 l 66 374 m 199 359 q 213 246 199 297 q 254 162 227 195 l 258 162 l 484 619 q 441 636 464 630 q 393 642 418 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 589 374 q 577 476 589 428 q 544 555 566 524 l 540 555 l 318 106 q 353 94 334 98 q 395 90 372 90 q 540 166 490 90 q 589 359 589 242 l 589 374 z "},"ù":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 450 825 l 342 825 l 173 1002 l 175 1006 l 331 1006 l 450 825 z "},"ú":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 458 1003 l 614 1003 l 615 999 l 432 823 l 332 823 l 458 1003 z "},"û":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 602 870 l 602 853 l 493 853 l 393 954 l 294 853 l 186 853 l 186 871 l 353 1031 l 434 1031 l 602 870 z "},"ü":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 627 856 l 479 856 l 479 991 l 627 991 l 627 856 m 309 856 l 160 856 l 160 991 l 309 991 l 309 856 z "},"ý":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 413 1003 l 569 1003 l 570 999 l 387 823 l 288 823 l 413 1003 z "},"þ":{"ha":804,"x_min":104,"x_max":729,"o":"m 729 339 q 653 83 729 180 q 444 -14 577 -14 q 325 8 376 -14 q 237 77 273 31 l 237 -282 l 104 -282 l 104 1058 l 237 1058 l 237 652 q 324 723 273 699 q 441 747 375 747 q 653 639 578 747 q 729 353 729 531 l 729 339 m 595 353 q 546 558 595 478 q 401 639 497 639 q 303 613 344 639 q 237 541 262 587 l 237 186 q 303 116 262 141 q 403 90 344 90 q 547 160 498 90 q 595 339 595 229 l 595 353 z "},"ÿ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 583 856 l 434 856 l 434 991 l 583 991 l 583 856 m 264 856 l 115 856 l 115 991 l 264 991 l 264 856 z "},"Ā":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 694 1112 l 205 1112 l 205 1211 l 694 1211 l 694 1112 z "},"ā":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 625 894 l 136 894 l 136 993 l 625 993 l 625 894 z "},"Ă":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 z "},"ă":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 z "},"Ą":{"ha":899,"x_min":14,"x_max":923,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 869 0 q 795 -67 822 -35 q 768 -136 768 -100 q 784 -181 768 -165 q 833 -197 799 -197 q 867 -192 852 -197 q 901 -180 883 -187 l 923 -263 q 869 -284 899 -276 q 799 -293 840 -293 q 693 -256 734 -293 q 652 -151 652 -218 q 693 -48 652 -97 q 821 39 734 1 l 869 0 z "},"ą":{"ha":764,"x_min":72,"x_max":723,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 669 0 q 595 -67 622 -35 q 568 -136 568 -100 q 584 -181 568 -165 q 633 -197 599 -197 q 667 -192 652 -197 q 701 -180 683 -187 l 723 -263 q 669 -284 699 -276 q 599 -293 640 -293 q 493 -256 534 -293 q 452 -151 452 -218 q 493 -48 452 -97 q 621 39 534 1 l 669 0 z "},"Ć":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 524 1250 l 680 1250 l 682 1246 l 498 1069 l 399 1069 l 524 1250 z "},"ć":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 429 1017 l 585 1017 l 586 1013 l 403 837 l 303 837 l 429 1017 z "},"Ĉ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 669 1117 l 669 1100 l 559 1100 l 460 1200 l 361 1100 l 252 1100 l 252 1118 l 419 1278 l 500 1278 l 669 1117 z "},"ĉ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 573 884 l 573 867 l 464 867 l 364 968 l 265 867 l 157 867 l 157 885 l 323 1046 l 405 1046 l 573 884 z "},"Ċ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 534 1102 l 386 1102 l 386 1238 l 534 1238 l 534 1102 z "},"ċ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 438 869 l 290 869 l 290 1006 l 438 1006 l 438 869 z "},"Č":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 459 1179 l 559 1279 l 674 1279 l 674 1267 l 500 1101 l 419 1101 l 247 1265 l 247 1279 l 359 1279 l 459 1179 z "},"č":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 363 946 l 463 1046 l 578 1046 l 578 1034 l 404 868 l 323 868 l 151 1033 l 151 1046 l 264 1046 l 363 946 z "},"Ď":{"ha":915,"x_min":122,"x_max":854,"o":"m 122 0 l 122 987 l 425 987 q 736 868 619 987 q 854 561 854 749 l 854 426 q 736 118 854 237 q 425 0 619 0 l 122 0 m 256 882 l 256 104 l 425 104 q 641 195 562 104 q 720 426 720 285 l 720 562 q 641 792 720 702 q 425 882 562 882 l 256 882 m 420 1164 l 519 1265 l 635 1265 l 635 1253 l 460 1086 l 380 1086 l 208 1251 l 208 1265 l 320 1265 l 420 1164 z "},"ď":{"ha":891,"x_min":66,"x_max":888,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 m 888 963 l 805 802 l 739 802 l 781 969 l 781 1058 l 888 1058 l 888 963 z "},"Đ":{"ha":935,"x_min":28,"x_max":874,"o":"m 142 0 l 142 450 l 28 450 l 28 555 l 142 555 l 142 987 l 446 987 q 757 868 640 987 q 874 561 874 749 l 874 426 q 757 118 874 237 q 446 0 640 0 l 142 0 m 463 450 l 276 450 l 276 104 l 446 104 q 662 195 583 104 q 741 426 741 285 l 741 562 q 662 792 741 702 q 446 882 583 882 l 276 882 l 276 555 l 463 555 l 463 450 z "},"đ":{"ha":810,"x_min":66,"x_max":821,"o":"m 821 835 l 687 835 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 835 l 386 835 l 386 940 l 553 940 l 553 1058 l 687 1058 l 687 940 l 821 940 l 821 835 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"Ē":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 674 1112 l 185 1112 l 185 1211 l 674 1211 l 674 1112 z "},"ē":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 627 895 l 138 895 l 138 994 l 627 994 l 627 895 z "},"Ĕ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 625 1268 l 627 1264 q 574 1136 629 1185 q 427 1086 519 1086 q 278 1136 333 1086 q 227 1264 223 1185 l 228 1268 l 330 1268 q 353 1196 330 1223 q 427 1168 376 1168 q 499 1196 475 1168 q 523 1268 523 1224 l 625 1268 z "},"ĕ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 578 1050 l 579 1046 q 527 918 582 968 q 379 869 472 869 q 231 918 286 869 q 179 1046 176 968 l 180 1050 l 283 1050 q 306 978 283 1006 q 379 950 329 950 q 452 978 428 950 q 475 1050 475 1006 l 578 1050 z "},"Ė":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 500 1088 l 352 1088 l 352 1224 l 500 1224 l 500 1088 z "},"ė":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 452 870 l 304 870 l 304 1006 l 452 1006 l 452 870 z "},"Ę":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 488 0 q 413 -67 440 -35 q 387 -136 387 -100 q 402 -181 387 -165 q 452 -197 417 -197 q 486 -192 470 -197 q 519 -180 501 -187 l 541 -263 q 487 -284 517 -276 q 417 -293 458 -293 q 312 -256 353 -293 q 271 -151 271 -218 q 312 -48 271 -97 q 439 39 353 1 l 488 0 z "},"ę":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 597 42 q 523 -25 549 7 q 496 -94 496 -58 q 511 -139 496 -123 q 561 -155 526 -155 q 595 -150 579 -155 q 628 -138 610 -145 l 650 -221 q 596 -242 626 -234 q 526 -251 567 -251 q 421 -214 462 -251 q 380 -109 380 -176 q 421 -6 380 -55 q 548 81 462 43 l 597 42 z "},"Ě":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 425 1164 l 525 1265 l 640 1265 l 640 1253 l 466 1086 l 385 1086 l 213 1251 l 213 1265 l 326 1265 l 425 1164 z "},"ě":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 378 947 l 477 1047 l 593 1047 l 593 1035 l 418 869 l 338 869 l 165 1034 l 165 1047 l 278 1047 l 378 947 z "},"Ĝ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 662 1117 l 662 1100 l 553 1100 l 453 1200 l 354 1100 l 245 1100 l 245 1118 l 412 1278 l 494 1278 l 662 1117 z "},"ĝ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 591 884 l 591 867 l 482 867 l 382 968 l 283 867 l 175 867 l 175 885 l 342 1046 l 423 1046 l 591 884 z "},"Ğ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 652 1282 l 654 1278 q 602 1150 656 1200 q 454 1101 547 1101 q 305 1150 360 1101 q 254 1278 250 1200 l 255 1282 l 357 1282 q 380 1210 357 1238 q 454 1182 404 1182 q 526 1210 503 1182 q 550 1282 550 1238 l 652 1282 z "},"ğ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 582 1050 l 583 1046 q 531 918 586 967 q 383 868 476 868 q 235 918 290 868 q 183 1046 180 967 l 184 1050 l 287 1050 q 310 977 287 1005 q 383 949 333 949 q 456 978 432 949 q 479 1050 479 1006 l 582 1050 z "},"Ġ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 527 1102 l 379 1102 l 379 1238 l 527 1238 l 527 1102 z "},"ġ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 456 869 l 309 869 l 309 1006 l 456 1006 l 456 869 z "},"Ģ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 502 -174 l 419 -335 l 353 -335 l 395 -168 l 395 -79 l 502 -79 l 502 -174 z "},"ģ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 311 950 l 393 1118 l 459 1118 l 445 943 l 445 850 l 311 850 l 311 950 z "},"Ĥ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 698 1103 l 698 1086 l 589 1086 l 489 1186 l 390 1086 l 281 1086 l 281 1103 l 448 1264 l 530 1264 l 698 1103 z "},"ĥ":{"ha":789,"x_min":97,"x_max":694,"o":"m 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 1058 l 231 1058 l 231 635 m 554 1102 l 554 1085 l 445 1085 l 345 1185 l 246 1085 l 138 1085 l 138 1103 l 304 1263 l 386 1263 l 554 1102 z "},"Ħ":{"ha":976,"x_min":21,"x_max":965,"o":"m 867 793 l 965 793 l 965 694 l 867 694 l 867 0 l 734 0 l 734 436 l 254 436 l 254 0 l 121 0 l 121 694 l 21 694 l 21 793 l 121 793 l 121 987 l 254 987 l 254 793 l 734 793 l 734 987 l 867 987 l 867 793 m 254 541 l 734 541 l 734 694 l 254 694 l 254 541 z "},"ħ":{"ha":810,"x_min":1,"x_max":715,"o":"m 435 835 l 251 835 l 251 635 q 344 718 289 688 q 467 747 399 747 q 649 677 584 747 q 715 460 715 606 l 715 0 l 581 0 l 581 461 q 542 595 581 552 q 426 639 503 639 q 325 613 370 639 q 251 542 280 587 l 251 0 l 117 0 l 117 835 l 1 835 l 1 940 l 117 940 l 117 1058 l 251 1058 l 251 940 l 435 940 l 435 835 z "},"Ĩ":{"ha":393,"x_min":-38,"x_max":433,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 433 1251 q 392 1143 433 1187 q 291 1099 352 1099 q 191 1130 243 1099 q 104 1162 138 1162 q 55 1140 75 1162 q 35 1086 35 1118 l -38 1104 q 2 1214 -38 1167 q 104 1260 42 1260 q 199 1228 142 1260 q 291 1196 256 1196 q 339 1218 319 1196 q 359 1272 359 1240 l 433 1251 z "},"ĩ":{"ha":349,"x_min":-63,"x_max":408,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 408 1018 q 367 910 408 954 q 266 866 327 866 q 165 898 218 866 q 79 929 113 929 q 30 907 50 929 q 10 854 10 885 l -63 871 q -23 981 -63 935 q 79 1027 17 1027 q 174 996 117 1027 q 266 964 231 964 q 314 986 294 964 q 334 1040 334 1008 l 408 1018 z "},"Ī":{"ha":393,"x_min":-43,"x_max":446,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 446 1112 l -43 1112 l -43 1211 l 446 1211 l 446 1112 z "},"ī":{"ha":349,"x_min":-68,"x_max":420,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 420 881 l -68 881 l -68 980 l 420 980 l 420 881 z "},"Ĭ":{"ha":393,"x_min":-5,"x_max":401,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 397 1268 l 398 1264 q 346 1136 401 1185 q 198 1086 291 1086 q 50 1136 104 1086 q -2 1264 -5 1185 l -1 1268 l 102 1268 q 125 1196 102 1223 q 198 1168 148 1168 q 271 1196 247 1168 q 294 1268 294 1224 l 397 1268 z "},"ĭ":{"ha":349,"x_min":-31,"x_max":376,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 372 1036 l 373 1031 q 321 903 376 953 q 173 854 266 854 q 24 903 79 854 q -27 1031 -31 953 l -26 1036 l 77 1036 q 100 963 77 991 q 173 935 123 935 q 245 963 222 935 q 269 1036 269 991 l 372 1036 z "},"Į":{"ha":393,"x_min":31,"x_max":302,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 248 0 q 174 -67 201 -35 q 147 -136 147 -100 q 162 -181 147 -165 q 212 -197 178 -197 q 246 -192 231 -197 q 279 -180 262 -187 l 302 -263 q 248 -284 277 -276 q 178 -293 218 -293 q 72 -256 113 -293 q 31 -151 31 -218 q 72 -48 31 -97 q 199 39 113 1 l 248 0 z "},"į":{"ha":350,"x_min":9,"x_max":280,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 227 0 q 152 -67 179 -35 q 125 -136 125 -100 q 141 -181 125 -165 q 191 -197 156 -197 q 224 -192 209 -197 q 258 -180 240 -187 l 280 -263 q 226 -284 256 -276 q 156 -293 197 -293 q 51 -256 92 -293 q 9 -151 9 -218 q 51 -48 9 -97 q 178 39 92 1 l 227 0 z "},"İ":{"ha":393,"x_min":122,"x_max":270,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 270 1088 l 122 1088 l 122 1224 l 270 1224 l 270 1088 z "},"ı":{"ha":349,"x_min":104,"x_max":237,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 z "},"IJ":{"ha":1159,"x_min":129,"x_max":1046,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 912 987 l 1046 987 l 1046 273 q 962 63 1046 141 q 745 -14 878 -14 q 518 58 602 -14 q 437 268 434 130 l 439 272 l 567 272 q 613 135 567 180 q 745 90 660 90 q 865 140 819 90 q 912 273 912 190 l 912 987 z "},"ij":{"ha":709,"x_min":108,"x_max":601,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 601 734 l 601 -60 q 543 -235 601 -174 q 380 -296 484 -296 q 342 -293 359 -296 q 305 -284 325 -290 l 315 -179 q 342 -185 324 -182 q 371 -187 359 -187 q 441 -157 415 -187 q 467 -60 467 -127 l 467 734 l 601 734 m 597 924 l 463 924 l 463 1058 l 597 1058 l 597 924 z "},"Ĵ":{"ha":766,"x_min":41,"x_max":789,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 m 789 1095 l 789 1078 l 680 1078 l 581 1178 l 481 1078 l 373 1078 l 373 1095 l 540 1256 l 621 1256 l 789 1095 z "},"ĵ":{"ha":355,"x_min":-46,"x_max":393,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 m 393 858 l 393 841 l 284 841 l 184 941 l 85 841 l -23 841 l -23 859 l 144 1019 l 225 1019 l 393 858 z "},"Ķ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 479 -163 l 396 -323 l 330 -323 l 372 -157 l 372 -68 l 479 -68 l 479 -163 z "},"ķ":{"ha":712,"x_min":98,"x_max":713,"o":"m 318 338 l 231 338 l 231 0 l 98 0 l 98 1058 l 231 1058 l 231 445 l 317 445 l 517 734 l 677 734 l 427 400 l 713 0 l 556 0 l 318 338 m 412 -161 l 329 -322 l 263 -322 l 304 -155 l 304 -66 l 412 -66 l 412 -161 z "},"ĸ":{"ha":776,"x_min":104,"x_max":738,"o":"m 306 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 714 734 l 715 730 l 410 382 l 738 3 l 736 0 l 573 0 l 306 311 z "},"Ĺ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 252 1236 l 408 1236 l 409 1232 l 226 1055 l 126 1055 l 252 1236 z "},"ĺ":{"ha":350,"x_min":108,"x_max":397,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 239 1261 l 395 1261 l 397 1257 l 214 1081 l 114 1081 l 239 1261 z "},"Ļ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 474 -161 l 391 -322 l 326 -322 l 367 -155 l 367 -66 l 474 -66 l 474 -161 z "},"ļ":{"ha":350,"x_min":75,"x_max":241,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 223 -161 l 140 -322 l 75 -322 l 116 -155 l 116 -66 l 223 -66 l 223 -161 z "},"Ľ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 544 893 l 461 732 l 395 732 l 437 899 l 437 988 l 544 988 l 544 893 z "},"ľ":{"ha":452,"x_min":108,"x_max":456,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 456 963 l 373 802 l 307 802 l 349 969 l 349 1058 l 456 1058 l 456 963 z "},"Ŀ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 564 467 l 416 467 l 416 603 l 564 603 l 564 467 z "},"ŀ":{"ha":499,"x_min":108,"x_max":477,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 477 455 l 329 455 l 329 591 l 477 591 l 477 455 z "},"Ł":{"ha":730,"x_min":27,"x_max":711,"o":"m 244 572 l 427 630 l 427 517 l 244 459 l 244 104 l 711 104 l 711 0 l 111 0 l 111 417 l 27 391 l 27 503 l 111 530 l 111 987 l 244 987 l 244 572 z "},"ł":{"ha":378,"x_min":25,"x_max":357,"o":"m 255 591 l 357 631 l 357 519 l 255 479 l 255 0 l 121 0 l 121 429 l 25 392 l 25 504 l 121 541 l 121 1058 l 255 1058 l 255 591 z "},"Ń":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 558 1236 l 714 1236 l 715 1232 l 532 1055 l 433 1055 l 558 1236 z "},"ń":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 456 1017 l 612 1017 l 614 1013 l 431 837 l 331 837 l 456 1017 z "},"Ņ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 542 -161 l 459 -322 l 393 -322 l 435 -155 l 435 -66 l 542 -66 l 542 -161 z "},"ņ":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 440 -161 l 357 -322 l 292 -322 l 333 -155 l 333 -66 l 440 -66 l 440 -161 z "},"Ň":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 493 1164 l 593 1265 l 708 1265 l 708 1253 l 534 1086 l 453 1086 l 281 1251 l 281 1265 l 393 1265 l 493 1164 z "},"ň":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 391 946 l 491 1046 l 606 1046 l 606 1034 l 432 868 l 351 868 l 179 1033 l 179 1046 l 292 1046 l 391 946 z "},"ʼn":{"ha":789,"x_min":-22,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 127 963 l 44 802 l -22 802 l 20 969 l 20 1058 l 127 1058 l 127 963 z "},"Ŋ":{"ha":960,"x_min":109,"x_max":856,"o":"m 856 987 l 856 -60 q 798 -235 856 -174 q 635 -296 739 -296 q 596 -293 614 -296 q 559 -284 578 -290 l 568 -182 q 600 -189 577 -186 q 635 -191 623 -191 q 699 -156 675 -191 q 722 -60 722 -121 l 722 0 l 247 754 l 243 753 l 243 0 l 109 0 l 109 987 l 243 987 l 718 233 l 722 235 l 722 987 l 856 987 z "},"ŋ":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 622 678 557 747 q 687 463 687 608 l 687 -60 q 629 -235 687 -174 q 466 -296 570 -296 q 427 -293 445 -296 q 390 -284 409 -290 l 399 -176 q 431 -181 408 -179 q 466 -184 454 -184 q 530 -152 507 -184 q 553 -60 553 -120 l 553 460 q 515 598 553 557 q 397 639 476 639 q 298 616 340 639 q 231 554 256 594 l 231 0 l 97 0 l 97 734 l 216 734 z "},"Ō":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 722 1126 l 233 1126 l 233 1225 l 722 1225 l 722 1126 z "},"ō":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 643 894 l 154 894 l 154 993 l 643 993 l 643 894 z "},"Ŏ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 673 1282 l 674 1278 q 622 1150 677 1200 q 474 1101 567 1101 q 326 1150 380 1101 q 274 1278 271 1200 l 275 1282 l 378 1282 q 401 1210 378 1238 q 474 1182 424 1182 q 547 1210 523 1182 q 570 1282 570 1238 l 673 1282 z "},"ŏ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 594 1050 l 595 1046 q 543 918 598 967 q 395 868 488 868 q 247 918 302 868 q 195 1046 192 967 l 197 1050 l 299 1050 q 322 977 299 1005 q 395 949 345 949 q 468 978 444 949 q 492 1050 492 1006 l 594 1050 z "},"Ő":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 659 1280 l 814 1280 l 817 1276 l 613 1100 l 497 1100 l 496 1103 l 659 1280 m 438 1280 l 581 1280 l 583 1277 l 420 1100 l 313 1100 l 438 1280 z "},"ő":{"ha":789,"x_min":66,"x_max":738,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 581 1048 l 736 1048 l 738 1044 l 534 867 l 418 867 l 417 871 l 581 1048 m 359 1048 l 503 1048 l 504 1044 l 341 867 l 235 867 l 359 1048 z "},"Œ":{"ha":1329,"x_min":71,"x_max":1269,"o":"m 1269 0 l 646 0 q 540 -11 584 -7 q 450 -14 496 -14 q 176 98 281 -14 q 71 391 71 211 l 71 597 q 175 889 71 777 q 449 1002 280 1002 q 544 998 496 1002 q 646 987 593 994 l 1261 987 l 1261 882 l 747 882 l 747 563 l 1200 563 l 1200 458 l 747 458 l 747 104 l 1269 104 l 1269 0 m 450 91 q 533 94 492 91 q 614 103 574 97 l 614 884 q 531 893 572 889 q 449 897 490 897 q 269 820 334 897 q 204 598 204 743 l 204 391 q 270 168 204 245 q 450 91 336 91 z "},"œ":{"ha":1257,"x_min":66,"x_max":1209,"o":"m 929 -14 q 771 21 840 -14 q 659 122 703 57 q 548 21 615 57 q 395 -14 481 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 154 642 66 536 q 393 747 242 747 q 549 710 481 747 q 661 607 617 673 q 770 710 705 673 q 909 747 836 747 q 1133 658 1057 747 q 1209 419 1209 568 l 1209 336 l 733 336 l 731 332 q 785 158 733 227 q 929 90 836 90 q 1048 110 997 90 q 1137 163 1099 129 l 1189 76 q 1085 12 1149 37 q 929 -14 1021 -14 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 909 642 q 794 586 842 642 q 736 444 747 529 l 737 440 l 1076 440 l 1076 458 q 1034 589 1076 536 q 909 642 991 642 z "},"Ŕ":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 485 1236 l 641 1236 l 642 1232 l 459 1055 l 359 1055 l 485 1236 z "},"ŕ":{"ha":476,"x_min":97,"x_max":502,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 345 1017 l 500 1017 l 502 1013 l 319 837 l 219 837 l 345 1017 z "},"Ŗ":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 469 -161 l 386 -322 l 320 -322 l 361 -155 l 361 -66 l 469 -66 l 469 -161 z "},"ŗ":{"ha":476,"x_min":73,"x_max":463,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 221 -161 l 138 -322 l 73 -322 l 114 -155 l 114 -66 l 221 -66 l 221 -161 z "},"Ř":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 420 1164 l 519 1265 l 635 1265 l 635 1253 l 460 1086 l 380 1086 l 208 1251 l 208 1265 l 320 1265 l 420 1164 z "},"ř":{"ha":476,"x_min":68,"x_max":496,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 281 946 l 380 1046 l 496 1046 l 496 1034 l 321 868 l 241 868 l 68 1033 l 68 1046 l 181 1046 l 281 946 z "},"Ś":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 480 1250 l 636 1250 l 637 1246 l 454 1069 l 355 1069 l 480 1250 z "},"ś":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 427 1017 l 583 1017 l 585 1013 l 401 837 l 302 837 l 427 1017 z "},"Ŝ":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 625 1117 l 625 1100 l 515 1100 l 416 1200 l 317 1100 l 208 1100 l 208 1118 l 375 1278 l 456 1278 l 625 1117 z "},"ŝ":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 572 884 l 572 867 l 463 867 l 363 968 l 264 867 l 155 867 l 155 885 l 322 1046 l 404 1046 l 572 884 z "},"Ş":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 467 -5 l 459 -41 q 532 -76 503 -48 q 562 -158 562 -104 q 508 -262 562 -223 q 355 -300 454 -300 l 350 -227 q 429 -210 399 -227 q 458 -160 458 -193 q 434 -115 458 -127 q 351 -97 410 -102 l 372 -5 l 467 -5 z "},"ş":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 414 -5 l 406 -40 q 479 -75 450 -47 q 509 -157 509 -103 q 455 -261 509 -222 q 302 -300 401 -300 l 297 -226 q 376 -209 346 -226 q 406 -159 406 -193 q 381 -114 406 -127 q 298 -96 357 -101 l 319 -5 l 414 -5 z "},"Š":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 415 1179 l 515 1279 l 630 1279 l 630 1267 l 456 1101 l 375 1101 l 203 1265 l 203 1279 l 315 1279 l 415 1179 z "},"š":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 362 946 l 462 1046 l 577 1046 l 577 1034 l 403 868 l 322 868 l 150 1033 l 150 1046 l 262 1046 l 362 946 z "},"Ţ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 457 -161 l 374 -322 l 309 -322 l 350 -155 l 350 -66 l 457 -66 l 457 -161 z "},"ţ":{"ha":480,"x_min":23,"x_max":419,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 m 357 -168 l 275 -329 l 209 -329 l 250 -162 l 250 -73 l 357 -73 l 357 -168 z "},"Ť":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 408 1164 l 508 1264 l 623 1264 l 623 1252 l 449 1086 l 368 1086 l 196 1251 l 196 1264 l 309 1264 l 408 1164 z "},"ť":{"ha":507,"x_min":23,"x_max":518,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 m 518 981 l 435 821 l 370 821 l 411 987 l 411 1076 l 518 1076 l 518 981 z "},"Ŧ":{"ha":814,"x_min":23,"x_max":791,"o":"m 623 556 l 473 556 l 473 0 l 340 0 l 340 556 l 188 556 l 188 661 l 340 661 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 l 473 882 l 473 661 l 623 661 l 623 556 z "},"ŧ":{"ha":480,"x_min":-5,"x_max":429,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 512 l 429 512 l 429 407 l 273 407 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 407 l -5 407 l -5 512 l 140 512 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 z "},"Ũ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 708 1251 q 668 1143 708 1187 q 566 1099 627 1099 q 466 1130 518 1099 q 379 1162 414 1162 q 330 1140 350 1162 q 311 1086 311 1118 l 237 1104 q 277 1214 237 1167 q 379 1260 317 1260 q 474 1228 417 1260 q 566 1196 531 1196 q 614 1218 594 1196 q 635 1272 635 1240 l 708 1251 z "},"ũ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 629 1018 q 588 910 629 954 q 487 866 548 866 q 387 898 439 866 q 300 929 334 929 q 251 907 271 929 q 231 854 231 885 l 158 871 q 198 981 158 935 q 300 1027 238 1027 q 395 996 338 1027 q 487 964 452 964 q 535 986 515 964 q 555 1040 555 1008 l 629 1018 z "},"Ū":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 721 1112 l 232 1112 l 232 1211 l 721 1211 l 721 1112 z "},"ū":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 642 881 l 153 881 l 153 980 l 642 980 l 642 881 z "},"Ŭ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 672 1268 l 673 1264 q 621 1136 676 1185 q 473 1086 566 1086 q 325 1136 380 1086 q 273 1264 270 1185 l 275 1268 l 377 1268 q 400 1196 377 1223 q 473 1168 423 1168 q 546 1196 522 1168 q 570 1268 570 1224 l 672 1268 z "},"ŭ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 593 1036 l 594 1031 q 542 903 597 953 q 394 854 487 854 q 245 903 300 854 q 194 1031 191 953 l 195 1036 l 298 1036 q 321 963 298 991 q 394 935 344 935 q 467 963 443 935 q 490 1036 490 991 l 593 1036 z "},"Ů":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 331 1176 q 372 1272 331 1233 q 472 1310 413 1310 q 570 1272 530 1310 q 611 1176 611 1234 q 571 1081 611 1118 q 472 1044 530 1044 q 372 1081 413 1044 q 331 1176 331 1118 m 401 1176 q 422 1126 401 1147 q 472 1105 443 1105 q 521 1125 500 1105 q 541 1176 541 1145 q 521 1227 541 1206 q 472 1249 500 1249 q 422 1227 443 1249 q 401 1176 401 1206 z "},"ů":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 252 943 q 293 1039 252 1000 q 393 1078 334 1078 q 491 1039 450 1078 q 532 943 532 1001 q 491 848 532 885 q 393 812 451 812 q 293 848 334 812 q 252 943 252 885 m 321 943 q 342 893 321 914 q 393 873 363 873 q 441 893 421 873 q 462 943 462 913 q 441 995 462 974 q 393 1016 421 1016 q 342 995 363 1016 q 321 943 321 974 z "},"Ű":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 659 1266 l 814 1266 l 816 1262 l 612 1086 l 496 1086 l 495 1089 l 659 1266 m 437 1266 l 581 1266 l 582 1263 l 419 1086 l 313 1086 l 437 1266 z "},"ű":{"ha":789,"x_min":94,"x_max":736,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 579 1034 l 734 1034 l 736 1029 l 533 853 l 417 853 l 416 857 l 579 1034 m 358 1034 l 501 1034 l 503 1030 l 340 853 l 233 853 l 358 1034 z "},"Ų":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 551 0 q 477 -67 504 -35 q 450 -136 450 -100 q 466 -181 450 -165 q 515 -197 481 -197 q 549 -192 534 -197 q 583 -180 565 -187 l 605 -263 q 551 -284 581 -276 q 481 -293 522 -293 q 375 -256 416 -293 q 334 -151 334 -218 q 375 -48 334 -97 q 503 39 416 1 l 551 0 z "},"ų":{"ha":789,"x_min":94,"x_max":728,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 675 0 q 601 -67 627 -35 q 574 -136 574 -100 q 589 -181 574 -165 q 639 -197 604 -197 q 673 -192 657 -197 q 706 -180 688 -187 l 728 -263 q 674 -284 704 -276 q 604 -293 645 -293 q 499 -256 540 -293 q 458 -151 458 -218 q 499 -48 458 -97 q 626 39 540 1 l 675 0 z "},"Ŵ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 818 1103 l 818 1086 l 709 1086 l 609 1186 l 510 1086 l 401 1086 l 401 1103 l 568 1264 l 650 1264 l 818 1103 z "},"ŵ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 733 870 l 733 853 l 624 853 l 524 954 l 425 853 l 317 853 l 317 871 l 484 1031 l 565 1031 l 733 870 z "},"Ŷ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 633 1102 l 633 1085 l 524 1085 l 424 1185 l 325 1085 l 216 1085 l 216 1103 l 383 1263 l 465 1263 l 633 1102 z "},"ŷ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 557 870 l 557 853 l 448 853 l 349 954 l 250 853 l 141 853 l 141 871 l 308 1031 l 389 1031 l 557 870 z "},"Ÿ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 658 1088 l 509 1088 l 509 1223 l 658 1223 l 658 1088 m 339 1088 l 191 1088 l 191 1223 l 339 1223 l 339 1088 z "},"Ź":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 477 1236 l 633 1236 l 635 1232 l 452 1055 l 352 1055 l 477 1236 z "},"ź":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 420 1003 l 576 1003 l 577 999 l 394 823 l 294 823 l 420 1003 z "},"Ż":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 487 1088 l 339 1088 l 339 1224 l 487 1224 l 487 1088 z "},"ż":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 429 855 l 281 855 l 281 991 l 429 991 l 429 855 z "},"Ž":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 412 1164 l 512 1265 l 627 1265 l 627 1253 l 453 1086 l 372 1086 l 200 1251 l 200 1265 l 313 1265 l 412 1164 z "},"ž":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 355 932 l 454 1032 l 570 1032 l 570 1020 l 395 854 l 315 854 l 142 1019 l 142 1032 l 255 1032 l 355 932 z "},"ſ":{"ha":350,"x_min":108,"x_max":439,"o":"m 108 0 l 108 827 q 170 1008 108 944 q 341 1072 231 1072 q 387 1068 363 1072 q 439 1058 410 1065 l 422 960 q 394 965 409 963 q 362 967 379 967 q 272 930 303 967 q 241 827 241 893 l 241 0 l 108 0 z "},"ƒ":{"ha":476,"x_min":-16,"x_max":477,"o":"m 413 635 l 280 635 l 280 -60 q 222 -235 280 -174 q 60 -296 164 -296 q 20 -293 38 -296 q -16 -284 3 -290 l -6 -182 q 25 -189 2 -186 q 60 -191 47 -191 q 123 -156 100 -191 q 146 -60 146 -121 l 146 635 l 32 635 l 32 734 l 146 734 l 146 827 q 208 1008 146 944 q 379 1072 269 1072 q 426 1068 402 1072 q 477 1058 449 1065 l 461 956 q 432 961 449 959 q 396 963 414 963 q 309 928 337 963 q 280 827 280 893 l 280 734 l 413 734 l 413 635 z "},"Ơ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 z "},"ơ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"Ư":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 z "},"ư":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 z "},"ǰ":{"ha":355,"x_min":-46,"x_max":399,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 m 184 920 l 283 1020 l 399 1020 l 399 1008 l 224 842 l 144 842 l -28 1006 l -28 1020 l 84 1020 l 184 920 z "},"Ǻ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 515 1414 l 650 1414 l 651 1410 l 505 1289 l 417 1289 l 515 1414 m 330 1142 q 365 1223 330 1191 q 449 1255 399 1255 q 530 1223 496 1255 q 564 1142 564 1191 q 531 1062 564 1093 q 449 1031 497 1031 q 365 1062 399 1031 q 330 1142 330 1093 m 390 1142 q 407 1102 390 1118 q 449 1085 425 1085 q 489 1101 472 1085 q 505 1142 505 1118 q 489 1184 505 1167 q 449 1202 472 1202 q 407 1184 425 1202 q 390 1142 390 1167 z "},"ǻ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 444 1196 l 579 1196 l 581 1192 l 434 1070 l 347 1070 l 444 1196 m 260 924 q 294 1005 260 972 q 378 1037 329 1037 q 460 1005 426 1037 q 494 924 494 972 q 460 844 494 875 q 378 812 427 812 q 294 844 329 812 q 260 924 260 875 m 319 924 q 337 883 319 900 q 378 867 354 867 q 418 883 401 867 q 435 924 435 899 q 418 966 435 949 q 378 983 401 983 q 337 966 354 983 q 319 924 319 949 z "},"Ǽ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 m 699 1236 l 854 1236 l 856 1232 l 673 1055 l 573 1055 l 699 1236 z "},"ǽ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 m 647 1018 l 803 1018 l 804 1014 l 621 838 l 522 838 l 647 1018 z "},"Ǿ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 357 0 408 -14 q 262 41 306 14 l 201 -64 l 100 -64 l 194 95 q 107 232 137 152 q 77 406 77 312 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 587 984 530 1002 q 693 936 644 967 l 748 1029 l 849 1029 l 760 879 q 841 745 812 822 q 869 581 869 669 l 869 406 m 210 406 q 223 298 210 348 q 260 214 236 249 l 264 213 l 633 834 q 557 877 599 862 q 466 892 515 892 q 280 805 349 892 q 210 583 210 718 l 210 406 m 736 583 q 725 679 736 633 q 693 758 713 724 l 689 759 l 322 141 q 388 106 352 118 q 466 95 424 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 538 1278 l 694 1278 l 695 1274 l 512 1097 l 412 1097 l 538 1278 z "},"ǿ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 466 740 431 747 q 532 719 501 732 l 583 820 l 670 820 l 600 677 q 691 548 659 627 q 723 374 723 469 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 329 -8 360 -14 q 269 8 298 -3 l 220 -92 l 133 -92 l 201 47 q 101 176 136 95 q 66 359 66 258 l 66 374 m 199 359 q 213 246 199 297 q 254 162 227 195 l 258 162 l 484 619 q 441 636 464 630 q 393 642 418 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 589 374 q 577 476 589 428 q 544 555 566 524 l 540 555 l 318 106 q 353 94 334 98 q 395 90 372 90 q 540 166 490 90 q 589 359 589 242 l 589 374 m 430 1017 l 586 1017 l 587 1013 l 404 836 l 304 836 l 430 1017 z "},"Ș":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 464 -175 l 381 -336 l 315 -336 l 357 -169 l 357 -80 l 464 -80 l 464 -175 z "},"ș":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 411 -174 l 328 -335 l 262 -335 l 304 -168 l 304 -79 l 411 -79 l 411 -174 z "},"ȷ":{"ha":355,"x_min":-46,"x_max":250,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 z "},"ʼ":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 z "},"ˆ":{"ha":664,"x_min":116,"x_max":532,"o":"m 532 866 l 532 849 l 423 849 l 323 949 l 224 849 l 116 849 l 116 867 l 283 1027 l 364 1027 l 532 866 z "},"ˇ":{"ha":625,"x_min":95,"x_max":522,"o":"m 307 927 l 407 1027 l 522 1027 l 522 1015 l 348 849 l 267 849 l 95 1014 l 95 1027 l 208 1027 l 307 927 z "},"˘":{"ha":594,"x_min":87,"x_max":494,"o":"m 490 987 l 491 983 q 439 855 494 905 q 291 806 384 806 q 142 855 197 806 q 91 983 87 905 l 92 987 l 195 987 q 218 915 195 943 q 291 887 241 887 q 363 915 340 887 q 387 987 387 943 l 490 987 z "},"˙":{"ha":377,"x_min":109,"x_max":256,"o":"m 256 851 l 109 851 l 109 987 l 256 987 l 256 851 z "},"˚":{"ha":472,"x_min":90,"x_max":370,"o":"m 90 887 q 131 983 90 944 q 231 1021 172 1021 q 330 983 289 1021 q 370 887 370 945 q 330 792 370 829 q 231 755 290 755 q 131 792 172 755 q 90 887 90 829 m 160 887 q 181 837 160 858 q 231 817 202 817 q 280 837 260 817 q 300 887 300 857 q 280 939 300 918 q 231 960 260 960 q 181 939 202 960 q 160 887 160 918 z "},"˛":{"ha":381,"x_min":46,"x_max":317,"o":"m 263 0 q 189 -67 216 -35 q 162 -136 162 -100 q 177 -181 162 -165 q 227 -197 193 -197 q 261 -192 245 -197 q 294 -180 277 -187 l 317 -263 q 263 -284 292 -276 q 193 -293 233 -293 q 87 -256 128 -293 q 46 -151 46 -218 q 87 -48 46 -97 q 214 39 128 1 l 263 0 z "},"˜":{"ha":664,"x_min":92,"x_max":562,"o":"m 562 1011 q 522 903 562 947 q 420 859 481 859 q 320 891 372 859 q 233 922 268 922 q 184 900 204 922 q 165 847 165 878 l 92 865 q 132 974 92 928 q 233 1021 172 1021 q 328 989 271 1021 q 420 957 385 957 q 469 979 448 957 q 489 1033 489 1001 l 562 1011 z "},"˝":{"ha":517,"x_min":68,"x_max":571,"o":"m 414 1029 l 569 1029 l 571 1025 l 368 849 l 252 849 l 250 852 l 414 1029 m 193 1029 l 336 1029 l 337 1026 l 174 849 l 68 849 l 193 1029 z "},"˳":{"ha":456,"x_min":123,"x_max":334,"o":"m 123 -159 q 154 -86 123 -115 q 230 -57 185 -57 q 303 -86 273 -57 q 334 -159 334 -115 q 303 -229 334 -201 q 230 -257 273 -257 q 154 -229 185 -257 q 123 -159 123 -201 m 184 -159 q 197 -189 184 -176 q 230 -202 211 -202 q 260 -190 248 -202 q 273 -159 273 -177 q 260 -125 273 -138 q 230 -112 248 -112 q 197 -125 211 -112 q 184 -159 184 -139 z "},"̀":{"ha":0,"x_min":-553,"x_max":-345,"o":"m -345 821 l -433 821 l -553 1057 l -427 1057 l -345 821 z "},"́":{"ha":0,"x_min":-446,"x_max":-236,"o":"m -359 1057 l -236 1057 l -363 821 l -446 821 l -359 1057 z "},"̃":{"ha":0,"x_min":-599,"x_max":-128,"o":"m -128 1011 q -169 903 -128 947 q -270 859 -209 859 q -370 891 -318 859 q -457 922 -422 922 q -506 900 -486 922 q -526 847 -526 878 l -599 865 q -559 974 -599 928 q -457 1021 -519 1021 q -362 989 -419 1021 q -270 957 -305 957 q -222 979 -242 957 q -201 1033 -201 1001 l -128 1011 z "},"̉":{"ha":0,"x_min":-459,"x_max":-248,"o":"m -446 842 l -446 947 q -373 960 -395 949 q -351 996 -351 971 q -380 1036 -351 1023 q -459 1048 -410 1048 l -454 1122 q -301 1087 -355 1122 q -248 994 -248 1052 q -277 921 -248 946 q -350 890 -306 897 l -351 842 l -446 842 z "},"̏":{"ha":0,"x_min":-665,"x_max":-161,"o":"m -344 852 l -345 849 l -461 849 l -665 1025 l -663 1029 l -507 1029 l -344 852 m -161 849 l -268 849 l -431 1026 l -430 1029 l -286 1029 l -161 849 z "},"̣":{"ha":0,"x_min":-480,"x_max":-332,"o":"m -332 -229 l -480 -229 l -480 -93 l -332 -93 l -332 -229 z "},"΄":{"ha":357,"x_min":132,"x_max":315,"o":"m 174 1119 l 315 1119 l 196 861 l 132 861 l 174 1119 z "},"΅":{"ha":709,"x_min":109,"x_max":589,"o":"m 589 852 l 454 852 l 454 987 l 589 987 l 589 852 m 243 852 l 109 852 l 109 987 l 243 987 l 243 852 m 337 1173 l 485 1173 l 399 996 l 302 996 l 337 1173 z "},"Ά":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 205 1119 l 346 1119 l 227 861 l 163 861 l 205 1119 z "},"·":{"ha":367,"x_min":109,"x_max":243,"o":"m 243 423 l 109 423 l 109 567 l 243 567 l 243 423 z "},"Έ":{"ha":880,"x_min":-20,"x_max":845,"o":"m 776 458 l 323 458 l 323 104 l 845 104 l 845 0 l 190 0 l 190 987 l 838 987 l 838 882 l 323 882 l 323 563 l 776 563 l 776 458 m 22 1120 l 163 1120 l 44 863 l -20 863 l 22 1120 z "},"Ή":{"ha":1059,"x_min":14,"x_max":937,"o":"m 937 0 l 803 0 l 803 436 l 323 436 l 323 0 l 190 0 l 190 987 l 323 987 l 323 541 l 803 541 l 803 987 l 937 987 l 937 0 m 55 1120 l 196 1120 l 77 863 l 14 863 l 55 1120 z "},"Ί":{"ha":460,"x_min":16,"x_max":331,"o":"m 331 0 l 197 0 l 197 987 l 331 987 l 331 0 m 58 1120 l 199 1120 l 80 862 l 16 862 l 58 1120 z "},"Ό":{"ha":960,"x_min":46,"x_max":883,"o":"m 883 406 q 771 105 883 224 q 479 -14 659 -14 q 198 105 307 -14 q 90 406 90 224 l 90 581 q 198 882 90 762 q 479 1002 307 1002 q 771 882 659 1002 q 883 581 883 762 l 883 406 m 749 583 q 676 805 749 718 q 479 892 603 892 q 293 805 363 892 q 224 583 224 718 l 224 406 q 293 182 224 269 q 479 95 363 95 q 676 181 604 95 q 749 406 749 268 l 749 583 m 87 1119 l 229 1119 l 110 861 l 46 861 l 87 1119 z "},"Ύ":{"ha":916,"x_min":-86,"x_max":902,"o":"m 492 486 l 749 987 l 902 987 l 555 347 l 555 0 l 422 0 l 422 356 l 81 987 l 234 987 l 492 486 m -45 1119 l 96 1119 l -22 861 l -86 861 l -45 1119 z "},"Ώ":{"ha":940,"x_min":41,"x_max":849,"o":"m 512 108 q 661 222 608 125 q 714 490 714 319 l 714 570 q 649 812 714 728 q 468 897 583 897 q 288 812 353 897 q 223 570 223 728 l 223 490 q 278 222 223 319 q 429 108 332 124 l 429 0 l 94 0 l 94 104 l 252 104 q 133 274 176 170 q 90 490 90 378 l 90 568 q 194 881 90 761 q 468 1002 299 1002 q 742 881 637 1002 q 848 568 848 761 l 848 490 q 804 274 848 378 q 686 104 761 170 l 849 104 l 849 0 l 512 0 l 512 108 m 83 1119 l 224 1119 l 105 861 l 41 861 l 83 1119 z "},"ΐ":{"ha":456,"x_min":-35,"x_max":446,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 446 803 l 311 803 l 311 939 l 446 939 l 446 803 m 100 803 l -35 803 l -35 939 l 100 939 l 100 803 m 193 1124 l 341 1124 l 255 947 l 159 947 l 193 1124 z "},"Α":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"Β":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"Γ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"Δ":{"ha":981,"x_min":20,"x_max":944,"o":"m 439 987 l 553 987 l 944 0 l 20 0 l 439 987 m 199 104 l 768 104 l 496 817 l 492 817 l 199 104 z "},"Ε":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"Ζ":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 z "},"Η":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"Θ":{"ha":947,"x_min":77,"x_max":869,"o":"m 650 447 l 305 447 l 305 551 l 650 551 l 650 447 m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Ι":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Κ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"Λ":{"ha":912,"x_min":33,"x_max":873,"o":"m 455 794 l 451 794 l 170 0 l 33 0 l 395 987 l 511 987 l 873 0 l 736 0 l 455 794 z "},"Μ":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"Ν":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 z "},"Ξ":{"ha":795,"x_min":83,"x_max":719,"o":"m 83 104 l 719 104 l 719 0 l 83 0 l 83 104 m 140 565 l 656 565 l 656 460 l 140 460 l 140 565 m 84 987 l 711 987 l 711 882 l 84 882 l 84 987 z "},"Ο":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Π":{"ha":992,"x_min":122,"x_max":870,"o":"m 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 0 z "},"Ρ":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"Σ":{"ha":795,"x_min":47,"x_max":743,"o":"m 514 485 l 212 109 l 214 105 l 743 105 l 743 0 l 47 0 l 47 100 l 373 494 l 47 888 l 47 987 l 709 987 l 709 882 l 214 882 l 212 879 l 514 501 l 514 485 z "},"Τ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"Υ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"Φ":{"ha":976,"x_min":57,"x_max":920,"o":"m 556 833 q 816 734 712 830 q 920 490 920 638 q 816 244 920 340 q 556 144 712 147 l 556 0 l 422 0 l 422 144 q 161 243 265 146 q 57 489 57 339 q 161 734 57 637 q 422 834 265 831 l 422 987 l 556 987 l 556 833 m 189 489 q 249 316 189 380 q 418 254 309 253 l 422 255 l 422 724 l 418 725 q 250 662 310 726 q 189 489 189 597 m 787 490 q 727 662 787 598 q 560 725 667 725 l 556 724 l 556 255 l 560 254 q 727 317 667 253 q 787 490 787 381 z "},"Χ":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Ψ":{"ha":960,"x_min":59,"x_max":886,"o":"m 534 329 l 538 328 q 694 412 635 340 q 753 600 753 484 l 753 987 l 886 987 l 886 600 q 789 333 886 434 q 534 216 692 233 l 534 0 l 399 0 l 399 217 q 152 334 245 233 q 59 600 59 434 l 59 987 l 192 987 l 192 600 q 248 414 192 485 q 395 329 303 342 l 399 330 l 399 987 l 534 987 l 534 329 z "},"Ω":{"ha":926,"x_min":76,"x_max":836,"o":"m 498 108 q 647 222 594 125 q 701 490 701 319 l 701 570 q 635 812 701 728 q 454 897 570 897 q 275 812 340 897 q 210 570 210 728 l 210 490 q 264 222 210 319 q 416 108 319 124 l 416 0 l 80 0 l 80 104 l 239 104 q 119 274 163 170 q 76 490 76 378 l 76 568 q 181 881 76 761 q 454 1002 286 1002 q 729 881 623 1002 q 834 568 834 761 l 834 490 q 791 274 834 378 q 673 104 747 170 l 836 104 l 836 0 l 498 0 l 498 108 z "},"Ϊ":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ϋ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 658 1088 l 509 1088 l 509 1223 l 658 1223 l 658 1088 m 339 1088 l 191 1088 l 191 1223 l 339 1223 l 339 1088 z "},"ά":{"ha":785,"x_min":66,"x_max":781,"o":"m 679 733 l 679 187 q 693 119 679 138 q 730 100 707 100 q 749 101 741 100 q 766 105 758 102 l 781 9 q 741 -9 762 -5 q 696 -14 721 -14 q 611 7 645 -14 q 561 76 577 29 q 472 8 524 31 q 353 -14 420 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 475 723 422 747 q 565 652 528 699 l 600 733 l 679 733 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 114 441 90 q 545 182 519 138 l 545 187 l 545 554 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 m 427 1124 l 568 1124 l 449 866 l 385 866 l 427 1124 z "},"έ":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 m 376 1123 l 517 1123 l 399 865 l 335 865 l 376 1123 z "},"ή":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 623 681 559 747 q 687 463 687 615 l 687 -281 l 553 -281 l 553 460 q 515 600 553 557 q 397 642 476 642 q 298 620 340 642 q 231 556 256 597 l 231 0 l 97 0 l 97 734 l 216 734 m 395 1124 l 536 1124 l 417 866 l 353 866 l 395 1124 z "},"ί":{"ha":456,"x_min":134,"x_max":425,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 208 1111 l 349 1111 l 230 853 l 166 853 l 208 1111 z "},"ΰ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 646 803 l 511 803 l 511 939 l 646 939 l 646 803 m 300 803 l 166 803 l 166 939 l 300 939 l 300 803 m 394 1124 l 542 1124 l 456 947 l 359 947 l 394 1124 z "},"α":{"ha":785,"x_min":66,"x_max":781,"o":"m 679 733 l 679 187 q 693 119 679 138 q 730 100 707 100 q 749 101 741 100 q 766 105 758 102 l 781 9 q 741 -9 762 -5 q 696 -14 721 -14 q 611 7 645 -14 q 561 76 577 29 q 472 8 524 31 q 353 -14 420 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 475 723 422 747 q 565 652 528 699 l 600 733 l 679 733 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 114 441 90 q 545 182 519 138 l 545 187 l 545 554 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 z "},"β":{"ha":823,"x_min":106,"x_max":748,"o":"m 412 1002 q 624 927 544 1002 q 704 734 704 852 q 670 621 704 673 q 577 538 636 569 q 703 442 658 509 q 748 288 748 376 q 662 66 748 146 q 439 -14 576 -14 q 332 1 384 -14 q 240 47 280 16 l 240 -260 l 106 -260 l 106 728 q 197 921 106 840 q 412 1002 287 1002 m 409 583 q 531 624 492 583 q 570 737 570 665 q 528 849 570 802 q 412 897 486 897 q 290 848 339 897 q 240 728 240 800 l 240 165 q 318 110 270 130 q 426 90 366 90 q 564 144 513 90 q 614 286 614 199 q 570 420 614 361 q 450 478 526 478 l 353 478 l 353 583 l 409 583 z "},"γ":{"ha":699,"x_min":31,"x_max":675,"o":"m 539 734 l 675 734 l 421 42 l 421 -283 l 288 -283 l 288 45 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 z "},"δ":{"ha":789,"x_min":66,"x_max":723,"o":"m 636 987 l 636 885 l 328 885 l 327 881 l 578 693 q 685 559 647 642 q 723 374 723 475 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 137 621 66 519 q 334 743 209 722 l 339 743 l 340 747 l 139 907 l 139 987 l 636 987 m 393 642 q 249 566 298 642 q 199 374 199 489 l 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 z "},"ε":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 z "},"ζ":{"ha":726,"x_min":78,"x_max":658,"o":"m 658 987 l 658 911 l 426 618 q 300 438 338 515 q 262 262 262 361 q 297 144 262 184 q 402 104 333 104 l 424 104 q 593 61 532 104 q 654 -68 654 18 q 600 -200 652 -144 q 473 -288 547 -256 l 418 -201 q 495 -151 463 -180 q 527 -75 527 -122 q 499 -20 527 -39 q 410 0 471 0 l 375 0 q 191 73 254 0 q 128 259 128 145 q 175 449 128 340 q 320 673 222 558 l 502 879 l 500 882 l 78 882 l 78 987 l 658 987 z "},"η":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 623 681 559 747 q 687 463 687 615 l 687 -281 l 553 -281 l 553 460 q 515 600 553 557 q 397 642 476 642 q 298 620 340 642 q 231 556 256 597 l 231 0 l 97 0 l 97 734 l 216 734 z "},"θ":{"ha":790,"x_min":81,"x_max":709,"o":"m 709 376 q 626 86 709 187 q 396 -14 542 -14 q 165 87 250 -14 q 81 376 81 188 l 81 610 q 165 900 81 799 q 395 1002 250 1002 q 625 900 541 1002 q 709 610 709 799 l 709 376 m 214 545 l 576 545 l 576 639 q 529 832 576 767 q 395 897 483 897 q 260 832 307 897 q 214 639 214 767 l 214 545 m 576 439 l 214 439 l 214 349 q 261 156 214 221 q 396 90 308 90 q 530 155 484 90 q 576 349 576 220 l 576 439 z "},"ι":{"ha":456,"x_min":134,"x_max":425,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 z "},"κ":{"ha":776,"x_min":104,"x_max":738,"o":"m 306 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 714 734 l 715 730 l 410 382 l 738 3 l 736 0 l 573 0 l 306 311 z "},"λ":{"ha":789,"x_min":38,"x_max":758,"o":"m 186 0 l 38 0 l 303 697 l 266 795 q 215 888 244 852 q 144 924 185 924 q 119 923 138 924 q 98 921 101 921 l 98 1019 q 133 1026 112 1023 q 171 1029 155 1029 q 310 975 258 1029 q 389 846 362 920 l 633 205 q 672 128 648 157 q 726 100 696 100 q 739 100 736 100 q 758 103 742 100 l 756 -1 q 736 -8 749 -5 q 716 -12 724 -12 q 588 33 636 -12 q 503 172 539 77 l 366 532 l 362 531 l 342 454 l 186 0 z "},"μ":{"ha":789,"x_min":104,"x_max":685,"o":"m 237 734 l 237 298 q 276 134 238 178 q 381 90 313 90 q 489 115 448 90 q 551 186 530 139 l 551 734 l 685 734 l 685 0 l 565 0 l 559 73 q 486 8 529 31 q 387 -14 443 -14 q 301 -3 338 -14 q 237 32 264 8 l 237 -282 l 104 -282 l 104 734 l 237 734 z "},"ν":{"ha":699,"x_min":31,"x_max":675,"o":"m 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 406 0 l 304 0 l 31 734 l 168 734 l 342 216 z "},"ξ":{"ha":707,"x_min":58,"x_max":675,"o":"m 625 987 l 625 882 l 425 882 q 299 833 341 877 q 256 724 256 789 q 306 624 256 660 q 459 589 355 589 l 556 589 l 556 484 l 459 484 q 266 431 332 484 q 200 274 200 378 q 254 142 200 193 q 404 91 309 91 l 445 91 q 614 48 554 91 q 675 -83 675 5 q 621 -214 673 -158 q 494 -301 568 -269 l 441 -215 q 517 -165 485 -194 q 549 -89 549 -136 q 525 -33 549 -54 q 450 -13 501 -13 l 404 -13 q 160 64 254 -13 q 66 277 66 141 q 120 442 66 375 q 277 538 174 509 q 163 613 204 566 q 123 721 123 661 q 143 814 123 773 q 204 882 164 855 l 58 882 l 58 987 l 625 987 z "},"ο":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"π":{"ha":828,"x_min":54,"x_max":833,"o":"m 758 628 l 675 628 l 675 182 q 693 113 675 132 q 741 94 711 94 q 774 99 758 94 q 803 112 791 104 l 833 22 q 772 -7 802 1 q 706 -14 742 -14 q 583 34 625 -14 q 541 189 541 82 l 541 628 l 273 628 l 273 0 l 140 0 l 140 628 l 54 628 l 54 734 l 758 734 l 758 628 z "},"ρ":{"ha":789,"x_min":97,"x_max":719,"o":"m 719 339 q 645 83 719 180 q 437 -14 570 -14 q 319 6 371 -14 q 231 66 268 26 l 231 -282 l 97 -282 l 97 391 l 97 391 q 184 653 97 559 q 401 747 271 747 q 637 640 555 747 q 719 353 719 532 l 719 339 m 585 353 q 541 562 585 481 q 401 642 496 642 q 274 564 318 642 q 231 391 231 486 l 231 186 q 296 116 256 141 q 396 90 337 90 q 538 159 490 90 q 585 339 585 229 l 585 353 z "},"ς":{"ha":747,"x_min":66,"x_max":673,"o":"m 389 747 q 594 676 515 747 q 671 485 673 606 l 669 481 l 549 481 q 507 596 549 551 q 389 642 466 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 256 188 200 256 q 424 103 311 119 q 603 50 546 85 q 660 -70 660 14 q 606 -201 659 -145 q 479 -288 553 -256 l 425 -202 q 501 -152 469 -181 q 533 -76 533 -123 q 503 -26 533 -43 q 409 0 473 -9 q 153 115 239 24 q 66 353 66 207 l 66 381 q 153 641 66 535 q 389 747 239 747 z "},"σ":{"ha":789,"x_min":66,"x_max":779,"o":"m 779 628 l 591 628 q 689 507 654 579 q 723 347 723 435 l 723 332 q 633 88 723 190 q 395 -14 544 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 154 632 66 530 q 393 734 241 733 l 779 734 l 779 628 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 555 589 483 q 393 628 490 628 q 249 555 298 628 q 199 374 199 483 l 199 359 z "},"τ":{"ha":727,"x_min":55,"x_max":670,"o":"m 670 630 l 427 630 l 427 0 l 294 0 l 294 630 l 55 630 l 55 734 l 670 734 l 670 630 z "},"υ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 z "},"φ":{"ha":983,"x_min":56,"x_max":927,"o":"m 552 734 q 817 625 707 734 q 927 355 927 516 q 835 106 927 208 q 552 -11 743 5 l 552 -324 l 418 -324 l 418 -11 q 144 115 233 6 q 56 382 56 224 q 78 573 56 486 q 135 734 100 661 l 275 734 q 214 555 236 643 q 190 382 191 468 q 245 197 190 277 q 414 100 300 116 l 418 102 l 418 734 l 552 734 m 793 355 q 728 546 791 466 q 556 624 665 627 l 552 623 l 552 100 l 556 98 q 735 186 676 113 q 793 355 793 260 z "},"χ":{"ha":775,"x_min":64,"x_max":745,"o":"m 137 744 q 276 690 224 744 q 355 562 327 635 l 418 412 l 422 412 l 580 734 l 714 734 l 483 254 l 621 -79 q 664 -154 643 -129 q 714 -180 685 -180 q 727 -179 724 -180 q 745 -176 730 -178 l 743 -286 q 724 -293 736 -290 q 703 -296 711 -296 q 577 -252 624 -296 q 494 -112 530 -208 l 408 92 l 404 92 l 224 -281 l 83 -281 l 342 249 l 232 511 q 180 601 210 566 q 111 635 151 635 q 85 633 104 635 q 64 631 67 631 l 64 734 q 99 741 78 737 q 137 744 121 744 z "},"ψ":{"ha":977,"x_min":62,"x_max":920,"o":"m 546 733 l 546 101 l 550 100 q 728 197 670 115 q 787 385 787 279 q 762 556 785 469 q 702 734 740 644 l 842 734 q 899 575 877 662 q 920 385 920 488 q 830 113 920 222 q 546 -12 740 3 l 546 -321 l 412 -321 l 412 -10 q 153 109 244 7 q 62 404 62 212 l 62 734 l 195 734 l 195 403 q 253 188 195 262 q 408 101 310 114 l 412 102 l 412 733 l 546 733 z "},"ω":{"ha":1181,"x_min":73,"x_max":1107,"o":"m 318 734 q 239 555 268 643 q 207 381 210 467 q 245 171 207 252 q 364 90 283 90 q 480 145 437 90 q 523 319 523 200 l 523 522 l 657 522 l 657 319 q 700 145 657 200 q 816 90 743 90 q 935 171 897 90 q 973 381 973 251 q 941 555 970 467 q 862 734 911 643 l 1002 734 q 1078 574 1048 662 q 1107 381 1107 486 q 1038 97 1107 208 q 823 -14 970 -14 q 678 27 737 -14 q 590 149 618 68 q 502 27 561 68 q 357 -14 442 -14 q 142 97 210 -14 q 73 381 73 208 q 102 574 73 486 q 178 734 131 663 l 318 734 z "},"ϊ":{"ha":456,"x_min":-27,"x_max":440,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 440 856 l 292 856 l 292 991 l 440 991 l 440 856 m 121 856 l -27 856 l -27 991 l 121 991 l 121 856 z "},"ϋ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 641 856 l 492 856 l 492 991 l 641 991 l 641 856 m 322 856 l 174 856 l 174 991 l 322 991 l 322 856 z "},"ό":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 397 1124 l 538 1124 l 420 866 l 356 866 l 397 1124 z "},"ύ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 410 1111 l 551 1111 l 432 853 l 368 853 l 410 1111 z "},"ώ":{"ha":1181,"x_min":73,"x_max":1107,"o":"m 318 734 q 239 555 268 643 q 207 381 210 467 q 245 171 207 252 q 364 90 283 90 q 480 145 437 90 q 523 319 523 200 l 523 522 l 657 522 l 657 319 q 700 145 657 200 q 816 90 743 90 q 935 171 897 90 q 973 381 973 251 q 941 555 970 467 q 862 734 911 643 l 1002 734 q 1078 574 1048 662 q 1107 381 1107 486 q 1038 97 1107 208 q 823 -14 970 -14 q 678 27 737 -14 q 590 149 618 68 q 502 27 561 68 q 357 -14 442 -14 q 142 97 210 -14 q 73 381 73 208 q 102 574 73 486 q 178 734 131 663 l 318 734 m 593 1111 l 734 1111 l 615 853 l 551 853 l 593 1111 z "},"ϑ":{"ha":858,"x_min":77,"x_max":814,"o":"m 353 703 l 353 745 q 417 931 353 861 q 584 1002 481 1002 q 752 933 690 1002 q 814 745 814 865 l 814 401 q 710 99 814 213 q 439 -14 607 -14 q 177 104 277 -14 q 77 401 77 222 l 77 849 l 210 850 l 210 401 q 272 180 210 266 q 439 95 334 95 q 615 175 551 95 q 681 397 680 256 q 443 487 533 402 q 353 703 353 572 m 681 745 q 656 855 681 818 q 584 892 631 892 q 513 855 539 892 q 487 745 487 818 l 487 701 q 538 566 487 620 q 677 510 589 513 l 681 511 l 681 745 z "},"ϒ":{"ha":739,"x_min":-15,"x_max":751,"o":"m 503 840 q 579 960 538 924 q 676 995 621 995 q 716 991 699 995 q 751 979 733 988 l 735 876 q 724 879 732 878 q 708 881 717 881 q 667 869 686 881 q 637 831 648 857 l 434 392 l 434 0 l 301 0 l 301 392 l 98 831 q 67 869 86 857 q 27 881 49 881 q 11 879 18 881 q 0 876 3 878 l -15 979 q 18 991 1 988 q 59 995 35 995 q 155 960 113 995 q 232 840 197 924 l 353 565 l 366 517 l 370 517 l 382 565 l 503 840 z "},"ϖ":{"ha":1090,"x_min":53,"x_max":1059,"o":"m 1059 628 l 968 628 q 999 511 988 573 q 1010 381 1010 450 q 951 97 1010 209 q 768 -14 892 -14 q 631 27 687 -14 q 548 146 575 68 q 465 26 520 67 q 329 -14 409 -14 q 145 97 203 -14 q 86 381 86 209 q 97 511 86 450 q 128 628 108 573 l 53 628 l 53 734 l 1059 734 l 1059 628 m 877 381 q 861 503 875 441 q 825 628 848 565 l 272 628 q 235 503 248 564 q 220 381 221 441 q 248 171 220 252 q 336 90 276 90 q 442 145 403 90 q 481 319 481 200 l 481 488 l 616 488 l 616 319 q 655 145 616 200 q 761 90 694 90 q 848 171 820 90 q 877 381 877 252 z "},"Ѐ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 482 1058 l 375 1058 l 205 1234 l 208 1238 l 363 1238 l 482 1058 z "},"Ё":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 660 1088 l 511 1088 l 511 1224 l 660 1224 l 660 1088 m 341 1088 l 193 1088 l 193 1224 l 341 1224 l 341 1088 z "},"Ђ":{"ha":1042,"x_min":28,"x_max":990,"o":"m 795 882 l 444 882 l 444 570 q 549 595 498 585 q 637 605 601 605 q 897 523 804 605 q 990 294 990 441 q 904 71 990 147 q 656 -4 818 -6 l 653 -3 l 652 93 q 809 144 762 93 q 857 294 857 196 q 801 442 855 391 q 637 493 746 493 q 544 485 595 493 q 444 462 492 476 l 444 0 l 311 0 l 311 882 l 28 882 l 28 987 l 795 987 l 795 882 z "},"Ѓ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 m 489 1236 l 645 1236 l 646 1232 l 463 1055 l 363 1055 l 489 1236 z "},"Є":{"ha":940,"x_min":92,"x_max":836,"o":"m 831 317 l 833 313 q 735 80 836 174 q 470 -14 635 -14 q 197 105 302 -14 q 92 406 92 224 l 92 582 q 197 883 92 764 q 470 1002 302 1002 q 736 913 637 1002 q 833 677 836 824 l 831 673 l 703 673 q 642 837 703 777 q 470 897 581 897 q 292 807 358 897 q 225 583 225 717 l 225 551 l 606 551 l 606 447 l 225 447 l 225 406 q 292 181 225 271 q 470 91 358 91 q 642 151 581 91 q 703 317 703 211 l 831 317 z "},"Ѕ":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 z "},"І":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ї":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ј":{"ha":766,"x_min":41,"x_max":653,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 z "},"Љ":{"ha":1493,"x_min":34,"x_max":1438,"o":"m 862 987 l 862 603 l 1105 603 q 1351 519 1263 603 q 1438 301 1438 435 q 1351 83 1438 167 q 1105 0 1263 0 l 728 0 l 728 882 l 387 882 l 386 522 q 312 125 385 250 q 69 0 239 0 l 34 0 l 34 104 l 61 104 q 208 201 163 104 q 253 522 252 298 l 253 987 l 862 987 m 862 498 l 862 104 l 1105 104 q 1255 162 1205 104 q 1305 302 1305 219 q 1255 441 1305 385 q 1105 498 1205 498 l 862 498 z "},"Њ":{"ha":1500,"x_min":122,"x_max":1446,"o":"m 256 561 l 736 561 l 736 987 l 869 987 l 869 570 l 1114 570 q 1359 492 1272 570 q 1446 288 1446 415 q 1358 80 1446 159 q 1114 0 1271 0 l 736 0 l 736 456 l 256 456 l 256 0 l 122 0 l 122 987 l 256 987 l 256 561 m 869 465 l 869 112 l 1114 112 q 1263 162 1213 112 q 1313 289 1313 212 q 1263 415 1313 365 q 1114 465 1213 465 l 869 465 z "},"Ћ":{"ha":1133,"x_min":45,"x_max":1016,"o":"m 812 882 l 454 882 l 454 589 q 559 609 506 602 q 684 616 613 616 q 931 544 847 616 q 1016 312 1016 472 l 1016 0 l 882 0 l 882 312 q 835 467 882 423 q 684 510 788 510 q 567 503 623 510 q 454 481 510 495 l 454 0 l 321 0 l 321 882 l 45 882 l 45 987 l 812 987 l 812 882 z "},"Ќ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 481 1236 l 637 1236 l 638 1232 l 455 1055 l 355 1055 l 481 1236 z "},"Ѝ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 553 1058 l 446 1058 l 277 1234 l 279 1238 l 435 1238 l 553 1058 z "},"Ў":{"ha":880,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 637 1268 l 639 1264 q 587 1136 642 1185 q 439 1086 532 1086 q 290 1136 345 1086 q 239 1264 235 1185 l 240 1268 l 342 1268 q 366 1196 342 1223 q 439 1168 389 1168 q 511 1196 488 1168 q 535 1268 535 1224 l 637 1268 z "},"Џ":{"ha":992,"x_min":122,"x_max":869,"o":"m 122 987 l 256 987 l 256 104 l 736 104 l 736 987 l 869 987 l 869 0 l 568 0 l 568 -243 l 434 -243 l 434 0 l 122 0 l 122 987 z "},"А":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"Б":{"ha":876,"x_min":111,"x_max":821,"o":"m 716 882 l 244 882 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 987 l 716 987 l 716 882 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"В":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"Г":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"Д":{"ha":1051,"x_min":33,"x_max":1013,"o":"m 877 104 l 1013 104 l 999 -242 l 879 -242 l 879 0 l 166 0 l 166 -243 l 53 -243 l 33 104 l 117 104 q 213 278 170 163 q 260 588 256 392 l 283 987 l 877 987 l 877 104 m 395 588 q 355 304 389 427 q 266 104 320 181 l 743 104 l 743 882 l 411 882 l 395 588 z "},"Е":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"Ж":{"ha":1267,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 z "},"З":{"ha":939,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 z "},"И":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 z "},"Й":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 696 1268 l 698 1264 q 646 1136 701 1185 q 498 1086 591 1086 q 349 1136 404 1086 q 298 1264 294 1185 l 299 1268 l 401 1268 q 425 1196 401 1223 q 498 1168 448 1168 q 570 1196 547 1168 q 594 1268 594 1224 l 696 1268 z "},"К":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"Л":{"ha":984,"x_min":33,"x_max":862,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 z "},"М":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"Н":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"О":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"П":{"ha":992,"x_min":122,"x_max":870,"o":"m 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 0 z "},"Р":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"С":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 z "},"Т":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"У":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 z "},"Ф":{"ha":1079,"x_min":58,"x_max":1029,"o":"m 610 889 l 628 889 q 914 778 799 889 q 1029 492 1029 666 q 914 204 1029 316 q 628 92 799 92 l 610 92 l 610 -41 l 477 -41 l 477 92 l 457 92 q 172 203 286 92 q 58 490 58 315 q 172 777 58 665 q 457 889 286 889 l 477 889 l 477 1028 l 610 1028 l 610 889 m 457 784 q 259 705 329 784 q 190 490 190 625 q 259 275 190 354 q 457 197 329 197 l 477 197 l 477 784 l 457 784 m 610 784 l 610 197 l 629 197 q 826 276 755 197 q 896 492 896 355 q 826 705 896 626 q 629 784 755 784 l 610 784 z "},"Х":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Ц":{"ha":1069,"x_min":123,"x_max":1015,"o":"m 1015 -243 l 881 -243 l 881 109 l 1015 109 l 1015 -243 m 123 987 l 257 987 l 257 105 l 737 105 l 737 987 l 871 987 l 871 0 l 123 0 l 123 987 z "},"Ч":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 z "},"Ш":{"ha":1310,"x_min":122,"x_max":1188,"o":"m 256 987 l 256 104 l 589 104 l 589 987 l 722 987 l 722 104 l 1055 104 l 1055 987 l 1188 987 l 1188 0 l 122 0 l 122 987 l 256 987 z "},"Щ":{"ha":1352,"x_min":122,"x_max":1293,"o":"m 256 987 l 256 104 l 589 104 l 589 987 l 722 987 l 722 104 l 1055 104 l 1055 987 l 1188 987 l 1188 105 l 1293 105 l 1280 -238 l 1161 -238 l 1161 0 l 122 0 l 122 987 l 256 987 z "},"Ъ":{"ha":1050,"x_min":15,"x_max":996,"o":"m 15 987 l 418 987 l 418 583 l 662 583 q 908 505 821 583 q 996 294 996 427 q 908 81 996 161 q 662 0 820 0 l 285 0 l 285 882 l 15 882 l 15 987 m 418 478 l 418 104 l 662 104 q 812 159 762 104 q 862 296 862 213 q 812 427 862 376 q 662 478 762 478 l 418 478 z "},"Ы":{"ha":1208,"x_min":122,"x_max":1087,"o":"m 256 597 l 499 597 q 745 516 657 597 q 833 301 833 435 q 745 83 833 167 q 499 0 656 0 l 122 0 l 122 987 l 256 987 l 256 597 m 256 492 l 256 104 l 499 104 q 649 162 599 104 q 699 302 699 219 q 649 437 699 383 q 499 492 600 492 l 256 492 m 1087 0 l 953 0 l 953 987 l 1087 987 l 1087 0 z "},"Ь":{"ha":875,"x_min":111,"x_max":821,"o":"m 244 583 l 488 583 q 734 505 646 583 q 821 294 821 427 q 733 81 821 161 q 488 0 646 0 l 111 0 l 111 987 l 244 987 l 244 583 m 244 478 l 244 104 l 488 104 q 638 159 588 104 q 688 296 688 213 q 638 427 688 376 q 488 478 588 478 l 244 478 z "},"Э":{"ha":939,"x_min":123,"x_max":867,"o":"m 127 671 l 126 675 q 223 908 123 813 q 488 1002 324 1002 q 762 883 656 1002 q 867 582 867 764 l 867 406 q 762 105 867 223 q 488 -14 656 -14 q 222 75 321 -14 q 126 311 123 163 l 127 315 l 256 315 q 316 151 256 211 q 488 91 376 91 q 666 181 600 91 q 733 405 733 271 l 733 460 l 344 460 l 344 565 l 733 565 l 733 582 q 666 807 733 716 q 488 897 600 897 q 316 837 376 897 q 256 671 256 777 l 127 671 z "},"Ю":{"ha":1238,"x_min":129,"x_max":1195,"o":"m 1195 406 q 1083 105 1195 224 q 791 -14 970 -14 q 510 105 618 -14 q 402 406 402 224 l 402 429 l 263 429 l 263 0 l 129 0 l 129 987 l 263 987 l 263 533 l 402 533 l 402 581 q 510 882 402 762 q 791 1002 618 1002 q 1083 882 970 1002 q 1195 581 1195 762 l 1195 406 m 1061 583 q 988 805 1061 718 q 791 892 915 892 q 605 805 675 892 q 536 583 536 718 l 536 406 q 605 182 536 269 q 791 95 675 95 q 988 181 916 95 q 1061 406 1061 268 l 1061 583 z "},"Я":{"ha":890,"x_min":67,"x_max":769,"o":"m 209 0 l 67 0 l 298 422 q 153 526 203 458 q 103 690 103 594 q 196 909 103 831 q 453 987 290 987 l 769 987 l 769 0 l 635 0 l 635 386 l 414 386 l 209 0 m 635 882 l 453 882 q 292 831 347 882 q 237 692 237 781 q 292 547 237 603 q 452 492 348 492 l 635 492 l 635 882 z "},"а":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 z "},"б":{"ha":768,"x_min":66,"x_max":723,"o":"m 420 692 q 642 598 560 692 q 723 359 723 505 l 723 345 q 635 86 723 186 q 395 -14 547 -14 q 154 86 243 -14 q 66 345 66 186 l 66 439 q 157 785 66 656 q 401 945 248 914 q 525 982 487 960 q 564 1053 564 1004 l 667 1053 l 668 1049 q 612 892 670 930 q 427 833 553 855 q 269 762 334 818 q 188 599 203 707 l 191 595 q 291 665 231 638 q 420 692 351 692 m 393 587 q 249 522 298 587 q 200 359 200 457 l 200 345 q 249 162 200 234 q 395 90 298 90 q 540 162 490 90 q 589 345 589 234 l 589 359 q 539 522 589 457 q 393 587 490 587 z "},"в":{"ha":789,"x_min":98,"x_max":718,"o":"m 98 0 l 98 734 l 387 734 q 612 683 532 734 q 692 532 692 633 q 661 440 692 480 q 573 378 630 399 q 680 315 642 361 q 718 209 718 269 q 643 53 718 106 q 434 0 568 0 l 98 0 m 231 321 l 231 103 l 434 103 q 546 131 508 103 q 584 212 584 159 q 546 293 584 264 q 434 321 508 321 l 231 321 m 231 424 l 388 424 q 516 449 473 424 q 559 525 559 474 q 515 604 559 577 q 387 630 472 630 l 231 630 l 231 424 z "},"г":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 z "},"д":{"ha":846,"x_min":31,"x_max":803,"o":"m 89 104 q 175 240 145 170 q 215 458 205 311 l 226 734 l 705 734 l 705 104 l 803 104 l 790 -216 l 669 -216 l 669 0 l 165 0 l 165 -216 l 44 -216 l 31 104 l 89 104 m 349 458 q 314 250 341 336 q 243 104 286 163 l 572 104 l 572 615 l 355 615 l 349 458 z "},"е":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 z "},"ж":{"ha":1063,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 z "},"з":{"ha":705,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 z "},"и":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 z "},"й":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 593 1036 l 595 1031 q 543 903 597 953 q 395 854 488 854 q 246 903 301 854 q 195 1031 191 953 l 196 1036 l 298 1036 q 321 963 298 991 q 395 935 345 935 q 467 963 444 935 q 491 1036 491 991 l 593 1036 z "},"к":{"ha":736,"x_min":104,"x_max":739,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 z "},"л":{"ha":768,"x_min":18,"x_max":692,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 z "},"м":{"ha":1036,"x_min":104,"x_max":926,"o":"m 515 175 l 519 175 l 759 734 l 926 734 l 926 0 l 792 0 l 792 509 l 788 511 l 563 0 l 471 0 l 241 522 l 237 521 l 237 0 l 104 0 l 104 734 l 276 734 l 515 175 z "},"н":{"ha":789,"x_min":97,"x_max":691,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 z "},"о":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"п":{"ha":789,"x_min":97,"x_max":692,"o":"m 692 0 l 558 0 l 558 628 l 231 628 l 231 0 l 97 0 l 97 734 l 692 734 l 692 0 z "},"р":{"ha":789,"x_min":97,"x_max":722,"o":"m 722 339 q 646 83 722 180 q 437 -14 570 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 199 734 l 220 639 q 310 720 256 692 q 435 747 363 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 540 160 491 90 q 589 339 589 229 l 589 353 z "},"с":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 z "},"т":{"ha":711,"x_min":48,"x_max":663,"o":"m 663 630 l 420 630 l 420 0 l 287 0 l 287 630 l 48 630 l 48 734 l 663 734 l 663 630 z "},"у":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 z "},"ф":{"ha":1007,"x_min":66,"x_max":941,"o":"m 66 353 q 136 640 66 532 q 334 747 205 747 q 385 743 361 747 q 431 730 409 738 l 431 1058 l 564 1058 l 564 726 q 614 742 587 736 q 673 747 642 747 q 871 640 802 747 q 941 353 941 532 l 941 339 q 871 82 941 179 q 675 -14 802 -14 q 615 -9 643 -14 q 564 6 587 -3 l 564 -282 l 431 -282 l 431 3 q 385 -10 409 -5 q 332 -14 360 -14 q 136 82 205 -14 q 66 339 66 179 l 66 353 m 807 353 q 764 562 807 481 q 633 642 721 642 q 595 639 612 642 q 564 631 578 636 l 564 100 q 595 93 578 95 q 634 90 612 90 q 765 159 722 90 q 807 339 807 227 l 807 353 m 200 339 q 239 158 200 226 q 366 90 278 90 q 401 93 385 90 q 431 100 417 95 l 431 633 q 401 640 417 637 q 368 642 386 642 q 240 562 279 642 q 200 353 200 482 l 200 339 z "},"х":{"ha":699,"x_min":31,"x_max":665,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 z "},"ц":{"ha":826,"x_min":97,"x_max":781,"o":"m 97 734 l 231 734 l 231 104 l 558 104 l 558 734 l 692 734 l 692 104 l 781 104 l 781 -240 l 648 -240 l 648 0 l 97 0 l 97 734 z "},"ч":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 z "},"ш":{"ha":1116,"x_min":97,"x_max":1015,"o":"m 231 734 l 231 104 l 489 104 l 489 734 l 623 734 l 623 104 l 881 104 l 881 734 l 1015 734 l 1015 0 l 97 0 l 97 734 l 231 734 z "},"щ":{"ha":1175,"x_min":97,"x_max":1134,"o":"m 231 734 l 231 104 l 489 104 l 489 734 l 623 734 l 623 104 l 881 104 l 881 734 l 1015 734 l 1015 98 l 1134 98 l 1134 -229 l 1002 -229 l 1002 0 l 97 0 l 97 734 l 231 734 z "},"ъ":{"ha":860,"x_min":20,"x_max":825,"o":"m 368 481 l 551 481 q 753 415 681 481 q 825 243 825 349 q 752 68 825 137 q 551 0 680 0 l 235 0 l 235 629 l 20 629 l 20 734 l 368 734 l 368 481 m 368 377 l 368 104 l 551 104 q 657 143 623 104 q 691 239 691 181 q 656 336 691 294 q 551 377 622 377 l 368 377 z "},"ы":{"ha":1097,"x_min":117,"x_max":971,"o":"m 251 454 l 433 454 q 636 392 564 454 q 708 230 708 330 q 635 65 708 130 q 433 0 562 0 l 117 0 l 117 734 l 251 734 l 251 454 m 971 0 l 838 0 l 838 734 l 971 734 l 971 0 m 251 350 l 251 104 l 433 104 q 539 139 505 104 q 574 225 574 174 q 539 313 574 275 q 433 350 505 350 l 251 350 z "},"ь":{"ha":755,"x_min":104,"x_max":694,"o":"m 237 454 l 420 454 q 622 392 550 454 q 694 230 694 330 q 622 65 694 130 q 420 0 549 0 l 104 0 l 104 734 l 237 734 l 237 454 m 237 350 l 237 104 l 420 104 q 526 139 492 104 q 560 225 560 174 q 526 313 560 275 q 420 350 491 350 l 237 350 z "},"э":{"ha":747,"x_min":67,"x_max":675,"o":"m 353 642 q 242 601 290 642 q 193 500 193 559 l 73 500 l 71 505 q 154 673 67 599 q 353 747 240 747 q 588 641 502 747 q 675 381 675 535 l 675 353 q 588 92 675 198 q 353 -14 501 -14 q 148 61 229 -14 q 71 248 67 137 l 72 252 l 193 252 q 239 137 193 184 q 353 90 285 90 q 485 157 438 90 q 537 318 532 223 l 535 321 l 274 321 l 274 426 l 534 426 l 535 429 q 482 580 528 518 q 353 642 435 642 z "},"ю":{"ha":1133,"x_min":104,"x_max":1066,"o":"m 237 424 l 411 424 q 511 657 425 568 q 736 747 597 747 q 978 642 889 747 q 1066 374 1066 537 l 1066 359 q 978 90 1066 195 q 738 -14 890 -14 q 508 78 595 -14 q 410 319 421 171 l 237 319 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 m 543 359 q 592 166 543 242 q 738 90 641 90 q 883 166 833 90 q 932 359 932 242 l 932 374 q 883 566 932 489 q 736 642 833 642 q 592 566 641 642 q 543 374 543 489 l 543 359 z "},"я":{"ha":789,"x_min":53,"x_max":685,"o":"m 685 734 l 685 0 l 551 0 l 551 285 l 370 285 l 197 0 l 53 0 l 239 304 q 131 384 169 330 q 94 509 94 438 q 169 671 94 608 q 375 734 243 734 l 685 734 m 228 508 q 261 425 228 459 q 361 390 294 390 l 551 390 l 551 630 l 375 630 q 265 594 302 630 q 228 508 228 557 z "},"ѐ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 435 840 l 328 840 l 158 1017 l 160 1021 l 316 1021 l 435 840 z "},"ё":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 612 871 l 464 871 l 464 1006 l 612 1006 l 612 871 m 294 871 l 145 871 l 145 1006 l 294 1006 l 294 871 z "},"ђ":{"ha":789,"x_min":-17,"x_max":695,"o":"m 418 819 l 231 819 l 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 309 l 695 309 l 695 -60 q 637 -235 695 -174 q 474 -296 578 -296 q 434 -294 452 -296 q 397 -285 416 -291 l 408 -182 q 439 -189 416 -186 q 474 -191 463 -191 q 538 -156 515 -191 q 561 -60 561 -121 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 819 l -17 819 l -17 924 l 97 924 l 97 1058 l 231 1058 l 231 924 l 418 924 l 418 819 z "},"ѓ":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 m 351 1003 l 507 1003 l 508 999 l 325 823 l 225 823 l 351 1003 z "},"є":{"ha":748,"x_min":73,"x_max":694,"o":"m 402 90 q 519 131 469 90 q 570 232 570 172 l 690 232 l 691 228 q 607 59 694 133 q 402 -14 519 -14 q 157 90 242 -14 q 73 353 73 195 l 73 381 q 158 643 73 538 q 402 747 243 747 q 613 671 531 747 q 692 485 694 595 l 690 481 l 570 481 q 522 595 570 548 q 402 642 475 642 q 261 579 307 642 q 212 422 215 515 l 213 418 l 468 418 l 468 314 l 213 314 l 212 311 q 260 153 215 215 q 402 90 306 90 z "},"ѕ":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 z "},"і":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 z "},"ї":{"ha":349,"x_min":-61,"x_max":406,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 406 856 l 258 856 l 258 991 l 406 991 l 406 856 m 87 856 l -61 856 l -61 991 l 87 991 l 87 856 z "},"ј":{"ha":359,"x_min":-45,"x_max":251,"o":"m 251 734 l 251 -60 q 193 -235 251 -174 q 31 -296 134 -296 q -8 -293 9 -296 q -45 -284 -25 -290 l -35 -179 q -8 -185 -26 -182 q 21 -187 9 -187 q 91 -157 65 -187 q 117 -60 117 -127 l 117 734 l 251 734 m 247 924 l 113 924 l 113 1058 l 247 1058 l 247 924 z "},"љ":{"ha":1208,"x_min":44,"x_max":1149,"o":"m 692 734 l 692 455 l 874 455 q 1076 392 1004 455 q 1149 230 1149 330 q 1076 65 1149 130 q 874 0 1003 0 l 558 0 l 558 628 l 342 628 l 342 420 q 282 102 342 205 q 82 0 222 0 l 44 0 l 47 107 l 75 108 q 178 182 146 108 q 209 420 209 256 l 209 734 l 692 734 m 692 351 l 692 103 l 874 103 q 980 141 945 103 q 1015 231 1015 179 q 980 316 1015 281 q 874 351 946 351 l 692 351 z "},"њ":{"ha":1208,"x_min":97,"x_max":1148,"o":"m 231 457 l 557 457 l 557 734 l 691 734 l 691 454 l 873 454 q 1076 392 1004 454 q 1148 230 1148 330 q 1075 65 1148 130 q 873 0 1002 0 l 557 0 l 557 353 l 231 353 l 231 0 l 97 0 l 97 734 l 231 734 l 231 457 m 691 351 l 691 103 l 873 103 q 979 141 945 103 q 1014 231 1014 179 q 980 316 1014 281 q 873 351 945 351 l 691 351 z "},"ћ":{"ha":789,"x_min":13,"x_max":694,"o":"m 448 821 l 231 821 l 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 821 l 13 821 l 13 926 l 97 926 l 97 1058 l 231 1058 l 231 926 l 448 926 l 448 821 z "},"ќ":{"ha":736,"x_min":104,"x_max":739,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 m 430 1002 l 586 1002 l 587 998 l 404 822 l 304 822 l 430 1002 z "},"ѝ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 450 825 l 343 825 l 174 1002 l 176 1006 l 332 1006 l 450 825 z "},"ў":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 548 1036 l 549 1031 q 497 903 552 953 q 349 854 442 854 q 201 903 256 854 q 149 1031 146 953 l 151 1036 l 253 1036 q 276 963 253 991 q 349 935 299 935 q 422 963 398 935 q 446 1036 446 991 l 548 1036 z "},"џ":{"ha":789,"x_min":97,"x_max":692,"o":"m 231 734 l 231 104 l 558 104 l 558 734 l 692 734 l 692 0 l 463 0 l 463 -241 l 330 -241 l 330 0 l 97 0 l 97 734 l 231 734 z "},"Ѡ":{"ha":1219,"x_min":106,"x_max":1113,"o":"m 1113 987 l 1113 265 q 1035 57 1113 129 q 829 -14 956 -14 q 698 15 755 -14 q 608 103 640 45 q 514 15 574 45 q 378 -14 454 -14 q 181 57 256 -14 q 106 265 106 129 l 106 987 l 240 987 l 240 265 q 278 135 240 179 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 987 l 679 987 l 679 265 q 720 135 679 180 q 829 90 762 90 q 938 135 897 90 q 979 265 979 180 l 979 987 l 1113 987 z "},"ѡ":{"ha":1058,"x_min":84,"x_max":979,"o":"m 979 734 l 979 239 q 908 51 979 115 q 722 -14 837 -14 q 610 10 659 -14 q 530 82 560 34 q 446 10 498 34 q 328 -14 394 -14 q 151 50 218 -14 q 84 239 84 115 l 84 734 l 218 734 l 218 239 q 248 128 218 165 q 328 90 277 90 q 427 128 389 90 q 465 239 465 166 l 465 734 l 599 734 l 599 239 q 632 128 599 165 q 722 90 666 90 q 811 128 777 90 q 846 239 846 166 l 846 734 l 979 734 z "},"Ѣ":{"ha":875,"x_min":-35,"x_max":821,"o":"m 400 747 l 244 747 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 747 l -35 747 l -35 852 l 111 852 l 111 987 l 244 987 l 244 852 l 400 852 l 400 747 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"ѣ":{"ha":755,"x_min":-25,"x_max":694,"o":"m 455 734 l 237 734 l 237 507 l 420 507 q 622 438 550 507 q 694 256 694 368 q 622 72 694 144 q 420 0 549 0 l 104 0 l 104 734 l -25 734 l -25 838 l 104 838 l 104 1058 l 237 1058 l 237 838 l 455 838 l 455 734 m 237 403 l 237 104 l 420 104 q 526 147 492 104 q 560 252 560 189 q 526 358 560 313 q 420 403 491 403 l 237 403 z "},"Ѥ":{"ha":1246,"x_min":130,"x_max":1138,"o":"m 264 566 l 397 566 l 397 582 q 502 883 397 764 q 775 1002 608 1002 q 1032 915 939 1002 q 1138 677 1125 828 l 1137 673 l 1008 673 q 937 839 992 781 q 775 897 882 897 q 597 807 663 897 q 530 583 530 717 l 530 566 l 896 566 l 896 460 l 530 460 l 530 406 q 597 181 530 271 q 775 91 663 91 q 938 149 883 91 q 1008 317 992 206 l 1137 317 l 1138 313 q 1031 78 1125 170 q 775 -14 937 -14 q 502 105 608 -14 q 397 406 397 224 l 397 460 l 264 460 l 264 0 l 130 0 l 130 987 l 264 987 l 264 566 z "},"ѥ":{"ha":1038,"x_min":102,"x_max":983,"o":"m 236 418 l 363 418 q 457 654 372 561 q 691 747 541 747 q 902 671 820 747 q 981 485 983 595 l 979 481 l 859 481 q 811 595 859 548 q 691 642 764 642 q 550 579 595 642 q 500 421 504 515 l 502 418 l 786 418 l 786 313 l 502 313 l 500 310 q 549 153 504 215 q 691 90 595 90 q 808 131 758 90 q 859 232 859 172 l 979 232 l 980 228 q 896 59 983 133 q 691 -14 808 -14 q 457 78 541 -14 q 363 313 373 170 l 236 313 l 236 0 l 102 0 l 102 734 l 236 734 l 236 418 z "},"Ѧ":{"ha":838,"x_min":29,"x_max":848,"o":"m 611 297 l 502 297 l 502 0 l 369 0 l 369 297 l 269 297 l 165 0 l 29 0 l 385 987 l 500 987 l 848 0 l 712 0 l 611 297 m 307 408 l 573 408 l 444 790 l 439 790 l 307 408 z "},"ѧ":{"ha":744,"x_min":9,"x_max":722,"o":"m 505 199 l 433 199 l 433 0 l 300 0 l 300 199 l 224 199 l 145 0 l 9 0 l 307 734 l 422 734 l 722 0 l 586 0 l 505 199 m 265 304 l 463 304 l 378 516 l 366 562 l 361 562 l 349 516 l 265 304 z "},"Ѩ":{"ha":1224,"x_min":142,"x_max":1207,"o":"m 276 409 l 535 409 l 744 987 l 859 987 l 1207 0 l 1071 0 l 970 297 l 861 297 l 861 0 l 728 0 l 728 297 l 627 297 l 524 0 l 388 0 l 495 298 l 276 298 l 276 0 l 142 0 l 142 987 l 276 987 l 276 409 m 666 408 l 932 408 l 802 790 l 798 790 l 666 408 z "},"ѩ":{"ha":1050,"x_min":126,"x_max":1025,"o":"m 260 304 l 435 304 l 610 734 l 725 734 l 1025 0 l 889 0 l 808 199 l 736 199 l 736 0 l 604 0 l 604 199 l 527 199 l 448 0 l 312 0 l 393 199 l 260 199 l 260 0 l 126 0 l 126 734 l 260 734 l 260 304 m 568 304 l 766 304 l 681 516 l 669 562 l 665 562 l 652 516 l 568 304 z "},"Ѫ":{"ha":1193,"x_min":101,"x_max":1093,"o":"m 746 552 l 761 552 q 1008 481 923 552 q 1093 252 1093 410 l 1093 0 l 959 0 l 959 252 q 912 404 959 361 q 761 447 865 447 l 682 447 l 665 418 l 665 0 l 532 0 l 532 429 l 520 447 l 431 447 q 281 404 328 447 q 235 252 235 361 l 235 0 l 101 0 l 101 252 q 185 481 101 410 q 431 552 269 552 l 456 552 l 191 987 l 1011 987 l 746 552 m 598 552 l 605 552 l 798 882 l 406 882 l 598 552 z "},"ѫ":{"ha":1028,"x_min":101,"x_max":927,"o":"m 762 734 l 845 734 l 652 409 q 857 331 786 402 q 927 110 927 260 l 927 0 l 793 0 l 793 110 q 752 263 793 220 q 623 305 712 305 l 591 305 l 583 292 l 583 0 l 450 0 l 450 298 l 446 305 l 405 305 q 275 263 315 305 q 235 110 235 220 l 235 0 l 101 0 l 101 110 q 173 334 101 263 q 384 409 245 404 l 191 734 l 298 734 l 298 734 l 762 734 l 762 734 m 518 412 l 637 629 l 399 629 l 518 412 z "},"Ѭ":{"ha":1577,"x_min":129,"x_max":1477,"o":"m 1131 552 q 1392 481 1308 552 q 1477 252 1477 410 l 1477 0 l 1343 0 l 1343 252 q 1296 404 1343 361 q 1145 447 1249 447 l 1067 447 l 1050 418 l 1050 0 l 916 0 l 916 429 l 905 447 l 816 447 q 666 404 712 447 q 619 252 619 361 l 619 0 l 486 0 l 486 252 q 538 448 486 377 l 263 448 l 263 0 l 129 0 l 129 987 l 263 987 l 263 552 l 841 552 l 576 987 l 1396 987 l 1131 552 m 983 552 l 989 552 l 1182 882 l 790 882 l 983 552 z "},"ѭ":{"ha":1369,"x_min":104,"x_max":1270,"o":"m 1188 734 l 995 409 q 1200 331 1129 402 q 1270 110 1270 260 l 1270 0 l 1136 0 l 1136 110 q 1096 263 1136 220 q 966 305 1055 305 l 934 305 l 926 292 l 926 0 l 793 0 l 793 298 l 789 305 l 748 305 q 618 263 658 305 q 578 110 578 220 l 578 0 l 444 0 l 444 110 q 492 305 444 235 l 237 305 l 237 0 l 104 0 l 104 734 l 237 734 l 237 410 l 726 410 l 534 734 l 1188 734 m 861 412 l 981 629 l 742 629 l 861 412 z "},"Ѯ":{"ha":720,"x_min":50,"x_max":632,"o":"m 279 559 q 433 603 384 559 q 483 723 483 646 q 434 837 483 792 q 293 882 386 882 l 87 882 l 87 987 l 293 987 q 527 915 436 987 q 617 720 617 843 q 573 595 617 650 q 451 509 529 539 q 585 422 538 481 q 632 276 632 362 q 542 63 632 140 q 309 -14 452 -14 l 275 -14 q 200 -34 224 -14 q 177 -90 177 -54 q 209 -166 177 -137 q 285 -215 240 -195 l 231 -301 q 103 -214 156 -269 q 50 -83 51 -158 q 111 48 50 5 q 281 92 172 92 l 309 92 q 447 142 397 92 q 498 273 498 193 q 442 409 498 363 q 279 454 387 454 l 182 454 l 182 559 l 279 559 m 363 1192 l 463 1293 l 578 1293 l 578 1280 l 404 1114 l 323 1114 l 151 1279 l 151 1293 l 263 1293 l 363 1192 z "},"ѯ":{"ha":669,"x_min":50,"x_max":603,"o":"m 278 419 q 411 447 368 419 q 454 526 454 475 q 412 599 454 570 q 292 628 370 628 l 86 628 l 86 734 l 292 734 q 505 677 422 734 q 588 524 588 620 q 553 434 588 474 q 456 370 518 393 q 565 306 528 347 q 603 206 603 264 q 520 45 603 104 q 308 -14 438 -14 l 275 -14 q 200 -34 223 -14 q 176 -90 176 -54 q 208 -166 176 -137 q 285 -215 240 -195 l 231 -301 q 103 -214 155 -269 q 50 -83 50 -158 q 110 48 50 5 q 280 92 171 92 l 308 92 q 426 122 382 92 q 469 203 469 153 q 420 287 469 259 q 278 314 372 314 l 181 314 l 181 419 l 278 419 m 314 959 l 414 1059 l 529 1059 l 529 1047 l 355 881 l 274 881 l 102 1046 l 102 1059 l 214 1059 l 314 959 z "},"Ѱ":{"ha":960,"x_min":59,"x_max":886,"o":"m 534 329 l 538 328 q 694 412 635 340 q 753 600 753 484 l 753 987 l 886 987 l 886 600 q 789 333 886 434 q 534 216 692 233 l 534 0 l 399 0 l 399 217 q 152 334 245 233 q 59 600 59 434 l 59 987 l 192 987 l 192 600 q 248 414 192 485 q 395 329 303 342 l 399 330 l 399 987 l 534 987 l 534 329 z "},"ѱ":{"ha":977,"x_min":62,"x_max":920,"o":"m 546 733 l 546 101 l 550 100 q 728 197 670 115 q 787 385 787 279 q 762 556 785 469 q 702 734 740 644 l 842 734 q 899 575 877 662 q 920 385 920 488 q 830 113 920 222 q 546 -12 740 3 l 546 -321 l 412 -321 l 412 -10 q 153 109 244 7 q 62 404 62 212 l 62 734 l 195 734 l 195 403 q 253 188 195 262 q 408 101 310 114 l 412 102 l 412 733 l 546 733 z "},"Ѳ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 z "},"ѳ":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 z "},"Ѵ":{"ha":878,"x_min":15,"x_max":844,"o":"m 394 259 l 416 177 l 420 177 l 443 259 l 614 805 q 692 956 648 911 q 810 1001 736 1001 l 844 1000 l 843 886 l 835 886 q 780 864 801 886 q 741 789 760 843 l 475 0 l 361 0 l 15 987 l 160 987 l 394 259 z "},"ѵ":{"ha":699,"x_min":31,"x_max":704,"o":"m 328 216 l 339 165 l 343 165 l 356 216 l 457 579 q 530 706 485 665 q 630 747 574 747 q 670 743 653 747 q 704 730 688 740 l 689 627 q 678 631 686 629 q 662 633 671 633 q 620 615 640 633 q 591 569 600 597 l 391 0 l 290 0 l 31 734 l 168 734 l 328 216 z "},"Ѷ":{"ha":878,"x_min":15,"x_max":844,"o":"m 394 259 l 416 177 l 420 177 l 443 259 l 614 805 q 692 956 648 911 q 810 1001 736 1001 l 844 1000 l 843 886 l 835 886 q 780 864 801 886 q 741 789 760 843 l 475 0 l 361 0 l 15 987 l 160 987 l 394 259 m 389 1087 l 387 1084 l 271 1084 l 68 1260 l 70 1264 l 225 1264 l 389 1087 m 571 1084 l 465 1084 l 301 1261 l 302 1264 l 446 1264 l 571 1084 z "},"ѷ":{"ha":699,"x_min":-1,"x_max":704,"o":"m 328 216 l 339 165 l 343 165 l 356 216 l 457 579 q 530 706 485 665 q 630 747 574 747 q 670 743 653 747 q 704 730 688 740 l 689 627 q 678 631 686 629 q 662 633 671 633 q 620 615 640 633 q 591 569 600 597 l 391 0 l 290 0 l 31 734 l 168 734 l 328 216 m 319 886 l 318 883 l 202 883 l -1 1059 l 1 1063 l 156 1063 l 319 886 m 502 883 l 395 883 l 232 1060 l 233 1063 l 377 1063 l 502 883 z "},"Ѹ":{"ha":1645,"x_min":77,"x_max":1625,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 1268 272 l 1292 180 l 1296 180 l 1476 734 l 1625 734 l 1316 -113 q 1237 -241 1289 -186 q 1097 -296 1185 -296 q 1055 -293 1080 -296 q 1017 -286 1030 -289 l 1030 -180 q 1054 -182 1026 -180 q 1090 -184 1082 -184 q 1160 -146 1133 -184 q 1205 -62 1187 -108 l 1237 15 l 964 734 l 1114 734 l 1268 272 z "},"ѹ":{"ha":1488,"x_min":66,"x_max":1468,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 1111 272 l 1135 180 l 1139 180 l 1319 734 l 1468 734 l 1159 -113 q 1079 -241 1131 -186 q 939 -296 1027 -296 q 898 -293 923 -296 q 859 -286 873 -289 l 873 -180 q 897 -182 869 -180 q 932 -184 925 -184 q 1002 -146 975 -184 q 1048 -62 1029 -108 l 1080 15 l 807 734 l 956 734 l 1111 272 z "},"Ѻ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 541 -8 669 12 l 541 -96 l 407 -96 l 407 -10 q 184 105 274 7 q 77 406 77 224 l 77 581 q 184 882 77 762 q 407 998 274 981 l 407 1078 l 541 1078 l 541 996 q 757 882 669 975 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 541 882 613 863 l 541 814 l 407 814 l 407 886 q 279 805 330 869 q 210 583 210 718 l 210 406 q 279 182 210 269 q 407 101 330 118 l 407 174 l 541 174 l 541 104 q 663 181 613 123 q 736 406 736 268 l 736 583 z "},"ѻ":{"ha":789,"x_min":66,"x_max":723,"o":"m 462 -80 l 328 -80 l 328 -9 q 154 90 222 9 q 66 359 66 195 l 66 374 q 154 642 66 536 q 328 742 222 724 l 328 818 l 462 818 l 462 741 q 634 642 567 722 q 723 374 723 537 l 723 359 q 635 90 723 195 q 462 -8 567 10 l 462 -80 m 199 359 q 248 166 199 242 q 328 100 279 119 l 328 170 l 462 170 l 462 101 q 540 166 509 119 q 589 359 589 242 l 589 374 q 539 566 589 489 q 462 630 509 612 l 462 567 l 328 567 l 328 631 q 248 566 279 613 q 199 374 199 489 l 199 359 z "},"Ѽ":{"ha":1214,"x_min":106,"x_max":1109,"o":"m 838 981 l 842 983 q 1035 907 960 985 q 1109 682 1109 829 l 1109 291 q 1035 64 1109 142 q 838 -14 960 -14 q 701 15 761 -14 q 608 101 642 43 q 513 15 573 43 q 378 -14 454 -14 q 181 64 256 -14 q 106 291 106 142 l 106 682 q 181 907 106 829 q 374 983 255 985 l 378 981 l 378 882 q 278 831 315 882 q 240 682 240 779 l 240 291 q 278 141 240 193 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 558 l 675 558 l 675 265 q 720 135 675 179 q 838 90 764 90 q 938 141 899 90 q 976 291 976 193 l 976 682 q 938 831 976 779 q 838 882 899 882 l 838 981 m 907 1191 l 907 1103 l 876 1103 q 686 1145 787 1103 q 556 1187 585 1187 q 503 1168 522 1187 q 483 1115 483 1150 l 483 1103 l 392 1103 l 392 1127 q 434 1238 392 1200 q 553 1275 476 1275 q 701 1233 602 1275 q 878 1191 800 1191 l 907 1191 m 578 894 l 524 939 l 564 1017 l 563 1112 l 690 1112 l 690 1028 l 578 894 z "},"ѽ":{"ha":1058,"x_min":83,"x_max":978,"o":"m 734 734 l 738 736 q 911 668 844 739 q 978 462 978 597 l 978 264 q 911 57 978 128 q 734 -14 844 -14 q 614 10 667 -14 q 530 83 562 35 q 446 10 498 35 q 328 -14 394 -14 q 150 57 217 -14 q 83 264 83 128 l 83 462 q 150 668 83 597 q 323 736 216 739 l 328 734 l 328 635 q 247 591 277 635 q 217 462 217 547 l 217 264 q 247 134 217 178 q 328 90 277 90 q 426 128 388 90 q 464 239 464 166 l 464 397 l 597 397 l 597 239 q 635 128 597 166 q 734 90 673 90 q 814 134 784 90 q 844 264 844 178 l 844 462 q 814 591 844 547 q 734 635 784 635 l 734 734 m 852 947 l 852 860 l 821 860 q 630 902 732 860 q 500 943 529 943 q 447 925 467 943 q 427 871 427 907 l 427 859 l 336 859 l 336 884 q 378 994 336 957 q 498 1031 420 1031 q 646 989 547 1031 q 822 947 745 947 l 852 947 m 524 655 l 471 704 l 508 778 l 507 863 l 633 859 l 633 780 l 524 655 z "},"Ѿ":{"ha":1219,"x_min":106,"x_max":1113,"o":"m 318 1145 l 318 1219 l 875 1219 l 876 1145 l 651 1145 l 651 1059 l 530 1059 l 530 1145 l 318 1145 m 1113 987 l 1113 265 q 1035 57 1113 129 q 829 -14 956 -14 q 698 15 755 -14 q 608 103 640 45 q 514 15 574 45 q 378 -14 454 -14 q 181 57 256 -14 q 106 265 106 129 l 106 987 l 240 987 l 240 265 q 278 135 240 179 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 987 l 679 987 l 679 265 q 720 135 679 180 q 829 90 762 90 q 938 135 897 90 q 979 265 979 180 l 979 987 l 1113 987 z "},"ѿ":{"ha":1059,"x_min":84,"x_max":979,"o":"m 258 914 l 258 988 l 814 988 l 817 914 l 591 914 l 591 827 l 470 827 l 470 914 l 258 914 m 979 734 l 979 239 q 908 51 979 115 q 722 -14 837 -14 q 610 10 659 -14 q 530 82 560 34 q 446 10 498 34 q 328 -14 394 -14 q 151 50 218 -14 q 84 239 84 115 l 84 734 l 218 734 l 218 239 q 248 128 218 165 q 328 90 277 90 q 427 128 389 90 q 465 239 465 166 l 465 734 l 599 734 l 599 239 q 632 128 599 165 q 722 90 666 90 q 811 128 777 90 q 846 239 846 166 l 846 734 l 979 734 z "},"Ҁ":{"ha":906,"x_min":80,"x_max":824,"o":"m 536 -260 l 404 -260 l 404 -10 q 169 124 258 9 q 80 406 80 239 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 l 536 90 l 536 -260 z "},"ҁ":{"ha":745,"x_min":66,"x_max":673,"o":"m 460 -260 l 327 -260 l 327 -9 q 135 114 204 13 q 66 353 66 215 l 66 381 q 153 641 66 535 q 389 747 239 747 q 593 671 512 747 q 671 485 673 595 l 669 481 l 549 481 q 502 595 549 547 q 389 642 456 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 247 168 200 245 q 389 90 294 90 l 460 90 l 460 -260 z "},"҂":{"ha":869,"x_min":76,"x_max":795,"o":"m 410 303 l 606 186 l 557 102 l 360 217 l 237 0 l 119 0 l 272 270 l 76 386 l 124 470 l 323 354 l 460 601 l 263 717 l 313 802 l 510 686 l 635 910 l 751 910 l 596 634 l 795 518 l 744 434 l 549 549 l 410 303 z "},"҃":{"ha":790,"x_min":144,"x_max":631,"o":"m 262 891 l 262 806 l 144 806 l 144 966 l 513 966 l 513 1039 l 631 1038 l 631 891 l 262 891 z "},"҄":{"ha":820,"x_min":170,"x_max":686,"o":"m 200 972 q 376 1014 277 972 q 524 1056 475 1056 q 643 1019 601 1056 q 686 908 686 981 l 686 884 l 595 884 l 595 896 q 575 949 595 931 q 522 968 555 968 q 391 926 492 968 q 201 884 290 884 l 170 884 l 170 972 l 200 972 z "},"҅":{"ha":534,"x_min":174,"x_max":340,"o":"m 174 1017 l 174 1101 l 300 1101 l 300 1006 l 340 927 l 285 882 l 174 1017 z "},"҆":{"ha":574,"x_min":203,"x_max":370,"o":"m 258 882 l 203 927 l 243 1006 l 242 1101 l 370 1101 l 370 1017 l 258 882 z "},"҈":{"ha":1408,"x_min":40,"x_max":1359,"o":"m 553 859 l 551 863 q 586 952 548 917 q 690 987 624 987 q 793 952 755 987 q 829 863 831 917 l 827 859 l 757 859 q 740 908 757 888 q 690 928 724 928 q 640 908 656 928 q 624 859 624 888 l 553 859 m 954 671 l 952 675 q 987 764 949 728 q 1090 799 1025 799 q 1194 764 1156 799 q 1230 675 1233 729 l 1229 671 l 1158 671 q 1141 721 1158 701 q 1090 740 1125 740 q 1041 720 1057 740 q 1025 671 1025 701 l 954 671 m 1080 330 l 1079 334 q 1114 422 1076 387 q 1217 458 1152 458 q 1321 423 1282 458 q 1356 334 1359 388 l 1355 330 l 1284 330 q 1268 379 1284 360 q 1217 399 1252 399 q 1167 379 1183 399 q 1152 330 1152 359 l 1080 330 m 947 -22 l 945 -18 q 980 71 942 35 q 1084 106 1019 106 q 1187 71 1149 106 q 1223 -18 1225 36 l 1221 -22 l 1151 -22 q 1135 27 1151 8 q 1084 47 1118 47 q 1034 27 1050 47 q 1018 -22 1018 7 l 947 -22 m 556 -214 l 555 -210 q 589 -121 551 -156 q 693 -87 627 -87 q 797 -121 758 -87 q 832 -210 835 -156 l 831 -214 l 760 -214 q 744 -165 760 -184 q 693 -146 727 -146 q 643 -165 659 -146 q 627 -214 627 -184 l 556 -214 m 164 671 l 163 675 q 198 764 159 729 q 302 799 236 799 q 405 764 367 799 q 441 675 444 729 l 439 671 l 369 671 q 352 720 369 701 q 302 740 336 740 q 252 720 268 740 q 236 671 236 701 l 164 671 m 45 330 l 43 334 q 78 422 40 387 q 182 458 117 458 q 285 423 247 458 q 321 334 323 388 l 319 330 l 249 330 q 233 379 249 360 q 182 399 216 399 q 132 379 148 399 q 116 330 116 359 l 45 330 m 157 -22 l 156 -18 q 191 71 153 35 q 294 106 229 106 q 398 71 359 106 q 434 -18 437 36 l 433 -22 l 361 -22 q 345 27 361 8 q 294 47 329 47 q 245 27 260 47 q 229 -22 229 7 l 157 -22 z "},"҉":{"ha":1362,"x_min":52,"x_max":1311,"o":"m 749 -41 l 756 -50 l 673 -280 l 608 -280 l 656 -41 l 749 -41 m 616 764 l 608 773 l 691 1002 l 756 1002 l 709 764 l 616 764 m 1076 428 l 1085 436 l 1311 352 l 1311 286 l 1076 334 l 1076 428 m 287 294 l 278 286 l 52 370 l 52 436 l 287 388 l 287 294 m 913 680 l 915 690 l 1133 794 l 1179 748 q 979 612 1154 732 l 913 680 m 450 14 l 448 3 l 231 -101 l 184 -54 l 384 81 l 450 14 m 355 583 l 343 585 l 243 806 l 287 852 l 421 650 l 355 583 m 1007 109 l 1019 108 l 1120 -113 l 1075 -161 l 941 42 l 1007 109 z "},"Ҋ":{"ha":991,"x_min":122,"x_max":980,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 696 1268 l 698 1264 q 646 1136 701 1185 q 498 1086 591 1086 q 349 1136 404 1086 q 298 1264 294 1185 l 299 1268 l 401 1268 q 425 1196 401 1223 q 498 1168 448 1168 q 570 1196 547 1168 q 594 1268 594 1224 l 696 1268 m 980 7 l 878 -201 l 799 -201 l 846 14 l 846 124 l 980 124 l 980 7 z "},"ҋ":{"ha":789,"x_min":97,"x_max":803,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 593 1036 l 595 1031 q 543 903 597 953 q 395 854 488 854 q 246 903 301 854 q 195 1031 191 953 l 196 1036 l 298 1036 q 321 963 298 991 q 395 935 345 935 q 467 963 444 935 q 491 1036 491 991 l 593 1036 m 803 7 l 701 -201 l 622 -201 l 669 14 l 669 124 l 803 124 l 803 7 z "},"Ҍ":{"ha":875,"x_min":-35,"x_max":821,"o":"m 400 747 l 244 747 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 747 l -35 747 l -35 852 l 111 852 l 111 987 l 244 987 l 244 852 l 400 852 l 400 747 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"ҍ":{"ha":755,"x_min":-25,"x_max":694,"o":"m 455 882 l 237 882 l 237 507 l 420 507 q 622 438 550 507 q 694 256 694 368 q 622 72 694 144 q 420 0 549 0 l 104 0 l 104 882 l -25 882 l -25 987 l 104 987 l 104 1119 l 237 1119 l 237 987 l 455 987 l 455 882 m 237 403 l 237 104 l 420 104 q 526 147 492 104 q 560 252 560 189 q 526 358 560 313 q 420 403 491 403 l 237 403 z "},"Ҏ":{"ha":888,"x_min":122,"x_max":850,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 759 490 833 568 l 850 391 l 772 320 l 672 429 q 500 396 600 396 l 256 396 m 256 500 l 500 500 q 592 515 553 500 l 519 595 l 597 666 l 672 584 q 699 690 699 629 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"ҏ":{"ha":789,"x_min":97,"x_max":724,"o":"m 722 339 q 652 92 722 187 l 724 12 l 646 -59 l 574 20 q 437 -14 515 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 212 734 l 224 644 q 313 721 260 694 q 435 747 366 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 490 113 451 90 l 417 195 l 496 266 l 560 194 q 589 339 589 255 l 589 353 z "},"Ґ":{"ha":762,"x_min":111,"x_max":719,"o":"m 719 882 l 244 882 l 244 0 l 111 0 l 111 987 l 585 987 l 585 1214 l 719 1214 l 719 882 z "},"ґ":{"ha":621,"x_min":97,"x_max":567,"o":"m 567 628 l 231 628 l 231 0 l 97 0 l 97 734 l 433 734 l 433 947 l 567 947 l 567 628 z "},"Ғ":{"ha":773,"x_min":-3,"x_max":728,"o":"m 432 462 l 256 462 l 256 0 l 122 0 l 122 462 l -3 462 l -3 567 l 122 567 l 122 987 l 728 987 l 728 882 l 256 882 l 256 567 l 432 567 l 432 462 z "},"ғ":{"ha":576,"x_min":-9,"x_max":564,"o":"m 426 323 l 231 323 l 231 0 l 97 0 l 97 323 l -9 323 l -9 428 l 97 428 l 97 734 l 564 734 l 564 628 l 231 628 l 231 428 l 426 428 l 426 323 z "},"Ҕ":{"ha":850,"x_min":122,"x_max":785,"o":"m 728 882 l 256 882 l 256 565 l 378 565 q 677 461 570 565 q 785 173 785 357 q 702 -100 785 -5 q 466 -193 620 -195 l 462 -191 l 460 -95 q 607 -26 563 -95 q 651 173 651 43 q 582 381 650 310 q 378 453 514 453 l 256 453 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"ҕ":{"ha":700,"x_min":97,"x_max":652,"o":"m 564 628 l 231 628 l 231 439 l 306 439 q 556 352 459 439 q 652 115 652 264 q 585 -74 651 22 q 391 -193 519 -169 l 355 -92 q 481 -15 443 -68 q 519 115 519 38 q 459 271 517 215 q 306 327 401 327 l 231 327 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 z "},"Җ":{"ha":1267,"x_min":19,"x_max":1297,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 1297 -243 l 1164 -243 l 1164 106 l 1297 106 l 1297 -243 z "},"җ":{"ha":1063,"x_min":14,"x_max":1069,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 1069 -243 l 935 -243 l 935 106 l 1069 106 l 1069 -243 z "},"Ҙ":{"ha":824,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 m 477 -292 l 344 -292 l 344 58 l 477 58 l 477 -292 z "},"ҙ":{"ha":705,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 m 419 -291 l 286 -291 l 286 59 l 419 59 l 419 -291 z "},"Қ":{"ha":893,"x_min":122,"x_max":937,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 937 -243 l 804 -243 l 804 106 l 937 106 l 937 -243 z "},"қ":{"ha":736,"x_min":104,"x_max":772,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 m 772 -243 l 638 -243 l 638 106 l 772 106 l 772 -243 z "},"Ҝ":{"ha":878,"x_min":111,"x_max":867,"o":"m 839 987 l 569 520 l 867 0 l 701 0 l 470 446 l 417 446 l 417 281 l 311 281 l 311 446 l 244 446 l 244 0 l 111 0 l 111 987 l 244 987 l 244 551 l 311 551 l 311 724 l 417 724 l 417 551 l 469 551 l 684 987 l 839 987 z "},"ҝ":{"ha":765,"x_min":104,"x_max":762,"o":"m 738 734 l 508 387 l 762 0 l 593 0 l 410 311 l 395 311 l 395 182 l 288 182 l 288 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 288 424 l 288 567 l 395 567 l 395 424 l 405 424 l 578 734 l 738 734 z "},"Ҟ":{"ha":907,"x_min":-7,"x_max":903,"o":"m 385 446 l 269 446 l 269 0 l 136 0 l 136 783 l -7 783 l -7 888 l 136 888 l 136 987 l 269 987 l 269 888 l 427 888 l 427 783 l 269 783 l 269 551 l 372 551 l 726 987 l 873 987 l 876 984 l 492 510 l 903 3 l 901 0 l 742 0 l 385 446 z "},"ҟ":{"ha":726,"x_min":-33,"x_max":727,"o":"m 332 338 l 245 338 l 245 0 l 111 0 l 111 823 l -33 823 l -33 928 l 111 928 l 111 1058 l 245 1058 l 245 928 l 401 928 l 401 823 l 245 823 l 245 445 l 330 445 l 531 734 l 691 734 l 440 400 l 727 0 l 570 0 l 332 338 z "},"Ҡ":{"ha":1139,"x_min":45,"x_max":1136,"o":"m 617 446 l 502 446 l 502 0 l 368 0 l 368 882 l 45 882 l 45 987 l 502 987 l 502 551 l 605 551 l 958 987 l 1106 987 l 1108 984 l 725 510 l 1136 3 l 1134 0 l 975 0 l 617 446 z "},"ҡ":{"ha":964,"x_min":44,"x_max":959,"o":"m 545 311 l 458 311 l 458 0 l 324 0 l 324 628 l 44 628 l 44 734 l 458 734 l 458 424 l 535 424 l 777 734 l 934 734 l 936 730 l 648 382 l 959 3 l 957 0 l 793 0 l 545 311 z "},"Ң":{"ha":991,"x_min":122,"x_max":967,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 967 -243 l 833 -243 l 833 106 l 967 106 l 967 -243 z "},"ң":{"ha":789,"x_min":97,"x_max":789,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 m 789 -243 l 656 -243 l 656 106 l 789 106 l 789 -243 z "},"Ҥ":{"ha":1348,"x_min":122,"x_max":1298,"o":"m 256 541 l 735 541 l 735 987 l 1298 987 l 1298 882 l 869 882 l 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 z "},"ҥ":{"ha":987,"x_min":97,"x_max":937,"o":"m 231 416 l 557 416 l 557 734 l 937 734 l 937 628 l 691 628 l 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 z "},"Ҧ":{"ha":1423,"x_min":122,"x_max":1354,"o":"m 870 565 l 947 565 q 1247 461 1140 565 q 1354 173 1354 357 q 1272 -100 1354 -5 q 1036 -193 1190 -195 l 1031 -191 l 1030 -95 q 1177 -26 1133 -95 q 1221 173 1221 43 q 1152 381 1219 310 q 947 453 1084 453 l 870 453 l 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 565 z "},"ҧ":{"ha":1211,"x_min":97,"x_max":1166,"o":"m 692 439 l 806 439 q 1065 352 965 439 q 1166 115 1166 264 q 1099 -74 1164 22 q 904 -193 1033 -169 l 869 -92 q 994 -15 956 -68 q 1032 115 1032 38 q 969 271 1031 216 q 806 327 907 327 l 692 327 l 692 0 l 558 0 l 558 628 l 231 628 l 231 0 l 97 0 l 97 734 l 692 734 l 692 439 z "},"Ҩ":{"ha":1029,"x_min":77,"x_max":973,"o":"m 973 -20 q 832 -8 899 -20 q 705 30 764 5 q 604 -3 657 8 q 494 -14 551 -14 q 193 121 309 -14 q 77 459 77 256 l 77 572 q 162 878 77 755 q 381 996 248 1000 l 385 995 l 385 892 q 257 802 304 892 q 210 573 210 712 l 210 459 q 287 197 210 300 q 494 95 364 95 q 538 97 517 95 q 579 104 559 99 q 452 265 496 170 q 408 473 408 360 l 408 627 q 485 893 408 787 q 687 1000 563 1000 q 888 896 810 1000 q 965 627 965 791 l 965 459 q 927 262 965 353 q 819 106 888 171 q 892 93 854 97 q 973 88 930 88 l 973 -20 m 541 472 q 581 289 541 368 q 696 163 621 209 l 700 163 q 797 286 762 208 q 831 459 831 363 l 831 629 q 792 819 831 746 q 687 891 753 891 q 581 817 621 891 q 541 629 541 742 l 541 472 z "},"ҩ":{"ha":840,"x_min":73,"x_max":804,"o":"m 804 -8 q 688 1 743 -8 q 586 31 633 11 q 501 -3 546 8 q 408 -14 456 -14 q 166 100 260 -14 q 73 387 73 214 l 73 425 q 139 655 73 564 q 309 742 205 745 l 313 741 l 313 638 q 235 579 264 638 q 207 427 207 519 l 207 387 q 261 177 207 259 q 408 95 315 95 q 437 97 422 95 q 466 102 452 98 q 370 229 403 156 q 336 389 336 302 l 336 458 q 395 667 336 587 q 553 748 454 748 q 711 662 651 748 q 770 444 770 576 l 770 373 q 748 232 770 298 q 685 115 726 165 q 741 104 711 107 q 804 100 770 100 l 804 -8 m 637 373 l 637 446 q 614 584 637 530 q 555 635 592 638 l 551 635 q 491 590 512 638 q 470 460 470 541 l 470 387 q 495 263 470 318 q 568 174 521 208 l 572 174 q 620 259 603 208 q 637 373 637 311 z "},"Ҫ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 528 -292 l 394 -292 l 394 58 l 528 58 l 528 -292 z "},"ҫ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 432 -292 l 298 -292 l 298 58 l 432 58 l 432 -292 z "},"Ҭ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 572 -243 l 438 -243 l 438 106 l 572 106 l 572 -243 z "},"ҭ":{"ha":711,"x_min":48,"x_max":663,"o":"m 663 630 l 420 630 l 420 0 l 287 0 l 287 630 l 48 630 l 48 734 l 663 734 l 663 630 m 519 -243 l 385 -243 l 385 106 l 519 106 l 519 -243 z "},"Ү":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"ү":{"ha":699,"x_min":31,"x_max":675,"o":"m 539 734 l 675 734 l 421 42 l 421 -283 l 288 -283 l 288 45 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 z "},"Ұ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 550 463 l 631 463 l 631 357 l 493 357 l 488 347 l 488 0 l 355 0 l 355 356 l 354 357 l 197 357 l 197 463 l 297 463 l 14 987 l 166 987 l 424 486 z "},"ұ":{"ha":699,"x_min":31,"x_max":675,"o":"m 576 -9 l 421 -9 l 421 -283 l 288 -283 l 288 -9 l 141 -9 l 141 96 l 269 96 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 441 96 l 576 96 l 576 -9 z "},"Ҳ":{"ha":878,"x_min":45,"x_max":857,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 m 857 -243 l 724 -243 l 724 106 l 857 106 l 857 -243 z "},"ҳ":{"ha":699,"x_min":31,"x_max":702,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 m 702 -243 l 568 -243 l 568 106 l 702 106 l 702 -243 z "},"Ҵ":{"ha":1253,"x_min":37,"x_max":1147,"o":"m 269 881 l 37 881 l 37 986 l 684 986 l 684 881 l 403 881 l 403 105 l 883 105 l 883 987 l 1017 987 l 1017 109 l 1147 109 l 1147 -241 l 1014 -241 l 1014 0 l 269 0 l 269 881 z "},"ҵ":{"ha":968,"x_min":22,"x_max":888,"o":"m 203 629 l 22 629 l 22 734 l 502 734 l 502 629 l 337 629 l 337 104 l 665 104 l 665 734 l 798 734 l 798 104 l 888 104 l 888 -240 l 754 -240 l 754 0 l 203 0 l 203 629 z "},"Ҷ":{"ha":956,"x_min":100,"x_max":931,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 931 -243 l 798 -243 l 798 106 l 931 106 l 931 -243 z "},"ҷ":{"ha":767,"x_min":86,"x_max":768,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 768 -243 l 635 -243 l 635 106 l 768 106 l 768 -243 z "},"Ҹ":{"ha":948,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 541 376 558 378 l 541 214 l 434 214 l 434 372 l 431 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 l 434 477 l 434 690 l 541 690 l 541 484 q 567 487 554 485 q 699 515 632 496 l 699 987 l 833 987 z "},"ҹ":{"ha":768,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 439 245 448 246 l 439 155 l 332 155 l 332 243 q 161 312 226 252 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 332 351 285 359 l 332 539 l 439 539 l 439 351 l 456 353 q 536 368 496 358 l 536 734 l 670 734 l 670 0 z "},"Һ":{"ha":948,"x_min":94,"x_max":827,"o":"m 94 0 l 94 987 l 227 987 l 227 578 q 352 606 292 597 q 495 616 412 616 q 742 544 658 616 q 827 312 827 472 l 827 0 l 692 0 l 692 312 q 645 467 692 423 q 495 510 598 510 q 359 500 424 510 q 227 472 294 490 l 227 0 l 94 0 z "},"һ":{"ha":768,"x_min":100,"x_max":684,"o":"m 100 735 l 234 735 l 234 473 q 313 488 273 483 q 400 493 354 493 q 609 423 533 493 q 684 216 684 353 l 684 1 l 551 1 l 551 216 q 513 346 551 305 q 400 387 475 387 q 315 382 355 387 q 234 367 274 377 l 234 1 l 100 1 l 100 735 z "},"Ҽ":{"ha":1179,"x_min":52,"x_max":1072,"o":"m 743 -16 q 394 119 519 -16 q 270 475 270 254 l 270 479 q 107 559 163 493 q 55 730 52 626 l 56 734 l 159 734 q 187 628 159 671 q 273 574 215 586 q 405 882 291 763 q 677 1000 519 1000 q 970 880 869 1000 q 1072 549 1072 760 l 1072 475 l 408 475 l 407 472 q 487 196 404 303 q 743 89 571 89 q 867 104 815 89 q 970 146 920 120 l 1003 52 q 904 6 969 27 q 743 -16 839 -16 m 677 895 q 490 804 562 895 q 409 570 419 712 l 411 567 l 938 567 l 938 588 q 877 811 938 728 q 677 895 815 895 z "},"ҽ":{"ha":824,"x_min":-22,"x_max":755,"o":"m 475 -14 q 232 88 322 -14 q 141 353 141 190 l 141 358 q 19 437 60 378 q -22 588 -22 496 l 84 588 q 99 508 84 541 q 147 458 115 475 q 257 667 170 586 q 455 747 345 747 q 680 658 604 747 q 755 419 755 568 l 755 336 l 279 336 l 277 332 q 331 158 279 227 q 475 90 382 90 q 594 110 543 90 q 683 163 646 129 l 735 76 q 631 12 696 37 q 475 -14 567 -14 m 455 642 q 340 586 388 642 q 282 444 293 529 l 283 440 l 622 440 l 622 458 q 580 589 622 536 q 455 642 538 642 z "},"Ҿ":{"ha":1078,"x_min":52,"x_max":1072,"o":"m 743 -16 q 394 119 519 -16 q 270 475 270 254 l 270 479 q 107 559 163 493 q 55 730 52 626 l 56 734 l 159 734 q 187 628 159 671 q 273 574 215 586 q 405 882 291 763 q 677 1000 519 1000 q 970 880 869 1000 q 1072 549 1072 760 l 1072 475 l 408 475 l 407 472 q 487 196 404 303 q 743 89 571 89 q 867 104 815 89 q 970 146 920 120 l 1003 52 q 904 6 969 27 q 743 -16 839 -16 m 677 895 q 490 804 562 895 q 409 570 419 712 l 411 567 l 938 567 l 938 588 q 877 811 938 728 q 677 895 815 895 m 730 -288 l 596 -288 l 596 62 l 730 62 l 730 -288 z "},"ҿ":{"ha":824,"x_min":-22,"x_max":755,"o":"m 475 -14 q 232 88 322 -14 q 141 353 141 190 l 141 358 q 19 437 60 378 q -22 588 -22 496 l 84 588 q 99 508 84 541 q 147 458 115 475 q 257 667 170 586 q 455 747 345 747 q 680 658 604 747 q 755 419 755 568 l 755 336 l 279 336 l 277 332 q 331 158 279 227 q 475 90 382 90 q 594 110 543 90 q 683 163 646 129 l 735 76 q 631 12 696 37 q 475 -14 567 -14 m 455 642 q 340 586 388 642 q 282 444 293 529 l 283 440 l 622 440 l 622 458 q 580 589 622 536 q 455 642 538 642 m 549 -287 l 415 -287 l 415 63 l 549 63 l 549 -287 z "},"Ӏ":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ӂ":{"ha":1267,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 836 1268 l 837 1264 q 785 1136 840 1185 q 637 1086 730 1086 q 488 1136 543 1086 q 437 1264 433 1185 l 438 1268 l 541 1268 q 564 1196 541 1223 q 637 1168 587 1168 q 709 1196 686 1168 q 733 1268 733 1224 l 836 1268 z "},"ӂ":{"ha":1063,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 731 1036 l 732 1031 q 680 903 735 953 q 532 854 625 854 q 384 903 439 854 q 332 1031 329 953 l 334 1036 l 436 1036 q 459 963 436 991 q 532 935 482 935 q 605 963 581 935 q 629 1036 629 991 l 731 1036 z "},"Ӄ":{"ha":878,"x_min":111,"x_max":836,"o":"m 481 560 q 739 443 648 545 q 831 171 831 340 q 748 -102 831 -7 q 512 -195 666 -197 l 508 -193 l 507 -97 q 653 -28 609 -97 q 697 171 697 41 q 628 379 696 308 q 424 450 560 450 l 244 450 l 244 0 l 111 0 l 111 987 l 244 987 l 244 563 l 331 563 l 684 987 l 834 987 l 836 984 l 481 560 z "},"ӄ":{"ha":760,"x_min":104,"x_max":715,"o":"m 440 416 q 638 320 565 395 q 711 119 711 245 q 644 -61 709 29 q 449 -175 578 -152 l 414 -73 q 539 -2 501 -52 q 578 119 578 47 q 516 264 576 217 q 351 311 456 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 713 734 l 715 730 l 440 416 z "},"Ӆ":{"ha":984,"x_min":33,"x_max":973,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 m 973 7 l 871 -201 l 792 -201 l 840 14 l 840 124 l 973 124 l 973 7 z "},"ӆ":{"ha":768,"x_min":18,"x_max":803,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 m 803 7 l 701 -201 l 622 -201 l 669 14 l 669 124 l 803 124 l 803 7 z "},"Ӈ":{"ha":991,"x_min":122,"x_max":869,"o":"m 256 987 l 256 541 l 734 541 l 734 987 l 869 987 l 869 -60 q 810 -235 869 -174 q 647 -296 751 -296 q 608 -293 626 -296 q 571 -284 590 -290 l 581 -182 q 612 -189 589 -186 q 647 -191 635 -191 q 711 -156 688 -191 q 734 -60 734 -121 l 734 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 z "},"ӈ":{"ha":789,"x_min":97,"x_max":691,"o":"m 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 -60 q 632 -235 691 -174 q 470 -296 574 -296 q 431 -293 449 -296 q 394 -284 413 -290 l 404 -182 q 436 -189 413 -186 q 470 -191 458 -191 q 534 -156 511 -191 q 557 -60 557 -121 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 z "},"Ӊ":{"ha":991,"x_min":122,"x_max":980,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 980 7 l 878 -201 l 799 -201 l 846 14 l 846 124 l 980 124 l 980 7 z "},"ӊ":{"ha":789,"x_min":97,"x_max":802,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 m 802 7 l 701 -201 l 621 -201 l 669 14 l 669 124 l 802 124 l 802 7 z "},"Ӌ":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 734 -243 l 600 -243 l 600 106 l 734 106 l 734 -243 z "},"ӌ":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 570 -243 l 437 -243 l 437 106 l 570 106 l 570 -243 z "},"Ӎ":{"ha":1220,"x_min":122,"x_max":1208,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 m 1208 7 l 1107 -201 l 1027 -201 l 1075 14 l 1075 124 l 1208 124 l 1208 7 z "},"ӎ":{"ha":1036,"x_min":104,"x_max":1037,"o":"m 515 175 l 519 175 l 759 734 l 926 734 l 926 0 l 792 0 l 792 509 l 788 511 l 563 0 l 471 0 l 241 522 l 237 521 l 237 0 l 104 0 l 104 734 l 276 734 l 515 175 m 1037 7 l 935 -201 l 856 -201 l 903 14 l 903 124 l 1037 124 l 1037 7 z "},"ӏ":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ӑ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 z "},"ӑ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 z "},"Ӓ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 684 1088 l 535 1088 l 535 1224 l 684 1224 l 684 1088 m 365 1088 l 216 1088 l 216 1224 l 365 1224 l 365 1088 z "},"ӓ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 610 870 l 462 870 l 462 1006 l 610 1006 l 610 870 m 292 870 l 143 870 l 143 1006 l 292 1006 l 292 870 z "},"Ӕ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 z "},"ӕ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 z "},"Ӗ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 625 1268 l 627 1264 q 574 1136 629 1185 q 427 1086 519 1086 q 278 1136 333 1086 q 227 1264 223 1185 l 228 1268 l 330 1268 q 353 1196 330 1223 q 427 1168 376 1168 q 499 1196 475 1168 q 523 1268 523 1224 l 625 1268 z "},"ӗ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 578 1050 l 579 1046 q 527 918 582 968 q 379 869 472 869 q 231 918 286 869 q 179 1046 176 968 l 180 1050 l 283 1050 q 306 978 283 1006 q 379 950 329 950 q 452 978 428 950 q 475 1050 475 1006 l 578 1050 z "},"Ә":{"ha":966,"x_min":60,"x_max":890,"o":"m 416 1002 q 765 866 640 1002 q 890 510 890 731 l 890 477 q 772 124 890 262 q 482 -14 653 -14 q 168 105 276 -14 q 60 437 60 225 l 60 510 l 752 510 l 753 513 q 672 789 756 682 q 416 897 587 897 q 291 881 343 897 q 189 840 239 865 l 156 934 q 255 980 190 958 q 416 1002 319 1002 m 482 90 q 669 181 597 90 q 751 415 740 273 l 749 418 l 194 418 l 194 397 q 261 174 194 257 q 482 90 329 90 z "},"ә":{"ha":739,"x_min":79,"x_max":693,"o":"m 359 748 q 603 646 512 748 q 693 381 693 544 l 693 351 q 600 91 693 195 q 379 -13 507 -13 q 155 76 231 -13 q 79 315 79 166 l 79 398 l 555 398 l 557 402 q 503 575 555 507 q 359 644 452 644 q 240 624 291 644 q 151 571 189 605 l 99 658 q 203 722 138 696 q 359 748 267 748 m 379 92 q 494 148 446 92 q 552 290 541 205 l 551 294 l 212 294 l 212 276 q 254 145 212 197 q 379 92 296 92 z "},"Ӛ":{"ha":966,"x_min":60,"x_max":890,"o":"m 416 1002 q 765 866 640 1002 q 890 510 890 731 l 890 477 q 772 124 890 262 q 482 -14 653 -14 q 168 105 276 -14 q 60 437 60 225 l 60 510 l 752 510 l 753 513 q 672 789 756 682 q 416 897 587 897 q 291 881 343 897 q 189 840 239 865 l 156 934 q 255 980 190 958 q 416 1002 319 1002 m 482 90 q 669 181 597 90 q 751 415 740 273 l 749 418 l 194 418 l 194 397 q 261 174 194 257 q 482 90 329 90 m 661 1057 l 512 1057 l 512 1193 l 661 1193 l 661 1057 m 342 1057 l 193 1057 l 193 1193 l 342 1193 l 342 1057 z "},"ӛ":{"ha":739,"x_min":79,"x_max":693,"o":"m 359 748 q 603 646 512 748 q 693 381 693 544 l 693 351 q 600 91 693 195 q 379 -13 507 -13 q 155 76 231 -13 q 79 315 79 166 l 79 398 l 555 398 l 557 402 q 503 575 555 507 q 359 644 452 644 q 240 624 291 644 q 151 571 189 605 l 99 658 q 203 722 138 696 q 359 748 267 748 m 379 92 q 494 148 446 92 q 552 290 541 205 l 551 294 l 212 294 l 212 276 q 254 145 212 197 q 379 92 296 92 m 619 870 l 471 870 l 471 1006 l 619 1006 l 619 870 m 300 870 l 152 870 l 152 1006 l 300 1006 l 300 870 z "},"Ӝ":{"ha":1144,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 873 1088 l 725 1088 l 725 1224 l 873 1224 l 873 1088 m 555 1088 l 406 1088 l 406 1224 l 555 1224 l 555 1088 z "},"ӝ":{"ha":999,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 733 856 l 585 856 l 585 991 l 733 991 l 733 856 m 414 856 l 266 856 l 266 991 l 414 991 l 414 856 z "},"Ӟ":{"ha":939,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 m 697 1103 l 549 1103 l 549 1238 l 697 1238 l 697 1103 m 378 1103 l 230 1103 l 230 1238 l 378 1238 l 378 1103 z "},"ӟ":{"ha":748,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 m 608 869 l 459 869 l 459 1005 l 608 1005 l 608 869 m 289 869 l 140 869 l 140 1005 l 289 1005 l 289 869 z "},"Ӡ":{"ha":810,"x_min":71,"x_max":725,"o":"m 530 879 l 528 882 l 100 882 l 100 987 l 690 987 l 690 906 l 413 573 q 643 487 562 564 q 725 275 725 410 q 632 63 725 140 q 389 -14 539 -14 q 165 58 259 -14 q 75 270 71 131 l 76 274 l 204 274 q 256 143 204 197 q 389 90 307 90 q 537 141 484 90 q 591 273 591 191 q 537 425 591 376 q 374 473 482 473 l 275 473 l 275 578 l 530 879 z "},"ӡ":{"ha":810,"x_min":71,"x_max":725,"o":"m 515 625 l 513 628 l 100 628 l 100 734 l 690 734 l 690 652 l 422 318 q 646 231 567 307 q 725 21 725 154 q 632 -191 725 -113 q 389 -268 538 -268 q 165 -195 260 -268 q 75 16 71 -122 l 76 20 l 204 20 q 256 -110 204 -56 q 389 -163 307 -163 q 537 -112 484 -163 q 591 18 591 -62 q 536 171 591 123 q 373 219 481 219 l 273 219 l 273 323 l 515 625 z "},"Ӣ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 745 1112 l 256 1112 l 256 1211 l 745 1211 l 745 1112 z "},"ӣ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 642 881 l 153 881 l 153 980 l 642 980 l 642 881 z "},"Ӥ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 731 1088 l 583 1088 l 583 1224 l 731 1224 l 731 1088 m 412 1088 l 264 1088 l 264 1224 l 412 1224 l 412 1088 z "},"ӥ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 628 856 l 479 856 l 479 991 l 628 991 l 628 856 m 309 856 l 161 856 l 161 991 l 309 991 l 309 856 z "},"Ӧ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 707 1103 l 559 1103 l 559 1238 l 707 1238 l 707 1103 m 389 1103 l 240 1103 l 240 1238 l 389 1238 l 389 1103 z "},"ӧ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 629 870 l 480 870 l 480 1006 l 629 1006 l 629 870 m 310 870 l 161 870 l 161 1006 l 310 1006 l 310 870 z "},"Ө":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 z "},"ө":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 z "},"Ӫ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 m 722 1085 l 574 1085 l 574 1221 l 722 1221 l 722 1085 m 404 1085 l 255 1085 l 255 1221 l 404 1221 l 404 1085 z "},"ӫ":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 m 613 889 l 465 889 l 465 1025 l 613 1025 l 613 889 m 294 889 l 146 889 l 146 1025 l 294 1025 l 294 889 z "},"Ӭ":{"ha":939,"x_min":123,"x_max":867,"o":"m 127 671 l 126 675 q 223 908 123 813 q 488 1002 324 1002 q 762 883 656 1002 q 867 582 867 764 l 867 406 q 762 105 867 223 q 488 -14 656 -14 q 222 75 321 -14 q 126 311 123 163 l 127 315 l 256 315 q 316 151 256 211 q 488 91 376 91 q 666 181 600 91 q 733 405 733 271 l 733 460 l 344 460 l 344 565 l 733 565 l 733 582 q 666 807 733 716 q 488 897 600 897 q 316 837 376 897 q 256 671 256 777 l 127 671 m 703 1103 l 555 1103 l 555 1239 l 703 1239 l 703 1103 m 385 1103 l 236 1103 l 236 1239 l 385 1239 l 385 1103 z "},"ӭ":{"ha":747,"x_min":67,"x_max":675,"o":"m 353 642 q 242 601 290 642 q 193 500 193 559 l 73 500 l 71 505 q 154 673 67 599 q 353 747 240 747 q 588 641 502 747 q 675 381 675 535 l 675 353 q 588 92 675 198 q 353 -14 501 -14 q 148 61 229 -14 q 71 248 67 137 l 72 252 l 193 252 q 239 137 193 184 q 353 90 285 90 q 485 157 438 90 q 537 318 532 223 l 535 321 l 274 321 l 274 426 l 534 426 l 535 429 q 482 580 528 518 q 353 642 435 642 m 605 870 l 456 870 l 456 1006 l 605 1006 l 605 870 m 286 870 l 138 870 l 138 1006 l 286 1006 l 286 870 z "},"Ӯ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 686 1112 l 197 1112 l 197 1211 l 686 1211 l 686 1112 z "},"ӯ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 597 881 l 108 881 l 108 980 l 597 980 l 597 881 z "},"Ӱ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 672 1088 l 524 1088 l 524 1224 l 672 1224 l 672 1088 m 353 1088 l 205 1088 l 205 1224 l 353 1224 l 353 1088 z "},"ӱ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 583 856 l 434 856 l 434 991 l 583 991 l 583 856 m 264 856 l 115 856 l 115 991 l 264 991 l 264 856 z "},"Ӳ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 624 1266 l 779 1266 l 781 1262 l 578 1086 l 462 1086 l 460 1089 l 624 1266 m 403 1266 l 546 1266 l 547 1263 l 385 1086 l 278 1086 l 403 1266 z "},"ӳ":{"ha":699,"x_min":18,"x_max":692,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 534 1034 l 690 1034 l 692 1029 l 488 853 l 372 853 l 371 857 l 534 1034 m 313 1034 l 456 1034 l 458 1030 l 295 853 l 189 853 l 313 1034 z "},"Ӵ":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 701 1088 l 552 1088 l 552 1224 l 701 1224 l 701 1088 m 382 1088 l 233 1088 l 233 1224 l 382 1224 l 382 1088 z "},"ӵ":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 608 856 l 460 856 l 460 991 l 608 991 l 608 856 m 290 856 l 141 856 l 141 991 l 290 991 l 290 856 z "},"Ӷ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 m 354 -243 l 220 -243 l 220 106 l 354 106 l 354 -243 z "},"ӷ":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 m 307 -243 l 173 -243 l 173 106 l 307 106 l 307 -243 z "},"Ӹ":{"ha":1208,"x_min":122,"x_max":1087,"o":"m 256 583 l 499 583 q 745 505 658 583 q 833 294 833 427 q 745 81 833 161 q 499 0 657 0 l 122 0 l 122 987 l 256 987 l 256 583 m 256 478 l 256 104 l 499 104 q 649 159 600 104 q 699 296 699 213 q 649 427 699 376 q 499 478 600 478 l 256 478 m 1087 0 l 953 0 l 953 987 l 1087 987 l 1087 0 m 839 1088 l 690 1088 l 690 1224 l 839 1224 l 839 1088 m 520 1088 l 372 1088 l 372 1224 l 520 1224 l 520 1088 z "},"ӹ":{"ha":1097,"x_min":117,"x_max":971,"o":"m 251 454 l 433 454 q 636 392 564 454 q 708 230 708 330 q 635 65 708 130 q 433 0 562 0 l 117 0 l 117 734 l 251 734 l 251 454 m 251 350 l 251 104 l 433 104 q 539 139 505 104 q 574 225 574 174 q 539 313 574 275 q 433 350 505 350 l 251 350 m 971 0 l 838 0 l 838 734 l 971 734 l 971 0 m 779 856 l 631 856 l 631 991 l 779 991 l 779 856 m 460 856 l 312 856 l 312 991 l 460 991 l 460 856 z "},"Ӻ":{"ha":824,"x_min":44,"x_max":779,"o":"m 779 882 l 307 882 l 307 0 l 174 0 l 174 987 l 779 987 l 779 882 m 479 482 l 44 482 l 44 587 l 479 587 l 479 482 m 429 104 l 429 -60 q 370 -235 429 -174 q 208 -296 312 -296 q 170 -293 187 -296 q 133 -284 153 -290 l 142 -176 q 174 -181 152 -179 q 208 -184 195 -184 q 272 -152 249 -184 q 295 -60 295 -120 l 295 104 l 429 104 z "},"ӻ":{"ha":570,"x_min":45,"x_max":630,"o":"m 630 628 l 297 628 l 297 0 l 163 0 l 163 734 l 630 734 l 630 628 m 479 364 l 45 364 l 45 469 l 479 469 l 479 364 m 418 104 l 418 -60 q 359 -235 418 -174 q 197 -296 301 -296 q 159 -293 176 -296 q 122 -284 142 -290 l 132 -176 q 163 -181 141 -179 q 197 -184 184 -184 q 261 -152 238 -184 q 284 -60 284 -120 l 284 104 l 418 104 z "},"Ӽ":{"ha":878,"x_min":45,"x_max":892,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 m 892 104 l 892 -60 q 833 -235 892 -174 q 671 -296 775 -296 q 633 -293 650 -296 q 596 -284 616 -290 l 606 -176 q 637 -181 615 -179 q 671 -184 659 -184 q 735 -152 712 -184 q 758 -60 758 -120 l 758 104 l 892 104 z "},"ӽ":{"ha":699,"x_min":31,"x_max":736,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 m 736 104 l 736 -60 q 678 -235 736 -174 q 516 -296 620 -296 q 478 -293 495 -296 q 441 -284 460 -290 l 450 -176 q 481 -181 460 -179 q 516 -184 503 -184 q 580 -152 557 -184 q 603 -60 603 -120 l 603 104 l 736 104 z "},"Ӿ":{"ha":878,"x_min":45,"x_max":840,"o":"m 665 452 l 549 452 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 336 452 l 231 452 l 231 557 l 326 557 l 52 987 l 212 987 l 441 602 l 671 987 l 833 987 l 557 557 l 665 557 l 665 452 z "},"ӿ":{"ha":699,"x_min":31,"x_max":665,"o":"m 559 324 l 450 324 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 245 324 l 124 324 l 124 429 l 237 429 l 38 734 l 192 734 l 346 463 l 502 734 l 658 734 l 458 429 l 559 429 l 559 324 z "},"Ԁ":{"ha":873,"x_min":62,"x_max":772,"o":"m 638 597 l 638 987 l 772 987 l 772 0 l 395 0 q 149 83 237 0 q 62 301 62 167 q 149 516 62 435 q 395 597 236 597 l 638 597 m 638 104 l 638 492 l 395 492 q 245 437 294 492 q 195 302 195 383 q 245 162 195 219 q 395 104 294 104 l 638 104 z "},"ԁ":{"ha":789,"x_min":66,"x_max":687,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"Ԃ":{"ha":1153,"x_min":62,"x_max":1116,"o":"m 395 0 q 149 83 237 0 q 62 301 62 167 q 149 516 62 435 q 395 597 236 597 l 638 597 l 638 987 l 772 987 l 772 104 l 828 104 q 939 153 900 105 q 981 286 979 201 q 971 395 982 338 q 941 505 961 452 l 1069 505 q 1104 386 1092 437 q 1115 286 1116 335 q 1030 74 1112 148 q 828 0 947 0 l 395 0 m 638 104 l 638 492 l 395 492 q 245 437 294 492 q 195 302 195 383 q 245 162 195 219 q 395 104 294 104 l 638 104 z "},"ԃ":{"ha":1197,"x_min":66,"x_max":1120,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 462 726 414 747 q 545 665 509 705 l 545 1058 l 678 1058 l 678 234 q 708 130 677 170 q 791 90 739 90 q 932 162 882 91 q 985 359 983 233 q 976 493 986 425 q 945 631 965 562 l 1074 632 q 1108 485 1096 549 q 1119 359 1120 421 q 1023 82 1116 178 q 791 -14 929 -14 q 655 13 710 -16 q 572 105 600 42 q 481 16 536 46 q 353 -14 427 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 545 554 q 479 616 518 593 q 388 639 441 639 q 246 559 292 639 q 200 353 200 480 l 200 339 q 245 161 200 228 q 387 94 291 94 q 483 119 444 94 q 549 189 522 144 q 546 210 547 199 q 545 234 545 222 l 545 554 z "},"Ԅ":{"ha":1101,"x_min":37,"x_max":1011,"o":"m 477 251 q 430 380 477 331 q 305 429 384 429 l 176 429 l 176 534 l 268 534 q 430 577 380 534 q 480 709 480 620 q 432 837 480 791 q 280 882 383 882 l 37 882 l 37 987 l 280 987 q 528 916 442 987 q 614 707 614 844 q 574 575 614 631 q 459 486 534 519 q 575 399 539 458 q 610 252 610 339 l 610 206 q 636 122 610 155 q 709 90 663 90 q 831 163 787 91 q 877 359 874 235 q 867 494 878 425 q 836 632 857 563 l 965 632 q 1000 480 988 541 q 1010 359 1011 420 q 922 82 1008 179 q 709 -14 836 -14 q 541 37 600 -16 q 477 206 482 90 l 477 251 z "},"ԅ":{"ha":898,"x_min":33,"x_max":854,"o":"m 507 144 q 520 101 507 116 q 565 86 533 86 q 676 139 637 87 q 719 284 716 191 q 709 391 720 336 q 677 500 698 447 l 806 500 q 842 382 829 431 q 852 284 854 332 q 767 60 849 138 q 565 -18 685 -18 q 425 19 471 -20 q 374 145 378 58 l 374 196 q 338 271 374 244 q 235 298 302 298 l 92 298 l 91 402 l 216 402 q 335 430 296 402 q 373 511 373 458 q 334 598 373 566 q 215 630 296 630 l 37 630 l 33 734 l 215 734 q 431 676 355 734 q 508 515 508 618 q 475 422 508 460 q 380 359 443 383 q 477 298 447 340 q 507 197 507 257 l 507 144 z "},"Ԇ":{"ha":745,"x_min":54,"x_max":663,"o":"m 663 31 l 561 -176 l 481 -176 l 520 0 l 479 0 q 444 68 451 24 q 437 161 437 113 l 437 250 q 390 379 437 330 q 265 429 344 429 l 116 429 l 116 533 l 226 533 q 390 576 339 533 q 440 707 440 619 q 392 836 440 790 q 240 882 343 882 l 54 882 l 54 987 l 240 987 q 488 916 402 987 q 573 706 573 844 q 533 574 573 630 q 418 485 494 517 q 535 398 499 458 q 570 252 570 338 l 570 159 l 570 149 l 663 149 l 663 31 z "},"ԇ":{"ha":701,"x_min":83,"x_max":652,"o":"m 652 18 l 550 -190 l 471 -190 l 513 0 l 470 0 q 438 49 445 16 q 431 117 431 83 l 431 182 q 395 267 431 236 q 293 298 359 298 l 131 298 l 131 402 l 273 402 q 393 430 355 402 q 431 511 431 458 q 392 598 431 566 q 273 630 354 630 l 83 630 l 83 734 l 273 734 q 489 676 412 734 q 565 515 565 619 q 532 421 565 460 q 435 357 499 382 q 535 293 505 338 q 565 182 565 248 l 565 135 l 652 135 l 652 18 z "},"Ԉ":{"ha":1347,"x_min":47,"x_max":1295,"o":"m 719 882 l 399 882 l 399 521 q 326 125 399 251 q 83 0 252 0 l 47 0 l 47 104 l 75 104 q 221 202 176 104 q 266 521 266 300 l 266 987 l 852 987 l 852 234 q 883 130 852 170 q 966 90 913 90 q 1107 162 1057 91 q 1160 359 1157 233 q 1150 493 1160 425 q 1119 631 1139 562 l 1248 632 q 1282 485 1270 549 q 1293 359 1295 421 q 1197 82 1291 178 q 966 -14 1103 -14 q 787 44 850 -16 q 719 234 724 104 l 719 882 z "},"ԉ":{"ha":1122,"x_min":44,"x_max":1086,"o":"m 551 628 l 343 628 l 343 422 q 283 103 343 207 q 83 0 222 0 l 44 0 l 47 114 l 75 115 q 178 186 147 115 q 210 422 210 258 l 210 734 l 685 734 l 685 234 q 715 130 684 170 q 797 90 745 90 q 909 154 869 91 q 951 329 948 218 q 941 456 951 391 q 909 587 930 522 l 1039 587 q 1074 447 1061 506 q 1084 329 1086 387 q 999 75 1082 163 q 797 -14 917 -14 q 620 44 683 -16 q 551 234 557 104 l 551 628 z "},"Ԋ":{"ha":1391,"x_min":115,"x_max":1301,"o":"m 859 987 l 859 234 q 888 130 858 170 q 971 90 919 90 q 1113 162 1063 91 q 1166 359 1163 233 q 1156 493 1166 425 q 1124 631 1145 562 l 1253 632 q 1289 485 1276 548 q 1299 359 1301 421 q 1202 82 1296 178 q 971 -14 1109 -14 q 793 44 857 -16 q 725 234 730 104 l 725 436 l 248 436 l 248 0 l 115 0 l 115 987 l 248 987 l 248 541 l 725 541 l 725 987 l 859 987 z "},"ԋ":{"ha":1152,"x_min":97,"x_max":1099,"o":"m 564 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 564 416 l 564 734 l 698 734 l 698 234 q 728 130 697 170 q 810 90 759 90 q 922 154 882 91 q 964 329 962 218 q 955 456 965 391 q 924 587 944 522 l 1052 587 q 1087 446 1075 504 q 1098 329 1099 387 q 1013 75 1095 163 q 810 -14 931 -14 q 633 44 696 -16 q 564 234 570 104 l 564 312 z "},"Ԍ":{"ha":862,"x_min":80,"x_max":802,"o":"m 473 -14 q 190 104 299 -14 q 80 405 80 223 l 80 583 q 190 883 80 764 q 473 1002 299 1002 q 610 986 549 1002 q 716 942 670 971 l 673 850 q 579 885 628 873 q 473 897 530 897 q 284 806 354 897 q 214 584 214 716 l 214 405 q 284 180 214 271 q 473 90 354 90 q 615 143 565 91 q 668 288 665 195 q 660 406 669 345 q 634 533 650 467 l 763 533 q 797 370 791 399 q 802 288 802 341 q 705 64 799 142 q 473 -14 611 -14 z "},"ԍ":{"ha":715,"x_min":66,"x_max":655,"o":"m 402 90 q 493 119 467 91 q 522 204 519 147 q 518 285 522 243 q 509 364 515 326 l 637 364 q 650 279 646 318 q 655 204 655 240 q 583 41 652 97 q 402 -14 514 -14 q 156 92 246 -14 q 66 353 66 199 l 66 381 q 153 641 66 534 q 388 747 239 747 q 500 735 452 747 q 580 703 548 724 l 549 603 q 475 632 517 621 q 388 642 434 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 251 168 200 245 q 402 90 301 90 z "},"Ԏ":{"ha":986,"x_min":24,"x_max":920,"o":"m 345 882 l 24 882 l 24 987 l 806 987 l 806 882 l 478 882 l 478 234 q 508 130 477 170 q 591 90 539 90 q 733 162 682 91 q 786 359 783 233 q 776 492 787 424 q 744 631 765 561 l 873 632 q 908 485 897 548 q 919 359 920 421 q 823 82 916 178 q 591 -14 729 -14 q 413 44 476 -16 q 345 234 350 104 l 345 882 z "},"ԏ":{"ha":890,"x_min":47,"x_max":823,"o":"m 288 630 l 47 630 l 47 734 l 663 734 l 663 630 l 421 630 l 421 234 q 451 130 420 170 q 534 90 481 90 q 645 143 606 91 q 688 289 685 196 q 677 397 688 340 q 647 508 667 454 l 775 508 q 810 388 798 438 q 821 289 823 338 q 736 64 819 142 q 534 -14 654 -14 q 356 44 419 -16 q 288 234 293 104 l 288 630 z "},"Ԑ":{"ha":939,"x_min":106,"x_max":869,"o":"m 486 450 q 301 407 363 450 q 239 273 239 363 q 308 141 239 191 q 497 90 376 90 q 670 143 603 90 q 738 274 738 197 l 865 274 l 867 270 q 760 58 869 131 q 497 -14 650 -14 q 214 63 322 -14 q 106 275 106 140 q 154 419 106 361 q 294 507 202 478 q 166 595 212 539 q 121 720 121 652 q 222 928 121 855 q 497 1002 323 1002 q 752 927 648 1002 q 852 733 856 852 l 852 729 l 724 729 q 658 848 724 800 q 497 897 592 897 q 315 848 376 897 q 254 723 254 799 q 311 601 254 646 q 486 556 368 556 l 611 556 l 611 450 l 486 450 z "},"ԑ":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 z "},"Ԓ":{"ha":984,"x_min":33,"x_max":995,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 m 995 104 l 995 -60 q 937 -235 995 -174 q 774 -296 878 -296 q 736 -293 753 -296 q 699 -284 719 -290 l 709 -176 q 740 -181 718 -179 q 774 -184 762 -184 q 838 -152 815 -184 q 861 -60 861 -120 l 861 104 l 995 104 z "},"ԓ":{"ha":768,"x_min":18,"x_max":825,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 m 825 104 l 825 -60 q 766 -235 825 -174 q 604 -296 708 -296 q 566 -293 583 -296 q 529 -284 549 -290 l 538 -176 q 570 -181 548 -179 q 604 -184 591 -184 q 668 -152 645 -184 q 691 -60 691 -120 l 691 104 l 825 104 z "},"Ḁ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 355 -159 q 386 -86 355 -115 q 461 -57 416 -57 q 534 -86 504 -57 q 565 -159 565 -115 q 535 -229 565 -201 q 461 -257 505 -257 q 386 -229 416 -257 q 355 -159 355 -201 m 415 -159 q 429 -189 415 -176 q 461 -202 442 -202 q 492 -190 479 -202 q 505 -159 505 -177 q 492 -125 505 -138 q 461 -112 479 -112 q 429 -125 442 -112 q 415 -159 415 -139 z "},"ḁ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 229 -159 q 260 -86 229 -115 q 336 -57 291 -57 q 409 -86 378 -57 q 439 -159 439 -115 q 409 -229 439 -201 q 336 -257 379 -257 q 260 -229 291 -257 q 229 -159 229 -201 m 290 -159 q 303 -189 290 -176 q 336 -202 317 -202 q 366 -190 353 -202 q 379 -159 379 -177 q 366 -125 379 -138 q 336 -112 353 -112 q 303 -125 317 -112 q 290 -159 290 -139 z "},"Ḿ":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 m 674 1236 l 830 1236 l 831 1232 l 648 1055 l 549 1055 l 674 1236 z "},"ḿ":{"ha":1214,"x_min":97,"x_max":1117,"o":"m 216 734 l 226 637 q 317 719 262 690 q 446 747 372 747 q 571 713 519 747 q 650 612 624 680 q 742 711 685 674 q 875 747 799 747 q 1052 670 987 747 q 1117 439 1117 593 l 1117 0 l 983 0 l 983 440 q 946 594 983 549 q 835 639 909 639 q 724 591 766 639 q 674 471 682 544 l 674 466 l 674 0 l 540 0 l 540 440 q 502 591 540 543 q 391 639 463 639 q 291 614 330 639 q 231 543 252 589 l 231 0 l 97 0 l 97 734 l 216 734 m 686 1017 l 842 1017 l 843 1013 l 660 837 l 560 837 l 686 1017 z "},"Ẁ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 665 1058 l 558 1058 l 389 1234 l 391 1238 l 547 1238 l 665 1058 z "},"ẁ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 581 825 l 473 825 l 304 1002 l 306 1006 l 462 1006 l 581 825 z "},"Ẃ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 673 1236 l 829 1236 l 831 1232 l 648 1055 l 548 1055 l 673 1236 z "},"ẃ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 589 1003 l 745 1003 l 746 999 l 563 823 l 463 823 l 589 1003 z "},"Ẅ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 843 1088 l 694 1088 l 694 1224 l 843 1224 l 843 1088 m 524 1088 l 376 1088 l 376 1224 l 524 1224 l 524 1088 z "},"ẅ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 758 856 l 610 856 l 610 991 l 758 991 l 758 856 m 439 856 l 291 856 l 291 991 l 439 991 l 439 856 z "},"Ạ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ạ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ả":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 406 1072 l 406 1176 q 479 1190 457 1179 q 501 1225 501 1200 q 472 1265 501 1252 q 393 1278 442 1278 l 397 1351 q 551 1316 497 1351 q 604 1223 604 1281 q 575 1151 604 1175 q 502 1120 546 1126 l 501 1072 l 406 1072 z "},"ả":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 332 853 l 331 958 q 405 971 382 960 q 427 1007 427 982 q 397 1046 427 1034 q 318 1059 368 1059 l 323 1133 q 476 1098 422 1133 q 530 1004 530 1063 q 500 932 530 957 q 427 901 471 907 l 427 853 l 332 853 z "},"Ấ":{"ha":899,"x_min":14,"x_max":892,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 399 1261 l 506 1261 l 701 1087 l 700 1083 l 566 1083 l 452 1190 l 339 1083 l 204 1083 l 203 1087 l 399 1261 m 749 1379 l 892 1379 l 754 1202 l 654 1202 l 749 1379 z "},"ấ":{"ha":764,"x_min":72,"x_max":812,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 319 1043 l 426 1043 l 621 869 l 620 865 l 486 865 l 372 971 l 259 865 l 124 865 l 123 869 l 319 1043 m 669 1160 l 812 1160 l 674 984 l 574 984 l 669 1160 z "},"Ầ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 706 1076 l 705 1072 l 570 1072 l 456 1178 l 343 1072 l 209 1072 l 208 1076 l 403 1250 l 510 1250 l 706 1076 m 254 1191 l 155 1191 l 17 1367 l 159 1367 l 254 1191 z "},"ầ":{"ha":764,"x_min":-63,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 626 857 l 625 853 l 490 853 l 376 960 l 263 853 l 129 853 l 127 857 l 323 1031 l 430 1031 l 626 857 m 174 972 l 75 972 l -63 1149 l 79 1149 l 174 972 z "},"Ẩ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 701 1062 l 699 1058 l 584 1058 l 451 1184 l 318 1058 l 203 1058 l 202 1062 l 384 1236 l 518 1236 l 701 1062 m 684 1152 l 683 1242 q 746 1253 728 1244 q 765 1284 765 1262 q 740 1318 765 1307 q 672 1329 714 1329 l 676 1392 q 809 1363 762 1392 q 855 1282 855 1333 q 830 1219 855 1241 q 766 1193 804 1198 l 765 1152 l 684 1152 z "},"ẩ":{"ha":764,"x_min":72,"x_max":775,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 621 845 l 619 841 l 504 841 l 371 967 l 238 841 l 123 841 l 122 845 l 304 1019 l 438 1019 l 621 845 m 604 935 l 603 1025 q 666 1036 648 1027 q 685 1067 685 1045 q 660 1101 685 1090 q 592 1112 634 1112 l 596 1175 q 729 1146 682 1175 q 775 1065 775 1116 q 750 1002 775 1024 q 686 976 724 981 l 685 935 l 604 935 z "},"Ẫ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 699 1063 l 698 1059 l 583 1059 l 450 1179 l 317 1059 l 202 1059 l 201 1063 l 397 1238 l 503 1238 l 699 1063 m 627 1407 q 594 1324 627 1360 q 516 1288 561 1288 q 442 1312 480 1288 q 378 1336 404 1336 q 339 1316 357 1336 q 322 1273 322 1296 l 268 1286 q 300 1370 268 1333 q 378 1408 333 1408 q 448 1384 406 1408 q 516 1361 490 1361 q 555 1380 536 1361 q 573 1423 573 1399 l 627 1407 z "},"ẫ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 623 846 l 622 842 l 507 842 l 374 962 l 241 842 l 126 842 l 125 846 l 321 1021 l 427 1021 l 623 846 m 551 1190 q 518 1107 551 1143 q 440 1071 485 1071 q 366 1095 404 1071 q 302 1119 328 1119 q 263 1099 281 1119 q 246 1056 246 1079 l 192 1069 q 224 1153 192 1116 q 302 1191 257 1191 q 372 1167 330 1191 q 440 1144 414 1144 q 479 1163 460 1144 q 497 1206 497 1182 l 551 1190 z "},"Ậ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 658 1103 l 658 1086 l 549 1086 l 449 1186 l 350 1086 l 241 1086 l 241 1103 l 408 1264 l 490 1264 l 658 1103 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ậ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 585 884 l 585 867 l 476 867 l 376 968 l 277 867 l 169 867 l 169 885 l 336 1046 l 417 1046 l 585 884 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ắ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 650 1218 l 652 1214 q 600 1115 656 1154 q 450 1076 545 1076 q 300 1115 355 1076 q 249 1214 244 1154 l 250 1218 l 353 1218 q 376 1162 353 1183 q 450 1141 399 1141 q 524 1162 500 1141 q 547 1218 547 1183 l 650 1218 m 484 1367 l 589 1367 l 591 1363 l 483 1233 l 411 1233 l 484 1367 z "},"ắ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 573 1000 l 574 996 q 523 897 578 935 q 373 858 467 858 q 222 897 278 858 q 172 996 167 935 l 173 1000 l 275 1000 q 299 944 275 965 q 373 922 322 922 q 446 944 422 922 q 470 1000 470 965 l 573 1000 m 407 1148 l 512 1148 l 513 1145 l 406 1015 l 334 1015 l 407 1148 z "},"Ằ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 495 1233 l 414 1233 l 279 1362 l 280 1367 l 401 1367 l 495 1233 m 650 1218 l 652 1214 q 600 1115 656 1154 q 450 1076 545 1076 q 300 1115 355 1076 q 249 1214 244 1154 l 250 1218 l 353 1218 q 376 1162 353 1183 q 450 1141 399 1141 q 524 1162 500 1141 q 547 1218 547 1183 l 650 1218 z "},"ằ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 418 1015 l 337 1015 l 201 1144 l 203 1148 l 324 1148 l 418 1015 m 573 1000 l 574 996 q 523 897 578 935 q 373 858 467 858 q 222 897 278 858 q 172 996 167 935 l 173 1000 l 275 1000 q 299 944 275 965 q 373 922 322 922 q 446 944 422 922 q 470 1000 470 965 l 573 1000 z "},"Ẳ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 643 1213 l 644 1209 q 595 1111 648 1149 q 450 1072 541 1072 q 305 1111 359 1072 q 256 1209 252 1149 l 258 1213 l 355 1213 q 378 1158 355 1179 q 450 1136 401 1136 q 522 1157 498 1136 q 545 1213 545 1179 l 643 1213 m 417 1251 l 416 1337 q 486 1347 466 1339 q 503 1372 507 1354 l 503 1376 q 479 1404 507 1395 q 404 1413 452 1413 l 409 1470 q 557 1443 505 1470 q 608 1372 608 1417 q 580 1317 608 1336 q 509 1294 552 1299 l 509 1251 l 417 1251 z "},"ẳ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 561 992 l 562 988 q 513 889 566 928 q 368 851 459 851 q 223 889 277 851 q 174 988 170 928 l 176 992 l 273 992 q 296 937 273 958 q 368 915 319 915 q 439 936 416 915 q 463 992 463 958 l 561 992 m 335 1030 l 334 1116 q 404 1125 384 1118 q 421 1151 425 1133 l 421 1155 q 397 1183 425 1174 q 322 1192 370 1192 l 327 1249 q 475 1222 423 1249 q 526 1151 526 1196 q 498 1096 526 1115 q 427 1073 470 1078 l 427 1030 l 335 1030 z "},"Ẵ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 646 1216 l 647 1212 q 597 1111 651 1150 q 450 1072 543 1072 q 303 1111 357 1072 q 253 1212 248 1150 l 254 1216 l 355 1216 q 377 1159 355 1181 q 450 1137 400 1137 q 522 1159 498 1137 q 545 1216 545 1181 l 646 1216 m 646 1398 q 613 1311 646 1347 q 533 1274 581 1274 q 452 1300 494 1274 q 382 1326 410 1326 q 343 1308 359 1326 q 328 1265 328 1291 l 269 1280 q 300 1368 269 1330 q 382 1405 332 1405 q 458 1379 412 1405 q 533 1354 504 1354 q 571 1371 555 1354 q 587 1414 587 1388 l 646 1398 z "},"ẵ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 567 998 l 568 994 q 518 892 572 932 q 372 853 464 853 q 224 892 279 853 q 174 994 170 932 l 176 998 l 276 998 q 299 940 276 962 q 372 918 321 918 q 443 940 420 918 q 467 998 467 962 l 567 998 m 567 1179 q 535 1092 567 1128 q 454 1056 503 1056 q 373 1082 415 1056 q 303 1107 331 1107 q 265 1090 280 1107 q 250 1047 250 1072 l 190 1061 q 222 1149 190 1112 q 303 1187 254 1187 q 379 1161 334 1187 q 454 1135 425 1135 q 492 1153 476 1135 q 509 1196 509 1170 l 567 1179 z "},"Ặ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ặ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ẹ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 500 -222 l 352 -222 l 352 -86 l 500 -86 l 500 -222 z "},"ẹ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 463 -229 l 315 -229 l 315 -93 l 463 -93 l 463 -229 z "},"Ẻ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 381 1072 l 380 1176 q 454 1190 432 1179 q 476 1225 476 1200 q 447 1265 476 1252 q 368 1278 417 1278 l 372 1351 q 526 1316 472 1351 q 579 1223 579 1281 q 550 1151 579 1175 q 477 1120 521 1126 l 476 1072 l 381 1072 z "},"ẻ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 334 854 l 333 958 q 407 972 385 961 q 429 1008 429 983 q 399 1047 429 1034 q 320 1060 370 1060 l 325 1133 q 478 1098 425 1133 q 532 1005 532 1063 q 503 933 532 958 q 429 902 473 908 l 429 854 l 334 854 z "},"Ẽ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 661 1251 q 621 1143 661 1187 q 519 1099 581 1099 q 419 1130 471 1099 q 332 1162 367 1162 q 283 1140 303 1162 q 264 1086 264 1118 l 191 1104 q 231 1214 191 1167 q 332 1260 271 1260 q 427 1228 370 1260 q 519 1196 484 1196 q 568 1218 547 1196 q 588 1272 588 1240 l 661 1251 z "},"ẽ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 614 1033 q 573 925 614 969 q 472 881 533 881 q 372 912 424 881 q 285 944 319 944 q 236 922 256 944 q 216 869 216 900 l 143 886 q 183 996 143 949 q 285 1042 223 1042 q 380 1010 323 1042 q 472 979 437 979 q 520 1001 500 979 q 541 1055 541 1023 l 614 1033 z "},"Ế":{"ha":812,"x_min":122,"x_max":862,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 368 1261 l 475 1261 l 671 1087 l 669 1083 l 535 1083 l 422 1190 l 309 1083 l 174 1083 l 172 1087 l 368 1261 m 719 1379 l 862 1379 l 724 1202 l 624 1202 l 719 1379 z "},"ế":{"ha":734,"x_min":67,"x_max":814,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 321 1044 l 428 1044 l 623 869 l 622 865 l 488 865 l 374 972 l 261 865 l 126 865 l 125 869 l 321 1044 m 671 1161 l 814 1161 l 676 985 l 576 985 l 671 1161 z "},"Ề":{"ha":812,"x_min":-14,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 675 1076 l 674 1072 l 539 1072 l 426 1178 l 313 1072 l 178 1072 l 177 1076 l 372 1250 l 479 1250 l 675 1076 m 224 1191 l 124 1191 l -14 1367 l 129 1367 l 224 1191 z "},"ề":{"ha":734,"x_min":-61,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 628 858 l 627 854 l 492 854 l 378 960 l 265 854 l 131 854 l 130 858 l 325 1032 l 432 1032 l 628 858 m 176 973 l 77 973 l -61 1149 l 81 1149 l 176 973 z "},"Ể":{"ha":812,"x_min":122,"x_max":825,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 670 1062 l 669 1058 l 553 1058 l 420 1184 l 288 1058 l 173 1058 l 172 1062 l 353 1236 l 488 1236 l 670 1062 m 653 1152 l 652 1242 q 716 1253 697 1244 q 734 1284 734 1262 q 709 1318 734 1307 q 642 1329 684 1329 l 646 1392 q 778 1363 732 1392 q 825 1282 825 1333 q 799 1219 825 1241 q 735 1193 774 1198 l 734 1152 l 653 1152 z "},"ể":{"ha":734,"x_min":67,"x_max":777,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 623 846 l 621 842 l 506 842 l 373 968 l 240 842 l 125 842 l 124 846 l 306 1020 l 440 1020 l 623 846 m 606 935 l 605 1025 q 668 1037 650 1028 q 687 1067 687 1046 q 662 1101 687 1090 q 594 1112 636 1112 l 598 1176 q 731 1146 684 1176 q 777 1066 777 1117 q 752 1003 777 1025 q 688 977 726 981 l 687 935 l 606 935 z "},"Ễ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 673 1063 l 671 1059 l 556 1059 l 423 1179 l 290 1059 l 176 1059 l 174 1063 l 370 1238 l 477 1238 l 673 1063 m 600 1407 q 567 1324 600 1360 q 490 1288 534 1288 q 415 1312 454 1288 q 351 1336 377 1336 q 313 1316 330 1336 q 296 1273 296 1296 l 241 1286 q 274 1370 241 1333 q 351 1408 307 1408 q 421 1384 380 1408 q 490 1361 463 1361 q 528 1380 510 1361 q 547 1423 547 1399 l 600 1407 z "},"ễ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 625 847 l 624 843 l 509 843 l 376 962 l 243 843 l 128 843 l 127 847 l 323 1021 l 429 1021 l 625 847 m 553 1191 q 520 1108 553 1144 q 442 1072 487 1072 q 368 1096 406 1072 q 304 1120 330 1120 q 266 1100 283 1120 q 248 1057 248 1080 l 194 1069 q 227 1154 194 1116 q 304 1192 259 1192 q 374 1168 332 1192 q 442 1145 416 1145 q 481 1164 463 1145 q 499 1206 499 1183 l 553 1191 z "},"Ệ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 635 1103 l 635 1086 l 526 1086 l 426 1186 l 327 1086 l 218 1086 l 218 1103 l 385 1264 l 467 1264 l 635 1103 m 500 -222 l 352 -222 l 352 -86 l 500 -86 l 500 -222 z "},"ệ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 587 885 l 587 868 l 478 868 l 378 968 l 279 868 l 171 868 l 171 886 l 338 1046 l 419 1046 l 587 885 m 463 -229 l 315 -229 l 315 -93 l 463 -93 l 463 -229 z "},"Ỉ":{"ha":393,"x_min":129,"x_max":349,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 151 1072 l 151 1176 q 224 1190 202 1179 q 246 1225 246 1200 q 217 1265 246 1252 q 138 1278 187 1278 l 142 1351 q 296 1316 242 1351 q 349 1223 349 1281 q 320 1151 349 1175 q 247 1120 291 1126 l 246 1072 l 151 1072 z "},"ỉ":{"ha":349,"x_min":104,"x_max":324,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 126 840 l 125 945 q 199 958 177 947 q 221 994 221 969 q 192 1034 221 1021 q 113 1046 162 1046 l 117 1120 q 271 1085 217 1120 q 324 991 324 1050 q 295 919 324 944 q 222 888 266 895 l 221 840 l 126 840 z "},"Ị":{"ha":393,"x_min":122,"x_max":270,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 270 -222 l 122 -222 l 122 -87 l 270 -87 l 270 -222 z "},"ị":{"ha":350,"x_min":101,"x_max":249,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 249 -222 l 101 -222 l 101 -86 l 249 -86 l 249 -222 z "},"Ọ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 547 -233 l 399 -233 l 399 -98 l 547 -98 l 547 -233 z "},"ọ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ỏ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 429 1086 l 428 1190 q 502 1204 479 1193 q 524 1240 524 1215 q 494 1279 524 1266 q 415 1292 465 1292 l 420 1365 q 573 1330 519 1365 q 627 1237 627 1295 q 597 1165 627 1190 q 524 1134 568 1140 l 524 1086 l 429 1086 z "},"ỏ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 350 853 l 349 958 q 423 971 401 960 q 445 1007 445 982 q 415 1046 445 1034 q 336 1059 386 1059 l 341 1133 q 494 1098 441 1133 q 548 1004 548 1063 q 519 932 548 957 q 446 901 490 907 l 445 853 l 350 853 z "},"Ố":{"ha":947,"x_min":77,"x_max":909,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 416 1276 l 523 1276 l 718 1101 l 717 1097 l 583 1097 l 469 1204 l 356 1097 l 221 1097 l 220 1101 l 416 1276 m 766 1393 l 909 1393 l 771 1217 l 671 1217 l 766 1393 z "},"ố":{"ha":789,"x_min":66,"x_max":831,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 337 1043 l 444 1043 l 640 869 l 638 865 l 504 865 l 391 971 l 277 865 l 142 865 l 141 869 l 337 1043 m 688 1160 l 831 1160 l 692 984 l 593 984 l 688 1160 z "},"Ồ":{"ha":947,"x_min":34,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 723 1090 l 722 1086 l 587 1086 l 473 1192 l 360 1086 l 226 1086 l 224 1090 l 420 1264 l 527 1264 l 723 1090 m 271 1205 l 172 1205 l 34 1381 l 176 1381 l 271 1205 z "},"ồ":{"ha":789,"x_min":-45,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 644 857 l 643 853 l 508 853 l 395 960 l 281 853 l 147 853 l 146 857 l 341 1031 l 448 1031 l 644 857 m 193 972 l 93 972 l -45 1149 l 98 1149 l 193 972 z "},"Ổ":{"ha":947,"x_min":77,"x_max":872,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 718 1076 l 716 1072 l 601 1072 l 468 1198 l 335 1072 l 220 1072 l 219 1076 l 401 1251 l 535 1251 l 718 1076 m 701 1166 l 700 1256 q 763 1267 745 1259 q 782 1298 782 1276 q 756 1332 782 1321 q 689 1343 731 1343 l 693 1407 q 826 1377 779 1407 q 872 1297 872 1348 q 847 1234 872 1255 q 783 1207 821 1212 l 782 1166 l 701 1166 z "},"ổ":{"ha":789,"x_min":66,"x_max":793,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 639 845 l 637 841 l 522 841 l 389 967 l 256 841 l 142 841 l 140 845 l 322 1019 l 456 1019 l 639 845 m 622 935 l 621 1025 q 685 1036 666 1027 q 703 1067 703 1045 q 678 1101 703 1090 q 610 1112 652 1112 l 614 1175 q 747 1146 701 1175 q 793 1065 793 1116 q 768 1002 793 1024 q 704 976 743 981 l 703 935 l 622 935 z "},"Ỗ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 720 1078 l 719 1074 l 604 1074 l 471 1193 l 338 1074 l 223 1074 l 222 1078 l 418 1252 l 524 1252 l 720 1078 m 648 1421 q 615 1338 648 1375 q 537 1302 582 1302 q 463 1326 501 1302 q 399 1350 425 1350 q 360 1330 378 1350 q 343 1287 343 1310 l 289 1300 q 321 1384 289 1347 q 399 1422 354 1422 q 469 1399 427 1422 q 537 1375 511 1375 q 576 1394 557 1375 q 594 1437 594 1413 l 648 1421 z "},"ỗ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 642 846 l 640 842 l 525 842 l 392 962 l 259 842 l 144 842 l 143 846 l 339 1021 l 446 1021 l 642 846 m 569 1190 q 536 1107 569 1143 q 458 1071 503 1071 q 384 1095 422 1071 q 320 1119 346 1119 q 282 1099 299 1119 q 264 1056 264 1079 l 210 1069 q 243 1153 210 1116 q 320 1191 275 1191 q 390 1167 349 1191 q 458 1144 432 1144 q 497 1163 479 1144 q 515 1206 515 1182 l 569 1190 z "},"Ộ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 682 1117 l 682 1100 l 573 1100 l 473 1200 l 374 1100 l 266 1100 l 266 1118 l 433 1278 l 514 1278 l 682 1117 m 547 -233 l 399 -233 l 399 -98 l 547 -98 l 547 -233 z "},"ộ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 604 884 l 604 867 l 494 867 l 395 968 l 296 867 l 187 867 l 187 885 l 354 1046 l 435 1046 l 604 884 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ớ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 540 1223 l 696 1223 l 697 1219 l 514 1043 l 414 1043 l 540 1223 z "},"ớ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 459 1017 l 615 1017 l 616 1013 l 433 837 l 334 837 l 459 1017 z "},"Ờ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 532 1046 l 425 1046 l 255 1222 l 257 1226 l 413 1226 l 532 1046 z "},"ờ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 451 840 l 344 840 l 174 1016 l 176 1020 l 332 1020 l 451 840 z "},"Ở":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 431 1086 l 430 1190 q 504 1204 481 1193 q 526 1240 526 1215 q 496 1279 526 1266 q 417 1292 467 1292 l 422 1365 q 575 1330 522 1365 q 629 1237 629 1295 q 600 1165 629 1190 q 526 1134 570 1140 l 526 1086 l 431 1086 z "},"ở":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 350 853 l 349 958 q 423 971 401 960 q 445 1007 445 982 q 415 1046 445 1034 q 336 1059 386 1059 l 341 1133 q 494 1098 441 1133 q 548 1004 548 1063 q 519 932 548 957 q 446 901 490 907 l 445 853 l 350 853 z "},"Ỡ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 711 1238 q 670 1131 711 1175 q 569 1086 630 1086 q 469 1118 521 1086 q 382 1149 416 1149 q 333 1127 353 1149 q 313 1074 313 1105 l 240 1092 q 280 1201 240 1155 q 382 1248 320 1248 q 477 1216 420 1248 q 569 1184 534 1184 q 617 1206 597 1184 q 637 1260 637 1228 l 711 1238 z "},"ỡ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 630 1032 q 590 924 630 968 q 488 880 549 880 q 388 912 440 880 q 301 943 336 943 q 252 921 272 943 q 233 868 233 899 l 159 886 q 199 995 159 949 q 301 1042 239 1042 q 396 1010 339 1042 q 488 978 453 978 q 536 1000 516 978 q 557 1054 557 1022 l 630 1032 z "},"Ợ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 543 -229 l 395 -229 l 395 -93 l 543 -93 l 543 -229 z "},"ợ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ụ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 546 -233 l 398 -233 l 398 -98 l 546 -98 l 546 -233 z "},"ụ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 422 -229 l 275 -229 l 275 -93 l 422 -93 l 422 -229 z "},"Ủ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 428 1072 l 427 1176 q 501 1190 479 1179 q 523 1225 523 1200 q 493 1265 523 1252 q 414 1278 464 1278 l 419 1351 q 572 1316 519 1351 q 626 1223 626 1281 q 597 1151 626 1175 q 524 1120 568 1126 l 523 1072 l 428 1072 z "},"ủ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 349 840 l 348 945 q 421 958 399 947 q 444 994 444 969 q 414 1034 444 1021 q 335 1046 385 1046 l 340 1120 q 493 1085 439 1120 q 547 991 547 1050 q 517 919 547 944 q 444 888 488 895 l 444 840 l 349 840 z "},"Ứ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 536 1236 l 692 1236 l 694 1232 l 511 1055 l 411 1055 l 536 1236 z "},"ứ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 458 1003 l 614 1003 l 615 999 l 432 823 l 332 823 l 458 1003 z "},"Ừ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 528 1058 l 421 1058 l 252 1234 l 254 1238 l 410 1238 l 528 1058 z "},"ừ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 450 825 l 342 825 l 173 1002 l 175 1006 l 331 1006 l 450 825 z "},"Ử":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 427 1072 l 427 1176 q 500 1190 478 1179 q 522 1225 522 1200 q 493 1265 522 1252 q 414 1278 463 1278 l 418 1351 q 572 1316 518 1351 q 625 1223 625 1281 q 596 1151 625 1175 q 523 1120 567 1126 l 522 1072 l 427 1072 z "},"ử":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 349 840 l 348 945 q 421 958 399 947 q 444 994 444 969 q 414 1034 444 1021 q 335 1046 385 1046 l 340 1120 q 493 1085 439 1120 q 547 991 547 1050 q 517 919 547 944 q 444 888 488 895 l 444 840 l 349 840 z "},"Ữ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 707 1251 q 667 1143 707 1187 q 566 1099 627 1099 q 465 1130 517 1099 q 378 1162 413 1162 q 330 1140 349 1162 q 310 1086 310 1118 l 237 1104 q 277 1214 237 1167 q 378 1260 317 1260 q 473 1228 416 1260 q 566 1196 530 1196 q 614 1218 593 1196 q 634 1272 634 1240 l 707 1251 z "},"ữ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 629 1018 q 588 910 629 954 q 487 866 548 866 q 387 898 439 866 q 300 929 334 929 q 251 907 271 929 q 231 854 231 885 l 158 871 q 198 981 158 935 q 300 1027 238 1027 q 395 996 338 1027 q 487 964 452 964 q 535 986 515 964 q 555 1040 555 1008 l 629 1018 z "},"Ự":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 546 -233 l 398 -233 l 398 -98 l 546 -98 l 546 -233 z "},"ự":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 422 -229 l 275 -229 l 275 -93 l 422 -93 l 422 -229 z "},"Ỳ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 480 1057 l 373 1057 l 203 1234 l 205 1238 l 361 1238 l 480 1057 z "},"ỳ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 405 825 l 298 825 l 128 1002 l 130 1006 l 286 1006 l 405 825 z "},"Ỵ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 498 -222 l 350 -222 l 350 -86 l 498 -86 l 498 -222 z "},"ỵ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 560 -334 l 412 -334 l 412 -199 l 560 -199 l 560 -334 z "},"Ỷ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 379 1071 l 378 1175 q 452 1189 430 1178 q 474 1225 474 1200 q 445 1264 474 1251 q 366 1277 415 1277 l 370 1350 q 524 1315 470 1350 q 577 1222 577 1280 q 548 1150 577 1175 q 475 1119 519 1125 l 474 1071 l 379 1071 z "},"ỷ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 304 840 l 303 945 q 377 958 355 947 q 399 994 399 969 q 369 1034 399 1021 q 290 1046 340 1046 l 295 1120 q 448 1085 395 1120 q 502 991 502 1050 q 473 919 502 944 q 399 888 444 895 l 399 840 l 304 840 z "},"Ỹ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 659 1250 q 619 1142 659 1186 q 517 1098 578 1098 q 417 1129 469 1098 q 330 1161 365 1161 q 281 1139 301 1161 q 262 1086 262 1117 l 189 1103 q 229 1213 189 1166 q 330 1259 269 1259 q 425 1227 368 1259 q 517 1196 482 1196 q 566 1218 545 1196 q 586 1272 586 1240 l 659 1250 z "},"ỹ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 584 1018 q 544 910 584 954 q 442 866 503 866 q 342 898 394 866 q 255 929 290 929 q 206 907 226 929 q 186 854 186 885 l 113 871 q 153 981 113 935 q 255 1027 193 1027 q 350 996 293 1027 q 442 964 407 964 q 490 986 470 964 q 511 1040 511 1008 l 584 1018 z "},"Ὅ":{"ha":947,"x_min":-149,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 55 929 l 124 1141 l 266 1141 l 266 1127 l 111 911 l 55 911 l 55 929 m -149 1006 l -39 1141 l 34 1141 l -22 1002 l -22 909 l -149 909 l -149 1006 z "}," ":{"ha":708,"x_min":0,"x_max":0,"o":""}," ":{"ha":1417,"x_min":0,"x_max":0,"o":""}," ":{"ha":708,"x_min":0,"x_max":0,"o":""}," ":{"ha":1417,"x_min":0,"x_max":0,"o":""}," ":{"ha":473,"x_min":0,"x_max":0,"o":""}," ":{"ha":354,"x_min":0,"x_max":0,"o":""}," ":{"ha":236,"x_min":0,"x_max":0,"o":""}," ":{"ha":781,"x_min":0,"x_max":0,"o":""}," ":{"ha":380,"x_min":0,"x_max":0,"o":""}," ":{"ha":283,"x_min":0,"x_max":0,"o":""}," ":{"ha":142,"x_min":0,"x_max":0,"o":""},"​":{"ha":0,"x_min":0,"x_max":0,"o":""},"–":{"ha":960,"x_min":122,"x_max":859,"o":"m 858 439 l 122 439 l 122 545 l 858 545 l 858 439 z "},"—":{"ha":1126,"x_min":128,"x_max":1034,"o":"m 1034 439 l 128 439 l 128 545 l 1034 545 l 1034 439 z "},"―":{"ha":1126,"x_min":128,"x_max":1034,"o":"m 1034 439 l 128 439 l 128 545 l 1034 545 l 1034 439 z "},"‗":{"ha":634,"x_min":3,"x_max":629,"o":"m 629 -277 l 3 -277 l 3 -172 l 629 -172 l 629 -277 m 629 -104 l 3 -104 l 3 0 l 629 0 l 629 -104 z "},"‘":{"ha":283,"x_min":54,"x_max":229,"o":"m 54 817 l 163 1058 l 229 1058 l 188 812 l 188 692 l 54 692 l 54 817 z "},"’":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 z "},"‚":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 18 l 120 -173 l 54 -173 l 95 5 l 95 169 l 229 169 l 229 18 z "},"‛":{"ha":283,"x_min":29,"x_max":203,"o":"m 29 927 l 138 692 l 203 692 l 163 925 l 163 1058 l 29 1058 l 29 927 z "},"“":{"ha":505,"x_min":54,"x_max":452,"o":"m 54 817 l 163 1058 l 229 1058 l 188 812 l 188 692 l 54 692 l 54 817 m 277 817 l 386 1058 l 452 1058 l 411 812 l 411 692 l 277 692 l 277 817 z "},"”":{"ha":510,"x_min":54,"x_max":457,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 m 457 927 l 349 692 l 283 692 l 323 925 l 323 1058 l 457 1058 l 457 927 z "},"„":{"ha":492,"x_min":54,"x_max":437,"o":"m 229 65 l 120 -162 l 54 -162 l 95 58 l 95 190 l 229 190 l 229 65 m 437 65 l 329 -162 l 263 -162 l 304 64 l 304 190 l 437 190 l 437 65 z "},"†":{"ha":766,"x_min":47,"x_max":719,"o":"m 719 628 l 448 628 l 448 0 l 315 0 l 315 628 l 47 628 l 47 734 l 315 734 l 315 987 l 448 987 l 448 734 l 719 734 l 719 628 z "},"‡":{"ha":793,"x_min":59,"x_max":730,"o":"m 730 0 l 459 0 l 459 -282 l 326 -282 l 326 0 l 59 0 l 59 104 l 326 104 l 326 628 l 59 628 l 59 734 l 326 734 l 326 987 l 459 987 l 459 734 l 730 734 l 730 628 l 459 628 l 459 104 l 730 104 l 730 0 z "},"•":{"ha":471,"x_min":93,"x_max":374,"o":"m 93 538 q 131 635 93 597 q 233 673 169 673 q 335 635 297 673 q 374 538 374 597 l 374 497 q 336 400 374 437 q 233 363 298 363 q 131 400 169 363 q 93 497 93 437 l 93 538 z "},"‥":{"ha":658,"x_min":109,"x_max":543,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 543 0 l 410 0 l 410 137 l 543 137 l 543 0 z "},"…":{"ha":936,"x_min":109,"x_max":828,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 543 0 l 410 0 l 410 137 l 543 137 l 543 0 m 828 0 l 694 0 l 694 137 l 828 137 l 828 0 z "},"‰":{"ha":1326,"x_min":43,"x_max":1273,"o":"m 555 242 q 611 387 555 328 q 759 446 667 446 q 849 424 809 446 q 913 366 888 403 q 978 424 939 403 q 1068 446 1018 446 q 1217 387 1161 446 q 1273 242 1273 328 l 1273 189 q 1217 44 1273 102 q 1069 -14 1162 -14 q 979 6 1019 -14 q 913 64 939 27 q 849 6 888 27 q 760 -14 810 -14 q 611 44 667 -14 q 555 189 555 102 l 555 242 m 43 798 q 99 943 43 884 q 247 1002 155 1002 q 396 943 340 1002 q 452 798 452 884 l 452 745 q 396 601 452 659 q 248 543 340 543 q 99 601 155 543 q 43 745 43 659 l 43 798 m 654 189 q 682 103 654 138 q 760 68 709 68 q 838 103 810 68 q 865 189 865 138 l 865 242 q 837 328 865 292 q 759 363 809 363 q 682 328 709 363 q 654 242 654 292 l 654 189 m 964 189 q 991 103 964 138 q 1069 68 1019 68 q 1146 103 1119 68 q 1173 189 1173 138 l 1173 242 q 1146 328 1173 292 q 1068 363 1118 363 q 991 328 1018 363 q 964 242 964 292 l 964 189 m 142 745 q 170 660 142 695 q 248 625 197 625 q 325 660 298 625 q 353 745 353 694 l 353 798 q 325 883 353 848 q 247 919 297 919 q 170 883 197 919 q 142 798 142 848 l 142 745 m 311 75 l 237 120 l 719 892 l 793 846 l 311 75 z "},"′":{"ha":243,"x_min":54,"x_max":189,"o":"m 189 907 l 120 715 l 54 715 l 55 895 l 55 1058 l 189 1058 l 189 907 z "},"″":{"ha":453,"x_min":54,"x_max":398,"o":"m 189 875 l 120 705 l 54 705 l 55 868 l 55 1058 l 189 1058 l 189 875 m 398 875 l 330 705 l 264 705 l 264 873 l 264 1058 l 398 1058 l 398 875 z "},"‹":{"ha":417,"x_min":73,"x_max":374,"o":"m 201 373 l 374 103 l 273 103 l 73 367 l 73 380 l 273 644 l 374 644 l 201 373 z "},"›":{"ha":417,"x_min":60,"x_max":360,"o":"m 160 644 l 360 380 l 360 367 l 160 103 l 60 103 l 233 373 l 60 644 l 160 644 z "},"‼":{"ha":731,"x_min":116,"x_max":616,"o":"m 250 324 l 116 324 l 116 987 l 250 987 l 250 324 m 250 0 l 116 0 l 116 138 l 250 138 l 250 0 m 615 324 l 481 324 l 481 987 l 615 987 l 615 324 m 616 0 l 481 0 l 481 138 l 616 138 l 616 0 z "},"⁄":{"ha":633,"x_min":40,"x_max":596,"o":"m 114 75 l 40 120 l 522 892 l 596 846 l 114 75 z "},"⁴":{"ha":638,"x_min":48,"x_max":577,"o":"m 476 592 l 577 592 l 577 505 l 476 505 l 476 380 l 359 380 l 359 505 l 51 505 l 48 570 l 356 1002 l 476 1002 l 476 592 m 177 592 l 359 592 l 359 848 l 355 848 l 347 831 l 177 592 z "},"ⁿ":{"ha":586,"x_min":83,"x_max":517,"o":"m 175 984 l 196 903 q 258 970 220 946 q 342 994 295 994 q 471 943 425 994 q 517 786 517 892 l 517 441 l 395 441 l 395 764 q 371 864 395 833 q 302 895 347 895 q 242 876 267 895 q 205 826 218 858 l 205 441 l 83 441 l 83 984 l 175 984 z "},"₣":{"ha":809,"x_min":19,"x_max":775,"o":"m 706 437 l 256 437 l 256 0 l 122 0 l 122 987 l 775 987 l 775 882 l 256 882 l 256 542 l 706 542 l 706 437 m 454 179 l 19 179 l 19 284 l 454 284 l 454 179 z "},"₤":{"ha":810,"x_min":47,"x_max":753,"o":"m 295 299 q 283 192 294 243 q 255 104 273 141 l 753 104 l 753 0 l 91 0 l 91 104 l 98 104 q 144 171 128 113 q 163 299 160 230 l 47 299 l 47 404 l 159 404 l 155 501 l 48 501 l 48 606 l 151 606 l 148 705 q 224 923 148 844 q 427 1002 300 1002 q 637 931 563 1002 q 709 743 712 860 l 708 739 l 579 739 q 536 858 579 819 q 427 897 494 897 q 321 846 360 897 q 281 705 281 795 l 285 606 l 571 606 l 571 501 l 289 501 l 292 404 l 571 404 l 571 299 l 295 299 z "},"₧":{"ha":1141,"x_min":111,"x_max":1053,"o":"m 907 911 l 907 734 l 1046 734 l 1046 635 l 907 635 l 907 189 q 928 117 907 138 q 985 96 949 96 q 1010 98 996 96 q 1035 105 1024 101 l 1053 14 q 1009 -6 1038 1 q 951 -14 980 -14 q 821 35 869 -14 q 773 189 773 84 l 773 635 l 682 635 q 597 466 668 534 q 351 382 509 382 l 245 382 l 245 0 l 111 0 l 111 987 l 351 987 q 597 903 509 987 q 682 734 668 834 l 773 734 l 773 911 l 907 911 m 245 488 l 351 488 q 501 544 451 488 q 551 684 551 601 q 501 825 551 767 q 351 882 451 882 l 245 882 l 245 488 z "},"₫":{"ha":810,"x_min":66,"x_max":821,"o":"m 738 -187 l 112 -187 l 112 -83 l 738 -83 l 738 -187 m 821 835 l 687 835 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 835 l 386 835 l 386 940 l 553 940 l 553 1058 l 687 1058 l 687 940 l 821 940 l 821 835 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"€":{"ha":738,"x_min":54,"x_max":665,"o":"m 620 326 l 291 326 l 290 323 q 345 160 287 229 q 503 90 403 90 q 579 96 541 90 q 652 113 617 102 l 665 7 q 586 -9 627 -3 q 503 -14 544 -14 q 249 84 346 -14 q 153 326 153 182 l 54 326 l 54 431 l 153 431 l 153 524 l 54 524 l 54 629 l 153 629 l 153 639 q 249 903 153 804 q 501 1002 345 1002 q 581 996 541 1002 q 665 981 621 991 l 652 873 q 577 890 616 884 q 501 897 539 897 q 344 827 402 897 q 286 640 286 757 l 286 629 l 620 629 l 620 524 l 286 524 l 286 431 l 620 431 l 620 326 z "},"℅":{"ha":1026,"x_min":84,"x_max":957,"o":"m 459 716 l 460 712 q 412 592 463 641 q 276 543 361 543 q 136 601 189 543 q 84 745 84 659 l 84 798 q 136 943 84 884 q 275 1002 188 1002 q 412 951 360 1002 q 460 831 463 901 l 459 827 l 366 827 q 342 892 366 866 q 275 919 318 919 q 207 883 231 919 q 183 798 183 848 l 183 745 q 207 660 183 695 q 276 625 231 625 q 342 652 319 625 q 366 716 366 680 l 459 716 m 549 242 q 604 387 549 328 q 753 446 660 446 q 901 387 845 446 q 957 242 957 328 l 957 189 q 901 44 957 102 q 754 -14 846 -14 q 605 44 661 -14 q 549 189 549 102 l 549 242 m 648 189 q 675 103 648 138 q 754 68 703 68 q 831 103 804 68 q 858 189 858 138 l 858 242 q 830 328 858 292 q 753 363 802 363 q 675 328 703 363 q 648 242 648 292 l 648 189 m 303 75 l 229 120 l 711 892 l 785 846 l 303 75 z "},"ℓ":{"ha":663,"x_min":72,"x_max":599,"o":"m 487 -9 l 483 -10 q 278 68 348 -13 q 209 292 209 149 l 209 300 q 141 288 176 292 q 72 284 107 284 l 72 406 q 143 411 109 406 q 209 424 178 415 l 209 745 q 263 933 209 865 q 411 1002 317 1002 q 546 943 494 1002 q 599 787 599 885 l 599 759 q 530 537 599 652 q 342 355 461 422 l 342 292 q 375 144 342 194 q 487 95 408 95 l 487 -9 m 465 758 l 465 787 q 450 865 465 839 q 411 892 435 892 q 359 855 376 892 q 342 745 342 818 l 342 503 l 347 502 q 435 614 404 545 q 465 758 465 684 z "},"№":{"ha":1527,"x_min":116,"x_max":1442,"o":"m 863 0 l 729 0 l 254 764 l 250 762 l 250 0 l 116 0 l 116 987 l 250 987 l 725 225 l 729 227 l 729 987 l 863 987 l 863 0 m 973 772 q 1037 936 973 873 q 1207 1000 1101 1000 q 1378 936 1314 1000 q 1442 772 1442 873 l 1442 693 q 1378 529 1442 592 q 1208 467 1315 467 q 1037 529 1101 467 q 973 693 973 592 l 973 772 m 1090 693 q 1120 598 1090 633 q 1208 562 1150 562 q 1295 598 1265 562 q 1325 693 1325 634 l 1325 772 q 1295 866 1325 829 q 1207 902 1264 902 q 1120 866 1150 902 q 1090 772 1090 829 l 1090 693 m 1364 206 l 1031 206 l 1031 311 l 1364 311 l 1364 206 z "},"™":{"ha":871,"x_min":70,"x_max":760,"o":"m 696 856 l 692 857 l 591 623 l 556 623 l 450 867 l 446 865 l 446 623 l 383 623 l 383 987 l 462 987 l 571 723 l 575 723 l 685 987 l 760 987 l 760 623 l 696 623 l 696 856 m 330 932 l 232 932 l 232 623 l 168 623 l 168 932 l 70 932 l 70 987 l 330 987 l 330 932 z "},"℮":{"ha":884,"x_min":103,"x_max":794,"o":"m 709 64 q 587 6 650 26 q 458 -14 523 -14 q 207 98 311 -14 q 103 367 103 210 q 213 634 103 520 q 458 747 322 747 q 696 642 599 747 q 794 380 794 537 l 794 349 l 273 349 l 273 126 q 358 71 311 90 q 458 52 406 52 q 586 72 523 52 q 708 134 649 92 l 709 64 m 458 684 q 360 662 408 684 q 273 601 313 640 l 273 413 l 640 413 l 640 607 q 556 663 603 642 q 458 684 509 684 z "},"⅛":{"ha":1171,"x_min":73,"x_max":1097,"o":"m 277 438 l 159 438 l 159 882 l 73 882 l 73 973 l 277 989 l 277 438 m 292 75 l 218 120 l 700 892 l 774 846 l 292 75 m 1081 401 q 1056 331 1081 361 q 989 281 1031 300 q 1068 229 1039 262 q 1097 153 1097 195 q 1035 35 1097 77 q 877 -7 972 -7 q 713 35 778 -7 q 648 153 648 77 q 678 229 648 195 q 760 282 708 263 q 690 331 715 300 q 665 401 665 361 q 725 512 665 473 q 876 551 785 551 q 1023 512 964 551 q 1081 401 1081 473 m 982 157 q 951 215 982 192 q 876 237 920 237 q 796 215 829 237 q 764 157 764 193 q 796 101 764 122 q 877 81 828 81 q 951 101 921 81 q 982 157 982 122 m 964 395 q 939 444 964 426 q 876 462 913 462 q 808 444 835 462 q 781 395 781 427 q 809 345 781 364 q 877 326 836 326 q 939 345 913 326 q 964 395 964 364 z "},"⅜":{"ha":1274,"x_min":75,"x_max":1200,"o":"m 290 767 q 358 786 336 767 q 380 841 380 806 q 355 890 380 870 q 283 909 330 909 q 222 893 245 909 q 199 850 199 877 l 89 850 l 87 854 q 141 958 83 918 q 283 998 198 998 q 439 958 382 998 q 496 843 496 918 q 472 775 496 806 q 406 726 448 744 q 479 678 453 711 q 505 600 505 646 q 444 482 505 524 q 283 440 382 440 q 136 479 197 440 q 79 594 75 519 l 79 598 l 190 598 q 215 548 190 567 q 283 528 241 528 q 361 548 332 528 q 389 601 389 568 q 365 662 389 643 q 290 682 340 682 l 201 682 l 201 767 l 290 767 m 417 75 l 343 120 l 825 892 l 899 846 l 417 75 m 1183 401 q 1159 331 1183 361 q 1092 281 1134 300 q 1171 229 1141 262 q 1200 153 1200 195 q 1137 35 1200 77 q 979 -7 1074 -7 q 815 35 880 -7 q 750 153 750 77 q 780 229 750 195 q 862 282 810 263 q 792 331 817 300 q 767 401 767 361 q 827 512 767 473 q 979 551 887 551 q 1125 512 1067 551 q 1183 401 1183 473 m 1084 157 q 1053 215 1084 192 q 979 237 1022 237 q 899 215 931 237 q 866 157 866 193 q 898 101 866 122 q 979 81 930 81 q 1054 101 1023 81 q 1084 157 1084 122 m 1067 395 q 1041 444 1067 426 q 979 462 1016 462 q 910 444 937 462 q 884 395 884 427 q 911 345 884 364 q 979 326 939 326 q 1041 345 1015 326 q 1067 395 1067 364 z "},"⅝":{"ha":1306,"x_min":76,"x_max":1232,"o":"m 98 678 l 133 987 l 483 987 l 483 897 l 237 897 l 219 771 q 266 791 239 783 q 321 800 293 800 q 462 752 411 802 q 513 616 513 703 q 459 485 513 534 q 292 436 405 436 q 138 473 199 436 q 80 583 76 511 l 81 587 l 191 593 q 218 543 191 561 q 292 524 246 524 q 371 548 345 524 q 397 616 397 571 q 371 688 397 661 q 299 715 345 715 q 232 704 255 715 q 199 672 210 692 l 98 678 m 455 75 l 381 120 l 863 892 l 937 846 l 455 75 m 1216 401 q 1191 331 1216 361 q 1124 281 1166 300 q 1203 229 1174 262 q 1232 153 1232 195 q 1170 35 1232 77 q 1012 -7 1107 -7 q 848 35 913 -7 q 783 153 783 77 q 813 229 783 195 q 895 282 843 263 q 825 331 850 300 q 800 401 800 361 q 860 512 800 473 q 1011 551 920 551 q 1158 512 1099 551 q 1216 401 1216 473 m 1117 157 q 1086 215 1117 192 q 1011 237 1055 237 q 931 215 964 237 q 899 157 899 193 q 931 101 899 122 q 1012 81 963 81 q 1086 101 1056 81 q 1117 157 1117 122 m 1099 395 q 1074 444 1099 426 q 1011 462 1048 462 q 943 444 970 462 q 916 395 916 427 q 944 345 916 364 q 1012 326 971 326 q 1074 345 1048 326 q 1099 395 1099 364 z "},"⅞":{"ha":1193,"x_min":73,"x_max":1119,"o":"m 472 898 q 343 722 381 791 q 306 530 306 653 l 306 444 l 189 444 l 189 530 q 242 747 189 652 q 355 898 294 842 l 73 898 l 73 987 l 472 987 l 472 898 m 324 75 l 250 120 l 732 892 l 806 846 l 324 75 m 1103 401 q 1078 331 1103 361 q 1011 281 1053 300 q 1090 229 1061 262 q 1119 153 1119 195 q 1056 35 1119 77 q 899 -7 994 -7 q 734 35 800 -7 q 669 153 669 77 q 700 229 669 195 q 781 282 730 263 q 711 331 736 300 q 686 401 686 361 q 746 512 686 473 q 898 551 806 551 q 1044 512 986 551 q 1103 401 1103 473 m 1004 157 q 972 215 1004 192 q 898 237 941 237 q 818 215 850 237 q 785 157 785 193 q 818 101 785 122 q 899 81 850 81 q 973 101 943 81 q 1004 157 1004 122 m 986 395 q 961 444 986 426 q 898 462 935 462 q 830 444 857 462 q 803 395 803 427 q 830 345 803 364 q 899 326 858 326 q 960 345 935 326 q 986 395 986 364 z "},"∂":{"ha":804,"x_min":49,"x_max":727,"o":"m 330 1029 q 618 849 509 986 q 727 512 727 712 l 727 363 q 629 91 727 197 q 386 -14 532 -14 q 143 81 238 -14 q 49 316 49 176 q 134 569 49 474 q 366 663 218 663 q 488 643 431 663 q 581 588 545 623 l 583 591 q 489 802 568 722 q 290 918 409 882 l 330 1029 m 389 90 q 536 168 479 90 q 593 363 593 245 l 593 450 q 511 527 569 496 q 366 558 452 558 q 228 490 274 558 q 182 316 182 422 q 238 158 182 227 q 389 90 294 90 z "},"∏":{"ha":974,"x_min":114,"x_max":860,"o":"m 860 -143 l 726 -143 l 726 882 l 248 882 l 248 -143 l 114 -143 l 114 987 l 860 987 l 860 -143 z "},"∑":{"ha":815,"x_min":47,"x_max":812,"o":"m 589 372 l 211 -74 l 212 -77 l 812 -77 l 812 -182 l 47 -182 l 47 -83 l 461 400 l 47 888 l 47 987 l 759 987 l 759 882 l 211 882 l 210 879 l 589 428 l 589 372 z "},"−":{"ha":793,"x_min":114,"x_max":680,"o":"m 680 439 l 114 439 l 114 545 l 680 545 l 680 439 z "},"√":{"ha":840,"x_min":43,"x_max":812,"o":"m 376 231 l 388 174 l 392 174 l 405 231 l 675 987 l 812 987 l 439 0 l 338 0 l 170 426 l 43 426 l 43 532 l 265 532 l 376 231 z "},"∞":{"ha":1421,"x_min":71,"x_max":1342,"o":"m 1342 344 q 1257 89 1342 191 q 1034 -14 1173 -14 q 841 59 922 -14 q 706 237 760 132 q 571 59 652 132 q 379 -14 490 -14 q 155 89 239 -14 q 71 344 71 191 l 71 389 q 155 644 71 541 q 378 747 239 747 q 570 674 489 747 q 707 496 652 601 q 842 674 760 600 q 1035 747 923 747 q 1257 644 1173 747 q 1342 389 1342 540 l 1342 344 m 204 344 q 249 159 204 229 q 379 90 293 90 q 544 185 472 90 q 635 353 616 279 l 635 381 q 544 548 616 453 q 378 642 471 642 q 248 572 292 642 q 204 389 204 502 l 204 344 m 1208 389 q 1164 572 1208 502 q 1035 642 1119 642 q 869 548 941 642 q 777 381 798 454 l 777 353 q 869 184 797 278 q 1034 90 941 90 q 1164 160 1119 90 q 1208 344 1208 229 l 1208 389 z "},"∫":{"ha":356,"x_min":-46,"x_max":447,"o":"m 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 827 q 178 1008 116 944 q 349 1072 239 1072 q 395 1068 372 1072 q 447 1058 418 1065 l 431 960 q 402 965 417 963 q 370 967 387 967 q 280 930 311 967 q 250 827 250 893 l 250 -60 z "},"≈":{"ha":783,"x_min":68,"x_max":708,"o":"m 75 593 q 149 665 107 639 q 235 692 191 692 q 304 684 282 692 q 394 642 326 676 q 475 604 452 612 q 542 595 497 595 q 627 622 585 595 q 702 694 669 648 l 708 576 q 633 504 675 530 q 548 477 591 477 q 481 486 503 477 q 400 524 458 494 q 310 566 332 558 q 241 574 288 574 q 155 547 197 574 q 81 475 113 521 l 75 593 m 68 303 q 142 376 100 349 q 229 402 184 402 q 297 395 275 403 q 387 353 319 387 q 469 313 448 321 q 535 305 491 305 q 621 331 578 305 q 695 405 663 357 l 701 286 q 627 213 669 239 q 541 187 585 187 q 474 196 496 187 q 393 234 452 204 q 301 277 322 269 q 235 284 281 285 q 149 258 191 284 q 75 184 106 231 l 68 303 z "},"≠":{"ha":727,"x_min":103,"x_max":669,"o":"m 538 669 l 669 669 l 669 558 l 476 558 l 381 387 l 669 387 l 669 276 l 319 276 l 229 113 l 170 154 l 238 276 l 103 276 l 103 387 l 300 387 l 395 558 l 103 558 l 103 669 l 456 669 l 556 848 l 615 808 l 538 669 z "},"≤":{"ha":732,"x_min":107,"x_max":677,"o":"m 677 5 l 111 5 l 111 110 l 677 110 l 677 5 m 281 469 l 224 458 l 224 454 l 281 442 l 661 303 l 661 181 l 107 412 l 107 503 l 661 734 l 661 611 l 281 469 z "},"≥":{"ha":738,"x_min":104,"x_max":685,"o":"m 677 3 l 111 3 l 111 109 l 677 109 l 677 3 m 104 615 l 104 734 l 685 503 l 685 412 l 104 181 l 104 300 l 510 445 l 568 456 l 568 460 l 510 472 l 104 615 z "},"◊":{"ha":700,"x_min":28,"x_max":672,"o":"m 298 987 l 399 987 l 672 493 l 401 0 l 300 0 l 28 493 l 298 987 m 536 493 l 361 840 l 350 874 l 346 874 l 334 840 l 164 493 l 338 146 l 350 113 l 354 113 l 366 146 l 536 493 z "},"":{"ha":353,"x_min":68,"x_max":217,"o":"m 217 70 l 134 -91 l 68 -91 l 110 76 l 110 165 l 217 165 l 217 70 z "},"fi":{"ha":773,"x_min":19,"x_max":665,"o":"m 134 0 l 134 635 l 19 635 l 19 734 l 134 734 l 134 813 q 207 1004 134 937 q 407 1072 279 1072 q 496 1062 452 1072 q 600 1031 541 1051 l 577 922 q 500 947 541 937 q 417 957 460 957 q 303 922 338 957 q 268 813 268 886 l 268 734 l 414 734 l 414 635 l 268 635 l 268 0 l 134 0 m 665 0 l 531 0 l 531 734 l 665 734 l 665 0 z "},"fl":{"ha":829,"x_min":38,"x_max":721,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 721 0 l 587 0 l 587 1058 l 721 1058 l 721 0 z "},"ffi":{"ha":1253,"x_min":38,"x_max":1145,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 614 0 l 614 635 l 498 635 l 498 734 l 614 734 l 614 813 q 686 1004 614 937 q 886 1072 758 1072 q 976 1062 932 1072 q 1080 1031 1020 1051 l 1057 922 q 980 947 1021 937 q 897 957 939 957 q 782 922 817 957 q 747 813 747 886 l 747 734 l 893 734 l 893 635 l 747 635 l 747 0 l 614 0 m 1145 0 l 1010 0 l 1010 734 l 1145 734 l 1145 0 z "},"ffl":{"ha":1309,"x_min":38,"x_max":1200,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 632 0 l 632 635 l 517 635 l 517 734 l 632 734 l 632 827 q 693 1008 632 944 q 865 1072 755 1072 q 911 1068 888 1072 q 963 1058 935 1065 l 947 956 q 917 961 935 959 q 881 963 900 963 q 794 928 823 963 q 766 827 766 893 l 766 734 l 918 734 l 918 635 l 766 635 l 766 0 l 632 0 m 1200 0 l 1067 0 l 1067 1058 l 1200 1058 l 1200 0 z "},"":{"ha":0,"x_min":0,"x_max":0,"o":""},"":{"ha":1424,"x_min":62,"x_max":1377,"o":"m 559 317 q 516 210 559 251 q 404 169 473 169 q 290 210 334 169 q 247 317 247 251 l 247 393 q 290 500 247 458 q 403 542 334 542 q 516 500 472 542 q 559 393 559 458 l 559 317 m 605 171 l 605 543 l 732 543 q 839 518 801 543 q 876 441 876 492 q 861 394 876 415 q 819 363 846 373 q 870 330 852 354 q 888 276 888 307 q 853 198 888 224 q 755 171 817 171 l 605 171 m 497 393 q 472 466 497 439 q 403 493 446 493 q 334 466 359 493 q 309 393 309 439 l 309 317 q 334 244 309 271 q 404 218 359 218 q 472 244 447 218 q 497 317 497 271 l 497 393 m 1144 545 l 1206 545 l 1206 288 q 1171 203 1206 235 q 1080 172 1135 172 q 982 200 1018 172 q 951 279 947 227 l 952 283 l 1009 283 q 1027 237 1009 252 q 1080 222 1046 222 q 1125 240 1107 222 q 1144 288 1144 258 l 1144 545 m 62 -270 l 62 -56 l 138 -56 l 138 -193 l 271 -193 l 271 -270 l 62 -270 m 1166 -270 l 1166 -193 l 1301 -193 l 1301 -56 l 1377 -56 l 1377 -270 l 1166 -270 m 62 793 l 62 986 l 271 986 l 271 907 l 138 907 l 138 793 l 62 793 m 1166 907 l 1166 986 l 1377 986 l 1377 793 l 1301 793 l 1301 907 l 1166 907 m 669 336 l 669 222 l 755 222 q 807 236 789 222 q 825 278 825 251 q 807 319 825 304 q 758 336 790 335 l 755 336 l 669 336 m 896 907 l 896 986 l 1084 986 l 1084 907 l 896 907 m 625 907 l 625 986 l 813 986 l 813 907 l 625 907 m 355 907 l 355 986 l 542 986 l 542 907 l 355 907 m 896 -270 l 896 -193 l 1084 -193 l 1084 -270 l 896 -270 m 625 -270 l 625 -193 l 813 -193 l 813 -270 l 625 -270 m 355 -270 l 355 -193 l 542 -193 l 542 -270 l 355 -270 m 669 384 l 732 384 q 792 397 772 384 q 812 438 812 411 q 793 475 812 465 q 732 486 774 486 l 669 486 l 669 384 m 138 282 l 62 282 l 62 453 l 138 453 l 138 282 m 138 538 l 62 538 l 62 708 l 138 708 l 138 538 m 138 28 l 62 28 l 62 197 l 138 197 l 138 28 m 1377 282 l 1301 282 l 1301 453 l 1377 453 l 1377 282 m 1377 538 l 1301 538 l 1301 708 l 1377 708 l 1377 538 m 1377 28 l 1301 28 l 1301 197 l 1377 197 l 1377 28 z "},"�":{"ha":1425,"x_min":62,"x_max":1361,"o":"m 711 1097 l 1361 436 l 711 -225 l 62 436 l 711 1097 m 767 273 q 776 329 767 309 q 812 368 785 350 q 892 438 861 394 q 924 536 924 483 q 867 674 924 623 q 710 725 810 725 q 561 681 618 725 q 507 549 505 637 l 509 545 l 640 545 q 661 597 641 579 q 710 614 681 614 q 767 593 747 614 q 787 536 787 572 q 769 476 787 502 q 724 431 752 450 q 650 363 670 393 q 630 273 630 332 l 767 273 m 767 211 l 630 211 l 630 96 l 767 96 l 767 211 m 716 -374 l 719 -374 l 719 -376 l 716 -376 l 716 -374 m 715 1455 l 718 1455 l 718 1453 l 715 1453 l 715 1455 z "}},"familyName":"Roboto","ascender":1455,"descender":-376,"underlinePosition":-150,"underlineThickness":100,"boundingBox":{"yMin":-555,"xMin":-980,"yMax":2167,"xMax":2396},"resolution":1000,"original_font_information":{"format":0,"copyright":"Font data copyright Google 2012","fontFamily":"Roboto","fontSubfamily":"Regular","uniqueID":"Google:Roboto Regular:2013","fullName":"Roboto Regular","version":"Version 1.100141; 2013","postScriptName":"Roboto-Regular","trademark":"Roboto is a trademark of Google.","designer":"Google","manufacturerURL":"Google.com","designerURL":"Christian Robertson","licence":"Licensed under the Apache License, Version 2.0","licenceURL":"http://www.apache.org/licenses/LICENSE-2.0"},"cssFontWeight":"normal","cssFontStyle":"normal"} \ No newline at end of file diff --git a/activities/3DVolume.activity/js/loader.js b/activities/3DVolume.activity/js/loader.js new file mode 100644 index 000000000..01021e981 --- /dev/null +++ b/activities/3DVolume.activity/js/loader.js @@ -0,0 +1,8 @@ +requirejs.config({ + baseUrl: "lib", + paths: { + activity: "../js" + } +}); + +requirejs(["activity/activity"]); diff --git a/activities/3DVolume.activity/js/orbit.js b/activities/3DVolume.activity/js/orbit.js new file mode 100644 index 000000000..eec2346b2 --- /dev/null +++ b/activities/3DVolume.activity/js/orbit.js @@ -0,0 +1,1555 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) : + typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.OrbitControls = {}, global.THREE)); +})(this, (function (exports, three) { 'use strict'; + + // OrbitControls performs orbiting, dollying (zooming), and panning. + // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). + // + // Orbit - left mouse / touch: one-finger move + // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish + // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + + const _changeEvent = { type: 'change' }; + const _startEvent = { type: 'start' }; + const _endEvent = { type: 'end' }; + const _ray = new THREE.Ray(); + const _plane = new THREE.Plane(); + const TILT_LIMIT = Math.cos( 70 * THREE.MathUtils.DEG2RAD ); + + class OrbitControls extends THREE.EventDispatcher { + + constructor( object, domElement ) { + + super(); + + this.object = object; + this.domElement = domElement; + this.domElement.style.touchAction = 'none'; // disable touch scroll + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // Sets the 3D cursor (similar to Blender), from which the maxTargetRadius takes effect + this.cursor = new THREE.Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // Limit camera target within a spherical area around the cursor + this.minTargetRadius = 0; + this.maxTargetRadius = Infinity; + + // 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 + + // How far you can orbit horizontally, upper and lower limits. + // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.05; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + this.zoomToCursor = false; + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 + + // The four arrow keys + this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; + + // Mouse buttons + this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // the target DOM element for key events + this._domElementKeyEvents = null; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.getDistance = function () { + + return this.object.position.distanceTo( this.target ); + + }; + + this.listenToKeyEvents = function ( domElement ) { + + domElement.addEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = domElement; + + }; + + this.stopListenToKeyEvents = function () { + + this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = null; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( _changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + const offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + const quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + const quatInverse = quat.clone().invert(); + + const lastPosition = new THREE.Vector3(); + const lastQuaternion = new THREE.Quaternion(); + const lastTargetPosition = new THREE.Vector3(); + + const twoPI = 2 * Math.PI; + + return function update( deltaTime = null ) { + + const position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle( deltaTime ) ); + + } + + if ( scope.enableDamping ) { + + spherical.theta += sphericalDelta.theta * scope.dampingFactor; + spherical.phi += sphericalDelta.phi * scope.dampingFactor; + + } else { + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + } + + // restrict theta to be between desired limits + + let min = scope.minAzimuthAngle; + let max = scope.maxAzimuthAngle; + + if ( isFinite( min ) && isFinite( max ) ) { + + if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI; + + if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI; + + if ( min <= max ) { + + spherical.theta = Math.max( min, Math.min( max, spherical.theta ) ); + + } else { + + spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ? + Math.max( min, spherical.theta ) : + Math.min( max, spherical.theta ); + + } + + } + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + // move target to panned location + + if ( scope.enableDamping === true ) { + + scope.target.addScaledVector( panOffset, scope.dampingFactor ); + + } else { + + scope.target.add( panOffset ); + + } + + // Limit the target distance from the cursor to create a sphere around the center of interest + scope.target.sub( scope.cursor ); + scope.target.clampLength( scope.minTargetRadius, scope.maxTargetRadius ); + scope.target.add( scope.cursor ); + + let zoomChanged = false; + // adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera + // we adjust zoom later in these cases + if ( scope.zoomToCursor && performCursorZoom || scope.object.isOrthographicCamera ) { + + spherical.radius = clampDistance( spherical.radius ); + + } else { + + const prevRadius = spherical.radius; + spherical.radius = clampDistance( spherical.radius * scale ); + zoomChanged = prevRadius != spherical.radius; + + } + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + // adjust camera position + if ( scope.zoomToCursor && performCursorZoom ) { + + let newRadius = null; + if ( scope.object.isPerspectiveCamera ) { + + // move the camera down the pointer ray + // this method avoids floating point error + const prevRadius = offset.length(); + newRadius = clampDistance( prevRadius * scale ); + + const radiusDelta = prevRadius - newRadius; + scope.object.position.addScaledVector( dollyDirection, radiusDelta ); + scope.object.updateMatrixWorld(); + + zoomChanged = !! radiusDelta; + + } else if ( scope.object.isOrthographicCamera ) { + + // adjust the ortho camera position based on zoom changes + const mouseBefore = new THREE.Vector3( mouse.x, mouse.y, 0 ); + mouseBefore.unproject( scope.object ); + + const prevZoom = scope.object.zoom; + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) ); + scope.object.updateProjectionMatrix(); + + zoomChanged = prevZoom !== scope.object.zoom; + + const mouseAfter = new THREE.Vector3( mouse.x, mouse.y, 0 ); + mouseAfter.unproject( scope.object ); + + scope.object.position.sub( mouseAfter ).add( mouseBefore ); + scope.object.updateMatrixWorld(); + + newRadius = offset.length(); + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' ); + scope.zoomToCursor = false; + + } + + // handle the placement of the target + if ( newRadius !== null ) { + + if ( this.screenSpacePanning ) { + + // position the orbit target in front of the new camera position + scope.target.set( 0, 0, - 1 ) + .transformDirection( scope.object.matrix ) + .multiplyScalar( newRadius ) + .add( scope.object.position ); + + } else { + + // get the ray and translation plane to compute target + _ray.origin.copy( scope.object.position ); + _ray.direction.set( 0, 0, - 1 ).transformDirection( scope.object.matrix ); + + // if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid + // extremely large values + if ( Math.abs( scope.object.up.dot( _ray.direction ) ) < TILT_LIMIT ) { + + object.lookAt( scope.target ); + + } else { + + _plane.setFromNormalAndCoplanarPoint( scope.object.up, scope.target ); + _ray.intersectPlane( _plane, scope.target ); + + } + + } + + } + + } else if ( scope.object.isOrthographicCamera ) { + + const prevZoom = scope.object.zoom; + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) ); + + if ( prevZoom !== scope.object.zoom ) { + + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } + + } + + scale = 1; + performCursorZoom = false; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS || + lastTargetPosition.distanceToSquared( scope.target ) > EPS ) { + + scope.dispatchEvent( _changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + lastTargetPosition.copy( scope.target ); + + return true; + + } + + return false; + + }; + + }(); + + this.rotateRight = function () { + const element = scope.domElement; + rotateLeft(-0.3) + scope.update(); + }; + + this.rotateLeft = function () { + const element = scope.domElement; + rotateLeft(0.3) + scope.update(); + }; + + this.rotateUp = function () { + const element = scope.domElement; + rotateUp(0.3) + scope.update(); + }; + + this.rotateDown = function () { + const element = scope.domElement; + rotateUp(-0.3) + scope.update(); + }; + + this.dollyOut = function () { + dollyOut( getZoomScale() ); + scope.update(); + }; + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.removeEventListener( 'pointerdown', onPointerDown ); + scope.domElement.removeEventListener( 'pointercancel', onPointerUp ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + const document = scope.domElement.getRootNode(); // offscreen canvas compatibility + + document.removeEventListener( 'keydown', interceptControlDown, { capture: true } ); + + if ( scope._domElementKeyEvents !== null ) { + + scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + scope._domElementKeyEvents = null; + + } + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + const scope = this; + + const STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; + + let state = STATE.NONE; + + const EPS = 0.000001; + + // current position in spherical coordinates + const spherical = new THREE.Spherical(); + const sphericalDelta = new THREE.Spherical(); + + let scale = 1; + const panOffset = new THREE.Vector3(); + + const rotateStart = new THREE.Vector2(); + const rotateEnd = new THREE.Vector2(); + const rotateDelta = new THREE.Vector2(); + + const panStart = new THREE.Vector2(); + const panEnd = new THREE.Vector2(); + const panDelta = new THREE.Vector2(); + + const dollyStart = new THREE.Vector2(); + const dollyEnd = new THREE.Vector2(); + const dollyDelta = new THREE.Vector2(); + + const dollyDirection = new THREE.Vector3(); + const mouse = new THREE.Vector2(); + let performCursorZoom = false; + + const pointers = []; + const pointerPositions = {}; + + let controlActive = false; + + function getAutoRotationAngle( deltaTime ) { + + if ( deltaTime !== null ) { + + return ( 2 * Math.PI / 60 * scope.autoRotateSpeed ) * deltaTime; + + } else { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + } + + function getZoomScale( delta ) { + + const normalizedDelta = Math.abs( delta * 0.01 ); + return Math.pow( 0.95, scope.zoomSpeed * normalizedDelta ); + + } + + function rotateLeft( angle ) { + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + const panLeft = function () { + + const v = new THREE.Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + const panUp = function () { + + const v = new THREE.Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + const pan = function () { + + const offset = new THREE.Vector3(); + + return function pan( deltaX, deltaY ) { + + const element = scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + const position = scope.object.position; + offset.copy( position ).sub( scope.target ); + let 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 use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) { + + scale /= dollyScale; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) { + + scale *= dollyScale; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function updateZoomParameters( x, y ) { + + if ( ! scope.zoomToCursor ) { + + return; + + } + + performCursorZoom = true; + + const rect = scope.domElement.getBoundingClientRect(); + const dx = x - rect.left; + const dy = y - rect.top; + const w = rect.width; + const h = rect.height; + + mouse.x = ( dx / w ) * 2 - 1; + mouse.y = - ( dy / h ) * 2 + 1; + + dollyDirection.set( mouse.x, mouse.y, 1 ).unproject( scope.object ).sub( scope.object.position ).normalize(); + + } + + function clampDistance( dist ) { + + return Math.max( scope.minDistance, Math.min( scope.maxDistance, dist ) ); + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + updateZoomParameters( event.clientX, event.clientX ); + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyOut( getZoomScale( dollyDelta.y ) ); + + } else if ( dollyDelta.y < 0 ) { + + dollyIn( getZoomScale( dollyDelta.y ) ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseWheel( event ) { + + updateZoomParameters( event.clientX, event.clientY ); + + if ( event.deltaY < 0 ) { + + dollyIn( getZoomScale( event.deltaY ) ); + + } else if ( event.deltaY > 0 ) { + + dollyOut( getZoomScale( event.deltaY ) ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + let needsUpdate = false; + + switch ( event.code ) { + + case scope.keys.UP: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, - scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.LEFT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + case scope.keys.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( - scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate( event ) { + + if ( pointers.length === 1 ) { + + rotateStart.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + rotateStart.set( x, y ); + + } + + } + + function handleTouchStartPan( event ) { + + if ( pointers.length === 1 ) { + + panStart.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + panStart.set( x, y ); + + } + + } + + function handleTouchStartDolly( event ) { + + const position = getSecondPointerPosition( event ); + + const dx = event.pageX - position.x; + const dy = event.pageY - position.y; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + function handleTouchStartDollyPan( event ) { + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enablePan ) handleTouchStartPan( event ); + + } + + function handleTouchStartDollyRotate( event ) { + + if ( scope.enableZoom ) handleTouchStartDolly( event ); + + if ( scope.enableRotate ) handleTouchStartRotate( event ); + + } + + function handleTouchMoveRotate( event ) { + + if ( pointers.length == 1 ) { + + rotateEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + rotateEnd.set( x, y ); + + } + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + } + + function handleTouchMovePan( event ) { + + if ( pointers.length === 1 ) { + + panEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + function handleTouchMoveDolly( event ) { + + const position = getSecondPointerPosition( event ); + + const dx = event.pageX - position.x; + const dy = event.pageY - position.y; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyOut( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + const centerX = ( event.pageX + position.x ) * 0.5; + const centerY = ( event.pageY + position.y ) * 0.5; + + updateZoomParameters( centerX, centerY ); + + } + + function handleTouchMoveDollyPan( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enablePan ) handleTouchMovePan( event ); + + } + + function handleTouchMoveDollyRotate( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enableRotate ) handleTouchMoveRotate( event ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onPointerDown( event ) { + + if ( scope.enabled === false ) return; + + if ( pointers.length === 0 ) { + + scope.domElement.setPointerCapture( event.pointerId ); + + scope.domElement.addEventListener( 'pointermove', onPointerMove ); + scope.domElement.addEventListener( 'pointerup', onPointerUp ); + + } + + // + + if ( isTrackingPointer( event ) ) return; + + // + + addPointer( event ); + + if ( event.pointerType === 'touch' ) { + + onTouchStart( event ); + + } else { + + onMouseDown( event ); + + } + + } + + function onPointerMove( event ) { + + if ( scope.enabled === false ) return; + + if ( event.pointerType === 'touch' ) { + + onTouchMove( event ); + + } else { + + onMouseMove( event ); + + } + + } + + function onPointerUp( event ) { + + removePointer( event ); + + switch ( pointers.length ) { + + case 0: + + scope.domElement.releasePointerCapture( event.pointerId ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + scope.dispatchEvent( _endEvent ); + + state = STATE.NONE; + + break; + + case 1: + + const pointerId = pointers[ 0 ]; + const position = pointerPositions[ pointerId ]; + + // minimal placeholder event - allows state correction on pointer-up + onTouchStart( { pointerId: pointerId, pageX: position.x, pageY: position.y } ); + + break; + + } + + } + + function onMouseDown( event ) { + + let mouseAction; + + switch ( event.button ) { + + case 0: + + mouseAction = scope.mouseButtons.LEFT; + break; + + case 1: + + mouseAction = scope.mouseButtons.MIDDLE; + break; + + case 2: + + mouseAction = scope.mouseButtons.RIGHT; + break; + + default: + + mouseAction = - 1; + + } + + switch ( mouseAction ) { + + case THREE.MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case THREE.MOUSE.ROTATE: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } else { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } + + break; + + case THREE.MOUSE.PAN: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onMouseMove( event ) { + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; + + event.preventDefault(); + + scope.dispatchEvent( _startEvent ); + + handleMouseWheel( customWheelEvent( event ) ); + + scope.dispatchEvent( _endEvent ); + + } + + function customWheelEvent( event ) { + + const mode = event.deltaMode; + + // minimal wheel event altered to meet delta-zoom demand + const newEvent = { + clientX: event.clientX, + clientY: event.clientY, + deltaY: event.deltaY, + }; + + switch ( mode ) { + + case 1: // LINE_MODE + newEvent.deltaY *= 16; + break; + + case 2: // PAGE_MODE + newEvent.deltaY *= 100; + break; + + } + + // detect if event was triggered by pinching + if ( event.ctrlKey && ! controlActive ) { + + newEvent.deltaY *= 10; + + } + + return newEvent; + + } + + function interceptControlDown( event ) { + + if ( event.key === 'Control' ) { + + controlActive = true; + + + const document = scope.domElement.getRootNode(); // offscreen canvas compatibility + + document.addEventListener( 'keyup', interceptControlUp, { passive: true, capture: true } ); + + } + + } + + function interceptControlUp( event ) { + + if ( event.key === 'Control' ) { + + controlActive = false; + + + const document = scope.domElement.getRootNode(); // offscreen canvas compatibility + + document.removeEventListener( 'keyup', interceptControlUp, { passive: true, capture: true } ); + + } + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + trackPointer( event ); + + switch ( pointers.length ) { + + case 1: + + switch ( scope.touches.ONE ) { + + case THREE.TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case THREE.TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan( event ); + + state = STATE.TOUCH_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + case 2: + + switch ( scope.touches.TWO ) { + + case THREE.TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case THREE.TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchStartDollyRotate( event ); + + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onTouchMove( event ) { + + trackPointer( event ); + + switch ( state ) { + + case STATE.TOUCH_ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchMoveRotate( event ); + + scope.update(); + + break; + + case STATE.TOUCH_PAN: + + if ( scope.enablePan === false ) return; + + handleTouchMovePan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchMoveDollyPan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchMoveDollyRotate( event ); + + scope.update(); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + function addPointer( event ) { + + pointers.push( event.pointerId ); + + } + + function removePointer( event ) { + + delete pointerPositions[ event.pointerId ]; + + for ( let i = 0; i < pointers.length; i ++ ) { + + if ( pointers[ i ] == event.pointerId ) { + + pointers.splice( i, 1 ); + return; + + } + + } + + } + + function isTrackingPointer( event ) { + + for ( let i = 0; i < pointers.length; i ++ ) { + + if ( pointers[ i ] == event.pointerId ) return true; + + } + + return false; + + } + + function trackPointer( event ) { + + let position = pointerPositions[ event.pointerId ]; + + if ( position === undefined ) { + + position = new THREE.Vector2(); + pointerPositions[ event.pointerId ] = position; + + } + + position.set( event.pageX, event.pageY ); + + } + + function getSecondPointerPosition( event ) { + + const pointerId = ( event.pointerId === pointers[ 0 ] ) ? pointers[ 1 ] : pointers[ 0 ]; + + return pointerPositions[ pointerId ]; + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.addEventListener( 'pointerdown', onPointerDown ); + scope.domElement.addEventListener( 'pointercancel', onPointerUp ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + const document = scope.domElement.getRootNode(); // offscreen canvas compatibility + + document.addEventListener( 'keydown', interceptControlDown, { passive: true, capture: true } ); + + // force an update at start + + this.update(); + + } + + } + + exports.OrbitControls = OrbitControls; + +})); diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.html b/activities/3DVolume.activity/js/palettes/bgpalette.html new file mode 100644 index 000000000..3fc9c8f41 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/bgpalette.html @@ -0,0 +1,18 @@ +
+

Select Background

+
+
+ + + +
+
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.js b/activities/3DVolume.activity/js/palettes/bgpalette.js new file mode 100644 index 000000000..fc119c509 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/bgpalette.js @@ -0,0 +1,66 @@ +define([ + "sugar-web/graphics/palette", + "text!activity/palettes/bgpalette.html", + "sugar-web/graphics/presencepalette", +], function (palette, template, presencepalette) { + var bgpalette = {}; + bgpalette.BgPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "bg-palette"; + + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; + + this.setContent([containerElem]); + + this.bgSelectedEvent = document.createEvent("CustomEvent"); + this.bgSelectedEvent.initCustomEvent("bg-selected", true, true, { bg: "" }); + + let that = this; + let backgroundChangeCallback = null; + + this.setBackgroundChangeCallback = function (callback) { + backgroundChangeCallback = callback; + }; + + document.getElementById("green-board").addEventListener("click", onClick); + document.getElementById("default").addEventListener("click", onClick); + document.getElementById("wood").addEventListener("click", onClick); + + function onClick(event) { + that.bgSelectedEvent.bg = event.currentTarget.id; + if (document.querySelector("#green-board").classList.contains("active")) { + document.querySelector("#green-board").classList.remove("active"); + } + if (document.querySelector("#wood").classList.contains("active")) { + document.querySelector("#wood").classList.remove("active"); + } + if (document.querySelector("#default").classList.contains("active")) { + document.querySelector("#default").classList.remove("active"); + } + event.currentTarget.classList.add("active"); + + if (backgroundChangeCallback) { + backgroundChangeCallback(that.bgSelectedEvent.bg); + } + + that.popDown(); + } + }; + + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + bgpalette.BgPalette.prototype = Object.create(palette.Palette.prototype, { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + }); + + return bgpalette; +}); diff --git a/activities/3DVolume.activity/js/palettes/colorpalette.html b/activities/3DVolume.activity/js/palettes/colorpalette.html new file mode 100644 index 000000000..93e4e1f56 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/colorpalette.html @@ -0,0 +1,11 @@ +
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/colorpalette.js b/activities/3DVolume.activity/js/palettes/colorpalette.js new file mode 100644 index 000000000..f50203215 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/colorpalette.js @@ -0,0 +1,48 @@ +define([ + "sugar-web/graphics/palette", + "text!activity/palettes/colorpalette.html", +], function (palette, template) { + var colorpalette = {}; + colorpalette.ColorPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "color-palette"; + + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; + + this.setContent([containerElem]); + + let that = this; + + const colors = document.querySelectorAll(".color"); + colors.forEach((color) => { + color.addEventListener("click", function () { + const selectedColor = this.style.backgroundColor; + const colorChangeEvent = new CustomEvent("color-selected", { + detail: { color: selectedColor }, + }); + document.dispatchEvent(colorChangeEvent); + that.popDown(); + }); + }); + }; + + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + colorpalette.ColorPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); + + return colorpalette; +}); diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html new file mode 100644 index 000000000..19fcfeeee --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.js b/activities/3DVolume.activity/js/palettes/volumepalette.js new file mode 100644 index 000000000..4dcf35854 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/volumepalette.js @@ -0,0 +1,35 @@ +define([ + 'sugar-web/graphics/palette', + 'text!activity/palettes/volumepalette.html', +], function (palette, template) { + var volumepalette = {} + volumepalette.VolumePalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText) + this.getPalette().id = 'volume-palette' + + var containerElem = document.createElement('div') + containerElem.innerHTML = template + + this.setContent([containerElem]) + + + } + + var addEventListener = function (type, listener, useCapture) { + return this.getPalette().addEventListener(type, listener, useCapture) + } + + volumepalette.VolumePalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + }, + ) + + return volumepalette +}) diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.html b/activities/3DVolume.activity/js/palettes/zoompalette.html new file mode 100644 index 000000000..aa562e92e --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/zoompalette.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.js b/activities/3DVolume.activity/js/palettes/zoompalette.js new file mode 100644 index 000000000..58a6dd907 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/zoompalette.js @@ -0,0 +1,36 @@ +define([ + 'sugar-web/graphics/palette', + 'text!activity/palettes/zoompalette.html', + ], function (palette, template) { + var zoompalette = {} + zoompalette.ZoomPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText) + this.getPalette().id = 'zoom-palette' + + var containerElem = document.createElement('div') + containerElem.innerHTML = template + + this.setContent([containerElem]) + + + } + + var addEventListener = function (type, listener, useCapture) { + return this.getPalette().addEventListener(type, listener, useCapture) + } + + zoompalette.ZoomPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + }, + ) + + return zoompalette + }) + \ No newline at end of file diff --git a/activities/3DVolume.activity/js/three.js b/activities/3DVolume.activity/js/three.js new file mode 100644 index 000000000..804eded1f --- /dev/null +++ b/activities/3DVolume.activity/js/three.js @@ -0,0 +1,38464 @@ +// threejs.org/license +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.THREE = {})); +}(this, (function (exports) { 'use strict'; + + /** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + var runtime = function (exports) { + + var Op = Object.prototype; + var hasOwn = Op.hasOwnProperty; + var undefined$1; // More compressible than void 0. + + var $Symbol = typeof Symbol === "function" ? Symbol : {}; + var iteratorSymbol = $Symbol.iterator || "@@iterator"; + var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator"; + var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; + + function define(obj, key, value) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + return obj[key]; + } + + try { + // IE 8 has a broken Object.defineProperty that only works on DOM objects. + define({}, ""); + } catch (err) { + define = function define(obj, key, value) { + return obj[key] = value; + }; + } + + function wrap(innerFn, outerFn, self, tryLocsList) { + // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. + var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; + var generator = Object.create(protoGenerator.prototype); + var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next, + // .throw, and .return methods. + + generator._invoke = makeInvokeMethod(innerFn, self, context); + return generator; + } + + exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion + // record like context.tryEntries[i].completion. This interface could + // have been (and was previously) designed to take a closure to be + // invoked without arguments, but in all the cases we care about we + // already have an existing method we want to call, so there's no need + // to create a new function object. We can even get away with assuming + // the method takes exactly one argument, since that happens to be true + // in every case, so we don't have to touch the arguments object. The + // only additional allocation required is the completion record, which + // has a stable shape and so hopefully should be cheap to allocate. + + function tryCatch(fn, obj, arg) { + try { + return { + type: "normal", + arg: fn.call(obj, arg) + }; + } catch (err) { + return { + type: "throw", + arg: err + }; + } + } + + var GenStateSuspendedStart = "suspendedStart"; + var GenStateSuspendedYield = "suspendedYield"; + var GenStateExecuting = "executing"; + var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as + // breaking out of the dispatch switch statement. + + var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and + // .constructor.prototype properties for functions that return Generator + // objects. For full spec compliance, you may wish to configure your + // minifier not to mangle the names of these two functions. + + function Generator() {} + + function GeneratorFunction() {} + + function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that + // don't natively support it. + + + var IteratorPrototype = {}; + + IteratorPrototype[iteratorSymbol] = function () { + return this; + }; + + var getProto = Object.getPrototypeOf; + var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); + + if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { + // This environment has a native %IteratorPrototype%; use it instead + // of the polyfill. + IteratorPrototype = NativeIteratorPrototype; + } + + var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); + GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; + GeneratorFunctionPrototype.constructor = GeneratorFunction; + GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the + // Iterator interface in terms of a single ._invoke method. + + function defineIteratorMethods(prototype) { + ["next", "throw", "return"].forEach(function (method) { + define(prototype, method, function (arg) { + return this._invoke(method, arg); + }); + }); + } + + exports.isGeneratorFunction = function (genFun) { + var ctor = typeof genFun === "function" && genFun.constructor; + return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can + // do is to check its .name property. + (ctor.displayName || ctor.name) === "GeneratorFunction" : false; + }; + + exports.mark = function (genFun) { + if (Object.setPrototypeOf) { + Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); + } else { + genFun.__proto__ = GeneratorFunctionPrototype; + define(genFun, toStringTagSymbol, "GeneratorFunction"); + } + + genFun.prototype = Object.create(Gp); + return genFun; + }; // Within the body of any async function, `await x` is transformed to + // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test + // `hasOwn.call(value, "__await")` to determine if the yielded value is + // meant to be awaited. + + + exports.awrap = function (arg) { + return { + __await: arg + }; + }; + + function AsyncIterator(generator, PromiseImpl) { + function invoke(method, arg, resolve, reject) { + var record = tryCatch(generator[method], generator, arg); + + if (record.type === "throw") { + reject(record.arg); + } else { + var result = record.arg; + var value = result.value; + + if (value && typeof value === "object" && hasOwn.call(value, "__await")) { + return PromiseImpl.resolve(value.__await).then(function (value) { + invoke("next", value, resolve, reject); + }, function (err) { + invoke("throw", err, resolve, reject); + }); + } + + return PromiseImpl.resolve(value).then(function (unwrapped) { + // When a yielded Promise is resolved, its final value becomes + // the .value of the Promise<{value,done}> result for the + // current iteration. + result.value = unwrapped; + resolve(result); + }, function (error) { + // If a rejected Promise was yielded, throw the rejection back + // into the async generator function so it can be handled there. + return invoke("throw", error, resolve, reject); + }); + } + } + + var previousPromise; + + function enqueue(method, arg) { + function callInvokeWithMethodAndArg() { + return new PromiseImpl(function (resolve, reject) { + invoke(method, arg, resolve, reject); + }); + } + + return previousPromise = // If enqueue has been called before, then we want to wait until + // all previous Promises have been resolved before calling invoke, + // so that results are always delivered in the correct order. If + // enqueue has not been called before, then it is important to + // call invoke immediately, without waiting on a callback to fire, + // so that the async generator function has the opportunity to do + // any necessary setup in a predictable way. This predictability + // is why the Promise constructor synchronously invokes its + // executor callback, and why async functions synchronously + // execute code before the first await. Since we implement simple + // async functions in terms of async generators, it is especially + // important to get this right, even though it requires care. + previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later + // invocations of the iterator. + callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); + } // Define the unified helper method that is used to implement .next, + // .throw, and .return (see defineIteratorMethods). + + + this._invoke = enqueue; + } + + defineIteratorMethods(AsyncIterator.prototype); + + AsyncIterator.prototype[asyncIteratorSymbol] = function () { + return this; + }; + + exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of + // AsyncIterator objects; they just return a Promise for the value of + // the final result produced by the iterator. + + exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { + if (PromiseImpl === void 0) PromiseImpl = Promise; + var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); + return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator. + : iter.next().then(function (result) { + return result.done ? result.value : iter.next(); + }); + }; + + function makeInvokeMethod(innerFn, self, context) { + var state = GenStateSuspendedStart; + return function invoke(method, arg) { + if (state === GenStateExecuting) { + throw new Error("Generator is already running"); + } + + if (state === GenStateCompleted) { + if (method === "throw") { + throw arg; + } // Be forgiving, per 25.3.3.3.3 of the spec: + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume + + + return doneResult(); + } + + context.method = method; + context.arg = arg; + + while (true) { + var delegate = context.delegate; + + if (delegate) { + var delegateResult = maybeInvokeDelegate(delegate, context); + + if (delegateResult) { + if (delegateResult === ContinueSentinel) continue; + return delegateResult; + } + } + + if (context.method === "next") { + // Setting context._sent for legacy support of Babel's + // function.sent implementation. + context.sent = context._sent = context.arg; + } else if (context.method === "throw") { + if (state === GenStateSuspendedStart) { + state = GenStateCompleted; + throw context.arg; + } + + context.dispatchException(context.arg); + } else if (context.method === "return") { + context.abrupt("return", context.arg); + } + + state = GenStateExecuting; + var record = tryCatch(innerFn, self, context); + + if (record.type === "normal") { + // If an exception is thrown from innerFn, we leave state === + // GenStateExecuting and loop back for another invocation. + state = context.done ? GenStateCompleted : GenStateSuspendedYield; + + if (record.arg === ContinueSentinel) { + continue; + } + + return { + value: record.arg, + done: context.done + }; + } else if (record.type === "throw") { + state = GenStateCompleted; // Dispatch the exception by looping back around to the + // context.dispatchException(context.arg) call above. + + context.method = "throw"; + context.arg = record.arg; + } + } + }; + } // Call delegate.iterator[context.method](context.arg) and handle the + // result, either by returning a { value, done } result from the + // delegate iterator, or by modifying context.method and context.arg, + // setting context.delegate to null, and returning the ContinueSentinel. + + + function maybeInvokeDelegate(delegate, context) { + var method = delegate.iterator[context.method]; + + if (method === undefined$1) { + // A .throw or .return when the delegate iterator has no .throw + // method always terminates the yield* loop. + context.delegate = null; + + if (context.method === "throw") { + // Note: ["return"] must be used for ES3 parsing compatibility. + if (delegate.iterator["return"]) { + // If the delegate iterator has a return method, give it a + // chance to clean up. + context.method = "return"; + context.arg = undefined$1; + maybeInvokeDelegate(delegate, context); + + if (context.method === "throw") { + // If maybeInvokeDelegate(context) changed context.method from + // "return" to "throw", let that override the TypeError below. + return ContinueSentinel; + } + } + + context.method = "throw"; + context.arg = new TypeError("The iterator does not provide a 'throw' method"); + } + + return ContinueSentinel; + } + + var record = tryCatch(method, delegate.iterator, context.arg); + + if (record.type === "throw") { + context.method = "throw"; + context.arg = record.arg; + context.delegate = null; + return ContinueSentinel; + } + + var info = record.arg; + + if (!info) { + context.method = "throw"; + context.arg = new TypeError("iterator result is not an object"); + context.delegate = null; + return ContinueSentinel; + } + + if (info.done) { + // Assign the result of the finished delegate to the temporary + // variable specified by delegate.resultName (see delegateYield). + context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield). + + context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the + // exception, let the outer generator proceed normally. If + // context.method was "next", forget context.arg since it has been + // "consumed" by the delegate iterator. If context.method was + // "return", allow the original .return call to continue in the + // outer generator. + + if (context.method !== "return") { + context.method = "next"; + context.arg = undefined$1; + } + } else { + // Re-yield the result returned by the delegate method. + return info; + } // The delegate iterator is finished, so forget it and continue with + // the outer generator. + + + context.delegate = null; + return ContinueSentinel; + } // Define Generator.prototype.{next,throw,return} in terms of the + // unified ._invoke helper method. + + + defineIteratorMethods(Gp); + define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the + // @@iterator function is called on it. Some browsers' implementations of the + // iterator prototype chain incorrectly implement this, causing the Generator + // object to not be returned from this call. This ensures that doesn't happen. + // See https://github.com/facebook/regenerator/issues/274 for more details. + + Gp[iteratorSymbol] = function () { + return this; + }; + + Gp.toString = function () { + return "[object Generator]"; + }; + + function pushTryEntry(locs) { + var entry = { + tryLoc: locs[0] + }; + + if (1 in locs) { + entry.catchLoc = locs[1]; + } + + if (2 in locs) { + entry.finallyLoc = locs[2]; + entry.afterLoc = locs[3]; + } + + this.tryEntries.push(entry); + } + + function resetTryEntry(entry) { + var record = entry.completion || {}; + record.type = "normal"; + delete record.arg; + entry.completion = record; + } + + function Context(tryLocsList) { + // The root entry object (effectively a try statement without a catch + // or a finally block) gives us a place to store values thrown from + // locations where there is no enclosing try statement. + this.tryEntries = [{ + tryLoc: "root" + }]; + tryLocsList.forEach(pushTryEntry, this); + this.reset(true); + } + + exports.keys = function (object) { + var keys = []; + + for (var key in object) { + keys.push(key); + } + + keys.reverse(); // Rather than returning an object with a next method, we keep + // things simple and return the next function itself. + + return function next() { + while (keys.length) { + var key = keys.pop(); + + if (key in object) { + next.value = key; + next.done = false; + return next; + } + } // To avoid creating an additional object, we just hang the .value + // and .done properties off the next function object itself. This + // also ensures that the minifier will not anonymize the function. + + + next.done = true; + return next; + }; + }; + + function values(iterable) { + if (iterable) { + var iteratorMethod = iterable[iteratorSymbol]; + + if (iteratorMethod) { + return iteratorMethod.call(iterable); + } + + if (typeof iterable.next === "function") { + return iterable; + } + + if (!isNaN(iterable.length)) { + var i = -1, + next = function next() { + while (++i < iterable.length) { + if (hasOwn.call(iterable, i)) { + next.value = iterable[i]; + next.done = false; + return next; + } + } + + next.value = undefined$1; + next.done = true; + return next; + }; + + return next.next = next; + } + } // Return an iterator with no values. + + + return { + next: doneResult + }; + } + + exports.values = values; + + function doneResult() { + return { + value: undefined$1, + done: true + }; + } + + Context.prototype = { + constructor: Context, + reset: function reset(skipTempReset) { + this.prev = 0; + this.next = 0; // Resetting context._sent for legacy support of Babel's + // function.sent implementation. + + this.sent = this._sent = undefined$1; + this.done = false; + this.delegate = null; + this.method = "next"; + this.arg = undefined$1; + this.tryEntries.forEach(resetTryEntry); + + if (!skipTempReset) { + for (var name in this) { + // Not sure about the optimal order of these conditions: + if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) { + this[name] = undefined$1; + } + } + } + }, + stop: function stop() { + this.done = true; + var rootEntry = this.tryEntries[0]; + var rootRecord = rootEntry.completion; + + if (rootRecord.type === "throw") { + throw rootRecord.arg; + } + + return this.rval; + }, + dispatchException: function dispatchException(exception) { + if (this.done) { + throw exception; + } + + var context = this; + + function handle(loc, caught) { + record.type = "throw"; + record.arg = exception; + context.next = loc; + + if (caught) { + // If the dispatched exception was caught by a catch block, + // then let that catch block handle the exception normally. + context.method = "next"; + context.arg = undefined$1; + } + + return !!caught; + } + + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + var record = entry.completion; + + if (entry.tryLoc === "root") { + // Exception thrown outside of any try block that could handle + // it, so set the completion value of the entire function to + // throw the exception. + return handle("end"); + } + + if (entry.tryLoc <= this.prev) { + var hasCatch = hasOwn.call(entry, "catchLoc"); + var hasFinally = hasOwn.call(entry, "finallyLoc"); + + if (hasCatch && hasFinally) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } else if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + } else if (hasCatch) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } + } else if (hasFinally) { + if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + } else { + throw new Error("try statement without catch or finally"); + } + } + } + }, + abrupt: function abrupt(type, arg) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { + var finallyEntry = entry; + break; + } + } + + if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) { + // Ignore the finally entry if control is not jumping to a + // location outside the try/catch block. + finallyEntry = null; + } + + var record = finallyEntry ? finallyEntry.completion : {}; + record.type = type; + record.arg = arg; + + if (finallyEntry) { + this.method = "next"; + this.next = finallyEntry.finallyLoc; + return ContinueSentinel; + } + + return this.complete(record); + }, + complete: function complete(record, afterLoc) { + if (record.type === "throw") { + throw record.arg; + } + + if (record.type === "break" || record.type === "continue") { + this.next = record.arg; + } else if (record.type === "return") { + this.rval = this.arg = record.arg; + this.method = "return"; + this.next = "end"; + } else if (record.type === "normal" && afterLoc) { + this.next = afterLoc; + } + + return ContinueSentinel; + }, + finish: function finish(finallyLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.finallyLoc === finallyLoc) { + this.complete(entry.completion, entry.afterLoc); + resetTryEntry(entry); + return ContinueSentinel; + } + } + }, + "catch": function _catch(tryLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.tryLoc === tryLoc) { + var record = entry.completion; + + if (record.type === "throw") { + var thrown = record.arg; + resetTryEntry(entry); + } + + return thrown; + } + } // The context.catch method must only be called with a location + // argument that corresponds to a known catch block. + + + throw new Error("illegal catch attempt"); + }, + delegateYield: function delegateYield(iterable, resultName, nextLoc) { + this.delegate = { + iterator: values(iterable), + resultName: resultName, + nextLoc: nextLoc + }; + + if (this.method === "next") { + // Deliberately forget the last sent value so that we don't + // accidentally pass it on to the delegate. + this.arg = undefined$1; + } + + return ContinueSentinel; + } + }; // Regardless of whether this script is executing as a CommonJS module + // or not, return the runtime object so that we can declare the variable + // regeneratorRuntime in the outer scope, which allows this module to be + // injected easily by `bin/regenerator --include-runtime script.js`. + + return exports; + }( // If this script is executing as a CommonJS module, use module.exports + // as the regeneratorRuntime namespace. Otherwise create a new empty + // object. Either way, the resulting object will be used to initialize + // the regeneratorRuntime variable at the top of this file. + typeof module === "object" ? module.exports : {}); + + try { + regeneratorRuntime = runtime; + } catch (accidentalStrictMode) { + // This module should not be running in strict mode, so the above + // assignment should always work unless something is misconfigured. Just + // in case runtime.js accidentally runs in strict mode, we can escape + // strict mode using a global Function call. This could conceivably fail + // if a Content Security Policy forbids using Function, but in that case + // the proper solution is to fix the accidental strict mode problem. If + // you've misconfigured your bundler to force strict mode and applied a + // CSP to forbid Function, and you're not willing to fix either of those + // problems, please detail your unique predicament in a GitHub issue. + Function("r", "regeneratorRuntime = r")(runtime); + } + + var REVISION = '126'; + var MOUSE = { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2, + ROTATE: 0, + DOLLY: 1, + PAN: 2 + }; + var TOUCH = { + ROTATE: 0, + PAN: 1, + DOLLY_PAN: 2, + DOLLY_ROTATE: 3 + }; + var CullFaceNone = 0; + var CullFaceBack = 1; + var CullFaceFront = 2; + var CullFaceFrontBack = 3; + var BasicShadowMap = 0; + var PCFShadowMap = 1; + var PCFSoftShadowMap = 2; + var VSMShadowMap = 3; + var FrontSide = 0; + var BackSide = 1; + var DoubleSide = 2; + var FlatShading = 1; + var SmoothShading = 2; + var NoBlending = 0; + var NormalBlending = 1; + var AdditiveBlending = 2; + var SubtractiveBlending = 3; + var MultiplyBlending = 4; + var CustomBlending = 5; + var AddEquation = 100; + var SubtractEquation = 101; + var ReverseSubtractEquation = 102; + var MinEquation = 103; + var MaxEquation = 104; + var ZeroFactor = 200; + var OneFactor = 201; + var SrcColorFactor = 202; + var OneMinusSrcColorFactor = 203; + var SrcAlphaFactor = 204; + var OneMinusSrcAlphaFactor = 205; + var DstAlphaFactor = 206; + var OneMinusDstAlphaFactor = 207; + var DstColorFactor = 208; + var OneMinusDstColorFactor = 209; + var SrcAlphaSaturateFactor = 210; + var NeverDepth = 0; + var AlwaysDepth = 1; + var LessDepth = 2; + var LessEqualDepth = 3; + var EqualDepth = 4; + var GreaterEqualDepth = 5; + var GreaterDepth = 6; + var NotEqualDepth = 7; + var MultiplyOperation = 0; + var MixOperation = 1; + var AddOperation = 2; + var NoToneMapping = 0; + var LinearToneMapping = 1; + var ReinhardToneMapping = 2; + var CineonToneMapping = 3; + var ACESFilmicToneMapping = 4; + var CustomToneMapping = 5; + var UVMapping = 300; + var CubeReflectionMapping = 301; + var CubeRefractionMapping = 302; + var EquirectangularReflectionMapping = 303; + var EquirectangularRefractionMapping = 304; + var CubeUVReflectionMapping = 306; + var CubeUVRefractionMapping = 307; + var RepeatWrapping = 1000; + var ClampToEdgeWrapping = 1001; + var MirroredRepeatWrapping = 1002; + var NearestFilter = 1003; + var NearestMipmapNearestFilter = 1004; + var NearestMipMapNearestFilter = 1004; + var NearestMipmapLinearFilter = 1005; + var NearestMipMapLinearFilter = 1005; + var LinearFilter = 1006; + var LinearMipmapNearestFilter = 1007; + var LinearMipMapNearestFilter = 1007; + var LinearMipmapLinearFilter = 1008; + var LinearMipMapLinearFilter = 1008; + var UnsignedByteType = 1009; + var ByteType = 1010; + var ShortType = 1011; + var UnsignedShortType = 1012; + var IntType = 1013; + var UnsignedIntType = 1014; + var FloatType = 1015; + var HalfFloatType = 1016; + var UnsignedShort4444Type = 1017; + var UnsignedShort5551Type = 1018; + var UnsignedShort565Type = 1019; + var UnsignedInt248Type = 1020; + var AlphaFormat = 1021; + var RGBFormat = 1022; + var RGBAFormat = 1023; + var LuminanceFormat = 1024; + var LuminanceAlphaFormat = 1025; + var RGBEFormat = RGBAFormat; + var DepthFormat = 1026; + var DepthStencilFormat = 1027; + var RedFormat = 1028; + var RedIntegerFormat = 1029; + var RGFormat = 1030; + var RGIntegerFormat = 1031; + var RGBIntegerFormat = 1032; + var RGBAIntegerFormat = 1033; + var RGB_S3TC_DXT1_Format = 33776; + var RGBA_S3TC_DXT1_Format = 33777; + var RGBA_S3TC_DXT3_Format = 33778; + var RGBA_S3TC_DXT5_Format = 33779; + var RGB_PVRTC_4BPPV1_Format = 35840; + var RGB_PVRTC_2BPPV1_Format = 35841; + var RGBA_PVRTC_4BPPV1_Format = 35842; + var RGBA_PVRTC_2BPPV1_Format = 35843; + var RGB_ETC1_Format = 36196; + var RGB_ETC2_Format = 37492; + var RGBA_ETC2_EAC_Format = 37496; + var RGBA_ASTC_4x4_Format = 37808; + var RGBA_ASTC_5x4_Format = 37809; + var RGBA_ASTC_5x5_Format = 37810; + var RGBA_ASTC_6x5_Format = 37811; + var RGBA_ASTC_6x6_Format = 37812; + var RGBA_ASTC_8x5_Format = 37813; + var RGBA_ASTC_8x6_Format = 37814; + var RGBA_ASTC_8x8_Format = 37815; + var RGBA_ASTC_10x5_Format = 37816; + var RGBA_ASTC_10x6_Format = 37817; + var RGBA_ASTC_10x8_Format = 37818; + var RGBA_ASTC_10x10_Format = 37819; + var RGBA_ASTC_12x10_Format = 37820; + var RGBA_ASTC_12x12_Format = 37821; + var RGBA_BPTC_Format = 36492; + var SRGB8_ALPHA8_ASTC_4x4_Format = 37840; + var SRGB8_ALPHA8_ASTC_5x4_Format = 37841; + var SRGB8_ALPHA8_ASTC_5x5_Format = 37842; + var SRGB8_ALPHA8_ASTC_6x5_Format = 37843; + var SRGB8_ALPHA8_ASTC_6x6_Format = 37844; + var SRGB8_ALPHA8_ASTC_8x5_Format = 37845; + var SRGB8_ALPHA8_ASTC_8x6_Format = 37846; + var SRGB8_ALPHA8_ASTC_8x8_Format = 37847; + var SRGB8_ALPHA8_ASTC_10x5_Format = 37848; + var SRGB8_ALPHA8_ASTC_10x6_Format = 37849; + var SRGB8_ALPHA8_ASTC_10x8_Format = 37850; + var SRGB8_ALPHA8_ASTC_10x10_Format = 37851; + var SRGB8_ALPHA8_ASTC_12x10_Format = 37852; + var SRGB8_ALPHA8_ASTC_12x12_Format = 37853; + var LoopOnce = 2200; + var LoopRepeat = 2201; + var LoopPingPong = 2202; + var InterpolateDiscrete = 2300; + var InterpolateLinear = 2301; + var InterpolateSmooth = 2302; + var ZeroCurvatureEnding = 2400; + var ZeroSlopeEnding = 2401; + var WrapAroundEnding = 2402; + var NormalAnimationBlendMode = 2500; + var AdditiveAnimationBlendMode = 2501; + var TrianglesDrawMode = 0; + var TriangleStripDrawMode = 1; + var TriangleFanDrawMode = 2; + var LinearEncoding = 3000; + var sRGBEncoding = 3001; + var GammaEncoding = 3007; + var RGBEEncoding = 3002; + var LogLuvEncoding = 3003; + var RGBM7Encoding = 3004; + var RGBM16Encoding = 3005; + var RGBDEncoding = 3006; + var BasicDepthPacking = 3200; + var RGBADepthPacking = 3201; + var TangentSpaceNormalMap = 0; + var ObjectSpaceNormalMap = 1; + var ZeroStencilOp = 0; + var KeepStencilOp = 7680; + var ReplaceStencilOp = 7681; + var IncrementStencilOp = 7682; + var DecrementStencilOp = 7683; + var IncrementWrapStencilOp = 34055; + var DecrementWrapStencilOp = 34056; + var InvertStencilOp = 5386; + var NeverStencilFunc = 512; + var LessStencilFunc = 513; + var EqualStencilFunc = 514; + var LessEqualStencilFunc = 515; + var GreaterStencilFunc = 516; + var NotEqualStencilFunc = 517; + var GreaterEqualStencilFunc = 518; + var AlwaysStencilFunc = 519; + var StaticDrawUsage = 35044; + var DynamicDrawUsage = 35048; + var StreamDrawUsage = 35040; + var StaticReadUsage = 35045; + var DynamicReadUsage = 35049; + var StreamReadUsage = 35041; + var StaticCopyUsage = 35046; + var DynamicCopyUsage = 35050; + var StreamCopyUsage = 35042; + var GLSL1 = '100'; + var GLSL3 = '300 es'; + + function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { + try { + var info = gen[key](arg); + var value = info.value; + } catch (error) { + reject(error); + return; + } + + if (info.done) { + resolve(value); + } else { + Promise.resolve(value).then(_next, _throw); + } + } + + function _asyncToGenerator(fn) { + return function () { + var self = this, + args = arguments; + return new Promise(function (resolve, reject) { + var gen = fn.apply(self, args); + + function _next(value) { + asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); + } + + function _throw(err) { + asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); + } + + _next(undefined); + }); + }; + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + + _setPrototypeOf(subClass, superClass); + } + + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); + } + + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it; + + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + it = o[Symbol.iterator](); + return it.next.bind(it); + } + + /** + * https://github.com/mrdoob/eventdispatcher.js/ + */ + function EventDispatcher() {} + + Object.assign(EventDispatcher.prototype, { + addEventListener: function addEventListener(type, listener) { + if (this._listeners === undefined) this._listeners = {}; + var listeners = this._listeners; + + if (listeners[type] === undefined) { + listeners[type] = []; + } + + if (listeners[type].indexOf(listener) === -1) { + listeners[type].push(listener); + } + }, + hasEventListener: function hasEventListener(type, listener) { + if (this._listeners === undefined) return false; + var listeners = this._listeners; + return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; + }, + removeEventListener: function removeEventListener(type, listener) { + if (this._listeners === undefined) return; + var listeners = this._listeners; + var listenerArray = listeners[type]; + + if (listenerArray !== undefined) { + var index = listenerArray.indexOf(listener); + + if (index !== -1) { + listenerArray.splice(index, 1); + } + } + }, + dispatchEvent: function dispatchEvent(event) { + if (this._listeners === undefined) return; + var listeners = this._listeners; + var listenerArray = listeners[event.type]; + + if (listenerArray !== undefined) { + event.target = this; // Make a copy, in case listeners are removed while iterating. + + var array = listenerArray.slice(0); + + for (var i = 0, l = array.length; i < l; i++) { + array[i].call(this, event); + } + } + } + }); + + var _lut = []; + + for (var i = 0; i < 256; i++) { + _lut[i] = (i < 16 ? '0' : '') + i.toString(16); + } + + var _seed = 1234567; + var MathUtils = { + DEG2RAD: Math.PI / 180, + RAD2DEG: 180 / Math.PI, + generateUUID: function generateUUID() { + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 + var d0 = Math.random() * 0xffffffff | 0; + var d1 = Math.random() * 0xffffffff | 0; + var d2 = Math.random() * 0xffffffff | 0; + var d3 = Math.random() * 0xffffffff | 0; + var uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + '-' + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + '-' + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + '-' + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + '-' + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space. + + return uuid.toUpperCase(); + }, + clamp: function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); + }, + // compute euclidian modulo of m % n + // https://en.wikipedia.org/wiki/Modulo_operation + euclideanModulo: function euclideanModulo(n, m) { + return (n % m + m) % m; + }, + // Linear mapping from range to range + mapLinear: function mapLinear(x, a1, a2, b1, b2) { + return b1 + (x - a1) * (b2 - b1) / (a2 - a1); + }, + // https://en.wikipedia.org/wiki/Linear_interpolation + lerp: function lerp(x, y, t) { + return (1 - t) * x + t * y; + }, + // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ + damp: function damp(x, y, lambda, dt) { + return MathUtils.lerp(x, y, 1 - Math.exp(-lambda * dt)); + }, + // https://www.desmos.com/calculator/vcsjnyz7x4 + pingpong: function pingpong(x, length) { + if (length === void 0) { + length = 1; + } + + return length - Math.abs(MathUtils.euclideanModulo(x, length * 2) - length); + }, + // http://en.wikipedia.org/wiki/Smoothstep + smoothstep: function smoothstep(x, min, max) { + if (x <= min) return 0; + if (x >= max) return 1; + x = (x - min) / (max - min); + return x * x * (3 - 2 * x); + }, + smootherstep: function smootherstep(x, min, max) { + if (x <= min) return 0; + if (x >= max) return 1; + x = (x - min) / (max - min); + return x * x * x * (x * (x * 6 - 15) + 10); + }, + // Random integer from interval + randInt: function randInt(low, high) { + return low + Math.floor(Math.random() * (high - low + 1)); + }, + // Random float from interval + randFloat: function randFloat(low, high) { + return low + Math.random() * (high - low); + }, + // Random float from <-range/2, range/2> interval + randFloatSpread: function randFloatSpread(range) { + return range * (0.5 - Math.random()); + }, + // Deterministic pseudo-random float in the interval [ 0, 1 ] + seededRandom: function seededRandom(s) { + if (s !== undefined) _seed = s % 2147483647; // Park-Miller algorithm + + _seed = _seed * 16807 % 2147483647; + return (_seed - 1) / 2147483646; + }, + degToRad: function degToRad(degrees) { + return degrees * MathUtils.DEG2RAD; + }, + radToDeg: function radToDeg(radians) { + return radians * MathUtils.RAD2DEG; + }, + isPowerOfTwo: function isPowerOfTwo(value) { + return (value & value - 1) === 0 && value !== 0; + }, + ceilPowerOfTwo: function ceilPowerOfTwo(value) { + return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); + }, + floorPowerOfTwo: function floorPowerOfTwo(value) { + return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); + }, + setQuaternionFromProperEuler: function setQuaternionFromProperEuler(q, a, b, c, order) { + // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles + // rotations are applied to the axes in the order specified by 'order' + // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' + // angles are in radians + var cos = Math.cos; + var sin = Math.sin; + var c2 = cos(b / 2); + var s2 = sin(b / 2); + var c13 = cos((a + c) / 2); + var s13 = sin((a + c) / 2); + var c1_3 = cos((a - c) / 2); + var s1_3 = sin((a - c) / 2); + var c3_1 = cos((c - a) / 2); + var s3_1 = sin((c - a) / 2); + + switch (order) { + case 'XYX': + q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); + break; + + case 'YZY': + q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); + break; + + case 'ZXZ': + q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); + break; + + case 'XZX': + q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); + break; + + case 'YXY': + q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); + break; + + case 'ZYZ': + q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); + break; + + default: + console.warn('THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order); + } + } + }; + + var Vector2 = /*#__PURE__*/function () { + function Vector2(x, y) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + this.x = x; + this.y = y; + } + + var _proto = Vector2.prototype; + + _proto.set = function set(x, y) { + this.x = x; + this.y = y; + return this; + }; + + _proto.setScalar = function setScalar(scalar) { + this.x = scalar; + this.y = scalar; + return this; + }; + + _proto.setX = function setX(x) { + this.x = x; + return this; + }; + + _proto.setY = function setY(y) { + this.y = y; + return this; + }; + + _proto.setComponent = function setComponent(index, value) { + switch (index) { + case 0: + this.x = value; + break; + + case 1: + this.y = value; + break; + + default: + throw new Error('index is out of range: ' + index); + } + + return this; + }; + + _proto.getComponent = function getComponent(index) { + switch (index) { + case 0: + return this.x; + + case 1: + return this.y; + + default: + throw new Error('index is out of range: ' + index); + } + }; + + _proto.clone = function clone() { + return new this.constructor(this.x, this.y); + }; + + _proto.copy = function copy(v) { + this.x = v.x; + this.y = v.y; + return this; + }; + + _proto.add = function add(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.'); + return this.addVectors(v, w); + } + + this.x += v.x; + this.y += v.y; + return this; + }; + + _proto.addScalar = function addScalar(s) { + this.x += s; + this.y += s; + return this; + }; + + _proto.addVectors = function addVectors(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + return this; + }; + + _proto.addScaledVector = function addScaledVector(v, s) { + this.x += v.x * s; + this.y += v.y * s; + return this; + }; + + _proto.sub = function sub(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.'); + return this.subVectors(v, w); + } + + this.x -= v.x; + this.y -= v.y; + return this; + }; + + _proto.subScalar = function subScalar(s) { + this.x -= s; + this.y -= s; + return this; + }; + + _proto.subVectors = function subVectors(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + return this; + }; + + _proto.multiply = function multiply(v) { + this.x *= v.x; + this.y *= v.y; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(scalar) { + this.x *= scalar; + this.y *= scalar; + return this; + }; + + _proto.divide = function divide(v) { + this.x /= v.x; + this.y /= v.y; + return this; + }; + + _proto.divideScalar = function divideScalar(scalar) { + return this.multiplyScalar(1 / scalar); + }; + + _proto.applyMatrix3 = function applyMatrix3(m) { + var x = this.x, + y = this.y; + var e = m.elements; + this.x = e[0] * x + e[3] * y + e[6]; + this.y = e[1] * x + e[4] * y + e[7]; + return this; + }; + + _proto.min = function min(v) { + this.x = Math.min(this.x, v.x); + this.y = Math.min(this.y, v.y); + return this; + }; + + _proto.max = function max(v) { + this.x = Math.max(this.x, v.x); + this.y = Math.max(this.y, v.y); + return this; + }; + + _proto.clamp = function clamp(min, max) { + // assumes min < max, componentwise + this.x = Math.max(min.x, Math.min(max.x, this.x)); + this.y = Math.max(min.y, Math.min(max.y, this.y)); + return this; + }; + + _proto.clampScalar = function clampScalar(minVal, maxVal) { + this.x = Math.max(minVal, Math.min(maxVal, this.x)); + this.y = Math.max(minVal, Math.min(maxVal, this.y)); + return this; + }; + + _proto.clampLength = function clampLength(min, max) { + var length = this.length(); + return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); + }; + + _proto.floor = function floor() { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }; + + _proto.ceil = function ceil() { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + return this; + }; + + _proto.round = function round() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }; + + _proto.roundToZero = function roundToZero() { + this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); + this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); + return this; + }; + + _proto.negate = function negate() { + this.x = -this.x; + this.y = -this.y; + return this; + }; + + _proto.dot = function dot(v) { + return this.x * v.x + this.y * v.y; + }; + + _proto.cross = function cross(v) { + return this.x * v.y - this.y * v.x; + }; + + _proto.lengthSq = function lengthSq() { + return this.x * this.x + this.y * this.y; + }; + + _proto.length = function length() { + return Math.sqrt(this.x * this.x + this.y * this.y); + }; + + _proto.manhattanLength = function manhattanLength() { + return Math.abs(this.x) + Math.abs(this.y); + }; + + _proto.normalize = function normalize() { + return this.divideScalar(this.length() || 1); + }; + + _proto.angle = function angle() { + // computes the angle in radians with respect to the positive x-axis + var angle = Math.atan2(-this.y, -this.x) + Math.PI; + return angle; + }; + + _proto.distanceTo = function distanceTo(v) { + return Math.sqrt(this.distanceToSquared(v)); + }; + + _proto.distanceToSquared = function distanceToSquared(v) { + var dx = this.x - v.x, + dy = this.y - v.y; + return dx * dx + dy * dy; + }; + + _proto.manhattanDistanceTo = function manhattanDistanceTo(v) { + return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); + }; + + _proto.setLength = function setLength(length) { + return this.normalize().multiplyScalar(length); + }; + + _proto.lerp = function lerp(v, alpha) { + this.x += (v.x - this.x) * alpha; + this.y += (v.y - this.y) * alpha; + return this; + }; + + _proto.lerpVectors = function lerpVectors(v1, v2, alpha) { + this.x = v1.x + (v2.x - v1.x) * alpha; + this.y = v1.y + (v2.y - v1.y) * alpha; + return this; + }; + + _proto.equals = function equals(v) { + return v.x === this.x && v.y === this.y; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + this.x = array[offset]; + this.y = array[offset + 1]; + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this.x; + array[offset + 1] = this.y; + return array; + }; + + _proto.fromBufferAttribute = function fromBufferAttribute(attribute, index, offset) { + if (offset !== undefined) { + console.warn('THREE.Vector2: offset has been removed from .fromBufferAttribute().'); + } + + this.x = attribute.getX(index); + this.y = attribute.getY(index); + return this; + }; + + _proto.rotateAround = function rotateAround(center, angle) { + var c = Math.cos(angle), + s = Math.sin(angle); + var x = this.x - center.x; + var y = this.y - center.y; + this.x = x * c - y * s + center.x; + this.y = x * s + y * c + center.y; + return this; + }; + + _proto.random = function random() { + this.x = Math.random(); + this.y = Math.random(); + return this; + }; + + _createClass(Vector2, [{ + key: "width", + get: function get() { + return this.x; + }, + set: function set(value) { + this.x = value; + } + }, { + key: "height", + get: function get() { + return this.y; + }, + set: function set(value) { + this.y = value; + } + }]); + + return Vector2; + }(); + + Vector2.prototype.isVector2 = true; + + var Matrix3 = /*#__PURE__*/function () { + function Matrix3() { + this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; + + if (arguments.length > 0) { + console.error('THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.'); + } + } + + var _proto = Matrix3.prototype; + + _proto.set = function set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { + var te = this.elements; + te[0] = n11; + te[1] = n21; + te[2] = n31; + te[3] = n12; + te[4] = n22; + te[5] = n32; + te[6] = n13; + te[7] = n23; + te[8] = n33; + return this; + }; + + _proto.identity = function identity() { + this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); + return this; + }; + + _proto.copy = function copy(m) { + var te = this.elements; + var me = m.elements; + te[0] = me[0]; + te[1] = me[1]; + te[2] = me[2]; + te[3] = me[3]; + te[4] = me[4]; + te[5] = me[5]; + te[6] = me[6]; + te[7] = me[7]; + te[8] = me[8]; + return this; + }; + + _proto.extractBasis = function extractBasis(xAxis, yAxis, zAxis) { + xAxis.setFromMatrix3Column(this, 0); + yAxis.setFromMatrix3Column(this, 1); + zAxis.setFromMatrix3Column(this, 2); + return this; + }; + + _proto.setFromMatrix4 = function setFromMatrix4(m) { + var me = m.elements; + this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); + return this; + }; + + _proto.multiply = function multiply(m) { + return this.multiplyMatrices(this, m); + }; + + _proto.premultiply = function premultiply(m) { + return this.multiplyMatrices(m, this); + }; + + _proto.multiplyMatrices = function multiplyMatrices(a, b) { + var ae = a.elements; + var be = b.elements; + var te = this.elements; + var a11 = ae[0], + a12 = ae[3], + a13 = ae[6]; + var a21 = ae[1], + a22 = ae[4], + a23 = ae[7]; + var a31 = ae[2], + a32 = ae[5], + a33 = ae[8]; + var b11 = be[0], + b12 = be[3], + b13 = be[6]; + var b21 = be[1], + b22 = be[4], + b23 = be[7]; + var b31 = be[2], + b32 = be[5], + b33 = be[8]; + te[0] = a11 * b11 + a12 * b21 + a13 * b31; + te[3] = a11 * b12 + a12 * b22 + a13 * b32; + te[6] = a11 * b13 + a12 * b23 + a13 * b33; + te[1] = a21 * b11 + a22 * b21 + a23 * b31; + te[4] = a21 * b12 + a22 * b22 + a23 * b32; + te[7] = a21 * b13 + a22 * b23 + a23 * b33; + te[2] = a31 * b11 + a32 * b21 + a33 * b31; + te[5] = a31 * b12 + a32 * b22 + a33 * b32; + te[8] = a31 * b13 + a32 * b23 + a33 * b33; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(s) { + var te = this.elements; + te[0] *= s; + te[3] *= s; + te[6] *= s; + te[1] *= s; + te[4] *= s; + te[7] *= s; + te[2] *= s; + te[5] *= s; + te[8] *= s; + return this; + }; + + _proto.determinant = function determinant() { + var te = this.elements; + var a = te[0], + b = te[1], + c = te[2], + d = te[3], + e = te[4], + f = te[5], + g = te[6], + h = te[7], + i = te[8]; + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + }; + + _proto.invert = function invert() { + var te = this.elements, + n11 = te[0], + n21 = te[1], + n31 = te[2], + n12 = te[3], + n22 = te[4], + n32 = te[5], + n13 = te[6], + n23 = te[7], + n33 = te[8], + t11 = n33 * n22 - n32 * n23, + t12 = n32 * n13 - n33 * n12, + t13 = n23 * n12 - n22 * n13, + det = n11 * t11 + n21 * t12 + n31 * t13; + if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); + var detInv = 1 / det; + te[0] = t11 * detInv; + te[1] = (n31 * n23 - n33 * n21) * detInv; + te[2] = (n32 * n21 - n31 * n22) * detInv; + te[3] = t12 * detInv; + te[4] = (n33 * n11 - n31 * n13) * detInv; + te[5] = (n31 * n12 - n32 * n11) * detInv; + te[6] = t13 * detInv; + te[7] = (n21 * n13 - n23 * n11) * detInv; + te[8] = (n22 * n11 - n21 * n12) * detInv; + return this; + }; + + _proto.transpose = function transpose() { + var tmp; + var m = this.elements; + tmp = m[1]; + m[1] = m[3]; + m[3] = tmp; + tmp = m[2]; + m[2] = m[6]; + m[6] = tmp; + tmp = m[5]; + m[5] = m[7]; + m[7] = tmp; + return this; + }; + + _proto.getNormalMatrix = function getNormalMatrix(matrix4) { + return this.setFromMatrix4(matrix4).invert().transpose(); + }; + + _proto.transposeIntoArray = function transposeIntoArray(r) { + var m = this.elements; + r[0] = m[0]; + r[1] = m[3]; + r[2] = m[6]; + r[3] = m[1]; + r[4] = m[4]; + r[5] = m[7]; + r[6] = m[2]; + r[7] = m[5]; + r[8] = m[8]; + return this; + }; + + _proto.setUvTransform = function setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { + var c = Math.cos(rotation); + var s = Math.sin(rotation); + this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); + return this; + }; + + _proto.scale = function scale(sx, sy) { + var te = this.elements; + te[0] *= sx; + te[3] *= sx; + te[6] *= sx; + te[1] *= sy; + te[4] *= sy; + te[7] *= sy; + return this; + }; + + _proto.rotate = function rotate(theta) { + var c = Math.cos(theta); + var s = Math.sin(theta); + var te = this.elements; + var a11 = te[0], + a12 = te[3], + a13 = te[6]; + var a21 = te[1], + a22 = te[4], + a23 = te[7]; + te[0] = c * a11 + s * a21; + te[3] = c * a12 + s * a22; + te[6] = c * a13 + s * a23; + te[1] = -s * a11 + c * a21; + te[4] = -s * a12 + c * a22; + te[7] = -s * a13 + c * a23; + return this; + }; + + _proto.translate = function translate(tx, ty) { + var te = this.elements; + te[0] += tx * te[2]; + te[3] += tx * te[5]; + te[6] += tx * te[8]; + te[1] += ty * te[2]; + te[4] += ty * te[5]; + te[7] += ty * te[8]; + return this; + }; + + _proto.equals = function equals(matrix) { + var te = this.elements; + var me = matrix.elements; + + for (var i = 0; i < 9; i++) { + if (te[i] !== me[i]) return false; + } + + return true; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + for (var i = 0; i < 9; i++) { + this.elements[i] = array[i + offset]; + } + + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + var te = this.elements; + array[offset] = te[0]; + array[offset + 1] = te[1]; + array[offset + 2] = te[2]; + array[offset + 3] = te[3]; + array[offset + 4] = te[4]; + array[offset + 5] = te[5]; + array[offset + 6] = te[6]; + array[offset + 7] = te[7]; + array[offset + 8] = te[8]; + return array; + }; + + _proto.clone = function clone() { + return new this.constructor().fromArray(this.elements); + }; + + return Matrix3; + }(); + + Matrix3.prototype.isMatrix3 = true; + + var _canvas; + + var ImageUtils = { + getDataURL: function getDataURL(image) { + if (/^data:/i.test(image.src)) { + return image.src; + } + + if (typeof HTMLCanvasElement == 'undefined') { + return image.src; + } + + var canvas; + + if (image instanceof HTMLCanvasElement) { + canvas = image; + } else { + if (_canvas === undefined) _canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); + _canvas.width = image.width; + _canvas.height = image.height; + + var context = _canvas.getContext('2d'); + + if (image instanceof ImageData) { + context.putImageData(image, 0, 0); + } else { + context.drawImage(image, 0, 0, image.width, image.height); + } + + canvas = _canvas; + } + + if (canvas.width > 2048 || canvas.height > 2048) { + return canvas.toDataURL('image/jpeg', 0.6); + } else { + return canvas.toDataURL('image/png'); + } + } + }; + + var textureId = 0; + + var Texture = /*#__PURE__*/function (_EventDispatcher) { + _inheritsLoose(Texture, _EventDispatcher); + + function Texture(image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { + var _this; + + if (image === void 0) { + image = Texture.DEFAULT_IMAGE; + } + + if (mapping === void 0) { + mapping = Texture.DEFAULT_MAPPING; + } + + if (wrapS === void 0) { + wrapS = ClampToEdgeWrapping; + } + + if (wrapT === void 0) { + wrapT = ClampToEdgeWrapping; + } + + if (magFilter === void 0) { + magFilter = LinearFilter; + } + + if (minFilter === void 0) { + minFilter = LinearMipmapLinearFilter; + } + + if (format === void 0) { + format = RGBAFormat; + } + + if (type === void 0) { + type = UnsignedByteType; + } + + if (anisotropy === void 0) { + anisotropy = 1; + } + + if (encoding === void 0) { + encoding = LinearEncoding; + } + + _this = _EventDispatcher.call(this) || this; + Object.defineProperty(_assertThisInitialized(_this), 'id', { + value: textureId++ + }); + _this.uuid = MathUtils.generateUUID(); + _this.name = ''; + _this.image = image; + _this.mipmaps = []; + _this.mapping = mapping; + _this.wrapS = wrapS; + _this.wrapT = wrapT; + _this.magFilter = magFilter; + _this.minFilter = minFilter; + _this.anisotropy = anisotropy; + _this.format = format; + _this.internalFormat = null; + _this.type = type; + _this.offset = new Vector2(0, 0); + _this.repeat = new Vector2(1, 1); + _this.center = new Vector2(0, 0); + _this.rotation = 0; + _this.matrixAutoUpdate = true; + _this.matrix = new Matrix3(); + _this.generateMipmaps = true; + _this.premultiplyAlpha = false; + _this.flipY = true; + _this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + + _this.encoding = encoding; + _this.version = 0; + _this.onUpdate = null; + return _this; + } + + var _proto = Texture.prototype; + + _proto.updateMatrix = function updateMatrix() { + this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.copy = function copy(source) { + this.name = source.name; + this.image = source.image; + this.mipmaps = source.mipmaps.slice(0); + this.mapping = source.mapping; + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; + this.anisotropy = source.anisotropy; + this.format = source.format; + this.internalFormat = source.internalFormat; + this.type = source.type; + this.offset.copy(source.offset); + this.repeat.copy(source.repeat); + this.center.copy(source.center); + this.rotation = source.rotation; + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrix.copy(source.matrix); + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; + return this; + }; + + _proto.toJSON = function toJSON(meta) { + var isRootObject = meta === undefined || typeof meta === 'string'; + + if (!isRootObject && meta.textures[this.uuid] !== undefined) { + return meta.textures[this.uuid]; + } + + var output = { + metadata: { + version: 4.5, + type: 'Texture', + generator: 'Texture.toJSON' + }, + uuid: this.uuid, + name: this.name, + mapping: this.mapping, + repeat: [this.repeat.x, this.repeat.y], + offset: [this.offset.x, this.offset.y], + center: [this.center.x, this.center.y], + rotation: this.rotation, + wrap: [this.wrapS, this.wrapT], + format: this.format, + type: this.type, + encoding: this.encoding, + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, + flipY: this.flipY, + premultiplyAlpha: this.premultiplyAlpha, + unpackAlignment: this.unpackAlignment + }; + + if (this.image !== undefined) { + // TODO: Move to THREE.Image + var image = this.image; + + if (image.uuid === undefined) { + image.uuid = MathUtils.generateUUID(); // UGH + } + + if (!isRootObject && meta.images[image.uuid] === undefined) { + var url; + + if (Array.isArray(image)) { + // process array of images e.g. CubeTexture + url = []; + + for (var i = 0, l = image.length; i < l; i++) { + // check cube texture with data textures + if (image[i].isDataTexture) { + url.push(serializeImage(image[i].image)); + } else { + url.push(serializeImage(image[i])); + } + } + } else { + // process single image + url = serializeImage(image); + } + + meta.images[image.uuid] = { + uuid: image.uuid, + url: url + }; + } + + output.image = image.uuid; + } + + if (!isRootObject) { + meta.textures[this.uuid] = output; + } + + return output; + }; + + _proto.dispose = function dispose() { + this.dispatchEvent({ + type: 'dispose' + }); + }; + + _proto.transformUv = function transformUv(uv) { + if (this.mapping !== UVMapping) return uv; + uv.applyMatrix3(this.matrix); + + if (uv.x < 0 || uv.x > 1) { + switch (this.wrapS) { + case RepeatWrapping: + uv.x = uv.x - Math.floor(uv.x); + break; + + case ClampToEdgeWrapping: + uv.x = uv.x < 0 ? 0 : 1; + break; + + case MirroredRepeatWrapping: + if (Math.abs(Math.floor(uv.x) % 2) === 1) { + uv.x = Math.ceil(uv.x) - uv.x; + } else { + uv.x = uv.x - Math.floor(uv.x); + } + + break; + } + } + + if (uv.y < 0 || uv.y > 1) { + switch (this.wrapT) { + case RepeatWrapping: + uv.y = uv.y - Math.floor(uv.y); + break; + + case ClampToEdgeWrapping: + uv.y = uv.y < 0 ? 0 : 1; + break; + + case MirroredRepeatWrapping: + if (Math.abs(Math.floor(uv.y) % 2) === 1) { + uv.y = Math.ceil(uv.y) - uv.y; + } else { + uv.y = uv.y - Math.floor(uv.y); + } + + break; + } + } + + if (this.flipY) { + uv.y = 1 - uv.y; + } + + return uv; + }; + + _createClass(Texture, [{ + key: "needsUpdate", + set: function set(value) { + if (value === true) this.version++; + } + }]); + + return Texture; + }(EventDispatcher); + + Texture.DEFAULT_IMAGE = undefined; + Texture.DEFAULT_MAPPING = UVMapping; + Texture.prototype.isTexture = true; + + function serializeImage(image) { + if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) { + // default images + return ImageUtils.getDataURL(image); + } else { + if (image.data) { + // images of DataTexture + return { + data: Array.prototype.slice.call(image.data), + width: image.width, + height: image.height, + type: image.data.constructor.name + }; + } else { + console.warn('THREE.Texture: Unable to serialize Texture.'); + return {}; + } + } + } + + var Vector4 = /*#__PURE__*/function () { + function Vector4(x, y, z, w) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + if (z === void 0) { + z = 0; + } + + if (w === void 0) { + w = 1; + } + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + var _proto = Vector4.prototype; + + _proto.set = function set(x, y, z, w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + }; + + _proto.setScalar = function setScalar(scalar) { + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; + return this; + }; + + _proto.setX = function setX(x) { + this.x = x; + return this; + }; + + _proto.setY = function setY(y) { + this.y = y; + return this; + }; + + _proto.setZ = function setZ(z) { + this.z = z; + return this; + }; + + _proto.setW = function setW(w) { + this.w = w; + return this; + }; + + _proto.setComponent = function setComponent(index, value) { + switch (index) { + case 0: + this.x = value; + break; + + case 1: + this.y = value; + break; + + case 2: + this.z = value; + break; + + case 3: + this.w = value; + break; + + default: + throw new Error('index is out of range: ' + index); + } + + return this; + }; + + _proto.getComponent = function getComponent(index) { + switch (index) { + case 0: + return this.x; + + case 1: + return this.y; + + case 2: + return this.z; + + case 3: + return this.w; + + default: + throw new Error('index is out of range: ' + index); + } + }; + + _proto.clone = function clone() { + return new this.constructor(this.x, this.y, this.z, this.w); + }; + + _proto.copy = function copy(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = v.w !== undefined ? v.w : 1; + return this; + }; + + _proto.add = function add(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.'); + return this.addVectors(v, w); + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + return this; + }; + + _proto.addScalar = function addScalar(s) { + this.x += s; + this.y += s; + this.z += s; + this.w += s; + return this; + }; + + _proto.addVectors = function addVectors(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + return this; + }; + + _proto.addScaledVector = function addScaledVector(v, s) { + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; + return this; + }; + + _proto.sub = function sub(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.'); + return this.subVectors(v, w); + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + return this; + }; + + _proto.subScalar = function subScalar(s) { + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; + return this; + }; + + _proto.subVectors = function subVectors(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + return this; + }; + + _proto.multiply = function multiply(v) { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + this.w *= v.w; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + return this; + }; + + _proto.applyMatrix4 = function applyMatrix4(m) { + var x = this.x, + y = this.y, + z = this.z, + w = this.w; + var e = m.elements; + this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; + this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; + this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; + this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; + return this; + }; + + _proto.divideScalar = function divideScalar(scalar) { + return this.multiplyScalar(1 / scalar); + }; + + _proto.setAxisAngleFromQuaternion = function setAxisAngleFromQuaternion(q) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + // q is assumed to be normalized + this.w = 2 * Math.acos(q.w); + var s = Math.sqrt(1 - q.w * q.w); + + if (s < 0.0001) { + this.x = 1; + this.y = 0; + this.z = 0; + } else { + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + } + + return this; + }; + + _proto.setAxisAngleFromRotationMatrix = function setAxisAngleFromRotationMatrix(m) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + var angle, x, y, z; // variables for result + + var epsilon = 0.01, + // margin to allow for rounding errors + epsilon2 = 0.1, + // margin to distinguish between 0 and 180 degrees + te = m.elements, + m11 = te[0], + m12 = te[4], + m13 = te[8], + m21 = te[1], + m22 = te[5], + m23 = te[9], + m31 = te[2], + m32 = te[6], + m33 = te[10]; + + if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { + // this singularity is identity matrix so angle = 0 + this.set(1, 0, 0, 0); + return this; // zero angle, arbitrary axis + } // otherwise this singularity is angle = 180 + + + angle = Math.PI; + var xx = (m11 + 1) / 2; + var yy = (m22 + 1) / 2; + var zz = (m33 + 1) / 2; + var xy = (m12 + m21) / 4; + var xz = (m13 + m31) / 4; + var yz = (m23 + m32) / 4; + + if (xx > yy && xx > zz) { + // m11 is the largest diagonal term + if (xx < epsilon) { + x = 0; + y = 0.707106781; + z = 0.707106781; + } else { + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + } + } else if (yy > zz) { + // m22 is the largest diagonal term + if (yy < epsilon) { + x = 0.707106781; + y = 0; + z = 0.707106781; + } else { + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + } + } else { + // m33 is the largest diagonal term so base result on this + if (zz < epsilon) { + x = 0.707106781; + y = 0.707106781; + z = 0; + } else { + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + } + } + + this.set(x, y, z, angle); + return this; // return 180 deg rotation + } // as we have reached here there are no singularities so we can handle normally + + + var s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize + + if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = (m32 - m23) / s; + this.y = (m13 - m31) / s; + this.z = (m21 - m12) / s; + this.w = Math.acos((m11 + m22 + m33 - 1) / 2); + return this; + }; + + _proto.min = function min(v) { + this.x = Math.min(this.x, v.x); + this.y = Math.min(this.y, v.y); + this.z = Math.min(this.z, v.z); + this.w = Math.min(this.w, v.w); + return this; + }; + + _proto.max = function max(v) { + this.x = Math.max(this.x, v.x); + this.y = Math.max(this.y, v.y); + this.z = Math.max(this.z, v.z); + this.w = Math.max(this.w, v.w); + return this; + }; + + _proto.clamp = function clamp(min, max) { + // assumes min < max, componentwise + this.x = Math.max(min.x, Math.min(max.x, this.x)); + this.y = Math.max(min.y, Math.min(max.y, this.y)); + this.z = Math.max(min.z, Math.min(max.z, this.z)); + this.w = Math.max(min.w, Math.min(max.w, this.w)); + return this; + }; + + _proto.clampScalar = function clampScalar(minVal, maxVal) { + this.x = Math.max(minVal, Math.min(maxVal, this.x)); + this.y = Math.max(minVal, Math.min(maxVal, this.y)); + this.z = Math.max(minVal, Math.min(maxVal, this.z)); + this.w = Math.max(minVal, Math.min(maxVal, this.w)); + return this; + }; + + _proto.clampLength = function clampLength(min, max) { + var length = this.length(); + return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); + }; + + _proto.floor = function floor() { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + this.z = Math.floor(this.z); + this.w = Math.floor(this.w); + return this; + }; + + _proto.ceil = function ceil() { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + this.z = Math.ceil(this.z); + this.w = Math.ceil(this.w); + return this; + }; + + _proto.round = function round() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + this.z = Math.round(this.z); + this.w = Math.round(this.w); + return this; + }; + + _proto.roundToZero = function roundToZero() { + this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); + this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); + this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); + this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); + return this; + }; + + _proto.negate = function negate() { + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + this.w = -this.w; + return this; + }; + + _proto.dot = function dot(v) { + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + }; + + _proto.lengthSq = function lengthSq() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + }; + + _proto.length = function length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + }; + + _proto.manhattanLength = function manhattanLength() { + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); + }; + + _proto.normalize = function normalize() { + return this.divideScalar(this.length() || 1); + }; + + _proto.setLength = function setLength(length) { + return this.normalize().multiplyScalar(length); + }; + + _proto.lerp = function lerp(v, alpha) { + this.x += (v.x - this.x) * alpha; + this.y += (v.y - this.y) * alpha; + this.z += (v.z - this.z) * alpha; + this.w += (v.w - this.w) * alpha; + return this; + }; + + _proto.lerpVectors = function lerpVectors(v1, v2, alpha) { + this.x = v1.x + (v2.x - v1.x) * alpha; + this.y = v1.y + (v2.y - v1.y) * alpha; + this.z = v1.z + (v2.z - v1.z) * alpha; + this.w = v1.w + (v2.w - v1.w) * alpha; + return this; + }; + + _proto.equals = function equals(v) { + return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + this.x = array[offset]; + this.y = array[offset + 1]; + this.z = array[offset + 2]; + this.w = array[offset + 3]; + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this.x; + array[offset + 1] = this.y; + array[offset + 2] = this.z; + array[offset + 3] = this.w; + return array; + }; + + _proto.fromBufferAttribute = function fromBufferAttribute(attribute, index, offset) { + if (offset !== undefined) { + console.warn('THREE.Vector4: offset has been removed from .fromBufferAttribute().'); + } + + this.x = attribute.getX(index); + this.y = attribute.getY(index); + this.z = attribute.getZ(index); + this.w = attribute.getW(index); + return this; + }; + + _proto.random = function random() { + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); + this.w = Math.random(); + return this; + }; + + _createClass(Vector4, [{ + key: "width", + get: function get() { + return this.z; + }, + set: function set(value) { + this.z = value; + } + }, { + key: "height", + get: function get() { + return this.w; + }, + set: function set(value) { + this.w = value; + } + }]); + + return Vector4; + }(); + + Vector4.prototype.isVector4 = true; + + /* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers + */ + + var WebGLRenderTarget = /*#__PURE__*/function (_EventDispatcher) { + _inheritsLoose(WebGLRenderTarget, _EventDispatcher); + + function WebGLRenderTarget(width, height, options) { + var _this; + + _this = _EventDispatcher.call(this) || this; + _this.width = width; + _this.height = height; + _this.depth = 1; + _this.scissor = new Vector4(0, 0, width, height); + _this.scissorTest = false; + _this.viewport = new Vector4(0, 0, width, height); + options = options || {}; + _this.texture = new Texture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); + _this.texture.image = {}; + _this.texture.image.width = width; + _this.texture.image.height = height; + _this.texture.image.depth = 1; + _this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + _this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; + _this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + _this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; + _this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; + return _this; + } + + var _proto = WebGLRenderTarget.prototype; + + _proto.setTexture = function setTexture(texture) { + texture.image = { + width: this.width, + height: this.height, + depth: this.depth + }; + this.texture = texture; + }; + + _proto.setSize = function setSize(width, height, depth) { + if (depth === void 0) { + depth = 1; + } + + if (this.width !== width || this.height !== height || this.depth !== depth) { + this.width = width; + this.height = height; + this.depth = depth; + this.texture.image.width = width; + this.texture.image.height = height; + this.texture.image.depth = depth; + this.dispose(); + } + + this.viewport.set(0, 0, width, height); + this.scissor.set(0, 0, width, height); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.copy = function copy(source) { + this.width = source.width; + this.height = source.height; + this.depth = source.depth; + this.viewport.copy(source.viewport); + this.texture = source.texture.clone(); + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; + return this; + }; + + _proto.dispose = function dispose() { + this.dispatchEvent({ + type: 'dispose' + }); + }; + + return WebGLRenderTarget; + }(EventDispatcher); + + WebGLRenderTarget.prototype.isWebGLRenderTarget = true; + + var WebGLMultisampleRenderTarget = /*#__PURE__*/function (_WebGLRenderTarget) { + _inheritsLoose(WebGLMultisampleRenderTarget, _WebGLRenderTarget); + + function WebGLMultisampleRenderTarget(width, height, options) { + var _this; + + _this = _WebGLRenderTarget.call(this, width, height, options) || this; + _this.samples = 4; + return _this; + } + + var _proto = WebGLMultisampleRenderTarget.prototype; + + _proto.copy = function copy(source) { + _WebGLRenderTarget.prototype.copy.call(this, source); + + this.samples = source.samples; + return this; + }; + + return WebGLMultisampleRenderTarget; + }(WebGLRenderTarget); + + WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; + + var Quaternion = /*#__PURE__*/function () { + function Quaternion(x, y, z, w) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + if (z === void 0) { + z = 0; + } + + if (w === void 0) { + w = 1; + } + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + } + + Quaternion.slerp = function slerp(qa, qb, qm, t) { + return qm.copy(qa).slerp(qb, t); + }; + + Quaternion.slerpFlat = function slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { + // fuzz-free, array-based Quaternion SLERP operation + var x0 = src0[srcOffset0 + 0], + y0 = src0[srcOffset0 + 1], + z0 = src0[srcOffset0 + 2], + w0 = src0[srcOffset0 + 3]; + var x1 = src1[srcOffset1 + 0], + y1 = src1[srcOffset1 + 1], + z1 = src1[srcOffset1 + 2], + w1 = src1[srcOffset1 + 3]; + + if (t === 0) { + dst[dstOffset + 0] = x0; + dst[dstOffset + 1] = y0; + dst[dstOffset + 2] = z0; + dst[dstOffset + 3] = w0; + return; + } + + if (t === 1) { + dst[dstOffset + 0] = x1; + dst[dstOffset + 1] = y1; + dst[dstOffset + 2] = z1; + dst[dstOffset + 3] = w1; + return; + } + + if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { + var s = 1 - t; + var cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + dir = cos >= 0 ? 1 : -1, + sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: + + if (sqrSin > Number.EPSILON) { + var sin = Math.sqrt(sqrSin), + len = Math.atan2(sin, cos * dir); + s = Math.sin(s * len) / sin; + t = Math.sin(t * len) / sin; + } + + var tDir = t * dir; + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: + + if (s === 1 - t) { + var f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; + } + } + + dst[dstOffset] = x0; + dst[dstOffset + 1] = y0; + dst[dstOffset + 2] = z0; + dst[dstOffset + 3] = w0; + }; + + Quaternion.multiplyQuaternionsFlat = function multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { + var x0 = src0[srcOffset0]; + var y0 = src0[srcOffset0 + 1]; + var z0 = src0[srcOffset0 + 2]; + var w0 = src0[srcOffset0 + 3]; + var x1 = src1[srcOffset1]; + var y1 = src1[srcOffset1 + 1]; + var z1 = src1[srcOffset1 + 2]; + var w1 = src1[srcOffset1 + 3]; + dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; + dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; + dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; + dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; + return dst; + }; + + var _proto = Quaternion.prototype; + + _proto.set = function set(x, y, z, w) { + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this._onChangeCallback(); + + return this; + }; + + _proto.clone = function clone() { + return new this.constructor(this._x, this._y, this._z, this._w); + }; + + _proto.copy = function copy(quaternion) { + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this._onChangeCallback(); + + return this; + }; + + _proto.setFromEuler = function setFromEuler(euler, update) { + if (!(euler && euler.isEuler)) { + throw new Error('THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.'); + } + + var x = euler._x, + y = euler._y, + z = euler._z, + order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var cos = Math.cos; + var sin = Math.sin; + var c1 = cos(x / 2); + var c2 = cos(y / 2); + var c3 = cos(z / 2); + var s1 = sin(x / 2); + var s2 = sin(y / 2); + var s3 = sin(z / 2); + + switch (order) { + case 'XYZ': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; + + case 'YXZ': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; + + case 'ZXY': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; + + case 'ZYX': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; + + case 'YZX': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; + + case 'XZY': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; + + default: + console.warn('THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order); + } + + if (update !== false) this._onChangeCallback(); + return this; + }; + + _proto.setFromAxisAngle = function setFromAxisAngle(axis, angle) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + // assumes axis is normalized + var halfAngle = angle / 2, + s = Math.sin(halfAngle); + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos(halfAngle); + + this._onChangeCallback(); + + return this; + }; + + _proto.setFromRotationMatrix = function setFromRotationMatrix(m) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + var te = m.elements, + m11 = te[0], + m12 = te[4], + m13 = te[8], + m21 = te[1], + m22 = te[5], + m23 = te[9], + m31 = te[2], + m32 = te[6], + m33 = te[10], + trace = m11 + m22 + m33; + + if (trace > 0) { + var s = 0.5 / Math.sqrt(trace + 1.0); + this._w = 0.25 / s; + this._x = (m32 - m23) * s; + this._y = (m13 - m31) * s; + this._z = (m21 - m12) * s; + } else if (m11 > m22 && m11 > m33) { + var _s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); + + this._w = (m32 - m23) / _s; + this._x = 0.25 * _s; + this._y = (m12 + m21) / _s; + this._z = (m13 + m31) / _s; + } else if (m22 > m33) { + var _s2 = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); + + this._w = (m13 - m31) / _s2; + this._x = (m12 + m21) / _s2; + this._y = 0.25 * _s2; + this._z = (m23 + m32) / _s2; + } else { + var _s3 = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); + + this._w = (m21 - m12) / _s3; + this._x = (m13 + m31) / _s3; + this._y = (m23 + m32) / _s3; + this._z = 0.25 * _s3; + } + + this._onChangeCallback(); + + return this; + }; + + _proto.setFromUnitVectors = function setFromUnitVectors(vFrom, vTo) { + // assumes direction vectors vFrom and vTo are normalized + var EPS = 0.000001; + var r = vFrom.dot(vTo) + 1; + + if (r < EPS) { + r = 0; + + if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { + this._x = -vFrom.y; + this._y = vFrom.x; + this._z = 0; + this._w = r; + } else { + this._x = 0; + this._y = -vFrom.z; + this._z = vFrom.y; + this._w = r; + } + } else { + // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 + this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; + this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; + this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; + this._w = r; + } + + return this.normalize(); + }; + + _proto.angleTo = function angleTo(q) { + return 2 * Math.acos(Math.abs(MathUtils.clamp(this.dot(q), -1, 1))); + }; + + _proto.rotateTowards = function rotateTowards(q, step) { + var angle = this.angleTo(q); + if (angle === 0) return this; + var t = Math.min(1, step / angle); + this.slerp(q, t); + return this; + }; + + _proto.identity = function identity() { + return this.set(0, 0, 0, 1); + }; + + _proto.invert = function invert() { + // quaternion is assumed to have unit length + return this.conjugate(); + }; + + _proto.conjugate = function conjugate() { + this._x *= -1; + this._y *= -1; + this._z *= -1; + + this._onChangeCallback(); + + return this; + }; + + _proto.dot = function dot(v) { + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + }; + + _proto.lengthSq = function lengthSq() { + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + }; + + _proto.length = function length() { + return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); + }; + + _proto.normalize = function normalize() { + var l = this.length(); + + if (l === 0) { + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + } else { + l = 1 / l; + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + } + + this._onChangeCallback(); + + return this; + }; + + _proto.multiply = function multiply(q, p) { + if (p !== undefined) { + console.warn('THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.'); + return this.multiplyQuaternions(q, p); + } + + return this.multiplyQuaternions(this, q); + }; + + _proto.premultiply = function premultiply(q) { + return this.multiplyQuaternions(q, this); + }; + + _proto.multiplyQuaternions = function multiplyQuaternions(a, b) { + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + var qax = a._x, + qay = a._y, + qaz = a._z, + qaw = a._w; + var qbx = b._x, + qby = b._y, + qbz = b._z, + qbw = b._w; + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this._onChangeCallback(); + + return this; + }; + + _proto.slerp = function slerp(qb, t) { + if (t === 0) return this; + if (t === 1) return this.copy(qb); + var x = this._x, + y = this._y, + z = this._z, + w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if (cosHalfTheta < 0) { + this._w = -qb._w; + this._x = -qb._x; + this._y = -qb._y; + this._z = -qb._z; + cosHalfTheta = -cosHalfTheta; + } else { + this.copy(qb); + } + + if (cosHalfTheta >= 1.0) { + this._w = w; + this._x = x; + this._y = y; + this._z = z; + return this; + } + + var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + + if (sqrSinHalfTheta <= Number.EPSILON) { + var s = 1 - t; + this._w = s * w + t * this._w; + this._x = s * x + t * this._x; + this._y = s * y + t * this._y; + this._z = s * z + t * this._z; + this.normalize(); + + this._onChangeCallback(); + + return this; + } + + var sinHalfTheta = Math.sqrt(sqrSinHalfTheta); + var halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); + var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + this._w = w * ratioA + this._w * ratioB; + this._x = x * ratioA + this._x * ratioB; + this._y = y * ratioA + this._y * ratioB; + this._z = z * ratioA + this._z * ratioB; + + this._onChangeCallback(); + + return this; + }; + + _proto.equals = function equals(quaternion) { + return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + this._x = array[offset]; + this._y = array[offset + 1]; + this._z = array[offset + 2]; + this._w = array[offset + 3]; + + this._onChangeCallback(); + + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this._x; + array[offset + 1] = this._y; + array[offset + 2] = this._z; + array[offset + 3] = this._w; + return array; + }; + + _proto.fromBufferAttribute = function fromBufferAttribute(attribute, index) { + this._x = attribute.getX(index); + this._y = attribute.getY(index); + this._z = attribute.getZ(index); + this._w = attribute.getW(index); + return this; + }; + + _proto._onChange = function _onChange(callback) { + this._onChangeCallback = callback; + return this; + }; + + _proto._onChangeCallback = function _onChangeCallback() {}; + + _createClass(Quaternion, [{ + key: "x", + get: function get() { + return this._x; + }, + set: function set(value) { + this._x = value; + + this._onChangeCallback(); + } + }, { + key: "y", + get: function get() { + return this._y; + }, + set: function set(value) { + this._y = value; + + this._onChangeCallback(); + } + }, { + key: "z", + get: function get() { + return this._z; + }, + set: function set(value) { + this._z = value; + + this._onChangeCallback(); + } + }, { + key: "w", + get: function get() { + return this._w; + }, + set: function set(value) { + this._w = value; + + this._onChangeCallback(); + } + }]); + + return Quaternion; + }(); + + Quaternion.prototype.isQuaternion = true; + + var Vector3 = /*#__PURE__*/function () { + function Vector3(x, y, z) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + if (z === void 0) { + z = 0; + } + + this.x = x; + this.y = y; + this.z = z; + } + + var _proto = Vector3.prototype; + + _proto.set = function set(x, y, z) { + if (z === undefined) z = this.z; // sprite.scale.set(x,y) + + this.x = x; + this.y = y; + this.z = z; + return this; + }; + + _proto.setScalar = function setScalar(scalar) { + this.x = scalar; + this.y = scalar; + this.z = scalar; + return this; + }; + + _proto.setX = function setX(x) { + this.x = x; + return this; + }; + + _proto.setY = function setY(y) { + this.y = y; + return this; + }; + + _proto.setZ = function setZ(z) { + this.z = z; + return this; + }; + + _proto.setComponent = function setComponent(index, value) { + switch (index) { + case 0: + this.x = value; + break; + + case 1: + this.y = value; + break; + + case 2: + this.z = value; + break; + + default: + throw new Error('index is out of range: ' + index); + } + + return this; + }; + + _proto.getComponent = function getComponent(index) { + switch (index) { + case 0: + return this.x; + + case 1: + return this.y; + + case 2: + return this.z; + + default: + throw new Error('index is out of range: ' + index); + } + }; + + _proto.clone = function clone() { + return new this.constructor(this.x, this.y, this.z); + }; + + _proto.copy = function copy(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + return this; + }; + + _proto.add = function add(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.'); + return this.addVectors(v, w); + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + }; + + _proto.addScalar = function addScalar(s) { + this.x += s; + this.y += s; + this.z += s; + return this; + }; + + _proto.addVectors = function addVectors(a, b) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + return this; + }; + + _proto.addScaledVector = function addScaledVector(v, s) { + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + return this; + }; + + _proto.sub = function sub(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.'); + return this.subVectors(v, w); + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + return this; + }; + + _proto.subScalar = function subScalar(s) { + this.x -= s; + this.y -= s; + this.z -= s; + return this; + }; + + _proto.subVectors = function subVectors(a, b) { + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + return this; + }; + + _proto.multiply = function multiply(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.'); + return this.multiplyVectors(v, w); + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + return this; + }; + + _proto.multiplyVectors = function multiplyVectors(a, b) { + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + return this; + }; + + _proto.applyEuler = function applyEuler(euler) { + if (!(euler && euler.isEuler)) { + console.error('THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.'); + } + + return this.applyQuaternion(_quaternion.setFromEuler(euler)); + }; + + _proto.applyAxisAngle = function applyAxisAngle(axis, angle) { + return this.applyQuaternion(_quaternion.setFromAxisAngle(axis, angle)); + }; + + _proto.applyMatrix3 = function applyMatrix3(m) { + var x = this.x, + y = this.y, + z = this.z; + var e = m.elements; + this.x = e[0] * x + e[3] * y + e[6] * z; + this.y = e[1] * x + e[4] * y + e[7] * z; + this.z = e[2] * x + e[5] * y + e[8] * z; + return this; + }; + + _proto.applyNormalMatrix = function applyNormalMatrix(m) { + return this.applyMatrix3(m).normalize(); + }; + + _proto.applyMatrix4 = function applyMatrix4(m) { + var x = this.x, + y = this.y, + z = this.z; + var e = m.elements; + var w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); + this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; + this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; + this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; + return this; + }; + + _proto.applyQuaternion = function applyQuaternion(q) { + var x = this.x, + y = this.y, + z = this.z; + var qx = q.x, + qy = q.y, + qz = q.z, + qw = q.w; // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat + + this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return this; + }; + + _proto.project = function project(camera) { + return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); + }; + + _proto.unproject = function unproject(camera) { + return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); + }; + + _proto.transformDirection = function transformDirection(m) { + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + var x = this.x, + y = this.y, + z = this.z; + var e = m.elements; + this.x = e[0] * x + e[4] * y + e[8] * z; + this.y = e[1] * x + e[5] * y + e[9] * z; + this.z = e[2] * x + e[6] * y + e[10] * z; + return this.normalize(); + }; + + _proto.divide = function divide(v) { + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + return this; + }; + + _proto.divideScalar = function divideScalar(scalar) { + return this.multiplyScalar(1 / scalar); + }; + + _proto.min = function min(v) { + this.x = Math.min(this.x, v.x); + this.y = Math.min(this.y, v.y); + this.z = Math.min(this.z, v.z); + return this; + }; + + _proto.max = function max(v) { + this.x = Math.max(this.x, v.x); + this.y = Math.max(this.y, v.y); + this.z = Math.max(this.z, v.z); + return this; + }; + + _proto.clamp = function clamp(min, max) { + // assumes min < max, componentwise + this.x = Math.max(min.x, Math.min(max.x, this.x)); + this.y = Math.max(min.y, Math.min(max.y, this.y)); + this.z = Math.max(min.z, Math.min(max.z, this.z)); + return this; + }; + + _proto.clampScalar = function clampScalar(minVal, maxVal) { + this.x = Math.max(minVal, Math.min(maxVal, this.x)); + this.y = Math.max(minVal, Math.min(maxVal, this.y)); + this.z = Math.max(minVal, Math.min(maxVal, this.z)); + return this; + }; + + _proto.clampLength = function clampLength(min, max) { + var length = this.length(); + return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); + }; + + _proto.floor = function floor() { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + this.z = Math.floor(this.z); + return this; + }; + + _proto.ceil = function ceil() { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + this.z = Math.ceil(this.z); + return this; + }; + + _proto.round = function round() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + this.z = Math.round(this.z); + return this; + }; + + _proto.roundToZero = function roundToZero() { + this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); + this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); + this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); + return this; + }; + + _proto.negate = function negate() { + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + return this; + }; + + _proto.dot = function dot(v) { + return this.x * v.x + this.y * v.y + this.z * v.z; + } // TODO lengthSquared? + ; + + _proto.lengthSq = function lengthSq() { + return this.x * this.x + this.y * this.y + this.z * this.z; + }; + + _proto.length = function length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); + }; + + _proto.manhattanLength = function manhattanLength() { + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); + }; + + _proto.normalize = function normalize() { + return this.divideScalar(this.length() || 1); + }; + + _proto.setLength = function setLength(length) { + return this.normalize().multiplyScalar(length); + }; + + _proto.lerp = function lerp(v, alpha) { + this.x += (v.x - this.x) * alpha; + this.y += (v.y - this.y) * alpha; + this.z += (v.z - this.z) * alpha; + return this; + }; + + _proto.lerpVectors = function lerpVectors(v1, v2, alpha) { + this.x = v1.x + (v2.x - v1.x) * alpha; + this.y = v1.y + (v2.y - v1.y) * alpha; + this.z = v1.z + (v2.z - v1.z) * alpha; + return this; + }; + + _proto.cross = function cross(v, w) { + if (w !== undefined) { + console.warn('THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.'); + return this.crossVectors(v, w); + } + + return this.crossVectors(this, v); + }; + + _proto.crossVectors = function crossVectors(a, b) { + var ax = a.x, + ay = a.y, + az = a.z; + var bx = b.x, + by = b.y, + bz = b.z; + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + return this; + }; + + _proto.projectOnVector = function projectOnVector(v) { + var denominator = v.lengthSq(); + if (denominator === 0) return this.set(0, 0, 0); + var scalar = v.dot(this) / denominator; + return this.copy(v).multiplyScalar(scalar); + }; + + _proto.projectOnPlane = function projectOnPlane(planeNormal) { + _vector.copy(this).projectOnVector(planeNormal); + + return this.sub(_vector); + }; + + _proto.reflect = function reflect(normal) { + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + return this.sub(_vector.copy(normal).multiplyScalar(2 * this.dot(normal))); + }; + + _proto.angleTo = function angleTo(v) { + var denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); + if (denominator === 0) return Math.PI / 2; + var theta = this.dot(v) / denominator; // clamp, to handle numerical problems + + return Math.acos(MathUtils.clamp(theta, -1, 1)); + }; + + _proto.distanceTo = function distanceTo(v) { + return Math.sqrt(this.distanceToSquared(v)); + }; + + _proto.distanceToSquared = function distanceToSquared(v) { + var dx = this.x - v.x, + dy = this.y - v.y, + dz = this.z - v.z; + return dx * dx + dy * dy + dz * dz; + }; + + _proto.manhattanDistanceTo = function manhattanDistanceTo(v) { + return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); + }; + + _proto.setFromSpherical = function setFromSpherical(s) { + return this.setFromSphericalCoords(s.radius, s.phi, s.theta); + }; + + _proto.setFromSphericalCoords = function setFromSphericalCoords(radius, phi, theta) { + var sinPhiRadius = Math.sin(phi) * radius; + this.x = sinPhiRadius * Math.sin(theta); + this.y = Math.cos(phi) * radius; + this.z = sinPhiRadius * Math.cos(theta); + return this; + }; + + _proto.setFromCylindrical = function setFromCylindrical(c) { + return this.setFromCylindricalCoords(c.radius, c.theta, c.y); + }; + + _proto.setFromCylindricalCoords = function setFromCylindricalCoords(radius, theta, y) { + this.x = radius * Math.sin(theta); + this.y = y; + this.z = radius * Math.cos(theta); + return this; + }; + + _proto.setFromMatrixPosition = function setFromMatrixPosition(m) { + var e = m.elements; + this.x = e[12]; + this.y = e[13]; + this.z = e[14]; + return this; + }; + + _proto.setFromMatrixScale = function setFromMatrixScale(m) { + var sx = this.setFromMatrixColumn(m, 0).length(); + var sy = this.setFromMatrixColumn(m, 1).length(); + var sz = this.setFromMatrixColumn(m, 2).length(); + this.x = sx; + this.y = sy; + this.z = sz; + return this; + }; + + _proto.setFromMatrixColumn = function setFromMatrixColumn(m, index) { + return this.fromArray(m.elements, index * 4); + }; + + _proto.setFromMatrix3Column = function setFromMatrix3Column(m, index) { + return this.fromArray(m.elements, index * 3); + }; + + _proto.equals = function equals(v) { + return v.x === this.x && v.y === this.y && v.z === this.z; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + this.x = array[offset]; + this.y = array[offset + 1]; + this.z = array[offset + 2]; + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this.x; + array[offset + 1] = this.y; + array[offset + 2] = this.z; + return array; + }; + + _proto.fromBufferAttribute = function fromBufferAttribute(attribute, index, offset) { + if (offset !== undefined) { + console.warn('THREE.Vector3: offset has been removed from .fromBufferAttribute().'); + } + + this.x = attribute.getX(index); + this.y = attribute.getY(index); + this.z = attribute.getZ(index); + return this; + }; + + _proto.random = function random() { + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); + return this; + }; + + return Vector3; + }(); + + Vector3.prototype.isVector3 = true; + + var _vector = /*@__PURE__*/new Vector3(); + + var _quaternion = /*@__PURE__*/new Quaternion(); + + var Box3 = /*#__PURE__*/function () { + function Box3(min, max) { + if (min === void 0) { + min = new Vector3(+Infinity, +Infinity, +Infinity); + } + + if (max === void 0) { + max = new Vector3(-Infinity, -Infinity, -Infinity); + } + + this.min = min; + this.max = max; + } + + var _proto = Box3.prototype; + + _proto.set = function set(min, max) { + this.min.copy(min); + this.max.copy(max); + return this; + }; + + _proto.setFromArray = function setFromArray(array) { + var minX = +Infinity; + var minY = +Infinity; + var minZ = +Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + var maxZ = -Infinity; + + for (var i = 0, l = array.length; i < l; i += 3) { + var x = array[i]; + var y = array[i + 1]; + var z = array[i + 2]; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (z < minZ) minZ = z; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + if (z > maxZ) maxZ = z; + } + + this.min.set(minX, minY, minZ); + this.max.set(maxX, maxY, maxZ); + return this; + }; + + _proto.setFromBufferAttribute = function setFromBufferAttribute(attribute) { + var minX = +Infinity; + var minY = +Infinity; + var minZ = +Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + var maxZ = -Infinity; + + for (var i = 0, l = attribute.count; i < l; i++) { + var x = attribute.getX(i); + var y = attribute.getY(i); + var z = attribute.getZ(i); + if (x < minX) minX = x; + if (y < minY) minY = y; + if (z < minZ) minZ = z; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + if (z > maxZ) maxZ = z; + } + + this.min.set(minX, minY, minZ); + this.max.set(maxX, maxY, maxZ); + return this; + }; + + _proto.setFromPoints = function setFromPoints(points) { + this.makeEmpty(); + + for (var i = 0, il = points.length; i < il; i++) { + this.expandByPoint(points[i]); + } + + return this; + }; + + _proto.setFromCenterAndSize = function setFromCenterAndSize(center, size) { + var halfSize = _vector$1.copy(size).multiplyScalar(0.5); + + this.min.copy(center).sub(halfSize); + this.max.copy(center).add(halfSize); + return this; + }; + + _proto.setFromObject = function setFromObject(object) { + this.makeEmpty(); + return this.expandByObject(object); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.copy = function copy(box) { + this.min.copy(box.min); + this.max.copy(box.max); + return this; + }; + + _proto.makeEmpty = function makeEmpty() { + this.min.x = this.min.y = this.min.z = +Infinity; + this.max.x = this.max.y = this.max.z = -Infinity; + return this; + }; + + _proto.isEmpty = function isEmpty() { + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; + }; + + _proto.getCenter = function getCenter(target) { + if (target === undefined) { + console.warn('THREE.Box3: .getCenter() target is now required'); + target = new Vector3(); + } + + return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); + }; + + _proto.getSize = function getSize(target) { + if (target === undefined) { + console.warn('THREE.Box3: .getSize() target is now required'); + target = new Vector3(); + } + + return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); + }; + + _proto.expandByPoint = function expandByPoint(point) { + this.min.min(point); + this.max.max(point); + return this; + }; + + _proto.expandByVector = function expandByVector(vector) { + this.min.sub(vector); + this.max.add(vector); + return this; + }; + + _proto.expandByScalar = function expandByScalar(scalar) { + this.min.addScalar(-scalar); + this.max.addScalar(scalar); + return this; + }; + + _proto.expandByObject = function expandByObject(object) { + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms + object.updateWorldMatrix(false, false); + var geometry = object.geometry; + + if (geometry !== undefined) { + if (geometry.boundingBox === null) { + geometry.computeBoundingBox(); + } + + _box.copy(geometry.boundingBox); + + _box.applyMatrix4(object.matrixWorld); + + this.union(_box); + } + + var children = object.children; + + for (var i = 0, l = children.length; i < l; i++) { + this.expandByObject(children[i]); + } + + return this; + }; + + _proto.containsPoint = function containsPoint(point) { + return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; + }; + + _proto.containsBox = function containsBox(box) { + return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; + }; + + _proto.getParameter = function getParameter(point, target) { + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + if (target === undefined) { + console.warn('THREE.Box3: .getParameter() target is now required'); + target = new Vector3(); + } + + return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); + }; + + _proto.intersectsBox = function intersectsBox(box) { + // using 6 splitting planes to rule out intersections. + return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; + }; + + _proto.intersectsSphere = function intersectsSphere(sphere) { + // Find the point on the AABB closest to the sphere center. + this.clampPoint(sphere.center, _vector$1); // If that point is inside the sphere, the AABB and sphere intersect. + + return _vector$1.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; + }; + + _proto.intersectsPlane = function intersectsPlane(plane) { + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. + var min, max; + + if (plane.normal.x > 0) { + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; + } else { + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; + } + + if (plane.normal.y > 0) { + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; + } else { + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; + } + + if (plane.normal.z > 0) { + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; + } else { + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; + } + + return min <= -plane.constant && max >= -plane.constant; + }; + + _proto.intersectsTriangle = function intersectsTriangle(triangle) { + if (this.isEmpty()) { + return false; + } // compute box center and extents + + + this.getCenter(_center); + + _extents.subVectors(this.max, _center); // translate triangle to aabb origin + + + _v0.subVectors(triangle.a, _center); + + _v1.subVectors(triangle.b, _center); + + _v2.subVectors(triangle.c, _center); // compute edge vectors for triangle + + + _f0.subVectors(_v1, _v0); + + _f1.subVectors(_v2, _v1); + + _f2.subVectors(_v0, _v2); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb + // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation + // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) + + + var axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; + + if (!satForAxes(axes, _v0, _v1, _v2, _extents)) { + return false; + } // test 3 face normals from the aabb + + + axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; + + if (!satForAxes(axes, _v0, _v1, _v2, _extents)) { + return false; + } // finally testing the face normal of the triangle + // use already existing triangle edge vectors here + + + _triangleNormal.crossVectors(_f0, _f1); + + axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; + return satForAxes(axes, _v0, _v1, _v2, _extents); + }; + + _proto.clampPoint = function clampPoint(point, target) { + if (target === undefined) { + console.warn('THREE.Box3: .clampPoint() target is now required'); + target = new Vector3(); + } + + return target.copy(point).clamp(this.min, this.max); + }; + + _proto.distanceToPoint = function distanceToPoint(point) { + var clampedPoint = _vector$1.copy(point).clamp(this.min, this.max); + + return clampedPoint.sub(point).length(); + }; + + _proto.getBoundingSphere = function getBoundingSphere(target) { + if (target === undefined) { + console.error('THREE.Box3: .getBoundingSphere() target is now required'); //target = new Sphere(); // removed to avoid cyclic dependency + } + + this.getCenter(target.center); + target.radius = this.getSize(_vector$1).length() * 0.5; + return target; + }; + + _proto.intersect = function intersect(box) { + this.min.max(box.min); + this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. + + if (this.isEmpty()) this.makeEmpty(); + return this; + }; + + _proto.union = function union(box) { + this.min.min(box.min); + this.max.max(box.max); + return this; + }; + + _proto.applyMatrix4 = function applyMatrix4(matrix) { + // transform of empty box is an empty box. + if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below + + _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 + + + _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 + + + _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 + + + _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 + + + _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 + + + _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 + + + _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 + + + _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 + + + this.setFromPoints(_points); + return this; + }; + + _proto.translate = function translate(offset) { + this.min.add(offset); + this.max.add(offset); + return this; + }; + + _proto.equals = function equals(box) { + return box.min.equals(this.min) && box.max.equals(this.max); + }; + + return Box3; + }(); + + Box3.prototype.isBox3 = true; + var _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; + + var _vector$1 = /*@__PURE__*/new Vector3(); + + var _box = /*@__PURE__*/new Box3(); // triangle centered vertices + + + var _v0 = /*@__PURE__*/new Vector3(); + + var _v1 = /*@__PURE__*/new Vector3(); + + var _v2 = /*@__PURE__*/new Vector3(); // triangle edge vectors + + + var _f0 = /*@__PURE__*/new Vector3(); + + var _f1 = /*@__PURE__*/new Vector3(); + + var _f2 = /*@__PURE__*/new Vector3(); + + var _center = /*@__PURE__*/new Vector3(); + + var _extents = /*@__PURE__*/new Vector3(); + + var _triangleNormal = /*@__PURE__*/new Vector3(); + + var _testAxis = /*@__PURE__*/new Vector3(); + + function satForAxes(axes, v0, v1, v2, extents) { + for (var i = 0, j = axes.length - 3; i <= j; i += 3) { + _testAxis.fromArray(axes, i); // project the aabb onto the seperating axis + + + var r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the seperating axis + + var p0 = v0.dot(_testAxis); + var p1 = v1.dot(_testAxis); + var p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r + + if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is seperating and we can exit + return false; + } + } + + return true; + } + + var _box$1 = /*@__PURE__*/new Box3(); + + var Sphere = /*#__PURE__*/function () { + function Sphere(center, radius) { + if (center === void 0) { + center = new Vector3(); + } + + if (radius === void 0) { + radius = -1; + } + + this.center = center; + this.radius = radius; + } + + var _proto = Sphere.prototype; + + _proto.set = function set(center, radius) { + this.center.copy(center); + this.radius = radius; + return this; + }; + + _proto.setFromPoints = function setFromPoints(points, optionalCenter) { + var center = this.center; + + if (optionalCenter !== undefined) { + center.copy(optionalCenter); + } else { + _box$1.setFromPoints(points).getCenter(center); + } + + var maxRadiusSq = 0; + + for (var i = 0, il = points.length; i < il; i++) { + maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); + } + + this.radius = Math.sqrt(maxRadiusSq); + return this; + }; + + _proto.copy = function copy(sphere) { + this.center.copy(sphere.center); + this.radius = sphere.radius; + return this; + }; + + _proto.isEmpty = function isEmpty() { + return this.radius < 0; + }; + + _proto.makeEmpty = function makeEmpty() { + this.center.set(0, 0, 0); + this.radius = -1; + return this; + }; + + _proto.containsPoint = function containsPoint(point) { + return point.distanceToSquared(this.center) <= this.radius * this.radius; + }; + + _proto.distanceToPoint = function distanceToPoint(point) { + return point.distanceTo(this.center) - this.radius; + }; + + _proto.intersectsSphere = function intersectsSphere(sphere) { + var radiusSum = this.radius + sphere.radius; + return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; + }; + + _proto.intersectsBox = function intersectsBox(box) { + return box.intersectsSphere(this); + }; + + _proto.intersectsPlane = function intersectsPlane(plane) { + return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; + }; + + _proto.clampPoint = function clampPoint(point, target) { + var deltaLengthSq = this.center.distanceToSquared(point); + + if (target === undefined) { + console.warn('THREE.Sphere: .clampPoint() target is now required'); + target = new Vector3(); + } + + target.copy(point); + + if (deltaLengthSq > this.radius * this.radius) { + target.sub(this.center).normalize(); + target.multiplyScalar(this.radius).add(this.center); + } + + return target; + }; + + _proto.getBoundingBox = function getBoundingBox(target) { + if (target === undefined) { + console.warn('THREE.Sphere: .getBoundingBox() target is now required'); + target = new Box3(); + } + + if (this.isEmpty()) { + // Empty sphere produces empty bounding box + target.makeEmpty(); + return target; + } + + target.set(this.center, this.center); + target.expandByScalar(this.radius); + return target; + }; + + _proto.applyMatrix4 = function applyMatrix4(matrix) { + this.center.applyMatrix4(matrix); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + return this; + }; + + _proto.translate = function translate(offset) { + this.center.add(offset); + return this; + }; + + _proto.equals = function equals(sphere) { + return sphere.center.equals(this.center) && sphere.radius === this.radius; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Sphere; + }(); + + var _vector$2 = /*@__PURE__*/new Vector3(); + + var _segCenter = /*@__PURE__*/new Vector3(); + + var _segDir = /*@__PURE__*/new Vector3(); + + var _diff = /*@__PURE__*/new Vector3(); + + var _edge1 = /*@__PURE__*/new Vector3(); + + var _edge2 = /*@__PURE__*/new Vector3(); + + var _normal = /*@__PURE__*/new Vector3(); + + var Ray = /*#__PURE__*/function () { + function Ray(origin, direction) { + if (origin === void 0) { + origin = new Vector3(); + } + + if (direction === void 0) { + direction = new Vector3(0, 0, -1); + } + + this.origin = origin; + this.direction = direction; + } + + var _proto = Ray.prototype; + + _proto.set = function set(origin, direction) { + this.origin.copy(origin); + this.direction.copy(direction); + return this; + }; + + _proto.copy = function copy(ray) { + this.origin.copy(ray.origin); + this.direction.copy(ray.direction); + return this; + }; + + _proto.at = function at(t, target) { + if (target === undefined) { + console.warn('THREE.Ray: .at() target is now required'); + target = new Vector3(); + } + + return target.copy(this.direction).multiplyScalar(t).add(this.origin); + }; + + _proto.lookAt = function lookAt(v) { + this.direction.copy(v).sub(this.origin).normalize(); + return this; + }; + + _proto.recast = function recast(t) { + this.origin.copy(this.at(t, _vector$2)); + return this; + }; + + _proto.closestPointToPoint = function closestPointToPoint(point, target) { + if (target === undefined) { + console.warn('THREE.Ray: .closestPointToPoint() target is now required'); + target = new Vector3(); + } + + target.subVectors(point, this.origin); + var directionDistance = target.dot(this.direction); + + if (directionDistance < 0) { + return target.copy(this.origin); + } + + return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); + }; + + _proto.distanceToPoint = function distanceToPoint(point) { + return Math.sqrt(this.distanceSqToPoint(point)); + }; + + _proto.distanceSqToPoint = function distanceSqToPoint(point) { + var directionDistance = _vector$2.subVectors(point, this.origin).dot(this.direction); // point behind the ray + + + if (directionDistance < 0) { + return this.origin.distanceToSquared(point); + } + + _vector$2.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); + + return _vector$2.distanceToSquared(point); + }; + + _proto.distanceSqToSegment = function distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + _segCenter.copy(v0).add(v1).multiplyScalar(0.5); + + _segDir.copy(v1).sub(v0).normalize(); + + _diff.copy(this.origin).sub(_segCenter); + + var segExtent = v0.distanceTo(v1) * 0.5; + var a01 = -this.direction.dot(_segDir); + + var b0 = _diff.dot(this.direction); + + var b1 = -_diff.dot(_segDir); + + var c = _diff.lengthSq(); + + var det = Math.abs(1 - a01 * a01); + var s0, s1, sqrDist, extDet; + + if (det > 0) { + // The ray and segment are not parallel. + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if (s0 >= 0) { + if (s1 >= -extDet) { + if (s1 <= extDet) { + // region 0 + // Minimum at interior points of ray and segment. + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; + } else { + // region 1 + s1 = segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + } + } else { + // region 5 + s1 = -segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + } + } else { + if (s1 <= -extDet) { + // region 4 + s0 = Math.max(0, -(-a01 * segExtent + b0)); + s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + } else if (s1 <= extDet) { + // region 3 + s0 = 0; + s1 = Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = s1 * (s1 + 2 * b1) + c; + } else { + // region 2 + s0 = Math.max(0, -(a01 * segExtent + b0)); + s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + } + } + } else { + // Ray and segment are parallel. + s1 = a01 > 0 ? -segExtent : segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + } + + if (optionalPointOnRay) { + optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); + } + + if (optionalPointOnSegment) { + optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter); + } + + return sqrDist; + }; + + _proto.intersectSphere = function intersectSphere(sphere, target) { + _vector$2.subVectors(sphere.center, this.origin); + + var tca = _vector$2.dot(this.direction); + + var d2 = _vector$2.dot(_vector$2) - tca * tca; + var radius2 = sphere.radius * sphere.radius; + if (d2 > radius2) return null; + var thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere + + var t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere + + var t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null + + if (t0 < 0 && t1 < 0) return null; // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + + if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 + + return this.at(t0, target); + }; + + _proto.intersectsSphere = function intersectsSphere(sphere) { + return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; + }; + + _proto.distanceToPlane = function distanceToPlane(plane) { + var denominator = plane.normal.dot(this.direction); + + if (denominator === 0) { + // line is coplanar, return origin + if (plane.distanceToPoint(this.origin) === 0) { + return 0; + } // Null is preferable to undefined since undefined means.... it is undefined + + + return null; + } + + var t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + }; + + _proto.intersectPlane = function intersectPlane(plane, target) { + var t = this.distanceToPlane(plane); + + if (t === null) { + return null; + } + + return this.at(t, target); + }; + + _proto.intersectsPlane = function intersectsPlane(plane) { + // check if the ray lies on the plane first + var distToPoint = plane.distanceToPoint(this.origin); + + if (distToPoint === 0) { + return true; + } + + var denominator = plane.normal.dot(this.direction); + + if (denominator * distToPoint < 0) { + return true; + } // ray origin is behind the plane (and is pointing behind it) + + + return false; + }; + + _proto.intersectBox = function intersectBox(box, target) { + var tmin, tmax, tymin, tymax, tzmin, tzmax; + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + var origin = this.origin; + + if (invdirx >= 0) { + tmin = (box.min.x - origin.x) * invdirx; + tmax = (box.max.x - origin.x) * invdirx; + } else { + tmin = (box.max.x - origin.x) * invdirx; + tmax = (box.min.x - origin.x) * invdirx; + } + + if (invdiry >= 0) { + tymin = (box.min.y - origin.y) * invdiry; + tymax = (box.max.y - origin.y) * invdiry; + } else { + tymin = (box.max.y - origin.y) * invdiry; + tymax = (box.min.y - origin.y) * invdiry; + } + + if (tmin > tymax || tymin > tmax) return null; // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if (tymin > tmin || tmin !== tmin) tmin = tymin; + if (tymax < tmax || tmax !== tmax) tmax = tymax; + + if (invdirz >= 0) { + tzmin = (box.min.z - origin.z) * invdirz; + tzmax = (box.max.z - origin.z) * invdirz; + } else { + tzmin = (box.max.z - origin.z) * invdirz; + tzmax = (box.min.z - origin.z) * invdirz; + } + + if (tmin > tzmax || tzmin > tmax) return null; + if (tzmin > tmin || tmin !== tmin) tmin = tzmin; + if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) + + if (tmax < 0) return null; + return this.at(tmin >= 0 ? tmin : tmax, target); + }; + + _proto.intersectsBox = function intersectsBox(box) { + return this.intersectBox(box, _vector$2) !== null; + }; + + _proto.intersectTriangle = function intersectTriangle(a, b, c, backfaceCulling, target) { + // Compute the offset origin, edges, and normal. + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + _edge1.subVectors(b, a); + + _edge2.subVectors(c, a); + + _normal.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + + + var DdN = this.direction.dot(_normal); + var sign; + + if (DdN > 0) { + if (backfaceCulling) return null; + sign = 1; + } else if (DdN < 0) { + sign = -1; + DdN = -DdN; + } else { + return null; + } + + _diff.subVectors(this.origin, a); + + var DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection + + if (DdQxE2 < 0) { + return null; + } + + var DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection + + if (DdE1xQ < 0) { + return null; + } // b1+b2 > 1, no intersection + + + if (DdQxE2 + DdE1xQ > DdN) { + return null; + } // Line intersects triangle, check if ray does. + + + var QdN = -sign * _diff.dot(_normal); // t < 0, no intersection + + + if (QdN < 0) { + return null; + } // Ray intersects triangle. + + + return this.at(QdN / DdN, target); + }; + + _proto.applyMatrix4 = function applyMatrix4(matrix4) { + this.origin.applyMatrix4(matrix4); + this.direction.transformDirection(matrix4); + return this; + }; + + _proto.equals = function equals(ray) { + return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Ray; + }(); + + var Matrix4 = /*#__PURE__*/function () { + function Matrix4() { + this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + + if (arguments.length > 0) { + console.error('THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.'); + } + } + + var _proto = Matrix4.prototype; + + _proto.set = function set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { + var te = this.elements; + te[0] = n11; + te[4] = n12; + te[8] = n13; + te[12] = n14; + te[1] = n21; + te[5] = n22; + te[9] = n23; + te[13] = n24; + te[2] = n31; + te[6] = n32; + te[10] = n33; + te[14] = n34; + te[3] = n41; + te[7] = n42; + te[11] = n43; + te[15] = n44; + return this; + }; + + _proto.identity = function identity() { + this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return this; + }; + + _proto.clone = function clone() { + return new Matrix4().fromArray(this.elements); + }; + + _proto.copy = function copy(m) { + var te = this.elements; + var me = m.elements; + te[0] = me[0]; + te[1] = me[1]; + te[2] = me[2]; + te[3] = me[3]; + te[4] = me[4]; + te[5] = me[5]; + te[6] = me[6]; + te[7] = me[7]; + te[8] = me[8]; + te[9] = me[9]; + te[10] = me[10]; + te[11] = me[11]; + te[12] = me[12]; + te[13] = me[13]; + te[14] = me[14]; + te[15] = me[15]; + return this; + }; + + _proto.copyPosition = function copyPosition(m) { + var te = this.elements, + me = m.elements; + te[12] = me[12]; + te[13] = me[13]; + te[14] = me[14]; + return this; + }; + + _proto.setFromMatrix3 = function setFromMatrix3(m) { + var me = m.elements; + this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); + return this; + }; + + _proto.extractBasis = function extractBasis(xAxis, yAxis, zAxis) { + xAxis.setFromMatrixColumn(this, 0); + yAxis.setFromMatrixColumn(this, 1); + zAxis.setFromMatrixColumn(this, 2); + return this; + }; + + _proto.makeBasis = function makeBasis(xAxis, yAxis, zAxis) { + this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); + return this; + }; + + _proto.extractRotation = function extractRotation(m) { + // this method does not support reflection matrices + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / _v1$1.setFromMatrixColumn(m, 0).length(); + + var scaleY = 1 / _v1$1.setFromMatrixColumn(m, 1).length(); + + var scaleZ = 1 / _v1$1.setFromMatrixColumn(m, 2).length(); + + te[0] = me[0] * scaleX; + te[1] = me[1] * scaleX; + te[2] = me[2] * scaleX; + te[3] = 0; + te[4] = me[4] * scaleY; + te[5] = me[5] * scaleY; + te[6] = me[6] * scaleY; + te[7] = 0; + te[8] = me[8] * scaleZ; + te[9] = me[9] * scaleZ; + te[10] = me[10] * scaleZ; + te[11] = 0; + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + return this; + }; + + _proto.makeRotationFromEuler = function makeRotationFromEuler(euler) { + if (!(euler && euler.isEuler)) { + console.error('THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.'); + } + + var te = this.elements; + var x = euler.x, + y = euler.y, + z = euler.z; + var a = Math.cos(x), + b = Math.sin(x); + var c = Math.cos(y), + d = Math.sin(y); + var e = Math.cos(z), + f = Math.sin(z); + + if (euler.order === 'XYZ') { + var ae = a * e, + af = a * f, + be = b * e, + bf = b * f; + te[0] = c * e; + te[4] = -c * f; + te[8] = d; + te[1] = af + be * d; + te[5] = ae - bf * d; + te[9] = -b * c; + te[2] = bf - ae * d; + te[6] = be + af * d; + te[10] = a * c; + } else if (euler.order === 'YXZ') { + var ce = c * e, + cf = c * f, + de = d * e, + df = d * f; + te[0] = ce + df * b; + te[4] = de * b - cf; + te[8] = a * d; + te[1] = a * f; + te[5] = a * e; + te[9] = -b; + te[2] = cf * b - de; + te[6] = df + ce * b; + te[10] = a * c; + } else if (euler.order === 'ZXY') { + var _ce = c * e, + _cf = c * f, + _de = d * e, + _df = d * f; + + te[0] = _ce - _df * b; + te[4] = -a * f; + te[8] = _de + _cf * b; + te[1] = _cf + _de * b; + te[5] = a * e; + te[9] = _df - _ce * b; + te[2] = -a * d; + te[6] = b; + te[10] = a * c; + } else if (euler.order === 'ZYX') { + var _ae = a * e, + _af = a * f, + _be = b * e, + _bf = b * f; + + te[0] = c * e; + te[4] = _be * d - _af; + te[8] = _ae * d + _bf; + te[1] = c * f; + te[5] = _bf * d + _ae; + te[9] = _af * d - _be; + te[2] = -d; + te[6] = b * c; + te[10] = a * c; + } else if (euler.order === 'YZX') { + var ac = a * c, + ad = a * d, + bc = b * c, + bd = b * d; + te[0] = c * e; + te[4] = bd - ac * f; + te[8] = bc * f + ad; + te[1] = f; + te[5] = a * e; + te[9] = -b * e; + te[2] = -d * e; + te[6] = ad * f + bc; + te[10] = ac - bd * f; + } else if (euler.order === 'XZY') { + var _ac = a * c, + _ad = a * d, + _bc = b * c, + _bd = b * d; + + te[0] = c * e; + te[4] = -f; + te[8] = d * e; + te[1] = _ac * f + _bd; + te[5] = a * e; + te[9] = _ad * f - _bc; + te[2] = _bc * f - _ad; + te[6] = b * e; + te[10] = _bd * f + _ac; + } // bottom row + + + te[3] = 0; + te[7] = 0; + te[11] = 0; // last column + + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + return this; + }; + + _proto.makeRotationFromQuaternion = function makeRotationFromQuaternion(q) { + return this.compose(_zero, q, _one); + }; + + _proto.lookAt = function lookAt(eye, target, up) { + var te = this.elements; + + _z.subVectors(eye, target); + + if (_z.lengthSq() === 0) { + // eye and target are in the same position + _z.z = 1; + } + + _z.normalize(); + + _x.crossVectors(up, _z); + + if (_x.lengthSq() === 0) { + // up and z are parallel + if (Math.abs(up.z) === 1) { + _z.x += 0.0001; + } else { + _z.z += 0.0001; + } + + _z.normalize(); + + _x.crossVectors(up, _z); + } + + _x.normalize(); + + _y.crossVectors(_z, _x); + + te[0] = _x.x; + te[4] = _y.x; + te[8] = _z.x; + te[1] = _x.y; + te[5] = _y.y; + te[9] = _z.y; + te[2] = _x.z; + te[6] = _y.z; + te[10] = _z.z; + return this; + }; + + _proto.multiply = function multiply(m, n) { + if (n !== undefined) { + console.warn('THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.'); + return this.multiplyMatrices(m, n); + } + + return this.multiplyMatrices(this, m); + }; + + _proto.premultiply = function premultiply(m) { + return this.multiplyMatrices(m, this); + }; + + _proto.multiplyMatrices = function multiplyMatrices(a, b) { + var ae = a.elements; + var be = b.elements; + var te = this.elements; + var a11 = ae[0], + a12 = ae[4], + a13 = ae[8], + a14 = ae[12]; + var a21 = ae[1], + a22 = ae[5], + a23 = ae[9], + a24 = ae[13]; + var a31 = ae[2], + a32 = ae[6], + a33 = ae[10], + a34 = ae[14]; + var a41 = ae[3], + a42 = ae[7], + a43 = ae[11], + a44 = ae[15]; + var b11 = be[0], + b12 = be[4], + b13 = be[8], + b14 = be[12]; + var b21 = be[1], + b22 = be[5], + b23 = be[9], + b24 = be[13]; + var b31 = be[2], + b32 = be[6], + b33 = be[10], + b34 = be[14]; + var b41 = be[3], + b42 = be[7], + b43 = be[11], + b44 = be[15]; + te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(s) { + var te = this.elements; + te[0] *= s; + te[4] *= s; + te[8] *= s; + te[12] *= s; + te[1] *= s; + te[5] *= s; + te[9] *= s; + te[13] *= s; + te[2] *= s; + te[6] *= s; + te[10] *= s; + te[14] *= s; + te[3] *= s; + te[7] *= s; + te[11] *= s; + te[15] *= s; + return this; + }; + + _proto.determinant = function determinant() { + var te = this.elements; + var n11 = te[0], + n12 = te[4], + n13 = te[8], + n14 = te[12]; + var n21 = te[1], + n22 = te[5], + n23 = te[9], + n24 = te[13]; + var n31 = te[2], + n32 = te[6], + n33 = te[10], + n34 = te[14]; + var n41 = te[3], + n42 = te[7], + n43 = te[11], + n44 = te[15]; //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); + }; + + _proto.transpose = function transpose() { + var te = this.elements; + var tmp; + tmp = te[1]; + te[1] = te[4]; + te[4] = tmp; + tmp = te[2]; + te[2] = te[8]; + te[8] = tmp; + tmp = te[6]; + te[6] = te[9]; + te[9] = tmp; + tmp = te[3]; + te[3] = te[12]; + te[12] = tmp; + tmp = te[7]; + te[7] = te[13]; + te[13] = tmp; + tmp = te[11]; + te[11] = te[14]; + te[14] = tmp; + return this; + }; + + _proto.setPosition = function setPosition(x, y, z) { + var te = this.elements; + + if (x.isVector3) { + te[12] = x.x; + te[13] = x.y; + te[14] = x.z; + } else { + te[12] = x; + te[13] = y; + te[14] = z; + } + + return this; + }; + + _proto.invert = function invert() { + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements, + n11 = te[0], + n21 = te[1], + n31 = te[2], + n41 = te[3], + n12 = te[4], + n22 = te[5], + n32 = te[6], + n42 = te[7], + n13 = te[8], + n23 = te[9], + n33 = te[10], + n43 = te[11], + n14 = te[12], + n24 = te[13], + n34 = te[14], + n44 = te[15], + t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, + t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, + t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, + t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + var detInv = 1 / det; + te[0] = t11 * detInv; + te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; + te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; + te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; + te[4] = t12 * detInv; + te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; + te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; + te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; + te[8] = t13 * detInv; + te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; + te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; + te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; + te[12] = t14 * detInv; + te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; + te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; + te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; + return this; + }; + + _proto.scale = function scale(v) { + var te = this.elements; + var x = v.x, + y = v.y, + z = v.z; + te[0] *= x; + te[4] *= y; + te[8] *= z; + te[1] *= x; + te[5] *= y; + te[9] *= z; + te[2] *= x; + te[6] *= y; + te[10] *= z; + te[3] *= x; + te[7] *= y; + te[11] *= z; + return this; + }; + + _proto.getMaxScaleOnAxis = function getMaxScaleOnAxis() { + var te = this.elements; + var scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; + var scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; + var scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; + return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); + }; + + _proto.makeTranslation = function makeTranslation(x, y, z) { + this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); + return this; + }; + + _proto.makeRotationX = function makeRotationX(theta) { + var c = Math.cos(theta), + s = Math.sin(theta); + this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); + return this; + }; + + _proto.makeRotationY = function makeRotationY(theta) { + var c = Math.cos(theta), + s = Math.sin(theta); + this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); + return this; + }; + + _proto.makeRotationZ = function makeRotationZ(theta) { + var c = Math.cos(theta), + s = Math.sin(theta); + this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return this; + }; + + _proto.makeRotationAxis = function makeRotationAxis(axis, angle) { + // Based on http://www.gamedev.net/reference/articles/article1199.asp + var c = Math.cos(angle); + var s = Math.sin(angle); + var t = 1 - c; + var x = axis.x, + y = axis.y, + z = axis.z; + var tx = t * x, + ty = t * y; + this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); + return this; + }; + + _proto.makeScale = function makeScale(x, y, z) { + this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); + return this; + }; + + _proto.makeShear = function makeShear(x, y, z) { + this.set(1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1); + return this; + }; + + _proto.compose = function compose(position, quaternion, scale) { + var te = this.elements; + var x = quaternion._x, + y = quaternion._y, + z = quaternion._z, + w = quaternion._w; + var x2 = x + x, + y2 = y + y, + z2 = z + z; + var xx = x * x2, + xy = x * y2, + xz = x * z2; + var yy = y * y2, + yz = y * z2, + zz = z * z2; + var wx = w * x2, + wy = w * y2, + wz = w * z2; + var sx = scale.x, + sy = scale.y, + sz = scale.z; + te[0] = (1 - (yy + zz)) * sx; + te[1] = (xy + wz) * sx; + te[2] = (xz - wy) * sx; + te[3] = 0; + te[4] = (xy - wz) * sy; + te[5] = (1 - (xx + zz)) * sy; + te[6] = (yz + wx) * sy; + te[7] = 0; + te[8] = (xz + wy) * sz; + te[9] = (yz - wx) * sz; + te[10] = (1 - (xx + yy)) * sz; + te[11] = 0; + te[12] = position.x; + te[13] = position.y; + te[14] = position.z; + te[15] = 1; + return this; + }; + + _proto.decompose = function decompose(position, quaternion, scale) { + var te = this.elements; + + var sx = _v1$1.set(te[0], te[1], te[2]).length(); + + var sy = _v1$1.set(te[4], te[5], te[6]).length(); + + var sz = _v1$1.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale + + + var det = this.determinant(); + if (det < 0) sx = -sx; + position.x = te[12]; + position.y = te[13]; + position.z = te[14]; // scale the rotation part + + _m1.copy(this); + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + _m1.elements[0] *= invSX; + _m1.elements[1] *= invSX; + _m1.elements[2] *= invSX; + _m1.elements[4] *= invSY; + _m1.elements[5] *= invSY; + _m1.elements[6] *= invSY; + _m1.elements[8] *= invSZ; + _m1.elements[9] *= invSZ; + _m1.elements[10] *= invSZ; + quaternion.setFromRotationMatrix(_m1); + scale.x = sx; + scale.y = sy; + scale.z = sz; + return this; + }; + + _proto.makePerspective = function makePerspective(left, right, top, bottom, near, far) { + if (far === undefined) { + console.warn('THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.'); + } + + var te = this.elements; + var x = 2 * near / (right - left); + var y = 2 * near / (top - bottom); + var a = (right + left) / (right - left); + var b = (top + bottom) / (top - bottom); + var c = -(far + near) / (far - near); + var d = -2 * far * near / (far - near); + te[0] = x; + te[4] = 0; + te[8] = a; + te[12] = 0; + te[1] = 0; + te[5] = y; + te[9] = b; + te[13] = 0; + te[2] = 0; + te[6] = 0; + te[10] = c; + te[14] = d; + te[3] = 0; + te[7] = 0; + te[11] = -1; + te[15] = 0; + return this; + }; + + _proto.makeOrthographic = function makeOrthographic(left, right, top, bottom, near, far) { + var te = this.elements; + var w = 1.0 / (right - left); + var h = 1.0 / (top - bottom); + var p = 1.0 / (far - near); + var x = (right + left) * w; + var y = (top + bottom) * h; + var z = (far + near) * p; + te[0] = 2 * w; + te[4] = 0; + te[8] = 0; + te[12] = -x; + te[1] = 0; + te[5] = 2 * h; + te[9] = 0; + te[13] = -y; + te[2] = 0; + te[6] = 0; + te[10] = -2 * p; + te[14] = -z; + te[3] = 0; + te[7] = 0; + te[11] = 0; + te[15] = 1; + return this; + }; + + _proto.equals = function equals(matrix) { + var te = this.elements; + var me = matrix.elements; + + for (var i = 0; i < 16; i++) { + if (te[i] !== me[i]) return false; + } + + return true; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + for (var i = 0; i < 16; i++) { + this.elements[i] = array[i + offset]; + } + + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + var te = this.elements; + array[offset] = te[0]; + array[offset + 1] = te[1]; + array[offset + 2] = te[2]; + array[offset + 3] = te[3]; + array[offset + 4] = te[4]; + array[offset + 5] = te[5]; + array[offset + 6] = te[6]; + array[offset + 7] = te[7]; + array[offset + 8] = te[8]; + array[offset + 9] = te[9]; + array[offset + 10] = te[10]; + array[offset + 11] = te[11]; + array[offset + 12] = te[12]; + array[offset + 13] = te[13]; + array[offset + 14] = te[14]; + array[offset + 15] = te[15]; + return array; + }; + + return Matrix4; + }(); + + Matrix4.prototype.isMatrix4 = true; + + var _v1$1 = /*@__PURE__*/new Vector3(); + + var _m1 = /*@__PURE__*/new Matrix4(); + + var _zero = /*@__PURE__*/new Vector3(0, 0, 0); + + var _one = /*@__PURE__*/new Vector3(1, 1, 1); + + var _x = /*@__PURE__*/new Vector3(); + + var _y = /*@__PURE__*/new Vector3(); + + var _z = /*@__PURE__*/new Vector3(); + + var _matrix = /*@__PURE__*/new Matrix4(); + + var _quaternion$1 = /*@__PURE__*/new Quaternion(); + + var Euler = /*#__PURE__*/function () { + function Euler(x, y, z, order) { + if (x === void 0) { + x = 0; + } + + if (y === void 0) { + y = 0; + } + + if (z === void 0) { + z = 0; + } + + if (order === void 0) { + order = Euler.DefaultOrder; + } + + this._x = x; + this._y = y; + this._z = z; + this._order = order; + } + + var _proto = Euler.prototype; + + _proto.set = function set(x, y, z, order) { + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this._onChangeCallback(); + + return this; + }; + + _proto.clone = function clone() { + return new this.constructor(this._x, this._y, this._z, this._order); + }; + + _proto.copy = function copy(euler) { + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this._onChangeCallback(); + + return this; + }; + + _proto.setFromRotationMatrix = function setFromRotationMatrix(m, order, update) { + var clamp = MathUtils.clamp; // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[0], + m12 = te[4], + m13 = te[8]; + var m21 = te[1], + m22 = te[5], + m23 = te[9]; + var m31 = te[2], + m32 = te[6], + m33 = te[10]; + order = order || this._order; + + switch (order) { + case 'XYZ': + this._y = Math.asin(clamp(m13, -1, 1)); + + if (Math.abs(m13) < 0.9999999) { + this._x = Math.atan2(-m23, m33); + this._z = Math.atan2(-m12, m11); + } else { + this._x = Math.atan2(m32, m22); + this._z = 0; + } + + break; + + case 'YXZ': + this._x = Math.asin(-clamp(m23, -1, 1)); + + if (Math.abs(m23) < 0.9999999) { + this._y = Math.atan2(m13, m33); + this._z = Math.atan2(m21, m22); + } else { + this._y = Math.atan2(-m31, m11); + this._z = 0; + } + + break; + + case 'ZXY': + this._x = Math.asin(clamp(m32, -1, 1)); + + if (Math.abs(m32) < 0.9999999) { + this._y = Math.atan2(-m31, m33); + this._z = Math.atan2(-m12, m22); + } else { + this._y = 0; + this._z = Math.atan2(m21, m11); + } + + break; + + case 'ZYX': + this._y = Math.asin(-clamp(m31, -1, 1)); + + if (Math.abs(m31) < 0.9999999) { + this._x = Math.atan2(m32, m33); + this._z = Math.atan2(m21, m11); + } else { + this._x = 0; + this._z = Math.atan2(-m12, m22); + } + + break; + + case 'YZX': + this._z = Math.asin(clamp(m21, -1, 1)); + + if (Math.abs(m21) < 0.9999999) { + this._x = Math.atan2(-m23, m22); + this._y = Math.atan2(-m31, m11); + } else { + this._x = 0; + this._y = Math.atan2(m13, m33); + } + + break; + + case 'XZY': + this._z = Math.asin(-clamp(m12, -1, 1)); + + if (Math.abs(m12) < 0.9999999) { + this._x = Math.atan2(m32, m22); + this._y = Math.atan2(m13, m11); + } else { + this._x = Math.atan2(-m23, m33); + this._y = 0; + } + + break; + + default: + console.warn('THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order); + } + + this._order = order; + if (update !== false) this._onChangeCallback(); + return this; + }; + + _proto.setFromQuaternion = function setFromQuaternion(q, order, update) { + _matrix.makeRotationFromQuaternion(q); + + return this.setFromRotationMatrix(_matrix, order, update); + }; + + _proto.setFromVector3 = function setFromVector3(v, order) { + return this.set(v.x, v.y, v.z, order || this._order); + }; + + _proto.reorder = function reorder(newOrder) { + // WARNING: this discards revolution information -bhouston + _quaternion$1.setFromEuler(this); + + return this.setFromQuaternion(_quaternion$1, newOrder); + }; + + _proto.equals = function equals(euler) { + return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; + }; + + _proto.fromArray = function fromArray(array) { + this._x = array[0]; + this._y = array[1]; + this._z = array[2]; + if (array[3] !== undefined) this._order = array[3]; + + this._onChangeCallback(); + + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this._x; + array[offset + 1] = this._y; + array[offset + 2] = this._z; + array[offset + 3] = this._order; + return array; + }; + + _proto.toVector3 = function toVector3(optionalResult) { + if (optionalResult) { + return optionalResult.set(this._x, this._y, this._z); + } else { + return new Vector3(this._x, this._y, this._z); + } + }; + + _proto._onChange = function _onChange(callback) { + this._onChangeCallback = callback; + return this; + }; + + _proto._onChangeCallback = function _onChangeCallback() {}; + + _createClass(Euler, [{ + key: "x", + get: function get() { + return this._x; + }, + set: function set(value) { + this._x = value; + + this._onChangeCallback(); + } + }, { + key: "y", + get: function get() { + return this._y; + }, + set: function set(value) { + this._y = value; + + this._onChangeCallback(); + } + }, { + key: "z", + get: function get() { + return this._z; + }, + set: function set(value) { + this._z = value; + + this._onChangeCallback(); + } + }, { + key: "order", + get: function get() { + return this._order; + }, + set: function set(value) { + this._order = value; + + this._onChangeCallback(); + } + }]); + + return Euler; + }(); + + Euler.prototype.isEuler = true; + Euler.DefaultOrder = 'XYZ'; + Euler.RotationOrders = ['XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX']; + + var Layers = /*#__PURE__*/function () { + function Layers() { + this.mask = 1 | 0; + } + + var _proto = Layers.prototype; + + _proto.set = function set(channel) { + this.mask = 1 << channel | 0; + }; + + _proto.enable = function enable(channel) { + this.mask |= 1 << channel | 0; + }; + + _proto.enableAll = function enableAll() { + this.mask = 0xffffffff | 0; + }; + + _proto.toggle = function toggle(channel) { + this.mask ^= 1 << channel | 0; + }; + + _proto.disable = function disable(channel) { + this.mask &= ~(1 << channel | 0); + }; + + _proto.disableAll = function disableAll() { + this.mask = 0; + }; + + _proto.test = function test(layers) { + return (this.mask & layers.mask) !== 0; + }; + + return Layers; + }(); + + var _object3DId = 0; + + var _v1$2 = new Vector3(); + + var _q1 = new Quaternion(); + + var _m1$1 = new Matrix4(); + + var _target = new Vector3(); + + var _position = new Vector3(); + + var _scale = new Vector3(); + + var _quaternion$2 = new Quaternion(); + + var _xAxis = new Vector3(1, 0, 0); + + var _yAxis = new Vector3(0, 1, 0); + + var _zAxis = new Vector3(0, 0, 1); + + var _addedEvent = { + type: 'added' + }; + var _removedEvent = { + type: 'removed' + }; + + function Object3D() { + Object.defineProperty(this, 'id', { + value: _object3DId++ + }); + this.uuid = MathUtils.generateUUID(); + this.name = ''; + this.type = 'Object3D'; + this.parent = null; + this.children = []; + this.up = Object3D.DefaultUp.clone(); + var position = new Vector3(); + var rotation = new Euler(); + var quaternion = new Quaternion(); + var scale = new Vector3(1, 1, 1); + + function onRotationChange() { + quaternion.setFromEuler(rotation, false); + } + + function onQuaternionChange() { + rotation.setFromQuaternion(quaternion, undefined, false); + } + + rotation._onChange(onRotationChange); + + quaternion._onChange(onQuaternionChange); + + Object.defineProperties(this, { + position: { + configurable: true, + enumerable: true, + value: position + }, + rotation: { + configurable: true, + enumerable: true, + value: rotation + }, + quaternion: { + configurable: true, + enumerable: true, + value: quaternion + }, + scale: { + configurable: true, + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new Matrix4() + }, + normalMatrix: { + value: new Matrix3() + } + }); + this.matrix = new Matrix4(); + this.matrixWorld = new Matrix4(); + this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; + this.layers = new Layers(); + this.visible = true; + this.castShadow = false; + this.receiveShadow = false; + this.frustumCulled = true; + this.renderOrder = 0; + this.animations = []; + this.userData = {}; + } + + Object3D.DefaultUp = new Vector3(0, 1, 0); + Object3D.DefaultMatrixAutoUpdate = true; + Object3D.prototype = Object.assign(Object.create(EventDispatcher.prototype), { + constructor: Object3D, + isObject3D: true, + onBeforeRender: function onBeforeRender() {}, + onAfterRender: function onAfterRender() {}, + applyMatrix4: function applyMatrix4(matrix) { + if (this.matrixAutoUpdate) this.updateMatrix(); + this.matrix.premultiply(matrix); + this.matrix.decompose(this.position, this.quaternion, this.scale); + }, + applyQuaternion: function applyQuaternion(q) { + this.quaternion.premultiply(q); + return this; + }, + setRotationFromAxisAngle: function setRotationFromAxisAngle(axis, angle) { + // assumes axis is normalized + this.quaternion.setFromAxisAngle(axis, angle); + }, + setRotationFromEuler: function setRotationFromEuler(euler) { + this.quaternion.setFromEuler(euler, true); + }, + setRotationFromMatrix: function setRotationFromMatrix(m) { + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + this.quaternion.setFromRotationMatrix(m); + }, + setRotationFromQuaternion: function setRotationFromQuaternion(q) { + // assumes q is normalized + this.quaternion.copy(q); + }, + rotateOnAxis: function rotateOnAxis(axis, angle) { + // rotate object on axis in object space + // axis is assumed to be normalized + _q1.setFromAxisAngle(axis, angle); + + this.quaternion.multiply(_q1); + return this; + }, + rotateOnWorldAxis: function rotateOnWorldAxis(axis, angle) { + // rotate object on axis in world space + // axis is assumed to be normalized + // method assumes no rotated parent + _q1.setFromAxisAngle(axis, angle); + + this.quaternion.premultiply(_q1); + return this; + }, + rotateX: function rotateX(angle) { + return this.rotateOnAxis(_xAxis, angle); + }, + rotateY: function rotateY(angle) { + return this.rotateOnAxis(_yAxis, angle); + }, + rotateZ: function rotateZ(angle) { + return this.rotateOnAxis(_zAxis, angle); + }, + translateOnAxis: function translateOnAxis(axis, distance) { + // translate object by distance along axis in object space + // axis is assumed to be normalized + _v1$2.copy(axis).applyQuaternion(this.quaternion); + + this.position.add(_v1$2.multiplyScalar(distance)); + return this; + }, + translateX: function translateX(distance) { + return this.translateOnAxis(_xAxis, distance); + }, + translateY: function translateY(distance) { + return this.translateOnAxis(_yAxis, distance); + }, + translateZ: function translateZ(distance) { + return this.translateOnAxis(_zAxis, distance); + }, + localToWorld: function localToWorld(vector) { + return vector.applyMatrix4(this.matrixWorld); + }, + worldToLocal: function worldToLocal(vector) { + return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); + }, + lookAt: function lookAt(x, y, z) { + // This method does not support objects having non-uniformly-scaled parent(s) + if (x.isVector3) { + _target.copy(x); + } else { + _target.set(x, y, z); + } + + var parent = this.parent; + this.updateWorldMatrix(true, false); + + _position.setFromMatrixPosition(this.matrixWorld); + + if (this.isCamera || this.isLight) { + _m1$1.lookAt(_position, _target, this.up); + } else { + _m1$1.lookAt(_target, _position, this.up); + } + + this.quaternion.setFromRotationMatrix(_m1$1); + + if (parent) { + _m1$1.extractRotation(parent.matrixWorld); + + _q1.setFromRotationMatrix(_m1$1); + + this.quaternion.premultiply(_q1.invert()); + } + }, + add: function add(object) { + if (arguments.length > 1) { + for (var i = 0; i < arguments.length; i++) { + this.add(arguments[i]); + } + + return this; + } + + if (object === this) { + console.error('THREE.Object3D.add: object can\'t be added as a child of itself.', object); + return this; + } + + if (object && object.isObject3D) { + if (object.parent !== null) { + object.parent.remove(object); + } + + object.parent = this; + this.children.push(object); + object.dispatchEvent(_addedEvent); + } else { + console.error('THREE.Object3D.add: object not an instance of THREE.Object3D.', object); + } + + return this; + }, + remove: function remove(object) { + if (arguments.length > 1) { + for (var i = 0; i < arguments.length; i++) { + this.remove(arguments[i]); + } + + return this; + } + + var index = this.children.indexOf(object); + + if (index !== -1) { + object.parent = null; + this.children.splice(index, 1); + object.dispatchEvent(_removedEvent); + } + + return this; + }, + clear: function clear() { + for (var i = 0; i < this.children.length; i++) { + var object = this.children[i]; + object.parent = null; + object.dispatchEvent(_removedEvent); + } + + this.children.length = 0; + return this; + }, + attach: function attach(object) { + // adds object as a child of this, while maintaining the object's world transform + this.updateWorldMatrix(true, false); + + _m1$1.copy(this.matrixWorld).invert(); + + if (object.parent !== null) { + object.parent.updateWorldMatrix(true, false); + + _m1$1.multiply(object.parent.matrixWorld); + } + + object.applyMatrix4(_m1$1); + this.add(object); + object.updateWorldMatrix(false, true); + return this; + }, + getObjectById: function getObjectById(id) { + return this.getObjectByProperty('id', id); + }, + getObjectByName: function getObjectByName(name) { + return this.getObjectByProperty('name', name); + }, + getObjectByProperty: function getObjectByProperty(name, value) { + if (this[name] === value) return this; + + for (var i = 0, l = this.children.length; i < l; i++) { + var child = this.children[i]; + var object = child.getObjectByProperty(name, value); + + if (object !== undefined) { + return object; + } + } + + return undefined; + }, + getWorldPosition: function getWorldPosition(target) { + if (target === undefined) { + console.warn('THREE.Object3D: .getWorldPosition() target is now required'); + target = new Vector3(); + } + + this.updateWorldMatrix(true, false); + return target.setFromMatrixPosition(this.matrixWorld); + }, + getWorldQuaternion: function getWorldQuaternion(target) { + if (target === undefined) { + console.warn('THREE.Object3D: .getWorldQuaternion() target is now required'); + target = new Quaternion(); + } + + this.updateWorldMatrix(true, false); + this.matrixWorld.decompose(_position, target, _scale); + return target; + }, + getWorldScale: function getWorldScale(target) { + if (target === undefined) { + console.warn('THREE.Object3D: .getWorldScale() target is now required'); + target = new Vector3(); + } + + this.updateWorldMatrix(true, false); + this.matrixWorld.decompose(_position, _quaternion$2, target); + return target; + }, + getWorldDirection: function getWorldDirection(target) { + if (target === undefined) { + console.warn('THREE.Object3D: .getWorldDirection() target is now required'); + target = new Vector3(); + } + + this.updateWorldMatrix(true, false); + var e = this.matrixWorld.elements; + return target.set(e[8], e[9], e[10]).normalize(); + }, + raycast: function raycast() {}, + traverse: function traverse(callback) { + callback(this); + var children = this.children; + + for (var i = 0, l = children.length; i < l; i++) { + children[i].traverse(callback); + } + }, + traverseVisible: function traverseVisible(callback) { + if (this.visible === false) return; + callback(this); + var children = this.children; + + for (var i = 0, l = children.length; i < l; i++) { + children[i].traverseVisible(callback); + } + }, + traverseAncestors: function traverseAncestors(callback) { + var parent = this.parent; + + if (parent !== null) { + callback(parent); + parent.traverseAncestors(callback); + } + }, + updateMatrix: function updateMatrix() { + this.matrix.compose(this.position, this.quaternion, this.scale); + this.matrixWorldNeedsUpdate = true; + }, + updateMatrixWorld: function updateMatrixWorld(force) { + if (this.matrixAutoUpdate) this.updateMatrix(); + + if (this.matrixWorldNeedsUpdate || force) { + if (this.parent === null) { + this.matrixWorld.copy(this.matrix); + } else { + this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); + } + + this.matrixWorldNeedsUpdate = false; + force = true; + } // update children + + + var children = this.children; + + for (var i = 0, l = children.length; i < l; i++) { + children[i].updateMatrixWorld(force); + } + }, + updateWorldMatrix: function updateWorldMatrix(updateParents, updateChildren) { + var parent = this.parent; + + if (updateParents === true && parent !== null) { + parent.updateWorldMatrix(true, false); + } + + if (this.matrixAutoUpdate) this.updateMatrix(); + + if (this.parent === null) { + this.matrixWorld.copy(this.matrix); + } else { + this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); + } // update children + + + if (updateChildren === true) { + var children = this.children; + + for (var i = 0, l = children.length; i < l; i++) { + children[i].updateWorldMatrix(false, true); + } + } + }, + toJSON: function toJSON(meta) { + // meta is a string when called from JSON.stringify + var isRootObject = meta === undefined || typeof meta === 'string'; + var output = {}; // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + + if (isRootObject) { + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {}, + shapes: {}, + skeletons: {}, + animations: {} + }; + output.metadata = { + version: 4.5, + type: 'Object', + generator: 'Object3D.toJSON' + }; + } // standard Object3D serialization + + + var object = {}; + object.uuid = this.uuid; + object.type = this.type; + if (this.name !== '') object.name = this.name; + if (this.castShadow === true) object.castShadow = true; + if (this.receiveShadow === true) object.receiveShadow = true; + if (this.visible === false) object.visible = false; + if (this.frustumCulled === false) object.frustumCulled = false; + if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; + if (JSON.stringify(this.userData) !== '{}') object.userData = this.userData; + object.layers = this.layers.mask; + object.matrix = this.matrix.toArray(); + if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties + + if (this.isInstancedMesh) { + object.type = 'InstancedMesh'; + object.count = this.count; + object.instanceMatrix = this.instanceMatrix.toJSON(); + } // + + + function serialize(library, element) { + if (library[element.uuid] === undefined) { + library[element.uuid] = element.toJSON(meta); + } + + return element.uuid; + } + + if (this.isMesh || this.isLine || this.isPoints) { + object.geometry = serialize(meta.geometries, this.geometry); + var parameters = this.geometry.parameters; + + if (parameters !== undefined && parameters.shapes !== undefined) { + var shapes = parameters.shapes; + + if (Array.isArray(shapes)) { + for (var i = 0, l = shapes.length; i < l; i++) { + var shape = shapes[i]; + serialize(meta.shapes, shape); + } + } else { + serialize(meta.shapes, shapes); + } + } + } + + if (this.isSkinnedMesh) { + object.bindMode = this.bindMode; + object.bindMatrix = this.bindMatrix.toArray(); + + if (this.skeleton !== undefined) { + serialize(meta.skeletons, this.skeleton); + object.skeleton = this.skeleton.uuid; + } + } + + if (this.material !== undefined) { + if (Array.isArray(this.material)) { + var uuids = []; + + for (var _i = 0, _l = this.material.length; _i < _l; _i++) { + uuids.push(serialize(meta.materials, this.material[_i])); + } + + object.material = uuids; + } else { + object.material = serialize(meta.materials, this.material); + } + } // + + + if (this.children.length > 0) { + object.children = []; + + for (var _i2 = 0; _i2 < this.children.length; _i2++) { + object.children.push(this.children[_i2].toJSON(meta).object); + } + } // + + + if (this.animations.length > 0) { + object.animations = []; + + for (var _i3 = 0; _i3 < this.animations.length; _i3++) { + var animation = this.animations[_i3]; + object.animations.push(serialize(meta.animations, animation)); + } + } + + if (isRootObject) { + var geometries = extractFromCache(meta.geometries); + var materials = extractFromCache(meta.materials); + var textures = extractFromCache(meta.textures); + var images = extractFromCache(meta.images); + + var _shapes = extractFromCache(meta.shapes); + + var skeletons = extractFromCache(meta.skeletons); + var animations = extractFromCache(meta.animations); + if (geometries.length > 0) output.geometries = geometries; + if (materials.length > 0) output.materials = materials; + if (textures.length > 0) output.textures = textures; + if (images.length > 0) output.images = images; + if (_shapes.length > 0) output.shapes = _shapes; + if (skeletons.length > 0) output.skeletons = skeletons; + if (animations.length > 0) output.animations = animations; + } + + output.object = object; + return output; // extract data from the cache hash + // remove metadata on each item + // and return as array + + function extractFromCache(cache) { + var values = []; + + for (var key in cache) { + var data = cache[key]; + delete data.metadata; + values.push(data); + } + + return values; + } + }, + clone: function clone(recursive) { + return new this.constructor().copy(this, recursive); + }, + copy: function copy(source, recursive) { + if (recursive === void 0) { + recursive = true; + } + + this.name = source.name; + this.up.copy(source.up); + this.position.copy(source.position); + this.rotation.order = source.rotation.order; + this.quaternion.copy(source.quaternion); + this.scale.copy(source.scale); + this.matrix.copy(source.matrix); + this.matrixWorld.copy(source.matrixWorld); + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + this.layers.mask = source.layers.mask; + this.visible = source.visible; + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; + this.userData = JSON.parse(JSON.stringify(source.userData)); + + if (recursive === true) { + for (var i = 0; i < source.children.length; i++) { + var child = source.children[i]; + this.add(child.clone()); + } + } + + return this; + } + }); + + var _vector1 = /*@__PURE__*/new Vector3(); + + var _vector2 = /*@__PURE__*/new Vector3(); + + var _normalMatrix = /*@__PURE__*/new Matrix3(); + + var Plane = /*#__PURE__*/function () { + function Plane(normal, constant) { + if (normal === void 0) { + normal = new Vector3(1, 0, 0); + } + + if (constant === void 0) { + constant = 0; + } + + // normal is assumed to be normalized + this.normal = normal; + this.constant = constant; + } + + var _proto = Plane.prototype; + + _proto.set = function set(normal, constant) { + this.normal.copy(normal); + this.constant = constant; + return this; + }; + + _proto.setComponents = function setComponents(x, y, z, w) { + this.normal.set(x, y, z); + this.constant = w; + return this; + }; + + _proto.setFromNormalAndCoplanarPoint = function setFromNormalAndCoplanarPoint(normal, point) { + this.normal.copy(normal); + this.constant = -point.dot(this.normal); + return this; + }; + + _proto.setFromCoplanarPoints = function setFromCoplanarPoints(a, b, c) { + var normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + + this.setFromNormalAndCoplanarPoint(normal, a); + return this; + }; + + _proto.copy = function copy(plane) { + this.normal.copy(plane.normal); + this.constant = plane.constant; + return this; + }; + + _proto.normalize = function normalize() { + // Note: will lead to a divide by zero if the plane is invalid. + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar(inverseNormalLength); + this.constant *= inverseNormalLength; + return this; + }; + + _proto.negate = function negate() { + this.constant *= -1; + this.normal.negate(); + return this; + }; + + _proto.distanceToPoint = function distanceToPoint(point) { + return this.normal.dot(point) + this.constant; + }; + + _proto.distanceToSphere = function distanceToSphere(sphere) { + return this.distanceToPoint(sphere.center) - sphere.radius; + }; + + _proto.projectPoint = function projectPoint(point, target) { + if (target === undefined) { + console.warn('THREE.Plane: .projectPoint() target is now required'); + target = new Vector3(); + } + + return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); + }; + + _proto.intersectLine = function intersectLine(line, target) { + if (target === undefined) { + console.warn('THREE.Plane: .intersectLine() target is now required'); + target = new Vector3(); + } + + var direction = line.delta(_vector1); + var denominator = this.normal.dot(direction); + + if (denominator === 0) { + // line is coplanar, return origin + if (this.distanceToPoint(line.start) === 0) { + return target.copy(line.start); + } // Unsure if this is the correct method to handle this case. + + + return undefined; + } + + var t = -(line.start.dot(this.normal) + this.constant) / denominator; + + if (t < 0 || t > 1) { + return undefined; + } + + return target.copy(direction).multiplyScalar(t).add(line.start); + }; + + _proto.intersectsLine = function intersectsLine(line) { + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + var startSign = this.distanceToPoint(line.start); + var endSign = this.distanceToPoint(line.end); + return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; + }; + + _proto.intersectsBox = function intersectsBox(box) { + return box.intersectsPlane(this); + }; + + _proto.intersectsSphere = function intersectsSphere(sphere) { + return sphere.intersectsPlane(this); + }; + + _proto.coplanarPoint = function coplanarPoint(target) { + if (target === undefined) { + console.warn('THREE.Plane: .coplanarPoint() target is now required'); + target = new Vector3(); + } + + return target.copy(this.normal).multiplyScalar(-this.constant); + }; + + _proto.applyMatrix4 = function applyMatrix4(matrix, optionalNormalMatrix) { + var normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); + + var referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); + var normal = this.normal.applyMatrix3(normalMatrix).normalize(); + this.constant = -referencePoint.dot(normal); + return this; + }; + + _proto.translate = function translate(offset) { + this.constant -= offset.dot(this.normal); + return this; + }; + + _proto.equals = function equals(plane) { + return plane.normal.equals(this.normal) && plane.constant === this.constant; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Plane; + }(); + + Plane.prototype.isPlane = true; + + var _v0$1 = /*@__PURE__*/new Vector3(); + + var _v1$3 = /*@__PURE__*/new Vector3(); + + var _v2$1 = /*@__PURE__*/new Vector3(); + + var _v3 = /*@__PURE__*/new Vector3(); + + var _vab = /*@__PURE__*/new Vector3(); + + var _vac = /*@__PURE__*/new Vector3(); + + var _vbc = /*@__PURE__*/new Vector3(); + + var _vap = /*@__PURE__*/new Vector3(); + + var _vbp = /*@__PURE__*/new Vector3(); + + var _vcp = /*@__PURE__*/new Vector3(); + + var Triangle = /*#__PURE__*/function () { + function Triangle(a, b, c) { + if (a === void 0) { + a = new Vector3(); + } + + if (b === void 0) { + b = new Vector3(); + } + + if (c === void 0) { + c = new Vector3(); + } + + this.a = a; + this.b = b; + this.c = c; + } + + Triangle.getNormal = function getNormal(a, b, c, target) { + if (target === undefined) { + console.warn('THREE.Triangle: .getNormal() target is now required'); + target = new Vector3(); + } + + target.subVectors(c, b); + + _v0$1.subVectors(a, b); + + target.cross(_v0$1); + var targetLengthSq = target.lengthSq(); + + if (targetLengthSq > 0) { + return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); + } + + return target.set(0, 0, 0); + } // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + ; + + Triangle.getBarycoord = function getBarycoord(point, a, b, c, target) { + _v0$1.subVectors(c, a); + + _v1$3.subVectors(b, a); + + _v2$1.subVectors(point, a); + + var dot00 = _v0$1.dot(_v0$1); + + var dot01 = _v0$1.dot(_v1$3); + + var dot02 = _v0$1.dot(_v2$1); + + var dot11 = _v1$3.dot(_v1$3); + + var dot12 = _v1$3.dot(_v2$1); + + var denom = dot00 * dot11 - dot01 * dot01; + + if (target === undefined) { + console.warn('THREE.Triangle: .getBarycoord() target is now required'); + target = new Vector3(); + } // collinear or singular triangle + + + if (denom === 0) { + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return target.set(-2, -1, -1); + } + + var invDenom = 1 / denom; + var u = (dot11 * dot02 - dot01 * dot12) * invDenom; + var v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 + + return target.set(1 - u - v, v, u); + }; + + Triangle.containsPoint = function containsPoint(point, a, b, c) { + this.getBarycoord(point, a, b, c, _v3); + return _v3.x >= 0 && _v3.y >= 0 && _v3.x + _v3.y <= 1; + }; + + Triangle.getUV = function getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { + this.getBarycoord(point, p1, p2, p3, _v3); + target.set(0, 0); + target.addScaledVector(uv1, _v3.x); + target.addScaledVector(uv2, _v3.y); + target.addScaledVector(uv3, _v3.z); + return target; + }; + + Triangle.isFrontFacing = function isFrontFacing(a, b, c, direction) { + _v0$1.subVectors(c, b); + + _v1$3.subVectors(a, b); // strictly front facing + + + return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; + }; + + var _proto = Triangle.prototype; + + _proto.set = function set(a, b, c) { + this.a.copy(a); + this.b.copy(b); + this.c.copy(c); + return this; + }; + + _proto.setFromPointsAndIndices = function setFromPointsAndIndices(points, i0, i1, i2) { + this.a.copy(points[i0]); + this.b.copy(points[i1]); + this.c.copy(points[i2]); + return this; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.copy = function copy(triangle) { + this.a.copy(triangle.a); + this.b.copy(triangle.b); + this.c.copy(triangle.c); + return this; + }; + + _proto.getArea = function getArea() { + _v0$1.subVectors(this.c, this.b); + + _v1$3.subVectors(this.a, this.b); + + return _v0$1.cross(_v1$3).length() * 0.5; + }; + + _proto.getMidpoint = function getMidpoint(target) { + if (target === undefined) { + console.warn('THREE.Triangle: .getMidpoint() target is now required'); + target = new Vector3(); + } + + return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); + }; + + _proto.getNormal = function getNormal(target) { + return Triangle.getNormal(this.a, this.b, this.c, target); + }; + + _proto.getPlane = function getPlane(target) { + if (target === undefined) { + console.warn('THREE.Triangle: .getPlane() target is now required'); + target = new Plane(); + } + + return target.setFromCoplanarPoints(this.a, this.b, this.c); + }; + + _proto.getBarycoord = function getBarycoord(point, target) { + return Triangle.getBarycoord(point, this.a, this.b, this.c, target); + }; + + _proto.getUV = function getUV(point, uv1, uv2, uv3, target) { + return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, target); + }; + + _proto.containsPoint = function containsPoint(point) { + return Triangle.containsPoint(point, this.a, this.b, this.c); + }; + + _proto.isFrontFacing = function isFrontFacing(direction) { + return Triangle.isFrontFacing(this.a, this.b, this.c, direction); + }; + + _proto.intersectsBox = function intersectsBox(box) { + return box.intersectsTriangle(this); + }; + + _proto.closestPointToPoint = function closestPointToPoint(p, target) { + if (target === undefined) { + console.warn('THREE.Triangle: .closestPointToPoint() target is now required'); + target = new Vector3(); + } + + var a = this.a, + b = this.b, + c = this.c; + var v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., + // under the accompanying license; see chapter 5.1.5 for detailed explanation. + // basically, we're distinguishing which of the voronoi regions of the triangle + // the point lies in with the minimum amount of redundant computation. + + _vab.subVectors(b, a); + + _vac.subVectors(c, a); + + _vap.subVectors(p, a); + + var d1 = _vab.dot(_vap); + + var d2 = _vac.dot(_vap); + + if (d1 <= 0 && d2 <= 0) { + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy(a); + } + + _vbp.subVectors(p, b); + + var d3 = _vab.dot(_vbp); + + var d4 = _vac.dot(_vbp); + + if (d3 >= 0 && d4 <= d3) { + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy(b); + } + + var vc = d1 * d4 - d3 * d2; + + if (vc <= 0 && d1 >= 0 && d3 <= 0) { + v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) + + return target.copy(a).addScaledVector(_vab, v); + } + + _vcp.subVectors(p, c); + + var d5 = _vab.dot(_vcp); + + var d6 = _vac.dot(_vcp); + + if (d6 >= 0 && d5 <= d6) { + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy(c); + } + + var vb = d5 * d2 - d1 * d6; + + if (vb <= 0 && d2 >= 0 && d6 <= 0) { + w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) + + return target.copy(a).addScaledVector(_vac, w); + } + + var va = d3 * d6 - d5 * d4; + + if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { + _vbc.subVectors(c, b); + + w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) + + return target.copy(b).addScaledVector(_vbc, w); // edge region of BC + } // face region + + + var denom = 1 / (va + vb + vc); // u = va * denom + + v = vb * denom; + w = vc * denom; + return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); + }; + + _proto.equals = function equals(triangle) { + return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); + }; + + return Triangle; + }(); + + var materialId = 0; + + function Material() { + Object.defineProperty(this, 'id', { + value: materialId++ + }); + this.uuid = MathUtils.generateUUID(); + this.name = ''; + this.type = 'Material'; + this.fog = true; + this.blending = NormalBlending; + this.side = FrontSide; + this.vertexColors = false; + this.opacity = 1; + this.transparent = false; + this.blendSrc = SrcAlphaFactor; + this.blendDst = OneMinusSrcAlphaFactor; + this.blendEquation = AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; + this.depthFunc = LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; + this.stencilWriteMask = 0xff; + this.stencilFunc = AlwaysStencilFunc; + this.stencilRef = 0; + this.stencilFuncMask = 0xff; + this.stencilFail = KeepStencilOp; + this.stencilZFail = KeepStencilOp; + this.stencilZPass = KeepStencilOp; + this.stencilWrite = false; + this.clippingPlanes = null; + this.clipIntersection = false; + this.clipShadows = false; + this.shadowSide = null; + this.colorWrite = true; + this.precision = null; // override the renderer's default precision for this material + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + this.dithering = false; + this.alphaTest = 0; + this.premultipliedAlpha = false; + this.visible = true; + this.toneMapped = true; + this.userData = {}; + this.version = 0; + } + + Material.prototype = Object.assign(Object.create(EventDispatcher.prototype), { + constructor: Material, + isMaterial: true, + onBeforeCompile: function onBeforeCompile() + /* shaderobject, renderer */ + {}, + customProgramCacheKey: function customProgramCacheKey() { + return this.onBeforeCompile.toString(); + }, + setValues: function setValues(values) { + if (values === undefined) return; + + for (var key in values) { + var newValue = values[key]; + + if (newValue === undefined) { + console.warn('THREE.Material: \'' + key + '\' parameter is undefined.'); + continue; + } // for backward compatability if shading is set in the constructor + + + if (key === 'shading') { + console.warn('THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.'); + this.flatShading = newValue === FlatShading ? true : false; + continue; + } + + var currentValue = this[key]; + + if (currentValue === undefined) { + console.warn('THREE.' + this.type + ': \'' + key + '\' is not a property of this material.'); + continue; + } + + if (currentValue && currentValue.isColor) { + currentValue.set(newValue); + } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { + currentValue.copy(newValue); + } else { + this[key] = newValue; + } + } + }, + toJSON: function toJSON(meta) { + var isRoot = meta === undefined || typeof meta === 'string'; + + if (isRoot) { + meta = { + textures: {}, + images: {} + }; + } + + var data = { + metadata: { + version: 4.5, + type: 'Material', + generator: 'Material.toJSON' + } + }; // standard Material serialization + + data.uuid = this.uuid; + data.type = this.type; + if (this.name !== '') data.name = this.name; + if (this.color && this.color.isColor) data.color = this.color.getHex(); + if (this.roughness !== undefined) data.roughness = this.roughness; + if (this.metalness !== undefined) data.metalness = this.metalness; + if (this.sheen && this.sheen.isColor) data.sheen = this.sheen.getHex(); + if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); + if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; + if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); + if (this.shininess !== undefined) data.shininess = this.shininess; + if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; + if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; + + if (this.clearcoatMap && this.clearcoatMap.isTexture) { + data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; + } + + if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { + data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; + } + + if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { + data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; + data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); + } + + if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; + if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; + if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; + + if (this.lightMap && this.lightMap.isTexture) { + data.lightMap = this.lightMap.toJSON(meta).uuid; + data.lightMapIntensity = this.lightMapIntensity; + } + + if (this.aoMap && this.aoMap.isTexture) { + data.aoMap = this.aoMap.toJSON(meta).uuid; + data.aoMapIntensity = this.aoMapIntensity; + } + + if (this.bumpMap && this.bumpMap.isTexture) { + data.bumpMap = this.bumpMap.toJSON(meta).uuid; + data.bumpScale = this.bumpScale; + } + + if (this.normalMap && this.normalMap.isTexture) { + data.normalMap = this.normalMap.toJSON(meta).uuid; + data.normalMapType = this.normalMapType; + data.normalScale = this.normalScale.toArray(); + } + + if (this.displacementMap && this.displacementMap.isTexture) { + data.displacementMap = this.displacementMap.toJSON(meta).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; + } + + if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; + if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; + if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; + if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; + + if (this.envMap && this.envMap.isTexture) { + data.envMap = this.envMap.toJSON(meta).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap + + data.refractionRatio = this.refractionRatio; + if (this.combine !== undefined) data.combine = this.combine; + if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; + } + + if (this.gradientMap && this.gradientMap.isTexture) { + data.gradientMap = this.gradientMap.toJSON(meta).uuid; + } + + if (this.size !== undefined) data.size = this.size; + if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; + if (this.blending !== NormalBlending) data.blending = this.blending; + if (this.side !== FrontSide) data.side = this.side; + if (this.vertexColors) data.vertexColors = true; + if (this.opacity < 1) data.opacity = this.opacity; + if (this.transparent === true) data.transparent = this.transparent; + data.depthFunc = this.depthFunc; + data.depthTest = this.depthTest; + data.depthWrite = this.depthWrite; + data.stencilWrite = this.stencilWrite; + data.stencilWriteMask = this.stencilWriteMask; + data.stencilFunc = this.stencilFunc; + data.stencilRef = this.stencilRef; + data.stencilFuncMask = this.stencilFuncMask; + data.stencilFail = this.stencilFail; + data.stencilZFail = this.stencilZFail; + data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) + + if (this.rotation && this.rotation !== 0) data.rotation = this.rotation; + if (this.polygonOffset === true) data.polygonOffset = true; + if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; + if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; + if (this.linewidth && this.linewidth !== 1) data.linewidth = this.linewidth; + if (this.dashSize !== undefined) data.dashSize = this.dashSize; + if (this.gapSize !== undefined) data.gapSize = this.gapSize; + if (this.scale !== undefined) data.scale = this.scale; + if (this.dithering === true) data.dithering = true; + if (this.alphaTest > 0) data.alphaTest = this.alphaTest; + if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; + if (this.wireframe === true) data.wireframe = this.wireframe; + if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; + if (this.wireframeLinecap !== 'round') data.wireframeLinecap = this.wireframeLinecap; + if (this.wireframeLinejoin !== 'round') data.wireframeLinejoin = this.wireframeLinejoin; + if (this.morphTargets === true) data.morphTargets = true; + if (this.morphNormals === true) data.morphNormals = true; + if (this.skinning === true) data.skinning = true; + if (this.flatShading === true) data.flatShading = this.flatShading; + if (this.visible === false) data.visible = false; + if (this.toneMapped === false) data.toneMapped = false; + if (JSON.stringify(this.userData) !== '{}') data.userData = this.userData; // TODO: Copied from Object3D.toJSON + + function extractFromCache(cache) { + var values = []; + + for (var key in cache) { + var _data = cache[key]; + delete _data.metadata; + values.push(_data); + } + + return values; + } + + if (isRoot) { + var textures = extractFromCache(meta.textures); + var images = extractFromCache(meta.images); + if (textures.length > 0) data.textures = textures; + if (images.length > 0) data.images = images; + } + + return data; + }, + clone: function clone() { + return new this.constructor().copy(this); + }, + copy: function copy(source) { + this.name = source.name; + this.fog = source.fog; + this.blending = source.blending; + this.side = source.side; + this.vertexColors = source.vertexColors; + this.opacity = source.opacity; + this.transparent = source.transparent; + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; + this.stencilWriteMask = source.stencilWriteMask; + this.stencilFunc = source.stencilFunc; + this.stencilRef = source.stencilRef; + this.stencilFuncMask = source.stencilFuncMask; + this.stencilFail = source.stencilFail; + this.stencilZFail = source.stencilZFail; + this.stencilZPass = source.stencilZPass; + this.stencilWrite = source.stencilWrite; + var srcPlanes = source.clippingPlanes; + var dstPlanes = null; + + if (srcPlanes !== null) { + var n = srcPlanes.length; + dstPlanes = new Array(n); + + for (var i = 0; i !== n; ++i) { + dstPlanes[i] = srcPlanes[i].clone(); + } + } + + this.clippingPlanes = dstPlanes; + this.clipIntersection = source.clipIntersection; + this.clipShadows = source.clipShadows; + this.shadowSide = source.shadowSide; + this.colorWrite = source.colorWrite; + this.precision = source.precision; + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; + this.dithering = source.dithering; + this.alphaTest = source.alphaTest; + this.premultipliedAlpha = source.premultipliedAlpha; + this.visible = source.visible; + this.toneMapped = source.toneMapped; + this.userData = JSON.parse(JSON.stringify(source.userData)); + return this; + }, + dispose: function dispose() { + this.dispatchEvent({ + type: 'dispose' + }); + } + }); + Object.defineProperty(Material.prototype, 'needsUpdate', { + set: function set(value) { + if (value === true) this.version++; + } + }); + + var _colorKeywords = { + 'aliceblue': 0xF0F8FF, + 'antiquewhite': 0xFAEBD7, + 'aqua': 0x00FFFF, + 'aquamarine': 0x7FFFD4, + 'azure': 0xF0FFFF, + 'beige': 0xF5F5DC, + 'bisque': 0xFFE4C4, + 'black': 0x000000, + 'blanchedalmond': 0xFFEBCD, + 'blue': 0x0000FF, + 'blueviolet': 0x8A2BE2, + 'brown': 0xA52A2A, + 'burlywood': 0xDEB887, + 'cadetblue': 0x5F9EA0, + 'chartreuse': 0x7FFF00, + 'chocolate': 0xD2691E, + 'coral': 0xFF7F50, + 'cornflowerblue': 0x6495ED, + 'cornsilk': 0xFFF8DC, + 'crimson': 0xDC143C, + 'cyan': 0x00FFFF, + 'darkblue': 0x00008B, + 'darkcyan': 0x008B8B, + 'darkgoldenrod': 0xB8860B, + 'darkgray': 0xA9A9A9, + 'darkgreen': 0x006400, + 'darkgrey': 0xA9A9A9, + 'darkkhaki': 0xBDB76B, + 'darkmagenta': 0x8B008B, + 'darkolivegreen': 0x556B2F, + 'darkorange': 0xFF8C00, + 'darkorchid': 0x9932CC, + 'darkred': 0x8B0000, + 'darksalmon': 0xE9967A, + 'darkseagreen': 0x8FBC8F, + 'darkslateblue': 0x483D8B, + 'darkslategray': 0x2F4F4F, + 'darkslategrey': 0x2F4F4F, + 'darkturquoise': 0x00CED1, + 'darkviolet': 0x9400D3, + 'deeppink': 0xFF1493, + 'deepskyblue': 0x00BFFF, + 'dimgray': 0x696969, + 'dimgrey': 0x696969, + 'dodgerblue': 0x1E90FF, + 'firebrick': 0xB22222, + 'floralwhite': 0xFFFAF0, + 'forestgreen': 0x228B22, + 'fuchsia': 0xFF00FF, + 'gainsboro': 0xDCDCDC, + 'ghostwhite': 0xF8F8FF, + 'gold': 0xFFD700, + 'goldenrod': 0xDAA520, + 'gray': 0x808080, + 'green': 0x008000, + 'greenyellow': 0xADFF2F, + 'grey': 0x808080, + 'honeydew': 0xF0FFF0, + 'hotpink': 0xFF69B4, + 'indianred': 0xCD5C5C, + 'indigo': 0x4B0082, + 'ivory': 0xFFFFF0, + 'khaki': 0xF0E68C, + 'lavender': 0xE6E6FA, + 'lavenderblush': 0xFFF0F5, + 'lawngreen': 0x7CFC00, + 'lemonchiffon': 0xFFFACD, + 'lightblue': 0xADD8E6, + 'lightcoral': 0xF08080, + 'lightcyan': 0xE0FFFF, + 'lightgoldenrodyellow': 0xFAFAD2, + 'lightgray': 0xD3D3D3, + 'lightgreen': 0x90EE90, + 'lightgrey': 0xD3D3D3, + 'lightpink': 0xFFB6C1, + 'lightsalmon': 0xFFA07A, + 'lightseagreen': 0x20B2AA, + 'lightskyblue': 0x87CEFA, + 'lightslategray': 0x778899, + 'lightslategrey': 0x778899, + 'lightsteelblue': 0xB0C4DE, + 'lightyellow': 0xFFFFE0, + 'lime': 0x00FF00, + 'limegreen': 0x32CD32, + 'linen': 0xFAF0E6, + 'magenta': 0xFF00FF, + 'maroon': 0x800000, + 'mediumaquamarine': 0x66CDAA, + 'mediumblue': 0x0000CD, + 'mediumorchid': 0xBA55D3, + 'mediumpurple': 0x9370DB, + 'mediumseagreen': 0x3CB371, + 'mediumslateblue': 0x7B68EE, + 'mediumspringgreen': 0x00FA9A, + 'mediumturquoise': 0x48D1CC, + 'mediumvioletred': 0xC71585, + 'midnightblue': 0x191970, + 'mintcream': 0xF5FFFA, + 'mistyrose': 0xFFE4E1, + 'moccasin': 0xFFE4B5, + 'navajowhite': 0xFFDEAD, + 'navy': 0x000080, + 'oldlace': 0xFDF5E6, + 'olive': 0x808000, + 'olivedrab': 0x6B8E23, + 'orange': 0xFFA500, + 'orangered': 0xFF4500, + 'orchid': 0xDA70D6, + 'palegoldenrod': 0xEEE8AA, + 'palegreen': 0x98FB98, + 'paleturquoise': 0xAFEEEE, + 'palevioletred': 0xDB7093, + 'papayawhip': 0xFFEFD5, + 'peachpuff': 0xFFDAB9, + 'peru': 0xCD853F, + 'pink': 0xFFC0CB, + 'plum': 0xDDA0DD, + 'powderblue': 0xB0E0E6, + 'purple': 0x800080, + 'rebeccapurple': 0x663399, + 'red': 0xFF0000, + 'rosybrown': 0xBC8F8F, + 'royalblue': 0x4169E1, + 'saddlebrown': 0x8B4513, + 'salmon': 0xFA8072, + 'sandybrown': 0xF4A460, + 'seagreen': 0x2E8B57, + 'seashell': 0xFFF5EE, + 'sienna': 0xA0522D, + 'silver': 0xC0C0C0, + 'skyblue': 0x87CEEB, + 'slateblue': 0x6A5ACD, + 'slategray': 0x708090, + 'slategrey': 0x708090, + 'snow': 0xFFFAFA, + 'springgreen': 0x00FF7F, + 'steelblue': 0x4682B4, + 'tan': 0xD2B48C, + 'teal': 0x008080, + 'thistle': 0xD8BFD8, + 'tomato': 0xFF6347, + 'turquoise': 0x40E0D0, + 'violet': 0xEE82EE, + 'wheat': 0xF5DEB3, + 'white': 0xFFFFFF, + 'whitesmoke': 0xF5F5F5, + 'yellow': 0xFFFF00, + 'yellowgreen': 0x9ACD32 + }; + var _hslA = { + h: 0, + s: 0, + l: 0 + }; + var _hslB = { + h: 0, + s: 0, + l: 0 + }; + + function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); + return p; + } + + function SRGBToLinear(c) { + return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); + } + + function LinearToSRGB(c) { + return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; + } + + var Color = /*#__PURE__*/function () { + function Color(r, g, b) { + if (g === undefined && b === undefined) { + // r is THREE.Color, hex or string + return this.set(r); + } + + return this.setRGB(r, g, b); + } + + var _proto = Color.prototype; + + _proto.set = function set(value) { + if (value && value.isColor) { + this.copy(value); + } else if (typeof value === 'number') { + this.setHex(value); + } else if (typeof value === 'string') { + this.setStyle(value); + } + + return this; + }; + + _proto.setScalar = function setScalar(scalar) { + this.r = scalar; + this.g = scalar; + this.b = scalar; + return this; + }; + + _proto.setHex = function setHex(hex) { + hex = Math.floor(hex); + this.r = (hex >> 16 & 255) / 255; + this.g = (hex >> 8 & 255) / 255; + this.b = (hex & 255) / 255; + return this; + }; + + _proto.setRGB = function setRGB(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + return this; + }; + + _proto.setHSL = function setHSL(h, s, l) { + // h,s,l ranges are in 0.0 - 1.0 + h = MathUtils.euclideanModulo(h, 1); + s = MathUtils.clamp(s, 0, 1); + l = MathUtils.clamp(l, 0, 1); + + if (s === 0) { + this.r = this.g = this.b = l; + } else { + var p = l <= 0.5 ? l * (1 + s) : l + s - l * s; + var q = 2 * l - p; + this.r = hue2rgb(q, p, h + 1 / 3); + this.g = hue2rgb(q, p, h); + this.b = hue2rgb(q, p, h - 1 / 3); + } + + return this; + }; + + _proto.setStyle = function setStyle(style) { + function handleAlpha(string) { + if (string === undefined) return; + + if (parseFloat(string) < 1) { + console.warn('THREE.Color: Alpha component of ' + style + ' will be ignored.'); + } + } + + var m; + + if (m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(style)) { + // rgb / hsl + var color; + var name = m[1]; + var components = m[2]; + + switch (name) { + case 'rgb': + case 'rgba': + if (color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { + // rgb(255,0,0) rgba(255,0,0,0.5) + this.r = Math.min(255, parseInt(color[1], 10)) / 255; + this.g = Math.min(255, parseInt(color[2], 10)) / 255; + this.b = Math.min(255, parseInt(color[3], 10)) / 255; + handleAlpha(color[4]); + return this; + } + + if (color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) + this.r = Math.min(100, parseInt(color[1], 10)) / 100; + this.g = Math.min(100, parseInt(color[2], 10)) / 100; + this.b = Math.min(100, parseInt(color[3], 10)) / 100; + handleAlpha(color[4]); + return this; + } + + break; + + case 'hsl': + case 'hsla': + if (color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + var h = parseFloat(color[1]) / 360; + var s = parseInt(color[2], 10) / 100; + var l = parseInt(color[3], 10) / 100; + handleAlpha(color[4]); + return this.setHSL(h, s, l); + } + + break; + } + } else if (m = /^\#([A-Fa-f\d]+)$/.exec(style)) { + // hex color + var hex = m[1]; + var size = hex.length; + + if (size === 3) { + // #ff0 + this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; + this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; + this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; + return this; + } else if (size === 6) { + // #ff0000 + this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; + this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; + this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; + return this; + } + } + + if (style && style.length > 0) { + return this.setColorName(style); + } + + return this; + }; + + _proto.setColorName = function setColorName(style) { + // color keywords + var hex = _colorKeywords[style]; + + if (hex !== undefined) { + // red + this.setHex(hex); + } else { + // unknown color + console.warn('THREE.Color: Unknown color ' + style); + } + + return this; + }; + + _proto.clone = function clone() { + return new this.constructor(this.r, this.g, this.b); + }; + + _proto.copy = function copy(color) { + this.r = color.r; + this.g = color.g; + this.b = color.b; + return this; + }; + + _proto.copyGammaToLinear = function copyGammaToLinear(color, gammaFactor) { + if (gammaFactor === void 0) { + gammaFactor = 2.0; + } + + this.r = Math.pow(color.r, gammaFactor); + this.g = Math.pow(color.g, gammaFactor); + this.b = Math.pow(color.b, gammaFactor); + return this; + }; + + _proto.copyLinearToGamma = function copyLinearToGamma(color, gammaFactor) { + if (gammaFactor === void 0) { + gammaFactor = 2.0; + } + + var safeInverse = gammaFactor > 0 ? 1.0 / gammaFactor : 1.0; + this.r = Math.pow(color.r, safeInverse); + this.g = Math.pow(color.g, safeInverse); + this.b = Math.pow(color.b, safeInverse); + return this; + }; + + _proto.convertGammaToLinear = function convertGammaToLinear(gammaFactor) { + this.copyGammaToLinear(this, gammaFactor); + return this; + }; + + _proto.convertLinearToGamma = function convertLinearToGamma(gammaFactor) { + this.copyLinearToGamma(this, gammaFactor); + return this; + }; + + _proto.copySRGBToLinear = function copySRGBToLinear(color) { + this.r = SRGBToLinear(color.r); + this.g = SRGBToLinear(color.g); + this.b = SRGBToLinear(color.b); + return this; + }; + + _proto.copyLinearToSRGB = function copyLinearToSRGB(color) { + this.r = LinearToSRGB(color.r); + this.g = LinearToSRGB(color.g); + this.b = LinearToSRGB(color.b); + return this; + }; + + _proto.convertSRGBToLinear = function convertSRGBToLinear() { + this.copySRGBToLinear(this); + return this; + }; + + _proto.convertLinearToSRGB = function convertLinearToSRGB() { + this.copyLinearToSRGB(this); + return this; + }; + + _proto.getHex = function getHex() { + return this.r * 255 << 16 ^ this.g * 255 << 8 ^ this.b * 255 << 0; + }; + + _proto.getHexString = function getHexString() { + return ('000000' + this.getHex().toString(16)).slice(-6); + }; + + _proto.getHSL = function getHSL(target) { + // h,s,l ranges are in 0.0 - 1.0 + if (target === undefined) { + console.warn('THREE.Color: .getHSL() target is now required'); + target = { + h: 0, + s: 0, + l: 0 + }; + } + + var r = this.r, + g = this.g, + b = this.b; + var max = Math.max(r, g, b); + var min = Math.min(r, g, b); + var hue, saturation; + var lightness = (min + max) / 2.0; + + if (min === max) { + hue = 0; + saturation = 0; + } else { + var delta = max - min; + saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); + + switch (max) { + case r: + hue = (g - b) / delta + (g < b ? 6 : 0); + break; + + case g: + hue = (b - r) / delta + 2; + break; + + case b: + hue = (r - g) / delta + 4; + break; + } + + hue /= 6; + } + + target.h = hue; + target.s = saturation; + target.l = lightness; + return target; + }; + + _proto.getStyle = function getStyle() { + return 'rgb(' + (this.r * 255 | 0) + ',' + (this.g * 255 | 0) + ',' + (this.b * 255 | 0) + ')'; + }; + + _proto.offsetHSL = function offsetHSL(h, s, l) { + this.getHSL(_hslA); + _hslA.h += h; + _hslA.s += s; + _hslA.l += l; + this.setHSL(_hslA.h, _hslA.s, _hslA.l); + return this; + }; + + _proto.add = function add(color) { + this.r += color.r; + this.g += color.g; + this.b += color.b; + return this; + }; + + _proto.addColors = function addColors(color1, color2) { + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + return this; + }; + + _proto.addScalar = function addScalar(s) { + this.r += s; + this.g += s; + this.b += s; + return this; + }; + + _proto.sub = function sub(color) { + this.r = Math.max(0, this.r - color.r); + this.g = Math.max(0, this.g - color.g); + this.b = Math.max(0, this.b - color.b); + return this; + }; + + _proto.multiply = function multiply(color) { + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + return this; + }; + + _proto.multiplyScalar = function multiplyScalar(s) { + this.r *= s; + this.g *= s; + this.b *= s; + return this; + }; + + _proto.lerp = function lerp(color, alpha) { + this.r += (color.r - this.r) * alpha; + this.g += (color.g - this.g) * alpha; + this.b += (color.b - this.b) * alpha; + return this; + }; + + _proto.lerpColors = function lerpColors(color1, color2, alpha) { + this.r = color1.r + (color2.r - color1.r) * alpha; + this.g = color1.g + (color2.g - color1.g) * alpha; + this.b = color1.b + (color2.b - color1.b) * alpha; + return this; + }; + + _proto.lerpHSL = function lerpHSL(color, alpha) { + this.getHSL(_hslA); + color.getHSL(_hslB); + var h = MathUtils.lerp(_hslA.h, _hslB.h, alpha); + var s = MathUtils.lerp(_hslA.s, _hslB.s, alpha); + var l = MathUtils.lerp(_hslA.l, _hslB.l, alpha); + this.setHSL(h, s, l); + return this; + }; + + _proto.equals = function equals(c) { + return c.r === this.r && c.g === this.g && c.b === this.b; + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + this.r = array[offset]; + this.g = array[offset + 1]; + this.b = array[offset + 2]; + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + array[offset] = this.r; + array[offset + 1] = this.g; + array[offset + 2] = this.b; + return array; + }; + + _proto.fromBufferAttribute = function fromBufferAttribute(attribute, index) { + this.r = attribute.getX(index); + this.g = attribute.getY(index); + this.b = attribute.getZ(index); + + if (attribute.normalized === true) { + // assuming Uint8Array + this.r /= 255; + this.g /= 255; + this.b /= 255; + } + + return this; + }; + + _proto.toJSON = function toJSON() { + return this.getHex(); + }; + + return Color; + }(); + + Color.NAMES = _colorKeywords; + Color.prototype.isColor = true; + Color.prototype.r = 1; + Color.prototype.g = 1; + Color.prototype.b = 1; + + /** + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: + * } + */ + + var MeshBasicMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshBasicMaterial, _Material); + + function MeshBasicMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshBasicMaterial'; + _this.color = new Color(0xffffff); // emissive + + _this.map = null; + _this.lightMap = null; + _this.lightMapIntensity = 1.0; + _this.aoMap = null; + _this.aoMapIntensity = 1.0; + _this.specularMap = null; + _this.alphaMap = null; + _this.envMap = null; + _this.combine = MultiplyOperation; + _this.reflectivity = 1; + _this.refractionRatio = 0.98; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.wireframeLinecap = 'round'; + _this.wireframeLinejoin = 'round'; + _this.skinning = false; + _this.morphTargets = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshBasicMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.map = source.map; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.specularMap = source.specularMap; + this.alphaMap = source.alphaMap; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + return this; + }; + + return MeshBasicMaterial; + }(Material); + + MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + + var _vector$3 = new Vector3(); + + var _vector2$1 = new Vector2(); + + function BufferAttribute(array, itemSize, normalized) { + if (Array.isArray(array)) { + throw new TypeError('THREE.BufferAttribute: array should be a Typed Array.'); + } + + this.name = ''; + this.array = array; + this.itemSize = itemSize; + this.count = array !== undefined ? array.length / itemSize : 0; + this.normalized = normalized === true; + this.usage = StaticDrawUsage; + this.updateRange = { + offset: 0, + count: -1 + }; + this.version = 0; + } + + Object.defineProperty(BufferAttribute.prototype, 'needsUpdate', { + set: function set(value) { + if (value === true) this.version++; + } + }); + Object.assign(BufferAttribute.prototype, { + isBufferAttribute: true, + onUploadCallback: function onUploadCallback() {}, + setUsage: function setUsage(value) { + this.usage = value; + return this; + }, + copy: function copy(source) { + this.name = source.name; + this.array = new source.array.constructor(source.array); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; + this.usage = source.usage; + return this; + }, + copyAt: function copyAt(index1, attribute, index2) { + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for (var i = 0, l = this.itemSize; i < l; i++) { + this.array[index1 + i] = attribute.array[index2 + i]; + } + + return this; + }, + copyArray: function copyArray(array) { + this.array.set(array); + return this; + }, + copyColorsArray: function copyColorsArray(colors) { + var array = this.array; + var offset = 0; + + for (var i = 0, l = colors.length; i < l; i++) { + var color = colors[i]; + + if (color === undefined) { + console.warn('THREE.BufferAttribute.copyColorsArray(): color is undefined', i); + color = new Color(); + } + + array[offset++] = color.r; + array[offset++] = color.g; + array[offset++] = color.b; + } + + return this; + }, + copyVector2sArray: function copyVector2sArray(vectors) { + var array = this.array; + var offset = 0; + + for (var i = 0, l = vectors.length; i < l; i++) { + var vector = vectors[i]; + + if (vector === undefined) { + console.warn('THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i); + vector = new Vector2(); + } + + array[offset++] = vector.x; + array[offset++] = vector.y; + } + + return this; + }, + copyVector3sArray: function copyVector3sArray(vectors) { + var array = this.array; + var offset = 0; + + for (var i = 0, l = vectors.length; i < l; i++) { + var vector = vectors[i]; + + if (vector === undefined) { + console.warn('THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i); + vector = new Vector3(); + } + + array[offset++] = vector.x; + array[offset++] = vector.y; + array[offset++] = vector.z; + } + + return this; + }, + copyVector4sArray: function copyVector4sArray(vectors) { + var array = this.array; + var offset = 0; + + for (var i = 0, l = vectors.length; i < l; i++) { + var vector = vectors[i]; + + if (vector === undefined) { + console.warn('THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i); + vector = new Vector4(); + } + + array[offset++] = vector.x; + array[offset++] = vector.y; + array[offset++] = vector.z; + array[offset++] = vector.w; + } + + return this; + }, + applyMatrix3: function applyMatrix3(m) { + if (this.itemSize === 2) { + for (var i = 0, l = this.count; i < l; i++) { + _vector2$1.fromBufferAttribute(this, i); + + _vector2$1.applyMatrix3(m); + + this.setXY(i, _vector2$1.x, _vector2$1.y); + } + } else if (this.itemSize === 3) { + for (var _i = 0, _l = this.count; _i < _l; _i++) { + _vector$3.fromBufferAttribute(this, _i); + + _vector$3.applyMatrix3(m); + + this.setXYZ(_i, _vector$3.x, _vector$3.y, _vector$3.z); + } + } + + return this; + }, + applyMatrix4: function applyMatrix4(m) { + for (var i = 0, l = this.count; i < l; i++) { + _vector$3.x = this.getX(i); + _vector$3.y = this.getY(i); + _vector$3.z = this.getZ(i); + + _vector$3.applyMatrix4(m); + + this.setXYZ(i, _vector$3.x, _vector$3.y, _vector$3.z); + } + + return this; + }, + applyNormalMatrix: function applyNormalMatrix(m) { + for (var i = 0, l = this.count; i < l; i++) { + _vector$3.x = this.getX(i); + _vector$3.y = this.getY(i); + _vector$3.z = this.getZ(i); + + _vector$3.applyNormalMatrix(m); + + this.setXYZ(i, _vector$3.x, _vector$3.y, _vector$3.z); + } + + return this; + }, + transformDirection: function transformDirection(m) { + for (var i = 0, l = this.count; i < l; i++) { + _vector$3.x = this.getX(i); + _vector$3.y = this.getY(i); + _vector$3.z = this.getZ(i); + + _vector$3.transformDirection(m); + + this.setXYZ(i, _vector$3.x, _vector$3.y, _vector$3.z); + } + + return this; + }, + set: function set(value, offset) { + if (offset === void 0) { + offset = 0; + } + + this.array.set(value, offset); + return this; + }, + getX: function getX(index) { + return this.array[index * this.itemSize]; + }, + setX: function setX(index, x) { + this.array[index * this.itemSize] = x; + return this; + }, + getY: function getY(index) { + return this.array[index * this.itemSize + 1]; + }, + setY: function setY(index, y) { + this.array[index * this.itemSize + 1] = y; + return this; + }, + getZ: function getZ(index) { + return this.array[index * this.itemSize + 2]; + }, + setZ: function setZ(index, z) { + this.array[index * this.itemSize + 2] = z; + return this; + }, + getW: function getW(index) { + return this.array[index * this.itemSize + 3]; + }, + setW: function setW(index, w) { + this.array[index * this.itemSize + 3] = w; + return this; + }, + setXY: function setXY(index, x, y) { + index *= this.itemSize; + this.array[index + 0] = x; + this.array[index + 1] = y; + return this; + }, + setXYZ: function setXYZ(index, x, y, z) { + index *= this.itemSize; + this.array[index + 0] = x; + this.array[index + 1] = y; + this.array[index + 2] = z; + return this; + }, + setXYZW: function setXYZW(index, x, y, z, w) { + index *= this.itemSize; + this.array[index + 0] = x; + this.array[index + 1] = y; + this.array[index + 2] = z; + this.array[index + 3] = w; + return this; + }, + onUpload: function onUpload(callback) { + this.onUploadCallback = callback; + return this; + }, + clone: function clone() { + return new this.constructor(this.array, this.itemSize).copy(this); + }, + toJSON: function toJSON() { + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: Array.prototype.slice.call(this.array), + normalized: this.normalized + }; + } + }); // + + function Int8BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Int8Array(array), itemSize, normalized); + } + + Int8BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; + + function Uint8BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Uint8Array(array), itemSize, normalized); + } + + Uint8BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; + + function Uint8ClampedBufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Uint8ClampedArray(array), itemSize, normalized); + } + + Uint8ClampedBufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; + + function Int16BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Int16Array(array), itemSize, normalized); + } + + Int16BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; + + function Uint16BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Uint16Array(array), itemSize, normalized); + } + + Uint16BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; + + function Int32BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Int32Array(array), itemSize, normalized); + } + + Int32BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; + + function Uint32BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Uint32Array(array), itemSize, normalized); + } + + Uint32BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; + + function Float16BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Uint16Array(array), itemSize, normalized); + } + + Float16BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Float16BufferAttribute.prototype.constructor = Float16BufferAttribute; + Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; + + function Float32BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Float32Array(array), itemSize, normalized); + } + + Float32BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; + + function Float64BufferAttribute(array, itemSize, normalized) { + BufferAttribute.call(this, new Float64Array(array), itemSize, normalized); + } + + Float64BufferAttribute.prototype = Object.create(BufferAttribute.prototype); + Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; // + + function arrayMax(array) { + if (array.length === 0) return -Infinity; + var max = array[0]; + + for (var i = 1, l = array.length; i < l; ++i) { + if (array[i] > max) max = array[i]; + } + + return max; + } + + var TYPED_ARRAYS = { + Int8Array: Int8Array, + Uint8Array: Uint8Array, + Uint8ClampedArray: Uint8ClampedArray, + Int16Array: Int16Array, + Uint16Array: Uint16Array, + Int32Array: Int32Array, + Uint32Array: Uint32Array, + Float32Array: Float32Array, + Float64Array: Float64Array + }; + + function getTypedArray(type, buffer) { + return new TYPED_ARRAYS[type](buffer); + } + + var _id = 0; + + var _m1$2 = new Matrix4(); + + var _obj = new Object3D(); + + var _offset = new Vector3(); + + var _box$2 = new Box3(); + + var _boxMorphTargets = new Box3(); + + var _vector$4 = new Vector3(); + + function BufferGeometry() { + Object.defineProperty(this, 'id', { + value: _id++ + }); + this.uuid = MathUtils.generateUUID(); + this.name = ''; + this.type = 'BufferGeometry'; + this.index = null; + this.attributes = {}; + this.morphAttributes = {}; + this.morphTargetsRelative = false; + this.groups = []; + this.boundingBox = null; + this.boundingSphere = null; + this.drawRange = { + start: 0, + count: Infinity + }; + this.userData = {}; + } + + BufferGeometry.prototype = Object.assign(Object.create(EventDispatcher.prototype), { + constructor: BufferGeometry, + isBufferGeometry: true, + getIndex: function getIndex() { + return this.index; + }, + setIndex: function setIndex(index) { + if (Array.isArray(index)) { + this.index = new (arrayMax(index) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); + } else { + this.index = index; + } + + return this; + }, + getAttribute: function getAttribute(name) { + return this.attributes[name]; + }, + setAttribute: function setAttribute(name, attribute) { + this.attributes[name] = attribute; + return this; + }, + deleteAttribute: function deleteAttribute(name) { + delete this.attributes[name]; + return this; + }, + hasAttribute: function hasAttribute(name) { + return this.attributes[name] !== undefined; + }, + addGroup: function addGroup(start, count, materialIndex) { + if (materialIndex === void 0) { + materialIndex = 0; + } + + this.groups.push({ + start: start, + count: count, + materialIndex: materialIndex + }); + }, + clearGroups: function clearGroups() { + this.groups = []; + }, + setDrawRange: function setDrawRange(start, count) { + this.drawRange.start = start; + this.drawRange.count = count; + }, + applyMatrix4: function applyMatrix4(matrix) { + var position = this.attributes.position; + + if (position !== undefined) { + position.applyMatrix4(matrix); + position.needsUpdate = true; + } + + var normal = this.attributes.normal; + + if (normal !== undefined) { + var normalMatrix = new Matrix3().getNormalMatrix(matrix); + normal.applyNormalMatrix(normalMatrix); + normal.needsUpdate = true; + } + + var tangent = this.attributes.tangent; + + if (tangent !== undefined) { + tangent.transformDirection(matrix); + tangent.needsUpdate = true; + } + + if (this.boundingBox !== null) { + this.computeBoundingBox(); + } + + if (this.boundingSphere !== null) { + this.computeBoundingSphere(); + } + + return this; + }, + rotateX: function rotateX(angle) { + // rotate geometry around world x-axis + _m1$2.makeRotationX(angle); + + this.applyMatrix4(_m1$2); + return this; + }, + rotateY: function rotateY(angle) { + // rotate geometry around world y-axis + _m1$2.makeRotationY(angle); + + this.applyMatrix4(_m1$2); + return this; + }, + rotateZ: function rotateZ(angle) { + // rotate geometry around world z-axis + _m1$2.makeRotationZ(angle); + + this.applyMatrix4(_m1$2); + return this; + }, + translate: function translate(x, y, z) { + // translate geometry + _m1$2.makeTranslation(x, y, z); + + this.applyMatrix4(_m1$2); + return this; + }, + scale: function scale(x, y, z) { + // scale geometry + _m1$2.makeScale(x, y, z); + + this.applyMatrix4(_m1$2); + return this; + }, + lookAt: function lookAt(vector) { + _obj.lookAt(vector); + + _obj.updateMatrix(); + + this.applyMatrix4(_obj.matrix); + return this; + }, + center: function center() { + this.computeBoundingBox(); + this.boundingBox.getCenter(_offset).negate(); + this.translate(_offset.x, _offset.y, _offset.z); + return this; + }, + setFromPoints: function setFromPoints(points) { + var position = []; + + for (var i = 0, l = points.length; i < l; i++) { + var point = points[i]; + position.push(point.x, point.y, point.z || 0); + } + + this.setAttribute('position', new Float32BufferAttribute(position, 3)); + return this; + }, + computeBoundingBox: function computeBoundingBox() { + if (this.boundingBox === null) { + this.boundingBox = new Box3(); + } + + var position = this.attributes.position; + var morphAttributesPosition = this.morphAttributes.position; + + if (position && position.isGLBufferAttribute) { + console.error('THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this); + this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); + return; + } + + if (position !== undefined) { + this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present + + if (morphAttributesPosition) { + for (var i = 0, il = morphAttributesPosition.length; i < il; i++) { + var morphAttribute = morphAttributesPosition[i]; + + _box$2.setFromBufferAttribute(morphAttribute); + + if (this.morphTargetsRelative) { + _vector$4.addVectors(this.boundingBox.min, _box$2.min); + + this.boundingBox.expandByPoint(_vector$4); + + _vector$4.addVectors(this.boundingBox.max, _box$2.max); + + this.boundingBox.expandByPoint(_vector$4); + } else { + this.boundingBox.expandByPoint(_box$2.min); + this.boundingBox.expandByPoint(_box$2.max); + } + } + } + } else { + this.boundingBox.makeEmpty(); + } + + if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { + console.error('THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this); + } + }, + computeBoundingSphere: function computeBoundingSphere() { + if (this.boundingSphere === null) { + this.boundingSphere = new Sphere(); + } + + var position = this.attributes.position; + var morphAttributesPosition = this.morphAttributes.position; + + if (position && position.isGLBufferAttribute) { + console.error('THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this); + this.boundingSphere.set(new Vector3(), Infinity); + return; + } + + if (position) { + // first, find the center of the bounding sphere + var center = this.boundingSphere.center; + + _box$2.setFromBufferAttribute(position); // process morph attributes if present + + + if (morphAttributesPosition) { + for (var i = 0, il = morphAttributesPosition.length; i < il; i++) { + var morphAttribute = morphAttributesPosition[i]; + + _boxMorphTargets.setFromBufferAttribute(morphAttribute); + + if (this.morphTargetsRelative) { + _vector$4.addVectors(_box$2.min, _boxMorphTargets.min); + + _box$2.expandByPoint(_vector$4); + + _vector$4.addVectors(_box$2.max, _boxMorphTargets.max); + + _box$2.expandByPoint(_vector$4); + } else { + _box$2.expandByPoint(_boxMorphTargets.min); + + _box$2.expandByPoint(_boxMorphTargets.max); + } + } + } + + _box$2.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + + var maxRadiusSq = 0; + + for (var _i = 0, _il = position.count; _i < _il; _i++) { + _vector$4.fromBufferAttribute(position, _i); + + maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$4)); + } // process morph attributes if present + + + if (morphAttributesPosition) { + for (var _i2 = 0, _il2 = morphAttributesPosition.length; _i2 < _il2; _i2++) { + var _morphAttribute = morphAttributesPosition[_i2]; + var morphTargetsRelative = this.morphTargetsRelative; + + for (var j = 0, jl = _morphAttribute.count; j < jl; j++) { + _vector$4.fromBufferAttribute(_morphAttribute, j); + + if (morphTargetsRelative) { + _offset.fromBufferAttribute(position, j); + + _vector$4.add(_offset); + } + + maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$4)); + } + } + } + + this.boundingSphere.radius = Math.sqrt(maxRadiusSq); + + if (isNaN(this.boundingSphere.radius)) { + console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this); + } + } + }, + computeFaceNormals: function computeFaceNormals() {// backwards compatibility + }, + computeTangents: function computeTangents() { + var index = this.index; + var attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) + + if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { + console.error('THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)'); + return; + } + + var indices = index.array; + var positions = attributes.position.array; + var normals = attributes.normal.array; + var uvs = attributes.uv.array; + var nVertices = positions.length / 3; + + if (attributes.tangent === undefined) { + this.setAttribute('tangent', new BufferAttribute(new Float32Array(4 * nVertices), 4)); + } + + var tangents = attributes.tangent.array; + var tan1 = [], + tan2 = []; + + for (var i = 0; i < nVertices; i++) { + tan1[i] = new Vector3(); + tan2[i] = new Vector3(); + } + + var vA = new Vector3(), + vB = new Vector3(), + vC = new Vector3(), + uvA = new Vector2(), + uvB = new Vector2(), + uvC = new Vector2(), + sdir = new Vector3(), + tdir = new Vector3(); + + function handleTriangle(a, b, c) { + vA.fromArray(positions, a * 3); + vB.fromArray(positions, b * 3); + vC.fromArray(positions, c * 3); + uvA.fromArray(uvs, a * 2); + uvB.fromArray(uvs, b * 2); + uvC.fromArray(uvs, c * 2); + vB.sub(vA); + vC.sub(vA); + uvB.sub(uvA); + uvC.sub(uvA); + var r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices + + if (!isFinite(r)) return; + sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); + tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); + tan1[a].add(sdir); + tan1[b].add(sdir); + tan1[c].add(sdir); + tan2[a].add(tdir); + tan2[b].add(tdir); + tan2[c].add(tdir); + } + + var groups = this.groups; + + if (groups.length === 0) { + groups = [{ + start: 0, + count: indices.length + }]; + } + + for (var _i3 = 0, il = groups.length; _i3 < il; ++_i3) { + var group = groups[_i3]; + var start = group.start; + var count = group.count; + + for (var j = start, jl = start + count; j < jl; j += 3) { + handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); + } + } + + var tmp = new Vector3(), + tmp2 = new Vector3(); + var n = new Vector3(), + n2 = new Vector3(); + + function handleVertex(v) { + n.fromArray(normals, v * 3); + n2.copy(n); + var t = tan1[v]; // Gram-Schmidt orthogonalize + + tmp.copy(t); + tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness + + tmp2.crossVectors(n2, t); + var test = tmp2.dot(tan2[v]); + var w = test < 0.0 ? -1.0 : 1.0; + tangents[v * 4] = tmp.x; + tangents[v * 4 + 1] = tmp.y; + tangents[v * 4 + 2] = tmp.z; + tangents[v * 4 + 3] = w; + } + + for (var _i4 = 0, _il3 = groups.length; _i4 < _il3; ++_i4) { + var _group = groups[_i4]; + var _start = _group.start; + var _count = _group.count; + + for (var _j = _start, _jl = _start + _count; _j < _jl; _j += 3) { + handleVertex(indices[_j + 0]); + handleVertex(indices[_j + 1]); + handleVertex(indices[_j + 2]); + } + } + }, + computeVertexNormals: function computeVertexNormals() { + var index = this.index; + var positionAttribute = this.getAttribute('position'); + + if (positionAttribute !== undefined) { + var normalAttribute = this.getAttribute('normal'); + + if (normalAttribute === undefined) { + normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); + this.setAttribute('normal', normalAttribute); + } else { + // reset existing normals to zero + for (var i = 0, il = normalAttribute.count; i < il; i++) { + normalAttribute.setXYZ(i, 0, 0, 0); + } + } + + var pA = new Vector3(), + pB = new Vector3(), + pC = new Vector3(); + var nA = new Vector3(), + nB = new Vector3(), + nC = new Vector3(); + var cb = new Vector3(), + ab = new Vector3(); // indexed elements + + if (index) { + for (var _i5 = 0, _il4 = index.count; _i5 < _il4; _i5 += 3) { + var vA = index.getX(_i5 + 0); + var vB = index.getX(_i5 + 1); + var vC = index.getX(_i5 + 2); + pA.fromBufferAttribute(positionAttribute, vA); + pB.fromBufferAttribute(positionAttribute, vB); + pC.fromBufferAttribute(positionAttribute, vC); + cb.subVectors(pC, pB); + ab.subVectors(pA, pB); + cb.cross(ab); + nA.fromBufferAttribute(normalAttribute, vA); + nB.fromBufferAttribute(normalAttribute, vB); + nC.fromBufferAttribute(normalAttribute, vC); + nA.add(cb); + nB.add(cb); + nC.add(cb); + normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); + normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); + normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); + } + } else { + // non-indexed elements (unconnected triangle soup) + for (var _i6 = 0, _il5 = positionAttribute.count; _i6 < _il5; _i6 += 3) { + pA.fromBufferAttribute(positionAttribute, _i6 + 0); + pB.fromBufferAttribute(positionAttribute, _i6 + 1); + pC.fromBufferAttribute(positionAttribute, _i6 + 2); + cb.subVectors(pC, pB); + ab.subVectors(pA, pB); + cb.cross(ab); + normalAttribute.setXYZ(_i6 + 0, cb.x, cb.y, cb.z); + normalAttribute.setXYZ(_i6 + 1, cb.x, cb.y, cb.z); + normalAttribute.setXYZ(_i6 + 2, cb.x, cb.y, cb.z); + } + } + + this.normalizeNormals(); + normalAttribute.needsUpdate = true; + } + }, + merge: function merge(geometry, offset) { + if (!(geometry && geometry.isBufferGeometry)) { + console.error('THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry); + return; + } + + if (offset === undefined) { + offset = 0; + console.warn('THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'); + } + + var attributes = this.attributes; + + for (var key in attributes) { + if (geometry.attributes[key] === undefined) continue; + var attribute1 = attributes[key]; + var attributeArray1 = attribute1.array; + var attribute2 = geometry.attributes[key]; + var attributeArray2 = attribute2.array; + var attributeOffset = attribute2.itemSize * offset; + var length = Math.min(attributeArray2.length, attributeArray1.length - attributeOffset); + + for (var i = 0, j = attributeOffset; i < length; i++, j++) { + attributeArray1[j] = attributeArray2[i]; + } + } + + return this; + }, + normalizeNormals: function normalizeNormals() { + var normals = this.attributes.normal; + + for (var i = 0, il = normals.count; i < il; i++) { + _vector$4.fromBufferAttribute(normals, i); + + _vector$4.normalize(); + + normals.setXYZ(i, _vector$4.x, _vector$4.y, _vector$4.z); + } + }, + toNonIndexed: function toNonIndexed() { + function convertBufferAttribute(attribute, indices) { + var array = attribute.array; + var itemSize = attribute.itemSize; + var normalized = attribute.normalized; + var array2 = new array.constructor(indices.length * itemSize); + var index = 0, + index2 = 0; + + for (var i = 0, l = indices.length; i < l; i++) { + index = indices[i] * itemSize; + + for (var j = 0; j < itemSize; j++) { + array2[index2++] = array[index++]; + } + } + + return new BufferAttribute(array2, itemSize, normalized); + } // + + + if (this.index === null) { + console.warn('THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.'); + return this; + } + + var geometry2 = new BufferGeometry(); + var indices = this.index.array; + var attributes = this.attributes; // attributes + + for (var name in attributes) { + var attribute = attributes[name]; + var newAttribute = convertBufferAttribute(attribute, indices); + geometry2.setAttribute(name, newAttribute); + } // morph attributes + + + var morphAttributes = this.morphAttributes; + + for (var _name in morphAttributes) { + var morphArray = []; + var morphAttribute = morphAttributes[_name]; // morphAttribute: array of Float32BufferAttributes + + for (var i = 0, il = morphAttribute.length; i < il; i++) { + var _attribute = morphAttribute[i]; + + var _newAttribute = convertBufferAttribute(_attribute, indices); + + morphArray.push(_newAttribute); + } + + geometry2.morphAttributes[_name] = morphArray; + } + + geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups + + var groups = this.groups; + + for (var _i7 = 0, l = groups.length; _i7 < l; _i7++) { + var group = groups[_i7]; + geometry2.addGroup(group.start, group.count, group.materialIndex); + } + + return geometry2; + }, + toJSON: function toJSON() { + var data = { + metadata: { + version: 4.5, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; // standard BufferGeometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if (this.name !== '') data.name = this.name; + if (Object.keys(this.userData).length > 0) data.userData = this.userData; + + if (this.parameters !== undefined) { + var parameters = this.parameters; + + for (var key in parameters) { + if (parameters[key] !== undefined) data[key] = parameters[key]; + } + + return data; + } + + data.data = { + attributes: {} + }; + var index = this.index; + + if (index !== null) { + data.data.index = { + type: index.array.constructor.name, + array: Array.prototype.slice.call(index.array) + }; + } + + var attributes = this.attributes; + + for (var _key in attributes) { + var attribute = attributes[_key]; + var attributeData = attribute.toJSON(data.data); + if (attribute.name !== '') attributeData.name = attribute.name; + data.data.attributes[_key] = attributeData; + } + + var morphAttributes = {}; + var hasMorphAttributes = false; + + for (var _key2 in this.morphAttributes) { + var attributeArray = this.morphAttributes[_key2]; + var array = []; + + for (var i = 0, il = attributeArray.length; i < il; i++) { + var _attribute2 = attributeArray[i]; + + var _attributeData = _attribute2.toJSON(data.data); + + if (_attribute2.name !== '') _attributeData.name = _attribute2.name; + array.push(_attributeData); + } + + if (array.length > 0) { + morphAttributes[_key2] = array; + hasMorphAttributes = true; + } + } + + if (hasMorphAttributes) { + data.data.morphAttributes = morphAttributes; + data.data.morphTargetsRelative = this.morphTargetsRelative; + } + + var groups = this.groups; + + if (groups.length > 0) { + data.data.groups = JSON.parse(JSON.stringify(groups)); + } + + var boundingSphere = this.boundingSphere; + + if (boundingSphere !== null) { + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; + } + + return data; + }, + clone: function clone() { + /* + // Handle primitives + const parameters = this.parameters; + if ( parameters !== undefined ) { + const values = []; + for ( const key in parameters ) { + values.push( parameters[ key ] ); + } + const geometry = Object.create( this.constructor.prototype ); + this.constructor.apply( geometry, values ); + return geometry; + } + return new this.constructor().copy( this ); + */ + return new BufferGeometry().copy(this); + }, + copy: function copy(source) { + // reset + this.index = null; + this.attributes = {}; + this.morphAttributes = {}; + this.groups = []; + this.boundingBox = null; + this.boundingSphere = null; // used for storing cloned, shared data + + var data = {}; // name + + this.name = source.name; // index + + var index = source.index; + + if (index !== null) { + this.setIndex(index.clone(data)); + } // attributes + + + var attributes = source.attributes; + + for (var name in attributes) { + var attribute = attributes[name]; + this.setAttribute(name, attribute.clone(data)); + } // morph attributes + + + var morphAttributes = source.morphAttributes; + + for (var _name2 in morphAttributes) { + var array = []; + var morphAttribute = morphAttributes[_name2]; // morphAttribute: array of Float32BufferAttributes + + for (var i = 0, l = morphAttribute.length; i < l; i++) { + array.push(morphAttribute[i].clone(data)); + } + + this.morphAttributes[_name2] = array; + } + + this.morphTargetsRelative = source.morphTargetsRelative; // groups + + var groups = source.groups; + + for (var _i8 = 0, _l = groups.length; _i8 < _l; _i8++) { + var group = groups[_i8]; + this.addGroup(group.start, group.count, group.materialIndex); + } // bounding box + + + var boundingBox = source.boundingBox; + + if (boundingBox !== null) { + this.boundingBox = boundingBox.clone(); + } // bounding sphere + + + var boundingSphere = source.boundingSphere; + + if (boundingSphere !== null) { + this.boundingSphere = boundingSphere.clone(); + } // draw range + + + this.drawRange.start = source.drawRange.start; + this.drawRange.count = source.drawRange.count; // user data + + this.userData = source.userData; + return this; + }, + dispose: function dispose() { + this.dispatchEvent({ + type: 'dispose' + }); + } + }); + + var _inverseMatrix = new Matrix4(); + + var _ray = new Ray(); + + var _sphere = new Sphere(); + + var _vA = new Vector3(); + + var _vB = new Vector3(); + + var _vC = new Vector3(); + + var _tempA = new Vector3(); + + var _tempB = new Vector3(); + + var _tempC = new Vector3(); + + var _morphA = new Vector3(); + + var _morphB = new Vector3(); + + var _morphC = new Vector3(); + + var _uvA = new Vector2(); + + var _uvB = new Vector2(); + + var _uvC = new Vector2(); + + var _intersectionPoint = new Vector3(); + + var _intersectionPointWorld = new Vector3(); + + function Mesh(geometry, material) { + if (geometry === void 0) { + geometry = new BufferGeometry(); + } + + if (material === void 0) { + material = new MeshBasicMaterial(); + } + + Object3D.call(this); + this.type = 'Mesh'; + this.geometry = geometry; + this.material = material; + this.updateMorphTargets(); + } + + Mesh.prototype = Object.assign(Object.create(Object3D.prototype), { + constructor: Mesh, + isMesh: true, + copy: function copy(source) { + Object3D.prototype.copy.call(this, source); + + if (source.morphTargetInfluences !== undefined) { + this.morphTargetInfluences = source.morphTargetInfluences.slice(); + } + + if (source.morphTargetDictionary !== undefined) { + this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); + } + + this.material = source.material; + this.geometry = source.geometry; + return this; + }, + updateMorphTargets: function updateMorphTargets() { + var geometry = this.geometry; + + if (geometry.isBufferGeometry) { + var morphAttributes = geometry.morphAttributes; + var keys = Object.keys(morphAttributes); + + if (keys.length > 0) { + var morphAttribute = morphAttributes[keys[0]]; + + if (morphAttribute !== undefined) { + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for (var m = 0, ml = morphAttribute.length; m < ml; m++) { + var name = morphAttribute[m].name || String(m); + this.morphTargetInfluences.push(0); + this.morphTargetDictionary[name] = m; + } + } + } + } else { + var morphTargets = geometry.morphTargets; + + if (morphTargets !== undefined && morphTargets.length > 0) { + console.error('THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + } + }, + raycast: function raycast(raycaster, intersects) { + var geometry = this.geometry; + var material = this.material; + var matrixWorld = this.matrixWorld; + if (material === undefined) return; // Checking boundingSphere distance to ray + + if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); + + _sphere.copy(geometry.boundingSphere); + + _sphere.applyMatrix4(matrixWorld); + + if (raycaster.ray.intersectsSphere(_sphere) === false) return; // + + _inverseMatrix.copy(matrixWorld).invert(); + + _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); // Check boundingBox before continuing + + + if (geometry.boundingBox !== null) { + if (_ray.intersectsBox(geometry.boundingBox) === false) return; + } + + var intersection; + + if (geometry.isBufferGeometry) { + var index = geometry.index; + var position = geometry.attributes.position; + var morphPosition = geometry.morphAttributes.position; + var morphTargetsRelative = geometry.morphTargetsRelative; + var uv = geometry.attributes.uv; + var uv2 = geometry.attributes.uv2; + var groups = geometry.groups; + var drawRange = geometry.drawRange; + + if (index !== null) { + // indexed buffer geometry + if (Array.isArray(material)) { + for (var i = 0, il = groups.length; i < il; i++) { + var group = groups[i]; + var groupMaterial = material[group.materialIndex]; + var start = Math.max(group.start, drawRange.start); + var end = Math.min(group.start + group.count, drawRange.start + drawRange.count); + + for (var j = start, jl = end; j < jl; j += 3) { + var a = index.getX(j); + var b = index.getX(j + 1); + var c = index.getX(j + 2); + intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c); + + if (intersection) { + intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics + + intersection.face.materialIndex = group.materialIndex; + intersects.push(intersection); + } + } + } + } else { + var _start = Math.max(0, drawRange.start); + + var _end = Math.min(index.count, drawRange.start + drawRange.count); + + for (var _i = _start, _il = _end; _i < _il; _i += 3) { + var _a = index.getX(_i); + + var _b = index.getX(_i + 1); + + var _c = index.getX(_i + 2); + + intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, _a, _b, _c); + + if (intersection) { + intersection.faceIndex = Math.floor(_i / 3); // triangle number in indexed buffer semantics + + intersects.push(intersection); + } + } + } + } else if (position !== undefined) { + // non-indexed buffer geometry + if (Array.isArray(material)) { + for (var _i2 = 0, _il2 = groups.length; _i2 < _il2; _i2++) { + var _group = groups[_i2]; + var _groupMaterial = material[_group.materialIndex]; + + var _start2 = Math.max(_group.start, drawRange.start); + + var _end2 = Math.min(_group.start + _group.count, drawRange.start + drawRange.count); + + for (var _j = _start2, _jl = _end2; _j < _jl; _j += 3) { + var _a2 = _j; + + var _b2 = _j + 1; + + var _c2 = _j + 2; + + intersection = checkBufferGeometryIntersection(this, _groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, _a2, _b2, _c2); + + if (intersection) { + intersection.faceIndex = Math.floor(_j / 3); // triangle number in non-indexed buffer semantics + + intersection.face.materialIndex = _group.materialIndex; + intersects.push(intersection); + } + } + } + } else { + var _start3 = Math.max(0, drawRange.start); + + var _end3 = Math.min(position.count, drawRange.start + drawRange.count); + + for (var _i3 = _start3, _il3 = _end3; _i3 < _il3; _i3 += 3) { + var _a3 = _i3; + + var _b3 = _i3 + 1; + + var _c3 = _i3 + 2; + + intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, _a3, _b3, _c3); + + if (intersection) { + intersection.faceIndex = Math.floor(_i3 / 3); // triangle number in non-indexed buffer semantics + + intersects.push(intersection); + } + } + } + } + } else if (geometry.isGeometry) { + console.error('THREE.Mesh.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + } + }); + + function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { + var intersect; + + if (material.side === BackSide) { + intersect = ray.intersectTriangle(pC, pB, pA, true, point); + } else { + intersect = ray.intersectTriangle(pA, pB, pC, material.side !== DoubleSide, point); + } + + if (intersect === null) return null; + + _intersectionPointWorld.copy(point); + + _intersectionPointWorld.applyMatrix4(object.matrixWorld); + + var distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); + if (distance < raycaster.near || distance > raycaster.far) return null; + return { + distance: distance, + point: _intersectionPointWorld.clone(), + object: object + }; + } + + function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) { + _vA.fromBufferAttribute(position, a); + + _vB.fromBufferAttribute(position, b); + + _vC.fromBufferAttribute(position, c); + + var morphInfluences = object.morphTargetInfluences; + + if (material.morphTargets && morphPosition && morphInfluences) { + _morphA.set(0, 0, 0); + + _morphB.set(0, 0, 0); + + _morphC.set(0, 0, 0); + + for (var i = 0, il = morphPosition.length; i < il; i++) { + var influence = morphInfluences[i]; + var morphAttribute = morphPosition[i]; + if (influence === 0) continue; + + _tempA.fromBufferAttribute(morphAttribute, a); + + _tempB.fromBufferAttribute(morphAttribute, b); + + _tempC.fromBufferAttribute(morphAttribute, c); + + if (morphTargetsRelative) { + _morphA.addScaledVector(_tempA, influence); + + _morphB.addScaledVector(_tempB, influence); + + _morphC.addScaledVector(_tempC, influence); + } else { + _morphA.addScaledVector(_tempA.sub(_vA), influence); + + _morphB.addScaledVector(_tempB.sub(_vB), influence); + + _morphC.addScaledVector(_tempC.sub(_vC), influence); + } + } + + _vA.add(_morphA); + + _vB.add(_morphB); + + _vC.add(_morphC); + } + + if (object.isSkinnedMesh && material.skinning) { + object.boneTransform(a, _vA); + object.boneTransform(b, _vB); + object.boneTransform(c, _vC); + } + + var intersection = checkIntersection(object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint); + + if (intersection) { + if (uv) { + _uvA.fromBufferAttribute(uv, a); + + _uvB.fromBufferAttribute(uv, b); + + _uvC.fromBufferAttribute(uv, c); + + intersection.uv = Triangle.getUV(_intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()); + } + + if (uv2) { + _uvA.fromBufferAttribute(uv2, a); + + _uvB.fromBufferAttribute(uv2, b); + + _uvC.fromBufferAttribute(uv2, c); + + intersection.uv2 = Triangle.getUV(_intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()); + } + + var face = { + a: a, + b: b, + c: c, + normal: new Vector3(), + materialIndex: 0 + }; + Triangle.getNormal(_vA, _vB, _vC, face.normal); + intersection.face = face; + } + + return intersection; + } + + var BoxGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(BoxGeometry, _BufferGeometry); + + function BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments) { + var _this; + + if (width === void 0) { + width = 1; + } + + if (height === void 0) { + height = 1; + } + + if (depth === void 0) { + depth = 1; + } + + if (widthSegments === void 0) { + widthSegments = 1; + } + + if (heightSegments === void 0) { + heightSegments = 1; + } + + if (depthSegments === void 0) { + depthSegments = 1; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'BoxGeometry'; + _this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + var scope = _assertThisInitialized(_this); // segments + + + widthSegments = Math.floor(widthSegments); + heightSegments = Math.floor(heightSegments); + depthSegments = Math.floor(depthSegments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var numberOfVertices = 0; + var groupStart = 0; // build each side of the box geometry + + buildPlane('z', 'y', 'x', -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px + + buildPlane('z', 'y', 'x', 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx + + buildPlane('x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py + + buildPlane('x', 'z', 'y', 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny + + buildPlane('x', 'y', 'z', 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz + + buildPlane('x', 'y', 'z', -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz + // build geometry + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { + var segmentWidth = width / gridX; + var segmentHeight = height / gridY; + var widthHalf = width / 2; + var heightHalf = height / 2; + var depthHalf = depth / 2; + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + var vertexCounter = 0; + var groupCount = 0; + var vector = new Vector3(); // generate vertices, normals and uvs + + for (var iy = 0; iy < gridY1; iy++) { + var y = iy * segmentHeight - heightHalf; + + for (var ix = 0; ix < gridX1; ix++) { + var x = ix * segmentWidth - widthHalf; // set values to correct vector component + + vector[u] = x * udir; + vector[v] = y * vdir; + vector[w] = depthHalf; // now apply vector to vertex buffer + + vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component + + vector[u] = 0; + vector[v] = 0; + vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer + + normals.push(vector.x, vector.y, vector.z); // uvs + + uvs.push(ix / gridX); + uvs.push(1 - iy / gridY); // counters + + vertexCounter += 1; + } + } // indices + // 1. you need three indices to draw a single face + // 2. a single segment consists of two faces + // 3. so we need to generate six (2*3) indices per segment + + + for (var _iy = 0; _iy < gridY; _iy++) { + for (var _ix = 0; _ix < gridX; _ix++) { + var a = numberOfVertices + _ix + gridX1 * _iy; + var b = numberOfVertices + _ix + gridX1 * (_iy + 1); + var c = numberOfVertices + (_ix + 1) + gridX1 * (_iy + 1); + var d = numberOfVertices + (_ix + 1) + gridX1 * _iy; // faces + + indices.push(a, b, d); + indices.push(b, c, d); // increase counter + + groupCount += 6; + } + } // add a group to the geometry. this will ensure multi material support + + + scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups + + groupStart += groupCount; // update total number of vertices + + numberOfVertices += vertexCounter; + } + + return _this; + } + + return BoxGeometry; + }(BufferGeometry); + + /** + * Uniform Utilities + */ + function cloneUniforms(src) { + var dst = {}; + + for (var u in src) { + dst[u] = {}; + + for (var p in src[u]) { + var property = src[u][p]; + + if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { + dst[u][p] = property.clone(); + } else if (Array.isArray(property)) { + dst[u][p] = property.slice(); + } else { + dst[u][p] = property; + } + } + } + + return dst; + } + function mergeUniforms(uniforms) { + var merged = {}; + + for (var u = 0; u < uniforms.length; u++) { + var tmp = cloneUniforms(uniforms[u]); + + for (var p in tmp) { + merged[p] = tmp[p]; + } + } + + return merged; + } // Legacy + + var UniformsUtils = { + clone: cloneUniforms, + merge: mergeUniforms + }; + + var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; + + var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; + + /** + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ + + function ShaderMaterial(parameters) { + Material.call(this); + this.type = 'ShaderMaterial'; + this.defines = {}; + this.uniforms = {}; + this.vertexShader = default_vertex; + this.fragmentShader = default_fragment; + this.linewidth = 1; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.clipping = false; // set to use user-defined clipping planes + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + + this.morphNormals = false; // set to use morph normals + + this.extensions = { + derivatives: false, + // set to use derivatives + fragDepth: false, + // set to use fragment depth values + drawBuffers: false, + // set to use draw buffers + shaderTextureLOD: false // set to use shader texture LOD + + }; // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + + this.defaultAttributeValues = { + 'color': [1, 1, 1], + 'uv': [0, 0], + 'uv2': [0, 0] + }; + this.index0AttributeName = undefined; + this.uniformsNeedUpdate = false; + this.glslVersion = null; + + if (parameters !== undefined) { + if (parameters.attributes !== undefined) { + console.error('THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.'); + } + + this.setValues(parameters); + } + } + + ShaderMaterial.prototype = Object.create(Material.prototype); + ShaderMaterial.prototype.constructor = ShaderMaterial; + ShaderMaterial.prototype.isShaderMaterial = true; + + ShaderMaterial.prototype.copy = function (source) { + Material.prototype.copy.call(this, source); + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; + this.uniforms = cloneUniforms(source.uniforms); + this.defines = Object.assign({}, source.defines); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.lights = source.lights; + this.clipping = source.clipping; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + this.extensions = Object.assign({}, source.extensions); + this.glslVersion = source.glslVersion; + return this; + }; + + ShaderMaterial.prototype.toJSON = function (meta) { + var data = Material.prototype.toJSON.call(this, meta); + data.glslVersion = this.glslVersion; + data.uniforms = {}; + + for (var name in this.uniforms) { + var uniform = this.uniforms[name]; + var value = uniform.value; + + if (value && value.isTexture) { + data.uniforms[name] = { + type: 't', + value: value.toJSON(meta).uuid + }; + } else if (value && value.isColor) { + data.uniforms[name] = { + type: 'c', + value: value.getHex() + }; + } else if (value && value.isVector2) { + data.uniforms[name] = { + type: 'v2', + value: value.toArray() + }; + } else if (value && value.isVector3) { + data.uniforms[name] = { + type: 'v3', + value: value.toArray() + }; + } else if (value && value.isVector4) { + data.uniforms[name] = { + type: 'v4', + value: value.toArray() + }; + } else if (value && value.isMatrix3) { + data.uniforms[name] = { + type: 'm3', + value: value.toArray() + }; + } else if (value && value.isMatrix4) { + data.uniforms[name] = { + type: 'm4', + value: value.toArray() + }; + } else { + data.uniforms[name] = { + value: value + }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far + } + } + + if (Object.keys(this.defines).length > 0) data.defines = this.defines; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; + var extensions = {}; + + for (var key in this.extensions) { + if (this.extensions[key] === true) extensions[key] = true; + } + + if (Object.keys(extensions).length > 0) data.extensions = extensions; + return data; + }; + + function Camera() { + Object3D.call(this); + this.type = 'Camera'; + this.matrixWorldInverse = new Matrix4(); + this.projectionMatrix = new Matrix4(); + this.projectionMatrixInverse = new Matrix4(); + } + + Camera.prototype = Object.assign(Object.create(Object3D.prototype), { + constructor: Camera, + isCamera: true, + copy: function copy(source, recursive) { + Object3D.prototype.copy.call(this, source, recursive); + this.matrixWorldInverse.copy(source.matrixWorldInverse); + this.projectionMatrix.copy(source.projectionMatrix); + this.projectionMatrixInverse.copy(source.projectionMatrixInverse); + return this; + }, + getWorldDirection: function getWorldDirection(target) { + if (target === undefined) { + console.warn('THREE.Camera: .getWorldDirection() target is now required'); + target = new Vector3(); + } + + this.updateWorldMatrix(true, false); + var e = this.matrixWorld.elements; + return target.set(-e[8], -e[9], -e[10]).normalize(); + }, + updateMatrixWorld: function updateMatrixWorld(force) { + Object3D.prototype.updateMatrixWorld.call(this, force); + this.matrixWorldInverse.copy(this.matrixWorld).invert(); + }, + updateWorldMatrix: function updateWorldMatrix(updateParents, updateChildren) { + Object3D.prototype.updateWorldMatrix.call(this, updateParents, updateChildren); + this.matrixWorldInverse.copy(this.matrixWorld).invert(); + }, + clone: function clone() { + return new this.constructor().copy(this); + } + }); + + function PerspectiveCamera(fov, aspect, near, far) { + if (fov === void 0) { + fov = 50; + } + + if (aspect === void 0) { + aspect = 1; + } + + if (near === void 0) { + near = 0.1; + } + + if (far === void 0) { + far = 2000; + } + + Camera.call(this); + this.type = 'PerspectiveCamera'; + this.fov = fov; + this.zoom = 1; + this.near = near; + this.far = far; + this.focus = 10; + this.aspect = aspect; + this.view = null; + this.filmGauge = 35; // width of the film (default in millimeters) + + this.filmOffset = 0; // horizontal film offset (same unit as gauge) + + this.updateProjectionMatrix(); + } + + PerspectiveCamera.prototype = Object.assign(Object.create(Camera.prototype), { + constructor: PerspectiveCamera, + isPerspectiveCamera: true, + copy: function copy(source, recursive) { + Camera.prototype.copy.call(this, source, recursive); + this.fov = source.fov; + this.zoom = source.zoom; + this.near = source.near; + this.far = source.far; + this.focus = source.focus; + this.aspect = source.aspect; + this.view = source.view === null ? null : Object.assign({}, source.view); + this.filmGauge = source.filmGauge; + this.filmOffset = source.filmOffset; + return this; + }, + + /** + * Sets the FOV by focal length in respect to the current .filmGauge. + * + * The default film gauge is 35, so that the focal length can be specified for + * a 35mm (full frame) camera. + * + * Values for focal length and film gauge must have the same unit. + */ + setFocalLength: function setFocalLength(focalLength) { + /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ + var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; + this.fov = MathUtils.RAD2DEG * 2 * Math.atan(vExtentSlope); + this.updateProjectionMatrix(); + }, + + /** + * Calculates the focal length from the current .fov and .filmGauge. + */ + getFocalLength: function getFocalLength() { + var vExtentSlope = Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov); + return 0.5 * this.getFilmHeight() / vExtentSlope; + }, + getEffectiveFOV: function getEffectiveFOV() { + return MathUtils.RAD2DEG * 2 * Math.atan(Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov) / this.zoom); + }, + getFilmWidth: function getFilmWidth() { + // film not completely covered in portrait format (aspect < 1) + return this.filmGauge * Math.min(this.aspect, 1); + }, + getFilmHeight: function getFilmHeight() { + // film not completely covered in landscape format (aspect > 1) + return this.filmGauge / Math.max(this.aspect, 1); + }, + + /** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * const w = 1920; + * const h = 1080; + * const fullWidth = w * 3; + * const fullHeight = h * 2; + * + * --A-- + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + setViewOffset: function setViewOffset(fullWidth, fullHeight, x, y, width, height) { + this.aspect = fullWidth / fullHeight; + + if (this.view === null) { + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; + } + + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; + this.updateProjectionMatrix(); + }, + clearViewOffset: function clearViewOffset() { + if (this.view !== null) { + this.view.enabled = false; + } + + this.updateProjectionMatrix(); + }, + updateProjectionMatrix: function updateProjectionMatrix() { + var near = this.near; + var top = near * Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov) / this.zoom; + var height = 2 * top; + var width = this.aspect * height; + var left = -0.5 * width; + var view = this.view; + + if (this.view !== null && this.view.enabled) { + var fullWidth = view.fullWidth, + fullHeight = view.fullHeight; + left += view.offsetX * width / fullWidth; + top -= view.offsetY * height / fullHeight; + width *= view.width / fullWidth; + height *= view.height / fullHeight; + } + + var skew = this.filmOffset; + if (skew !== 0) left += near * skew / this.getFilmWidth(); + this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far); + this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); + }, + toJSON: function toJSON(meta) { + var data = Object3D.prototype.toJSON.call(this, meta); + data.object.fov = this.fov; + data.object.zoom = this.zoom; + data.object.near = this.near; + data.object.far = this.far; + data.object.focus = this.focus; + data.object.aspect = this.aspect; + if (this.view !== null) data.object.view = Object.assign({}, this.view); + data.object.filmGauge = this.filmGauge; + data.object.filmOffset = this.filmOffset; + return data; + } + }); + + var fov = 90, + aspect = 1; + + var CubeCamera = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(CubeCamera, _Object3D); + + function CubeCamera(near, far, renderTarget) { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'CubeCamera'; + + if (renderTarget.isWebGLCubeRenderTarget !== true) { + console.error('THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.'); + return _assertThisInitialized(_this); + } + + _this.renderTarget = renderTarget; + var cameraPX = new PerspectiveCamera(fov, aspect, near, far); + cameraPX.layers = _this.layers; + cameraPX.up.set(0, -1, 0); + cameraPX.lookAt(new Vector3(1, 0, 0)); + + _this.add(cameraPX); + + var cameraNX = new PerspectiveCamera(fov, aspect, near, far); + cameraNX.layers = _this.layers; + cameraNX.up.set(0, -1, 0); + cameraNX.lookAt(new Vector3(-1, 0, 0)); + + _this.add(cameraNX); + + var cameraPY = new PerspectiveCamera(fov, aspect, near, far); + cameraPY.layers = _this.layers; + cameraPY.up.set(0, 0, 1); + cameraPY.lookAt(new Vector3(0, 1, 0)); + + _this.add(cameraPY); + + var cameraNY = new PerspectiveCamera(fov, aspect, near, far); + cameraNY.layers = _this.layers; + cameraNY.up.set(0, 0, -1); + cameraNY.lookAt(new Vector3(0, -1, 0)); + + _this.add(cameraNY); + + var cameraPZ = new PerspectiveCamera(fov, aspect, near, far); + cameraPZ.layers = _this.layers; + cameraPZ.up.set(0, -1, 0); + cameraPZ.lookAt(new Vector3(0, 0, 1)); + + _this.add(cameraPZ); + + var cameraNZ = new PerspectiveCamera(fov, aspect, near, far); + cameraNZ.layers = _this.layers; + cameraNZ.up.set(0, -1, 0); + cameraNZ.lookAt(new Vector3(0, 0, -1)); + + _this.add(cameraNZ); + + return _this; + } + + var _proto = CubeCamera.prototype; + + _proto.update = function update(renderer, scene) { + if (this.parent === null) this.updateMatrixWorld(); + var renderTarget = this.renderTarget; + var _this$children = this.children, + cameraPX = _this$children[0], + cameraNX = _this$children[1], + cameraPY = _this$children[2], + cameraNY = _this$children[3], + cameraPZ = _this$children[4], + cameraNZ = _this$children[5]; + var currentXrEnabled = renderer.xr.enabled; + var currentRenderTarget = renderer.getRenderTarget(); + renderer.xr.enabled = false; + var generateMipmaps = renderTarget.texture.generateMipmaps; + renderTarget.texture.generateMipmaps = false; + renderer.setRenderTarget(renderTarget, 0); + renderer.render(scene, cameraPX); + renderer.setRenderTarget(renderTarget, 1); + renderer.render(scene, cameraNX); + renderer.setRenderTarget(renderTarget, 2); + renderer.render(scene, cameraPY); + renderer.setRenderTarget(renderTarget, 3); + renderer.render(scene, cameraNY); + renderer.setRenderTarget(renderTarget, 4); + renderer.render(scene, cameraPZ); + renderTarget.texture.generateMipmaps = generateMipmaps; + renderer.setRenderTarget(renderTarget, 5); + renderer.render(scene, cameraNZ); + renderer.setRenderTarget(currentRenderTarget); + renderer.xr.enabled = currentXrEnabled; + }; + + return CubeCamera; + }(Object3D); + + var CubeTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(CubeTexture, _Texture); + + function CubeTexture(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) { + var _this; + + images = images !== undefined ? images : []; + mapping = mapping !== undefined ? mapping : CubeReflectionMapping; + format = format !== undefined ? format : RGBFormat; + _this = _Texture.call(this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) || this; // Why CubeTexture._needsFlipEnvMap is necessary: + // + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false) + // when using WebGLCubeRenderTarget.texture as a cube texture. + + _this._needsFlipEnvMap = true; + _this.flipY = false; + return _this; + } + + _createClass(CubeTexture, [{ + key: "images", + get: function get() { + return this.image; + }, + set: function set(value) { + this.image = value; + } + }]); + + return CubeTexture; + }(Texture); + + CubeTexture.prototype.isCubeTexture = true; + + var WebGLCubeRenderTarget = /*#__PURE__*/function (_WebGLRenderTarget) { + _inheritsLoose(WebGLCubeRenderTarget, _WebGLRenderTarget); + + function WebGLCubeRenderTarget(size, options, dummy) { + var _this; + + if (Number.isInteger(options)) { + console.warn('THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )'); + options = dummy; + } + + _this = _WebGLRenderTarget.call(this, size, size, options) || this; + options = options || {}; + _this.texture = new CubeTexture(undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding); + _this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + _this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; + _this.texture._needsFlipEnvMap = false; + return _this; + } + + var _proto = WebGLCubeRenderTarget.prototype; + + _proto.fromEquirectangularTexture = function fromEquirectangularTexture(renderer, texture) { + this.texture.type = texture.type; + this.texture.format = RGBAFormat; // see #18859 + + this.texture.encoding = texture.encoding; + this.texture.generateMipmaps = texture.generateMipmaps; + this.texture.minFilter = texture.minFilter; + this.texture.magFilter = texture.magFilter; + var shader = { + uniforms: { + tEquirect: { + value: null + } + }, + vertexShader: + /* glsl */ + "\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t", + fragmentShader: + /* glsl */ + "\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t" + }; + var geometry = new BoxGeometry(5, 5, 5); + var material = new ShaderMaterial({ + name: 'CubemapFromEquirect', + uniforms: cloneUniforms(shader.uniforms), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + side: BackSide, + blending: NoBlending + }); + material.uniforms.tEquirect.value = texture; + var mesh = new Mesh(geometry, material); + var currentMinFilter = texture.minFilter; // Avoid blurred poles + + if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; + var camera = new CubeCamera(1, 10, this); + camera.update(renderer, mesh); + texture.minFilter = currentMinFilter; + mesh.geometry.dispose(); + mesh.material.dispose(); + return this; + }; + + _proto.clear = function clear(renderer, color, depth, stencil) { + var currentRenderTarget = renderer.getRenderTarget(); + + for (var i = 0; i < 6; i++) { + renderer.setRenderTarget(this, i); + renderer.clear(color, depth, stencil); + } + + renderer.setRenderTarget(currentRenderTarget); + }; + + return WebGLCubeRenderTarget; + }(WebGLRenderTarget); + + WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; + + var DataTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(DataTexture, _Texture); + + function DataTexture(data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { + var _this; + + _this = _Texture.call(this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) || this; + _this.image = { + data: data || null, + width: width || 1, + height: height || 1 + }; + _this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + _this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + _this.generateMipmaps = false; + _this.flipY = false; + _this.unpackAlignment = 1; + _this.needsUpdate = true; + return _this; + } + + return DataTexture; + }(Texture); + + DataTexture.prototype.isDataTexture = true; + + var _sphere$1 = /*@__PURE__*/new Sphere(); + + var _vector$5 = /*@__PURE__*/new Vector3(); + + var Frustum = /*#__PURE__*/function () { + function Frustum(p0, p1, p2, p3, p4, p5) { + if (p0 === void 0) { + p0 = new Plane(); + } + + if (p1 === void 0) { + p1 = new Plane(); + } + + if (p2 === void 0) { + p2 = new Plane(); + } + + if (p3 === void 0) { + p3 = new Plane(); + } + + if (p4 === void 0) { + p4 = new Plane(); + } + + if (p5 === void 0) { + p5 = new Plane(); + } + + this.planes = [p0, p1, p2, p3, p4, p5]; + } + + var _proto = Frustum.prototype; + + _proto.set = function set(p0, p1, p2, p3, p4, p5) { + var planes = this.planes; + planes[0].copy(p0); + planes[1].copy(p1); + planes[2].copy(p2); + planes[3].copy(p3); + planes[4].copy(p4); + planes[5].copy(p5); + return this; + }; + + _proto.copy = function copy(frustum) { + var planes = this.planes; + + for (var i = 0; i < 6; i++) { + planes[i].copy(frustum.planes[i]); + } + + return this; + }; + + _proto.setFromProjectionMatrix = function setFromProjectionMatrix(m) { + var planes = this.planes; + var me = m.elements; + var me0 = me[0], + me1 = me[1], + me2 = me[2], + me3 = me[3]; + var me4 = me[4], + me5 = me[5], + me6 = me[6], + me7 = me[7]; + var me8 = me[8], + me9 = me[9], + me10 = me[10], + me11 = me[11]; + var me12 = me[12], + me13 = me[13], + me14 = me[14], + me15 = me[15]; + planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); + planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); + planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); + planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); + planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); + planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); + return this; + }; + + _proto.intersectsObject = function intersectsObject(object) { + var geometry = object.geometry; + if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); + + _sphere$1.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); + + return this.intersectsSphere(_sphere$1); + }; + + _proto.intersectsSprite = function intersectsSprite(sprite) { + _sphere$1.center.set(0, 0, 0); + + _sphere$1.radius = 0.7071067811865476; + + _sphere$1.applyMatrix4(sprite.matrixWorld); + + return this.intersectsSphere(_sphere$1); + }; + + _proto.intersectsSphere = function intersectsSphere(sphere) { + var planes = this.planes; + var center = sphere.center; + var negRadius = -sphere.radius; + + for (var i = 0; i < 6; i++) { + var distance = planes[i].distanceToPoint(center); + + if (distance < negRadius) { + return false; + } + } + + return true; + }; + + _proto.intersectsBox = function intersectsBox(box) { + var planes = this.planes; + + for (var i = 0; i < 6; i++) { + var plane = planes[i]; // corner at max distance + + _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; + _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; + _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + if (plane.distanceToPoint(_vector$5) < 0) { + return false; + } + } + + return true; + }; + + _proto.containsPoint = function containsPoint(point) { + var planes = this.planes; + + for (var i = 0; i < 6; i++) { + if (planes[i].distanceToPoint(point) < 0) { + return false; + } + } + + return true; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Frustum; + }(); + + function WebGLAnimation() { + var context = null; + var isAnimating = false; + var animationLoop = null; + var requestId = null; + + function onAnimationFrame(time, frame) { + animationLoop(time, frame); + requestId = context.requestAnimationFrame(onAnimationFrame); + } + + return { + start: function start() { + if (isAnimating === true) return; + if (animationLoop === null) return; + requestId = context.requestAnimationFrame(onAnimationFrame); + isAnimating = true; + }, + stop: function stop() { + context.cancelAnimationFrame(requestId); + isAnimating = false; + }, + setAnimationLoop: function setAnimationLoop(callback) { + animationLoop = callback; + }, + setContext: function setContext(value) { + context = value; + } + }; + } + + function WebGLAttributes(gl, capabilities) { + var isWebGL2 = capabilities.isWebGL2; + var buffers = new WeakMap(); + + function createBuffer(attribute, bufferType) { + var array = attribute.array; + var usage = attribute.usage; + var buffer = gl.createBuffer(); + gl.bindBuffer(bufferType, buffer); + gl.bufferData(bufferType, array, usage); + attribute.onUploadCallback(); + var type = 5126; + + if (array instanceof Float32Array) { + type = 5126; + } else if (array instanceof Float64Array) { + console.warn('THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.'); + } else if (array instanceof Uint16Array) { + if (attribute.isFloat16BufferAttribute) { + if (isWebGL2) { + type = 5131; + } else { + console.warn('THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.'); + } + } else { + type = 5123; + } + } else if (array instanceof Int16Array) { + type = 5122; + } else if (array instanceof Uint32Array) { + type = 5125; + } else if (array instanceof Int32Array) { + type = 5124; + } else if (array instanceof Int8Array) { + type = 5120; + } else if (array instanceof Uint8Array) { + type = 5121; + } + + return { + buffer: buffer, + type: type, + bytesPerElement: array.BYTES_PER_ELEMENT, + version: attribute.version + }; + } + + function updateBuffer(buffer, attribute, bufferType) { + var array = attribute.array; + var updateRange = attribute.updateRange; + gl.bindBuffer(bufferType, buffer); + + if (updateRange.count === -1) { + // Not using update ranges + gl.bufferSubData(bufferType, 0, array); + } else { + if (isWebGL2) { + gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); + } else { + gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); + } + + updateRange.count = -1; // reset range + } + } // + + + function get(attribute) { + if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; + return buffers.get(attribute); + } + + function remove(attribute) { + if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; + var data = buffers.get(attribute); + + if (data) { + gl.deleteBuffer(data.buffer); + buffers.delete(attribute); + } + } + + function update(attribute, bufferType) { + if (attribute.isGLBufferAttribute) { + var cached = buffers.get(attribute); + + if (!cached || cached.version < attribute.version) { + buffers.set(attribute, { + buffer: attribute.buffer, + type: attribute.type, + bytesPerElement: attribute.elementSize, + version: attribute.version + }); + } + + return; + } + + if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; + var data = buffers.get(attribute); + + if (data === undefined) { + buffers.set(attribute, createBuffer(attribute, bufferType)); + } else if (data.version < attribute.version) { + updateBuffer(data.buffer, attribute, bufferType); + data.version = attribute.version; + } + } + + return { + get: get, + remove: remove, + update: update + }; + } + + var PlaneGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(PlaneGeometry, _BufferGeometry); + + function PlaneGeometry(width, height, widthSegments, heightSegments) { + var _this; + + if (width === void 0) { + width = 1; + } + + if (height === void 0) { + height = 1; + } + + if (widthSegments === void 0) { + widthSegments = 1; + } + + if (heightSegments === void 0) { + heightSegments = 1; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'PlaneGeometry'; + _this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + var width_half = width / 2; + var height_half = height / 2; + var gridX = Math.floor(widthSegments); + var gridY = Math.floor(heightSegments); + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + var segment_width = width / gridX; + var segment_height = height / gridY; // + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; + + for (var iy = 0; iy < gridY1; iy++) { + var y = iy * segment_height - height_half; + + for (var ix = 0; ix < gridX1; ix++) { + var x = ix * segment_width - width_half; + vertices.push(x, -y, 0); + normals.push(0, 0, 1); + uvs.push(ix / gridX); + uvs.push(1 - iy / gridY); + } + } + + for (var _iy = 0; _iy < gridY; _iy++) { + for (var _ix = 0; _ix < gridX; _ix++) { + var a = _ix + gridX1 * _iy; + var b = _ix + gridX1 * (_iy + 1); + var c = _ix + 1 + gridX1 * (_iy + 1); + var d = _ix + 1 + gridX1 * _iy; + indices.push(a, b, d); + indices.push(b, c, d); + } + } + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + return _this; + } + + return PlaneGeometry; + }(BufferGeometry); + + var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; + + var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; + + var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; + + var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; + + var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; + + var begin_vertex = "vec3 transformed = vec3( position );"; + + var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; + + var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; + + var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; + + var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; + + var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; + + var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; + + var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; + + var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; + + var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; + + var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; + + var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; + + var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; + + var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; + + var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; + + var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; + + var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; + + var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; + + var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; + + var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; + + var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; + + var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; + + var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; + + var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; + + var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; + + var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; + + var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; + + var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; + + var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; + + var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; + + var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; + + var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; + + var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; + + var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; + + var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; + + var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; + + var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; + + var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; + + var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; + + var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; + + var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; + + var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; + + var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; + + var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; + + var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; + + var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; + + var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; + + var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; + + var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; + + var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; + + var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; + + var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; + + var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; + + var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; + + var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; + + var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; + + var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; + + var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; + + var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; + + var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; + + var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif"; + + var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; + + var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif"; + + var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; + + var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; + + var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; + + var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; + + var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; + + var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; + + var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; + + var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; + + var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; + + var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; + + var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif"; + + var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; + + var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; + + var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; + + var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; + + var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; + + var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; + + var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; + + var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; + + var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; + + var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; + + var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; + + var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; + + var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif"; + + var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; + + var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; + + var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif"; + + var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; + + var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; + + var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + + var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; + + var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; + + var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; + + var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; + + var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; + + var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; + + var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; + + var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + + var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; + + var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; + + var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + + var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + + var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; + + var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; + + var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; + + var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + + var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; + + var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; + + var ShaderChunk = { + alphamap_fragment: alphamap_fragment, + alphamap_pars_fragment: alphamap_pars_fragment, + alphatest_fragment: alphatest_fragment, + aomap_fragment: aomap_fragment, + aomap_pars_fragment: aomap_pars_fragment, + begin_vertex: begin_vertex, + beginnormal_vertex: beginnormal_vertex, + bsdfs: bsdfs, + bumpmap_pars_fragment: bumpmap_pars_fragment, + clipping_planes_fragment: clipping_planes_fragment, + clipping_planes_pars_fragment: clipping_planes_pars_fragment, + clipping_planes_pars_vertex: clipping_planes_pars_vertex, + clipping_planes_vertex: clipping_planes_vertex, + color_fragment: color_fragment, + color_pars_fragment: color_pars_fragment, + color_pars_vertex: color_pars_vertex, + color_vertex: color_vertex, + common: common, + cube_uv_reflection_fragment: cube_uv_reflection_fragment, + defaultnormal_vertex: defaultnormal_vertex, + displacementmap_pars_vertex: displacementmap_pars_vertex, + displacementmap_vertex: displacementmap_vertex, + emissivemap_fragment: emissivemap_fragment, + emissivemap_pars_fragment: emissivemap_pars_fragment, + encodings_fragment: encodings_fragment, + encodings_pars_fragment: encodings_pars_fragment, + envmap_fragment: envmap_fragment, + envmap_common_pars_fragment: envmap_common_pars_fragment, + envmap_pars_fragment: envmap_pars_fragment, + envmap_pars_vertex: envmap_pars_vertex, + envmap_physical_pars_fragment: envmap_physical_pars_fragment, + envmap_vertex: envmap_vertex, + fog_vertex: fog_vertex, + fog_pars_vertex: fog_pars_vertex, + fog_fragment: fog_fragment, + fog_pars_fragment: fog_pars_fragment, + gradientmap_pars_fragment: gradientmap_pars_fragment, + lightmap_fragment: lightmap_fragment, + lightmap_pars_fragment: lightmap_pars_fragment, + lights_lambert_vertex: lights_lambert_vertex, + lights_pars_begin: lights_pars_begin, + lights_toon_fragment: lights_toon_fragment, + lights_toon_pars_fragment: lights_toon_pars_fragment, + lights_phong_fragment: lights_phong_fragment, + lights_phong_pars_fragment: lights_phong_pars_fragment, + lights_physical_fragment: lights_physical_fragment, + lights_physical_pars_fragment: lights_physical_pars_fragment, + lights_fragment_begin: lights_fragment_begin, + lights_fragment_maps: lights_fragment_maps, + lights_fragment_end: lights_fragment_end, + logdepthbuf_fragment: logdepthbuf_fragment, + logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, + logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, + logdepthbuf_vertex: logdepthbuf_vertex, + map_fragment: map_fragment, + map_pars_fragment: map_pars_fragment, + map_particle_fragment: map_particle_fragment, + map_particle_pars_fragment: map_particle_pars_fragment, + metalnessmap_fragment: metalnessmap_fragment, + metalnessmap_pars_fragment: metalnessmap_pars_fragment, + morphnormal_vertex: morphnormal_vertex, + morphtarget_pars_vertex: morphtarget_pars_vertex, + morphtarget_vertex: morphtarget_vertex, + normal_fragment_begin: normal_fragment_begin, + normal_fragment_maps: normal_fragment_maps, + normalmap_pars_fragment: normalmap_pars_fragment, + clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, + clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, + clearcoat_pars_fragment: clearcoat_pars_fragment, + packing: packing, + premultiplied_alpha_fragment: premultiplied_alpha_fragment, + project_vertex: project_vertex, + dithering_fragment: dithering_fragment, + dithering_pars_fragment: dithering_pars_fragment, + roughnessmap_fragment: roughnessmap_fragment, + roughnessmap_pars_fragment: roughnessmap_pars_fragment, + shadowmap_pars_fragment: shadowmap_pars_fragment, + shadowmap_pars_vertex: shadowmap_pars_vertex, + shadowmap_vertex: shadowmap_vertex, + shadowmask_pars_fragment: shadowmask_pars_fragment, + skinbase_vertex: skinbase_vertex, + skinning_pars_vertex: skinning_pars_vertex, + skinning_vertex: skinning_vertex, + skinnormal_vertex: skinnormal_vertex, + specularmap_fragment: specularmap_fragment, + specularmap_pars_fragment: specularmap_pars_fragment, + tonemapping_fragment: tonemapping_fragment, + tonemapping_pars_fragment: tonemapping_pars_fragment, + transmissionmap_fragment: transmissionmap_fragment, + transmissionmap_pars_fragment: transmissionmap_pars_fragment, + uv_pars_fragment: uv_pars_fragment, + uv_pars_vertex: uv_pars_vertex, + uv_vertex: uv_vertex, + uv2_pars_fragment: uv2_pars_fragment, + uv2_pars_vertex: uv2_pars_vertex, + uv2_vertex: uv2_vertex, + worldpos_vertex: worldpos_vertex, + background_frag: background_frag, + background_vert: background_vert, + cube_frag: cube_frag, + cube_vert: cube_vert, + depth_frag: depth_frag, + depth_vert: depth_vert, + distanceRGBA_frag: distanceRGBA_frag, + distanceRGBA_vert: distanceRGBA_vert, + equirect_frag: equirect_frag, + equirect_vert: equirect_vert, + linedashed_frag: linedashed_frag, + linedashed_vert: linedashed_vert, + meshbasic_frag: meshbasic_frag, + meshbasic_vert: meshbasic_vert, + meshlambert_frag: meshlambert_frag, + meshlambert_vert: meshlambert_vert, + meshmatcap_frag: meshmatcap_frag, + meshmatcap_vert: meshmatcap_vert, + meshtoon_frag: meshtoon_frag, + meshtoon_vert: meshtoon_vert, + meshphong_frag: meshphong_frag, + meshphong_vert: meshphong_vert, + meshphysical_frag: meshphysical_frag, + meshphysical_vert: meshphysical_vert, + normal_frag: normal_frag, + normal_vert: normal_vert, + points_frag: points_frag, + points_vert: points_vert, + shadow_frag: shadow_frag, + shadow_vert: shadow_vert, + sprite_frag: sprite_frag, + sprite_vert: sprite_vert + }; + + /** + * Uniforms library for shared webgl shaders + */ + + var UniformsLib = { + common: { + diffuse: { + value: new Color(0xeeeeee) + }, + opacity: { + value: 1.0 + }, + map: { + value: null + }, + uvTransform: { + value: new Matrix3() + }, + uv2Transform: { + value: new Matrix3() + }, + alphaMap: { + value: null + } + }, + specularmap: { + specularMap: { + value: null + } + }, + envmap: { + envMap: { + value: null + }, + flipEnvMap: { + value: -1 + }, + reflectivity: { + value: 1.0 + }, + refractionRatio: { + value: 0.98 + }, + maxMipLevel: { + value: 0 + } + }, + aomap: { + aoMap: { + value: null + }, + aoMapIntensity: { + value: 1 + } + }, + lightmap: { + lightMap: { + value: null + }, + lightMapIntensity: { + value: 1 + } + }, + emissivemap: { + emissiveMap: { + value: null + } + }, + bumpmap: { + bumpMap: { + value: null + }, + bumpScale: { + value: 1 + } + }, + normalmap: { + normalMap: { + value: null + }, + normalScale: { + value: new Vector2(1, 1) + } + }, + displacementmap: { + displacementMap: { + value: null + }, + displacementScale: { + value: 1 + }, + displacementBias: { + value: 0 + } + }, + roughnessmap: { + roughnessMap: { + value: null + } + }, + metalnessmap: { + metalnessMap: { + value: null + } + }, + gradientmap: { + gradientMap: { + value: null + } + }, + fog: { + fogDensity: { + value: 0.00025 + }, + fogNear: { + value: 1 + }, + fogFar: { + value: 2000 + }, + fogColor: { + value: new Color(0xffffff) + } + }, + lights: { + ambientLightColor: { + value: [] + }, + lightProbe: { + value: [] + }, + directionalLights: { + value: [], + properties: { + direction: {}, + color: {} + } + }, + directionalLightShadows: { + value: [], + properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {} + } + }, + directionalShadowMap: { + value: [] + }, + directionalShadowMatrix: { + value: [] + }, + spotLights: { + value: [], + properties: { + color: {}, + position: {}, + direction: {}, + distance: {}, + coneCos: {}, + penumbraCos: {}, + decay: {} + } + }, + spotLightShadows: { + value: [], + properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {} + } + }, + spotShadowMap: { + value: [] + }, + spotShadowMatrix: { + value: [] + }, + pointLights: { + value: [], + properties: { + color: {}, + position: {}, + decay: {}, + distance: {} + } + }, + pointLightShadows: { + value: [], + properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {}, + shadowCameraNear: {}, + shadowCameraFar: {} + } + }, + pointShadowMap: { + value: [] + }, + pointShadowMatrix: { + value: [] + }, + hemisphereLights: { + value: [], + properties: { + direction: {}, + skyColor: {}, + groundColor: {} + } + }, + // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src + rectAreaLights: { + value: [], + properties: { + color: {}, + position: {}, + width: {}, + height: {} + } + }, + ltc_1: { + value: null + }, + ltc_2: { + value: null + } + }, + points: { + diffuse: { + value: new Color(0xeeeeee) + }, + opacity: { + value: 1.0 + }, + size: { + value: 1.0 + }, + scale: { + value: 1.0 + }, + map: { + value: null + }, + alphaMap: { + value: null + }, + uvTransform: { + value: new Matrix3() + } + }, + sprite: { + diffuse: { + value: new Color(0xeeeeee) + }, + opacity: { + value: 1.0 + }, + center: { + value: new Vector2(0.5, 0.5) + }, + rotation: { + value: 0.0 + }, + map: { + value: null + }, + alphaMap: { + value: null + }, + uvTransform: { + value: new Matrix3() + } + } + }; + + var ShaderLib = { + basic: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), + vertexShader: ShaderChunk.meshbasic_vert, + fragmentShader: ShaderChunk.meshbasic_frag + }, + lambert: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { + emissive: { + value: new Color(0x000000) + } + }]), + vertexShader: ShaderChunk.meshlambert_vert, + fragmentShader: ShaderChunk.meshlambert_frag + }, + phong: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { + emissive: { + value: new Color(0x000000) + }, + specular: { + value: new Color(0x111111) + }, + shininess: { + value: 30 + } + }]), + vertexShader: ShaderChunk.meshphong_vert, + fragmentShader: ShaderChunk.meshphong_frag + }, + standard: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { + emissive: { + value: new Color(0x000000) + }, + roughness: { + value: 1.0 + }, + metalness: { + value: 0.0 + }, + envMapIntensity: { + value: 1 + } // temporary + + }]), + vertexShader: ShaderChunk.meshphysical_vert, + fragmentShader: ShaderChunk.meshphysical_frag + }, + toon: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { + emissive: { + value: new Color(0x000000) + } + }]), + vertexShader: ShaderChunk.meshtoon_vert, + fragmentShader: ShaderChunk.meshtoon_frag + }, + matcap: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { + matcap: { + value: null + } + }]), + vertexShader: ShaderChunk.meshmatcap_vert, + fragmentShader: ShaderChunk.meshmatcap_frag + }, + points: { + uniforms: mergeUniforms([UniformsLib.points, UniformsLib.fog]), + vertexShader: ShaderChunk.points_vert, + fragmentShader: ShaderChunk.points_frag + }, + dashed: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.fog, { + scale: { + value: 1 + }, + dashSize: { + value: 1 + }, + totalSize: { + value: 2 + } + }]), + vertexShader: ShaderChunk.linedashed_vert, + fragmentShader: ShaderChunk.linedashed_frag + }, + depth: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), + vertexShader: ShaderChunk.depth_vert, + fragmentShader: ShaderChunk.depth_frag + }, + normal: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { + opacity: { + value: 1.0 + } + }]), + vertexShader: ShaderChunk.normal_vert, + fragmentShader: ShaderChunk.normal_frag + }, + sprite: { + uniforms: mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), + vertexShader: ShaderChunk.sprite_vert, + fragmentShader: ShaderChunk.sprite_frag + }, + background: { + uniforms: { + uvTransform: { + value: new Matrix3() + }, + t2D: { + value: null + } + }, + vertexShader: ShaderChunk.background_vert, + fragmentShader: ShaderChunk.background_frag + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + cube: { + uniforms: mergeUniforms([UniformsLib.envmap, { + opacity: { + value: 1.0 + } + }]), + vertexShader: ShaderChunk.cube_vert, + fragmentShader: ShaderChunk.cube_frag + }, + equirect: { + uniforms: { + tEquirect: { + value: null + } + }, + vertexShader: ShaderChunk.equirect_vert, + fragmentShader: ShaderChunk.equirect_frag + }, + distanceRGBA: { + uniforms: mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { + referencePosition: { + value: new Vector3() + }, + nearDistance: { + value: 1 + }, + farDistance: { + value: 1000 + } + }]), + vertexShader: ShaderChunk.distanceRGBA_vert, + fragmentShader: ShaderChunk.distanceRGBA_frag + }, + shadow: { + uniforms: mergeUniforms([UniformsLib.lights, UniformsLib.fog, { + color: { + value: new Color(0x00000) + }, + opacity: { + value: 1.0 + } + }]), + vertexShader: ShaderChunk.shadow_vert, + fragmentShader: ShaderChunk.shadow_frag + } + }; + ShaderLib.physical = { + uniforms: mergeUniforms([ShaderLib.standard.uniforms, { + clearcoat: { + value: 0 + }, + clearcoatMap: { + value: null + }, + clearcoatRoughness: { + value: 0 + }, + clearcoatRoughnessMap: { + value: null + }, + clearcoatNormalScale: { + value: new Vector2(1, 1) + }, + clearcoatNormalMap: { + value: null + }, + sheen: { + value: new Color(0x000000) + }, + transmission: { + value: 0 + }, + transmissionMap: { + value: null + } + }]), + vertexShader: ShaderChunk.meshphysical_vert, + fragmentShader: ShaderChunk.meshphysical_frag + }; + + function WebGLBackground(renderer, cubemaps, state, objects, premultipliedAlpha) { + var clearColor = new Color(0x000000); + var clearAlpha = 0; + var planeMesh; + var boxMesh; + var currentBackground = null; + var currentBackgroundVersion = 0; + var currentTonemapping = null; + + function render(renderList, scene, camera, forceClear) { + var background = scene.isScene === true ? scene.background : null; + + if (background && background.isTexture) { + background = cubemaps.get(background); + } // Ignore background in AR + // TODO: Reconsider this. + + + var xr = renderer.xr; + var session = xr.getSession && xr.getSession(); + + if (session && session.environmentBlendMode === 'additive') { + background = null; + } + + if (background === null) { + setClear(clearColor, clearAlpha); + } else if (background && background.isColor) { + setClear(background, 1); + forceClear = true; + } + + if (renderer.autoClear || forceClear) { + renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); + } + + if (background && (background.isCubeTexture || background.isWebGLCubeRenderTarget || background.mapping === CubeUVReflectionMapping)) { + if (boxMesh === undefined) { + boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ + name: 'BackgroundCubeMaterial', + uniforms: cloneUniforms(ShaderLib.cube.uniforms), + vertexShader: ShaderLib.cube.vertexShader, + fragmentShader: ShaderLib.cube.fragmentShader, + side: BackSide, + depthTest: false, + depthWrite: false, + fog: false + })); + boxMesh.geometry.deleteAttribute('normal'); + boxMesh.geometry.deleteAttribute('uv'); + + boxMesh.onBeforeRender = function (renderer, scene, camera) { + this.matrixWorld.copyPosition(camera.matrixWorld); + }; // enable code injection for non-built-in material + + + Object.defineProperty(boxMesh.material, 'envMap', { + get: function get() { + return this.uniforms.envMap.value; + } + }); + objects.update(boxMesh); + } + + if (background.isWebGLCubeRenderTarget) { + // TODO Deprecate + background = background.texture; + } + + boxMesh.material.uniforms.envMap.value = background; + boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background._needsFlipEnvMap ? -1 : 1; + + if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { + boxMesh.material.needsUpdate = true; + currentBackground = background; + currentBackgroundVersion = background.version; + currentTonemapping = renderer.toneMapping; + } // push to the pre-sorted opaque render list + + + renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); + } else if (background && background.isTexture) { + if (planeMesh === undefined) { + planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ + name: 'BackgroundMaterial', + uniforms: cloneUniforms(ShaderLib.background.uniforms), + vertexShader: ShaderLib.background.vertexShader, + fragmentShader: ShaderLib.background.fragmentShader, + side: FrontSide, + depthTest: false, + depthWrite: false, + fog: false + })); + planeMesh.geometry.deleteAttribute('normal'); // enable code injection for non-built-in material + + Object.defineProperty(planeMesh.material, 'map', { + get: function get() { + return this.uniforms.t2D.value; + } + }); + objects.update(planeMesh); + } + + planeMesh.material.uniforms.t2D.value = background; + + if (background.matrixAutoUpdate === true) { + background.updateMatrix(); + } + + planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); + + if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { + planeMesh.material.needsUpdate = true; + currentBackground = background; + currentBackgroundVersion = background.version; + currentTonemapping = renderer.toneMapping; + } // push to the pre-sorted opaque render list + + + renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); + } + } + + function setClear(color, alpha) { + state.buffers.color.setClear(color.r, color.g, color.b, alpha, premultipliedAlpha); + } + + return { + getClearColor: function getClearColor() { + return clearColor; + }, + setClearColor: function setClearColor(color, alpha) { + if (alpha === void 0) { + alpha = 1; + } + + clearColor.set(color); + clearAlpha = alpha; + setClear(clearColor, clearAlpha); + }, + getClearAlpha: function getClearAlpha() { + return clearAlpha; + }, + setClearAlpha: function setClearAlpha(alpha) { + clearAlpha = alpha; + setClear(clearColor, clearAlpha); + }, + render: render + }; + } + + function WebGLBindingStates(gl, extensions, attributes, capabilities) { + var maxVertexAttributes = gl.getParameter(34921); + var extension = capabilities.isWebGL2 ? null : extensions.get('OES_vertex_array_object'); + var vaoAvailable = capabilities.isWebGL2 || extension !== null; + var bindingStates = {}; + var defaultState = createBindingState(null); + var currentState = defaultState; + + function setup(object, material, program, geometry, index) { + var updateBuffers = false; + + if (vaoAvailable) { + var state = getBindingState(geometry, program, material); + + if (currentState !== state) { + currentState = state; + bindVertexArrayObject(currentState.object); + } + + updateBuffers = needsUpdate(geometry, index); + if (updateBuffers) saveCache(geometry, index); + } else { + var wireframe = material.wireframe === true; + + if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { + currentState.geometry = geometry.id; + currentState.program = program.id; + currentState.wireframe = wireframe; + updateBuffers = true; + } + } + + if (object.isInstancedMesh === true) { + updateBuffers = true; + } + + if (index !== null) { + attributes.update(index, 34963); + } + + if (updateBuffers) { + setupVertexAttributes(object, material, program, geometry); + + if (index !== null) { + gl.bindBuffer(34963, attributes.get(index).buffer); + } + } + } + + function createVertexArrayObject() { + if (capabilities.isWebGL2) return gl.createVertexArray(); + return extension.createVertexArrayOES(); + } + + function bindVertexArrayObject(vao) { + if (capabilities.isWebGL2) return gl.bindVertexArray(vao); + return extension.bindVertexArrayOES(vao); + } + + function deleteVertexArrayObject(vao) { + if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); + return extension.deleteVertexArrayOES(vao); + } + + function getBindingState(geometry, program, material) { + var wireframe = material.wireframe === true; + var programMap = bindingStates[geometry.id]; + + if (programMap === undefined) { + programMap = {}; + bindingStates[geometry.id] = programMap; + } + + var stateMap = programMap[program.id]; + + if (stateMap === undefined) { + stateMap = {}; + programMap[program.id] = stateMap; + } + + var state = stateMap[wireframe]; + + if (state === undefined) { + state = createBindingState(createVertexArrayObject()); + stateMap[wireframe] = state; + } + + return state; + } + + function createBindingState(vao) { + var newAttributes = []; + var enabledAttributes = []; + var attributeDivisors = []; + + for (var i = 0; i < maxVertexAttributes; i++) { + newAttributes[i] = 0; + enabledAttributes[i] = 0; + attributeDivisors[i] = 0; + } + + return { + // for backward compatibility on non-VAO support browser + geometry: null, + program: null, + wireframe: false, + newAttributes: newAttributes, + enabledAttributes: enabledAttributes, + attributeDivisors: attributeDivisors, + object: vao, + attributes: {}, + index: null + }; + } + + function needsUpdate(geometry, index) { + var cachedAttributes = currentState.attributes; + var geometryAttributes = geometry.attributes; + var attributesNum = 0; + + for (var key in geometryAttributes) { + var cachedAttribute = cachedAttributes[key]; + var geometryAttribute = geometryAttributes[key]; + if (cachedAttribute === undefined) return true; + if (cachedAttribute.attribute !== geometryAttribute) return true; + if (cachedAttribute.data !== geometryAttribute.data) return true; + attributesNum++; + } + + if (currentState.attributesNum !== attributesNum) return true; + if (currentState.index !== index) return true; + return false; + } + + function saveCache(geometry, index) { + var cache = {}; + var attributes = geometry.attributes; + var attributesNum = 0; + + for (var key in attributes) { + var attribute = attributes[key]; + var data = {}; + data.attribute = attribute; + + if (attribute.data) { + data.data = attribute.data; + } + + cache[key] = data; + attributesNum++; + } + + currentState.attributes = cache; + currentState.attributesNum = attributesNum; + currentState.index = index; + } + + function initAttributes() { + var newAttributes = currentState.newAttributes; + + for (var i = 0, il = newAttributes.length; i < il; i++) { + newAttributes[i] = 0; + } + } + + function enableAttribute(attribute) { + enableAttributeAndDivisor(attribute, 0); + } + + function enableAttributeAndDivisor(attribute, meshPerAttribute) { + var newAttributes = currentState.newAttributes; + var enabledAttributes = currentState.enabledAttributes; + var attributeDivisors = currentState.attributeDivisors; + newAttributes[attribute] = 1; + + if (enabledAttributes[attribute] === 0) { + gl.enableVertexAttribArray(attribute); + enabledAttributes[attribute] = 1; + } + + if (attributeDivisors[attribute] !== meshPerAttribute) { + var _extension = capabilities.isWebGL2 ? gl : extensions.get('ANGLE_instanced_arrays'); + + _extension[capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE'](attribute, meshPerAttribute); + + attributeDivisors[attribute] = meshPerAttribute; + } + } + + function disableUnusedAttributes() { + var newAttributes = currentState.newAttributes; + var enabledAttributes = currentState.enabledAttributes; + + for (var i = 0, il = enabledAttributes.length; i < il; i++) { + if (enabledAttributes[i] !== newAttributes[i]) { + gl.disableVertexAttribArray(i); + enabledAttributes[i] = 0; + } + } + } + + function vertexAttribPointer(index, size, type, normalized, stride, offset) { + if (capabilities.isWebGL2 === true && (type === 5124 || type === 5125)) { + gl.vertexAttribIPointer(index, size, type, stride, offset); + } else { + gl.vertexAttribPointer(index, size, type, normalized, stride, offset); + } + } + + function setupVertexAttributes(object, material, program, geometry) { + if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { + if (extensions.get('ANGLE_instanced_arrays') === null) return; + } + + initAttributes(); + var geometryAttributes = geometry.attributes; + var programAttributes = program.getAttributes(); + var materialDefaultAttributeValues = material.defaultAttributeValues; + + for (var name in programAttributes) { + var programAttribute = programAttributes[name]; + + if (programAttribute >= 0) { + var geometryAttribute = geometryAttributes[name]; + + if (geometryAttribute !== undefined) { + var normalized = geometryAttribute.normalized; + var size = geometryAttribute.itemSize; + var attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore + + if (attribute === undefined) continue; + var buffer = attribute.buffer; + var type = attribute.type; + var bytesPerElement = attribute.bytesPerElement; + + if (geometryAttribute.isInterleavedBufferAttribute) { + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; + + if (data && data.isInstancedInterleavedBuffer) { + enableAttributeAndDivisor(programAttribute, data.meshPerAttribute); + + if (geometry._maxInstanceCount === undefined) { + geometry._maxInstanceCount = data.meshPerAttribute * data.count; + } + } else { + enableAttribute(programAttribute); + } + + gl.bindBuffer(34962, buffer); + vertexAttribPointer(programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement); + } else { + if (geometryAttribute.isInstancedBufferAttribute) { + enableAttributeAndDivisor(programAttribute, geometryAttribute.meshPerAttribute); + + if (geometry._maxInstanceCount === undefined) { + geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + } + } else { + enableAttribute(programAttribute); + } + + gl.bindBuffer(34962, buffer); + vertexAttribPointer(programAttribute, size, type, normalized, 0, 0); + } + } else if (name === 'instanceMatrix') { + var _attribute = attributes.get(object.instanceMatrix); // TODO Attribute may not be available on context restore + + + if (_attribute === undefined) continue; + var _buffer = _attribute.buffer; + var _type = _attribute.type; + enableAttributeAndDivisor(programAttribute + 0, 1); + enableAttributeAndDivisor(programAttribute + 1, 1); + enableAttributeAndDivisor(programAttribute + 2, 1); + enableAttributeAndDivisor(programAttribute + 3, 1); + gl.bindBuffer(34962, _buffer); + gl.vertexAttribPointer(programAttribute + 0, 4, _type, false, 64, 0); + gl.vertexAttribPointer(programAttribute + 1, 4, _type, false, 64, 16); + gl.vertexAttribPointer(programAttribute + 2, 4, _type, false, 64, 32); + gl.vertexAttribPointer(programAttribute + 3, 4, _type, false, 64, 48); + } else if (name === 'instanceColor') { + var _attribute2 = attributes.get(object.instanceColor); // TODO Attribute may not be available on context restore + + + if (_attribute2 === undefined) continue; + var _buffer2 = _attribute2.buffer; + var _type2 = _attribute2.type; + enableAttributeAndDivisor(programAttribute, 1); + gl.bindBuffer(34962, _buffer2); + gl.vertexAttribPointer(programAttribute, 3, _type2, false, 12, 0); + } else if (materialDefaultAttributeValues !== undefined) { + var value = materialDefaultAttributeValues[name]; + + if (value !== undefined) { + switch (value.length) { + case 2: + gl.vertexAttrib2fv(programAttribute, value); + break; + + case 3: + gl.vertexAttrib3fv(programAttribute, value); + break; + + case 4: + gl.vertexAttrib4fv(programAttribute, value); + break; + + default: + gl.vertexAttrib1fv(programAttribute, value); + } + } + } + } + } + + disableUnusedAttributes(); + } + + function dispose() { + reset(); + + for (var geometryId in bindingStates) { + var programMap = bindingStates[geometryId]; + + for (var programId in programMap) { + var stateMap = programMap[programId]; + + for (var wireframe in stateMap) { + deleteVertexArrayObject(stateMap[wireframe].object); + delete stateMap[wireframe]; + } + + delete programMap[programId]; + } + + delete bindingStates[geometryId]; + } + } + + function releaseStatesOfGeometry(geometry) { + if (bindingStates[geometry.id] === undefined) return; + var programMap = bindingStates[geometry.id]; + + for (var programId in programMap) { + var stateMap = programMap[programId]; + + for (var wireframe in stateMap) { + deleteVertexArrayObject(stateMap[wireframe].object); + delete stateMap[wireframe]; + } + + delete programMap[programId]; + } + + delete bindingStates[geometry.id]; + } + + function releaseStatesOfProgram(program) { + for (var geometryId in bindingStates) { + var programMap = bindingStates[geometryId]; + if (programMap[program.id] === undefined) continue; + var stateMap = programMap[program.id]; + + for (var wireframe in stateMap) { + deleteVertexArrayObject(stateMap[wireframe].object); + delete stateMap[wireframe]; + } + + delete programMap[program.id]; + } + } + + function reset() { + resetDefaultState(); + if (currentState === defaultState) return; + currentState = defaultState; + bindVertexArrayObject(currentState.object); + } // for backward-compatilibity + + + function resetDefaultState() { + defaultState.geometry = null; + defaultState.program = null; + defaultState.wireframe = false; + } + + return { + setup: setup, + reset: reset, + resetDefaultState: resetDefaultState, + dispose: dispose, + releaseStatesOfGeometry: releaseStatesOfGeometry, + releaseStatesOfProgram: releaseStatesOfProgram, + initAttributes: initAttributes, + enableAttribute: enableAttribute, + disableUnusedAttributes: disableUnusedAttributes + }; + } + + function WebGLBufferRenderer(gl, extensions, info, capabilities) { + var isWebGL2 = capabilities.isWebGL2; + var mode; + + function setMode(value) { + mode = value; + } + + function render(start, count) { + gl.drawArrays(mode, start, count); + info.update(count, mode, 1); + } + + function renderInstances(start, count, primcount) { + if (primcount === 0) return; + var extension, methodName; + + if (isWebGL2) { + extension = gl; + methodName = 'drawArraysInstanced'; + } else { + extension = extensions.get('ANGLE_instanced_arrays'); + methodName = 'drawArraysInstancedANGLE'; + + if (extension === null) { + console.error('THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.'); + return; + } + } + + extension[methodName](mode, start, count, primcount); + info.update(count, mode, primcount); + } // + + + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; + } + + function WebGLCapabilities(gl, extensions, parameters) { + var maxAnisotropy; + + function getMaxAnisotropy() { + if (maxAnisotropy !== undefined) return maxAnisotropy; + + if (extensions.has('EXT_texture_filter_anisotropic') === true) { + var extension = extensions.get('EXT_texture_filter_anisotropic'); + maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + } else { + maxAnisotropy = 0; + } + + return maxAnisotropy; + } + + function getMaxPrecision(precision) { + if (precision === 'highp') { + if (gl.getShaderPrecisionFormat(35633, 36338).precision > 0 && gl.getShaderPrecisionFormat(35632, 36338).precision > 0) { + return 'highp'; + } + + precision = 'mediump'; + } + + if (precision === 'mediump') { + if (gl.getShaderPrecisionFormat(35633, 36337).precision > 0 && gl.getShaderPrecisionFormat(35632, 36337).precision > 0) { + return 'mediump'; + } + } + + return 'lowp'; + } + /* eslint-disable no-undef */ + + + var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext || typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext; + /* eslint-enable no-undef */ + + var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; + var maxPrecision = getMaxPrecision(precision); + + if (maxPrecision !== precision) { + console.warn('THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.'); + precision = maxPrecision; + } + + var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; + var maxTextures = gl.getParameter(34930); + var maxVertexTextures = gl.getParameter(35660); + var maxTextureSize = gl.getParameter(3379); + var maxCubemapSize = gl.getParameter(34076); + var maxAttributes = gl.getParameter(34921); + var maxVertexUniforms = gl.getParameter(36347); + var maxVaryings = gl.getParameter(36348); + var maxFragmentUniforms = gl.getParameter(36349); + var vertexTextures = maxVertexTextures > 0; + var floatFragmentTextures = isWebGL2 || extensions.has('OES_texture_float'); + var floatVertexTextures = vertexTextures && floatFragmentTextures; + var maxSamples = isWebGL2 ? gl.getParameter(36183) : 0; + return { + isWebGL2: isWebGL2, + getMaxAnisotropy: getMaxAnisotropy, + getMaxPrecision: getMaxPrecision, + precision: precision, + logarithmicDepthBuffer: logarithmicDepthBuffer, + maxTextures: maxTextures, + maxVertexTextures: maxVertexTextures, + maxTextureSize: maxTextureSize, + maxCubemapSize: maxCubemapSize, + maxAttributes: maxAttributes, + maxVertexUniforms: maxVertexUniforms, + maxVaryings: maxVaryings, + maxFragmentUniforms: maxFragmentUniforms, + vertexTextures: vertexTextures, + floatFragmentTextures: floatFragmentTextures, + floatVertexTextures: floatVertexTextures, + maxSamples: maxSamples + }; + } + + function WebGLClipping(properties) { + var scope = this; + var globalState = null, + numGlobalPlanes = 0, + localClippingEnabled = false, + renderingShadows = false; + var plane = new Plane(), + viewNormalMatrix = new Matrix3(), + uniform = { + value: null, + needsUpdate: false + }; + this.uniform = uniform; + this.numPlanes = 0; + this.numIntersection = 0; + + this.init = function (planes, enableLocalClipping, camera) { + var enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to + // run another frame in order to reset the state: + numGlobalPlanes !== 0 || localClippingEnabled; + localClippingEnabled = enableLocalClipping; + globalState = projectPlanes(planes, camera, 0); + numGlobalPlanes = planes.length; + return enabled; + }; + + this.beginShadows = function () { + renderingShadows = true; + projectPlanes(null); + }; + + this.endShadows = function () { + renderingShadows = false; + resetGlobalState(); + }; + + this.setState = function (material, camera, useCache) { + var planes = material.clippingPlanes, + clipIntersection = material.clipIntersection, + clipShadows = material.clipShadows; + var materialProperties = properties.get(material); + + if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { + // there's no local clipping + if (renderingShadows) { + // there's no global clipping + projectPlanes(null); + } else { + resetGlobalState(); + } + } else { + var nGlobal = renderingShadows ? 0 : numGlobalPlanes, + lGlobal = nGlobal * 4; + var dstArray = materialProperties.clippingState || null; + uniform.value = dstArray; // ensure unique state + + dstArray = projectPlanes(planes, camera, lGlobal, useCache); + + for (var i = 0; i !== lGlobal; ++i) { + dstArray[i] = globalState[i]; + } + + materialProperties.clippingState = dstArray; + this.numIntersection = clipIntersection ? this.numPlanes : 0; + this.numPlanes += nGlobal; + } + }; + + function resetGlobalState() { + if (uniform.value !== globalState) { + uniform.value = globalState; + uniform.needsUpdate = numGlobalPlanes > 0; + } + + scope.numPlanes = numGlobalPlanes; + scope.numIntersection = 0; + } + + function projectPlanes(planes, camera, dstOffset, skipTransform) { + var nPlanes = planes !== null ? planes.length : 0; + var dstArray = null; + + if (nPlanes !== 0) { + dstArray = uniform.value; + + if (skipTransform !== true || dstArray === null) { + var flatSize = dstOffset + nPlanes * 4, + viewMatrix = camera.matrixWorldInverse; + viewNormalMatrix.getNormalMatrix(viewMatrix); + + if (dstArray === null || dstArray.length < flatSize) { + dstArray = new Float32Array(flatSize); + } + + for (var i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { + plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); + plane.normal.toArray(dstArray, i4); + dstArray[i4 + 3] = plane.constant; + } + } + + uniform.value = dstArray; + uniform.needsUpdate = true; + } + + scope.numPlanes = nPlanes; + scope.numIntersection = 0; + return dstArray; + } + } + + function WebGLCubeMaps(renderer) { + var cubemaps = new WeakMap(); + + function mapTextureMapping(texture, mapping) { + if (mapping === EquirectangularReflectionMapping) { + texture.mapping = CubeReflectionMapping; + } else if (mapping === EquirectangularRefractionMapping) { + texture.mapping = CubeRefractionMapping; + } + + return texture; + } + + function get(texture) { + if (texture && texture.isTexture) { + var mapping = texture.mapping; + + if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { + if (cubemaps.has(texture)) { + var cubemap = cubemaps.get(texture).texture; + return mapTextureMapping(cubemap, texture.mapping); + } else { + var image = texture.image; + + if (image && image.height > 0) { + var currentRenderTarget = renderer.getRenderTarget(); + var renderTarget = new WebGLCubeRenderTarget(image.height / 2); + renderTarget.fromEquirectangularTexture(renderer, texture); + cubemaps.set(texture, renderTarget); + renderer.setRenderTarget(currentRenderTarget); + texture.addEventListener('dispose', onTextureDispose); + return mapTextureMapping(renderTarget.texture, texture.mapping); + } else { + // image not yet ready. try the conversion next frame + return null; + } + } + } + } + + return texture; + } + + function onTextureDispose(event) { + var texture = event.target; + texture.removeEventListener('dispose', onTextureDispose); + var cubemap = cubemaps.get(texture); + + if (cubemap !== undefined) { + cubemaps.delete(texture); + cubemap.dispose(); + } + } + + function dispose() { + cubemaps = new WeakMap(); + } + + return { + get: get, + dispose: dispose + }; + } + + function WebGLExtensions(gl) { + var extensions = {}; + + function getExtension(name) { + if (extensions[name] !== undefined) { + return extensions[name]; + } + + var extension; + + switch (name) { + case 'WEBGL_depth_texture': + extension = gl.getExtension('WEBGL_depth_texture') || gl.getExtension('MOZ_WEBGL_depth_texture') || gl.getExtension('WEBKIT_WEBGL_depth_texture'); + break; + + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); + break; + + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension('WEBGL_compressed_texture_s3tc') || gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); + break; + + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension('WEBGL_compressed_texture_pvrtc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); + break; + + default: + extension = gl.getExtension(name); + } + + extensions[name] = extension; + return extension; + } + + return { + has: function has(name) { + return getExtension(name) !== null; + }, + init: function init(capabilities) { + if (capabilities.isWebGL2) { + getExtension('EXT_color_buffer_float'); + } else { + getExtension('WEBGL_depth_texture'); + getExtension('OES_texture_float'); + getExtension('OES_texture_half_float'); + getExtension('OES_texture_half_float_linear'); + getExtension('OES_standard_derivatives'); + getExtension('OES_element_index_uint'); + getExtension('OES_vertex_array_object'); + getExtension('ANGLE_instanced_arrays'); + } + + getExtension('OES_texture_float_linear'); + getExtension('EXT_color_buffer_half_float'); + }, + get: function get(name) { + var extension = getExtension(name); + + if (extension === null) { + console.warn('THREE.WebGLRenderer: ' + name + ' extension not supported.'); + } + + return extension; + } + }; + } + + function WebGLGeometries(gl, attributes, info, bindingStates) { + var geometries = {}; + var wireframeAttributes = new WeakMap(); + + function onGeometryDispose(event) { + var geometry = event.target; + + if (geometry.index !== null) { + attributes.remove(geometry.index); + } + + for (var name in geometry.attributes) { + attributes.remove(geometry.attributes[name]); + } + + geometry.removeEventListener('dispose', onGeometryDispose); + delete geometries[geometry.id]; + var attribute = wireframeAttributes.get(geometry); + + if (attribute) { + attributes.remove(attribute); + wireframeAttributes.delete(geometry); + } + + bindingStates.releaseStatesOfGeometry(geometry); + + if (geometry.isInstancedBufferGeometry === true) { + delete geometry._maxInstanceCount; + } // + + + info.memory.geometries--; + } + + function get(object, geometry) { + if (geometries[geometry.id] === true) return geometry; + geometry.addEventListener('dispose', onGeometryDispose); + geometries[geometry.id] = true; + info.memory.geometries++; + return geometry; + } + + function update(geometry) { + var geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. + + for (var name in geometryAttributes) { + attributes.update(geometryAttributes[name], 34962); + } // morph targets + + + var morphAttributes = geometry.morphAttributes; + + for (var _name in morphAttributes) { + var array = morphAttributes[_name]; + + for (var i = 0, l = array.length; i < l; i++) { + attributes.update(array[i], 34962); + } + } + } + + function updateWireframeAttribute(geometry) { + var indices = []; + var geometryIndex = geometry.index; + var geometryPosition = geometry.attributes.position; + var version = 0; + + if (geometryIndex !== null) { + var array = geometryIndex.array; + version = geometryIndex.version; + + for (var i = 0, l = array.length; i < l; i += 3) { + var a = array[i + 0]; + var b = array[i + 1]; + var c = array[i + 2]; + indices.push(a, b, b, c, c, a); + } + } else { + var _array = geometryPosition.array; + version = geometryPosition.version; + + for (var _i = 0, _l = _array.length / 3 - 1; _i < _l; _i += 3) { + var _a = _i + 0; + + var _b = _i + 1; + + var _c = _i + 2; + + indices.push(_a, _b, _b, _c, _c, _a); + } + } + + var attribute = new (arrayMax(indices) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); + attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates + // + + var previousAttribute = wireframeAttributes.get(geometry); + if (previousAttribute) attributes.remove(previousAttribute); // + + wireframeAttributes.set(geometry, attribute); + } + + function getWireframeAttribute(geometry) { + var currentAttribute = wireframeAttributes.get(geometry); + + if (currentAttribute) { + var geometryIndex = geometry.index; + + if (geometryIndex !== null) { + // if the attribute is obsolete, create a new one + if (currentAttribute.version < geometryIndex.version) { + updateWireframeAttribute(geometry); + } + } + } else { + updateWireframeAttribute(geometry); + } + + return wireframeAttributes.get(geometry); + } + + return { + get: get, + update: update, + getWireframeAttribute: getWireframeAttribute + }; + } + + function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { + var isWebGL2 = capabilities.isWebGL2; + var mode; + + function setMode(value) { + mode = value; + } + + var type, bytesPerElement; + + function setIndex(value) { + type = value.type; + bytesPerElement = value.bytesPerElement; + } + + function render(start, count) { + gl.drawElements(mode, count, type, start * bytesPerElement); + info.update(count, mode, 1); + } + + function renderInstances(start, count, primcount) { + if (primcount === 0) return; + var extension, methodName; + + if (isWebGL2) { + extension = gl; + methodName = 'drawElementsInstanced'; + } else { + extension = extensions.get('ANGLE_instanced_arrays'); + methodName = 'drawElementsInstancedANGLE'; + + if (extension === null) { + console.error('THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.'); + return; + } + } + + extension[methodName](mode, count, type, start * bytesPerElement, primcount); + info.update(count, mode, primcount); + } // + + + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; + } + + function WebGLInfo(gl) { + var memory = { + geometries: 0, + textures: 0 + }; + var render = { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0 + }; + + function update(count, mode, instanceCount) { + render.calls++; + + switch (mode) { + case 4: + render.triangles += instanceCount * (count / 3); + break; + + case 1: + render.lines += instanceCount * (count / 2); + break; + + case 3: + render.lines += instanceCount * (count - 1); + break; + + case 2: + render.lines += instanceCount * count; + break; + + case 0: + render.points += instanceCount * count; + break; + + default: + console.error('THREE.WebGLInfo: Unknown draw mode:', mode); + break; + } + } + + function reset() { + render.frame++; + render.calls = 0; + render.triangles = 0; + render.points = 0; + render.lines = 0; + } + + return { + memory: memory, + render: render, + programs: null, + autoReset: true, + reset: reset, + update: update + }; + } + + function numericalSort(a, b) { + return a[0] - b[0]; + } + + function absNumericalSort(a, b) { + return Math.abs(b[1]) - Math.abs(a[1]); + } + + function WebGLMorphtargets(gl) { + var influencesList = {}; + var morphInfluences = new Float32Array(8); + var workInfluences = []; + + for (var i = 0; i < 8; i++) { + workInfluences[i] = [i, 0]; + } + + function update(object, geometry, material, program) { + var objectInfluences = object.morphTargetInfluences; // When object doesn't have morph target influences defined, we treat it as a 0-length array + // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences + + var length = objectInfluences === undefined ? 0 : objectInfluences.length; + var influences = influencesList[geometry.id]; + + if (influences === undefined) { + // initialise list + influences = []; + + for (var _i = 0; _i < length; _i++) { + influences[_i] = [_i, 0]; + } + + influencesList[geometry.id] = influences; + } // Collect influences + + + for (var _i2 = 0; _i2 < length; _i2++) { + var influence = influences[_i2]; + influence[0] = _i2; + influence[1] = objectInfluences[_i2]; + } + + influences.sort(absNumericalSort); + + for (var _i3 = 0; _i3 < 8; _i3++) { + if (_i3 < length && influences[_i3][1]) { + workInfluences[_i3][0] = influences[_i3][0]; + workInfluences[_i3][1] = influences[_i3][1]; + } else { + workInfluences[_i3][0] = Number.MAX_SAFE_INTEGER; + workInfluences[_i3][1] = 0; + } + } + + workInfluences.sort(numericalSort); + var morphTargets = material.morphTargets && geometry.morphAttributes.position; + var morphNormals = material.morphNormals && geometry.morphAttributes.normal; + var morphInfluencesSum = 0; + + for (var _i4 = 0; _i4 < 8; _i4++) { + var _influence = workInfluences[_i4]; + var index = _influence[0]; + var value = _influence[1]; + + if (index !== Number.MAX_SAFE_INTEGER && value) { + if (morphTargets && geometry.getAttribute('morphTarget' + _i4) !== morphTargets[index]) { + geometry.setAttribute('morphTarget' + _i4, morphTargets[index]); + } + + if (morphNormals && geometry.getAttribute('morphNormal' + _i4) !== morphNormals[index]) { + geometry.setAttribute('morphNormal' + _i4, morphNormals[index]); + } + + morphInfluences[_i4] = value; + morphInfluencesSum += value; + } else { + if (morphTargets && geometry.hasAttribute('morphTarget' + _i4) === true) { + geometry.deleteAttribute('morphTarget' + _i4); + } + + if (morphNormals && geometry.hasAttribute('morphNormal' + _i4) === true) { + geometry.deleteAttribute('morphNormal' + _i4); + } + + morphInfluences[_i4] = 0; + } + } // GLSL shader uses formula baseinfluence * base + sum(target * influence) + // This allows us to switch between absolute morphs and relative morphs without changing shader code + // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) + + + var morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; + program.getUniforms().setValue(gl, 'morphTargetBaseInfluence', morphBaseInfluence); + program.getUniforms().setValue(gl, 'morphTargetInfluences', morphInfluences); + } + + return { + update: update + }; + } + + function WebGLObjects(gl, geometries, attributes, info) { + var updateMap = new WeakMap(); + + function update(object) { + var frame = info.render.frame; + var geometry = object.geometry; + var buffergeometry = geometries.get(object, geometry); // Update once per frame + + if (updateMap.get(buffergeometry) !== frame) { + geometries.update(buffergeometry); + updateMap.set(buffergeometry, frame); + } + + if (object.isInstancedMesh) { + if (object.hasEventListener('dispose', onInstancedMeshDispose) === false) { + object.addEventListener('dispose', onInstancedMeshDispose); + } + + attributes.update(object.instanceMatrix, 34962); + + if (object.instanceColor !== null) { + attributes.update(object.instanceColor, 34962); + } + } + + return buffergeometry; + } + + function dispose() { + updateMap = new WeakMap(); + } + + function onInstancedMeshDispose(event) { + var instancedMesh = event.target; + instancedMesh.removeEventListener('dispose', onInstancedMeshDispose); + attributes.remove(instancedMesh.instanceMatrix); + if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); + } + + return { + update: update, + dispose: dispose + }; + } + + var DataTexture2DArray = /*#__PURE__*/function (_Texture) { + _inheritsLoose(DataTexture2DArray, _Texture); + + function DataTexture2DArray(data, width, height, depth) { + var _this; + + if (data === void 0) { + data = null; + } + + if (width === void 0) { + width = 1; + } + + if (height === void 0) { + height = 1; + } + + if (depth === void 0) { + depth = 1; + } + + _this = _Texture.call(this, null) || this; + _this.image = { + data: data, + width: width, + height: height, + depth: depth + }; + _this.magFilter = NearestFilter; + _this.minFilter = NearestFilter; + _this.wrapR = ClampToEdgeWrapping; + _this.generateMipmaps = false; + _this.flipY = false; + _this.needsUpdate = true; + return _this; + } + + return DataTexture2DArray; + }(Texture); + + DataTexture2DArray.prototype.isDataTexture2DArray = true; + + var DataTexture3D = /*#__PURE__*/function (_Texture) { + _inheritsLoose(DataTexture3D, _Texture); + + function DataTexture3D(data, width, height, depth) { + var _this; + + if (data === void 0) { + data = null; + } + + if (width === void 0) { + width = 1; + } + + if (height === void 0) { + height = 1; + } + + if (depth === void 0) { + depth = 1; + } + + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // const texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 + _this = _Texture.call(this, null) || this; + _this.image = { + data: data, + width: width, + height: height, + depth: depth + }; + _this.magFilter = NearestFilter; + _this.minFilter = NearestFilter; + _this.wrapR = ClampToEdgeWrapping; + _this.generateMipmaps = false; + _this.flipY = false; + _this.needsUpdate = true; + return _this; + } + + return DataTexture3D; + }(Texture); + + DataTexture3D.prototype.isDataTexture3D = true; + + /** + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [textures] ) + * + * uploads a uniform value(s) + * the 'textures' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (textures factorizations): + * + * .upload( gl, seq, values, textures ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * + * Methods of the top-level container (textures factorizations): + * + * .setValue( gl, name, value, textures ) + * + * sets uniform with name 'name' to 'value' + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + */ + var emptyTexture = new Texture(); + var emptyTexture2dArray = new DataTexture2DArray(); + var emptyTexture3d = new DataTexture3D(); + var emptyCubeTexture = new CubeTexture(); // --- Utilities --- + // Array Caches (provide typed arrays for temporary by size) + + var arrayCacheF32 = []; + var arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms + + var mat4array = new Float32Array(16); + var mat3array = new Float32Array(9); + var mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices + + function flatten(array, nBlocks, blockSize) { + var firstElem = array[0]; + if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 + + var n = nBlocks * blockSize; + var r = arrayCacheF32[n]; + + if (r === undefined) { + r = new Float32Array(n); + arrayCacheF32[n] = r; + } + + if (nBlocks !== 0) { + firstElem.toArray(r, 0); + + for (var i = 1, offset = 0; i !== nBlocks; ++i) { + offset += blockSize; + array[i].toArray(r, offset); + } + } + + return r; + } + + function arraysEqual(a, b) { + if (a.length !== b.length) return false; + + for (var i = 0, l = a.length; i < l; i++) { + if (a[i] !== b[i]) return false; + } + + return true; + } + + function copyArray(a, b) { + for (var i = 0, l = b.length; i < l; i++) { + a[i] = b[i]; + } + } // Texture unit allocation + + + function allocTexUnits(textures, n) { + var r = arrayCacheI32[n]; + + if (r === undefined) { + r = new Int32Array(n); + arrayCacheI32[n] = r; + } + + for (var i = 0; i !== n; ++i) { + r[i] = textures.allocateTextureUnit(); + } + + return r; + } // --- Setters --- + // Note: Defining these methods externally, because they come in a bunch + // and this way their names minify. + // Single scalar + + + function setValueV1f(gl, v) { + var cache = this.cache; + if (cache[0] === v) return; + gl.uniform1f(this.addr, v); + cache[0] = v; + } // Single float vector (from flat array or THREE.VectorN) + + + function setValueV2f(gl, v) { + var cache = this.cache; + + if (v.x !== undefined) { + if (cache[0] !== v.x || cache[1] !== v.y) { + gl.uniform2f(this.addr, v.x, v.y); + cache[0] = v.x; + cache[1] = v.y; + } + } else { + if (arraysEqual(cache, v)) return; + gl.uniform2fv(this.addr, v); + copyArray(cache, v); + } + } + + function setValueV3f(gl, v) { + var cache = this.cache; + + if (v.x !== undefined) { + if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { + gl.uniform3f(this.addr, v.x, v.y, v.z); + cache[0] = v.x; + cache[1] = v.y; + cache[2] = v.z; + } + } else if (v.r !== undefined) { + if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { + gl.uniform3f(this.addr, v.r, v.g, v.b); + cache[0] = v.r; + cache[1] = v.g; + cache[2] = v.b; + } + } else { + if (arraysEqual(cache, v)) return; + gl.uniform3fv(this.addr, v); + copyArray(cache, v); + } + } + + function setValueV4f(gl, v) { + var cache = this.cache; + + if (v.x !== undefined) { + if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { + gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); + cache[0] = v.x; + cache[1] = v.y; + cache[2] = v.z; + cache[3] = v.w; + } + } else { + if (arraysEqual(cache, v)) return; + gl.uniform4fv(this.addr, v); + copyArray(cache, v); + } + } // Single matrix (from flat array or MatrixN) + + + function setValueM2(gl, v) { + var cache = this.cache; + var elements = v.elements; + + if (elements === undefined) { + if (arraysEqual(cache, v)) return; + gl.uniformMatrix2fv(this.addr, false, v); + copyArray(cache, v); + } else { + if (arraysEqual(cache, elements)) return; + mat2array.set(elements); + gl.uniformMatrix2fv(this.addr, false, mat2array); + copyArray(cache, elements); + } + } + + function setValueM3(gl, v) { + var cache = this.cache; + var elements = v.elements; + + if (elements === undefined) { + if (arraysEqual(cache, v)) return; + gl.uniformMatrix3fv(this.addr, false, v); + copyArray(cache, v); + } else { + if (arraysEqual(cache, elements)) return; + mat3array.set(elements); + gl.uniformMatrix3fv(this.addr, false, mat3array); + copyArray(cache, elements); + } + } + + function setValueM4(gl, v) { + var cache = this.cache; + var elements = v.elements; + + if (elements === undefined) { + if (arraysEqual(cache, v)) return; + gl.uniformMatrix4fv(this.addr, false, v); + copyArray(cache, v); + } else { + if (arraysEqual(cache, elements)) return; + mat4array.set(elements); + gl.uniformMatrix4fv(this.addr, false, mat4array); + copyArray(cache, elements); + } + } // Single texture (2D / Cube) + + + function setValueT1(gl, v, textures) { + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if (cache[0] !== unit) { + gl.uniform1i(this.addr, unit); + cache[0] = unit; + } + + textures.safeSetTexture2D(v || emptyTexture, unit); + } + + function setValueT2DArray1(gl, v, textures) { + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if (cache[0] !== unit) { + gl.uniform1i(this.addr, unit); + cache[0] = unit; + } + + textures.setTexture2DArray(v || emptyTexture2dArray, unit); + } + + function setValueT3D1(gl, v, textures) { + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if (cache[0] !== unit) { + gl.uniform1i(this.addr, unit); + cache[0] = unit; + } + + textures.setTexture3D(v || emptyTexture3d, unit); + } + + function setValueT6(gl, v, textures) { + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if (cache[0] !== unit) { + gl.uniform1i(this.addr, unit); + cache[0] = unit; + } + + textures.safeSetTextureCube(v || emptyCubeTexture, unit); + } // Integer / Boolean vectors or arrays thereof (always flat arrays) + + + function setValueV1i(gl, v) { + var cache = this.cache; + if (cache[0] === v) return; + gl.uniform1i(this.addr, v); + cache[0] = v; + } + + function setValueV2i(gl, v) { + var cache = this.cache; + if (arraysEqual(cache, v)) return; + gl.uniform2iv(this.addr, v); + copyArray(cache, v); + } + + function setValueV3i(gl, v) { + var cache = this.cache; + if (arraysEqual(cache, v)) return; + gl.uniform3iv(this.addr, v); + copyArray(cache, v); + } + + function setValueV4i(gl, v) { + var cache = this.cache; + if (arraysEqual(cache, v)) return; + gl.uniform4iv(this.addr, v); + copyArray(cache, v); + } // uint + + + function setValueV1ui(gl, v) { + var cache = this.cache; + if (cache[0] === v) return; + gl.uniform1ui(this.addr, v); + cache[0] = v; + } // Helper to pick the right setter for the singular case + + + function getSingularSetter(type) { + switch (type) { + case 0x1406: + return setValueV1f; + // FLOAT + + case 0x8b50: + return setValueV2f; + // _VEC2 + + case 0x8b51: + return setValueV3f; + // _VEC3 + + case 0x8b52: + return setValueV4f; + // _VEC4 + + case 0x8b5a: + return setValueM2; + // _MAT2 + + case 0x8b5b: + return setValueM3; + // _MAT3 + + case 0x8b5c: + return setValueM4; + // _MAT4 + + case 0x1404: + case 0x8b56: + return setValueV1i; + // INT, BOOL + + case 0x8b53: + case 0x8b57: + return setValueV2i; + // _VEC2 + + case 0x8b54: + case 0x8b58: + return setValueV3i; + // _VEC3 + + case 0x8b55: + case 0x8b59: + return setValueV4i; + // _VEC4 + + case 0x1405: + return setValueV1ui; + // UINT + + case 0x8b5e: // SAMPLER_2D + + case 0x8d66: // SAMPLER_EXTERNAL_OES + + case 0x8dca: // INT_SAMPLER_2D + + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + + case 0x8b62: + // SAMPLER_2D_SHADOW + return setValueT1; + + case 0x8b5f: // SAMPLER_3D + + case 0x8dcb: // INT_SAMPLER_3D + + case 0x8dd3: + // UNSIGNED_INT_SAMPLER_3D + return setValueT3D1; + + case 0x8b60: // SAMPLER_CUBE + + case 0x8dcc: // INT_SAMPLER_CUBE + + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + + case 0x8dc5: + // SAMPLER_CUBE_SHADOW + return setValueT6; + + case 0x8dc1: // SAMPLER_2D_ARRAY + + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + + case 0x8dc4: + // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArray1; + } + } // Array of scalars + + + function setValueV1fArray(gl, v) { + gl.uniform1fv(this.addr, v); + } // Integer / Boolean vectors or arrays thereof (always flat arrays) + + + function setValueV1iArray(gl, v) { + gl.uniform1iv(this.addr, v); + } + + function setValueV2iArray(gl, v) { + gl.uniform2iv(this.addr, v); + } + + function setValueV3iArray(gl, v) { + gl.uniform3iv(this.addr, v); + } + + function setValueV4iArray(gl, v) { + gl.uniform4iv(this.addr, v); + } // Array of vectors (flat or from THREE classes) + + + function setValueV2fArray(gl, v) { + var data = flatten(v, this.size, 2); + gl.uniform2fv(this.addr, data); + } + + function setValueV3fArray(gl, v) { + var data = flatten(v, this.size, 3); + gl.uniform3fv(this.addr, data); + } + + function setValueV4fArray(gl, v) { + var data = flatten(v, this.size, 4); + gl.uniform4fv(this.addr, data); + } // Array of matrices (flat or from THREE clases) + + + function setValueM2Array(gl, v) { + var data = flatten(v, this.size, 4); + gl.uniformMatrix2fv(this.addr, false, data); + } + + function setValueM3Array(gl, v) { + var data = flatten(v, this.size, 9); + gl.uniformMatrix3fv(this.addr, false, data); + } + + function setValueM4Array(gl, v) { + var data = flatten(v, this.size, 16); + gl.uniformMatrix4fv(this.addr, false, data); + } // Array of textures (2D / Cube) + + + function setValueT1Array(gl, v, textures) { + var n = v.length; + var units = allocTexUnits(textures, n); + gl.uniform1iv(this.addr, units); + + for (var i = 0; i !== n; ++i) { + textures.safeSetTexture2D(v[i] || emptyTexture, units[i]); + } + } + + function setValueT6Array(gl, v, textures) { + var n = v.length; + var units = allocTexUnits(textures, n); + gl.uniform1iv(this.addr, units); + + for (var i = 0; i !== n; ++i) { + textures.safeSetTextureCube(v[i] || emptyCubeTexture, units[i]); + } + } // Helper to pick the right setter for a pure (bottom-level) array + + + function getPureArraySetter(type) { + switch (type) { + case 0x1406: + return setValueV1fArray; + // FLOAT + + case 0x8b50: + return setValueV2fArray; + // _VEC2 + + case 0x8b51: + return setValueV3fArray; + // _VEC3 + + case 0x8b52: + return setValueV4fArray; + // _VEC4 + + case 0x8b5a: + return setValueM2Array; + // _MAT2 + + case 0x8b5b: + return setValueM3Array; + // _MAT3 + + case 0x8b5c: + return setValueM4Array; + // _MAT4 + + case 0x1404: + case 0x8b56: + return setValueV1iArray; + // INT, BOOL + + case 0x8b53: + case 0x8b57: + return setValueV2iArray; + // _VEC2 + + case 0x8b54: + case 0x8b58: + return setValueV3iArray; + // _VEC3 + + case 0x8b55: + case 0x8b59: + return setValueV4iArray; + // _VEC4 + + case 0x8b5e: // SAMPLER_2D + + case 0x8d66: // SAMPLER_EXTERNAL_OES + + case 0x8dca: // INT_SAMPLER_2D + + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + + case 0x8b62: + // SAMPLER_2D_SHADOW + return setValueT1Array; + + case 0x8b60: // SAMPLER_CUBE + + case 0x8dcc: // INT_SAMPLER_CUBE + + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + + case 0x8dc5: + // SAMPLER_CUBE_SHADOW + return setValueT6Array; + } + } // --- Uniform Classes --- + + + function SingleUniform(id, activeInfo, addr) { + this.id = id; + this.addr = addr; + this.cache = []; + this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG + } + + function PureArrayUniform(id, activeInfo, addr) { + this.id = id; + this.addr = addr; + this.cache = []; + this.size = activeInfo.size; + this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG + } + + PureArrayUniform.prototype.updateCache = function (data) { + var cache = this.cache; + + if (data instanceof Float32Array && cache.length !== data.length) { + this.cache = new Float32Array(data.length); + } + + copyArray(cache, data); + }; + + function StructuredUniform(id) { + this.id = id; + this.seq = []; + this.map = {}; + } + + StructuredUniform.prototype.setValue = function (gl, value, textures) { + var seq = this.seq; + + for (var i = 0, n = seq.length; i !== n; ++i) { + var u = seq[i]; + u.setValue(gl, value[u.id], textures); + } + }; // --- Top-level --- + // Parser - builds up the property tree from the path strings + + + var RePathPart = /(\w+)(\])?(\[|\.)?/g; // extracts + // - the identifier (member name or array index) + // - followed by an optional right bracket (found when array index) + // - followed by an optional left bracket or dot (type of subscript) + // + // Note: These portions can be read in a non-overlapping fashion and + // allow straightforward parsing of the hierarchy that WebGL encodes + // in the uniform names. + + function addUniform(container, uniformObject) { + container.seq.push(uniformObject); + container.map[uniformObject.id] = uniformObject; + } + + function parseUniform(activeInfo, addr, container) { + var path = activeInfo.name, + pathLength = path.length; // reset RegExp object, because of the early exit of a previous run + + RePathPart.lastIndex = 0; + + while (true) { + var match = RePathPart.exec(path), + matchEnd = RePathPart.lastIndex; + var id = match[1]; + var idIsIndex = match[2] === ']', + subscript = match[3]; + if (idIsIndex) id = id | 0; // convert to integer + + if (subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength) { + // bare name or "pure" bottom-level array "[0]" suffix + addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); + break; + } else { + // step into inner node / create it in case it doesn't exist + var map = container.map; + var next = map[id]; + + if (next === undefined) { + next = new StructuredUniform(id); + addUniform(container, next); + } + + container = next; + } + } + } // Root Container + + + function WebGLUniforms(gl, program) { + this.seq = []; + this.map = {}; + var n = gl.getProgramParameter(program, 35718); + + for (var i = 0; i < n; ++i) { + var info = gl.getActiveUniform(program, i), + addr = gl.getUniformLocation(program, info.name); + parseUniform(info, addr, this); + } + } + + WebGLUniforms.prototype.setValue = function (gl, name, value, textures) { + var u = this.map[name]; + if (u !== undefined) u.setValue(gl, value, textures); + }; + + WebGLUniforms.prototype.setOptional = function (gl, object, name) { + var v = object[name]; + if (v !== undefined) this.setValue(gl, name, v); + }; // Static interface + + + WebGLUniforms.upload = function (gl, seq, values, textures) { + for (var i = 0, n = seq.length; i !== n; ++i) { + var u = seq[i], + v = values[u.id]; + + if (v.needsUpdate !== false) { + // note: always updating when .needsUpdate is undefined + u.setValue(gl, v.value, textures); + } + } + }; + + WebGLUniforms.seqWithValue = function (seq, values) { + var r = []; + + for (var i = 0, n = seq.length; i !== n; ++i) { + var u = seq[i]; + if (u.id in values) r.push(u); + } + + return r; + }; + + function WebGLShader(gl, type, string) { + var shader = gl.createShader(type); + gl.shaderSource(shader, string); + gl.compileShader(shader); + return shader; + } + + var programIdCount = 0; + + function addLineNumbers(string) { + var lines = string.split('\n'); + + for (var i = 0; i < lines.length; i++) { + lines[i] = i + 1 + ': ' + lines[i]; + } + + return lines.join('\n'); + } + + function getEncodingComponents(encoding) { + switch (encoding) { + case LinearEncoding: + return ['Linear', '( value )']; + + case sRGBEncoding: + return ['sRGB', '( value )']; + + case RGBEEncoding: + return ['RGBE', '( value )']; + + case RGBM7Encoding: + return ['RGBM', '( value, 7.0 )']; + + case RGBM16Encoding: + return ['RGBM', '( value, 16.0 )']; + + case RGBDEncoding: + return ['RGBD', '( value, 256.0 )']; + + case GammaEncoding: + return ['Gamma', '( value, float( GAMMA_FACTOR ) )']; + + case LogLuvEncoding: + return ['LogLuv', '( value )']; + + default: + console.warn('THREE.WebGLProgram: Unsupported encoding:', encoding); + return ['Linear', '( value )']; + } + } + + function getShaderErrors(gl, shader, type) { + var status = gl.getShaderParameter(shader, 35713); + var log = gl.getShaderInfoLog(shader).trim(); + if (status && log === '') return ''; // --enable-privileged-webgl-extension + // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + var source = gl.getShaderSource(shader); + return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers(source); + } + + function getTexelDecodingFunction(functionName, encoding) { + var components = getEncodingComponents(encoding); + return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[0] + 'ToLinear' + components[1] + '; }'; + } + + function getTexelEncodingFunction(functionName, encoding) { + var components = getEncodingComponents(encoding); + return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[0] + components[1] + '; }'; + } + + function getToneMappingFunction(functionName, toneMapping) { + var toneMappingName; + + switch (toneMapping) { + case LinearToneMapping: + toneMappingName = 'Linear'; + break; + + case ReinhardToneMapping: + toneMappingName = 'Reinhard'; + break; + + case CineonToneMapping: + toneMappingName = 'OptimizedCineon'; + break; + + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; + + case CustomToneMapping: + toneMappingName = 'Custom'; + break; + + default: + console.warn('THREE.WebGLProgram: Unsupported toneMapping:', toneMapping); + toneMappingName = 'Linear'; + } + + return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + } + + function generateExtensions(parameters) { + var chunks = [parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ? '#extension GL_OES_standard_derivatives : enable' : '', (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? '#extension GL_EXT_draw_buffers : require' : '', (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '']; + return chunks.filter(filterEmptyLine).join('\n'); + } + + function generateDefines(defines) { + var chunks = []; + + for (var name in defines) { + var value = defines[name]; + if (value === false) continue; + chunks.push('#define ' + name + ' ' + value); + } + + return chunks.join('\n'); + } + + function fetchAttributeLocations(gl, program) { + var attributes = {}; + var n = gl.getProgramParameter(program, 35721); + + for (var i = 0; i < n; i++) { + var info = gl.getActiveAttrib(program, i); + var name = info.name; // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + + attributes[name] = gl.getAttribLocation(program, name); + } + + return attributes; + } + + function filterEmptyLine(string) { + return string !== ''; + } + + function replaceLightNums(string, parameters) { + return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); + } + + function replaceClippingPlaneNums(string, parameters) { + return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); + } // Resolve Includes + + + var includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; + + function resolveIncludes(string) { + return string.replace(includePattern, includeReplacer); + } + + function includeReplacer(match, include) { + var string = ShaderChunk[include]; + + if (string === undefined) { + throw new Error('Can not resolve #include <' + include + '>'); + } + + return resolveIncludes(string); + } // Unroll Loops + + + var deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; + var unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; + + function unrollLoops(string) { + return string.replace(unrollLoopPattern, loopReplacer).replace(deprecatedUnrollLoopPattern, deprecatedLoopReplacer); + } + + function deprecatedLoopReplacer(match, start, end, snippet) { + console.warn('WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.'); + return loopReplacer(match, start, end, snippet); + } + + function loopReplacer(match, start, end, snippet) { + var string = ''; + + for (var i = parseInt(start); i < parseInt(end); i++) { + string += snippet.replace(/\[\s*i\s*\]/g, '[ ' + i + ' ]').replace(/UNROLLED_LOOP_INDEX/g, i); + } + + return string; + } // + + + function generatePrecision(parameters) { + var precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; + + if (parameters.precision === 'highp') { + precisionstring += '\n#define HIGH_PRECISION'; + } else if (parameters.precision === 'mediump') { + precisionstring += '\n#define MEDIUM_PRECISION'; + } else if (parameters.precision === 'lowp') { + precisionstring += '\n#define LOW_PRECISION'; + } + + return precisionstring; + } + + function generateShadowMapTypeDefine(parameters) { + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + + if (parameters.shadowMapType === PCFShadowMap) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + } else if (parameters.shadowMapType === PCFSoftShadowMap) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + } else if (parameters.shadowMapType === VSMShadowMap) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + } + + return shadowMapTypeDefine; + } + + function generateEnvMapTypeDefine(parameters) { + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + + if (parameters.envMap) { + switch (parameters.envMapMode) { + case CubeReflectionMapping: + case CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; + + case CubeUVReflectionMapping: + case CubeUVRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; + } + } + + return envMapTypeDefine; + } + + function generateEnvMapModeDefine(parameters) { + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + + if (parameters.envMap) { + switch (parameters.envMapMode) { + case CubeRefractionMapping: + case CubeUVRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; + } + } + + return envMapModeDefine; + } + + function generateEnvMapBlendingDefine(parameters) { + var envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; + + if (parameters.envMap) { + switch (parameters.combine) { + case MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; + + case MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; + + case AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; + } + } + + return envMapBlendingDefine; + } + + function WebGLProgram(renderer, cacheKey, parameters, bindingStates) { + var gl = renderer.getContext(); + var defines = parameters.defines; + var vertexShader = parameters.vertexShader; + var fragmentShader = parameters.fragmentShader; + var shadowMapTypeDefine = generateShadowMapTypeDefine(parameters); + var envMapTypeDefine = generateEnvMapTypeDefine(parameters); + var envMapModeDefine = generateEnvMapModeDefine(parameters); + var envMapBlendingDefine = generateEnvMapBlendingDefine(parameters); + var gammaFactorDefine = renderer.gammaFactor > 0 ? renderer.gammaFactor : 1.0; + var customExtensions = parameters.isWebGL2 ? '' : generateExtensions(parameters); + var customDefines = generateDefines(defines); + var program = gl.createProgram(); + var prefixVertex, prefixFragment; + var versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; + + if (parameters.isRawShaderMaterial) { + prefixVertex = [customDefines].filter(filterEmptyLine).join('\n'); + + if (prefixVertex.length > 0) { + prefixVertex += '\n'; + } + + prefixFragment = [customExtensions, customDefines].filter(filterEmptyLine).join('\n'); + + if (prefixFragment.length > 0) { + prefixFragment += '\n'; + } + } else { + prefixVertex = [generatePrecision(parameters), '#define SHADER_NAME ' + parameters.shaderName, customDefines, parameters.instancing ? '#define USE_INSTANCING' : '', parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, '#define MAX_BONES ' + parameters.maxBones, parameters.useFog && parameters.fog ? '#define USE_FOG' : '', parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.normalMap && parameters.objectSpaceNormalMap ? '#define OBJECTSPACE_NORMALMAP' : '', parameters.normalMap && parameters.tangentSpaceNormalMap ? '#define TANGENTSPACE_NORMALMAP' : '', parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', parameters.vertexTangents ? '#define USE_TANGENT' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.vertexUvs ? '#define USE_UV' : '', parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.skinning ? '#define USE_SKINNING' : '', parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'uniform mat3 normalMatrix;', 'uniform vec3 cameraPosition;', 'uniform bool isOrthographic;', '#ifdef USE_INSTANCING', ' attribute mat4 instanceMatrix;', '#endif', '#ifdef USE_INSTANCING_COLOR', ' attribute vec3 instanceColor;', '#endif', 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', '#ifdef USE_TANGENT', ' attribute vec4 tangent;', '#endif', '#ifdef USE_COLOR', ' attribute vec3 color;', '#endif', '#ifdef USE_MORPHTARGETS', ' attribute vec3 morphTarget0;', ' attribute vec3 morphTarget1;', ' attribute vec3 morphTarget2;', ' attribute vec3 morphTarget3;', ' #ifdef USE_MORPHNORMALS', ' attribute vec3 morphNormal0;', ' attribute vec3 morphNormal1;', ' attribute vec3 morphNormal2;', ' attribute vec3 morphNormal3;', ' #else', ' attribute vec3 morphTarget4;', ' attribute vec3 morphTarget5;', ' attribute vec3 morphTarget6;', ' attribute vec3 morphTarget7;', ' #endif', '#endif', '#ifdef USE_SKINNING', ' attribute vec4 skinIndex;', ' attribute vec4 skinWeight;', '#endif', '\n'].filter(filterEmptyLine).join('\n'); + prefixFragment = [customExtensions, generatePrecision(parameters), '#define SHADER_NAME ' + parameters.shaderName, customDefines, parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + (parameters.alphaTest % 1 ? '' : '.0') : '', // add '.0' if integer + '#define GAMMA_FACTOR ' + gammaFactorDefine, parameters.useFog && parameters.fog ? '#define USE_FOG' : '', parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.matcap ? '#define USE_MATCAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapTypeDefine : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.normalMap && parameters.objectSpaceNormalMap ? '#define OBJECTSPACE_NORMALMAP' : '', parameters.normalMap && parameters.tangentSpaceNormalMap ? '#define TANGENTSPACE_NORMALMAP' : '', parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.sheen ? '#define USE_SHEEN' : '', parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', parameters.vertexTangents ? '#define USE_TANGENT' : '', parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', parameters.vertexUvs ? '#define USE_UV' : '', parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', (parameters.extensionShaderTextureLOD || parameters.envMap) && parameters.rendererExtensionShaderTextureLod ? '#define TEXTURE_LOD_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', 'uniform bool isOrthographic;', parameters.toneMapping !== NoToneMapping ? '#define TONE_MAPPING' : '', parameters.toneMapping !== NoToneMapping ? ShaderChunk['tonemapping_pars_fragment'] : '', // this code is required here because it is used by the toneMapping() function defined below + parameters.toneMapping !== NoToneMapping ? getToneMappingFunction('toneMapping', parameters.toneMapping) : '', parameters.dithering ? '#define DITHERING' : '', ShaderChunk['encodings_pars_fragment'], // this code is required here because it is used by the various encoding/decoding function defined below + parameters.map ? getTexelDecodingFunction('mapTexelToLinear', parameters.mapEncoding) : '', parameters.matcap ? getTexelDecodingFunction('matcapTexelToLinear', parameters.matcapEncoding) : '', parameters.envMap ? getTexelDecodingFunction('envMapTexelToLinear', parameters.envMapEncoding) : '', parameters.emissiveMap ? getTexelDecodingFunction('emissiveMapTexelToLinear', parameters.emissiveMapEncoding) : '', parameters.lightMap ? getTexelDecodingFunction('lightMapTexelToLinear', parameters.lightMapEncoding) : '', getTexelEncodingFunction('linearToOutputTexel', parameters.outputEncoding), parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', '\n'].filter(filterEmptyLine).join('\n'); + } + + vertexShader = resolveIncludes(vertexShader); + vertexShader = replaceLightNums(vertexShader, parameters); + vertexShader = replaceClippingPlaneNums(vertexShader, parameters); + fragmentShader = resolveIncludes(fragmentShader); + fragmentShader = replaceLightNums(fragmentShader, parameters); + fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); + vertexShader = unrollLoops(vertexShader); + fragmentShader = unrollLoops(fragmentShader); + + if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { + // GLSL 3.0 conversion for built-in materials and ShaderMaterial + versionString = '#version 300 es\n'; + prefixVertex = ['#define attribute in', '#define varying out', '#define texture2D texture'].join('\n') + '\n' + prefixVertex; + prefixFragment = ['#define varying in', parameters.glslVersion === GLSL3 ? '' : 'out highp vec4 pc_fragColor;', parameters.glslVersion === GLSL3 ? '' : '#define gl_FragColor pc_fragColor', '#define gl_FragDepthEXT gl_FragDepth', '#define texture2D texture', '#define textureCube texture', '#define texture2DProj textureProj', '#define texture2DLodEXT textureLod', '#define texture2DProjLodEXT textureProjLod', '#define textureCubeLodEXT textureLod', '#define texture2DGradEXT textureGrad', '#define texture2DProjGradEXT textureProjGrad', '#define textureCubeGradEXT textureGrad'].join('\n') + '\n' + prefixFragment; + } + + var vertexGlsl = versionString + prefixVertex + vertexShader; + var fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); + + var glVertexShader = WebGLShader(gl, 35633, vertexGlsl); + var glFragmentShader = WebGLShader(gl, 35632, fragmentGlsl); + gl.attachShader(program, glVertexShader); + gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. + + if (parameters.index0AttributeName !== undefined) { + gl.bindAttribLocation(program, 0, parameters.index0AttributeName); + } else if (parameters.morphTargets === true) { + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation(program, 0, 'position'); + } + + gl.linkProgram(program); // check for link errors + + if (renderer.debug.checkShaderErrors) { + var programLog = gl.getProgramInfoLog(program).trim(); + var vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); + var fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); + var runnable = true; + var haveDiagnostics = true; + + if (gl.getProgramParameter(program, 35714) === false) { + runnable = false; + var vertexErrors = getShaderErrors(gl, glVertexShader, 'vertex'); + var fragmentErrors = getShaderErrors(gl, glFragmentShader, 'fragment'); + console.error('THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter(program, 35715), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors); + } else if (programLog !== '') { + console.warn('THREE.WebGLProgram: gl.getProgramInfoLog()', programLog); + } else if (vertexLog === '' || fragmentLog === '') { + haveDiagnostics = false; + } + + if (haveDiagnostics) { + this.diagnostics = { + runnable: runnable, + programLog: programLog, + vertexShader: { + log: vertexLog, + prefix: prefixVertex + }, + fragmentShader: { + log: fragmentLog, + prefix: prefixFragment + } + }; + } + } // Clean up + // Crashes in iOS9 and iOS10. #18402 + // gl.detachShader( program, glVertexShader ); + // gl.detachShader( program, glFragmentShader ); + + + gl.deleteShader(glVertexShader); + gl.deleteShader(glFragmentShader); // set up caching for uniform locations + + var cachedUniforms; + + this.getUniforms = function () { + if (cachedUniforms === undefined) { + cachedUniforms = new WebGLUniforms(gl, program); + } + + return cachedUniforms; + }; // set up caching for attribute locations + + + var cachedAttributes; + + this.getAttributes = function () { + if (cachedAttributes === undefined) { + cachedAttributes = fetchAttributeLocations(gl, program); + } + + return cachedAttributes; + }; // free resource + + + this.destroy = function () { + bindingStates.releaseStatesOfProgram(this); + gl.deleteProgram(program); + this.program = undefined; + }; // + + + this.name = parameters.shaderName; + this.id = programIdCount++; + this.cacheKey = cacheKey; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; + return this; + } + + function WebGLPrograms(renderer, cubemaps, extensions, capabilities, bindingStates, clipping) { + var programs = []; + var isWebGL2 = capabilities.isWebGL2; + var logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; + var floatVertexTextures = capabilities.floatVertexTextures; + var maxVertexUniforms = capabilities.maxVertexUniforms; + var vertexTextures = capabilities.vertexTextures; + var precision = capabilities.precision; + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshDistanceMaterial: 'distanceRGBA', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshToonMaterial: 'toon', + MeshStandardMaterial: 'physical', + MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points', + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' + }; + var parameterNames = ['precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap', 'roughnessMap', 'metalnessMap', 'gradientMap', 'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha', 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', 'sheen', 'transmissionMap']; + + function getMaxBones(object) { + var skeleton = object.skeleton; + var bones = skeleton.bones; + + if (floatVertexTextures) { + return 1024; + } else { + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + var nVertexUniforms = maxVertexUniforms; + var nVertexMatrices = Math.floor((nVertexUniforms - 20) / 4); + var maxBones = Math.min(nVertexMatrices, bones.length); + + if (maxBones < bones.length) { + console.warn('THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.'); + return 0; + } + + return maxBones; + } + } + + function getTextureEncodingFromMap(map) { + var encoding; + + if (map && map.isTexture) { + encoding = map.encoding; + } else if (map && map.isWebGLRenderTarget) { + console.warn('THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.'); + encoding = map.texture.encoding; + } else { + encoding = LinearEncoding; + } + + return encoding; + } + + function getParameters(material, lights, shadows, scene, object) { + var fog = scene.fog; + var environment = material.isMeshStandardMaterial ? scene.environment : null; + var envMap = cubemaps.get(material.envMap || environment); + var shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + var maxBones = object.isSkinnedMesh ? getMaxBones(object) : 0; + + if (material.precision !== null) { + precision = capabilities.getMaxPrecision(material.precision); + + if (precision !== material.precision) { + console.warn('THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.'); + } + } + + var vertexShader, fragmentShader; + + if (shaderID) { + var shader = ShaderLib[shaderID]; + vertexShader = shader.vertexShader; + fragmentShader = shader.fragmentShader; + } else { + vertexShader = material.vertexShader; + fragmentShader = material.fragmentShader; + } + + var currentRenderTarget = renderer.getRenderTarget(); + var parameters = { + isWebGL2: isWebGL2, + shaderID: shaderID, + shaderName: material.type, + vertexShader: vertexShader, + fragmentShader: fragmentShader, + defines: material.defines, + isRawShaderMaterial: material.isRawShaderMaterial === true, + glslVersion: material.glslVersion, + precision: precision, + instancing: object.isInstancedMesh === true, + instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, + supportsVertexTextures: vertexTextures, + outputEncoding: currentRenderTarget !== null ? getTextureEncodingFromMap(currentRenderTarget.texture) : renderer.outputEncoding, + map: !!material.map, + mapEncoding: getTextureEncodingFromMap(material.map), + matcap: !!material.matcap, + matcapEncoding: getTextureEncodingFromMap(material.matcap), + envMap: !!envMap, + envMapMode: envMap && envMap.mapping, + envMapEncoding: getTextureEncodingFromMap(envMap), + envMapCubeUV: !!envMap && (envMap.mapping === CubeUVReflectionMapping || envMap.mapping === CubeUVRefractionMapping), + lightMap: !!material.lightMap, + lightMapEncoding: getTextureEncodingFromMap(material.lightMap), + aoMap: !!material.aoMap, + emissiveMap: !!material.emissiveMap, + emissiveMapEncoding: getTextureEncodingFromMap(material.emissiveMap), + bumpMap: !!material.bumpMap, + normalMap: !!material.normalMap, + objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, + tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, + clearcoatMap: !!material.clearcoatMap, + clearcoatRoughnessMap: !!material.clearcoatRoughnessMap, + clearcoatNormalMap: !!material.clearcoatNormalMap, + displacementMap: !!material.displacementMap, + roughnessMap: !!material.roughnessMap, + metalnessMap: !!material.metalnessMap, + specularMap: !!material.specularMap, + alphaMap: !!material.alphaMap, + gradientMap: !!material.gradientMap, + sheen: !!material.sheen, + transmissionMap: !!material.transmissionMap, + combine: material.combine, + vertexTangents: material.normalMap && material.vertexTangents, + vertexColors: material.vertexColors, + vertexUvs: !!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatMap || !!material.clearcoatRoughnessMap || !!material.clearcoatNormalMap || !!material.displacementMap || !!material.transmissionMap, + uvsVertexOnly: !(!!material.map || !!material.bumpMap || !!material.normalMap || !!material.specularMap || !!material.alphaMap || !!material.emissiveMap || !!material.roughnessMap || !!material.metalnessMap || !!material.clearcoatNormalMap || !!material.transmissionMap) && !!material.displacementMap, + fog: !!fog, + useFog: material.fog, + fogExp2: fog && fog.isFogExp2, + flatShading: !!material.flatShading, + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: logarithmicDepthBuffer, + skinning: material.skinning && maxBones > 0, + maxBones: maxBones, + useVertexTexture: floatVertexTextures, + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numRectAreaLights: lights.rectArea.length, + numHemiLights: lights.hemi.length, + numDirLightShadows: lights.directionalShadowMap.length, + numPointLightShadows: lights.pointShadowMap.length, + numSpotLightShadows: lights.spotShadowMap.length, + numClippingPlanes: clipping.numPlanes, + numClipIntersection: clipping.numIntersection, + dithering: material.dithering, + shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, + shadowMapType: renderer.shadowMap.type, + toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, + physicallyCorrectLights: renderer.physicallyCorrectLights, + premultipliedAlpha: material.premultipliedAlpha, + alphaTest: material.alphaTest, + doubleSided: material.side === DoubleSide, + flipSided: material.side === BackSide, + depthPacking: material.depthPacking !== undefined ? material.depthPacking : false, + index0AttributeName: material.index0AttributeName, + extensionDerivatives: material.extensions && material.extensions.derivatives, + extensionFragDepth: material.extensions && material.extensions.fragDepth, + extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, + extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, + rendererExtensionFragDepth: isWebGL2 || extensions.has('EXT_frag_depth'), + rendererExtensionDrawBuffers: isWebGL2 || extensions.has('WEBGL_draw_buffers'), + rendererExtensionShaderTextureLod: isWebGL2 || extensions.has('EXT_shader_texture_lod'), + customProgramCacheKey: material.customProgramCacheKey() + }; + return parameters; + } + + function getProgramCacheKey(parameters) { + var array = []; + + if (parameters.shaderID) { + array.push(parameters.shaderID); + } else { + array.push(parameters.fragmentShader); + array.push(parameters.vertexShader); + } + + if (parameters.defines !== undefined) { + for (var name in parameters.defines) { + array.push(name); + array.push(parameters.defines[name]); + } + } + + if (parameters.isRawShaderMaterial === false) { + for (var i = 0; i < parameterNames.length; i++) { + array.push(parameters[parameterNames[i]]); + } + + array.push(renderer.outputEncoding); + array.push(renderer.gammaFactor); + } + + array.push(parameters.customProgramCacheKey); + return array.join(); + } + + function getUniforms(material) { + var shaderID = shaderIDs[material.type]; + var uniforms; + + if (shaderID) { + var shader = ShaderLib[shaderID]; + uniforms = UniformsUtils.clone(shader.uniforms); + } else { + uniforms = material.uniforms; + } + + return uniforms; + } + + function acquireProgram(parameters, cacheKey) { + var program; // Check if code has been already compiled + + for (var p = 0, pl = programs.length; p < pl; p++) { + var preexistingProgram = programs[p]; + + if (preexistingProgram.cacheKey === cacheKey) { + program = preexistingProgram; + ++program.usedTimes; + break; + } + } + + if (program === undefined) { + program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); + programs.push(program); + } + + return program; + } + + function releaseProgram(program) { + if (--program.usedTimes === 0) { + // Remove from unordered set + var i = programs.indexOf(program); + programs[i] = programs[programs.length - 1]; + programs.pop(); // Free WebGL resources + + program.destroy(); + } + } + + return { + getParameters: getParameters, + getProgramCacheKey: getProgramCacheKey, + getUniforms: getUniforms, + acquireProgram: acquireProgram, + releaseProgram: releaseProgram, + // Exposed for resource monitoring & error feedback via renderer.info: + programs: programs + }; + } + + function WebGLProperties() { + var properties = new WeakMap(); + + function get(object) { + var map = properties.get(object); + + if (map === undefined) { + map = {}; + properties.set(object, map); + } + + return map; + } + + function remove(object) { + properties.delete(object); + } + + function update(object, key, value) { + properties.get(object)[key] = value; + } + + function dispose() { + properties = new WeakMap(); + } + + return { + get: get, + remove: remove, + update: update, + dispose: dispose + }; + } + + function painterSortStable(a, b) { + if (a.groupOrder !== b.groupOrder) { + return a.groupOrder - b.groupOrder; + } else if (a.renderOrder !== b.renderOrder) { + return a.renderOrder - b.renderOrder; + } else if (a.program !== b.program) { + return a.program.id - b.program.id; + } else if (a.material.id !== b.material.id) { + return a.material.id - b.material.id; + } else if (a.z !== b.z) { + return a.z - b.z; + } else { + return a.id - b.id; + } + } + + function reversePainterSortStable(a, b) { + if (a.groupOrder !== b.groupOrder) { + return a.groupOrder - b.groupOrder; + } else if (a.renderOrder !== b.renderOrder) { + return a.renderOrder - b.renderOrder; + } else if (a.z !== b.z) { + return b.z - a.z; + } else { + return a.id - b.id; + } + } + + function WebGLRenderList(properties) { + var renderItems = []; + var renderItemsIndex = 0; + var opaque = []; + var transparent = []; + var defaultProgram = { + id: -1 + }; + + function init() { + renderItemsIndex = 0; + opaque.length = 0; + transparent.length = 0; + } + + function getNextRenderItem(object, geometry, material, groupOrder, z, group) { + var renderItem = renderItems[renderItemsIndex]; + var materialProperties = properties.get(material); + + if (renderItem === undefined) { + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + program: materialProperties.program || defaultProgram, + groupOrder: groupOrder, + renderOrder: object.renderOrder, + z: z, + group: group + }; + renderItems[renderItemsIndex] = renderItem; + } else { + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.program = materialProperties.program || defaultProgram; + renderItem.groupOrder = groupOrder; + renderItem.renderOrder = object.renderOrder; + renderItem.z = z; + renderItem.group = group; + } + + renderItemsIndex++; + return renderItem; + } + + function push(object, geometry, material, groupOrder, z, group) { + var renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); + (material.transparent === true ? transparent : opaque).push(renderItem); + } + + function unshift(object, geometry, material, groupOrder, z, group) { + var renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); + (material.transparent === true ? transparent : opaque).unshift(renderItem); + } + + function sort(customOpaqueSort, customTransparentSort) { + if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); + if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); + } + + function finish() { + // Clear references from inactive renderItems in the list + for (var i = renderItemsIndex, il = renderItems.length; i < il; i++) { + var renderItem = renderItems[i]; + if (renderItem.id === null) break; + renderItem.id = null; + renderItem.object = null; + renderItem.geometry = null; + renderItem.material = null; + renderItem.program = null; + renderItem.group = null; + } + } + + return { + opaque: opaque, + transparent: transparent, + init: init, + push: push, + unshift: unshift, + finish: finish, + sort: sort + }; + } + + function WebGLRenderLists(properties) { + var lists = new WeakMap(); + + function get(scene, renderCallDepth) { + var list; + + if (lists.has(scene) === false) { + list = new WebGLRenderList(properties); + lists.set(scene, [list]); + } else { + if (renderCallDepth >= lists.get(scene).length) { + list = new WebGLRenderList(properties); + lists.get(scene).push(list); + } else { + list = lists.get(scene)[renderCallDepth]; + } + } + + return list; + } + + function dispose() { + lists = new WeakMap(); + } + + return { + get: get, + dispose: dispose + }; + } + + function UniformsCache() { + var lights = {}; + return { + get: function get(light) { + if (lights[light.id] !== undefined) { + return lights[light.id]; + } + + var uniforms; + + switch (light.type) { + case 'DirectionalLight': + uniforms = { + direction: new Vector3(), + color: new Color() + }; + break; + + case 'SpotLight': + uniforms = { + position: new Vector3(), + direction: new Vector3(), + color: new Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0 + }; + break; + + case 'PointLight': + uniforms = { + position: new Vector3(), + color: new Color(), + distance: 0, + decay: 0 + }; + break; + + case 'HemisphereLight': + uniforms = { + direction: new Vector3(), + skyColor: new Color(), + groundColor: new Color() + }; + break; + + case 'RectAreaLight': + uniforms = { + color: new Color(), + position: new Vector3(), + halfWidth: new Vector3(), + halfHeight: new Vector3() + }; + break; + } + + lights[light.id] = uniforms; + return uniforms; + } + }; + } + + function ShadowUniformsCache() { + var lights = {}; + return { + get: function get(light) { + if (lights[light.id] !== undefined) { + return lights[light.id]; + } + + var uniforms; + + switch (light.type) { + case 'DirectionalLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; + + case 'SpotLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; + + case 'PointLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2(), + shadowCameraNear: 1, + shadowCameraFar: 1000 + }; + break; + // TODO (abelnation): set RectAreaLight shadow uniforms + } + + lights[light.id] = uniforms; + return uniforms; + } + }; + } + + var nextVersion = 0; + + function shadowCastingLightsFirst(lightA, lightB) { + return (lightB.castShadow ? 1 : 0) - (lightA.castShadow ? 1 : 0); + } + + function WebGLLights(extensions, capabilities) { + var cache = new UniformsCache(); + var shadowCache = ShadowUniformsCache(); + var state = { + version: 0, + hash: { + directionalLength: -1, + pointLength: -1, + spotLength: -1, + rectAreaLength: -1, + hemiLength: -1, + numDirectionalShadows: -1, + numPointShadows: -1, + numSpotShadows: -1 + }, + ambient: [0, 0, 0], + probe: [], + directional: [], + directionalShadow: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadow: [], + spotShadowMap: [], + spotShadowMatrix: [], + rectArea: [], + rectAreaLTC1: null, + rectAreaLTC2: null, + point: [], + pointShadow: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [] + }; + + for (var i = 0; i < 9; i++) { + state.probe.push(new Vector3()); + } + + var vector3 = new Vector3(); + var matrix4 = new Matrix4(); + var matrix42 = new Matrix4(); + + function setup(lights) { + var r = 0, + g = 0, + b = 0; + + for (var _i = 0; _i < 9; _i++) { + state.probe[_i].set(0, 0, 0); + } + + var directionalLength = 0; + var pointLength = 0; + var spotLength = 0; + var rectAreaLength = 0; + var hemiLength = 0; + var numDirectionalShadows = 0; + var numPointShadows = 0; + var numSpotShadows = 0; + lights.sort(shadowCastingLightsFirst); + + for (var _i2 = 0, l = lights.length; _i2 < l; _i2++) { + var light = lights[_i2]; + var color = light.color; + var intensity = light.intensity; + var distance = light.distance; + var shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; + + if (light.isAmbientLight) { + r += color.r * intensity; + g += color.g * intensity; + b += color.b * intensity; + } else if (light.isLightProbe) { + for (var j = 0; j < 9; j++) { + state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); + } + } else if (light.isDirectionalLight) { + var uniforms = cache.get(light); + uniforms.color.copy(light.color).multiplyScalar(light.intensity); + + if (light.castShadow) { + var shadow = light.shadow; + var shadowUniforms = shadowCache.get(light); + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; + state.directionalShadow[directionalLength] = shadowUniforms; + state.directionalShadowMap[directionalLength] = shadowMap; + state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; + numDirectionalShadows++; + } + + state.directional[directionalLength] = uniforms; + directionalLength++; + } else if (light.isSpotLight) { + var _uniforms = cache.get(light); + + _uniforms.position.setFromMatrixPosition(light.matrixWorld); + + _uniforms.color.copy(color).multiplyScalar(intensity); + + _uniforms.distance = distance; + _uniforms.coneCos = Math.cos(light.angle); + _uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); + _uniforms.decay = light.decay; + + if (light.castShadow) { + var _shadow = light.shadow; + + var _shadowUniforms = shadowCache.get(light); + + _shadowUniforms.shadowBias = _shadow.bias; + _shadowUniforms.shadowNormalBias = _shadow.normalBias; + _shadowUniforms.shadowRadius = _shadow.radius; + _shadowUniforms.shadowMapSize = _shadow.mapSize; + state.spotShadow[spotLength] = _shadowUniforms; + state.spotShadowMap[spotLength] = shadowMap; + state.spotShadowMatrix[spotLength] = light.shadow.matrix; + numSpotShadows++; + } + + state.spot[spotLength] = _uniforms; + spotLength++; + } else if (light.isRectAreaLight) { + var _uniforms2 = cache.get(light); // (a) intensity is the total visible light emitted + //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); + // (b) intensity is the brightness of the light + + + _uniforms2.color.copy(color).multiplyScalar(intensity); + + _uniforms2.halfWidth.set(light.width * 0.5, 0.0, 0.0); + + _uniforms2.halfHeight.set(0.0, light.height * 0.5, 0.0); + + state.rectArea[rectAreaLength] = _uniforms2; + rectAreaLength++; + } else if (light.isPointLight) { + var _uniforms3 = cache.get(light); + + _uniforms3.color.copy(light.color).multiplyScalar(light.intensity); + + _uniforms3.distance = light.distance; + _uniforms3.decay = light.decay; + + if (light.castShadow) { + var _shadow2 = light.shadow; + + var _shadowUniforms2 = shadowCache.get(light); + + _shadowUniforms2.shadowBias = _shadow2.bias; + _shadowUniforms2.shadowNormalBias = _shadow2.normalBias; + _shadowUniforms2.shadowRadius = _shadow2.radius; + _shadowUniforms2.shadowMapSize = _shadow2.mapSize; + _shadowUniforms2.shadowCameraNear = _shadow2.camera.near; + _shadowUniforms2.shadowCameraFar = _shadow2.camera.far; + state.pointShadow[pointLength] = _shadowUniforms2; + state.pointShadowMap[pointLength] = shadowMap; + state.pointShadowMatrix[pointLength] = light.shadow.matrix; + numPointShadows++; + } + + state.point[pointLength] = _uniforms3; + pointLength++; + } else if (light.isHemisphereLight) { + var _uniforms4 = cache.get(light); + + _uniforms4.skyColor.copy(light.color).multiplyScalar(intensity); + + _uniforms4.groundColor.copy(light.groundColor).multiplyScalar(intensity); + + state.hemi[hemiLength] = _uniforms4; + hemiLength++; + } + } + + if (rectAreaLength > 0) { + if (capabilities.isWebGL2) { + // WebGL 2 + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + } else { + // WebGL 1 + if (extensions.has('OES_texture_float_linear') === true) { + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + } else if (extensions.has('OES_texture_half_float_linear') === true) { + state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; + state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; + } else { + console.error('THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.'); + } + } + } + + state.ambient[0] = r; + state.ambient[1] = g; + state.ambient[2] = b; + var hash = state.hash; + + if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows) { + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; + state.directionalShadow.length = numDirectionalShadows; + state.directionalShadowMap.length = numDirectionalShadows; + state.pointShadow.length = numPointShadows; + state.pointShadowMap.length = numPointShadows; + state.spotShadow.length = numSpotShadows; + state.spotShadowMap.length = numSpotShadows; + state.directionalShadowMatrix.length = numDirectionalShadows; + state.pointShadowMatrix.length = numPointShadows; + state.spotShadowMatrix.length = numSpotShadows; + hash.directionalLength = directionalLength; + hash.pointLength = pointLength; + hash.spotLength = spotLength; + hash.rectAreaLength = rectAreaLength; + hash.hemiLength = hemiLength; + hash.numDirectionalShadows = numDirectionalShadows; + hash.numPointShadows = numPointShadows; + hash.numSpotShadows = numSpotShadows; + state.version = nextVersion++; + } + } + + function setupView(lights, camera) { + var directionalLength = 0; + var pointLength = 0; + var spotLength = 0; + var rectAreaLength = 0; + var hemiLength = 0; + var viewMatrix = camera.matrixWorldInverse; + + for (var _i3 = 0, l = lights.length; _i3 < l; _i3++) { + var light = lights[_i3]; + + if (light.isDirectionalLight) { + var uniforms = state.directional[directionalLength]; + uniforms.direction.setFromMatrixPosition(light.matrixWorld); + vector3.setFromMatrixPosition(light.target.matrixWorld); + uniforms.direction.sub(vector3); + uniforms.direction.transformDirection(viewMatrix); + directionalLength++; + } else if (light.isSpotLight) { + var _uniforms5 = state.spot[spotLength]; + + _uniforms5.position.setFromMatrixPosition(light.matrixWorld); + + _uniforms5.position.applyMatrix4(viewMatrix); + + _uniforms5.direction.setFromMatrixPosition(light.matrixWorld); + + vector3.setFromMatrixPosition(light.target.matrixWorld); + + _uniforms5.direction.sub(vector3); + + _uniforms5.direction.transformDirection(viewMatrix); + + spotLength++; + } else if (light.isRectAreaLight) { + var _uniforms6 = state.rectArea[rectAreaLength]; + + _uniforms6.position.setFromMatrixPosition(light.matrixWorld); + + _uniforms6.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors + + + matrix42.identity(); + matrix4.copy(light.matrixWorld); + matrix4.premultiply(viewMatrix); + matrix42.extractRotation(matrix4); + + _uniforms6.halfWidth.set(light.width * 0.5, 0.0, 0.0); + + _uniforms6.halfHeight.set(0.0, light.height * 0.5, 0.0); + + _uniforms6.halfWidth.applyMatrix4(matrix42); + + _uniforms6.halfHeight.applyMatrix4(matrix42); + + rectAreaLength++; + } else if (light.isPointLight) { + var _uniforms7 = state.point[pointLength]; + + _uniforms7.position.setFromMatrixPosition(light.matrixWorld); + + _uniforms7.position.applyMatrix4(viewMatrix); + + pointLength++; + } else if (light.isHemisphereLight) { + var _uniforms8 = state.hemi[hemiLength]; + + _uniforms8.direction.setFromMatrixPosition(light.matrixWorld); + + _uniforms8.direction.transformDirection(viewMatrix); + + _uniforms8.direction.normalize(); + + hemiLength++; + } + } + } + + return { + setup: setup, + setupView: setupView, + state: state + }; + } + + function WebGLRenderState(extensions, capabilities) { + var lights = new WebGLLights(extensions, capabilities); + var lightsArray = []; + var shadowsArray = []; + + function init() { + lightsArray.length = 0; + shadowsArray.length = 0; + } + + function pushLight(light) { + lightsArray.push(light); + } + + function pushShadow(shadowLight) { + shadowsArray.push(shadowLight); + } + + function setupLights() { + lights.setup(lightsArray); + } + + function setupLightsView(camera) { + lights.setupView(lightsArray, camera); + } + + var state = { + lightsArray: lightsArray, + shadowsArray: shadowsArray, + lights: lights + }; + return { + init: init, + state: state, + setupLights: setupLights, + setupLightsView: setupLightsView, + pushLight: pushLight, + pushShadow: pushShadow + }; + } + + function WebGLRenderStates(extensions, capabilities) { + var renderStates = new WeakMap(); + + function get(scene, renderCallDepth) { + if (renderCallDepth === void 0) { + renderCallDepth = 0; + } + + var renderState; + + if (renderStates.has(scene) === false) { + renderState = new WebGLRenderState(extensions, capabilities); + renderStates.set(scene, [renderState]); + } else { + if (renderCallDepth >= renderStates.get(scene).length) { + renderState = new WebGLRenderState(extensions, capabilities); + renderStates.get(scene).push(renderState); + } else { + renderState = renderStates.get(scene)[renderCallDepth]; + } + } + + return renderState; + } + + function dispose() { + renderStates = new WeakMap(); + } + + return { + get: get, + dispose: dispose + }; + } + + /** + * parameters = { + * + * opacity: , + * + * map: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + + var MeshDepthMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshDepthMaterial, _Material); + + function MeshDepthMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshDepthMaterial'; + _this.depthPacking = BasicDepthPacking; + _this.skinning = false; + _this.morphTargets = false; + _this.map = null; + _this.alphaMap = null; + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.fog = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshDepthMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.depthPacking = source.depthPacking; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.map = source.map; + this.alphaMap = source.alphaMap; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + return this; + }; + + return MeshDepthMaterial; + }(Material); + + MeshDepthMaterial.prototype.isMeshDepthMaterial = true; + + /** + * parameters = { + * + * referencePosition: , + * nearDistance: , + * farDistance: , + * + * skinning: , + * morphTargets: , + * + * map: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: + * + * } + */ + + var MeshDistanceMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshDistanceMaterial, _Material); + + function MeshDistanceMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshDistanceMaterial'; + _this.referencePosition = new Vector3(); + _this.nearDistance = 1; + _this.farDistance = 1000; + _this.skinning = false; + _this.morphTargets = false; + _this.map = null; + _this.alphaMap = null; + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.fog = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshDistanceMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.referencePosition.copy(source.referencePosition); + this.nearDistance = source.nearDistance; + this.farDistance = source.farDistance; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.map = source.map; + this.alphaMap = source.alphaMap; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + return this; + }; + + return MeshDistanceMaterial; + }(Material); + + MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; + + var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; + + var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; + + function WebGLShadowMap(_renderer, _objects, maxTextureSize) { + var _frustum = new Frustum(); + + var _shadowMapSize = new Vector2(), + _viewportSize = new Vector2(), + _viewport = new Vector4(), + _depthMaterials = [], + _distanceMaterials = [], + _materialCache = {}; + + var shadowSide = { + 0: BackSide, + 1: FrontSide, + 2: DoubleSide + }; + var shadowMaterialVertical = new ShaderMaterial({ + defines: { + SAMPLE_RATE: 2.0 / 8.0, + HALF_SAMPLE_RATE: 1.0 / 8.0 + }, + uniforms: { + shadow_pass: { + value: null + }, + resolution: { + value: new Vector2() + }, + radius: { + value: 4.0 + } + }, + vertexShader: vsm_vert, + fragmentShader: vsm_frag + }); + var shadowMaterialHorizontal = shadowMaterialVertical.clone(); + shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; + var fullScreenTri = new BufferGeometry(); + fullScreenTri.setAttribute('position', new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); + var fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); + var scope = this; + this.enabled = false; + this.autoUpdate = true; + this.needsUpdate = false; + this.type = PCFShadowMap; + + this.render = function (lights, scene, camera) { + if (scope.enabled === false) return; + if (scope.autoUpdate === false && scope.needsUpdate === false) return; + if (lights.length === 0) return; + + var currentRenderTarget = _renderer.getRenderTarget(); + + var activeCubeFace = _renderer.getActiveCubeFace(); + + var activeMipmapLevel = _renderer.getActiveMipmapLevel(); + + var _state = _renderer.state; // Set GL state for depth map. + + _state.setBlending(NoBlending); + + _state.buffers.color.setClear(1, 1, 1, 1); + + _state.buffers.depth.setTest(true); + + _state.setScissorTest(false); // render depth map + + + for (var i = 0, il = lights.length; i < il; i++) { + var light = lights[i]; + var shadow = light.shadow; + + if (shadow === undefined) { + console.warn('THREE.WebGLShadowMap:', light, 'has no shadow.'); + continue; + } + + if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; + + _shadowMapSize.copy(shadow.mapSize); + + var shadowFrameExtents = shadow.getFrameExtents(); + + _shadowMapSize.multiply(shadowFrameExtents); + + _viewportSize.copy(shadow.mapSize); + + if (_shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize) { + if (_shadowMapSize.x > maxTextureSize) { + _viewportSize.x = Math.floor(maxTextureSize / shadowFrameExtents.x); + _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; + shadow.mapSize.x = _viewportSize.x; + } + + if (_shadowMapSize.y > maxTextureSize) { + _viewportSize.y = Math.floor(maxTextureSize / shadowFrameExtents.y); + _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; + shadow.mapSize.y = _viewportSize.y; + } + } + + if (shadow.map === null && !shadow.isPointLightShadow && this.type === VSMShadowMap) { + var pars = { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat + }; + shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); + shadow.map.texture.name = light.name + '.shadowMap'; + shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); + shadow.camera.updateProjectionMatrix(); + } + + if (shadow.map === null) { + var _pars = { + minFilter: NearestFilter, + magFilter: NearestFilter, + format: RGBAFormat + }; + shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, _pars); + shadow.map.texture.name = light.name + '.shadowMap'; + shadow.camera.updateProjectionMatrix(); + } + + _renderer.setRenderTarget(shadow.map); + + _renderer.clear(); + + var viewportCount = shadow.getViewportCount(); + + for (var vp = 0; vp < viewportCount; vp++) { + var viewport = shadow.getViewport(vp); + + _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); + + _state.viewport(_viewport); + + shadow.updateMatrices(light, vp); + _frustum = shadow.getFrustum(); + renderObject(scene, camera, shadow.camera, light, this.type); + } // do blur pass for VSM + + + if (!shadow.isPointLightShadow && this.type === VSMShadowMap) { + VSMPass(shadow, camera); + } + + shadow.needsUpdate = false; + } + + scope.needsUpdate = false; + + _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); + }; + + function VSMPass(shadow, camera) { + var geometry = _objects.update(fullScreenMesh); // vertical pass + + + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + + _renderer.setRenderTarget(shadow.mapPass); + + _renderer.clear(); + + _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass + + + shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; + + _renderer.setRenderTarget(shadow.map); + + _renderer.clear(); + + _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); + } + + function getDepthMaterialVariant(useMorphing, useSkinning, useInstancing) { + var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + var material = _depthMaterials[index]; + + if (material === undefined) { + material = new MeshDepthMaterial({ + depthPacking: RGBADepthPacking, + morphTargets: useMorphing, + skinning: useSkinning + }); + _depthMaterials[index] = material; + } + + return material; + } + + function getDistanceMaterialVariant(useMorphing, useSkinning, useInstancing) { + var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + var material = _distanceMaterials[index]; + + if (material === undefined) { + material = new MeshDistanceMaterial({ + morphTargets: useMorphing, + skinning: useSkinning + }); + _distanceMaterials[index] = material; + } + + return material; + } + + function getDepthMaterial(object, geometry, material, light, shadowCameraNear, shadowCameraFar, type) { + var result = null; + var getMaterialVariant = getDepthMaterialVariant; + var customMaterial = object.customDepthMaterial; + + if (light.isPointLight === true) { + getMaterialVariant = getDistanceMaterialVariant; + customMaterial = object.customDistanceMaterial; + } + + if (customMaterial === undefined) { + var useMorphing = false; + + if (material.morphTargets === true) { + useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; + } + + var useSkinning = false; + + if (object.isSkinnedMesh === true) { + if (material.skinning === true) { + useSkinning = true; + } else { + console.warn('THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object); + } + } + + var useInstancing = object.isInstancedMesh === true; + result = getMaterialVariant(useMorphing, useSkinning, useInstancing); + } else { + result = customMaterial; + } + + if (_renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0) { + // in this case we need a unique material instance reflecting the + // appropriate state + var keyA = result.uuid, + keyB = material.uuid; + var materialsForVariant = _materialCache[keyA]; + + if (materialsForVariant === undefined) { + materialsForVariant = {}; + _materialCache[keyA] = materialsForVariant; + } + + var cachedMaterial = materialsForVariant[keyB]; + + if (cachedMaterial === undefined) { + cachedMaterial = result.clone(); + materialsForVariant[keyB] = cachedMaterial; + } + + result = cachedMaterial; + } + + result.visible = material.visible; + result.wireframe = material.wireframe; + + if (type === VSMShadowMap) { + result.side = material.shadowSide !== null ? material.shadowSide : material.side; + } else { + result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; + } + + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; + result.clipIntersection = material.clipIntersection; + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; + + if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { + result.referencePosition.setFromMatrixPosition(light.matrixWorld); + result.nearDistance = shadowCameraNear; + result.farDistance = shadowCameraFar; + } + + return result; + } + + function renderObject(object, camera, shadowCamera, light, type) { + if (object.visible === false) return; + var visible = object.layers.test(camera.layers); + + if (visible && (object.isMesh || object.isLine || object.isPoints)) { + if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { + object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); + + var geometry = _objects.update(object); + + var material = object.material; + + if (Array.isArray(material)) { + var groups = geometry.groups; + + for (var k = 0, kl = groups.length; k < kl; k++) { + var group = groups[k]; + var groupMaterial = material[group.materialIndex]; + + if (groupMaterial && groupMaterial.visible) { + var depthMaterial = getDepthMaterial(object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type); + + _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); + } + } + } else if (material.visible) { + var _depthMaterial = getDepthMaterial(object, geometry, material, light, shadowCamera.near, shadowCamera.far, type); + + _renderer.renderBufferDirect(shadowCamera, null, geometry, _depthMaterial, object, null); + } + } + } + + var children = object.children; + + for (var i = 0, l = children.length; i < l; i++) { + renderObject(children[i], camera, shadowCamera, light, type); + } + } + } + + function WebGLState(gl, extensions, capabilities) { + var _equationToGL, _factorToGL; + + var isWebGL2 = capabilities.isWebGL2; + + function ColorBuffer() { + var locked = false; + var color = new Vector4(); + var currentColorMask = null; + var currentColorClear = new Vector4(0, 0, 0, 0); + return { + setMask: function setMask(colorMask) { + if (currentColorMask !== colorMask && !locked) { + gl.colorMask(colorMask, colorMask, colorMask, colorMask); + currentColorMask = colorMask; + } + }, + setLocked: function setLocked(lock) { + locked = lock; + }, + setClear: function setClear(r, g, b, a, premultipliedAlpha) { + if (premultipliedAlpha === true) { + r *= a; + g *= a; + b *= a; + } + + color.set(r, g, b, a); + + if (currentColorClear.equals(color) === false) { + gl.clearColor(r, g, b, a); + currentColorClear.copy(color); + } + }, + reset: function reset() { + locked = false; + currentColorMask = null; + currentColorClear.set(-1, 0, 0, 0); // set to invalid state + } + }; + } + + function DepthBuffer() { + var locked = false; + var currentDepthMask = null; + var currentDepthFunc = null; + var currentDepthClear = null; + return { + setTest: function setTest(depthTest) { + if (depthTest) { + enable(2929); + } else { + disable(2929); + } + }, + setMask: function setMask(depthMask) { + if (currentDepthMask !== depthMask && !locked) { + gl.depthMask(depthMask); + currentDepthMask = depthMask; + } + }, + setFunc: function setFunc(depthFunc) { + if (currentDepthFunc !== depthFunc) { + if (depthFunc) { + switch (depthFunc) { + case NeverDepth: + gl.depthFunc(512); + break; + + case AlwaysDepth: + gl.depthFunc(519); + break; + + case LessDepth: + gl.depthFunc(513); + break; + + case LessEqualDepth: + gl.depthFunc(515); + break; + + case EqualDepth: + gl.depthFunc(514); + break; + + case GreaterEqualDepth: + gl.depthFunc(518); + break; + + case GreaterDepth: + gl.depthFunc(516); + break; + + case NotEqualDepth: + gl.depthFunc(517); + break; + + default: + gl.depthFunc(515); + } + } else { + gl.depthFunc(515); + } + + currentDepthFunc = depthFunc; + } + }, + setLocked: function setLocked(lock) { + locked = lock; + }, + setClear: function setClear(depth) { + if (currentDepthClear !== depth) { + gl.clearDepth(depth); + currentDepthClear = depth; + } + }, + reset: function reset() { + locked = false; + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; + } + }; + } + + function StencilBuffer() { + var locked = false; + var currentStencilMask = null; + var currentStencilFunc = null; + var currentStencilRef = null; + var currentStencilFuncMask = null; + var currentStencilFail = null; + var currentStencilZFail = null; + var currentStencilZPass = null; + var currentStencilClear = null; + return { + setTest: function setTest(stencilTest) { + if (!locked) { + if (stencilTest) { + enable(2960); + } else { + disable(2960); + } + } + }, + setMask: function setMask(stencilMask) { + if (currentStencilMask !== stencilMask && !locked) { + gl.stencilMask(stencilMask); + currentStencilMask = stencilMask; + } + }, + setFunc: function setFunc(stencilFunc, stencilRef, stencilMask) { + if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { + gl.stencilFunc(stencilFunc, stencilRef, stencilMask); + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; + } + }, + setOp: function setOp(stencilFail, stencilZFail, stencilZPass) { + if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { + gl.stencilOp(stencilFail, stencilZFail, stencilZPass); + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; + } + }, + setLocked: function setLocked(lock) { + locked = lock; + }, + setClear: function setClear(stencil) { + if (currentStencilClear !== stencil) { + gl.clearStencil(stencil); + currentStencilClear = stencil; + } + }, + reset: function reset() { + locked = false; + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; + } + }; + } // + + + var colorBuffer = new ColorBuffer(); + var depthBuffer = new DepthBuffer(); + var stencilBuffer = new StencilBuffer(); + var enabledCapabilities = {}; + var currentProgram = null; + var currentBlendingEnabled = false; + var currentBlending = null; + var currentBlendEquation = null; + var currentBlendSrc = null; + var currentBlendDst = null; + var currentBlendEquationAlpha = null; + var currentBlendSrcAlpha = null; + var currentBlendDstAlpha = null; + var currentPremultipledAlpha = false; + var currentFlipSided = null; + var currentCullFace = null; + var currentLineWidth = null; + var currentPolygonOffsetFactor = null; + var currentPolygonOffsetUnits = null; + var maxTextures = gl.getParameter(35661); + var lineWidthAvailable = false; + var version = 0; + var glVersion = gl.getParameter(7938); + + if (glVersion.indexOf('WebGL') !== -1) { + version = parseFloat(/^WebGL (\d)/.exec(glVersion)[1]); + lineWidthAvailable = version >= 1.0; + } else if (glVersion.indexOf('OpenGL ES') !== -1) { + version = parseFloat(/^OpenGL ES (\d)/.exec(glVersion)[1]); + lineWidthAvailable = version >= 2.0; + } + + var currentTextureSlot = null; + var currentBoundTextures = {}; + var currentScissor = new Vector4(); + var currentViewport = new Vector4(); + + function createTexture(type, target, count) { + var data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. + + var texture = gl.createTexture(); + gl.bindTexture(type, texture); + gl.texParameteri(type, 10241, 9728); + gl.texParameteri(type, 10240, 9728); + + for (var i = 0; i < count; i++) { + gl.texImage2D(target + i, 0, 6408, 1, 1, 0, 6408, 5121, data); + } + + return texture; + } + + var emptyTextures = {}; + emptyTextures[3553] = createTexture(3553, 3553, 1); + emptyTextures[34067] = createTexture(34067, 34069, 6); // init + + colorBuffer.setClear(0, 0, 0, 1); + depthBuffer.setClear(1); + stencilBuffer.setClear(0); + enable(2929); + depthBuffer.setFunc(LessEqualDepth); + setFlipSided(false); + setCullFace(CullFaceBack); + enable(2884); + setBlending(NoBlending); // + + function enable(id) { + if (enabledCapabilities[id] !== true) { + gl.enable(id); + enabledCapabilities[id] = true; + } + } + + function disable(id) { + if (enabledCapabilities[id] !== false) { + gl.disable(id); + enabledCapabilities[id] = false; + } + } + + function useProgram(program) { + if (currentProgram !== program) { + gl.useProgram(program); + currentProgram = program; + return true; + } + + return false; + } + + var equationToGL = (_equationToGL = {}, _equationToGL[AddEquation] = 32774, _equationToGL[SubtractEquation] = 32778, _equationToGL[ReverseSubtractEquation] = 32779, _equationToGL); + + if (isWebGL2) { + equationToGL[MinEquation] = 32775; + equationToGL[MaxEquation] = 32776; + } else { + var extension = extensions.get('EXT_blend_minmax'); + + if (extension !== null) { + equationToGL[MinEquation] = extension.MIN_EXT; + equationToGL[MaxEquation] = extension.MAX_EXT; + } + } + + var factorToGL = (_factorToGL = {}, _factorToGL[ZeroFactor] = 0, _factorToGL[OneFactor] = 1, _factorToGL[SrcColorFactor] = 768, _factorToGL[SrcAlphaFactor] = 770, _factorToGL[SrcAlphaSaturateFactor] = 776, _factorToGL[DstColorFactor] = 774, _factorToGL[DstAlphaFactor] = 772, _factorToGL[OneMinusSrcColorFactor] = 769, _factorToGL[OneMinusSrcAlphaFactor] = 771, _factorToGL[OneMinusDstColorFactor] = 775, _factorToGL[OneMinusDstAlphaFactor] = 773, _factorToGL); + + function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { + if (blending === NoBlending) { + if (currentBlendingEnabled === true) { + disable(3042); + currentBlendingEnabled = false; + } + + return; + } + + if (currentBlendingEnabled === false) { + enable(3042); + currentBlendingEnabled = true; + } + + if (blending !== CustomBlending) { + if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { + if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { + gl.blendEquation(32774); + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; + } + + if (premultipliedAlpha) { + switch (blending) { + case NormalBlending: + gl.blendFuncSeparate(1, 771, 1, 771); + break; + + case AdditiveBlending: + gl.blendFunc(1, 1); + break; + + case SubtractiveBlending: + gl.blendFuncSeparate(0, 0, 769, 771); + break; + + case MultiplyBlending: + gl.blendFuncSeparate(0, 768, 0, 770); + break; + + default: + console.error('THREE.WebGLState: Invalid blending: ', blending); + break; + } + } else { + switch (blending) { + case NormalBlending: + gl.blendFuncSeparate(770, 771, 1, 771); + break; + + case AdditiveBlending: + gl.blendFunc(770, 1); + break; + + case SubtractiveBlending: + gl.blendFunc(0, 769); + break; + + case MultiplyBlending: + gl.blendFunc(0, 768); + break; + + default: + console.error('THREE.WebGLState: Invalid blending: ', blending); + break; + } + } + + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; + } + + return; + } // custom blending + + + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; + + if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { + gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; + } + + if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { + gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; + } + + currentBlending = blending; + currentPremultipledAlpha = null; + } + + function setMaterial(material, frontFaceCW) { + material.side === DoubleSide ? disable(2884) : enable(2884); + var flipSided = material.side === BackSide; + if (frontFaceCW) flipSided = !flipSided; + setFlipSided(flipSided); + material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); + depthBuffer.setFunc(material.depthFunc); + depthBuffer.setTest(material.depthTest); + depthBuffer.setMask(material.depthWrite); + colorBuffer.setMask(material.colorWrite); + var stencilWrite = material.stencilWrite; + stencilBuffer.setTest(stencilWrite); + + if (stencilWrite) { + stencilBuffer.setMask(material.stencilWriteMask); + stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); + stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); + } + + setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); + } // + + + function setFlipSided(flipSided) { + if (currentFlipSided !== flipSided) { + if (flipSided) { + gl.frontFace(2304); + } else { + gl.frontFace(2305); + } + + currentFlipSided = flipSided; + } + } + + function setCullFace(cullFace) { + if (cullFace !== CullFaceNone) { + enable(2884); + + if (cullFace !== currentCullFace) { + if (cullFace === CullFaceBack) { + gl.cullFace(1029); + } else if (cullFace === CullFaceFront) { + gl.cullFace(1028); + } else { + gl.cullFace(1032); + } + } + } else { + disable(2884); + } + + currentCullFace = cullFace; + } + + function setLineWidth(width) { + if (width !== currentLineWidth) { + if (lineWidthAvailable) gl.lineWidth(width); + currentLineWidth = width; + } + } + + function setPolygonOffset(polygonOffset, factor, units) { + if (polygonOffset) { + enable(32823); + + if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { + gl.polygonOffset(factor, units); + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; + } + } else { + disable(32823); + } + } + + function setScissorTest(scissorTest) { + if (scissorTest) { + enable(3089); + } else { + disable(3089); + } + } // texture + + + function activeTexture(webglSlot) { + if (webglSlot === undefined) webglSlot = 33984 + maxTextures - 1; + + if (currentTextureSlot !== webglSlot) { + gl.activeTexture(webglSlot); + currentTextureSlot = webglSlot; + } + } + + function bindTexture(webglType, webglTexture) { + if (currentTextureSlot === null) { + activeTexture(); + } + + var boundTexture = currentBoundTextures[currentTextureSlot]; + + if (boundTexture === undefined) { + boundTexture = { + type: undefined, + texture: undefined + }; + currentBoundTextures[currentTextureSlot] = boundTexture; + } + + if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { + gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); + boundTexture.type = webglType; + boundTexture.texture = webglTexture; + } + } + + function unbindTexture() { + var boundTexture = currentBoundTextures[currentTextureSlot]; + + if (boundTexture !== undefined && boundTexture.type !== undefined) { + gl.bindTexture(boundTexture.type, null); + boundTexture.type = undefined; + boundTexture.texture = undefined; + } + } + + function compressedTexImage2D() { + try { + gl.compressedTexImage2D.apply(gl, arguments); + } catch (error) { + console.error('THREE.WebGLState:', error); + } + } + + function texImage2D() { + try { + gl.texImage2D.apply(gl, arguments); + } catch (error) { + console.error('THREE.WebGLState:', error); + } + } + + function texImage3D() { + try { + gl.texImage3D.apply(gl, arguments); + } catch (error) { + console.error('THREE.WebGLState:', error); + } + } // + + + function scissor(scissor) { + if (currentScissor.equals(scissor) === false) { + gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); + currentScissor.copy(scissor); + } + } + + function viewport(viewport) { + if (currentViewport.equals(viewport) === false) { + gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); + currentViewport.copy(viewport); + } + } // + + + function reset() { + // reset state + gl.disable(3042); + gl.disable(2884); + gl.disable(2929); + gl.disable(32823); + gl.disable(3089); + gl.disable(2960); + gl.blendEquation(32774); + gl.blendFunc(1, 0); + gl.blendFuncSeparate(1, 0, 1, 0); + gl.colorMask(true, true, true, true); + gl.clearColor(0, 0, 0, 0); + gl.depthMask(true); + gl.depthFunc(513); + gl.clearDepth(1); + gl.stencilMask(0xffffffff); + gl.stencilFunc(519, 0, 0xffffffff); + gl.stencilOp(7680, 7680, 7680); + gl.clearStencil(0); + gl.cullFace(1029); + gl.frontFace(2305); + gl.polygonOffset(0, 0); + gl.activeTexture(33984); + gl.useProgram(null); + gl.lineWidth(1); + gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals + + enabledCapabilities = {}; + currentTextureSlot = null; + currentBoundTextures = {}; + currentProgram = null; + currentBlendingEnabled = false; + currentBlending = null; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentPremultipledAlpha = false; + currentFlipSided = null; + currentCullFace = null; + currentLineWidth = null; + currentPolygonOffsetFactor = null; + currentPolygonOffsetUnits = null; + colorBuffer.reset(); + depthBuffer.reset(); + stencilBuffer.reset(); + } + + return { + buffers: { + color: colorBuffer, + depth: depthBuffer, + stencil: stencilBuffer + }, + enable: enable, + disable: disable, + useProgram: useProgram, + setBlending: setBlending, + setMaterial: setMaterial, + setFlipSided: setFlipSided, + setCullFace: setCullFace, + setLineWidth: setLineWidth, + setPolygonOffset: setPolygonOffset, + setScissorTest: setScissorTest, + activeTexture: activeTexture, + bindTexture: bindTexture, + unbindTexture: unbindTexture, + compressedTexImage2D: compressedTexImage2D, + texImage2D: texImage2D, + texImage3D: texImage3D, + scissor: scissor, + viewport: viewport, + reset: reset + }; + } + + function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { + var _wrappingToGL, _filterToGL; + + var isWebGL2 = capabilities.isWebGL2; + var maxTextures = capabilities.maxTextures; + var maxCubemapSize = capabilities.maxCubemapSize; + var maxTextureSize = capabilities.maxTextureSize; + var maxSamples = capabilities.maxSamples; + + var _videoTextures = new WeakMap(); + + var _canvas; // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, + // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! + // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). + + + var useOffscreenCanvas = false; + + try { + useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && new OffscreenCanvas(1, 1).getContext('2d') !== null; + } catch (err) {// Ignore any errors + } + + function createCanvas(width, height) { + // Use OffscreenCanvas when available. Specially needed in web workers + return useOffscreenCanvas ? new OffscreenCanvas(width, height) : document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); + } + + function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { + var scale = 1; // handle case if texture exceeds max size + + if (image.width > maxSize || image.height > maxSize) { + scale = maxSize / Math.max(image.width, image.height); + } // only perform resize if necessary + + + if (scale < 1 || needsPowerOfTwo === true) { + // only perform resize for certain image types + if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) { + var floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor; + var width = floor(scale * image.width); + var height = floor(scale * image.height); + if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can't reuse the same canvas + + var canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; + canvas.width = width; + canvas.height = height; + var context = canvas.getContext('2d'); + context.drawImage(image, 0, 0, width, height); + console.warn('THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').'); + return canvas; + } else { + if ('data' in image) { + console.warn('THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').'); + } + + return image; + } + } + + return image; + } + + function isPowerOfTwo(image) { + return MathUtils.isPowerOfTwo(image.width) && MathUtils.isPowerOfTwo(image.height); + } + + function textureNeedsPowerOfTwo(texture) { + if (isWebGL2) return false; + return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + } + + function textureNeedsGenerateMipmaps(texture, supportsMips) { + return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + } + + function generateMipmap(target, texture, width, height) { + _gl.generateMipmap(target); + + var textureProperties = properties.get(texture); + textureProperties.__maxMipLevel = Math.log2(Math.max(width, height)); + } + + function getInternalFormat(internalFormatName, glFormat, glType) { + if (isWebGL2 === false) return glFormat; + + if (internalFormatName !== null) { + if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; + console.warn('THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\''); + } + + var internalFormat = glFormat; + + if (glFormat === 6403) { + if (glType === 5126) internalFormat = 33326; + if (glType === 5131) internalFormat = 33325; + if (glType === 5121) internalFormat = 33321; + } + + if (glFormat === 6407) { + if (glType === 5126) internalFormat = 34837; + if (glType === 5131) internalFormat = 34843; + if (glType === 5121) internalFormat = 32849; + } + + if (glFormat === 6408) { + if (glType === 5126) internalFormat = 34836; + if (glType === 5131) internalFormat = 34842; + if (glType === 5121) internalFormat = 32856; + } + + if (internalFormat === 33325 || internalFormat === 33326 || internalFormat === 34842 || internalFormat === 34836) { + extensions.get('EXT_color_buffer_float'); + } + + return internalFormat; + } // Fallback filters for non-power-of-2 textures + + + function filterFallback(f) { + if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { + return 9728; + } + + return 9729; + } // + + + function onTextureDispose(event) { + var texture = event.target; + texture.removeEventListener('dispose', onTextureDispose); + deallocateTexture(texture); + + if (texture.isVideoTexture) { + _videoTextures.delete(texture); + } + + info.memory.textures--; + } + + function onRenderTargetDispose(event) { + var renderTarget = event.target; + renderTarget.removeEventListener('dispose', onRenderTargetDispose); + deallocateRenderTarget(renderTarget); + info.memory.textures--; + } // + + + function deallocateTexture(texture) { + var textureProperties = properties.get(texture); + if (textureProperties.__webglInit === undefined) return; + + _gl.deleteTexture(textureProperties.__webglTexture); + + properties.remove(texture); + } + + function deallocateRenderTarget(renderTarget) { + var texture = renderTarget.texture; + var renderTargetProperties = properties.get(renderTarget); + var textureProperties = properties.get(texture); + if (!renderTarget) return; + + if (textureProperties.__webglTexture !== undefined) { + _gl.deleteTexture(textureProperties.__webglTexture); + } + + if (renderTarget.depthTexture) { + renderTarget.depthTexture.dispose(); + } + + if (renderTarget.isWebGLCubeRenderTarget) { + for (var i = 0; i < 6; i++) { + _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); + + if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); + } + } else { + _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); + + if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); + if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); + if (renderTargetProperties.__webglColorRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer); + if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); + } + + properties.remove(texture); + properties.remove(renderTarget); + } // + + + var textureUnits = 0; + + function resetTextureUnits() { + textureUnits = 0; + } + + function allocateTextureUnit() { + var textureUnit = textureUnits; + + if (textureUnit >= maxTextures) { + console.warn('THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures); + } + + textureUnits += 1; + return textureUnit; + } // + + + function setTexture2D(texture, slot) { + var textureProperties = properties.get(texture); + if (texture.isVideoTexture) updateVideoTexture(texture); + + if (texture.version > 0 && textureProperties.__version !== texture.version) { + var image = texture.image; + + if (image === undefined) { + console.warn('THREE.WebGLRenderer: Texture marked for update but image is undefined'); + } else if (image.complete === false) { + console.warn('THREE.WebGLRenderer: Texture marked for update but image is incomplete'); + } else { + uploadTexture(textureProperties, texture, slot); + return; + } + } + + state.activeTexture(33984 + slot); + state.bindTexture(3553, textureProperties.__webglTexture); + } + + function setTexture2DArray(texture, slot) { + var textureProperties = properties.get(texture); + + if (texture.version > 0 && textureProperties.__version !== texture.version) { + uploadTexture(textureProperties, texture, slot); + return; + } + + state.activeTexture(33984 + slot); + state.bindTexture(35866, textureProperties.__webglTexture); + } + + function setTexture3D(texture, slot) { + var textureProperties = properties.get(texture); + + if (texture.version > 0 && textureProperties.__version !== texture.version) { + uploadTexture(textureProperties, texture, slot); + return; + } + + state.activeTexture(33984 + slot); + state.bindTexture(32879, textureProperties.__webglTexture); + } + + function setTextureCube(texture, slot) { + var textureProperties = properties.get(texture); + + if (texture.version > 0 && textureProperties.__version !== texture.version) { + uploadCubeTexture(textureProperties, texture, slot); + return; + } + + state.activeTexture(33984 + slot); + state.bindTexture(34067, textureProperties.__webglTexture); + } + + var wrappingToGL = (_wrappingToGL = {}, _wrappingToGL[RepeatWrapping] = 10497, _wrappingToGL[ClampToEdgeWrapping] = 33071, _wrappingToGL[MirroredRepeatWrapping] = 33648, _wrappingToGL); + var filterToGL = (_filterToGL = {}, _filterToGL[NearestFilter] = 9728, _filterToGL[NearestMipmapNearestFilter] = 9984, _filterToGL[NearestMipmapLinearFilter] = 9986, _filterToGL[LinearFilter] = 9729, _filterToGL[LinearMipmapNearestFilter] = 9985, _filterToGL[LinearMipmapLinearFilter] = 9987, _filterToGL); + + function setTextureParameters(textureType, texture, supportsMips) { + if (supportsMips) { + _gl.texParameteri(textureType, 10242, wrappingToGL[texture.wrapS]); + + _gl.texParameteri(textureType, 10243, wrappingToGL[texture.wrapT]); + + if (textureType === 32879 || textureType === 35866) { + _gl.texParameteri(textureType, 32882, wrappingToGL[texture.wrapR]); + } + + _gl.texParameteri(textureType, 10240, filterToGL[texture.magFilter]); + + _gl.texParameteri(textureType, 10241, filterToGL[texture.minFilter]); + } else { + _gl.texParameteri(textureType, 10242, 33071); + + _gl.texParameteri(textureType, 10243, 33071); + + if (textureType === 32879 || textureType === 35866) { + _gl.texParameteri(textureType, 32882, 33071); + } + + if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { + console.warn('THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.'); + } + + _gl.texParameteri(textureType, 10240, filterFallback(texture.magFilter)); + + _gl.texParameteri(textureType, 10241, filterFallback(texture.minFilter)); + + if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { + console.warn('THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.'); + } + } + + if (extensions.has('EXT_texture_filter_anisotropic') === true) { + var extension = extensions.get('EXT_texture_filter_anisotropic'); + if (texture.type === FloatType && extensions.has('OES_texture_float_linear') === false) return; // verify extension for WebGL 1 and WebGL 2 + + if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has('OES_texture_half_float_linear') === false) return; // verify extension for WebGL 1 only + + if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { + _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); + + properties.get(texture).__currentAnisotropy = texture.anisotropy; + } + } + } + + function initTexture(textureProperties, texture) { + if (textureProperties.__webglInit === undefined) { + textureProperties.__webglInit = true; + texture.addEventListener('dispose', onTextureDispose); + textureProperties.__webglTexture = _gl.createTexture(); + info.memory.textures++; + } + } + + function uploadTexture(textureProperties, texture, slot) { + var textureType = 3553; + if (texture.isDataTexture2DArray) textureType = 35866; + if (texture.isDataTexture3D) textureType = 32879; + initTexture(textureProperties, texture); + state.activeTexture(33984 + slot); + state.bindTexture(textureType, textureProperties.__webglTexture); + + _gl.pixelStorei(37440, texture.flipY); + + _gl.pixelStorei(37441, texture.premultiplyAlpha); + + _gl.pixelStorei(3317, texture.unpackAlignment); + + _gl.pixelStorei(37443, 0); + + var needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo(texture.image) === false; + var image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); + var supportsMips = isPowerOfTwo(image) || isWebGL2, + glFormat = utils.convert(texture.format); + var glType = utils.convert(texture.type), + glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType); + setTextureParameters(textureType, texture, supportsMips); + var mipmap; + var mipmaps = texture.mipmaps; + + if (texture.isDepthTexture) { + // populate depth texture with dummy data + glInternalFormat = 6402; + + if (isWebGL2) { + if (texture.type === FloatType) { + glInternalFormat = 36012; + } else if (texture.type === UnsignedIntType) { + glInternalFormat = 33190; + } else if (texture.type === UnsignedInt248Type) { + glInternalFormat = 35056; + } else { + glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D + } + } else { + if (texture.type === FloatType) { + console.error('WebGLRenderer: Floating point depth texture requires WebGL2.'); + } + } // validation checks for WebGL 1 + + + if (texture.format === DepthFormat && glInternalFormat === 6402) { + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { + console.warn('THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.'); + texture.type = UnsignedShortType; + glType = utils.convert(texture.type); + } + } + + if (texture.format === DepthStencilFormat && glInternalFormat === 6402) { + // Depth stencil textures need the DEPTH_STENCIL internal format + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + glInternalFormat = 34041; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + + if (texture.type !== UnsignedInt248Type) { + console.warn('THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.'); + texture.type = UnsignedInt248Type; + glType = utils.convert(texture.type); + } + } // + + + state.texImage2D(3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); + } else if (texture.isDataTexture) { + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + if (mipmaps.length > 0 && supportsMips) { + for (var i = 0, il = mipmaps.length; i < il; i++) { + mipmap = mipmaps[i]; + state.texImage2D(3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); + } + + texture.generateMipmaps = false; + textureProperties.__maxMipLevel = mipmaps.length - 1; + } else { + state.texImage2D(3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); + textureProperties.__maxMipLevel = 0; + } + } else if (texture.isCompressedTexture) { + for (var _i = 0, _il = mipmaps.length; _i < _il; _i++) { + mipmap = mipmaps[_i]; + + if (texture.format !== RGBAFormat && texture.format !== RGBFormat) { + if (glFormat !== null) { + state.compressedTexImage2D(3553, _i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); + } else { + console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()'); + } + } else { + state.texImage2D(3553, _i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); + } + } + + textureProperties.__maxMipLevel = mipmaps.length - 1; + } else if (texture.isDataTexture2DArray) { + state.texImage3D(35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); + textureProperties.__maxMipLevel = 0; + } else if (texture.isDataTexture3D) { + state.texImage3D(32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); + textureProperties.__maxMipLevel = 0; + } else { + // regular Texture (image, video, canvas) + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + if (mipmaps.length > 0 && supportsMips) { + for (var _i2 = 0, _il2 = mipmaps.length; _i2 < _il2; _i2++) { + mipmap = mipmaps[_i2]; + state.texImage2D(3553, _i2, glInternalFormat, glFormat, glType, mipmap); + } + + texture.generateMipmaps = false; + textureProperties.__maxMipLevel = mipmaps.length - 1; + } else { + state.texImage2D(3553, 0, glInternalFormat, glFormat, glType, image); + textureProperties.__maxMipLevel = 0; + } + } + + if (textureNeedsGenerateMipmaps(texture, supportsMips)) { + generateMipmap(textureType, texture, image.width, image.height); + } + + textureProperties.__version = texture.version; + if (texture.onUpdate) texture.onUpdate(texture); + } + + function uploadCubeTexture(textureProperties, texture, slot) { + if (texture.image.length !== 6) return; + initTexture(textureProperties, texture); + state.activeTexture(33984 + slot); + state.bindTexture(34067, textureProperties.__webglTexture); + + _gl.pixelStorei(37440, texture.flipY); + + _gl.pixelStorei(37441, texture.premultiplyAlpha); + + _gl.pixelStorei(3317, texture.unpackAlignment); + + _gl.pixelStorei(37443, 0); + + var isCompressed = texture && (texture.isCompressedTexture || texture.image[0].isCompressedTexture); + var isDataTexture = texture.image[0] && texture.image[0].isDataTexture; + var cubeImage = []; + + for (var i = 0; i < 6; i++) { + if (!isCompressed && !isDataTexture) { + cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); + } else { + cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; + } + } + + var image = cubeImage[0], + supportsMips = isPowerOfTwo(image) || isWebGL2, + glFormat = utils.convert(texture.format), + glType = utils.convert(texture.type), + glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType); + setTextureParameters(34067, texture, supportsMips); + var mipmaps; + + if (isCompressed) { + for (var _i3 = 0; _i3 < 6; _i3++) { + mipmaps = cubeImage[_i3].mipmaps; + + for (var j = 0; j < mipmaps.length; j++) { + var mipmap = mipmaps[j]; + + if (texture.format !== RGBAFormat && texture.format !== RGBFormat) { + if (glFormat !== null) { + state.compressedTexImage2D(34069 + _i3, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); + } else { + console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()'); + } + } else { + state.texImage2D(34069 + _i3, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); + } + } + } + + textureProperties.__maxMipLevel = mipmaps.length - 1; + } else { + mipmaps = texture.mipmaps; + + for (var _i4 = 0; _i4 < 6; _i4++) { + if (isDataTexture) { + state.texImage2D(34069 + _i4, 0, glInternalFormat, cubeImage[_i4].width, cubeImage[_i4].height, 0, glFormat, glType, cubeImage[_i4].data); + + for (var _j = 0; _j < mipmaps.length; _j++) { + var _mipmap = mipmaps[_j]; + var mipmapImage = _mipmap.image[_i4].image; + state.texImage2D(34069 + _i4, _j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); + } + } else { + state.texImage2D(34069 + _i4, 0, glInternalFormat, glFormat, glType, cubeImage[_i4]); + + for (var _j2 = 0; _j2 < mipmaps.length; _j2++) { + var _mipmap2 = mipmaps[_j2]; + state.texImage2D(34069 + _i4, _j2 + 1, glInternalFormat, glFormat, glType, _mipmap2.image[_i4]); + } + } + } + + textureProperties.__maxMipLevel = mipmaps.length; + } + + if (textureNeedsGenerateMipmaps(texture, supportsMips)) { + // We assume images for cube map have the same size. + generateMipmap(34067, texture, image.width, image.height); + } + + textureProperties.__version = texture.version; + if (texture.onUpdate) texture.onUpdate(texture); + } // Render targets + // Setup storage for target texture and bind it to correct framebuffer + + + function setupFrameBufferTexture(framebuffer, renderTarget, attachment, textureTarget) { + var texture = renderTarget.texture; + var glFormat = utils.convert(texture.format); + var glType = utils.convert(texture.type); + var glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType); + + if (textureTarget === 32879 || textureTarget === 35866) { + state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); + } else { + state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); + } + + _gl.bindFramebuffer(36160, framebuffer); + + _gl.framebufferTexture2D(36160, attachment, textureTarget, properties.get(texture).__webglTexture, 0); + + _gl.bindFramebuffer(36160, null); + } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + + + function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { + _gl.bindRenderbuffer(36161, renderbuffer); + + if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { + var glInternalFormat = 33189; + + if (isMultisample) { + var depthTexture = renderTarget.depthTexture; + + if (depthTexture && depthTexture.isDepthTexture) { + if (depthTexture.type === FloatType) { + glInternalFormat = 36012; + } else if (depthTexture.type === UnsignedIntType) { + glInternalFormat = 33190; + } + } + + var samples = getRenderTargetSamples(renderTarget); + + _gl.renderbufferStorageMultisample(36161, samples, glInternalFormat, renderTarget.width, renderTarget.height); + } else { + _gl.renderbufferStorage(36161, glInternalFormat, renderTarget.width, renderTarget.height); + } + + _gl.framebufferRenderbuffer(36160, 36096, 36161, renderbuffer); + } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { + if (isMultisample) { + var _samples = getRenderTargetSamples(renderTarget); + + _gl.renderbufferStorageMultisample(36161, _samples, 35056, renderTarget.width, renderTarget.height); + } else { + _gl.renderbufferStorage(36161, 34041, renderTarget.width, renderTarget.height); + } + + _gl.framebufferRenderbuffer(36160, 33306, 36161, renderbuffer); + } else { + var texture = renderTarget.texture; + var glFormat = utils.convert(texture.format); + var glType = utils.convert(texture.type); + + var _glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType); + + if (isMultisample) { + var _samples2 = getRenderTargetSamples(renderTarget); + + _gl.renderbufferStorageMultisample(36161, _samples2, _glInternalFormat, renderTarget.width, renderTarget.height); + } else { + _gl.renderbufferStorage(36161, _glInternalFormat, renderTarget.width, renderTarget.height); + } + } + + _gl.bindRenderbuffer(36161, null); + } // Setup resources for a Depth Texture for a FBO (needs an extension) + + + function setupDepthTexture(framebuffer, renderTarget) { + var isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; + if (isCube) throw new Error('Depth Texture with cube render targets is not supported'); + + _gl.bindFramebuffer(36160, framebuffer); + + if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { + throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture'); + } // upload an empty depth texture with framebuffer size + + + if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; + } + + setTexture2D(renderTarget.depthTexture, 0); + + var webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; + + if (renderTarget.depthTexture.format === DepthFormat) { + _gl.framebufferTexture2D(36160, 36096, 3553, webglDepthTexture, 0); + } else if (renderTarget.depthTexture.format === DepthStencilFormat) { + _gl.framebufferTexture2D(36160, 33306, 3553, webglDepthTexture, 0); + } else { + throw new Error('Unknown depthTexture format'); + } + } // Setup GL resources for a non-texture depth buffer + + + function setupDepthRenderbuffer(renderTarget) { + var renderTargetProperties = properties.get(renderTarget); + var isCube = renderTarget.isWebGLCubeRenderTarget === true; + + if (renderTarget.depthTexture) { + if (isCube) throw new Error('target.depthTexture not supported in Cube render targets'); + setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); + } else { + if (isCube) { + renderTargetProperties.__webglDepthbuffer = []; + + for (var i = 0; i < 6; i++) { + _gl.bindFramebuffer(36160, renderTargetProperties.__webglFramebuffer[i]); + + renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); + setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); + } + } else { + _gl.bindFramebuffer(36160, renderTargetProperties.__webglFramebuffer); + + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); + } + } + + _gl.bindFramebuffer(36160, null); + } // Set up GL resources for the render target + + + function setupRenderTarget(renderTarget) { + var texture = renderTarget.texture; + var renderTargetProperties = properties.get(renderTarget); + var textureProperties = properties.get(texture); + renderTarget.addEventListener('dispose', onRenderTargetDispose); + textureProperties.__webglTexture = _gl.createTexture(); + info.memory.textures++; + var isCube = renderTarget.isWebGLCubeRenderTarget === true; + var isMultisample = renderTarget.isWebGLMultisampleRenderTarget === true; + var isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; + var supportsMips = isPowerOfTwo(renderTarget) || isWebGL2; // Handles WebGL2 RGBFormat fallback - #18858 + + if (isWebGL2 && texture.format === RGBFormat && (texture.type === FloatType || texture.type === HalfFloatType)) { + texture.format = RGBAFormat; + console.warn('THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.'); + } // Setup framebuffer + + + if (isCube) { + renderTargetProperties.__webglFramebuffer = []; + + for (var i = 0; i < 6; i++) { + renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); + } + } else { + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + + if (isMultisample) { + if (isWebGL2) { + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + + _gl.bindRenderbuffer(36161, renderTargetProperties.__webglColorRenderbuffer); + + var glFormat = utils.convert(texture.format); + var glType = utils.convert(texture.type); + var glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType); + var samples = getRenderTargetSamples(renderTarget); + + _gl.renderbufferStorageMultisample(36161, samples, glInternalFormat, renderTarget.width, renderTarget.height); + + _gl.bindFramebuffer(36160, renderTargetProperties.__webglMultisampledFramebuffer); + + _gl.framebufferRenderbuffer(36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer); + + _gl.bindRenderbuffer(36161, null); + + if (renderTarget.depthBuffer) { + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); + } + + _gl.bindFramebuffer(36160, null); + } else { + console.warn('THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.'); + } + } + } // Setup color buffer + + + if (isCube) { + state.bindTexture(34067, textureProperties.__webglTexture); + setTextureParameters(34067, texture, supportsMips); + + for (var _i5 = 0; _i5 < 6; _i5++) { + setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[_i5], renderTarget, 36064, 34069 + _i5); + } + + if (textureNeedsGenerateMipmaps(texture, supportsMips)) { + generateMipmap(34067, texture, renderTarget.width, renderTarget.height); + } + + state.bindTexture(34067, null); + } else { + var glTextureType = 3553; + + if (isRenderTarget3D) { + // Render targets containing layers, i.e: Texture 3D and 2d arrays + if (isWebGL2) { + var isTexture3D = texture.isDataTexture3D; + glTextureType = isTexture3D ? 32879 : 35866; + } else { + console.warn('THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.'); + } + } + + state.bindTexture(glTextureType, textureProperties.__webglTexture); + setTextureParameters(glTextureType, texture, supportsMips); + setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, 36064, glTextureType); + + if (textureNeedsGenerateMipmaps(texture, supportsMips)) { + generateMipmap(3553, texture, renderTarget.width, renderTarget.height); + } + + state.bindTexture(3553, null); + } // Setup depth and stencil buffers + + + if (renderTarget.depthBuffer) { + setupDepthRenderbuffer(renderTarget); + } + } + + function updateRenderTargetMipmap(renderTarget) { + var texture = renderTarget.texture; + var supportsMips = isPowerOfTwo(renderTarget) || isWebGL2; + + if (textureNeedsGenerateMipmaps(texture, supportsMips)) { + var target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; + + var webglTexture = properties.get(texture).__webglTexture; + + state.bindTexture(target, webglTexture); + generateMipmap(target, texture, renderTarget.width, renderTarget.height); + state.bindTexture(target, null); + } + } + + function updateMultisampleRenderTarget(renderTarget) { + if (renderTarget.isWebGLMultisampleRenderTarget) { + if (isWebGL2) { + var renderTargetProperties = properties.get(renderTarget); + + _gl.bindFramebuffer(36008, renderTargetProperties.__webglMultisampledFramebuffer); + + _gl.bindFramebuffer(36009, renderTargetProperties.__webglFramebuffer); + + var width = renderTarget.width; + var height = renderTarget.height; + var mask = 16384; + if (renderTarget.depthBuffer) mask |= 256; + if (renderTarget.stencilBuffer) mask |= 1024; + + _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, 9728); + + _gl.bindFramebuffer(36160, renderTargetProperties.__webglMultisampledFramebuffer); // see #18905 + + } else { + console.warn('THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.'); + } + } + } + + function getRenderTargetSamples(renderTarget) { + return isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ? Math.min(maxSamples, renderTarget.samples) : 0; + } + + function updateVideoTexture(texture) { + var frame = info.render.frame; // Check the last frame we updated the VideoTexture + + if (_videoTextures.get(texture) !== frame) { + _videoTextures.set(texture, frame); + + texture.update(); + } + } // backwards compatibility + + + var warnedTexture2D = false; + var warnedTextureCube = false; + + function safeSetTexture2D(texture, slot) { + if (texture && texture.isWebGLRenderTarget) { + if (warnedTexture2D === false) { + console.warn('THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.'); + warnedTexture2D = true; + } + + texture = texture.texture; + } + + setTexture2D(texture, slot); + } + + function safeSetTextureCube(texture, slot) { + if (texture && texture.isWebGLCubeRenderTarget) { + if (warnedTextureCube === false) { + console.warn('THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.'); + warnedTextureCube = true; + } + + texture = texture.texture; + } + + setTextureCube(texture, slot); + } // + + + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; + this.setTextureCube = setTextureCube; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + this.safeSetTexture2D = safeSetTexture2D; + this.safeSetTextureCube = safeSetTextureCube; + } + + function WebGLUtils(gl, extensions, capabilities) { + var isWebGL2 = capabilities.isWebGL2; + + function convert(p) { + var extension; + if (p === UnsignedByteType) return 5121; + if (p === UnsignedShort4444Type) return 32819; + if (p === UnsignedShort5551Type) return 32820; + if (p === UnsignedShort565Type) return 33635; + if (p === ByteType) return 5120; + if (p === ShortType) return 5122; + if (p === UnsignedShortType) return 5123; + if (p === IntType) return 5124; + if (p === UnsignedIntType) return 5125; + if (p === FloatType) return 5126; + + if (p === HalfFloatType) { + if (isWebGL2) return 5131; + extension = extensions.get('OES_texture_half_float'); + + if (extension !== null) { + return extension.HALF_FLOAT_OES; + } else { + return null; + } + } + + if (p === AlphaFormat) return 6406; + if (p === RGBFormat) return 6407; + if (p === RGBAFormat) return 6408; + if (p === LuminanceFormat) return 6409; + if (p === LuminanceAlphaFormat) return 6410; + if (p === DepthFormat) return 6402; + if (p === DepthStencilFormat) return 34041; + if (p === RedFormat) return 6403; // WebGL2 formats. + + if (p === RedIntegerFormat) return 36244; + if (p === RGFormat) return 33319; + if (p === RGIntegerFormat) return 33320; + if (p === RGBIntegerFormat) return 36248; + if (p === RGBAIntegerFormat) return 36249; + + if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { + extension = extensions.get('WEBGL_compressed_texture_s3tc'); + + if (extension !== null) { + if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + } else { + return null; + } + } + + if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { + extension = extensions.get('WEBGL_compressed_texture_pvrtc'); + + if (extension !== null) { + if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + } else { + return null; + } + } + + if (p === RGB_ETC1_Format) { + extension = extensions.get('WEBGL_compressed_texture_etc1'); + + if (extension !== null) { + return extension.COMPRESSED_RGB_ETC1_WEBGL; + } else { + return null; + } + } + + if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { + extension = extensions.get('WEBGL_compressed_texture_etc'); + + if (extension !== null) { + if (p === RGB_ETC2_Format) return extension.COMPRESSED_RGB8_ETC2; + if (p === RGBA_ETC2_EAC_Format) return extension.COMPRESSED_RGBA8_ETC2_EAC; + } + } + + if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format) { + extension = extensions.get('WEBGL_compressed_texture_astc'); + + if (extension !== null) { + // TODO Complete? + return p; + } else { + return null; + } + } + + if (p === RGBA_BPTC_Format) { + extension = extensions.get('EXT_texture_compression_bptc'); + + if (extension !== null) { + // TODO Complete? + return p; + } else { + return null; + } + } + + if (p === UnsignedInt248Type) { + if (isWebGL2) return 34042; + extension = extensions.get('WEBGL_depth_texture'); + + if (extension !== null) { + return extension.UNSIGNED_INT_24_8_WEBGL; + } else { + return null; + } + } + } + + return { + convert: convert + }; + } + + function ArrayCamera(array) { + if (array === void 0) { + array = []; + } + + PerspectiveCamera.call(this); + this.cameras = array; + } + + ArrayCamera.prototype = Object.assign(Object.create(PerspectiveCamera.prototype), { + constructor: ArrayCamera, + isArrayCamera: true + }); + + var Group = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(Group, _Object3D); + + function Group() { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'Group'; + return _this; + } + + return Group; + }(Object3D); + + Group.prototype.isGroup = true; + + function WebXRController() { + this._targetRay = null; + this._grip = null; + this._hand = null; + } + + Object.assign(WebXRController.prototype, { + constructor: WebXRController, + getHandSpace: function getHandSpace() { + if (this._hand === null) { + this._hand = new Group(); + this._hand.matrixAutoUpdate = false; + this._hand.visible = false; + this._hand.joints = {}; + this._hand.inputState = { + pinching: false + }; + } + + return this._hand; + }, + getTargetRaySpace: function getTargetRaySpace() { + if (this._targetRay === null) { + this._targetRay = new Group(); + this._targetRay.matrixAutoUpdate = false; + this._targetRay.visible = false; + } + + return this._targetRay; + }, + getGripSpace: function getGripSpace() { + if (this._grip === null) { + this._grip = new Group(); + this._grip.matrixAutoUpdate = false; + this._grip.visible = false; + } + + return this._grip; + }, + dispatchEvent: function dispatchEvent(event) { + if (this._targetRay !== null) { + this._targetRay.dispatchEvent(event); + } + + if (this._grip !== null) { + this._grip.dispatchEvent(event); + } + + if (this._hand !== null) { + this._hand.dispatchEvent(event); + } + + return this; + }, + disconnect: function disconnect(inputSource) { + this.dispatchEvent({ + type: 'disconnected', + data: inputSource + }); + + if (this._targetRay !== null) { + this._targetRay.visible = false; + } + + if (this._grip !== null) { + this._grip.visible = false; + } + + if (this._hand !== null) { + this._hand.visible = false; + } + + return this; + }, + update: function update(inputSource, frame, referenceSpace) { + var inputPose = null; + var gripPose = null; + var handPose = null; + var targetRay = this._targetRay; + var grip = this._grip; + var hand = this._hand; + + if (inputSource && frame.session.visibilityState !== 'visible-blurred') { + if (hand && inputSource.hand) { + handPose = true; + + for (var _iterator = _createForOfIteratorHelperLoose(inputSource.hand.values()), _step; !(_step = _iterator()).done;) { + var inputjoint = _step.value; + // Update the joints groups with the XRJoint poses + var jointPose = frame.getJointPose(inputjoint, referenceSpace); + + if (hand.joints[inputjoint.jointName] === undefined) { + // The transform of this joint will be updated with the joint pose on each frame + var _joint = new Group(); + + _joint.matrixAutoUpdate = false; + _joint.visible = false; + hand.joints[inputjoint.jointName] = _joint; // ?? + + hand.add(_joint); + } + + var joint = hand.joints[inputjoint.jointName]; + + if (jointPose !== null) { + joint.matrix.fromArray(jointPose.transform.matrix); + joint.matrix.decompose(joint.position, joint.rotation, joint.scale); + joint.jointRadius = jointPose.radius; + } + + joint.visible = jointPose !== null; + } // Custom events + // Check pinchz + + + var indexTip = hand.joints['index-finger-tip']; + var thumbTip = hand.joints['thumb-tip']; + var distance = indexTip.position.distanceTo(thumbTip.position); + var distanceToPinch = 0.02; + var threshold = 0.005; + + if (hand.inputState.pinching && distance > distanceToPinch + threshold) { + hand.inputState.pinching = false; + this.dispatchEvent({ + type: 'pinchend', + handedness: inputSource.handedness, + target: this + }); + } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { + hand.inputState.pinching = true; + this.dispatchEvent({ + type: 'pinchstart', + handedness: inputSource.handedness, + target: this + }); + } + } else { + if (targetRay !== null) { + inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); + + if (inputPose !== null) { + targetRay.matrix.fromArray(inputPose.transform.matrix); + targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); + } + } + + if (grip !== null && inputSource.gripSpace) { + gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); + + if (gripPose !== null) { + grip.matrix.fromArray(gripPose.transform.matrix); + grip.matrix.decompose(grip.position, grip.rotation, grip.scale); + } + } + } + } + + if (targetRay !== null) { + targetRay.visible = inputPose !== null; + } + + if (grip !== null) { + grip.visible = gripPose !== null; + } + + if (hand !== null) { + hand.visible = handPose !== null; + } + + return this; + } + }); + + function WebXRManager(renderer, gl) { + var scope = this; + var session = null; + var framebufferScaleFactor = 1.0; + var referenceSpace = null; + var referenceSpaceType = 'local-floor'; + var pose = null; + var controllers = []; + var inputSourcesMap = new Map(); // + + var cameraL = new PerspectiveCamera(); + cameraL.layers.enable(1); + cameraL.viewport = new Vector4(); + var cameraR = new PerspectiveCamera(); + cameraR.layers.enable(2); + cameraR.viewport = new Vector4(); + var cameras = [cameraL, cameraR]; + var cameraVR = new ArrayCamera(); + cameraVR.layers.enable(1); + cameraVR.layers.enable(2); + var _currentDepthNear = null; + var _currentDepthFar = null; // + + this.enabled = false; + this.isPresenting = false; + + this.getController = function (index) { + var controller = controllers[index]; + + if (controller === undefined) { + controller = new WebXRController(); + controllers[index] = controller; + } + + return controller.getTargetRaySpace(); + }; + + this.getControllerGrip = function (index) { + var controller = controllers[index]; + + if (controller === undefined) { + controller = new WebXRController(); + controllers[index] = controller; + } + + return controller.getGripSpace(); + }; + + this.getHand = function (index) { + var controller = controllers[index]; + + if (controller === undefined) { + controller = new WebXRController(); + controllers[index] = controller; + } + + return controller.getHandSpace(); + }; // + + + function onSessionEvent(event) { + var controller = inputSourcesMap.get(event.inputSource); + + if (controller) { + controller.dispatchEvent({ + type: event.type, + data: event.inputSource + }); + } + } + + function onSessionEnd() { + inputSourcesMap.forEach(function (controller, inputSource) { + controller.disconnect(inputSource); + }); + inputSourcesMap.clear(); + _currentDepthNear = null; + _currentDepthFar = null; // + + renderer.setFramebuffer(null); + renderer.setRenderTarget(renderer.getRenderTarget()); // Hack #15830 + + animation.stop(); + scope.isPresenting = false; + scope.dispatchEvent({ + type: 'sessionend' + }); + } + + this.setFramebufferScaleFactor = function (value) { + framebufferScaleFactor = value; + + if (scope.isPresenting === true) { + console.warn('THREE.WebXRManager: Cannot change framebuffer scale while presenting.'); + } + }; + + this.setReferenceSpaceType = function (value) { + referenceSpaceType = value; + + if (scope.isPresenting === true) { + console.warn('THREE.WebXRManager: Cannot change reference space type while presenting.'); + } + }; + + this.getReferenceSpace = function () { + return referenceSpace; + }; + + this.getSession = function () { + return session; + }; + + this.setSession = /*#__PURE__*/function () { + var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(value) { + var attributes, layerInit, baseLayer; + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + session = value; + + if (!(session !== null)) { + _context.next = 24; + break; + } + + session.addEventListener('select', onSessionEvent); + session.addEventListener('selectstart', onSessionEvent); + session.addEventListener('selectend', onSessionEvent); + session.addEventListener('squeeze', onSessionEvent); + session.addEventListener('squeezestart', onSessionEvent); + session.addEventListener('squeezeend', onSessionEvent); + session.addEventListener('end', onSessionEnd); + session.addEventListener('inputsourceschange', onInputSourcesChange); + attributes = gl.getContextAttributes(); + + if (!(attributes.xrCompatible !== true)) { + _context.next = 14; + break; + } + + _context.next = 14; + return gl.makeXRCompatible(); + + case 14: + layerInit = { + antialias: attributes.antialias, + alpha: attributes.alpha, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; // eslint-disable-next-line no-undef + + baseLayer = new XRWebGLLayer(session, gl, layerInit); + session.updateRenderState({ + baseLayer: baseLayer + }); + _context.next = 19; + return session.requestReferenceSpace(referenceSpaceType); + + case 19: + referenceSpace = _context.sent; + animation.setContext(session); + animation.start(); + scope.isPresenting = true; + scope.dispatchEvent({ + type: 'sessionstart' + }); + + case 24: + case "end": + return _context.stop(); + } + } + }, _callee); + })); + + return function (_x) { + return _ref.apply(this, arguments); + }; + }(); + + function onInputSourcesChange(event) { + var inputSources = session.inputSources; // Assign inputSources to available controllers + + for (var i = 0; i < controllers.length; i++) { + inputSourcesMap.set(inputSources[i], controllers[i]); + } // Notify disconnected + + + for (var _i = 0; _i < event.removed.length; _i++) { + var inputSource = event.removed[_i]; + var controller = inputSourcesMap.get(inputSource); + + if (controller) { + controller.dispatchEvent({ + type: 'disconnected', + data: inputSource + }); + inputSourcesMap.delete(inputSource); + } + } // Notify connected + + + for (var _i2 = 0; _i2 < event.added.length; _i2++) { + var _inputSource = event.added[_i2]; + + var _controller = inputSourcesMap.get(_inputSource); + + if (_controller) { + _controller.dispatchEvent({ + type: 'connected', + data: _inputSource + }); + } + } + } // + + + var cameraLPos = new Vector3(); + var cameraRPos = new Vector3(); + /** + * Assumes 2 cameras that are parallel and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 + */ + + function setProjectionFromUnion(camera, cameraL, cameraR) { + cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); + cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); + var ipd = cameraLPos.distanceTo(cameraRPos); + var projL = cameraL.projectionMatrix.elements; + var projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // Use the left camera for these values. + + var near = projL[14] / (projL[10] - 1); + var far = projL[14] / (projL[10] + 1); + var topFov = (projL[9] + 1) / projL[5]; + var bottomFov = (projL[9] - 1) / projL[5]; + var leftFov = (projL[8] - 1) / projL[0]; + var rightFov = (projR[8] + 1) / projR[0]; + var left = near * leftFov; + var right = near * rightFov; // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + + var zOffset = ipd / (-leftFov + rightFov); + var xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? + + cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); + camera.translateX(xOffset); + camera.translateZ(zOffset); + camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); + camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + + var near2 = near + zOffset; + var far2 = far + zOffset; + var left2 = left - xOffset; + var right2 = right + (ipd - xOffset); + var top2 = topFov * far / far2 * near2; + var bottom2 = bottomFov * far / far2 * near2; + camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); + } + + function updateCamera(camera, parent) { + if (parent === null) { + camera.matrixWorld.copy(camera.matrix); + } else { + camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); + } + + camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); + } + + this.getCamera = function (camera) { + cameraVR.near = cameraR.near = cameraL.near = camera.near; + cameraVR.far = cameraR.far = cameraL.far = camera.far; + + if (_currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far) { + // Note that the new renderState won't apply until the next frame. See #18320 + session.updateRenderState({ + depthNear: cameraVR.near, + depthFar: cameraVR.far + }); + _currentDepthNear = cameraVR.near; + _currentDepthFar = cameraVR.far; + } + + var parent = camera.parent; + var cameras = cameraVR.cameras; + updateCamera(cameraVR, parent); + + for (var i = 0; i < cameras.length; i++) { + updateCamera(cameras[i], parent); + } // update camera and its children + + + camera.matrixWorld.copy(cameraVR.matrixWorld); + camera.matrix.copy(cameraVR.matrix); + camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); + var children = camera.children; + + for (var _i3 = 0, l = children.length; _i3 < l; _i3++) { + children[_i3].updateMatrixWorld(true); + } // update projection matrix for proper view frustum culling + + + if (cameras.length === 2) { + setProjectionFromUnion(cameraVR, cameraL, cameraR); + } else { + // assume single camera setup (AR) + cameraVR.projectionMatrix.copy(cameraL.projectionMatrix); + } + + return cameraVR; + }; // Animation Loop + + + var onAnimationFrameCallback = null; + + function onAnimationFrame(time, frame) { + pose = frame.getViewerPose(referenceSpace); + + if (pose !== null) { + var views = pose.views; + var baseLayer = session.renderState.baseLayer; + renderer.setFramebuffer(baseLayer.framebuffer); + var cameraVRNeedsUpdate = false; // check if it's necessary to rebuild cameraVR's camera list + + if (views.length !== cameraVR.cameras.length) { + cameraVR.cameras.length = 0; + cameraVRNeedsUpdate = true; + } + + for (var i = 0; i < views.length; i++) { + var view = views[i]; + var viewport = baseLayer.getViewport(view); + var camera = cameras[i]; + camera.matrix.fromArray(view.transform.matrix); + camera.projectionMatrix.fromArray(view.projectionMatrix); + camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); + + if (i === 0) { + cameraVR.matrix.copy(camera.matrix); + } + + if (cameraVRNeedsUpdate === true) { + cameraVR.cameras.push(camera); + } + } + } // + + + var inputSources = session.inputSources; + + for (var _i4 = 0; _i4 < controllers.length; _i4++) { + var controller = controllers[_i4]; + var inputSource = inputSources[_i4]; + controller.update(inputSource, frame, referenceSpace); + } + + if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); + } + + var animation = new WebGLAnimation(); + animation.setAnimationLoop(onAnimationFrame); + + this.setAnimationLoop = function (callback) { + onAnimationFrameCallback = callback; + }; + + this.dispose = function () {}; + } + + Object.assign(WebXRManager.prototype, EventDispatcher.prototype); + + function WebGLMaterials(properties) { + function refreshFogUniforms(uniforms, fog) { + uniforms.fogColor.value.copy(fog.color); + + if (fog.isFog) { + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + } else if (fog.isFogExp2) { + uniforms.fogDensity.value = fog.density; + } + } + + function refreshMaterialUniforms(uniforms, material, pixelRatio, height) { + if (material.isMeshBasicMaterial) { + refreshUniformsCommon(uniforms, material); + } else if (material.isMeshLambertMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsLambert(uniforms, material); + } else if (material.isMeshToonMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsToon(uniforms, material); + } else if (material.isMeshPhongMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsPhong(uniforms, material); + } else if (material.isMeshStandardMaterial) { + refreshUniformsCommon(uniforms, material); + + if (material.isMeshPhysicalMaterial) { + refreshUniformsPhysical(uniforms, material); + } else { + refreshUniformsStandard(uniforms, material); + } + } else if (material.isMeshMatcapMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsMatcap(uniforms, material); + } else if (material.isMeshDepthMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsDepth(uniforms, material); + } else if (material.isMeshDistanceMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsDistance(uniforms, material); + } else if (material.isMeshNormalMaterial) { + refreshUniformsCommon(uniforms, material); + refreshUniformsNormal(uniforms, material); + } else if (material.isLineBasicMaterial) { + refreshUniformsLine(uniforms, material); + + if (material.isLineDashedMaterial) { + refreshUniformsDash(uniforms, material); + } + } else if (material.isPointsMaterial) { + refreshUniformsPoints(uniforms, material, pixelRatio, height); + } else if (material.isSpriteMaterial) { + refreshUniformsSprites(uniforms, material); + } else if (material.isShadowMaterial) { + uniforms.color.value.copy(material.color); + uniforms.opacity.value = material.opacity; + } else if (material.isShaderMaterial) { + material.uniformsNeedUpdate = false; // #15581 + } + } + + function refreshUniformsCommon(uniforms, material) { + uniforms.opacity.value = material.opacity; + + if (material.color) { + uniforms.diffuse.value.copy(material.color); + } + + if (material.emissive) { + uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); + } + + if (material.map) { + uniforms.map.value = material.map; + } + + if (material.alphaMap) { + uniforms.alphaMap.value = material.alphaMap; + } + + if (material.specularMap) { + uniforms.specularMap.value = material.specularMap; + } + + var envMap = properties.get(material).envMap; + + if (envMap) { + uniforms.envMap.value = envMap; + uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap._needsFlipEnvMap ? -1 : 1; + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; + + var maxMipLevel = properties.get(envMap).__maxMipLevel; + + if (maxMipLevel !== undefined) { + uniforms.maxMipLevel.value = maxMipLevel; + } + } + + if (material.lightMap) { + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; + } + + if (material.aoMap) { + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; + } // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. displacementMap map + // 4. normal map + // 5. bump map + // 6. roughnessMap map + // 7. metalnessMap map + // 8. alphaMap map + // 9. emissiveMap map + // 10. clearcoat map + // 11. clearcoat normal map + // 12. clearcoat roughnessMap map + + + var uvScaleMap; + + if (material.map) { + uvScaleMap = material.map; + } else if (material.specularMap) { + uvScaleMap = material.specularMap; + } else if (material.displacementMap) { + uvScaleMap = material.displacementMap; + } else if (material.normalMap) { + uvScaleMap = material.normalMap; + } else if (material.bumpMap) { + uvScaleMap = material.bumpMap; + } else if (material.roughnessMap) { + uvScaleMap = material.roughnessMap; + } else if (material.metalnessMap) { + uvScaleMap = material.metalnessMap; + } else if (material.alphaMap) { + uvScaleMap = material.alphaMap; + } else if (material.emissiveMap) { + uvScaleMap = material.emissiveMap; + } else if (material.clearcoatMap) { + uvScaleMap = material.clearcoatMap; + } else if (material.clearcoatNormalMap) { + uvScaleMap = material.clearcoatNormalMap; + } else if (material.clearcoatRoughnessMap) { + uvScaleMap = material.clearcoatRoughnessMap; + } + + if (uvScaleMap !== undefined) { + // backwards compatibility + if (uvScaleMap.isWebGLRenderTarget) { + uvScaleMap = uvScaleMap.texture; + } + + if (uvScaleMap.matrixAutoUpdate === true) { + uvScaleMap.updateMatrix(); + } + + uniforms.uvTransform.value.copy(uvScaleMap.matrix); + } // uv repeat and offset setting priorities for uv2 + // 1. ao map + // 2. light map + + + var uv2ScaleMap; + + if (material.aoMap) { + uv2ScaleMap = material.aoMap; + } else if (material.lightMap) { + uv2ScaleMap = material.lightMap; + } + + if (uv2ScaleMap !== undefined) { + // backwards compatibility + if (uv2ScaleMap.isWebGLRenderTarget) { + uv2ScaleMap = uv2ScaleMap.texture; + } + + if (uv2ScaleMap.matrixAutoUpdate === true) { + uv2ScaleMap.updateMatrix(); + } + + uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix); + } + } + + function refreshUniformsLine(uniforms, material) { + uniforms.diffuse.value.copy(material.color); + uniforms.opacity.value = material.opacity; + } + + function refreshUniformsDash(uniforms, material) { + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + } + + function refreshUniformsPoints(uniforms, material, pixelRatio, height) { + uniforms.diffuse.value.copy(material.color); + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * pixelRatio; + uniforms.scale.value = height * 0.5; + + if (material.map) { + uniforms.map.value = material.map; + } + + if (material.alphaMap) { + uniforms.alphaMap.value = material.alphaMap; + } // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map + + + var uvScaleMap; + + if (material.map) { + uvScaleMap = material.map; + } else if (material.alphaMap) { + uvScaleMap = material.alphaMap; + } + + if (uvScaleMap !== undefined) { + if (uvScaleMap.matrixAutoUpdate === true) { + uvScaleMap.updateMatrix(); + } + + uniforms.uvTransform.value.copy(uvScaleMap.matrix); + } + } + + function refreshUniformsSprites(uniforms, material) { + uniforms.diffuse.value.copy(material.color); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; + + if (material.map) { + uniforms.map.value = material.map; + } + + if (material.alphaMap) { + uniforms.alphaMap.value = material.alphaMap; + } // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map + + + var uvScaleMap; + + if (material.map) { + uvScaleMap = material.map; + } else if (material.alphaMap) { + uvScaleMap = material.alphaMap; + } + + if (uvScaleMap !== undefined) { + if (uvScaleMap.matrixAutoUpdate === true) { + uvScaleMap.updateMatrix(); + } + + uniforms.uvTransform.value.copy(uvScaleMap.matrix); + } + } + + function refreshUniformsLambert(uniforms, material) { + if (material.emissiveMap) { + uniforms.emissiveMap.value = material.emissiveMap; + } + } + + function refreshUniformsPhong(uniforms, material) { + uniforms.specular.value.copy(material.specular); + uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) + + if (material.emissiveMap) { + uniforms.emissiveMap.value = material.emissiveMap; + } + + if (material.bumpMap) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if (material.side === BackSide) uniforms.bumpScale.value *= -1; + } + + if (material.normalMap) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy(material.normalScale); + if (material.side === BackSide) uniforms.normalScale.value.negate(); + } + + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + } + + function refreshUniformsToon(uniforms, material) { + if (material.gradientMap) { + uniforms.gradientMap.value = material.gradientMap; + } + + if (material.emissiveMap) { + uniforms.emissiveMap.value = material.emissiveMap; + } + + if (material.bumpMap) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if (material.side === BackSide) uniforms.bumpScale.value *= -1; + } + + if (material.normalMap) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy(material.normalScale); + if (material.side === BackSide) uniforms.normalScale.value.negate(); + } + + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + } + + function refreshUniformsStandard(uniforms, material) { + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; + + if (material.roughnessMap) { + uniforms.roughnessMap.value = material.roughnessMap; + } + + if (material.metalnessMap) { + uniforms.metalnessMap.value = material.metalnessMap; + } + + if (material.emissiveMap) { + uniforms.emissiveMap.value = material.emissiveMap; + } + + if (material.bumpMap) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if (material.side === BackSide) uniforms.bumpScale.value *= -1; + } + + if (material.normalMap) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy(material.normalScale); + if (material.side === BackSide) uniforms.normalScale.value.negate(); + } + + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + + var envMap = properties.get(material).envMap; + + if (envMap) { + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; + } + } + + function refreshUniformsPhysical(uniforms, material) { + refreshUniformsStandard(uniforms, material); + uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + + uniforms.clearcoat.value = material.clearcoat; + uniforms.clearcoatRoughness.value = material.clearcoatRoughness; + if (material.sheen) uniforms.sheen.value.copy(material.sheen); + + if (material.clearcoatMap) { + uniforms.clearcoatMap.value = material.clearcoatMap; + } + + if (material.clearcoatRoughnessMap) { + uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; + } + + if (material.clearcoatNormalMap) { + uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); + uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; + + if (material.side === BackSide) { + uniforms.clearcoatNormalScale.value.negate(); + } + } + + uniforms.transmission.value = material.transmission; + + if (material.transmissionMap) { + uniforms.transmissionMap.value = material.transmissionMap; + } + } + + function refreshUniformsMatcap(uniforms, material) { + if (material.matcap) { + uniforms.matcap.value = material.matcap; + } + + if (material.bumpMap) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if (material.side === BackSide) uniforms.bumpScale.value *= -1; + } + + if (material.normalMap) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy(material.normalScale); + if (material.side === BackSide) uniforms.normalScale.value.negate(); + } + + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + } + + function refreshUniformsDepth(uniforms, material) { + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + } + + function refreshUniformsDistance(uniforms, material) { + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + + uniforms.referencePosition.value.copy(material.referencePosition); + uniforms.nearDistance.value = material.nearDistance; + uniforms.farDistance.value = material.farDistance; + } + + function refreshUniformsNormal(uniforms, material) { + if (material.bumpMap) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if (material.side === BackSide) uniforms.bumpScale.value *= -1; + } + + if (material.normalMap) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy(material.normalScale); + if (material.side === BackSide) uniforms.normalScale.value.negate(); + } + + if (material.displacementMap) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + } + } + + return { + refreshFogUniforms: refreshFogUniforms, + refreshMaterialUniforms: refreshMaterialUniforms + }; + } + + function createCanvasElement() { + var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); + canvas.style.display = 'block'; + return canvas; + } + + function WebGLRenderer(parameters) { + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), + _context = parameters.context !== undefined ? parameters.context : null, + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', + _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; + + var currentRenderList = null; + var currentRenderState = null; // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. + + var renderListStack = []; + var renderStateStack = []; // public properties + + this.domElement = _canvas; // Debug configuration container + + this.debug = { + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true + }; // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; // scene graph + + this.sortObjects = true; // user-defined clipping + + this.clippingPlanes = []; + this.localClippingEnabled = false; // physically based shading + + this.gammaFactor = 2.0; // for backwards compatibility + + this.outputEncoding = LinearEncoding; // physical lights + + this.physicallyCorrectLights = false; // tone mapping + + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; // internal properties + + var _this = this; + + var _isContextLost = false; // internal state cache + + var _framebuffer = null; + var _currentActiveCubeFace = 0; + var _currentActiveMipmapLevel = 0; + var _currentRenderTarget = null; + var _currentFramebuffer = null; + + var _currentMaterialId = -1; + + var _currentCamera = null; + + var _currentViewport = new Vector4(); + + var _currentScissor = new Vector4(); + + var _currentScissorTest = null; // + + var _width = _canvas.width; + var _height = _canvas.height; + var _pixelRatio = 1; + var _opaqueSort = null; + var _transparentSort = null; + + var _viewport = new Vector4(0, 0, _width, _height); + + var _scissor = new Vector4(0, 0, _width, _height); + + var _scissorTest = false; // frustum + + var _frustum = new Frustum(); // clipping + + + var _clippingEnabled = false; + var _localClippingEnabled = false; // camera matrices cache + + var _projScreenMatrix = new Matrix4(); + + var _vector3 = new Vector3(); + + var _emptyScene = { + background: null, + fog: null, + environment: null, + overrideMaterial: null, + isScene: true + }; + + function getTargetPixelRatio() { + return _currentRenderTarget === null ? _pixelRatio : 1; + } // initialize + + + var _gl = _context; + + function getContext(contextNames, contextAttributes) { + for (var i = 0; i < contextNames.length; i++) { + var contextName = contextNames[i]; + + var context = _canvas.getContext(contextName, contextAttributes); + + if (context !== null) return context; + } + + return null; + } + + try { + var contextAttributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer, + powerPreference: _powerPreference, + failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat + }; // event listeners must be registered before WebGL context is created, see #12753 + + _canvas.addEventListener('webglcontextlost', onContextLost, false); + + _canvas.addEventListener('webglcontextrestored', onContextRestore, false); + + if (_gl === null) { + var contextNames = ['webgl2', 'webgl', 'experimental-webgl']; + + if (_this.isWebGL1Renderer === true) { + contextNames.shift(); + } + + _gl = getContext(contextNames, contextAttributes); + + if (_gl === null) { + if (getContext(contextNames)) { + throw new Error('Error creating WebGL context with your selected attributes.'); + } else { + throw new Error('Error creating WebGL context.'); + } + } + } // Some experimental-webgl implementations do not have getShaderPrecisionFormat + + + if (_gl.getShaderPrecisionFormat === undefined) { + _gl.getShaderPrecisionFormat = function () { + return { + 'rangeMin': 1, + 'rangeMax': 1, + 'precision': 1 + }; + }; + } + } catch (error) { + console.error('THREE.WebGLRenderer: ' + error.message); + throw error; + } + + var extensions, capabilities, state, info; + var properties, textures, cubemaps, attributes, geometries, objects; + var programCache, materials, renderLists, renderStates, clipping; + var background, morphtargets, bufferRenderer, indexedBufferRenderer; + var utils, bindingStates; + + function initGLContext() { + extensions = new WebGLExtensions(_gl); + capabilities = new WebGLCapabilities(_gl, extensions, parameters); + extensions.init(capabilities); + utils = new WebGLUtils(_gl, extensions, capabilities); + state = new WebGLState(_gl, extensions, capabilities); + state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); + state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); + info = new WebGLInfo(_gl); + properties = new WebGLProperties(); + textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); + cubemaps = new WebGLCubeMaps(_this); + attributes = new WebGLAttributes(_gl, capabilities); + bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); + geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); + objects = new WebGLObjects(_gl, geometries, attributes, info); + morphtargets = new WebGLMorphtargets(_gl); + clipping = new WebGLClipping(properties); + programCache = new WebGLPrograms(_this, cubemaps, extensions, capabilities, bindingStates, clipping); + materials = new WebGLMaterials(properties); + renderLists = new WebGLRenderLists(properties); + renderStates = new WebGLRenderStates(extensions, capabilities); + background = new WebGLBackground(_this, cubemaps, state, objects, _premultipliedAlpha); + bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); + indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); + info.programs = programCache.programs; + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.state = state; + _this.info = info; + } + + initGLContext(); // xr + + var xr = new WebXRManager(_this, _gl); + this.xr = xr; // shadow map + + var shadowMap = new WebGLShadowMap(_this, objects, capabilities.maxTextureSize); + this.shadowMap = shadowMap; // API + + this.getContext = function () { + return _gl; + }; + + this.getContextAttributes = function () { + return _gl.getContextAttributes(); + }; + + this.forceContextLoss = function () { + var extension = extensions.get('WEBGL_lose_context'); + if (extension) extension.loseContext(); + }; + + this.forceContextRestore = function () { + var extension = extensions.get('WEBGL_lose_context'); + if (extension) extension.restoreContext(); + }; + + this.getPixelRatio = function () { + return _pixelRatio; + }; + + this.setPixelRatio = function (value) { + if (value === undefined) return; + _pixelRatio = value; + this.setSize(_width, _height, false); + }; + + this.getSize = function (target) { + if (target === undefined) { + console.warn('WebGLRenderer: .getsize() now requires a Vector2 as an argument'); + target = new Vector2(); + } + + return target.set(_width, _height); + }; + + this.setSize = function (width, height, updateStyle) { + if (xr.isPresenting) { + console.warn('THREE.WebGLRenderer: Can\'t change size while VR device is presenting.'); + return; + } + + _width = width; + _height = height; + _canvas.width = Math.floor(width * _pixelRatio); + _canvas.height = Math.floor(height * _pixelRatio); + + if (updateStyle !== false) { + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + } + + this.setViewport(0, 0, width, height); + }; + + this.getDrawingBufferSize = function (target) { + if (target === undefined) { + console.warn('WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument'); + target = new Vector2(); + } + + return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); + }; + + this.setDrawingBufferSize = function (width, height, pixelRatio) { + _width = width; + _height = height; + _pixelRatio = pixelRatio; + _canvas.width = Math.floor(width * pixelRatio); + _canvas.height = Math.floor(height * pixelRatio); + this.setViewport(0, 0, width, height); + }; + + this.getCurrentViewport = function (target) { + if (target === undefined) { + console.warn('WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument'); + target = new Vector4(); + } + + return target.copy(_currentViewport); + }; + + this.getViewport = function (target) { + return target.copy(_viewport); + }; + + this.setViewport = function (x, y, width, height) { + if (x.isVector4) { + _viewport.set(x.x, x.y, x.z, x.w); + } else { + _viewport.set(x, y, width, height); + } + + state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); + }; + + this.getScissor = function (target) { + return target.copy(_scissor); + }; + + this.setScissor = function (x, y, width, height) { + if (x.isVector4) { + _scissor.set(x.x, x.y, x.z, x.w); + } else { + _scissor.set(x, y, width, height); + } + + state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); + }; + + this.getScissorTest = function () { + return _scissorTest; + }; + + this.setScissorTest = function (boolean) { + state.setScissorTest(_scissorTest = boolean); + }; + + this.setOpaqueSort = function (method) { + _opaqueSort = method; + }; + + this.setTransparentSort = function (method) { + _transparentSort = method; + }; // Clearing + + + this.getClearColor = function (target) { + if (target === undefined) { + console.warn('WebGLRenderer: .getClearColor() now requires a Color as an argument'); + target = new Color(); + } + + return target.copy(background.getClearColor()); + }; + + this.setClearColor = function () { + background.setClearColor.apply(background, arguments); + }; + + this.getClearAlpha = function () { + return background.getClearAlpha(); + }; + + this.setClearAlpha = function () { + background.setClearAlpha.apply(background, arguments); + }; + + this.clear = function (color, depth, stencil) { + var bits = 0; + if (color === undefined || color) bits |= 16384; + if (depth === undefined || depth) bits |= 256; + if (stencil === undefined || stencil) bits |= 1024; + + _gl.clear(bits); + }; + + this.clearColor = function () { + this.clear(true, false, false); + }; + + this.clearDepth = function () { + this.clear(false, true, false); + }; + + this.clearStencil = function () { + this.clear(false, false, true); + }; // + + + this.dispose = function () { + _canvas.removeEventListener('webglcontextlost', onContextLost, false); + + _canvas.removeEventListener('webglcontextrestored', onContextRestore, false); + + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + objects.dispose(); + bindingStates.dispose(); + xr.dispose(); + animation.stop(); + }; // Events + + + function onContextLost(event) { + event.preventDefault(); + console.log('THREE.WebGLRenderer: Context Lost.'); + _isContextLost = true; + } + + function onContextRestore() + /* event */ + { + console.log('THREE.WebGLRenderer: Context Restored.'); + _isContextLost = false; + initGLContext(); + } + + function onMaterialDispose(event) { + var material = event.target; + material.removeEventListener('dispose', onMaterialDispose); + deallocateMaterial(material); + } // Buffer deallocation + + + function deallocateMaterial(material) { + releaseMaterialProgramReference(material); + properties.remove(material); + } + + function releaseMaterialProgramReference(material) { + var programInfo = properties.get(material).program; + + if (programInfo !== undefined) { + programCache.releaseProgram(programInfo); + } + } // Buffer rendering + + + function renderObjectImmediate(object, program) { + object.render(function (object) { + _this.renderBufferImmediate(object, program); + }); + } + + this.renderBufferImmediate = function (object, program) { + bindingStates.initAttributes(); + var buffers = properties.get(object); + if (object.hasPositions && !buffers.position) buffers.position = _gl.createBuffer(); + if (object.hasNormals && !buffers.normal) buffers.normal = _gl.createBuffer(); + if (object.hasUvs && !buffers.uv) buffers.uv = _gl.createBuffer(); + if (object.hasColors && !buffers.color) buffers.color = _gl.createBuffer(); + var programAttributes = program.getAttributes(); + + if (object.hasPositions) { + _gl.bindBuffer(34962, buffers.position); + + _gl.bufferData(34962, object.positionArray, 35048); + + bindingStates.enableAttribute(programAttributes.position); + + _gl.vertexAttribPointer(programAttributes.position, 3, 5126, false, 0, 0); + } + + if (object.hasNormals) { + _gl.bindBuffer(34962, buffers.normal); + + _gl.bufferData(34962, object.normalArray, 35048); + + bindingStates.enableAttribute(programAttributes.normal); + + _gl.vertexAttribPointer(programAttributes.normal, 3, 5126, false, 0, 0); + } + + if (object.hasUvs) { + _gl.bindBuffer(34962, buffers.uv); + + _gl.bufferData(34962, object.uvArray, 35048); + + bindingStates.enableAttribute(programAttributes.uv); + + _gl.vertexAttribPointer(programAttributes.uv, 2, 5126, false, 0, 0); + } + + if (object.hasColors) { + _gl.bindBuffer(34962, buffers.color); + + _gl.bufferData(34962, object.colorArray, 35048); + + bindingStates.enableAttribute(programAttributes.color); + + _gl.vertexAttribPointer(programAttributes.color, 3, 5126, false, 0, 0); + } + + bindingStates.disableUnusedAttributes(); + + _gl.drawArrays(4, 0, object.count); + + object.count = 0; + }; + + this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { + if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + + var frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; + var program = setProgram(camera, scene, material, object); + state.setMaterial(material, frontFaceCW); // + + var index = geometry.index; + var position = geometry.attributes.position; // + + if (index === null) { + if (position === undefined || position.count === 0) return; + } else if (index.count === 0) { + return; + } // + + + var rangeFactor = 1; + + if (material.wireframe === true) { + index = geometries.getWireframeAttribute(geometry); + rangeFactor = 2; + } + + if (material.morphTargets || material.morphNormals) { + morphtargets.update(object, geometry, material, program); + } + + bindingStates.setup(object, material, program, geometry, index); + var attribute; + var renderer = bufferRenderer; + + if (index !== null) { + attribute = attributes.get(index); + renderer = indexedBufferRenderer; + renderer.setIndex(attribute); + } // + + + var dataCount = index !== null ? index.count : position.count; + var rangeStart = geometry.drawRange.start * rangeFactor; + var rangeCount = geometry.drawRange.count * rangeFactor; + var groupStart = group !== null ? group.start * rangeFactor : 0; + var groupCount = group !== null ? group.count * rangeFactor : Infinity; + var drawStart = Math.max(rangeStart, groupStart); + var drawEnd = Math.min(dataCount, rangeStart + rangeCount, groupStart + groupCount) - 1; + var drawCount = Math.max(0, drawEnd - drawStart + 1); + if (drawCount === 0) return; // + + if (object.isMesh) { + if (material.wireframe === true) { + state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); + renderer.setMode(1); + } else { + renderer.setMode(4); + } + } else if (object.isLine) { + var lineWidth = material.linewidth; + if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material + + state.setLineWidth(lineWidth * getTargetPixelRatio()); + + if (object.isLineSegments) { + renderer.setMode(1); + } else if (object.isLineLoop) { + renderer.setMode(2); + } else { + renderer.setMode(3); + } + } else if (object.isPoints) { + renderer.setMode(0); + } else if (object.isSprite) { + renderer.setMode(4); + } + + if (object.isInstancedMesh) { + renderer.renderInstances(drawStart, drawCount, object.count); + } else if (geometry.isInstancedBufferGeometry) { + var instanceCount = Math.min(geometry.instanceCount, geometry._maxInstanceCount); + renderer.renderInstances(drawStart, drawCount, instanceCount); + } else { + renderer.render(drawStart, drawCount); + } + }; // Compile + + + this.compile = function (scene, camera) { + currentRenderState = renderStates.get(scene); + currentRenderState.init(); + scene.traverseVisible(function (object) { + if (object.isLight && object.layers.test(camera.layers)) { + currentRenderState.pushLight(object); + + if (object.castShadow) { + currentRenderState.pushShadow(object); + } + } + }); + currentRenderState.setupLights(); + var compiled = new WeakMap(); + scene.traverse(function (object) { + var material = object.material; + + if (material) { + if (Array.isArray(material)) { + for (var i = 0; i < material.length; i++) { + var material2 = material[i]; + + if (compiled.has(material2) === false) { + initMaterial(material2, scene, object); + compiled.set(material2); + } + } + } else if (compiled.has(material) === false) { + initMaterial(material, scene, object); + compiled.set(material); + } + } + }); + }; // Animation Loop + + + var onAnimationFrameCallback = null; + + function onAnimationFrame(time) { + if (xr.isPresenting) return; + if (onAnimationFrameCallback) onAnimationFrameCallback(time); + } + + var animation = new WebGLAnimation(); + animation.setAnimationLoop(onAnimationFrame); + if (typeof window !== 'undefined') animation.setContext(window); + + this.setAnimationLoop = function (callback) { + onAnimationFrameCallback = callback; + xr.setAnimationLoop(callback); + callback === null ? animation.stop() : animation.start(); + }; // Rendering + + + this.render = function (scene, camera) { + var renderTarget, forceClear; + + if (arguments[2] !== undefined) { + console.warn('THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.'); + renderTarget = arguments[2]; + } + + if (arguments[3] !== undefined) { + console.warn('THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.'); + forceClear = arguments[3]; + } + + if (camera !== undefined && camera.isCamera !== true) { + console.error('THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.'); + return; + } + + if (_isContextLost === true) return; // reset caching for this frame + + bindingStates.resetDefaultState(); + _currentMaterialId = -1; + _currentCamera = null; // update scene graph + + if (scene.autoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum + + if (camera.parent === null) camera.updateMatrixWorld(); + + if (xr.enabled === true && xr.isPresenting === true) { + camera = xr.getCamera(camera); + } // + + + if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, renderTarget || _currentRenderTarget); + currentRenderState = renderStates.get(scene, renderStateStack.length); + currentRenderState.init(); + renderStateStack.push(currentRenderState); + + _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + + _frustum.setFromProjectionMatrix(_projScreenMatrix); + + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled, camera); + currentRenderList = renderLists.get(scene, renderListStack.length); + currentRenderList.init(); + renderListStack.push(currentRenderList); + projectObject(scene, camera, 0, _this.sortObjects); + currentRenderList.finish(); + + if (_this.sortObjects === true) { + currentRenderList.sort(_opaqueSort, _transparentSort); + } // + + + if (_clippingEnabled === true) clipping.beginShadows(); + var shadowsArray = currentRenderState.state.shadowsArray; + shadowMap.render(shadowsArray, scene, camera); + currentRenderState.setupLights(); + currentRenderState.setupLightsView(camera); + if (_clippingEnabled === true) clipping.endShadows(); // + + if (this.info.autoReset === true) this.info.reset(); + + if (renderTarget !== undefined) { + this.setRenderTarget(renderTarget); + } // + + + background.render(currentRenderList, scene, camera, forceClear); // render scene + + var opaqueObjects = currentRenderList.opaque; + var transparentObjects = currentRenderList.transparent; + if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); + if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // + + if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // + + if (_currentRenderTarget !== null) { + // Generate mipmap if we're using any kind of mipmap filtering + textures.updateRenderTargetMipmap(_currentRenderTarget); // resolve multisample renderbuffers to a single-sample texture if necessary + + textures.updateMultisampleRenderTarget(_currentRenderTarget); + } // Ensure depth buffer writing is enabled so it can be cleared on next render + + + state.buffers.depth.setTest(true); + state.buffers.depth.setMask(true); + state.buffers.color.setMask(true); + state.setPolygonOffset(false); // _gl.finish(); + + renderStateStack.pop(); + + if (renderStateStack.length > 0) { + currentRenderState = renderStateStack[renderStateStack.length - 1]; + } else { + currentRenderState = null; + } + + renderListStack.pop(); + + if (renderListStack.length > 0) { + currentRenderList = renderListStack[renderListStack.length - 1]; + } else { + currentRenderList = null; + } + }; + + function projectObject(object, camera, groupOrder, sortObjects) { + if (object.visible === false) return; + var visible = object.layers.test(camera.layers); + + if (visible) { + if (object.isGroup) { + groupOrder = object.renderOrder; + } else if (object.isLOD) { + if (object.autoUpdate === true) object.update(camera); + } else if (object.isLight) { + currentRenderState.pushLight(object); + + if (object.castShadow) { + currentRenderState.pushShadow(object); + } + } else if (object.isSprite) { + if (!object.frustumCulled || _frustum.intersectsSprite(object)) { + if (sortObjects) { + _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); + } + + var geometry = objects.update(object); + var material = object.material; + + if (material.visible) { + currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); + } + } + } else if (object.isImmediateRenderObject) { + if (sortObjects) { + _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); + } + + currentRenderList.push(object, null, object.material, groupOrder, _vector3.z, null); + } else if (object.isMesh || object.isLine || object.isPoints) { + if (object.isSkinnedMesh) { + // update skeleton only once in a frame + if (object.skeleton.frame !== info.render.frame) { + object.skeleton.update(); + object.skeleton.frame = info.render.frame; + } + } + + if (!object.frustumCulled || _frustum.intersectsObject(object)) { + if (sortObjects) { + _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); + } + + var _geometry = objects.update(object); + + var _material = object.material; + + if (Array.isArray(_material)) { + var groups = _geometry.groups; + + for (var i = 0, l = groups.length; i < l; i++) { + var group = groups[i]; + var groupMaterial = _material[group.materialIndex]; + + if (groupMaterial && groupMaterial.visible) { + currentRenderList.push(object, _geometry, groupMaterial, groupOrder, _vector3.z, group); + } + } + } else if (_material.visible) { + currentRenderList.push(object, _geometry, _material, groupOrder, _vector3.z, null); + } + } + } + } + + var children = object.children; + + for (var _i = 0, _l = children.length; _i < _l; _i++) { + projectObject(children[_i], camera, groupOrder, sortObjects); + } + } + + function renderObjects(renderList, scene, camera) { + var overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; + + for (var i = 0, l = renderList.length; i < l; i++) { + var renderItem = renderList[i]; + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === null ? renderItem.material : overrideMaterial; + var group = renderItem.group; + + if (camera.isArrayCamera) { + var cameras = camera.cameras; + + for (var j = 0, jl = cameras.length; j < jl; j++) { + var camera2 = cameras[j]; + + if (object.layers.test(camera2.layers)) { + state.viewport(_currentViewport.copy(camera2.viewport)); + currentRenderState.setupLightsView(camera2); + renderObject(object, scene, camera2, geometry, material, group); + } + } + } else { + renderObject(object, scene, camera, geometry, material, group); + } + } + } + + function renderObject(object, scene, camera, geometry, material, group) { + object.onBeforeRender(_this, scene, camera, geometry, material, group); + object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); + object.normalMatrix.getNormalMatrix(object.modelViewMatrix); + + if (object.isImmediateRenderObject) { + var program = setProgram(camera, scene, material, object); + state.setMaterial(material); + bindingStates.reset(); + renderObjectImmediate(object, program); + } else { + _this.renderBufferDirect(camera, scene, geometry, material, object, group); + } + + object.onAfterRender(_this, scene, camera, geometry, material, group); + } + + function initMaterial(material, scene, object) { + if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + + var materialProperties = properties.get(material); + var lights = currentRenderState.state.lights; + var shadowsArray = currentRenderState.state.shadowsArray; + var lightsStateVersion = lights.state.version; + var parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); + var programCacheKey = programCache.getProgramCacheKey(parameters); + var program = materialProperties.program; + var programChange = true; // always update environment and fog - changing these trigger an initMaterial call, but it's possible that the program doesn't change + + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = cubemaps.get(material.envMap || materialProperties.environment); + + if (program === undefined) { + // new material + material.addEventListener('dispose', onMaterialDispose); + } else if (program.cacheKey !== programCacheKey) { + // changed glsl or parameters + releaseMaterialProgramReference(material); + } else if (materialProperties.lightsStateVersion !== lightsStateVersion) { + programChange = false; + } else if (parameters.shaderID !== undefined) { + // same glsl and uniform list + return; + } else { + // only rebuild uniform list + programChange = false; + } + + if (programChange) { + parameters.uniforms = programCache.getUniforms(material); + material.onBeforeCompile(parameters, _this); + program = programCache.acquireProgram(parameters, programCacheKey); + materialProperties.program = program; + materialProperties.uniforms = parameters.uniforms; + materialProperties.outputEncoding = parameters.outputEncoding; + } + + var uniforms = materialProperties.uniforms; + + if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { + materialProperties.numClippingPlanes = clipping.numPlanes; + materialProperties.numIntersection = clipping.numIntersection; + uniforms.clippingPlanes = clipping.uniform; + } // store the light setup it was created for + + + materialProperties.needsLights = materialNeedsLights(material); + materialProperties.lightsStateVersion = lightsStateVersion; + + if (materialProperties.needsLights) { + // wire up the material to this renderer's lighting state + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.lightProbe.value = lights.state.probe; + uniforms.directionalLights.value = lights.state.directional; + uniforms.directionalLightShadows.value = lights.state.directionalShadow; + uniforms.spotLights.value = lights.state.spot; + uniforms.spotLightShadows.value = lights.state.spotShadow; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms + } + + var progUniforms = materialProperties.program.getUniforms(); + var uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); + materialProperties.uniformsList = uniformsList; + } + + function setProgram(camera, scene, material, object) { + if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + + textures.resetTextureUnits(); + var fog = scene.fog; + var environment = material.isMeshStandardMaterial ? scene.environment : null; + var encoding = _currentRenderTarget === null ? _this.outputEncoding : _currentRenderTarget.texture.encoding; + var envMap = cubemaps.get(material.envMap || environment); + var materialProperties = properties.get(material); + var lights = currentRenderState.state.lights; + + if (_clippingEnabled === true) { + if (_localClippingEnabled === true || camera !== _currentCamera) { + var useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + + clipping.setState(material, camera, useCache); + } + } + + if (material.version === materialProperties.__version) { + if (material.fog && materialProperties.fog !== fog) { + initMaterial(material, scene, object); + } else if (materialProperties.environment !== environment) { + initMaterial(material, scene, object); + } else if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { + initMaterial(material, scene, object); + } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { + initMaterial(material, scene, object); + } else if (materialProperties.outputEncoding !== encoding) { + initMaterial(material, scene, object); + } else if (materialProperties.envMap !== envMap) { + initMaterial(material, scene, object); + } + } else { + initMaterial(material, scene, object); + materialProperties.__version = material.version; + } + + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; + + if (state.useProgram(program.program)) { + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; + } + + if (material.id !== _currentMaterialId) { + _currentMaterialId = material.id; + refreshMaterial = true; + } + + if (refreshProgram || _currentCamera !== camera) { + p_uniforms.setValue(_gl, 'projectionMatrix', camera.projectionMatrix); + + if (capabilities.logarithmicDepthBuffer) { + p_uniforms.setValue(_gl, 'logDepthBufFC', 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); + } + + if (_currentCamera !== camera) { + _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: + + refreshMaterial = true; // set to true on material change + + refreshLights = true; // remains set until update done + } // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + + if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { + var uCamPos = p_uniforms.map.cameraPosition; + + if (uCamPos !== undefined) { + uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); + } + } + + if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { + p_uniforms.setValue(_gl, 'isOrthographic', camera.isOrthographicCamera === true); + } + + if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || material.skinning) { + p_uniforms.setValue(_gl, 'viewMatrix', camera.matrixWorldInverse); + } + } // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // otherwise textures used for skinning can take over texture units reserved for other material textures + + + if (material.skinning) { + p_uniforms.setOptional(_gl, object, 'bindMatrix'); + p_uniforms.setOptional(_gl, object, 'bindMatrixInverse'); + var skeleton = object.skeleton; + + if (skeleton) { + var bones = skeleton.bones; + + if (capabilities.floatVertexTextures) { + if (skeleton.boneTexture === null) { + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + var size = Math.sqrt(bones.length * 4); // 4 pixels needed for 1 matrix + + size = MathUtils.ceilPowerOfTwo(size); + size = Math.max(size, 4); + var boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel + + boneMatrices.set(skeleton.boneMatrices); // copy current values + + var boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); + skeleton.boneMatrices = boneMatrices; + skeleton.boneTexture = boneTexture; + skeleton.boneTextureSize = size; + } + + p_uniforms.setValue(_gl, 'boneTexture', skeleton.boneTexture, textures); + p_uniforms.setValue(_gl, 'boneTextureSize', skeleton.boneTextureSize); + } else { + p_uniforms.setOptional(_gl, skeleton, 'boneMatrices'); + } + } + } + + if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue(_gl, 'receiveShadow', object.receiveShadow); + } + + if (refreshMaterial) { + p_uniforms.setValue(_gl, 'toneMappingExposure', _this.toneMappingExposure); + + if (materialProperties.needsLights) { + // the current material requires lighting info + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required + markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); + } // refresh uniforms common to several materials + + + if (fog && material.fog) { + materials.refreshFogUniforms(m_uniforms, fog); + } + + materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height); + WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); + } + + if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { + WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); + material.uniformsNeedUpdate = false; + } + + if (material.isSpriteMaterial) { + p_uniforms.setValue(_gl, 'center', object.center); + } // common matrices + + + p_uniforms.setValue(_gl, 'modelViewMatrix', object.modelViewMatrix); + p_uniforms.setValue(_gl, 'normalMatrix', object.normalMatrix); + p_uniforms.setValue(_gl, 'modelMatrix', object.matrixWorld); + return program; + } // If uniforms are marked as clean, they don't need to be loaded to the GPU. + + + function markUniformsLightsNeedsUpdate(uniforms, value) { + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; + } + + function materialNeedsLights(material) { + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; + } // + + + this.setFramebuffer = function (value) { + if (_framebuffer !== value && _currentRenderTarget === null) _gl.bindFramebuffer(36160, value); + _framebuffer = value; + }; + + this.getActiveCubeFace = function () { + return _currentActiveCubeFace; + }; + + this.getActiveMipmapLevel = function () { + return _currentActiveMipmapLevel; + }; + + this.getRenderTarget = function () { + return _currentRenderTarget; + }; + + this.setRenderTarget = function (renderTarget, activeCubeFace, activeMipmapLevel) { + if (activeCubeFace === void 0) { + activeCubeFace = 0; + } + + if (activeMipmapLevel === void 0) { + activeMipmapLevel = 0; + } + + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; + + if (renderTarget && properties.get(renderTarget).__webglFramebuffer === undefined) { + textures.setupRenderTarget(renderTarget); + } + + var framebuffer = _framebuffer; + var isCube = false; + var isRenderTarget3D = false; + + if (renderTarget) { + var texture = renderTarget.texture; + + if (texture.isDataTexture3D || texture.isDataTexture2DArray) { + isRenderTarget3D = true; + } + + var __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; + + if (renderTarget.isWebGLCubeRenderTarget) { + framebuffer = __webglFramebuffer[activeCubeFace]; + isCube = true; + } else if (renderTarget.isWebGLMultisampleRenderTarget) { + framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; + } else { + framebuffer = __webglFramebuffer; + } + + _currentViewport.copy(renderTarget.viewport); + + _currentScissor.copy(renderTarget.scissor); + + _currentScissorTest = renderTarget.scissorTest; + } else { + _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); + + _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); + + _currentScissorTest = _scissorTest; + } + + if (_currentFramebuffer !== framebuffer) { + _gl.bindFramebuffer(36160, framebuffer); + + _currentFramebuffer = framebuffer; + } + + state.viewport(_currentViewport); + state.scissor(_currentScissor); + state.setScissorTest(_currentScissorTest); + + if (isCube) { + var textureProperties = properties.get(renderTarget.texture); + + _gl.framebufferTexture2D(36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); + } else if (isRenderTarget3D) { + var _textureProperties = properties.get(renderTarget.texture); + + var layer = activeCubeFace || 0; + + _gl.framebufferTextureLayer(36160, 36064, _textureProperties.__webglTexture, activeMipmapLevel || 0, layer); + } + }; + + this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { + if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { + console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.'); + return; + } + + var framebuffer = properties.get(renderTarget).__webglFramebuffer; + + if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { + framebuffer = framebuffer[activeCubeFaceIndex]; + } + + if (framebuffer) { + var restore = false; + + if (framebuffer !== _currentFramebuffer) { + _gl.bindFramebuffer(36160, framebuffer); + + restore = true; + } + + try { + var texture = renderTarget.texture; + var textureFormat = texture.format; + var textureType = texture.type; + + if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(35739)) { + console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.'); + return; + } + + var halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has('EXT_color_buffer_half_float') || capabilities.isWebGL2 && extensions.has('EXT_color_buffer_float')); + + if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(35738) && // Edge and Chrome Mac < 52 (#9513) + !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has('OES_texture_float') || extensions.has('WEBGL_color_buffer_float'))) && // Chrome Mac >= 52 and Firefox + !halfFloatSupportedByExt) { + console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.'); + return; + } + + if (_gl.checkFramebufferStatus(36160) === 36053) { + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { + _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); + } + } else { + console.error('THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.'); + } + } finally { + if (restore) { + _gl.bindFramebuffer(36160, _currentFramebuffer); + } + } + } + }; + + this.copyFramebufferToTexture = function (position, texture, level) { + if (level === void 0) { + level = 0; + } + + var levelScale = Math.pow(2, -level); + var width = Math.floor(texture.image.width * levelScale); + var height = Math.floor(texture.image.height * levelScale); + var glFormat = utils.convert(texture.format); + textures.setTexture2D(texture, 0); + + _gl.copyTexImage2D(3553, level, glFormat, position.x, position.y, width, height, 0); + + state.unbindTexture(); + }; + + this.copyTextureToTexture = function (position, srcTexture, dstTexture, level) { + if (level === void 0) { + level = 0; + } + + var width = srcTexture.image.width; + var height = srcTexture.image.height; + var glFormat = utils.convert(dstTexture.format); + var glType = utils.convert(dstTexture.type); + textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei + // parameters, make sure they are correct for the dstTexture + + _gl.pixelStorei(37440, dstTexture.flipY); + + _gl.pixelStorei(37441, dstTexture.premultiplyAlpha); + + _gl.pixelStorei(3317, dstTexture.unpackAlignment); + + if (srcTexture.isDataTexture) { + _gl.texSubImage2D(3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); + } else { + if (srcTexture.isCompressedTexture) { + _gl.compressedTexSubImage2D(3553, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); + } else { + _gl.texSubImage2D(3553, level, position.x, position.y, glFormat, glType, srcTexture.image); + } + } // Generate mipmaps only when copying level 0 + + + if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(3553); + state.unbindTexture(); + }; + + this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level) { + if (level === void 0) { + level = 0; + } + + if (_this.isWebGL1Renderer) { + console.warn('THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.'); + return; + } + + var _srcTexture$image = srcTexture.image, + width = _srcTexture$image.width, + height = _srcTexture$image.height, + data = _srcTexture$image.data; + var glFormat = utils.convert(dstTexture.format); + var glType = utils.convert(dstTexture.type); + var glTarget; + + if (dstTexture.isDataTexture3D) { + textures.setTexture3D(dstTexture, 0); + glTarget = 32879; + } else if (dstTexture.isDataTexture2DArray) { + textures.setTexture2DArray(dstTexture, 0); + glTarget = 35866; + } else { + console.warn('THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.'); + return; + } + + _gl.pixelStorei(37440, dstTexture.flipY); + + _gl.pixelStorei(37441, dstTexture.premultiplyAlpha); + + _gl.pixelStorei(3317, dstTexture.unpackAlignment); + + var unpackRowLen = _gl.getParameter(3314); + + var unpackImageHeight = _gl.getParameter(32878); + + var unpackSkipPixels = _gl.getParameter(3316); + + var unpackSkipRows = _gl.getParameter(3315); + + var unpackSkipImages = _gl.getParameter(32877); + + _gl.pixelStorei(3314, width); + + _gl.pixelStorei(32878, height); + + _gl.pixelStorei(3316, sourceBox.min.x); + + _gl.pixelStorei(3315, sourceBox.min.y); + + _gl.pixelStorei(32877, sourceBox.min.z); + + _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, sourceBox.max.x - sourceBox.min.x + 1, sourceBox.max.y - sourceBox.min.y + 1, sourceBox.max.z - sourceBox.min.z + 1, glFormat, glType, data); + + _gl.pixelStorei(3314, unpackRowLen); + + _gl.pixelStorei(32878, unpackImageHeight); + + _gl.pixelStorei(3316, unpackSkipPixels); + + _gl.pixelStorei(3315, unpackSkipRows); + + _gl.pixelStorei(32877, unpackSkipImages); // Generate mipmaps only when copying level 0 + + + if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); + state.unbindTexture(); + }; + + this.initTexture = function (texture) { + textures.setTexture2D(texture, 0); + state.unbindTexture(); + }; + + this.resetState = function () { + state.reset(); + bindingStates.reset(); + }; + + if (typeof __THREE_DEVTOOLS__ !== 'undefined') { + __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { + detail: this + })); // eslint-disable-line no-undef + + } + } + + var WebGL1Renderer = /*#__PURE__*/function (_WebGLRenderer) { + _inheritsLoose(WebGL1Renderer, _WebGLRenderer); + + function WebGL1Renderer() { + return _WebGLRenderer.apply(this, arguments) || this; + } + + return WebGL1Renderer; + }(WebGLRenderer); + + WebGL1Renderer.prototype.isWebGL1Renderer = true; + + var FogExp2 = /*#__PURE__*/function () { + function FogExp2(color, density) { + this.name = ''; + this.color = new Color(color); + this.density = density !== undefined ? density : 0.00025; + } + + var _proto = FogExp2.prototype; + + _proto.clone = function clone() { + return new FogExp2(this.color, this.density); + }; + + _proto.toJSON = function toJSON() + /* meta */ + { + return { + type: 'FogExp2', + color: this.color.getHex(), + density: this.density + }; + }; + + return FogExp2; + }(); + + FogExp2.prototype.isFogExp2 = true; + + var Fog = /*#__PURE__*/function () { + function Fog(color, near, far) { + this.name = ''; + this.color = new Color(color); + this.near = near !== undefined ? near : 1; + this.far = far !== undefined ? far : 1000; + } + + var _proto = Fog.prototype; + + _proto.clone = function clone() { + return new Fog(this.color, this.near, this.far); + }; + + _proto.toJSON = function toJSON() + /* meta */ + { + return { + type: 'Fog', + color: this.color.getHex(), + near: this.near, + far: this.far + }; + }; + + return Fog; + }(); + + Fog.prototype.isFog = true; + + var Scene = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(Scene, _Object3D); + + function Scene() { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'Scene'; + _this.background = null; + _this.environment = null; + _this.fog = null; + _this.overrideMaterial = null; + _this.autoUpdate = true; // checked by the renderer + + if (typeof __THREE_DEVTOOLS__ !== 'undefined') { + __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { + detail: _assertThisInitialized(_this) + })); // eslint-disable-line no-undef + + } + + return _this; + } + + var _proto = Scene.prototype; + + _proto.copy = function copy(source, recursive) { + _Object3D.prototype.copy.call(this, source, recursive); + + if (source.background !== null) this.background = source.background.clone(); + if (source.environment !== null) this.environment = source.environment.clone(); + if (source.fog !== null) this.fog = source.fog.clone(); + if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; + return this; + }; + + _proto.toJSON = function toJSON(meta) { + var data = _Object3D.prototype.toJSON.call(this, meta); + + if (this.background !== null) data.object.background = this.background.toJSON(meta); + if (this.environment !== null) data.object.environment = this.environment.toJSON(meta); + if (this.fog !== null) data.object.fog = this.fog.toJSON(); + return data; + }; + + return Scene; + }(Object3D); + + Scene.prototype.isScene = true; + + function InterleavedBuffer(array, stride) { + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; + this.usage = StaticDrawUsage; + this.updateRange = { + offset: 0, + count: -1 + }; + this.version = 0; + this.uuid = MathUtils.generateUUID(); + } + + Object.defineProperty(InterleavedBuffer.prototype, 'needsUpdate', { + set: function set(value) { + if (value === true) this.version++; + } + }); + Object.assign(InterleavedBuffer.prototype, { + isInterleavedBuffer: true, + onUploadCallback: function onUploadCallback() {}, + setUsage: function setUsage(value) { + this.usage = value; + return this; + }, + copy: function copy(source) { + this.array = new source.array.constructor(source.array); + this.count = source.count; + this.stride = source.stride; + this.usage = source.usage; + return this; + }, + copyAt: function copyAt(index1, attribute, index2) { + index1 *= this.stride; + index2 *= attribute.stride; + + for (var i = 0, l = this.stride; i < l; i++) { + this.array[index1 + i] = attribute.array[index2 + i]; + } + + return this; + }, + set: function set(value, offset) { + if (offset === void 0) { + offset = 0; + } + + this.array.set(value, offset); + return this; + }, + clone: function clone(data) { + if (data.arrayBuffers === undefined) { + data.arrayBuffers = {}; + } + + if (this.array.buffer._uuid === undefined) { + this.array.buffer._uuid = MathUtils.generateUUID(); + } + + if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { + data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; + } + + var array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); + var ib = new InterleavedBuffer(array, this.stride); + ib.setUsage(this.usage); + return ib; + }, + onUpload: function onUpload(callback) { + this.onUploadCallback = callback; + return this; + }, + toJSON: function toJSON(data) { + if (data.arrayBuffers === undefined) { + data.arrayBuffers = {}; + } // generate UUID for array buffer if necessary + + + if (this.array.buffer._uuid === undefined) { + this.array.buffer._uuid = MathUtils.generateUUID(); + } + + if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { + data.arrayBuffers[this.array.buffer._uuid] = Array.prototype.slice.call(new Uint32Array(this.array.buffer)); + } // + + + return { + uuid: this.uuid, + buffer: this.array.buffer._uuid, + type: this.array.constructor.name, + stride: this.stride + }; + } + }); + + var _vector$6 = new Vector3(); + + function InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, normalized) { + this.name = ''; + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; + this.normalized = normalized === true; + } + + Object.defineProperties(InterleavedBufferAttribute.prototype, { + count: { + get: function get() { + return this.data.count; + } + }, + array: { + get: function get() { + return this.data.array; + } + }, + needsUpdate: { + set: function set(value) { + this.data.needsUpdate = value; + } + } + }); + Object.assign(InterleavedBufferAttribute.prototype, { + isInterleavedBufferAttribute: true, + applyMatrix4: function applyMatrix4(m) { + for (var i = 0, l = this.data.count; i < l; i++) { + _vector$6.x = this.getX(i); + _vector$6.y = this.getY(i); + _vector$6.z = this.getZ(i); + + _vector$6.applyMatrix4(m); + + this.setXYZ(i, _vector$6.x, _vector$6.y, _vector$6.z); + } + + return this; + }, + setX: function setX(index, x) { + this.data.array[index * this.data.stride + this.offset] = x; + return this; + }, + setY: function setY(index, y) { + this.data.array[index * this.data.stride + this.offset + 1] = y; + return this; + }, + setZ: function setZ(index, z) { + this.data.array[index * this.data.stride + this.offset + 2] = z; + return this; + }, + setW: function setW(index, w) { + this.data.array[index * this.data.stride + this.offset + 3] = w; + return this; + }, + getX: function getX(index) { + return this.data.array[index * this.data.stride + this.offset]; + }, + getY: function getY(index) { + return this.data.array[index * this.data.stride + this.offset + 1]; + }, + getZ: function getZ(index) { + return this.data.array[index * this.data.stride + this.offset + 2]; + }, + getW: function getW(index) { + return this.data.array[index * this.data.stride + this.offset + 3]; + }, + setXY: function setXY(index, x, y) { + index = index * this.data.stride + this.offset; + this.data.array[index + 0] = x; + this.data.array[index + 1] = y; + return this; + }, + setXYZ: function setXYZ(index, x, y, z) { + index = index * this.data.stride + this.offset; + this.data.array[index + 0] = x; + this.data.array[index + 1] = y; + this.data.array[index + 2] = z; + return this; + }, + setXYZW: function setXYZW(index, x, y, z, w) { + index = index * this.data.stride + this.offset; + this.data.array[index + 0] = x; + this.data.array[index + 1] = y; + this.data.array[index + 2] = z; + this.data.array[index + 3] = w; + return this; + }, + clone: function clone(data) { + if (data === undefined) { + console.log('THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.'); + var array = []; + + for (var i = 0; i < this.count; i++) { + var index = i * this.data.stride + this.offset; + + for (var j = 0; j < this.itemSize; j++) { + array.push(this.data.array[index + j]); + } + } + + return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); + } else { + if (data.interleavedBuffers === undefined) { + data.interleavedBuffers = {}; + } + + if (data.interleavedBuffers[this.data.uuid] === undefined) { + data.interleavedBuffers[this.data.uuid] = this.data.clone(data); + } + + return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); + } + }, + toJSON: function toJSON(data) { + if (data === undefined) { + console.log('THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.'); + var array = []; + + for (var i = 0; i < this.count; i++) { + var index = i * this.data.stride + this.offset; + + for (var j = 0; j < this.itemSize; j++) { + array.push(this.data.array[index + j]); + } + } // deinterleave data and save it as an ordinary buffer attribute for now + + + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: array, + normalized: this.normalized + }; + } else { + // save as true interlaved attribtue + if (data.interleavedBuffers === undefined) { + data.interleavedBuffers = {}; + } + + if (data.interleavedBuffers[this.data.uuid] === undefined) { + data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); + } + + return { + isInterleavedBufferAttribute: true, + itemSize: this.itemSize, + data: this.data.uuid, + offset: this.offset, + normalized: this.normalized + }; + } + } + }); + + /** + * parameters = { + * color: , + * map: new THREE.Texture( ), + * alphaMap: new THREE.Texture( ), + * rotation: , + * sizeAttenuation: + * } + */ + + var SpriteMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(SpriteMaterial, _Material); + + function SpriteMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'SpriteMaterial'; + _this.color = new Color(0xffffff); + _this.map = null; + _this.alphaMap = null; + _this.rotation = 0; + _this.sizeAttenuation = true; + _this.transparent = true; + + _this.setValues(parameters); + + return _this; + } + + var _proto = SpriteMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.map = source.map; + this.alphaMap = source.alphaMap; + this.rotation = source.rotation; + this.sizeAttenuation = source.sizeAttenuation; + return this; + }; + + return SpriteMaterial; + }(Material); + + SpriteMaterial.prototype.isSpriteMaterial = true; + + var _geometry; + + var _intersectPoint = /*@__PURE__*/new Vector3(); + + var _worldScale = /*@__PURE__*/new Vector3(); + + var _mvPosition = /*@__PURE__*/new Vector3(); + + var _alignedPosition = /*@__PURE__*/new Vector2(); + + var _rotatedPosition = /*@__PURE__*/new Vector2(); + + var _viewWorldMatrix = /*@__PURE__*/new Matrix4(); + + var _vA$1 = /*@__PURE__*/new Vector3(); + + var _vB$1 = /*@__PURE__*/new Vector3(); + + var _vC$1 = /*@__PURE__*/new Vector3(); + + var _uvA$1 = /*@__PURE__*/new Vector2(); + + var _uvB$1 = /*@__PURE__*/new Vector2(); + + var _uvC$1 = /*@__PURE__*/new Vector2(); + + var Sprite = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(Sprite, _Object3D); + + function Sprite(material) { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'Sprite'; + + if (_geometry === undefined) { + _geometry = new BufferGeometry(); + var float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); + var interleavedBuffer = new InterleavedBuffer(float32Array, 5); + + _geometry.setIndex([0, 1, 2, 0, 2, 3]); + + _geometry.setAttribute('position', new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); + + _geometry.setAttribute('uv', new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); + } + + _this.geometry = _geometry; + _this.material = material !== undefined ? material : new SpriteMaterial(); + _this.center = new Vector2(0.5, 0.5); + return _this; + } + + var _proto = Sprite.prototype; + + _proto.raycast = function raycast(raycaster, intersects) { + if (raycaster.camera === null) { + console.error('THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.'); + } + + _worldScale.setFromMatrixScale(this.matrixWorld); + + _viewWorldMatrix.copy(raycaster.camera.matrixWorld); + + this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); + + _mvPosition.setFromMatrixPosition(this.modelViewMatrix); + + if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { + _worldScale.multiplyScalar(-_mvPosition.z); + } + + var rotation = this.material.rotation; + var sin, cos; + + if (rotation !== 0) { + cos = Math.cos(rotation); + sin = Math.sin(rotation); + } + + var center = this.center; + transformVertex(_vA$1.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); + transformVertex(_vB$1.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); + transformVertex(_vC$1.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); + + _uvA$1.set(0, 0); + + _uvB$1.set(1, 0); + + _uvC$1.set(1, 1); // check first triangle + + + var intersect = raycaster.ray.intersectTriangle(_vA$1, _vB$1, _vC$1, false, _intersectPoint); + + if (intersect === null) { + // check second triangle + transformVertex(_vB$1.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); + + _uvB$1.set(0, 1); + + intersect = raycaster.ray.intersectTriangle(_vA$1, _vC$1, _vB$1, false, _intersectPoint); + + if (intersect === null) { + return; + } + } + + var distance = raycaster.ray.origin.distanceTo(_intersectPoint); + if (distance < raycaster.near || distance > raycaster.far) return; + intersects.push({ + distance: distance, + point: _intersectPoint.clone(), + uv: Triangle.getUV(_intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()), + face: null, + object: this + }); + }; + + _proto.copy = function copy(source) { + _Object3D.prototype.copy.call(this, source); + + if (source.center !== undefined) this.center.copy(source.center); + this.material = source.material; + return this; + }; + + return Sprite; + }(Object3D); + + Sprite.prototype.isSprite = true; + + function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { + // compute position in camera space + _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero + + + if (sin !== undefined) { + _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; + _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; + } else { + _rotatedPosition.copy(_alignedPosition); + } + + vertexPosition.copy(mvPosition); + vertexPosition.x += _rotatedPosition.x; + vertexPosition.y += _rotatedPosition.y; // transform to world space + + vertexPosition.applyMatrix4(_viewWorldMatrix); + } + + var _v1$4 = /*@__PURE__*/new Vector3(); + + var _v2$2 = /*@__PURE__*/new Vector3(); + + var LOD = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(LOD, _Object3D); + + function LOD() { + var _this; + + _this = _Object3D.call(this) || this; + _this._currentLevel = 0; + _this.type = 'LOD'; + Object.defineProperties(_assertThisInitialized(_this), { + levels: { + enumerable: true, + value: [] + }, + isLOD: { + value: true + } + }); + _this.autoUpdate = true; + return _this; + } + + var _proto = LOD.prototype; + + _proto.copy = function copy(source) { + _Object3D.prototype.copy.call(this, source, false); + + var levels = source.levels; + + for (var i = 0, l = levels.length; i < l; i++) { + var level = levels[i]; + this.addLevel(level.object.clone(), level.distance); + } + + this.autoUpdate = source.autoUpdate; + return this; + }; + + _proto.addLevel = function addLevel(object, distance) { + if (distance === void 0) { + distance = 0; + } + + distance = Math.abs(distance); + var levels = this.levels; + var l; + + for (l = 0; l < levels.length; l++) { + if (distance < levels[l].distance) { + break; + } + } + + levels.splice(l, 0, { + distance: distance, + object: object + }); + this.add(object); + return this; + }; + + _proto.getCurrentLevel = function getCurrentLevel() { + return this._currentLevel; + }; + + _proto.getObjectForDistance = function getObjectForDistance(distance) { + var levels = this.levels; + + if (levels.length > 0) { + var i, l; + + for (i = 1, l = levels.length; i < l; i++) { + if (distance < levels[i].distance) { + break; + } + } + + return levels[i - 1].object; + } + + return null; + }; + + _proto.raycast = function raycast(raycaster, intersects) { + var levels = this.levels; + + if (levels.length > 0) { + _v1$4.setFromMatrixPosition(this.matrixWorld); + + var distance = raycaster.ray.origin.distanceTo(_v1$4); + this.getObjectForDistance(distance).raycast(raycaster, intersects); + } + }; + + _proto.update = function update(camera) { + var levels = this.levels; + + if (levels.length > 1) { + _v1$4.setFromMatrixPosition(camera.matrixWorld); + + _v2$2.setFromMatrixPosition(this.matrixWorld); + + var distance = _v1$4.distanceTo(_v2$2) / camera.zoom; + levels[0].object.visible = true; + var i, l; + + for (i = 1, l = levels.length; i < l; i++) { + if (distance >= levels[i].distance) { + levels[i - 1].object.visible = false; + levels[i].object.visible = true; + } else { + break; + } + } + + this._currentLevel = i - 1; + + for (; i < l; i++) { + levels[i].object.visible = false; + } + } + }; + + _proto.toJSON = function toJSON(meta) { + var data = _Object3D.prototype.toJSON.call(this, meta); + + if (this.autoUpdate === false) data.object.autoUpdate = false; + data.object.levels = []; + var levels = this.levels; + + for (var i = 0, l = levels.length; i < l; i++) { + var level = levels[i]; + data.object.levels.push({ + object: level.object.uuid, + distance: level.distance + }); + } + + return data; + }; + + return LOD; + }(Object3D); + + var _basePosition = new Vector3(); + + var _skinIndex = new Vector4(); + + var _skinWeight = new Vector4(); + + var _vector$7 = new Vector3(); + + var _matrix$1 = new Matrix4(); + + function SkinnedMesh(geometry, material) { + Mesh.call(this, geometry, material); + this.type = 'SkinnedMesh'; + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); + } + + SkinnedMesh.prototype = Object.assign(Object.create(Mesh.prototype), { + constructor: SkinnedMesh, + isSkinnedMesh: true, + copy: function copy(source) { + Mesh.prototype.copy.call(this, source); + this.bindMode = source.bindMode; + this.bindMatrix.copy(source.bindMatrix); + this.bindMatrixInverse.copy(source.bindMatrixInverse); + this.skeleton = source.skeleton; + return this; + }, + bind: function bind(skeleton, bindMatrix) { + this.skeleton = skeleton; + + if (bindMatrix === undefined) { + this.updateMatrixWorld(true); + this.skeleton.calculateInverses(); + bindMatrix = this.matrixWorld; + } + + this.bindMatrix.copy(bindMatrix); + this.bindMatrixInverse.copy(bindMatrix).invert(); + }, + pose: function pose() { + this.skeleton.pose(); + }, + normalizeSkinWeights: function normalizeSkinWeights() { + var vector = new Vector4(); + var skinWeight = this.geometry.attributes.skinWeight; + + for (var i = 0, l = skinWeight.count; i < l; i++) { + vector.x = skinWeight.getX(i); + vector.y = skinWeight.getY(i); + vector.z = skinWeight.getZ(i); + vector.w = skinWeight.getW(i); + var scale = 1.0 / vector.manhattanLength(); + + if (scale !== Infinity) { + vector.multiplyScalar(scale); + } else { + vector.set(1, 0, 0, 0); // do something reasonable + } + + skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); + } + }, + updateMatrixWorld: function updateMatrixWorld(force) { + Mesh.prototype.updateMatrixWorld.call(this, force); + + if (this.bindMode === 'attached') { + this.bindMatrixInverse.copy(this.matrixWorld).invert(); + } else if (this.bindMode === 'detached') { + this.bindMatrixInverse.copy(this.bindMatrix).invert(); + } else { + console.warn('THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode); + } + }, + boneTransform: function boneTransform(index, target) { + var skeleton = this.skeleton; + var geometry = this.geometry; + + _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); + + _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); + + _basePosition.fromBufferAttribute(geometry.attributes.position, index).applyMatrix4(this.bindMatrix); + + target.set(0, 0, 0); + + for (var i = 0; i < 4; i++) { + var weight = _skinWeight.getComponent(i); + + if (weight !== 0) { + var boneIndex = _skinIndex.getComponent(i); + + _matrix$1.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); + + target.addScaledVector(_vector$7.copy(_basePosition).applyMatrix4(_matrix$1), weight); + } + } + + return target.applyMatrix4(this.bindMatrixInverse); + } + }); + + function Bone() { + Object3D.call(this); + this.type = 'Bone'; + } + + Bone.prototype = Object.assign(Object.create(Object3D.prototype), { + constructor: Bone, + isBone: true + }); + + var _offsetMatrix = /*@__PURE__*/new Matrix4(); + + var _identityMatrix = /*@__PURE__*/new Matrix4(); + + var Skeleton = /*#__PURE__*/function () { + function Skeleton(bones, boneInverses) { + if (bones === void 0) { + bones = []; + } + + if (boneInverses === void 0) { + boneInverses = []; + } + + this.uuid = MathUtils.generateUUID(); + this.bones = bones.slice(0); + this.boneInverses = boneInverses; + this.boneMatrices = null; + this.boneTexture = null; + this.boneTextureSize = 0; + this.frame = -1; + this.init(); + } + + var _proto = Skeleton.prototype; + + _proto.init = function init() { + var bones = this.bones; + var boneInverses = this.boneInverses; + this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary + + if (boneInverses.length === 0) { + this.calculateInverses(); + } else { + // handle special case + if (bones.length !== boneInverses.length) { + console.warn('THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.'); + this.boneInverses = []; + + for (var i = 0, il = this.bones.length; i < il; i++) { + this.boneInverses.push(new Matrix4()); + } + } + } + }; + + _proto.calculateInverses = function calculateInverses() { + this.boneInverses.length = 0; + + for (var i = 0, il = this.bones.length; i < il; i++) { + var inverse = new Matrix4(); + + if (this.bones[i]) { + inverse.copy(this.bones[i].matrixWorld).invert(); + } + + this.boneInverses.push(inverse); + } + }; + + _proto.pose = function pose() { + // recover the bind-time world matrices + for (var i = 0, il = this.bones.length; i < il; i++) { + var bone = this.bones[i]; + + if (bone) { + bone.matrixWorld.copy(this.boneInverses[i]).invert(); + } + } // compute the local matrices, positions, rotations and scales + + + for (var _i = 0, _il = this.bones.length; _i < _il; _i++) { + var _bone = this.bones[_i]; + + if (_bone) { + if (_bone.parent && _bone.parent.isBone) { + _bone.matrix.copy(_bone.parent.matrixWorld).invert(); + + _bone.matrix.multiply(_bone.matrixWorld); + } else { + _bone.matrix.copy(_bone.matrixWorld); + } + + _bone.matrix.decompose(_bone.position, _bone.quaternion, _bone.scale); + } + } + }; + + _proto.update = function update() { + var bones = this.bones; + var boneInverses = this.boneInverses; + var boneMatrices = this.boneMatrices; + var boneTexture = this.boneTexture; // flatten bone matrices to array + + for (var i = 0, il = bones.length; i < il; i++) { + // compute the offset between the current and the original transform + var matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; + + _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); + + _offsetMatrix.toArray(boneMatrices, i * 16); + } + + if (boneTexture !== null) { + boneTexture.needsUpdate = true; + } + }; + + _proto.clone = function clone() { + return new Skeleton(this.bones, this.boneInverses); + }; + + _proto.getBoneByName = function getBoneByName(name) { + for (var i = 0, il = this.bones.length; i < il; i++) { + var bone = this.bones[i]; + + if (bone.name === name) { + return bone; + } + } + + return undefined; + }; + + _proto.dispose = function dispose() { + if (this.boneTexture !== null) { + this.boneTexture.dispose(); + this.boneTexture = null; + } + }; + + _proto.fromJSON = function fromJSON(json, bones) { + this.uuid = json.uuid; + + for (var i = 0, l = json.bones.length; i < l; i++) { + var uuid = json.bones[i]; + var bone = bones[uuid]; + + if (bone === undefined) { + console.warn('THREE.Skeleton: No bone found with UUID:', uuid); + bone = new Bone(); + } + + this.bones.push(bone); + this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); + } + + this.init(); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = { + metadata: { + version: 4.5, + type: 'Skeleton', + generator: 'Skeleton.toJSON' + }, + bones: [], + boneInverses: [] + }; + data.uuid = this.uuid; + var bones = this.bones; + var boneInverses = this.boneInverses; + + for (var i = 0, l = bones.length; i < l; i++) { + var bone = bones[i]; + data.bones.push(bone.uuid); + var boneInverse = boneInverses[i]; + data.boneInverses.push(boneInverse.toArray()); + } + + return data; + }; + + return Skeleton; + }(); + + var _instanceLocalMatrix = new Matrix4(); + + var _instanceWorldMatrix = new Matrix4(); + + var _instanceIntersects = []; + + var _mesh = new Mesh(); + + function InstancedMesh(geometry, material, count) { + Mesh.call(this, geometry, material); + this.instanceMatrix = new BufferAttribute(new Float32Array(count * 16), 16); + this.instanceColor = null; + this.count = count; + this.frustumCulled = false; + } + + InstancedMesh.prototype = Object.assign(Object.create(Mesh.prototype), { + constructor: InstancedMesh, + isInstancedMesh: true, + copy: function copy(source) { + Mesh.prototype.copy.call(this, source); + this.instanceMatrix.copy(source.instanceMatrix); + if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); + this.count = source.count; + return this; + }, + getColorAt: function getColorAt(index, color) { + color.fromArray(this.instanceColor.array, index * 3); + }, + getMatrixAt: function getMatrixAt(index, matrix) { + matrix.fromArray(this.instanceMatrix.array, index * 16); + }, + raycast: function raycast(raycaster, intersects) { + var matrixWorld = this.matrixWorld; + var raycastTimes = this.count; + _mesh.geometry = this.geometry; + _mesh.material = this.material; + if (_mesh.material === undefined) return; + + for (var instanceId = 0; instanceId < raycastTimes; instanceId++) { + // calculate the world matrix for each instance + this.getMatrixAt(instanceId, _instanceLocalMatrix); + + _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance + + + _mesh.matrixWorld = _instanceWorldMatrix; + + _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast + + + for (var i = 0, l = _instanceIntersects.length; i < l; i++) { + var intersect = _instanceIntersects[i]; + intersect.instanceId = instanceId; + intersect.object = this; + intersects.push(intersect); + } + + _instanceIntersects.length = 0; + } + }, + setColorAt: function setColorAt(index, color) { + if (this.instanceColor === null) { + this.instanceColor = new BufferAttribute(new Float32Array(this.count * 3), 3); + } + + color.toArray(this.instanceColor.array, index * 3); + }, + setMatrixAt: function setMatrixAt(index, matrix) { + matrix.toArray(this.instanceMatrix.array, index * 16); + }, + updateMorphTargets: function updateMorphTargets() {}, + dispose: function dispose() { + this.dispatchEvent({ + type: 'dispose' + }); + } + }); + + /** + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round" + * } + */ + + var LineBasicMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(LineBasicMaterial, _Material); + + function LineBasicMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'LineBasicMaterial'; + _this.color = new Color(0xffffff); + _this.linewidth = 1; + _this.linecap = 'round'; + _this.linejoin = 'round'; + _this.morphTargets = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = LineBasicMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + this.morphTargets = source.morphTargets; + return this; + }; + + return LineBasicMaterial; + }(Material); + + LineBasicMaterial.prototype.isLineBasicMaterial = true; + + var _start = new Vector3(); + + var _end = new Vector3(); + + var _inverseMatrix$1 = new Matrix4(); + + var _ray$1 = new Ray(); + + var _sphere$2 = new Sphere(); + + function Line(geometry, material) { + if (geometry === void 0) { + geometry = new BufferGeometry(); + } + + if (material === void 0) { + material = new LineBasicMaterial(); + } + + Object3D.call(this); + this.type = 'Line'; + this.geometry = geometry; + this.material = material; + this.updateMorphTargets(); + } + + Line.prototype = Object.assign(Object.create(Object3D.prototype), { + constructor: Line, + isLine: true, + copy: function copy(source) { + Object3D.prototype.copy.call(this, source); + this.material = source.material; + this.geometry = source.geometry; + return this; + }, + computeLineDistances: function computeLineDistances() { + var geometry = this.geometry; + + if (geometry.isBufferGeometry) { + // we assume non-indexed geometry + if (geometry.index === null) { + var positionAttribute = geometry.attributes.position; + var lineDistances = [0]; + + for (var i = 1, l = positionAttribute.count; i < l; i++) { + _start.fromBufferAttribute(positionAttribute, i - 1); + + _end.fromBufferAttribute(positionAttribute, i); + + lineDistances[i] = lineDistances[i - 1]; + lineDistances[i] += _start.distanceTo(_end); + } + + geometry.setAttribute('lineDistance', new Float32BufferAttribute(lineDistances, 1)); + } else { + console.warn('THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.'); + } + } else if (geometry.isGeometry) { + console.error('THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + + return this; + }, + raycast: function raycast(raycaster, intersects) { + var geometry = this.geometry; + var matrixWorld = this.matrixWorld; + var threshold = raycaster.params.Line.threshold; // Checking boundingSphere distance to ray + + if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); + + _sphere$2.copy(geometry.boundingSphere); + + _sphere$2.applyMatrix4(matrixWorld); + + _sphere$2.radius += threshold; + if (raycaster.ray.intersectsSphere(_sphere$2) === false) return; // + + _inverseMatrix$1.copy(matrixWorld).invert(); + + _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); + + var localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); + var localThresholdSq = localThreshold * localThreshold; + var vStart = new Vector3(); + var vEnd = new Vector3(); + var interSegment = new Vector3(); + var interRay = new Vector3(); + var step = this.isLineSegments ? 2 : 1; + + if (geometry.isBufferGeometry) { + var index = geometry.index; + var attributes = geometry.attributes; + var positionAttribute = attributes.position; + + if (index !== null) { + var indices = index.array; + + for (var i = 0, l = indices.length - 1; i < l; i += step) { + var a = indices[i]; + var b = indices[i + 1]; + vStart.fromBufferAttribute(positionAttribute, a); + vEnd.fromBufferAttribute(positionAttribute, b); + + var distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); + + if (distSq > localThresholdSq) continue; + interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo(interRay); + if (distance < raycaster.near || distance > raycaster.far) continue; + intersects.push({ + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4(this.matrixWorld), + index: i, + face: null, + faceIndex: null, + object: this + }); + } + } else { + for (var _i = 0, _l = positionAttribute.count - 1; _i < _l; _i += step) { + vStart.fromBufferAttribute(positionAttribute, _i); + vEnd.fromBufferAttribute(positionAttribute, _i + 1); + + var _distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); + + if (_distSq > localThresholdSq) continue; + interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation + + var _distance = raycaster.ray.origin.distanceTo(interRay); + + if (_distance < raycaster.near || _distance > raycaster.far) continue; + intersects.push({ + distance: _distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4(this.matrixWorld), + index: _i, + face: null, + faceIndex: null, + object: this + }); + } + } + } else if (geometry.isGeometry) { + console.error('THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + }, + updateMorphTargets: function updateMorphTargets() { + var geometry = this.geometry; + + if (geometry.isBufferGeometry) { + var morphAttributes = geometry.morphAttributes; + var keys = Object.keys(morphAttributes); + + if (keys.length > 0) { + var morphAttribute = morphAttributes[keys[0]]; + + if (morphAttribute !== undefined) { + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for (var m = 0, ml = morphAttribute.length; m < ml; m++) { + var name = morphAttribute[m].name || String(m); + this.morphTargetInfluences.push(0); + this.morphTargetDictionary[name] = m; + } + } + } + } else { + var morphTargets = geometry.morphTargets; + + if (morphTargets !== undefined && morphTargets.length > 0) { + console.error('THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + } + } + }); + + var _start$1 = new Vector3(); + + var _end$1 = new Vector3(); + + function LineSegments(geometry, material) { + Line.call(this, geometry, material); + this.type = 'LineSegments'; + } + + LineSegments.prototype = Object.assign(Object.create(Line.prototype), { + constructor: LineSegments, + isLineSegments: true, + computeLineDistances: function computeLineDistances() { + var geometry = this.geometry; + + if (geometry.isBufferGeometry) { + // we assume non-indexed geometry + if (geometry.index === null) { + var positionAttribute = geometry.attributes.position; + var lineDistances = []; + + for (var i = 0, l = positionAttribute.count; i < l; i += 2) { + _start$1.fromBufferAttribute(positionAttribute, i); + + _end$1.fromBufferAttribute(positionAttribute, i + 1); + + lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; + lineDistances[i + 1] = lineDistances[i] + _start$1.distanceTo(_end$1); + } + + geometry.setAttribute('lineDistance', new Float32BufferAttribute(lineDistances, 1)); + } else { + console.warn('THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.'); + } + } else if (geometry.isGeometry) { + console.error('THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + + return this; + } + }); + + var LineLoop = /*#__PURE__*/function (_Line) { + _inheritsLoose(LineLoop, _Line); + + function LineLoop(geometry, material) { + var _this; + + _this = _Line.call(this, geometry, material) || this; + _this.type = 'LineLoop'; + return _this; + } + + return LineLoop; + }(Line); + + LineLoop.prototype.isLineLoop = true; + + /** + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * alphaMap: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: + * + * morphTargets: + * } + */ + + var PointsMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(PointsMaterial, _Material); + + function PointsMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'PointsMaterial'; + _this.color = new Color(0xffffff); + _this.map = null; + _this.alphaMap = null; + _this.size = 1; + _this.sizeAttenuation = true; + _this.morphTargets = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = PointsMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.map = source.map; + this.alphaMap = source.alphaMap; + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; + this.morphTargets = source.morphTargets; + return this; + }; + + return PointsMaterial; + }(Material); + + PointsMaterial.prototype.isPointsMaterial = true; + + var _inverseMatrix$2 = new Matrix4(); + + var _ray$2 = new Ray(); + + var _sphere$3 = new Sphere(); + + var _position$1 = new Vector3(); + + function Points(geometry, material) { + if (geometry === void 0) { + geometry = new BufferGeometry(); + } + + if (material === void 0) { + material = new PointsMaterial(); + } + + Object3D.call(this); + this.type = 'Points'; + this.geometry = geometry; + this.material = material; + this.updateMorphTargets(); + } + + Points.prototype = Object.assign(Object.create(Object3D.prototype), { + constructor: Points, + isPoints: true, + copy: function copy(source) { + Object3D.prototype.copy.call(this, source); + this.material = source.material; + this.geometry = source.geometry; + return this; + }, + raycast: function raycast(raycaster, intersects) { + var geometry = this.geometry; + var matrixWorld = this.matrixWorld; + var threshold = raycaster.params.Points.threshold; // Checking boundingSphere distance to ray + + if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); + + _sphere$3.copy(geometry.boundingSphere); + + _sphere$3.applyMatrix4(matrixWorld); + + _sphere$3.radius += threshold; + if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // + + _inverseMatrix$2.copy(matrixWorld).invert(); + + _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); + + var localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); + var localThresholdSq = localThreshold * localThreshold; + + if (geometry.isBufferGeometry) { + var index = geometry.index; + var attributes = geometry.attributes; + var positionAttribute = attributes.position; + + if (index !== null) { + var indices = index.array; + + for (var i = 0, il = indices.length; i < il; i++) { + var a = indices[i]; + + _position$1.fromBufferAttribute(positionAttribute, a); + + testPoint(_position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this); + } + } else { + for (var _i = 0, l = positionAttribute.count; _i < l; _i++) { + _position$1.fromBufferAttribute(positionAttribute, _i); + + testPoint(_position$1, _i, localThresholdSq, matrixWorld, raycaster, intersects, this); + } + } + } else { + console.error('THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + }, + updateMorphTargets: function updateMorphTargets() { + var geometry = this.geometry; + + if (geometry.isBufferGeometry) { + var morphAttributes = geometry.morphAttributes; + var keys = Object.keys(morphAttributes); + + if (keys.length > 0) { + var morphAttribute = morphAttributes[keys[0]]; + + if (morphAttribute !== undefined) { + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for (var m = 0, ml = morphAttribute.length; m < ml; m++) { + var name = morphAttribute[m].name || String(m); + this.morphTargetInfluences.push(0); + this.morphTargetDictionary[name] = m; + } + } + } + } else { + var morphTargets = geometry.morphTargets; + + if (morphTargets !== undefined && morphTargets.length > 0) { + console.error('THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.'); + } + } + } + }); + + function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { + var rayPointDistanceSq = _ray$2.distanceSqToPoint(point); + + if (rayPointDistanceSq < localThresholdSq) { + var intersectPoint = new Vector3(); + + _ray$2.closestPointToPoint(point, intersectPoint); + + intersectPoint.applyMatrix4(matrixWorld); + var distance = raycaster.ray.origin.distanceTo(intersectPoint); + if (distance < raycaster.near || distance > raycaster.far) return; + intersects.push({ + distance: distance, + distanceToRay: Math.sqrt(rayPointDistanceSq), + point: intersectPoint, + index: index, + face: null, + object: object + }); + } + } + + var VideoTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(VideoTexture, _Texture); + + function VideoTexture(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { + var _this; + + _this = _Texture.call(this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) || this; + _this.format = format !== undefined ? format : RGBFormat; + _this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; + _this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + _this.generateMipmaps = false; + + var scope = _assertThisInitialized(_this); + + function updateVideo() { + scope.needsUpdate = true; + video.requestVideoFrameCallback(updateVideo); + } + + if ('requestVideoFrameCallback' in video) { + video.requestVideoFrameCallback(updateVideo); + } + + return _this; + } + + var _proto = VideoTexture.prototype; + + _proto.clone = function clone() { + return new this.constructor(this.image).copy(this); + }; + + _proto.update = function update() { + var video = this.image; + var hasVideoFrameCallback = ('requestVideoFrameCallback' in video); + + if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { + this.needsUpdate = true; + } + }; + + return VideoTexture; + }(Texture); + + VideoTexture.prototype.isVideoTexture = true; + + var CompressedTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(CompressedTexture, _Texture); + + function CompressedTexture(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding) { + var _this; + + _this = _Texture.call(this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding) || this; + _this.image = { + width: width, + height: height + }; + _this.mipmaps = mipmaps; // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + _this.flipY = false; // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + _this.generateMipmaps = false; + return _this; + } + + return CompressedTexture; + }(Texture); + + CompressedTexture.prototype.isCompressedTexture = true; + + var CanvasTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(CanvasTexture, _Texture); + + function CanvasTexture(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { + var _this; + + _this = _Texture.call(this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) || this; + _this.needsUpdate = true; + return _this; + } + + return CanvasTexture; + }(Texture); + + CanvasTexture.prototype.isCanvasTexture = true; + + var DepthTexture = /*#__PURE__*/function (_Texture) { + _inheritsLoose(DepthTexture, _Texture); + + function DepthTexture(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { + var _this; + + format = format !== undefined ? format : DepthFormat; + + if (format !== DepthFormat && format !== DepthStencilFormat) { + throw new Error('DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat'); + } + + if (type === undefined && format === DepthFormat) type = UnsignedShortType; + if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; + _this = _Texture.call(this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) || this; + _this.image = { + width: width, + height: height + }; + _this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + _this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + _this.flipY = false; + _this.generateMipmaps = false; + return _this; + } + + return DepthTexture; + }(Texture); + + DepthTexture.prototype.isDepthTexture = true; + + var CircleGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(CircleGeometry, _BufferGeometry); + + function CircleGeometry(radius, segments, thetaStart, thetaLength) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (segments === void 0) { + segments = 8; + } + + if (thetaStart === void 0) { + thetaStart = 0; + } + + if (thetaLength === void 0) { + thetaLength = Math.PI * 2; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'CircleGeometry'; + _this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + segments = Math.max(3, segments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var vertex = new Vector3(); + var uv = new Vector2(); // center point + + vertices.push(0, 0, 0); + normals.push(0, 0, 1); + uvs.push(0.5, 0.5); + + for (var s = 0, i = 3; s <= segments; s++, i += 3) { + var segment = thetaStart + s / segments * thetaLength; // vertex + + vertex.x = radius * Math.cos(segment); + vertex.y = radius * Math.sin(segment); + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + normals.push(0, 0, 1); // uvs + + uv.x = (vertices[i] / radius + 1) / 2; + uv.y = (vertices[i + 1] / radius + 1) / 2; + uvs.push(uv.x, uv.y); + } // indices + + + for (var _i = 1; _i <= segments; _i++) { + indices.push(_i, _i + 1, 0); + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + return _this; + } + + return CircleGeometry; + }(BufferGeometry); + + var CylinderGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(CylinderGeometry, _BufferGeometry); + + function CylinderGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) { + var _this; + + if (radiusTop === void 0) { + radiusTop = 1; + } + + if (radiusBottom === void 0) { + radiusBottom = 1; + } + + if (height === void 0) { + height = 1; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (heightSegments === void 0) { + heightSegments = 1; + } + + if (openEnded === void 0) { + openEnded = false; + } + + if (thetaStart === void 0) { + thetaStart = 0; + } + + if (thetaLength === void 0) { + thetaLength = Math.PI * 2; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'CylinderGeometry'; + _this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + var scope = _assertThisInitialized(_this); + + radialSegments = Math.floor(radialSegments); + heightSegments = Math.floor(heightSegments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var index = 0; + var indexArray = []; + var halfHeight = height / 2; + var groupStart = 0; // generate geometry + + generateTorso(); + + if (openEnded === false) { + if (radiusTop > 0) generateCap(true); + if (radiusBottom > 0) generateCap(false); + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + function generateTorso() { + var normal = new Vector3(); + var vertex = new Vector3(); + var groupCount = 0; // this will be used to calculate the normal + + var slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs + + for (var y = 0; y <= heightSegments; y++) { + var indexRow = []; + var v = y / heightSegments; // calculate the radius of the current row + + var radius = v * (radiusBottom - radiusTop) + radiusTop; + + for (var x = 0; x <= radialSegments; x++) { + var u = x / radialSegments; + var theta = u * thetaLength + thetaStart; + var sinTheta = Math.sin(theta); + var cosTheta = Math.cos(theta); // vertex + + vertex.x = radius * sinTheta; + vertex.y = -v * height + halfHeight; + vertex.z = radius * cosTheta; + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + normal.set(sinTheta, slope, cosTheta).normalize(); + normals.push(normal.x, normal.y, normal.z); // uv + + uvs.push(u, 1 - v); // save index of vertex in respective row + + indexRow.push(index++); + } // now save vertices of the row in our index array + + + indexArray.push(indexRow); + } // generate indices + + + for (var _x = 0; _x < radialSegments; _x++) { + for (var _y = 0; _y < heightSegments; _y++) { + // we use the index array to access the correct indices + var a = indexArray[_y][_x]; + var b = indexArray[_y + 1][_x]; + var c = indexArray[_y + 1][_x + 1]; + var d = indexArray[_y][_x + 1]; // faces + + indices.push(a, b, d); + indices.push(b, c, d); // update group counter + + groupCount += 6; + } + } // add a group to the geometry. this will ensure multi material support + + + scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups + + groupStart += groupCount; + } + + function generateCap(top) { + // save the index of the first center vertex + var centerIndexStart = index; + var uv = new Vector2(); + var vertex = new Vector3(); + var groupCount = 0; + var radius = top === true ? radiusTop : radiusBottom; + var sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. + // because the geometry needs one set of uvs per face, + // we must generate a center vertex per face/segment + + for (var x = 1; x <= radialSegments; x++) { + // vertex + vertices.push(0, halfHeight * sign, 0); // normal + + normals.push(0, sign, 0); // uv + + uvs.push(0.5, 0.5); // increase index + + index++; + } // save the index of the last center vertex + + + var centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs + + for (var _x2 = 0; _x2 <= radialSegments; _x2++) { + var u = _x2 / radialSegments; + var theta = u * thetaLength + thetaStart; + var cosTheta = Math.cos(theta); + var sinTheta = Math.sin(theta); // vertex + + vertex.x = radius * sinTheta; + vertex.y = halfHeight * sign; + vertex.z = radius * cosTheta; + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + normals.push(0, sign, 0); // uv + + uv.x = cosTheta * 0.5 + 0.5; + uv.y = sinTheta * 0.5 * sign + 0.5; + uvs.push(uv.x, uv.y); // increase index + + index++; + } // generate indices + + + for (var _x3 = 0; _x3 < radialSegments; _x3++) { + var c = centerIndexStart + _x3; + var i = centerIndexEnd + _x3; + + if (top === true) { + // face top + indices.push(i, i + 1, c); + } else { + // face bottom + indices.push(i + 1, i, c); + } + + groupCount += 3; + } // add a group to the geometry. this will ensure multi material support + + + scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups + + groupStart += groupCount; + } + + return _this; + } + + return CylinderGeometry; + }(BufferGeometry); + + var ConeGeometry = /*#__PURE__*/function (_CylinderGeometry) { + _inheritsLoose(ConeGeometry, _CylinderGeometry); + + function ConeGeometry(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (height === void 0) { + height = 1; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (heightSegments === void 0) { + heightSegments = 1; + } + + if (openEnded === void 0) { + openEnded = false; + } + + if (thetaStart === void 0) { + thetaStart = 0; + } + + if (thetaLength === void 0) { + thetaLength = Math.PI * 2; + } + + _this = _CylinderGeometry.call(this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) || this; + _this.type = 'ConeGeometry'; + _this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + return _this; + } + + return ConeGeometry; + }(CylinderGeometry); + + var PolyhedronGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(PolyhedronGeometry, _BufferGeometry); + + function PolyhedronGeometry(vertices, indices, radius, detail) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (detail === void 0) { + detail = 0; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'PolyhedronGeometry'; + _this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; // default buffer data + + var vertexBuffer = []; + var uvBuffer = []; // the subdivision creates the vertex buffer data + + subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius + + applyRadius(radius); // finally, create the uv data + + generateUVs(); // build non-indexed geometry + + _this.setAttribute('position', new Float32BufferAttribute(vertexBuffer, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(vertexBuffer.slice(), 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvBuffer, 2)); + + if (detail === 0) { + _this.computeVertexNormals(); // flat normals + + } else { + _this.normalizeNormals(); // smooth normals + + } // helper functions + + + function subdivide(detail) { + var a = new Vector3(); + var b = new Vector3(); + var c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value + + for (var i = 0; i < indices.length; i += 3) { + // get the vertices of the face + getVertexByIndex(indices[i + 0], a); + getVertexByIndex(indices[i + 1], b); + getVertexByIndex(indices[i + 2], c); // perform subdivision + + subdivideFace(a, b, c, detail); + } + } + + function subdivideFace(a, b, c, detail) { + var cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision + + var v = []; // construct all of the vertices for this subdivision + + for (var i = 0; i <= cols; i++) { + v[i] = []; + var aj = a.clone().lerp(c, i / cols); + var bj = b.clone().lerp(c, i / cols); + var rows = cols - i; + + for (var j = 0; j <= rows; j++) { + if (j === 0 && i === cols) { + v[i][j] = aj; + } else { + v[i][j] = aj.clone().lerp(bj, j / rows); + } + } + } // construct all of the faces + + + for (var _i = 0; _i < cols; _i++) { + for (var _j = 0; _j < 2 * (cols - _i) - 1; _j++) { + var k = Math.floor(_j / 2); + + if (_j % 2 === 0) { + pushVertex(v[_i][k + 1]); + pushVertex(v[_i + 1][k]); + pushVertex(v[_i][k]); + } else { + pushVertex(v[_i][k + 1]); + pushVertex(v[_i + 1][k + 1]); + pushVertex(v[_i + 1][k]); + } + } + } + } + + function applyRadius(radius) { + var vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex + + for (var i = 0; i < vertexBuffer.length; i += 3) { + vertex.x = vertexBuffer[i + 0]; + vertex.y = vertexBuffer[i + 1]; + vertex.z = vertexBuffer[i + 2]; + vertex.normalize().multiplyScalar(radius); + vertexBuffer[i + 0] = vertex.x; + vertexBuffer[i + 1] = vertex.y; + vertexBuffer[i + 2] = vertex.z; + } + } + + function generateUVs() { + var vertex = new Vector3(); + + for (var i = 0; i < vertexBuffer.length; i += 3) { + vertex.x = vertexBuffer[i + 0]; + vertex.y = vertexBuffer[i + 1]; + vertex.z = vertexBuffer[i + 2]; + var u = azimuth(vertex) / 2 / Math.PI + 0.5; + var v = inclination(vertex) / Math.PI + 0.5; + uvBuffer.push(u, 1 - v); + } + + correctUVs(); + correctSeam(); + } + + function correctSeam() { + // handle case when face straddles the seam, see #3269 + for (var i = 0; i < uvBuffer.length; i += 6) { + // uv data of a single face + var x0 = uvBuffer[i + 0]; + var x1 = uvBuffer[i + 2]; + var x2 = uvBuffer[i + 4]; + var max = Math.max(x0, x1, x2); + var min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary + + if (max > 0.9 && min < 0.1) { + if (x0 < 0.2) uvBuffer[i + 0] += 1; + if (x1 < 0.2) uvBuffer[i + 2] += 1; + if (x2 < 0.2) uvBuffer[i + 4] += 1; + } + } + } + + function pushVertex(vertex) { + vertexBuffer.push(vertex.x, vertex.y, vertex.z); + } + + function getVertexByIndex(index, vertex) { + var stride = index * 3; + vertex.x = vertices[stride + 0]; + vertex.y = vertices[stride + 1]; + vertex.z = vertices[stride + 2]; + } + + function correctUVs() { + var a = new Vector3(); + var b = new Vector3(); + var c = new Vector3(); + var centroid = new Vector3(); + var uvA = new Vector2(); + var uvB = new Vector2(); + var uvC = new Vector2(); + + for (var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { + a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); + b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); + c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); + uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); + uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); + uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); + centroid.copy(a).add(b).add(c).divideScalar(3); + var azi = azimuth(centroid); + correctUV(uvA, j + 0, a, azi); + correctUV(uvB, j + 2, b, azi); + correctUV(uvC, j + 4, c, azi); + } + } + + function correctUV(uv, stride, vector, azimuth) { + if (azimuth < 0 && uv.x === 1) { + uvBuffer[stride] = uv.x - 1; + } + + if (vector.x === 0 && vector.z === 0) { + uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; + } + } // Angle around the Y axis, counter-clockwise when looking from above. + + + function azimuth(vector) { + return Math.atan2(vector.z, -vector.x); + } // Angle above the XZ plane. + + + function inclination(vector) { + return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); + } + + return _this; + } + + return PolyhedronGeometry; + }(BufferGeometry); + + var DodecahedronGeometry = /*#__PURE__*/function (_PolyhedronGeometry) { + _inheritsLoose(DodecahedronGeometry, _PolyhedronGeometry); + + function DodecahedronGeometry(radius, detail) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (detail === void 0) { + detail = 0; + } + + var t = (1 + Math.sqrt(5)) / 2; + var r = 1 / t; + var vertices = [// (±1, ±1, ±1) + -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) + 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) + -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) + -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; + var indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; + _this = _PolyhedronGeometry.call(this, vertices, indices, radius, detail) || this; + _this.type = 'DodecahedronGeometry'; + _this.parameters = { + radius: radius, + detail: detail + }; + return _this; + } + + return DodecahedronGeometry; + }(PolyhedronGeometry); + + var _v0$2 = new Vector3(); + + var _v1$5 = new Vector3(); + + var _normal$1 = new Vector3(); + + var _triangle = new Triangle(); + + var EdgesGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(EdgesGeometry, _BufferGeometry); + + function EdgesGeometry(geometry, thresholdAngle) { + var _this; + + _this = _BufferGeometry.call(this) || this; + _this.type = 'EdgesGeometry'; + _this.parameters = { + thresholdAngle: thresholdAngle + }; + thresholdAngle = thresholdAngle !== undefined ? thresholdAngle : 1; + + if (geometry.isGeometry === true) { + console.error('THREE.EdgesGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + return _assertThisInitialized(_this); + } + + var precisionPoints = 4; + var precision = Math.pow(10, precisionPoints); + var thresholdDot = Math.cos(MathUtils.DEG2RAD * thresholdAngle); + var indexAttr = geometry.getIndex(); + var positionAttr = geometry.getAttribute('position'); + var indexCount = indexAttr ? indexAttr.count : positionAttr.count; + var indexArr = [0, 0, 0]; + var vertKeys = ['a', 'b', 'c']; + var hashes = new Array(3); + var edgeData = {}; + var vertices = []; + + for (var i = 0; i < indexCount; i += 3) { + if (indexAttr) { + indexArr[0] = indexAttr.getX(i); + indexArr[1] = indexAttr.getX(i + 1); + indexArr[2] = indexAttr.getX(i + 2); + } else { + indexArr[0] = i; + indexArr[1] = i + 1; + indexArr[2] = i + 2; + } + + var a = _triangle.a, + b = _triangle.b, + c = _triangle.c; + a.fromBufferAttribute(positionAttr, indexArr[0]); + b.fromBufferAttribute(positionAttr, indexArr[1]); + c.fromBufferAttribute(positionAttr, indexArr[2]); + + _triangle.getNormal(_normal$1); // create hashes for the edge from the vertices + + + hashes[0] = Math.round(a.x * precision) + "," + Math.round(a.y * precision) + "," + Math.round(a.z * precision); + hashes[1] = Math.round(b.x * precision) + "," + Math.round(b.y * precision) + "," + Math.round(b.z * precision); + hashes[2] = Math.round(c.x * precision) + "," + Math.round(c.y * precision) + "," + Math.round(c.z * precision); // skip degenerate triangles + + if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { + continue; + } // iterate over every edge + + + for (var j = 0; j < 3; j++) { + // get the first and next vertex making up the edge + var jNext = (j + 1) % 3; + var vecHash0 = hashes[j]; + var vecHash1 = hashes[jNext]; + var v0 = _triangle[vertKeys[j]]; + var v1 = _triangle[vertKeys[jNext]]; + var hash = vecHash0 + "_" + vecHash1; + var reverseHash = vecHash1 + "_" + vecHash0; + + if (reverseHash in edgeData && edgeData[reverseHash]) { + // if we found a sibling edge add it into the vertex array if + // it meets the angle threshold and delete the edge from the map. + if (_normal$1.dot(edgeData[reverseHash].normal) <= thresholdDot) { + vertices.push(v0.x, v0.y, v0.z); + vertices.push(v1.x, v1.y, v1.z); + } + + edgeData[reverseHash] = null; + } else if (!(hash in edgeData)) { + // if we've already got an edge here then skip adding a new one + edgeData[hash] = { + index0: indexArr[j], + index1: indexArr[jNext], + normal: _normal$1.clone() + }; + } + } + } // iterate over all remaining, unmatched edges and add them to the vertex array + + + for (var key in edgeData) { + if (edgeData[key]) { + var _edgeData$key = edgeData[key], + index0 = _edgeData$key.index0, + index1 = _edgeData$key.index1; + + _v0$2.fromBufferAttribute(positionAttr, index0); + + _v1$5.fromBufferAttribute(positionAttr, index1); + + vertices.push(_v0$2.x, _v0$2.y, _v0$2.z); + vertices.push(_v1$5.x, _v1$5.y, _v1$5.z); + } + } + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + return _this; + } + + return EdgesGeometry; + }(BufferGeometry); + + /** + * Port from https://github.com/mapbox/earcut (v2.2.2) + */ + var Earcut = { + triangulate: function triangulate(data, holeIndices, dim) { + dim = dim || 2; + var hasHoles = holeIndices && holeIndices.length; + var outerLen = hasHoles ? holeIndices[0] * dim : data.length; + var outerNode = linkedList(data, 0, outerLen, dim, true); + var triangles = []; + if (!outerNode || outerNode.next === outerNode.prev) return triangles; + var minX, minY, maxX, maxY, x, y, invSize; + if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + + if (data.length > 80 * dim) { + minX = maxX = data[0]; + minY = maxY = data[1]; + + for (var i = dim; i < outerLen; i += dim) { + x = data[i]; + y = data[i + 1]; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + } // minX, minY and invSize are later used to transform coords into integers for z-order calculation + + + invSize = Math.max(maxX - minX, maxY - minY); + invSize = invSize !== 0 ? 1 / invSize : 0; + } + + earcutLinked(outerNode, triangles, dim, minX, minY, invSize); + return triangles; + } + }; // create a circular doubly linked list from polygon points in the specified winding order + + function linkedList(data, start, end, dim, clockwise) { + var i, last; + + if (clockwise === signedArea(data, start, end, dim) > 0) { + for (i = start; i < end; i += dim) { + last = insertNode(i, data[i], data[i + 1], last); + } + } else { + for (i = end - dim; i >= start; i -= dim) { + last = insertNode(i, data[i], data[i + 1], last); + } + } + + if (last && equals(last, last.next)) { + removeNode(last); + last = last.next; + } + + return last; + } // eliminate colinear or duplicate points + + + function filterPoints(start, end) { + if (!start) return start; + if (!end) end = start; + var p = start, + again; + + do { + again = false; + + if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { + removeNode(p); + p = end = p.prev; + if (p === p.next) break; + again = true; + } else { + p = p.next; + } + } while (again || p !== end); + + return end; + } // main ear slicing loop which triangulates a polygon (given as a linked list) + + + function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { + if (!ear) return; // interlink polygon nodes in z-order + + if (!pass && invSize) indexCurve(ear, minX, minY, invSize); + var stop = ear, + prev, + next; // iterate through ears, slicing them one by one + + while (ear.prev !== ear.next) { + prev = ear.prev; + next = ear.next; + + if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { + // cut off the triangle + triangles.push(prev.i / dim); + triangles.push(ear.i / dim); + triangles.push(next.i / dim); + removeNode(ear); // skipping the next vertex leads to less sliver triangles + + ear = next.next; + stop = next.next; + continue; + } + + ear = next; // if we looped through the whole remaining polygon and can't find any more ears + + if (ear === stop) { + // try filtering points and slicing again + if (!pass) { + earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn't work, try curing all small self-intersections locally + } else if (pass === 1) { + ear = cureLocalIntersections(filterPoints(ear), triangles, dim); + earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two + } else if (pass === 2) { + splitEarcut(ear, triangles, dim, minX, minY, invSize); + } + + break; + } + } + } // check whether a polygon node forms a valid ear with adjacent nodes + + + function isEar(ear) { + var a = ear.prev, + b = ear, + c = ear.next; + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + // now make sure we don't have other points inside the potential ear + + var p = ear.next.next; + + while (p !== ear.prev) { + if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.next; + } + + return true; + } + + function isEarHashed(ear, minX, minY, invSize) { + var a = ear.prev, + b = ear, + c = ear.next; + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + // triangle bbox; min & max are calculated like this for speed + + var minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x, + minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y, + maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x, + maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox; + + var minZ = zOrder(minTX, minTY, minX, minY, invSize), + maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); + var p = ear.prevZ, + n = ear.nextZ; // look for points inside the triangle in both directions + + while (p && p.z >= minZ && n && n.z <= maxZ) { + if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } // look for remaining points in decreasing z-order + + + while (p && p.z >= minZ) { + if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + } // look for remaining points in increasing z-order + + + while (n && n.z <= maxZ) { + if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } + + return true; + } // go through all polygon nodes and cure small local self-intersections + + + function cureLocalIntersections(start, triangles, dim) { + var p = start; + + do { + var a = p.prev, + b = p.next.next; + + if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { + triangles.push(a.i / dim); + triangles.push(p.i / dim); + triangles.push(b.i / dim); // remove two nodes involved + + removeNode(p); + removeNode(p.next); + p = start = b; + } + + p = p.next; + } while (p !== start); + + return filterPoints(p); + } // try splitting polygon into two and triangulate them independently + + + function splitEarcut(start, triangles, dim, minX, minY, invSize) { + // look for a valid diagonal that divides the polygon into two + var a = start; + + do { + var b = a.next.next; + + while (b !== a.prev) { + if (a.i !== b.i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + var c = splitPolygon(a, b); // filter colinear points around the cuts + + a = filterPoints(a, a.next); + c = filterPoints(c, c.next); // run earcut on each half + + earcutLinked(a, triangles, dim, minX, minY, invSize); + earcutLinked(c, triangles, dim, minX, minY, invSize); + return; + } + + b = b.next; + } + + a = a.next; + } while (a !== start); + } // link every hole into the outer loop, producing a single-ring polygon without holes + + + function eliminateHoles(data, holeIndices, outerNode, dim) { + var queue = []; + var i, len, start, end, list; + + for (i = 0, len = holeIndices.length; i < len; i++) { + start = holeIndices[i] * dim; + end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; + list = linkedList(data, start, end, dim, false); + if (list === list.next) list.steiner = true; + queue.push(getLeftmost(list)); + } + + queue.sort(compareX); // process holes from left to right + + for (i = 0; i < queue.length; i++) { + eliminateHole(queue[i], outerNode); + outerNode = filterPoints(outerNode, outerNode.next); + } + + return outerNode; + } + + function compareX(a, b) { + return a.x - b.x; + } // find a bridge between vertices that connects hole with an outer ring and and link it + + + function eliminateHole(hole, outerNode) { + outerNode = findHoleBridge(hole, outerNode); + + if (outerNode) { + var b = splitPolygon(outerNode, hole); // filter collinear points around the cuts + + filterPoints(outerNode, outerNode.next); + filterPoints(b, b.next); + } + } // David Eberly's algorithm for finding a bridge between hole and outer polygon + + + function findHoleBridge(hole, outerNode) { + var p = outerNode; + var hx = hole.x; + var hy = hole.y; + var qx = -Infinity, + m; // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + + do { + if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { + var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); + + if (x <= hx && x > qx) { + qx = x; + + if (x === hx) { + if (hy === p.y) return p; + if (hy === p.next.y) return p.next; + } + + m = p.x < p.next.x ? p : p.next; + } + } + + p = p.next; + } while (p !== outerNode); + + if (!m) return null; + if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point + + var stop = m, + mx = m.x, + my = m.y; + var tanMin = Infinity, + tan; + p = m; + + do { + if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { + tan = Math.abs(hy - p.y) / (hx - p.x); // tangential + + if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { + m = p; + tanMin = tan; + } + } + + p = p.next; + } while (p !== stop); + + return m; + } // whether sector in vertex m contains sector in vertex p in the same coordinates + + + function sectorContainsSector(m, p) { + return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; + } // interlink polygon nodes in z-order + + + function indexCurve(start, minX, minY, invSize) { + var p = start; + + do { + if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + } while (p !== start); + + p.prevZ.nextZ = null; + p.prevZ = null; + sortLinked(p); + } // Simon Tatham's linked list merge sort algorithm + // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + + + function sortLinked(list) { + var i, + p, + q, + e, + tail, + numMerges, + pSize, + qSize, + inSize = 1; + + do { + p = list; + list = null; + tail = null; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + + for (i = 0; i < inSize; i++) { + pSize++; + q = q.nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || qSize > 0 && q) { + if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { + e = p; + p = p.nextZ; + pSize--; + } else { + e = q; + q = q.nextZ; + qSize--; + } + + if (tail) tail.nextZ = e;else list = e; + e.prevZ = tail; + tail = e; + } + + p = q; + } + + tail.nextZ = null; + inSize *= 2; + } while (numMerges > 1); + + return list; + } // z-order of a point given coords and inverse of the longer side of data bbox + + + function zOrder(x, y, minX, minY, invSize) { + // coords are transformed into non-negative 15-bit integer range + x = 32767 * (x - minX) * invSize; + y = 32767 * (y - minY) * invSize; + x = (x | x << 8) & 0x00FF00FF; + x = (x | x << 4) & 0x0F0F0F0F; + x = (x | x << 2) & 0x33333333; + x = (x | x << 1) & 0x55555555; + y = (y | y << 8) & 0x00FF00FF; + y = (y | y << 4) & 0x0F0F0F0F; + y = (y | y << 2) & 0x33333333; + y = (y | y << 1) & 0x55555555; + return x | y << 1; + } // find the leftmost node of a polygon ring + + + function getLeftmost(start) { + var p = start, + leftmost = start; + + do { + if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; + p = p.next; + } while (p !== start); + + return leftmost; + } // check if a point lies within a convex triangle + + + function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { + return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; + } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) + + + function isValidDiagonal(a, b) { + return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones't intersect other edges + locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible + area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors + equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case + } // signed area of a triangle + + + function area(p, q, r) { + return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + } // check if two points are equal + + + function equals(p1, p2) { + return p1.x === p2.x && p1.y === p2.y; + } // check if two segments intersect + + + function intersects(p1, q1, p2, q2) { + var o1 = sign(area(p1, q1, p2)); + var o2 = sign(area(p1, q1, q2)); + var o3 = sign(area(p2, q2, p1)); + var o4 = sign(area(p2, q2, q1)); + if (o1 !== o2 && o3 !== o4) return true; // general case + + if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + + if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + + if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + + if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; + } // for collinear points p, q, r, check if point q lies on segment pr + + + function onSegment(p, q, r) { + return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); + } + + function sign(num) { + return num > 0 ? 1 : num < 0 ? -1 : 0; + } // check if a polygon diagonal intersects any polygon segments + + + function intersectsPolygon(a, b) { + var p = a; + + do { + if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; + p = p.next; + } while (p !== a); + + return false; + } // check if a polygon diagonal is locally inside the polygon + + + function locallyInside(a, b) { + return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; + } // check if the middle point of a polygon diagonal is inside the polygon + + + function middleInside(a, b) { + var p = a, + inside = false; + var px = (a.x + b.x) / 2, + py = (a.y + b.y) / 2; + + do { + if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; + p = p.next; + } while (p !== a); + + return inside; + } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; + // if one belongs to the outer ring and another to a hole, it merges it into a single ring + + + function splitPolygon(a, b) { + var a2 = new Node(a.i, a.x, a.y), + b2 = new Node(b.i, b.x, b.y), + an = a.next, + bp = b.prev; + a.next = b; + b.prev = a; + a2.next = an; + an.prev = a2; + b2.next = a2; + a2.prev = b2; + bp.next = b2; + b2.prev = bp; + return b2; + } // create a node and optionally link it with previous one (in a circular doubly linked list) + + + function insertNode(i, x, y, last) { + var p = new Node(i, x, y); + + if (!last) { + p.prev = p; + p.next = p; + } else { + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; + } + + return p; + } + + function removeNode(p) { + p.next.prev = p.prev; + p.prev.next = p.next; + if (p.prevZ) p.prevZ.nextZ = p.nextZ; + if (p.nextZ) p.nextZ.prevZ = p.prevZ; + } + + function Node(i, x, y) { + // vertex index in coordinates array + this.i = i; // vertex coordinates + + this.x = x; + this.y = y; // previous and next vertex nodes in a polygon ring + + this.prev = null; + this.next = null; // z-order curve value + + this.z = null; // previous and next nodes in z-order + + this.prevZ = null; + this.nextZ = null; // indicates whether this is a steiner point + + this.steiner = false; + } + + function signedArea(data, start, end, dim) { + var sum = 0; + + for (var i = start, j = end - dim; i < end; i += dim) { + sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); + j = i; + } + + return sum; + } + + var ShapeUtils = { + // calculate area of the contour polygon + area: function area(contour) { + var n = contour.length; + var a = 0.0; + + for (var p = n - 1, q = 0; q < n; p = q++) { + a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; + } + + return a * 0.5; + }, + isClockWise: function isClockWise(pts) { + return ShapeUtils.area(pts) < 0; + }, + triangulateShape: function triangulateShape(contour, holes) { + var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] + + var holeIndices = []; // array of hole indices + + var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + + removeDupEndPts(contour); + addContour(vertices, contour); // + + var holeIndex = contour.length; + holes.forEach(removeDupEndPts); + + for (var i = 0; i < holes.length; i++) { + holeIndices.push(holeIndex); + holeIndex += holes[i].length; + addContour(vertices, holes[i]); + } // + + + var triangles = Earcut.triangulate(vertices, holeIndices); // + + for (var _i = 0; _i < triangles.length; _i += 3) { + faces.push(triangles.slice(_i, _i + 3)); + } + + return faces; + } + }; + + function removeDupEndPts(points) { + var l = points.length; + + if (l > 2 && points[l - 1].equals(points[0])) { + points.pop(); + } + } + + function addContour(vertices, contour) { + for (var i = 0; i < contour.length; i++) { + vertices.push(contour[i].x); + vertices.push(contour[i].y); + } + } + + var ExtrudeGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(ExtrudeGeometry, _BufferGeometry); + + function ExtrudeGeometry(shapes, options) { + var _this; + + _this = _BufferGeometry.call(this) || this; + _this.type = 'ExtrudeGeometry'; + _this.parameters = { + shapes: shapes, + options: options + }; + shapes = Array.isArray(shapes) ? shapes : [shapes]; + + var scope = _assertThisInitialized(_this); + + var verticesArray = []; + var uvArray = []; + + for (var i = 0, l = shapes.length; i < l; i++) { + var shape = shapes[i]; + addShape(shape); + } // build geometry + + + _this.setAttribute('position', new Float32BufferAttribute(verticesArray, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvArray, 2)); + + _this.computeVertexNormals(); // functions + + + function addShape(shape) { + var placeholder = []; // options + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + var steps = options.steps !== undefined ? options.steps : 1; + var depth = options.depth !== undefined ? options.depth : 100; + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; + var bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + var extrudePath = options.extrudePath; + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // deprecated options + + if (options.amount !== undefined) { + console.warn('THREE.ExtrudeBufferGeometry: amount has been renamed to depth.'); + depth = options.amount; + } // + + + var extrudePts, + extrudeByPath = false; + var splineTube, binormal, normal, position2; + + if (extrudePath) { + extrudePts = extrudePath.getSpacedPoints(steps); + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + // SETUP TNB variables + // TODO1 - have a .isClosed in spline? + + splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); + } // Safeguards if bevels are not enabled + + + if (!bevelEnabled) { + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + bevelOffset = 0; + } // Variables initialization + + + var shapePoints = shape.extractPoints(curveSegments); + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + var reverse = !ShapeUtils.isClockWise(vertices); + + if (reverse) { + vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for (var h = 0, hl = holes.length; h < hl; h++) { + var ahole = holes[h]; + + if (ShapeUtils.isClockWise(ahole)) { + holes[h] = ahole.reverse(); + } + } + } + + var faces = ShapeUtils.triangulateShape(vertices, holes); + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for (var _h = 0, _hl = holes.length; _h < _hl; _h++) { + var _ahole = holes[_h]; + vertices = vertices.concat(_ahole); + } + + function scalePt2(pt, vec, size) { + if (!vec) console.error('THREE.ExtrudeGeometry: vec does not exist'); + return vec.clone().multiplyScalar(size).add(pt); + } + + var vlen = vertices.length, + flen = faces.length; // Find directions for point movement + + function getBevelVec(inPt, inPrev, inNext) { + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + var v_prev_x = inPt.x - inPrev.x, + v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, + v_next_y = inNext.y - inPt.y; + var v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges + + var collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; + + if (Math.abs(collinear0) > Number.EPSILON) { + // not collinear + // length of vectors for normalizing + var v_prev_len = Math.sqrt(v_prev_lensq); + var v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; + var ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; + var ptNextShift_x = inNext.x - v_next_y / v_next_len; + var ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point + + var sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point + + v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; + v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + + var v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; + + if (v_trans_lensq <= 2) { + return new Vector2(v_trans_x, v_trans_y); + } else { + shrink_by = Math.sqrt(v_trans_lensq / 2); + } + } else { + // handle special case of collinear edges + var direction_eq = false; // assumes: opposite + + if (v_prev_x > Number.EPSILON) { + if (v_next_x > Number.EPSILON) { + direction_eq = true; + } + } else { + if (v_prev_x < -Number.EPSILON) { + if (v_next_x < -Number.EPSILON) { + direction_eq = true; + } + } else { + if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { + direction_eq = true; + } + } + } + + if (direction_eq) { + // console.log("Warning: lines are a straight sequence"); + v_trans_x = -v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt(v_prev_lensq); + } else { + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt(v_prev_lensq / 2); + } + } + + return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); + } + + var contourMovements = []; + + for (var _i = 0, il = contour.length, j = il - 1, k = _i + 1; _i < il; _i++, j++, k++) { + if (j === il) j = 0; + if (k === il) k = 0; // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + contourMovements[_i] = getBevelVec(contour[_i], contour[j], contour[k]); + } + + var holesMovements = []; + var oneHoleMovements, + verticesMovements = contourMovements.concat(); + + for (var _h2 = 0, _hl2 = holes.length; _h2 < _hl2; _h2++) { + var _ahole2 = holes[_h2]; + oneHoleMovements = []; + + for (var _i2 = 0, _il = _ahole2.length, _j = _il - 1, _k = _i2 + 1; _i2 < _il; _i2++, _j++, _k++) { + if (_j === _il) _j = 0; + if (_k === _il) _k = 0; // (j)---(i)---(k) + + oneHoleMovements[_i2] = getBevelVec(_ahole2[_i2], _ahole2[_j], _ahole2[_k]); + } + + holesMovements.push(oneHoleMovements); + verticesMovements = verticesMovements.concat(oneHoleMovements); + } // Loop bevelSegments, 1 for the front, 1 for the back + + + for (var b = 0; b < bevelSegments; b++) { + //for ( b = bevelSegments; b > 0; b -- ) { + var t = b / bevelSegments; + var z = bevelThickness * Math.cos(t * Math.PI / 2); + + var _bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape + + + for (var _i3 = 0, _il2 = contour.length; _i3 < _il2; _i3++) { + var vert = scalePt2(contour[_i3], contourMovements[_i3], _bs); + v(vert.x, vert.y, -z); + } // expand holes + + + for (var _h3 = 0, _hl3 = holes.length; _h3 < _hl3; _h3++) { + var _ahole3 = holes[_h3]; + oneHoleMovements = holesMovements[_h3]; + + for (var _i4 = 0, _il3 = _ahole3.length; _i4 < _il3; _i4++) { + var _vert = scalePt2(_ahole3[_i4], oneHoleMovements[_i4], _bs); + + v(_vert.x, _vert.y, -z); + } + } + } + + var bs = bevelSize + bevelOffset; // Back facing vertices + + for (var _i5 = 0; _i5 < vlen; _i5++) { + var _vert2 = bevelEnabled ? scalePt2(vertices[_i5], verticesMovements[_i5], bs) : vertices[_i5]; + + if (!extrudeByPath) { + v(_vert2.x, _vert2.y, 0); + } else { + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + normal.copy(splineTube.normals[0]).multiplyScalar(_vert2.x); + binormal.copy(splineTube.binormals[0]).multiplyScalar(_vert2.y); + position2.copy(extrudePts[0]).add(normal).add(binormal); + v(position2.x, position2.y, position2.z); + } + } // Add stepped vertices... + // Including front facing vertices + + + for (var s = 1; s <= steps; s++) { + for (var _i6 = 0; _i6 < vlen; _i6++) { + var _vert3 = bevelEnabled ? scalePt2(vertices[_i6], verticesMovements[_i6], bs) : vertices[_i6]; + + if (!extrudeByPath) { + v(_vert3.x, _vert3.y, depth / steps * s); + } else { + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + normal.copy(splineTube.normals[s]).multiplyScalar(_vert3.x); + binormal.copy(splineTube.binormals[s]).multiplyScalar(_vert3.y); + position2.copy(extrudePts[s]).add(normal).add(binormal); + v(position2.x, position2.y, position2.z); + } + } + } // Add bevel segments planes + //for ( b = 1; b <= bevelSegments; b ++ ) { + + + for (var _b = bevelSegments - 1; _b >= 0; _b--) { + var _t = _b / bevelSegments; + + var _z = bevelThickness * Math.cos(_t * Math.PI / 2); + + var _bs2 = bevelSize * Math.sin(_t * Math.PI / 2) + bevelOffset; // contract shape + + + for (var _i7 = 0, _il4 = contour.length; _i7 < _il4; _i7++) { + var _vert4 = scalePt2(contour[_i7], contourMovements[_i7], _bs2); + + v(_vert4.x, _vert4.y, depth + _z); + } // expand holes + + + for (var _h4 = 0, _hl4 = holes.length; _h4 < _hl4; _h4++) { + var _ahole4 = holes[_h4]; + oneHoleMovements = holesMovements[_h4]; + + for (var _i8 = 0, _il5 = _ahole4.length; _i8 < _il5; _i8++) { + var _vert5 = scalePt2(_ahole4[_i8], oneHoleMovements[_i8], _bs2); + + if (!extrudeByPath) { + v(_vert5.x, _vert5.y, depth + _z); + } else { + v(_vert5.x, _vert5.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + _z); + } + } + } + } + /* Faces */ + // Top and bottom faces + + + buildLidFaces(); // Sides faces + + buildSideFaces(); ///// Internal functions + + function buildLidFaces() { + var start = verticesArray.length / 3; + + if (bevelEnabled) { + var layer = 0; // steps + 1 + + var offset = vlen * layer; // Bottom faces + + for (var _i9 = 0; _i9 < flen; _i9++) { + var face = faces[_i9]; + f3(face[2] + offset, face[1] + offset, face[0] + offset); + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; // Top faces + + for (var _i10 = 0; _i10 < flen; _i10++) { + var _face = faces[_i10]; + f3(_face[0] + offset, _face[1] + offset, _face[2] + offset); + } + } else { + // Bottom faces + for (var _i11 = 0; _i11 < flen; _i11++) { + var _face2 = faces[_i11]; + f3(_face2[2], _face2[1], _face2[0]); + } // Top faces + + + for (var _i12 = 0; _i12 < flen; _i12++) { + var _face3 = faces[_i12]; + f3(_face3[0] + vlen * steps, _face3[1] + vlen * steps, _face3[2] + vlen * steps); + } + } + + scope.addGroup(start, verticesArray.length / 3 - start, 0); + } // Create faces for the z-sides of the shape + + + function buildSideFaces() { + var start = verticesArray.length / 3; + var layeroffset = 0; + sidewalls(contour, layeroffset); + layeroffset += contour.length; + + for (var _h5 = 0, _hl5 = holes.length; _h5 < _hl5; _h5++) { + var _ahole5 = holes[_h5]; + sidewalls(_ahole5, layeroffset); //, true + + layeroffset += _ahole5.length; + } + + scope.addGroup(start, verticesArray.length / 3 - start, 1); + } + + function sidewalls(contour, layeroffset) { + var i = contour.length; + + while (--i >= 0) { + var _j2 = i; + + var _k2 = i - 1; + + if (_k2 < 0) _k2 = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); + + for (var _s = 0, sl = steps + bevelSegments * 2; _s < sl; _s++) { + var slen1 = vlen * _s; + var slen2 = vlen * (_s + 1); + + var a = layeroffset + _j2 + slen1, + _b2 = layeroffset + _k2 + slen1, + c = layeroffset + _k2 + slen2, + d = layeroffset + _j2 + slen2; + + f4(a, _b2, c, d); + } + } + } + + function v(x, y, z) { + placeholder.push(x); + placeholder.push(y); + placeholder.push(z); + } + + function f3(a, b, c) { + addVertex(a); + addVertex(b); + addVertex(c); + var nextIndex = verticesArray.length / 3; + var uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); + addUV(uvs[0]); + addUV(uvs[1]); + addUV(uvs[2]); + } + + function f4(a, b, c, d) { + addVertex(a); + addVertex(b); + addVertex(d); + addVertex(b); + addVertex(c); + addVertex(d); + var nextIndex = verticesArray.length / 3; + var uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); + addUV(uvs[0]); + addUV(uvs[1]); + addUV(uvs[3]); + addUV(uvs[1]); + addUV(uvs[2]); + addUV(uvs[3]); + } + + function addVertex(index) { + verticesArray.push(placeholder[index * 3 + 0]); + verticesArray.push(placeholder[index * 3 + 1]); + verticesArray.push(placeholder[index * 3 + 2]); + } + + function addUV(vector2) { + uvArray.push(vector2.x); + uvArray.push(vector2.y); + } + } + + return _this; + } + + var _proto = ExtrudeGeometry.prototype; + + _proto.toJSON = function toJSON() { + var data = BufferGeometry.prototype.toJSON.call(this); + var shapes = this.parameters.shapes; + var options = this.parameters.options; + return _toJSON(shapes, options, data); + }; + + return ExtrudeGeometry; + }(BufferGeometry); + + var WorldUVGenerator = { + generateTopUV: function generateTopUV(geometry, vertices, indexA, indexB, indexC) { + var a_x = vertices[indexA * 3]; + var a_y = vertices[indexA * 3 + 1]; + var b_x = vertices[indexB * 3]; + var b_y = vertices[indexB * 3 + 1]; + var c_x = vertices[indexC * 3]; + var c_y = vertices[indexC * 3 + 1]; + return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; + }, + generateSideWallUV: function generateSideWallUV(geometry, vertices, indexA, indexB, indexC, indexD) { + var a_x = vertices[indexA * 3]; + var a_y = vertices[indexA * 3 + 1]; + var a_z = vertices[indexA * 3 + 2]; + var b_x = vertices[indexB * 3]; + var b_y = vertices[indexB * 3 + 1]; + var b_z = vertices[indexB * 3 + 2]; + var c_x = vertices[indexC * 3]; + var c_y = vertices[indexC * 3 + 1]; + var c_z = vertices[indexC * 3 + 2]; + var d_x = vertices[indexD * 3]; + var d_y = vertices[indexD * 3 + 1]; + var d_z = vertices[indexD * 3 + 2]; + + if (Math.abs(a_y - b_y) < 0.01) { + return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; + } else { + return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; + } + } + }; + + function _toJSON(shapes, options, data) { + data.shapes = []; + + if (Array.isArray(shapes)) { + for (var i = 0, l = shapes.length; i < l; i++) { + var shape = shapes[i]; + data.shapes.push(shape.uuid); + } + } else { + data.shapes.push(shapes.uuid); + } + + if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); + return data; + } + + var IcosahedronGeometry = /*#__PURE__*/function (_PolyhedronGeometry) { + _inheritsLoose(IcosahedronGeometry, _PolyhedronGeometry); + + function IcosahedronGeometry(radius, detail) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (detail === void 0) { + detail = 0; + } + + var t = (1 + Math.sqrt(5)) / 2; + var vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; + var indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; + _this = _PolyhedronGeometry.call(this, vertices, indices, radius, detail) || this; + _this.type = 'IcosahedronGeometry'; + _this.parameters = { + radius: radius, + detail: detail + }; + return _this; + } + + return IcosahedronGeometry; + }(PolyhedronGeometry); + + var LatheGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(LatheGeometry, _BufferGeometry); + + function LatheGeometry(points, segments, phiStart, phiLength) { + var _this; + + if (segments === void 0) { + segments = 12; + } + + if (phiStart === void 0) { + phiStart = 0; + } + + if (phiLength === void 0) { + phiLength = Math.PI * 2; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'LatheGeometry'; + _this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + segments = Math.floor(segments); // clamp phiLength so it's in range of [ 0, 2PI ] + + phiLength = MathUtils.clamp(phiLength, 0, Math.PI * 2); // buffers + + var indices = []; + var vertices = []; + var uvs = []; // helper variables + + var inverseSegments = 1.0 / segments; + var vertex = new Vector3(); + var uv = new Vector2(); // generate vertices and uvs + + for (var i = 0; i <= segments; i++) { + var phi = phiStart + i * inverseSegments * phiLength; + var sin = Math.sin(phi); + var cos = Math.cos(phi); + + for (var j = 0; j <= points.length - 1; j++) { + // vertex + vertex.x = points[j].x * sin; + vertex.y = points[j].y; + vertex.z = points[j].x * cos; + vertices.push(vertex.x, vertex.y, vertex.z); // uv + + uv.x = i / segments; + uv.y = j / (points.length - 1); + uvs.push(uv.x, uv.y); + } + } // indices + + + for (var _i = 0; _i < segments; _i++) { + for (var _j = 0; _j < points.length - 1; _j++) { + var base = _j + _i * points.length; + var a = base; + var b = base + points.length; + var c = base + points.length + 1; + var d = base + 1; // faces + + indices.push(a, b, d); + indices.push(b, c, d); + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // generate normals + + + _this.computeVertexNormals(); // if the geometry is closed, we need to average the normals along the seam. + // because the corresponding vertices are identical (but still have different UVs). + + + if (phiLength === Math.PI * 2) { + var normals = _this.attributes.normal.array; + var n1 = new Vector3(); + var n2 = new Vector3(); + var n = new Vector3(); // this is the buffer offset for the last line of vertices + + var _base = segments * points.length * 3; + + for (var _i2 = 0, _j2 = 0; _i2 < points.length; _i2++, _j2 += 3) { + // select the normal of the vertex in the first line + n1.x = normals[_j2 + 0]; + n1.y = normals[_j2 + 1]; + n1.z = normals[_j2 + 2]; // select the normal of the vertex in the last line + + n2.x = normals[_base + _j2 + 0]; + n2.y = normals[_base + _j2 + 1]; + n2.z = normals[_base + _j2 + 2]; // average normals + + n.addVectors(n1, n2).normalize(); // assign the new values to both normals + + normals[_j2 + 0] = normals[_base + _j2 + 0] = n.x; + normals[_j2 + 1] = normals[_base + _j2 + 1] = n.y; + normals[_j2 + 2] = normals[_base + _j2 + 2] = n.z; + } + } + + return _this; + } + + return LatheGeometry; + }(BufferGeometry); + + var OctahedronGeometry = /*#__PURE__*/function (_PolyhedronGeometry) { + _inheritsLoose(OctahedronGeometry, _PolyhedronGeometry); + + function OctahedronGeometry(radius, detail) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (detail === void 0) { + detail = 0; + } + + var vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; + var indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; + _this = _PolyhedronGeometry.call(this, vertices, indices, radius, detail) || this; + _this.type = 'OctahedronGeometry'; + _this.parameters = { + radius: radius, + detail: detail + }; + return _this; + } + + return OctahedronGeometry; + }(PolyhedronGeometry); + + /** + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html + */ + + function ParametricGeometry(func, slices, stacks) { + BufferGeometry.call(this); + this.type = 'ParametricGeometry'; + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; + var EPS = 0.00001; + var normal = new Vector3(); + var p0 = new Vector3(), + p1 = new Vector3(); + var pu = new Vector3(), + pv = new Vector3(); + + if (func.length < 3) { + console.error('THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.'); + } // generate vertices, normals and uvs + + + var sliceCount = slices + 1; + + for (var i = 0; i <= stacks; i++) { + var v = i / stacks; + + for (var j = 0; j <= slices; j++) { + var u = j / slices; // vertex + + func(u, v, p0); + vertices.push(p0.x, p0.y, p0.z); // normal + // approximate tangent vectors via finite differences + + if (u - EPS >= 0) { + func(u - EPS, v, p1); + pu.subVectors(p0, p1); + } else { + func(u + EPS, v, p1); + pu.subVectors(p1, p0); + } + + if (v - EPS >= 0) { + func(u, v - EPS, p1); + pv.subVectors(p0, p1); + } else { + func(u, v + EPS, p1); + pv.subVectors(p1, p0); + } // cross product of tangent vectors returns surface normal + + + normal.crossVectors(pu, pv).normalize(); + normals.push(normal.x, normal.y, normal.z); // uv + + uvs.push(u, v); + } + } // generate indices + + + for (var _i = 0; _i < stacks; _i++) { + for (var _j = 0; _j < slices; _j++) { + var a = _i * sliceCount + _j; + var b = _i * sliceCount + _j + 1; + var c = (_i + 1) * sliceCount + _j + 1; + var d = (_i + 1) * sliceCount + _j; // faces one and two + + indices.push(a, b, d); + indices.push(b, c, d); + } + } // build geometry + + + this.setIndex(indices); + this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + } + + ParametricGeometry.prototype = Object.create(BufferGeometry.prototype); + ParametricGeometry.prototype.constructor = ParametricGeometry; + + var RingGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(RingGeometry, _BufferGeometry); + + function RingGeometry(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) { + var _this; + + if (innerRadius === void 0) { + innerRadius = 0.5; + } + + if (outerRadius === void 0) { + outerRadius = 1; + } + + if (thetaSegments === void 0) { + thetaSegments = 8; + } + + if (phiSegments === void 0) { + phiSegments = 1; + } + + if (thetaStart === void 0) { + thetaStart = 0; + } + + if (thetaLength === void 0) { + thetaLength = Math.PI * 2; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'RingGeometry'; + _this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + thetaSegments = Math.max(3, thetaSegments); + phiSegments = Math.max(1, phiSegments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // some helper variables + + var radius = innerRadius; + var radiusStep = (outerRadius - innerRadius) / phiSegments; + var vertex = new Vector3(); + var uv = new Vector2(); // generate vertices, normals and uvs + + for (var j = 0; j <= phiSegments; j++) { + for (var i = 0; i <= thetaSegments; i++) { + // values are generate from the inside of the ring to the outside + var segment = thetaStart + i / thetaSegments * thetaLength; // vertex + + vertex.x = radius * Math.cos(segment); + vertex.y = radius * Math.sin(segment); + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + normals.push(0, 0, 1); // uv + + uv.x = (vertex.x / outerRadius + 1) / 2; + uv.y = (vertex.y / outerRadius + 1) / 2; + uvs.push(uv.x, uv.y); + } // increase the radius for next row of vertices + + + radius += radiusStep; + } // indices + + + for (var _j = 0; _j < phiSegments; _j++) { + var thetaSegmentLevel = _j * (thetaSegments + 1); + + for (var _i = 0; _i < thetaSegments; _i++) { + var _segment = _i + thetaSegmentLevel; + + var a = _segment; + var b = _segment + thetaSegments + 1; + var c = _segment + thetaSegments + 2; + var d = _segment + 1; // faces + + indices.push(a, b, d); + indices.push(b, c, d); + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + return _this; + } + + return RingGeometry; + }(BufferGeometry); + + var ShapeGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(ShapeGeometry, _BufferGeometry); + + function ShapeGeometry(shapes, curveSegments) { + var _this; + + if (curveSegments === void 0) { + curveSegments = 12; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'ShapeGeometry'; + _this.parameters = { + shapes: shapes, + curveSegments: curveSegments + }; // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var groupStart = 0; + var groupCount = 0; // allow single and array values for "shapes" parameter + + if (Array.isArray(shapes) === false) { + addShape(shapes); + } else { + for (var i = 0; i < shapes.length; i++) { + addShape(shapes[i]); + + _this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support + + + groupStart += groupCount; + groupCount = 0; + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // helper functions + + + function addShape(shape) { + var indexOffset = vertices.length / 3; + var points = shape.extractPoints(curveSegments); + var shapeVertices = points.shape; + var shapeHoles = points.holes; // check direction of vertices + + if (ShapeUtils.isClockWise(shapeVertices) === false) { + shapeVertices = shapeVertices.reverse(); + } + + for (var _i = 0, l = shapeHoles.length; _i < l; _i++) { + var shapeHole = shapeHoles[_i]; + + if (ShapeUtils.isClockWise(shapeHole) === true) { + shapeHoles[_i] = shapeHole.reverse(); + } + } + + var faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array + + for (var _i2 = 0, _l = shapeHoles.length; _i2 < _l; _i2++) { + var _shapeHole = shapeHoles[_i2]; + shapeVertices = shapeVertices.concat(_shapeHole); + } // vertices, normals, uvs + + + for (var _i3 = 0, _l2 = shapeVertices.length; _i3 < _l2; _i3++) { + var vertex = shapeVertices[_i3]; + vertices.push(vertex.x, vertex.y, 0); + normals.push(0, 0, 1); + uvs.push(vertex.x, vertex.y); // world uvs + } // incides + + + for (var _i4 = 0, _l3 = faces.length; _i4 < _l3; _i4++) { + var face = faces[_i4]; + var a = face[0] + indexOffset; + var b = face[1] + indexOffset; + var c = face[2] + indexOffset; + indices.push(a, b, c); + groupCount += 3; + } + } + + return _this; + } + + var _proto = ShapeGeometry.prototype; + + _proto.toJSON = function toJSON() { + var data = BufferGeometry.prototype.toJSON.call(this); + var shapes = this.parameters.shapes; + return _toJSON$1(shapes, data); + }; + + return ShapeGeometry; + }(BufferGeometry); + + function _toJSON$1(shapes, data) { + data.shapes = []; + + if (Array.isArray(shapes)) { + for (var i = 0, l = shapes.length; i < l; i++) { + var shape = shapes[i]; + data.shapes.push(shape.uuid); + } + } else { + data.shapes.push(shapes.uuid); + } + + return data; + } + + var SphereGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(SphereGeometry, _BufferGeometry); + + function SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (widthSegments === void 0) { + widthSegments = 8; + } + + if (heightSegments === void 0) { + heightSegments = 6; + } + + if (phiStart === void 0) { + phiStart = 0; + } + + if (phiLength === void 0) { + phiLength = Math.PI * 2; + } + + if (thetaStart === void 0) { + thetaStart = 0; + } + + if (thetaLength === void 0) { + thetaLength = Math.PI; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'SphereGeometry'; + _this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + widthSegments = Math.max(3, Math.floor(widthSegments)); + heightSegments = Math.max(2, Math.floor(heightSegments)); + var thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); + var index = 0; + var grid = []; + var vertex = new Vector3(); + var normal = new Vector3(); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // generate vertices, normals and uvs + + for (var iy = 0; iy <= heightSegments; iy++) { + var verticesRow = []; + var v = iy / heightSegments; // special case for the poles + + var uOffset = 0; + + if (iy == 0 && thetaStart == 0) { + uOffset = 0.5 / widthSegments; + } else if (iy == heightSegments && thetaEnd == Math.PI) { + uOffset = -0.5 / widthSegments; + } + + for (var ix = 0; ix <= widthSegments; ix++) { + var u = ix / widthSegments; // vertex + + vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); + vertex.y = radius * Math.cos(thetaStart + v * thetaLength); + vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + normal.copy(vertex).normalize(); + normals.push(normal.x, normal.y, normal.z); // uv + + uvs.push(u + uOffset, 1 - v); + verticesRow.push(index++); + } + + grid.push(verticesRow); + } // indices + + + for (var _iy = 0; _iy < heightSegments; _iy++) { + for (var _ix = 0; _ix < widthSegments; _ix++) { + var a = grid[_iy][_ix + 1]; + var b = grid[_iy][_ix]; + var c = grid[_iy + 1][_ix]; + var d = grid[_iy + 1][_ix + 1]; + if (_iy !== 0 || thetaStart > 0) indices.push(a, b, d); + if (_iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + return _this; + } + + return SphereGeometry; + }(BufferGeometry); + + var TetrahedronGeometry = /*#__PURE__*/function (_PolyhedronGeometry) { + _inheritsLoose(TetrahedronGeometry, _PolyhedronGeometry); + + function TetrahedronGeometry(radius, detail) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (detail === void 0) { + detail = 0; + } + + var vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; + var indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; + _this = _PolyhedronGeometry.call(this, vertices, indices, radius, detail) || this; + _this.type = 'TetrahedronGeometry'; + _this.parameters = { + radius: radius, + detail: detail + }; + return _this; + } + + return TetrahedronGeometry; + }(PolyhedronGeometry); + + var TextGeometry = /*#__PURE__*/function (_ExtrudeGeometry) { + _inheritsLoose(TextGeometry, _ExtrudeGeometry); + + function TextGeometry(text, parameters) { + var _this; + + if (parameters === void 0) { + parameters = {}; + } + + var font = parameters.font; + + if (!(font && font.isFont)) { + console.error('THREE.TextGeometry: font parameter is not an instance of THREE.Font.'); + return new BufferGeometry() || _assertThisInitialized(_this); + } + + var shapes = font.generateShapes(text, parameters.size); // translate parameters to ExtrudeGeometry API + + parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults + + if (parameters.bevelThickness === undefined) parameters.bevelThickness = 10; + if (parameters.bevelSize === undefined) parameters.bevelSize = 8; + if (parameters.bevelEnabled === undefined) parameters.bevelEnabled = false; + _this = _ExtrudeGeometry.call(this, shapes, parameters) || this; + _this.type = 'TextGeometry'; + return _this; + } + + return TextGeometry; + }(ExtrudeGeometry); + + var TorusGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(TorusGeometry, _BufferGeometry); + + function TorusGeometry(radius, tube, radialSegments, tubularSegments, arc) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (tube === void 0) { + tube = 0.4; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (tubularSegments === void 0) { + tubularSegments = 6; + } + + if (arc === void 0) { + arc = Math.PI * 2; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'TorusGeometry'; + _this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + radialSegments = Math.floor(radialSegments); + tubularSegments = Math.floor(tubularSegments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var center = new Vector3(); + var vertex = new Vector3(); + var normal = new Vector3(); // generate vertices, normals and uvs + + for (var j = 0; j <= radialSegments; j++) { + for (var i = 0; i <= tubularSegments; i++) { + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; // vertex + + vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); + vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); + vertex.z = tube * Math.sin(v); + vertices.push(vertex.x, vertex.y, vertex.z); // normal + + center.x = radius * Math.cos(u); + center.y = radius * Math.sin(u); + normal.subVectors(vertex, center).normalize(); + normals.push(normal.x, normal.y, normal.z); // uv + + uvs.push(i / tubularSegments); + uvs.push(j / radialSegments); + } + } // generate indices + + + for (var _j = 1; _j <= radialSegments; _j++) { + for (var _i = 1; _i <= tubularSegments; _i++) { + // indices + var a = (tubularSegments + 1) * _j + _i - 1; + var b = (tubularSegments + 1) * (_j - 1) + _i - 1; + var c = (tubularSegments + 1) * (_j - 1) + _i; + var d = (tubularSegments + 1) * _j + _i; // faces + + indices.push(a, b, d); + indices.push(b, c, d); + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); + + return _this; + } + + return TorusGeometry; + }(BufferGeometry); + + var TorusKnotGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(TorusKnotGeometry, _BufferGeometry); + + function TorusKnotGeometry(radius, tube, tubularSegments, radialSegments, p, q) { + var _this; + + if (radius === void 0) { + radius = 1; + } + + if (tube === void 0) { + tube = 0.4; + } + + if (tubularSegments === void 0) { + tubularSegments = 64; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (p === void 0) { + p = 2; + } + + if (q === void 0) { + q = 3; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'TorusKnotGeometry'; + _this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; + tubularSegments = Math.floor(tubularSegments); + radialSegments = Math.floor(radialSegments); // buffers + + var indices = []; + var vertices = []; + var normals = []; + var uvs = []; // helper variables + + var vertex = new Vector3(); + var normal = new Vector3(); + var P1 = new Vector3(); + var P2 = new Vector3(); + var B = new Vector3(); + var T = new Vector3(); + var N = new Vector3(); // generate vertices, normals and uvs + + for (var i = 0; i <= tubularSegments; ++i) { + // the radian "u" is used to calculate the position on the torus curve of the current tubular segement + var u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. + // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions + + calculatePositionOnCurve(u, p, q, radius, P1); + calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis + + T.subVectors(P2, P1); + N.addVectors(P2, P1); + B.crossVectors(T, N); + N.crossVectors(B, T); // normalize B, N. T can be ignored, we don't use it + + B.normalize(); + N.normalize(); + + for (var j = 0; j <= radialSegments; ++j) { + // now calculate the vertices. they are nothing more than an extrusion of the torus curve. + // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. + var v = j / radialSegments * Math.PI * 2; + var cx = -tube * Math.cos(v); + var cy = tube * Math.sin(v); // now calculate the final vertex position. + // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve + + vertex.x = P1.x + (cx * N.x + cy * B.x); + vertex.y = P1.y + (cx * N.y + cy * B.y); + vertex.z = P1.z + (cx * N.z + cy * B.z); + vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) + + normal.subVectors(vertex, P1).normalize(); + normals.push(normal.x, normal.y, normal.z); // uv + + uvs.push(i / tubularSegments); + uvs.push(j / radialSegments); + } + } // generate indices + + + for (var _j = 1; _j <= tubularSegments; _j++) { + for (var _i = 1; _i <= radialSegments; _i++) { + // indices + var a = (radialSegments + 1) * (_j - 1) + (_i - 1); + var b = (radialSegments + 1) * _j + (_i - 1); + var c = (radialSegments + 1) * _j + _i; + var d = (radialSegments + 1) * (_j - 1) + _i; // faces + + indices.push(a, b, d); + indices.push(b, c, d); + } + } // build geometry + + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve + + + function calculatePositionOnCurve(u, p, q, radius, position) { + var cu = Math.cos(u); + var su = Math.sin(u); + var quOverP = q / p * u; + var cs = Math.cos(quOverP); + position.x = radius * (2 + cs) * 0.5 * cu; + position.y = radius * (2 + cs) * su * 0.5; + position.z = radius * Math.sin(quOverP) * 0.5; + } + + return _this; + } + + return TorusKnotGeometry; + }(BufferGeometry); + + var TubeGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(TubeGeometry, _BufferGeometry); + + function TubeGeometry(path, tubularSegments, radius, radialSegments, closed) { + var _this; + + if (tubularSegments === void 0) { + tubularSegments = 64; + } + + if (radius === void 0) { + radius = 1; + } + + if (radialSegments === void 0) { + radialSegments = 8; + } + + if (closed === void 0) { + closed = false; + } + + _this = _BufferGeometry.call(this) || this; + _this.type = 'TubeGeometry'; + _this.parameters = { + path: path, + tubularSegments: tubularSegments, + radius: radius, + radialSegments: radialSegments, + closed: closed + }; + var frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals + + _this.tangents = frames.tangents; + _this.normals = frames.normals; + _this.binormals = frames.binormals; // helper variables + + var vertex = new Vector3(); + var normal = new Vector3(); + var uv = new Vector2(); + var P = new Vector3(); // buffer + + var vertices = []; + var normals = []; + var uvs = []; + var indices = []; // create buffer data + + generateBufferData(); // build geometry + + _this.setIndex(indices); + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + _this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); + + _this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // functions + + + function generateBufferData() { + for (var i = 0; i < tubularSegments; i++) { + generateSegment(i); + } // if the geometry is not closed, generate the last row of vertices and normals + // at the regular position on the given path + // + // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) + + + generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. + // this makes it easy compute correct values for closed geometries + + generateUVs(); // finally create faces + + generateIndices(); + } + + function generateSegment(i) { + // we use getPointAt to sample evenly distributed points from the given path + P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal + + var N = frames.normals[i]; + var B = frames.binormals[i]; // generate normals and vertices for the current segment + + for (var j = 0; j <= radialSegments; j++) { + var v = j / radialSegments * Math.PI * 2; + var sin = Math.sin(v); + var cos = -Math.cos(v); // normal + + normal.x = cos * N.x + sin * B.x; + normal.y = cos * N.y + sin * B.y; + normal.z = cos * N.z + sin * B.z; + normal.normalize(); + normals.push(normal.x, normal.y, normal.z); // vertex + + vertex.x = P.x + radius * normal.x; + vertex.y = P.y + radius * normal.y; + vertex.z = P.z + radius * normal.z; + vertices.push(vertex.x, vertex.y, vertex.z); + } + } + + function generateIndices() { + for (var j = 1; j <= tubularSegments; j++) { + for (var i = 1; i <= radialSegments; i++) { + var a = (radialSegments + 1) * (j - 1) + (i - 1); + var b = (radialSegments + 1) * j + (i - 1); + var c = (radialSegments + 1) * j + i; + var d = (radialSegments + 1) * (j - 1) + i; // faces + + indices.push(a, b, d); + indices.push(b, c, d); + } + } + } + + function generateUVs() { + for (var i = 0; i <= tubularSegments; i++) { + for (var j = 0; j <= radialSegments; j++) { + uv.x = i / tubularSegments; + uv.y = j / radialSegments; + uvs.push(uv.x, uv.y); + } + } + } + + return _this; + } + + var _proto = TubeGeometry.prototype; + + _proto.toJSON = function toJSON() { + var data = BufferGeometry.prototype.toJSON.call(this); + data.path = this.parameters.path.toJSON(); + return data; + }; + + return TubeGeometry; + }(BufferGeometry); + + var WireframeGeometry = /*#__PURE__*/function (_BufferGeometry) { + _inheritsLoose(WireframeGeometry, _BufferGeometry); + + function WireframeGeometry(geometry) { + var _this; + + _this = _BufferGeometry.call(this) || this; + _this.type = 'WireframeGeometry'; + + if (geometry.isGeometry === true) { + console.error('THREE.WireframeGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.'); + return _assertThisInitialized(_this); + } // buffer + + + var vertices = []; // helper variables + + var edge = [0, 0], + edges = {}; + var vertex = new Vector3(); + + if (geometry.index !== null) { + // indexed BufferGeometry + var position = geometry.attributes.position; + var indices = geometry.index; + var groups = geometry.groups; + + if (groups.length === 0) { + groups = [{ + start: 0, + count: indices.count, + materialIndex: 0 + }]; + } // create a data structure that contains all eges without duplicates + + + for (var o = 0, ol = groups.length; o < ol; ++o) { + var group = groups[o]; + var start = group.start; + var count = group.count; + + for (var i = start, l = start + count; i < l; i += 3) { + for (var j = 0; j < 3; j++) { + var edge1 = indices.getX(i + j); + var edge2 = indices.getX(i + (j + 1) % 3); + edge[0] = Math.min(edge1, edge2); // sorting prevents duplicates + + edge[1] = Math.max(edge1, edge2); + var key = edge[0] + ',' + edge[1]; + + if (edges[key] === undefined) { + edges[key] = { + index1: edge[0], + index2: edge[1] + }; + } + } + } + } // generate vertices + + + for (var _key in edges) { + var e = edges[_key]; + vertex.fromBufferAttribute(position, e.index1); + vertices.push(vertex.x, vertex.y, vertex.z); + vertex.fromBufferAttribute(position, e.index2); + vertices.push(vertex.x, vertex.y, vertex.z); + } + } else { + // non-indexed BufferGeometry + var _position = geometry.attributes.position; + + for (var _i = 0, _l = _position.count / 3; _i < _l; _i++) { + for (var _j = 0; _j < 3; _j++) { + // three edges per triangle, an edge is represented as (index1, index2) + // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) + var index1 = 3 * _i + _j; + vertex.fromBufferAttribute(_position, index1); + vertices.push(vertex.x, vertex.y, vertex.z); + var index2 = 3 * _i + (_j + 1) % 3; + vertex.fromBufferAttribute(_position, index2); + vertices.push(vertex.x, vertex.y, vertex.z); + } + } + } // build geometry + + + _this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + + return _this; + } + + return WireframeGeometry; + }(BufferGeometry); + + var Geometries = /*#__PURE__*/Object.freeze({ + __proto__: null, + BoxGeometry: BoxGeometry, + BoxBufferGeometry: BoxGeometry, + CircleGeometry: CircleGeometry, + CircleBufferGeometry: CircleGeometry, + ConeGeometry: ConeGeometry, + ConeBufferGeometry: ConeGeometry, + CylinderGeometry: CylinderGeometry, + CylinderBufferGeometry: CylinderGeometry, + DodecahedronGeometry: DodecahedronGeometry, + DodecahedronBufferGeometry: DodecahedronGeometry, + EdgesGeometry: EdgesGeometry, + ExtrudeGeometry: ExtrudeGeometry, + ExtrudeBufferGeometry: ExtrudeGeometry, + IcosahedronGeometry: IcosahedronGeometry, + IcosahedronBufferGeometry: IcosahedronGeometry, + LatheGeometry: LatheGeometry, + LatheBufferGeometry: LatheGeometry, + OctahedronGeometry: OctahedronGeometry, + OctahedronBufferGeometry: OctahedronGeometry, + ParametricGeometry: ParametricGeometry, + ParametricBufferGeometry: ParametricGeometry, + PlaneGeometry: PlaneGeometry, + PlaneBufferGeometry: PlaneGeometry, + PolyhedronGeometry: PolyhedronGeometry, + PolyhedronBufferGeometry: PolyhedronGeometry, + RingGeometry: RingGeometry, + RingBufferGeometry: RingGeometry, + ShapeGeometry: ShapeGeometry, + ShapeBufferGeometry: ShapeGeometry, + SphereGeometry: SphereGeometry, + SphereBufferGeometry: SphereGeometry, + TetrahedronGeometry: TetrahedronGeometry, + TetrahedronBufferGeometry: TetrahedronGeometry, + TextGeometry: TextGeometry, + TextBufferGeometry: TextGeometry, + TorusGeometry: TorusGeometry, + TorusBufferGeometry: TorusGeometry, + TorusKnotGeometry: TorusKnotGeometry, + TorusKnotBufferGeometry: TorusKnotGeometry, + TubeGeometry: TubeGeometry, + TubeBufferGeometry: TubeGeometry, + WireframeGeometry: WireframeGeometry + }); + + /** + * parameters = { + * color: + * } + */ + + var ShadowMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(ShadowMaterial, _Material); + + function ShadowMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'ShadowMaterial'; + _this.color = new Color(0x000000); + _this.transparent = true; + + _this.setValues(parameters); + + return _this; + } + + var _proto = ShadowMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + return this; + }; + + return ShadowMaterial; + }(Material); + + ShadowMaterial.prototype.isShadowMaterial = true; + + var RawShaderMaterial = /*#__PURE__*/function (_ShaderMaterial) { + _inheritsLoose(RawShaderMaterial, _ShaderMaterial); + + function RawShaderMaterial(parameters) { + var _this; + + _this = _ShaderMaterial.call(this, parameters) || this; + _this.type = 'RawShaderMaterial'; + return _this; + } + + return RawShaderMaterial; + }(ShaderMaterial); + + RawShaderMaterial.prototype.isRawShaderMaterial = true; + + /** + * parameters = { + * color: , + * roughness: , + * metalness: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * roughnessMap: new THREE.Texture( ), + * + * metalnessMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * envMapIntensity: + * + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * flatShading: + * } + */ + + function MeshStandardMaterial(parameters) { + Material.call(this); + this.defines = { + 'STANDARD': '' + }; + this.type = 'MeshStandardMaterial'; + this.color = new Color(0xffffff); // diffuse + + this.roughness = 1.0; + this.metalness = 0.0; + this.map = null; + this.lightMap = null; + this.lightMapIntensity = 1.0; + this.aoMap = null; + this.aoMapIntensity = 1.0; + this.emissive = new Color(0x000000); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + this.bumpMap = null; + this.bumpScale = 1; + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2(1, 1); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + this.roughnessMap = null; + this.metalnessMap = null; + this.alphaMap = null; + this.envMap = null; + this.envMapIntensity = 1.0; + this.refractionRatio = 0.98; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + this.flatShading = false; + this.vertexTangents = false; + this.setValues(parameters); + } + + MeshStandardMaterial.prototype = Object.create(Material.prototype); + MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; + MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + + MeshStandardMaterial.prototype.copy = function (source) { + Material.prototype.copy.call(this, source); + this.defines = { + 'STANDARD': '' + }; + this.color.copy(source.color); + this.roughness = source.roughness; + this.metalness = source.metalness; + this.map = source.map; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.emissive.copy(source.emissive); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy(source.normalScale); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.roughnessMap = source.roughnessMap; + this.metalnessMap = source.metalnessMap; + this.alphaMap = source.alphaMap; + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; + this.refractionRatio = source.refractionRatio; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + this.flatShading = source.flatShading; + this.vertexTangents = source.vertexTangents; + return this; + }; + + /** + * parameters = { + * clearcoat: , + * clearcoatMap: new THREE.Texture( ), + * clearcoatRoughness: , + * clearcoatRoughnessMap: new THREE.Texture( ), + * clearcoatNormalScale: , + * clearcoatNormalMap: new THREE.Texture( ), + * + * reflectivity: , + * ior: , + * + * sheen: , + * + * transmission: , + * transmissionMap: new THREE.Texture( ) + * } + */ + + function MeshPhysicalMaterial(parameters) { + MeshStandardMaterial.call(this); + this.defines = { + 'STANDARD': '', + 'PHYSICAL': '' + }; + this.type = 'MeshPhysicalMaterial'; + this.clearcoat = 0.0; + this.clearcoatMap = null; + this.clearcoatRoughness = 0.0; + this.clearcoatRoughnessMap = null; + this.clearcoatNormalScale = new Vector2(1, 1); + this.clearcoatNormalMap = null; + this.reflectivity = 0.5; // maps to F0 = 0.04 + + Object.defineProperty(this, 'ior', { + get: function get() { + return (1 + 0.4 * this.reflectivity) / (1 - 0.4 * this.reflectivity); + }, + set: function set(ior) { + this.reflectivity = MathUtils.clamp(2.5 * (ior - 1) / (ior + 1), 0, 1); + } + }); + this.sheen = null; // null will disable sheen bsdf + + this.transmission = 0.0; + this.transmissionMap = null; + this.setValues(parameters); + } + + MeshPhysicalMaterial.prototype = Object.create(MeshStandardMaterial.prototype); + MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; + MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; + + MeshPhysicalMaterial.prototype.copy = function (source) { + MeshStandardMaterial.prototype.copy.call(this, source); + this.defines = { + 'STANDARD': '', + 'PHYSICAL': '' + }; + this.clearcoat = source.clearcoat; + this.clearcoatMap = source.clearcoatMap; + this.clearcoatRoughness = source.clearcoatRoughness; + this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; + this.clearcoatNormalMap = source.clearcoatNormalMap; + this.clearcoatNormalScale.copy(source.clearcoatNormalScale); + this.reflectivity = source.reflectivity; + + if (source.sheen) { + this.sheen = (this.sheen || new Color()).copy(source.sheen); + } else { + this.sheen = null; + } + + this.transmission = source.transmission; + this.transmissionMap = source.transmissionMap; + return this; + }; + + /** + * parameters = { + * color: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.MultiplyOperation, + * reflectivity: , + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * flatShading: + * } + */ + + var MeshPhongMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshPhongMaterial, _Material); + + function MeshPhongMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshPhongMaterial'; + _this.color = new Color(0xffffff); // diffuse + + _this.specular = new Color(0x111111); + _this.shininess = 30; + _this.map = null; + _this.lightMap = null; + _this.lightMapIntensity = 1.0; + _this.aoMap = null; + _this.aoMapIntensity = 1.0; + _this.emissive = new Color(0x000000); + _this.emissiveIntensity = 1.0; + _this.emissiveMap = null; + _this.bumpMap = null; + _this.bumpScale = 1; + _this.normalMap = null; + _this.normalMapType = TangentSpaceNormalMap; + _this.normalScale = new Vector2(1, 1); + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.specularMap = null; + _this.alphaMap = null; + _this.envMap = null; + _this.combine = MultiplyOperation; + _this.reflectivity = 1; + _this.refractionRatio = 0.98; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.wireframeLinecap = 'round'; + _this.wireframeLinejoin = 'round'; + _this.skinning = false; + _this.morphTargets = false; + _this.morphNormals = false; + _this.flatShading = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshPhongMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.specular.copy(source.specular); + this.shininess = source.shininess; + this.map = source.map; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.emissive.copy(source.emissive); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy(source.normalScale); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.specularMap = source.specularMap; + this.alphaMap = source.alphaMap; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + this.flatShading = source.flatShading; + return this; + }; + + return MeshPhongMaterial; + }(Material); + + MeshPhongMaterial.prototype.isMeshPhongMaterial = true; + + /** + * parameters = { + * color: , + * + * map: new THREE.Texture( ), + * gradientMap: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * alphaMap: new THREE.Texture( ), + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ + + var MeshToonMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshToonMaterial, _Material); + + function MeshToonMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.defines = { + 'TOON': '' + }; + _this.type = 'MeshToonMaterial'; + _this.color = new Color(0xffffff); + _this.map = null; + _this.gradientMap = null; + _this.lightMap = null; + _this.lightMapIntensity = 1.0; + _this.aoMap = null; + _this.aoMapIntensity = 1.0; + _this.emissive = new Color(0x000000); + _this.emissiveIntensity = 1.0; + _this.emissiveMap = null; + _this.bumpMap = null; + _this.bumpScale = 1; + _this.normalMap = null; + _this.normalMapType = TangentSpaceNormalMap; + _this.normalScale = new Vector2(1, 1); + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.alphaMap = null; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.wireframeLinecap = 'round'; + _this.wireframeLinejoin = 'round'; + _this.skinning = false; + _this.morphTargets = false; + _this.morphNormals = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshToonMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.map = source.map; + this.gradientMap = source.gradientMap; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.emissive.copy(source.emissive); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy(source.normalScale); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.alphaMap = source.alphaMap; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + return this; + }; + + return MeshToonMaterial; + }(Material); + + MeshToonMaterial.prototype.isMeshToonMaterial = true; + + /** + * parameters = { + * opacity: , + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * wireframe: , + * wireframeLinewidth: + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * flatShading: + * } + */ + + var MeshNormalMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshNormalMaterial, _Material); + + function MeshNormalMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshNormalMaterial'; + _this.bumpMap = null; + _this.bumpScale = 1; + _this.normalMap = null; + _this.normalMapType = TangentSpaceNormalMap; + _this.normalScale = new Vector2(1, 1); + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.fog = false; + _this.skinning = false; + _this.morphTargets = false; + _this.morphNormals = false; + _this.flatShading = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshNormalMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy(source.normalScale); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + this.flatShading = source.flatShading; + return this; + }; + + return MeshNormalMaterial; + }(Material); + + MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + + /** + * parameters = { + * color: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ + + var MeshLambertMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshLambertMaterial, _Material); + + function MeshLambertMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.type = 'MeshLambertMaterial'; + _this.color = new Color(0xffffff); // diffuse + + _this.map = null; + _this.lightMap = null; + _this.lightMapIntensity = 1.0; + _this.aoMap = null; + _this.aoMapIntensity = 1.0; + _this.emissive = new Color(0x000000); + _this.emissiveIntensity = 1.0; + _this.emissiveMap = null; + _this.specularMap = null; + _this.alphaMap = null; + _this.envMap = null; + _this.combine = MultiplyOperation; + _this.reflectivity = 1; + _this.refractionRatio = 0.98; + _this.wireframe = false; + _this.wireframeLinewidth = 1; + _this.wireframeLinecap = 'round'; + _this.wireframeLinejoin = 'round'; + _this.skinning = false; + _this.morphTargets = false; + _this.morphNormals = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshLambertMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.map = source.map; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.emissive.copy(source.emissive); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + this.specularMap = source.specularMap; + this.alphaMap = source.alphaMap; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + return this; + }; + + return MeshLambertMaterial; + }(Material); + + MeshLambertMaterial.prototype.isMeshLambertMaterial = true; + + /** + * parameters = { + * color: , + * opacity: , + * + * matcap: new THREE.Texture( ), + * + * map: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * alphaMap: new THREE.Texture( ), + * + * skinning: , + * morphTargets: , + * morphNormals: + * + * flatShading: + * } + */ + + var MeshMatcapMaterial = /*#__PURE__*/function (_Material) { + _inheritsLoose(MeshMatcapMaterial, _Material); + + function MeshMatcapMaterial(parameters) { + var _this; + + _this = _Material.call(this) || this; + _this.defines = { + 'MATCAP': '' + }; + _this.type = 'MeshMatcapMaterial'; + _this.color = new Color(0xffffff); // diffuse + + _this.matcap = null; + _this.map = null; + _this.bumpMap = null; + _this.bumpScale = 1; + _this.normalMap = null; + _this.normalMapType = TangentSpaceNormalMap; + _this.normalScale = new Vector2(1, 1); + _this.displacementMap = null; + _this.displacementScale = 1; + _this.displacementBias = 0; + _this.alphaMap = null; + _this.skinning = false; + _this.morphTargets = false; + _this.morphNormals = false; + _this.flatShading = false; + + _this.setValues(parameters); + + return _this; + } + + var _proto = MeshMatcapMaterial.prototype; + + _proto.copy = function copy(source) { + _Material.prototype.copy.call(this, source); + + this.defines = { + 'MATCAP': '' + }; + this.color.copy(source.color); + this.matcap = source.matcap; + this.map = source.map; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy(source.normalScale); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + this.alphaMap = source.alphaMap; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + this.flatShading = source.flatShading; + return this; + }; + + return MeshMatcapMaterial; + }(Material); + + MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; + + /** + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: + * } + */ + + var LineDashedMaterial = /*#__PURE__*/function (_LineBasicMaterial) { + _inheritsLoose(LineDashedMaterial, _LineBasicMaterial); + + function LineDashedMaterial(parameters) { + var _this; + + _this = _LineBasicMaterial.call(this) || this; + _this.type = 'LineDashedMaterial'; + _this.scale = 1; + _this.dashSize = 3; + _this.gapSize = 1; + + _this.setValues(parameters); + + return _this; + } + + var _proto = LineDashedMaterial.prototype; + + _proto.copy = function copy(source) { + _LineBasicMaterial.prototype.copy.call(this, source); + + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; + return this; + }; + + return LineDashedMaterial; + }(LineBasicMaterial); + + LineDashedMaterial.prototype.isLineDashedMaterial = true; + + var Materials = /*#__PURE__*/Object.freeze({ + __proto__: null, + ShadowMaterial: ShadowMaterial, + SpriteMaterial: SpriteMaterial, + RawShaderMaterial: RawShaderMaterial, + ShaderMaterial: ShaderMaterial, + PointsMaterial: PointsMaterial, + MeshPhysicalMaterial: MeshPhysicalMaterial, + MeshStandardMaterial: MeshStandardMaterial, + MeshPhongMaterial: MeshPhongMaterial, + MeshToonMaterial: MeshToonMaterial, + MeshNormalMaterial: MeshNormalMaterial, + MeshLambertMaterial: MeshLambertMaterial, + MeshDepthMaterial: MeshDepthMaterial, + MeshDistanceMaterial: MeshDistanceMaterial, + MeshBasicMaterial: MeshBasicMaterial, + MeshMatcapMaterial: MeshMatcapMaterial, + LineDashedMaterial: LineDashedMaterial, + LineBasicMaterial: LineBasicMaterial, + Material: Material + }); + + var AnimationUtils = { + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function arraySlice(array, from, to) { + if (AnimationUtils.isTypedArray(array)) { + // in ios9 array.subarray(from, undefined) will return empty array + // but array.subarray(from) or array.subarray(from, len) is correct + return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); + } + + return array.slice(from, to); + }, + // converts an array to a specific type + convertArray: function convertArray(array, type, forceClone) { + if (!array || // let 'undefined' and 'null' pass + !forceClone && array.constructor === type) return array; + + if (typeof type.BYTES_PER_ELEMENT === 'number') { + return new type(array); // create typed array + } + + return Array.prototype.slice.call(array); // create Array + }, + isTypedArray: function isTypedArray(object) { + return ArrayBuffer.isView(object) && !(object instanceof DataView); + }, + // returns an array by which times and values can be sorted + getKeyframeOrder: function getKeyframeOrder(times) { + function compareTime(i, j) { + return times[i] - times[j]; + } + + var n = times.length; + var result = new Array(n); + + for (var i = 0; i !== n; ++i) { + result[i] = i; + } + + result.sort(compareTime); + return result; + }, + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function sortedArray(values, stride, order) { + var nValues = values.length; + var result = new values.constructor(nValues); + + for (var i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { + var srcOffset = order[i] * stride; + + for (var j = 0; j !== stride; ++j) { + result[dstOffset++] = values[srcOffset + j]; + } + } + + return result; + }, + // function for parsing AOS keyframe formats + flattenJSON: function flattenJSON(jsonKeys, times, values, valuePropertyName) { + var i = 1, + key = jsonKeys[0]; + + while (key !== undefined && key[valuePropertyName] === undefined) { + key = jsonKeys[i++]; + } + + if (key === undefined) return; // no data + + var value = key[valuePropertyName]; + if (value === undefined) return; // no data + + if (Array.isArray(value)) { + do { + value = key[valuePropertyName]; + + if (value !== undefined) { + times.push(key.time); + values.push.apply(values, value); // push all elements + } + + key = jsonKeys[i++]; + } while (key !== undefined); + } else if (value.toArray !== undefined) { + // ...assume THREE.Math-ish + do { + value = key[valuePropertyName]; + + if (value !== undefined) { + times.push(key.time); + value.toArray(values, values.length); + } + + key = jsonKeys[i++]; + } while (key !== undefined); + } else { + // otherwise push as-is + do { + value = key[valuePropertyName]; + + if (value !== undefined) { + times.push(key.time); + values.push(value); + } + + key = jsonKeys[i++]; + } while (key !== undefined); + } + }, + subclip: function subclip(sourceClip, name, startFrame, endFrame, fps) { + if (fps === void 0) { + fps = 30; + } + + var clip = sourceClip.clone(); + clip.name = name; + var tracks = []; + + for (var i = 0; i < clip.tracks.length; ++i) { + var track = clip.tracks[i]; + var valueSize = track.getValueSize(); + var times = []; + var values = []; + + for (var j = 0; j < track.times.length; ++j) { + var frame = track.times[j] * fps; + if (frame < startFrame || frame >= endFrame) continue; + times.push(track.times[j]); + + for (var k = 0; k < valueSize; ++k) { + values.push(track.values[j * valueSize + k]); + } + } + + if (times.length === 0) continue; + track.times = AnimationUtils.convertArray(times, track.times.constructor); + track.values = AnimationUtils.convertArray(values, track.values.constructor); + tracks.push(track); + } + + clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip + + var minStartTime = Infinity; + + for (var _i = 0; _i < clip.tracks.length; ++_i) { + if (minStartTime > clip.tracks[_i].times[0]) { + minStartTime = clip.tracks[_i].times[0]; + } + } // shift all tracks such that clip begins at t=0 + + + for (var _i2 = 0; _i2 < clip.tracks.length; ++_i2) { + clip.tracks[_i2].shift(-1 * minStartTime); + } + + clip.resetDuration(); + return clip; + }, + makeClipAdditive: function makeClipAdditive(targetClip, referenceFrame, referenceClip, fps) { + if (referenceFrame === void 0) { + referenceFrame = 0; + } + + if (referenceClip === void 0) { + referenceClip = targetClip; + } + + if (fps === void 0) { + fps = 30; + } + + if (fps <= 0) fps = 30; + var numTracks = referenceClip.tracks.length; + var referenceTime = referenceFrame / fps; // Make each track's values relative to the values at the reference frame + + var _loop = function _loop(i) { + var referenceTrack = referenceClip.tracks[i]; + var referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it's non-numeric + + if (referenceTrackType === 'bool' || referenceTrackType === 'string') return "continue"; // Find the track in the target clip whose name and type matches the reference track + + var targetTrack = targetClip.tracks.find(function (track) { + return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; + }); + if (targetTrack === undefined) return "continue"; + var referenceOffset = 0; + var referenceValueSize = referenceTrack.getValueSize(); + + if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { + referenceOffset = referenceValueSize / 3; + } + + var targetOffset = 0; + var targetValueSize = targetTrack.getValueSize(); + + if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { + targetOffset = targetValueSize / 3; + } + + var lastIndex = referenceTrack.times.length - 1; + var referenceValue = void 0; // Find the value to subtract out of the track + + if (referenceTime <= referenceTrack.times[0]) { + // Reference frame is earlier than the first keyframe, so just use the first keyframe + var startIndex = referenceOffset; + var endIndex = referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice(referenceTrack.values, startIndex, endIndex); + } else if (referenceTime >= referenceTrack.times[lastIndex]) { + // Reference frame is after the last keyframe, so just use the last keyframe + var _startIndex = lastIndex * referenceValueSize + referenceOffset; + + var _endIndex = _startIndex + referenceValueSize - referenceOffset; + + referenceValue = AnimationUtils.arraySlice(referenceTrack.values, _startIndex, _endIndex); + } else { + // Interpolate to the reference value + var interpolant = referenceTrack.createInterpolant(); + var _startIndex2 = referenceOffset; + + var _endIndex2 = referenceValueSize - referenceOffset; + + interpolant.evaluate(referenceTime); + referenceValue = AnimationUtils.arraySlice(interpolant.resultBuffer, _startIndex2, _endIndex2); + } // Conjugate the quaternion + + + if (referenceTrackType === 'quaternion') { + var referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); + referenceQuat.toArray(referenceValue); + } // Subtract the reference value from all of the track values + + + var numTimes = targetTrack.times.length; + + for (var j = 0; j < numTimes; ++j) { + var valueStart = j * targetValueSize + targetOffset; + + if (referenceTrackType === 'quaternion') { + // Multiply the conjugate for quaternion track types + Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); + } else { + var valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types + + for (var k = 0; k < valueEnd; ++k) { + targetTrack.values[valueStart + k] -= referenceValue[k]; + } + } + } + }; + + for (var i = 0; i < numTracks; ++i) { + var _ret = _loop(i); + + if (_ret === "continue") continue; + } + + targetClip.blendMode = AdditiveAnimationBlendMode; + return targetClip; + } + }; + + /** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + */ + function Interpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; + this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; + } + + Object.assign(Interpolant.prototype, { + evaluate: function evaluate(t) { + var pp = this.parameterPositions; + var i1 = this._cachedIndex, + t1 = pp[i1], + t0 = pp[i1 - 1]; + + validate_interval: { + seek: { + var right; + + linear_scan: { + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if (!(t < t1)) { + for (var giveUpAt = i1 + 2;;) { + if (t1 === undefined) { + if (t < t0) break forward_scan; // after end + + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_(i1 - 1, t, t0); + } + + if (i1 === giveUpAt) break; // this loop + + t0 = t1; + t1 = pp[++i1]; + + if (t < t1) { + // we have arrived at the sought interval + break seek; + } + } // prepare binary search on the right side of the index + + + right = pp.length; + break linear_scan; + } //- slower code: + //- if ( t < t0 || t0 === undefined ) { + + + if (!(t >= t0)) { + // looping? + var t1global = pp[1]; + + if (t < t1global) { + i1 = 2; // + 1, using the scan for the details + + t0 = t1global; + } // linear reverse scan + + + for (var _giveUpAt = i1 - 2;;) { + if (t0 === undefined) { + // before start + this._cachedIndex = 0; + return this.beforeStart_(0, t, t1); + } + + if (i1 === _giveUpAt) break; // this loop + + t1 = t0; + t0 = pp[--i1 - 1]; + + if (t >= t0) { + // we have arrived at the sought interval + break seek; + } + } // prepare binary search on the left side of the index + + + right = i1; + i1 = 0; + break linear_scan; + } // the interval is valid + + + break validate_interval; + } // linear scan + // binary search + + + while (i1 < right) { + var mid = i1 + right >>> 1; + + if (t < pp[mid]) { + right = mid; + } else { + i1 = mid + 1; + } + } + + t1 = pp[i1]; + t0 = pp[i1 - 1]; // check boundary cases, again + + if (t0 === undefined) { + this._cachedIndex = 0; + return this.beforeStart_(0, t, t1); + } + + if (t1 === undefined) { + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_(i1 - 1, t0, t); + } + } // seek + + + this._cachedIndex = i1; + this.intervalChanged_(i1, t0, t1); + } // validate_interval + + + return this.interpolate_(i1, t0, t, t1); + }, + settings: null, + // optional, subclass-specific settings structure + // Note: The indirection allows central control of many interpolants. + // --- Protected interface + DefaultSettings_: {}, + getSettings_: function getSettings_() { + return this.settings || this.DefaultSettings_; + }, + copySampleValue_: function copySampleValue_(index) { + // copies a sample value to the result buffer + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; + + for (var i = 0; i !== stride; ++i) { + result[i] = values[offset + i]; + } + + return result; + }, + // Template methods for derived classes: + interpolate_: function interpolate_() + /* i1, t0, t, t1 */ + { + throw new Error('call to abstract method'); // implementations shall return this.resultBuffer + }, + intervalChanged_: function intervalChanged_() + /* i1, t0, t1 */ + {// empty + } + }); // DECLARE ALIAS AFTER assign prototype + + Object.assign(Interpolant.prototype, { + //( 0, t, t0 ), returns this.resultBuffer + beforeStart_: Interpolant.prototype.copySampleValue_, + //( N-1, tN-1, t ), returns this.resultBuffer + afterEnd_: Interpolant.prototype.copySampleValue_ + }); + + /** + * Fast and simple cubic spline interpolant. + * + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. + */ + + function CubicInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer); + this._weightPrev = -0; + this._offsetPrev = -0; + this._weightNext = -0; + this._offsetNext = -0; + } + + CubicInterpolant.prototype = Object.assign(Object.create(Interpolant.prototype), { + constructor: CubicInterpolant, + DefaultSettings_: { + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + }, + intervalChanged_: function intervalChanged_(i1, t0, t1) { + var pp = this.parameterPositions; + var iPrev = i1 - 2, + iNext = i1 + 1, + tPrev = pp[iPrev], + tNext = pp[iNext]; + + if (tPrev === undefined) { + switch (this.getSettings_().endingStart) { + case ZeroSlopeEnding: + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; + break; + + case WrapAroundEnding: + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; + break; + + default: + // ZeroCurvatureEnding + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; + } + } + + if (tNext === undefined) { + switch (this.getSettings_().endingEnd) { + case ZeroSlopeEnding: + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; + break; + + case WrapAroundEnding: + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[1] - pp[0]; + break; + + default: + // ZeroCurvatureEnding + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; + } + } + + var halfDt = (t1 - t0) * 0.5, + stride = this.valueSize; + this._weightPrev = halfDt / (t0 - tPrev); + this._weightNext = halfDt / (tNext - t1); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; + }, + interpolate_: function interpolate_(i1, t0, t, t1) { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + o1 = i1 * stride, + o0 = o1 - stride, + oP = this._offsetPrev, + oN = this._offsetNext, + wP = this._weightPrev, + wN = this._weightNext, + p = (t - t0) / (t1 - t0), + pp = p * p, + ppp = pp * p; // evaluate polynomials + + var sP = -wP * ppp + 2 * wP * pp - wP * p; + var s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; + var s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; + var sN = wN * ppp - wN * pp; // combine data linearly + + for (var i = 0; i !== stride; ++i) { + result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; + } + + return result; + } + }); + + function LinearInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer); + } + + LinearInterpolant.prototype = Object.assign(Object.create(Interpolant.prototype), { + constructor: LinearInterpolant, + interpolate_: function interpolate_(i1, t0, t, t1) { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset1 = i1 * stride, + offset0 = offset1 - stride, + weight1 = (t - t0) / (t1 - t0), + weight0 = 1 - weight1; + + for (var i = 0; i !== stride; ++i) { + result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; + } + + return result; + } + }); + + /** + * + * Interpolant that evaluates to the sample value at the position preceeding + * the parameter. + */ + + function DiscreteInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer); + } + + DiscreteInterpolant.prototype = Object.assign(Object.create(Interpolant.prototype), { + constructor: DiscreteInterpolant, + interpolate_: function interpolate_(i1 + /*, t0, t, t1 */ + ) { + return this.copySampleValue_(i1 - 1); + } + }); + + var KeyframeTrack = /*#__PURE__*/function () { + function KeyframeTrack(name, times, values, interpolation) { + if (name === undefined) throw new Error('THREE.KeyframeTrack: track name is undefined'); + if (times === undefined || times.length === 0) throw new Error('THREE.KeyframeTrack: no keyframes in track named ' + name); + this.name = name; + this.times = AnimationUtils.convertArray(times, this.TimeBufferType); + this.values = AnimationUtils.convertArray(values, this.ValueBufferType); + this.setInterpolation(interpolation || this.DefaultInterpolation); + } // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): + + + KeyframeTrack.toJSON = function toJSON(track) { + var trackType = track.constructor; + var json; // derived classes can define a static toJSON method + + if (trackType.toJSON !== this.toJSON) { + json = trackType.toJSON(track); + } else { + // by default, we assume the data can be serialized as-is + json = { + 'name': track.name, + 'times': AnimationUtils.convertArray(track.times, Array), + 'values': AnimationUtils.convertArray(track.values, Array) + }; + var interpolation = track.getInterpolation(); + + if (interpolation !== track.DefaultInterpolation) { + json.interpolation = interpolation; + } + } + + json.type = track.ValueTypeName; // mandatory + + return json; + }; + + var _proto = KeyframeTrack.prototype; + + _proto.InterpolantFactoryMethodDiscrete = function InterpolantFactoryMethodDiscrete(result) { + return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); + }; + + _proto.InterpolantFactoryMethodLinear = function InterpolantFactoryMethodLinear(result) { + return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); + }; + + _proto.InterpolantFactoryMethodSmooth = function InterpolantFactoryMethodSmooth(result) { + return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); + }; + + _proto.setInterpolation = function setInterpolation(interpolation) { + var factoryMethod; + + switch (interpolation) { + case InterpolateDiscrete: + factoryMethod = this.InterpolantFactoryMethodDiscrete; + break; + + case InterpolateLinear: + factoryMethod = this.InterpolantFactoryMethodLinear; + break; + + case InterpolateSmooth: + factoryMethod = this.InterpolantFactoryMethodSmooth; + break; + } + + if (factoryMethod === undefined) { + var message = 'unsupported interpolation for ' + this.ValueTypeName + ' keyframe track named ' + this.name; + + if (this.createInterpolant === undefined) { + // fall back to default, unless the default itself is messed up + if (interpolation !== this.DefaultInterpolation) { + this.setInterpolation(this.DefaultInterpolation); + } else { + throw new Error(message); // fatal, in this case + } + } + + console.warn('THREE.KeyframeTrack:', message); + return this; + } + + this.createInterpolant = factoryMethod; + return this; + }; + + _proto.getInterpolation = function getInterpolation() { + switch (this.createInterpolant) { + case this.InterpolantFactoryMethodDiscrete: + return InterpolateDiscrete; + + case this.InterpolantFactoryMethodLinear: + return InterpolateLinear; + + case this.InterpolantFactoryMethodSmooth: + return InterpolateSmooth; + } + }; + + _proto.getValueSize = function getValueSize() { + return this.values.length / this.times.length; + } // move all keyframes either forwards or backwards in time + ; + + _proto.shift = function shift(timeOffset) { + if (timeOffset !== 0.0) { + var times = this.times; + + for (var i = 0, n = times.length; i !== n; ++i) { + times[i] += timeOffset; + } + } + + return this; + } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + ; + + _proto.scale = function scale(timeScale) { + if (timeScale !== 1.0) { + var times = this.times; + + for (var i = 0, n = times.length; i !== n; ++i) { + times[i] *= timeScale; + } + } + + return this; + } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + ; + + _proto.trim = function trim(startTime, endTime) { + var times = this.times, + nKeys = times.length; + var from = 0, + to = nKeys - 1; + + while (from !== nKeys && times[from] < startTime) { + ++from; + } + + while (to !== -1 && times[to] > endTime) { + --to; + } + + ++to; // inclusive -> exclusive bound + + if (from !== 0 || to !== nKeys) { + // empty tracks are forbidden, so keep at least one keyframe + if (from >= to) { + to = Math.max(to, 1); + from = to - 1; + } + + var stride = this.getValueSize(); + this.times = AnimationUtils.arraySlice(times, from, to); + this.values = AnimationUtils.arraySlice(this.values, from * stride, to * stride); + } + + return this; + } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + ; + + _proto.validate = function validate() { + var valid = true; + var valueSize = this.getValueSize(); + + if (valueSize - Math.floor(valueSize) !== 0) { + console.error('THREE.KeyframeTrack: Invalid value size in track.', this); + valid = false; + } + + var times = this.times, + values = this.values, + nKeys = times.length; + + if (nKeys === 0) { + console.error('THREE.KeyframeTrack: Track is empty.', this); + valid = false; + } + + var prevTime = null; + + for (var i = 0; i !== nKeys; i++) { + var currTime = times[i]; + + if (typeof currTime === 'number' && isNaN(currTime)) { + console.error('THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime); + valid = false; + break; + } + + if (prevTime !== null && prevTime > currTime) { + console.error('THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime); + valid = false; + break; + } + + prevTime = currTime; + } + + if (values !== undefined) { + if (AnimationUtils.isTypedArray(values)) { + for (var _i = 0, n = values.length; _i !== n; ++_i) { + var value = values[_i]; + + if (isNaN(value)) { + console.error('THREE.KeyframeTrack: Value is not a valid number.', this, _i, value); + valid = false; + break; + } + } + } + } + + return valid; + } // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + ; + + _proto.optimize = function optimize() { + // times or values may be shared with other tracks, so overwriting is unsafe + var times = AnimationUtils.arraySlice(this.times), + values = AnimationUtils.arraySlice(this.values), + stride = this.getValueSize(), + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + lastIndex = times.length - 1; + var writeIndex = 1; + + for (var i = 1; i < lastIndex; ++i) { + var keep = false; + var time = times[i]; + var timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time + + if (time !== timeNext && (i !== 1 || time !== times[0])) { + if (!smoothInterpolation) { + // remove unnecessary keyframes same as their neighbors + var offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; + + for (var j = 0; j !== stride; ++j) { + var value = values[offset + j]; + + if (value !== values[offsetP + j] || value !== values[offsetN + j]) { + keep = true; + break; + } + } + } else { + keep = true; + } + } // in-place compaction + + + if (keep) { + if (i !== writeIndex) { + times[writeIndex] = times[i]; + var readOffset = i * stride, + writeOffset = writeIndex * stride; + + for (var _j = 0; _j !== stride; ++_j) { + values[writeOffset + _j] = values[readOffset + _j]; + } + } + + ++writeIndex; + } + } // flush last keyframe (compaction looks ahead) + + + if (lastIndex > 0) { + times[writeIndex] = times[lastIndex]; + + for (var _readOffset = lastIndex * stride, _writeOffset = writeIndex * stride, _j2 = 0; _j2 !== stride; ++_j2) { + values[_writeOffset + _j2] = values[_readOffset + _j2]; + } + + ++writeIndex; + } + + if (writeIndex !== times.length) { + this.times = AnimationUtils.arraySlice(times, 0, writeIndex); + this.values = AnimationUtils.arraySlice(values, 0, writeIndex * stride); + } else { + this.times = times; + this.values = values; + } + + return this; + }; + + _proto.clone = function clone() { + var times = AnimationUtils.arraySlice(this.times, 0); + var values = AnimationUtils.arraySlice(this.values, 0); + var TypedKeyframeTrack = this.constructor; + var track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. + + track.createInterpolant = this.createInterpolant; + return track; + }; + + return KeyframeTrack; + }(); + + KeyframeTrack.prototype.TimeBufferType = Float32Array; + KeyframeTrack.prototype.ValueBufferType = Float32Array; + KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + + /** + * A Track of Boolean keyframe values. + */ + + var BooleanKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(BooleanKeyframeTrack, _KeyframeTrack); + + function BooleanKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + return BooleanKeyframeTrack; + }(KeyframeTrack); + + BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; + BooleanKeyframeTrack.prototype.ValueBufferType = Array; + BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; // Note: Actually this track could have a optimized / compressed + + /** + * A Track of keyframe values that represent color. + */ + + var ColorKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(ColorKeyframeTrack, _KeyframeTrack); + + function ColorKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + return ColorKeyframeTrack; + }(KeyframeTrack); + + ColorKeyframeTrack.prototype.ValueTypeName = 'color'; // ValueBufferType is inherited + + /** + * A Track of numeric keyframe values. + */ + + var NumberKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(NumberKeyframeTrack, _KeyframeTrack); + + function NumberKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + return NumberKeyframeTrack; + }(KeyframeTrack); + + NumberKeyframeTrack.prototype.ValueTypeName = 'number'; // ValueBufferType is inherited + + /** + * Spherical linear unit quaternion interpolant. + */ + + function QuaternionLinearInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer); + } + + QuaternionLinearInterpolant.prototype = Object.assign(Object.create(Interpolant.prototype), { + constructor: QuaternionLinearInterpolant, + interpolate_: function interpolate_(i1, t0, t, t1) { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + alpha = (t - t0) / (t1 - t0); + var offset = i1 * stride; + + for (var end = offset + stride; offset !== end; offset += 4) { + Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); + } + + return result; + } + }); + + /** + * A Track of quaternion keyframe values. + */ + + var QuaternionKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(QuaternionKeyframeTrack, _KeyframeTrack); + + function QuaternionKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + var _proto = QuaternionKeyframeTrack.prototype; + + _proto.InterpolantFactoryMethodLinear = function InterpolantFactoryMethodLinear(result) { + return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); + }; + + return QuaternionKeyframeTrack; + }(KeyframeTrack); + + QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; // ValueBufferType is inherited + + QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + + /** + * A Track that interpolates Strings + */ + + var StringKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(StringKeyframeTrack, _KeyframeTrack); + + function StringKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + return StringKeyframeTrack; + }(KeyframeTrack); + + StringKeyframeTrack.prototype.ValueTypeName = 'string'; + StringKeyframeTrack.prototype.ValueBufferType = Array; + StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + + /** + * A Track of vectored keyframe values. + */ + + var VectorKeyframeTrack = /*#__PURE__*/function (_KeyframeTrack) { + _inheritsLoose(VectorKeyframeTrack, _KeyframeTrack); + + function VectorKeyframeTrack() { + return _KeyframeTrack.apply(this, arguments) || this; + } + + return VectorKeyframeTrack; + }(KeyframeTrack); + + VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; // ValueBufferType is inherited + + var AnimationClip = /*#__PURE__*/function () { + function AnimationClip(name, duration, tracks, blendMode) { + if (duration === void 0) { + duration = -1; + } + + if (blendMode === void 0) { + blendMode = NormalAnimationBlendMode; + } + + this.name = name; + this.tracks = tracks; + this.duration = duration; + this.blendMode = blendMode; + this.uuid = MathUtils.generateUUID(); // this means it should figure out its duration by scanning the tracks + + if (this.duration < 0) { + this.resetDuration(); + } + } + + AnimationClip.parse = function parse(json) { + var tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / (json.fps || 1.0); + + for (var i = 0, n = jsonTracks.length; i !== n; ++i) { + tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); + } + + var clip = new this(json.name, json.duration, tracks, json.blendMode); + clip.uuid = json.uuid; + return clip; + }; + + AnimationClip.toJSON = function toJSON(clip) { + var tracks = [], + clipTracks = clip.tracks; + var json = { + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid, + 'blendMode': clip.blendMode + }; + + for (var i = 0, n = clipTracks.length; i !== n; ++i) { + tracks.push(KeyframeTrack.toJSON(clipTracks[i])); + } + + return json; + }; + + AnimationClip.CreateFromMorphTargetSequence = function CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { + var numMorphTargets = morphTargetSequence.length; + var tracks = []; + + for (var i = 0; i < numMorphTargets; i++) { + var times = []; + var values = []; + times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); + values.push(0, 1, 0); + var order = AnimationUtils.getKeyframeOrder(times); + times = AnimationUtils.sortedArray(times, 1, order); + values = AnimationUtils.sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + + if (!noLoop && times[0] === 0) { + times.push(numMorphTargets); + values.push(values[0]); + } + + tracks.push(new NumberKeyframeTrack('.morphTargetInfluences[' + morphTargetSequence[i].name + ']', times, values).scale(1.0 / fps)); + } + + return new this(name, -1, tracks); + }; + + AnimationClip.findByName = function findByName(objectOrClipArray, name) { + var clipArray = objectOrClipArray; + + if (!Array.isArray(objectOrClipArray)) { + var o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; + } + + for (var i = 0; i < clipArray.length; i++) { + if (clipArray[i].name === name) { + return clipArray[i]; + } + } + + return null; + }; + + AnimationClip.CreateClipsFromMorphTargetSequences = function CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { + var animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + + var pattern = /^([\w-]*?)([\d]+)$/; // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + + for (var i = 0, il = morphTargets.length; i < il; i++) { + var morphTarget = morphTargets[i]; + var parts = morphTarget.name.match(pattern); + + if (parts && parts.length > 1) { + var name = parts[1]; + var animationMorphTargets = animationToMorphTargets[name]; + + if (!animationMorphTargets) { + animationToMorphTargets[name] = animationMorphTargets = []; + } + + animationMorphTargets.push(morphTarget); + } + } + + var clips = []; + + for (var _name in animationToMorphTargets) { + clips.push(this.CreateFromMorphTargetSequence(_name, animationToMorphTargets[_name], fps, noLoop)); + } + + return clips; + } // parse the animation.hierarchy format + ; + + AnimationClip.parseAnimation = function parseAnimation(animation, bones) { + if (!animation) { + console.error('THREE.AnimationClip: No animation in JSONLoader data.'); + return null; + } + + var addNonemptyTrack = function addNonemptyTrack(trackType, trackName, animationKeys, propertyName, destTracks) { + // only return track if there are actually keys. + if (animationKeys.length !== 0) { + var times = []; + var values = []; + AnimationUtils.flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again + + if (times.length !== 0) { + destTracks.push(new trackType(trackName, times, values)); + } + } + }; + + var tracks = []; + var clipName = animation.name || 'default'; + var fps = animation.fps || 30; + var blendMode = animation.blendMode; // automatic length determination in AnimationClip. + + var duration = animation.length || -1; + var hierarchyTracks = animation.hierarchy || []; + + for (var h = 0; h < hierarchyTracks.length; h++) { + var animationKeys = hierarchyTracks[h].keys; // skip empty tracks + + if (!animationKeys || animationKeys.length === 0) continue; // process morph targets + + if (animationKeys[0].morphTargets) { + // figure out all morph targets used in this track + var morphTargetNames = {}; + var k = void 0; + + for (k = 0; k < animationKeys.length; k++) { + if (animationKeys[k].morphTargets) { + for (var m = 0; m < animationKeys[k].morphTargets.length; m++) { + morphTargetNames[animationKeys[k].morphTargets[m]] = -1; + } + } + } // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + + + for (var morphTargetName in morphTargetNames) { + var times = []; + var values = []; + + for (var _m = 0; _m !== animationKeys[k].morphTargets.length; ++_m) { + var animationKey = animationKeys[k]; + times.push(animationKey.time); + values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); + } + + tracks.push(new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values)); + } + + duration = morphTargetNames.length * (fps || 1.0); + } else { + // ...assume skeletal animation + var boneName = '.bones[' + bones[h].name + ']'; + addNonemptyTrack(VectorKeyframeTrack, boneName + '.position', animationKeys, 'pos', tracks); + addNonemptyTrack(QuaternionKeyframeTrack, boneName + '.quaternion', animationKeys, 'rot', tracks); + addNonemptyTrack(VectorKeyframeTrack, boneName + '.scale', animationKeys, 'scl', tracks); + } + } + + if (tracks.length === 0) { + return null; + } + + var clip = new this(clipName, duration, tracks, blendMode); + return clip; + }; + + var _proto = AnimationClip.prototype; + + _proto.resetDuration = function resetDuration() { + var tracks = this.tracks; + var duration = 0; + + for (var i = 0, n = tracks.length; i !== n; ++i) { + var track = this.tracks[i]; + duration = Math.max(duration, track.times[track.times.length - 1]); + } + + this.duration = duration; + return this; + }; + + _proto.trim = function trim() { + for (var i = 0; i < this.tracks.length; i++) { + this.tracks[i].trim(0, this.duration); + } + + return this; + }; + + _proto.validate = function validate() { + var valid = true; + + for (var i = 0; i < this.tracks.length; i++) { + valid = valid && this.tracks[i].validate(); + } + + return valid; + }; + + _proto.optimize = function optimize() { + for (var i = 0; i < this.tracks.length; i++) { + this.tracks[i].optimize(); + } + + return this; + }; + + _proto.clone = function clone() { + var tracks = []; + + for (var i = 0; i < this.tracks.length; i++) { + tracks.push(this.tracks[i].clone()); + } + + return new this.constructor(this.name, this.duration, tracks, this.blendMode); + }; + + _proto.toJSON = function toJSON() { + return this.constructor.toJSON(this); + }; + + return AnimationClip; + }(); + + function getTrackTypeForValueTypeName(typeName) { + switch (typeName.toLowerCase()) { + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': + return NumberKeyframeTrack; + + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': + return VectorKeyframeTrack; + + case 'color': + return ColorKeyframeTrack; + + case 'quaternion': + return QuaternionKeyframeTrack; + + case 'bool': + case 'boolean': + return BooleanKeyframeTrack; + + case 'string': + return StringKeyframeTrack; + } + + throw new Error('THREE.KeyframeTrack: Unsupported typeName: ' + typeName); + } + + function parseKeyframeTrack(json) { + if (json.type === undefined) { + throw new Error('THREE.KeyframeTrack: track type undefined, can not parse'); + } + + var trackType = getTrackTypeForValueTypeName(json.type); + + if (json.times === undefined) { + var times = [], + values = []; + AnimationUtils.flattenJSON(json.keys, times, values, 'value'); + json.times = times; + json.values = values; + } // derived classes can define a static parse method + + + if (trackType.parse !== undefined) { + return trackType.parse(json); + } else { + // by default, we assume a constructor compatible with the base + return new trackType(json.name, json.times, json.values, json.interpolation); + } + } + + var Cache = { + enabled: false, + files: {}, + add: function add(key, file) { + if (this.enabled === false) return; // console.log( 'THREE.Cache', 'Adding key:', key ); + + this.files[key] = file; + }, + get: function get(key) { + if (this.enabled === false) return; // console.log( 'THREE.Cache', 'Checking key:', key ); + + return this.files[key]; + }, + remove: function remove(key) { + delete this.files[key]; + }, + clear: function clear() { + this.files = {}; + } + }; + + function LoadingManager(onLoad, onProgress, onError) { + var scope = this; + var isLoading = false; + var itemsLoaded = 0; + var itemsTotal = 0; + var urlModifier = undefined; + var handlers = []; // Refer to #5689 for the reason why we don't set .onStart + // in the constructor + + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function (url) { + itemsTotal++; + + if (isLoading === false) { + if (scope.onStart !== undefined) { + scope.onStart(url, itemsLoaded, itemsTotal); + } + } + + isLoading = true; + }; + + this.itemEnd = function (url) { + itemsLoaded++; + + if (scope.onProgress !== undefined) { + scope.onProgress(url, itemsLoaded, itemsTotal); + } + + if (itemsLoaded === itemsTotal) { + isLoading = false; + + if (scope.onLoad !== undefined) { + scope.onLoad(); + } + } + }; + + this.itemError = function (url) { + if (scope.onError !== undefined) { + scope.onError(url); + } + }; + + this.resolveURL = function (url) { + if (urlModifier) { + return urlModifier(url); + } + + return url; + }; + + this.setURLModifier = function (transform) { + urlModifier = transform; + return this; + }; + + this.addHandler = function (regex, loader) { + handlers.push(regex, loader); + return this; + }; + + this.removeHandler = function (regex) { + var index = handlers.indexOf(regex); + + if (index !== -1) { + handlers.splice(index, 2); + } + + return this; + }; + + this.getHandler = function (file) { + for (var i = 0, l = handlers.length; i < l; i += 2) { + var regex = handlers[i]; + var loader = handlers[i + 1]; + if (regex.global) regex.lastIndex = 0; // see #17920 + + if (regex.test(file)) { + return loader; + } + } + + return null; + }; + } + + var DefaultLoadingManager = new LoadingManager(); + + function Loader(manager) { + this.manager = manager !== undefined ? manager : DefaultLoadingManager; + this.crossOrigin = 'anonymous'; + this.withCredentials = false; + this.path = ''; + this.resourcePath = ''; + this.requestHeader = {}; + } + + Object.assign(Loader.prototype, { + load: function load() + /* url, onLoad, onProgress, onError */ + {}, + loadAsync: function loadAsync(url, onProgress) { + var scope = this; + return new Promise(function (resolve, reject) { + scope.load(url, resolve, onProgress, reject); + }); + }, + parse: function parse() + /* data */ + {}, + setCrossOrigin: function setCrossOrigin(crossOrigin) { + this.crossOrigin = crossOrigin; + return this; + }, + setWithCredentials: function setWithCredentials(value) { + this.withCredentials = value; + return this; + }, + setPath: function setPath(path) { + this.path = path; + return this; + }, + setResourcePath: function setResourcePath(resourcePath) { + this.resourcePath = resourcePath; + return this; + }, + setRequestHeader: function setRequestHeader(requestHeader) { + this.requestHeader = requestHeader; + return this; + } + }); + + var loading = {}; + + function FileLoader(manager) { + Loader.call(this, manager); + } + + FileLoader.prototype = Object.assign(Object.create(Loader.prototype), { + constructor: FileLoader, + load: function load(url, onLoad, onProgress, onError) { + if (url === undefined) url = ''; + if (this.path !== undefined) url = this.path + url; + url = this.manager.resolveURL(url); + var scope = this; + var cached = Cache.get(url); + + if (cached !== undefined) { + scope.manager.itemStart(url); + setTimeout(function () { + if (onLoad) onLoad(cached); + scope.manager.itemEnd(url); + }, 0); + return cached; + } // Check if request is duplicate + + + if (loading[url] !== undefined) { + loading[url].push({ + onLoad: onLoad, + onProgress: onProgress, + onError: onError + }); + return; + } // Check for data: URI + + + var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; + var dataUriRegexResult = url.match(dataUriRegex); + var request; // Safari can not handle Data URIs through XMLHttpRequest so process manually + + if (dataUriRegexResult) { + var mimeType = dataUriRegexResult[1]; + var isBase64 = !!dataUriRegexResult[2]; + var data = dataUriRegexResult[3]; + data = decodeURIComponent(data); + if (isBase64) data = atob(data); + + try { + var response; + var responseType = (this.responseType || '').toLowerCase(); + + switch (responseType) { + case 'arraybuffer': + case 'blob': + var view = new Uint8Array(data.length); + + for (var i = 0; i < data.length; i++) { + view[i] = data.charCodeAt(i); + } + + if (responseType === 'blob') { + response = new Blob([view.buffer], { + type: mimeType + }); + } else { + response = view.buffer; + } + + break; + + case 'document': + var parser = new DOMParser(); + response = parser.parseFromString(data, mimeType); + break; + + case 'json': + response = JSON.parse(data); + break; + + default: + // 'text' or other + response = data; + break; + } // Wait for next browser tick like standard XMLHttpRequest event dispatching does + + + setTimeout(function () { + if (onLoad) onLoad(response); + scope.manager.itemEnd(url); + }, 0); + } catch (error) { + // Wait for next browser tick like standard XMLHttpRequest event dispatching does + setTimeout(function () { + if (onError) onError(error); + scope.manager.itemError(url); + scope.manager.itemEnd(url); + }, 0); + } + } else { + // Initialise array for duplicate requests + loading[url] = []; + loading[url].push({ + onLoad: onLoad, + onProgress: onProgress, + onError: onError + }); + request = new XMLHttpRequest(); + request.open('GET', url, true); + request.addEventListener('load', function (event) { + var response = this.response; + var callbacks = loading[url]; + delete loading[url]; + + if (this.status === 200 || this.status === 0) { + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. + if (this.status === 0) console.warn('THREE.FileLoader: HTTP Status 0 received.'); // Add to cache only on HTTP success, so that we do not cache + // error response bodies as proper responses to requests. + + Cache.add(url, response); + + for (var _i = 0, il = callbacks.length; _i < il; _i++) { + var callback = callbacks[_i]; + if (callback.onLoad) callback.onLoad(response); + } + + scope.manager.itemEnd(url); + } else { + for (var _i2 = 0, _il = callbacks.length; _i2 < _il; _i2++) { + var _callback = callbacks[_i2]; + if (_callback.onError) _callback.onError(event); + } + + scope.manager.itemError(url); + scope.manager.itemEnd(url); + } + }, false); + request.addEventListener('progress', function (event) { + var callbacks = loading[url]; + + for (var _i3 = 0, il = callbacks.length; _i3 < il; _i3++) { + var callback = callbacks[_i3]; + if (callback.onProgress) callback.onProgress(event); + } + }, false); + request.addEventListener('error', function (event) { + var callbacks = loading[url]; + delete loading[url]; + + for (var _i4 = 0, il = callbacks.length; _i4 < il; _i4++) { + var callback = callbacks[_i4]; + if (callback.onError) callback.onError(event); + } + + scope.manager.itemError(url); + scope.manager.itemEnd(url); + }, false); + request.addEventListener('abort', function (event) { + var callbacks = loading[url]; + delete loading[url]; + + for (var _i5 = 0, il = callbacks.length; _i5 < il; _i5++) { + var callback = callbacks[_i5]; + if (callback.onError) callback.onError(event); + } + + scope.manager.itemError(url); + scope.manager.itemEnd(url); + }, false); + if (this.responseType !== undefined) request.responseType = this.responseType; + if (this.withCredentials !== undefined) request.withCredentials = this.withCredentials; + if (request.overrideMimeType) request.overrideMimeType(this.mimeType !== undefined ? this.mimeType : 'text/plain'); + + for (var header in this.requestHeader) { + request.setRequestHeader(header, this.requestHeader[header]); + } + + request.send(null); + } + + scope.manager.itemStart(url); + return request; + }, + setResponseType: function setResponseType(value) { + this.responseType = value; + return this; + }, + setMimeType: function setMimeType(value) { + this.mimeType = value; + return this; + } + }); + + var AnimationLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(AnimationLoader, _Loader); + + function AnimationLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = AnimationLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new FileLoader(this.manager); + loader.setPath(this.path); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(this.withCredentials); + loader.load(url, function (text) { + try { + onLoad(scope.parse(JSON.parse(text))); + } catch (e) { + if (onError) { + onError(e); + } else { + console.error(e); + } + + scope.manager.itemError(url); + } + }, onProgress, onError); + }; + + _proto.parse = function parse(json) { + var animations = []; + + for (var i = 0; i < json.length; i++) { + var clip = AnimationClip.parse(json[i]); + animations.push(clip); + } + + return animations; + }; + + return AnimationLoader; + }(Loader); + + /** + * Abstract Base class to block based textures loader (dds, pvr, ...) + * + * Sub classes have to implement the parse() method which will be used in load(). + */ + + function CompressedTextureLoader(manager) { + Loader.call(this, manager); + } + + CompressedTextureLoader.prototype = Object.assign(Object.create(Loader.prototype), { + constructor: CompressedTextureLoader, + load: function load(url, onLoad, onProgress, onError) { + var scope = this; + var images = []; + var texture = new CompressedTexture(); + var loader = new FileLoader(this.manager); + loader.setPath(this.path); + loader.setResponseType('arraybuffer'); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(scope.withCredentials); + var loaded = 0; + + function loadTexture(i) { + loader.load(url[i], function (buffer) { + var texDatas = scope.parse(buffer, true); + images[i] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + loaded += 1; + + if (loaded === 6) { + if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; + texture.image = images; + texture.format = texDatas.format; + texture.needsUpdate = true; + if (onLoad) onLoad(texture); + } + }, onProgress, onError); + } + + if (Array.isArray(url)) { + for (var i = 0, il = url.length; i < il; ++i) { + loadTexture(i); + } + } else { + // compressed cubemap texture stored in a single DDS file + loader.load(url, function (buffer) { + var texDatas = scope.parse(buffer, true); + + if (texDatas.isCubemap) { + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + + for (var f = 0; f < faces; f++) { + images[f] = { + mipmaps: [] + }; + + for (var _i = 0; _i < texDatas.mipmapCount; _i++) { + images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + _i]); + images[f].format = texDatas.format; + images[f].width = texDatas.width; + images[f].height = texDatas.height; + } + } + + texture.image = images; + } else { + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + } + + if (texDatas.mipmapCount === 1) { + texture.minFilter = LinearFilter; + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + if (onLoad) onLoad(texture); + }, onProgress, onError); + } + + return texture; + } + }); + + var ImageLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(ImageLoader, _Loader); + + function ImageLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = ImageLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + if (this.path !== undefined) url = this.path + url; + url = this.manager.resolveURL(url); + var scope = this; + var cached = Cache.get(url); + + if (cached !== undefined) { + scope.manager.itemStart(url); + setTimeout(function () { + if (onLoad) onLoad(cached); + scope.manager.itemEnd(url); + }, 0); + return cached; + } + + var image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img'); + + function onImageLoad() { + image.removeEventListener('load', onImageLoad, false); + image.removeEventListener('error', onImageError, false); + Cache.add(url, this); + if (onLoad) onLoad(this); + scope.manager.itemEnd(url); + } + + function onImageError(event) { + image.removeEventListener('load', onImageLoad, false); + image.removeEventListener('error', onImageError, false); + if (onError) onError(event); + scope.manager.itemError(url); + scope.manager.itemEnd(url); + } + + image.addEventListener('load', onImageLoad, false); + image.addEventListener('error', onImageError, false); + + if (url.substr(0, 5) !== 'data:') { + if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; + } + + scope.manager.itemStart(url); + image.src = url; + return image; + }; + + return ImageLoader; + }(Loader); + + var CubeTextureLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(CubeTextureLoader, _Loader); + + function CubeTextureLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = CubeTextureLoader.prototype; + + _proto.load = function load(urls, onLoad, onProgress, onError) { + var texture = new CubeTexture(); + var loader = new ImageLoader(this.manager); + loader.setCrossOrigin(this.crossOrigin); + loader.setPath(this.path); + var loaded = 0; + + function loadTexture(i) { + loader.load(urls[i], function (image) { + texture.images[i] = image; + loaded++; + + if (loaded === 6) { + texture.needsUpdate = true; + if (onLoad) onLoad(texture); + } + }, undefined, onError); + } + + for (var i = 0; i < urls.length; ++i) { + loadTexture(i); + } + + return texture; + }; + + return CubeTextureLoader; + }(Loader); + + /** + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + * + * Sub classes have to implement the parse() method which will be used in load(). + */ + + function DataTextureLoader(manager) { + Loader.call(this, manager); + } + + DataTextureLoader.prototype = Object.assign(Object.create(Loader.prototype), { + constructor: DataTextureLoader, + load: function load(url, onLoad, onProgress, onError) { + var scope = this; + var texture = new DataTexture(); + var loader = new FileLoader(this.manager); + loader.setResponseType('arraybuffer'); + loader.setRequestHeader(this.requestHeader); + loader.setPath(this.path); + loader.setWithCredentials(scope.withCredentials); + loader.load(url, function (buffer) { + var texData = scope.parse(buffer); + if (!texData) return; + + if (texData.image !== undefined) { + texture.image = texData.image; + } else if (texData.data !== undefined) { + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; + } + + texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; + texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; + texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; + texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; + texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; + + if (texData.encoding !== undefined) { + texture.encoding = texData.encoding; + } + + if (texData.flipY !== undefined) { + texture.flipY = texData.flipY; + } + + if (texData.format !== undefined) { + texture.format = texData.format; + } + + if (texData.type !== undefined) { + texture.type = texData.type; + } + + if (texData.mipmaps !== undefined) { + texture.mipmaps = texData.mipmaps; + texture.minFilter = LinearMipmapLinearFilter; // presumably... + } + + if (texData.mipmapCount === 1) { + texture.minFilter = LinearFilter; + } + + texture.needsUpdate = true; + if (onLoad) onLoad(texture, texData); + }, onProgress, onError); + return texture; + } + }); + + function TextureLoader(manager) { + Loader.call(this, manager); + } + + TextureLoader.prototype = Object.assign(Object.create(Loader.prototype), { + constructor: TextureLoader, + load: function load(url, onLoad, onProgress, onError) { + var texture = new Texture(); + var loader = new ImageLoader(this.manager); + loader.setCrossOrigin(this.crossOrigin); + loader.setPath(this.path); + loader.load(url, function (image) { + texture.image = image; // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. + + var isJPEG = url.search(/\.jpe?g($|\?)/i) > 0 || url.search(/^data\:image\/jpeg/) === 0; + texture.format = isJPEG ? RGBFormat : RGBAFormat; + texture.needsUpdate = true; + + if (onLoad !== undefined) { + onLoad(texture); + } + }, onProgress, onError); + return texture; + } + }); + + /** + * Extensible curve object. + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following curves inherit from THREE.Curve: + * + * -- 2D curves -- + * THREE.ArcCurve + * THREE.CubicBezierCurve + * THREE.EllipseCurve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.SplineCurve + * + * -- 3D curves -- + * THREE.CatmullRomCurve3 + * THREE.CubicBezierCurve3 + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * + * A series of curves can be represented as a THREE.CurvePath. + * + **/ + + function Curve() { + this.type = 'Curve'; + this.arcLengthDivisions = 200; + } + + Object.assign(Curve.prototype, { + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] + getPoint: function getPoint() + /* t, optionalTarget */ + { + console.warn('THREE.Curve: .getPoint() not implemented.'); + return null; + }, + // Get point at relative position in curve according to arc length + // - u [0 .. 1] + getPointAt: function getPointAt(u, optionalTarget) { + var t = this.getUtoTmapping(u); + return this.getPoint(t, optionalTarget); + }, + // Get sequence of points using getPoint( t ) + getPoints: function getPoints(divisions) { + if (divisions === void 0) { + divisions = 5; + } + + var points = []; + + for (var d = 0; d <= divisions; d++) { + points.push(this.getPoint(d / divisions)); + } + + return points; + }, + // Get sequence of points using getPointAt( u ) + getSpacedPoints: function getSpacedPoints(divisions) { + if (divisions === void 0) { + divisions = 5; + } + + var points = []; + + for (var d = 0; d <= divisions; d++) { + points.push(this.getPointAt(d / divisions)); + } + + return points; + }, + // Get total curve arc length + getLength: function getLength() { + var lengths = this.getLengths(); + return lengths[lengths.length - 1]; + }, + // Get list of cumulative segment lengths + getLengths: function getLengths(divisions) { + if (divisions === undefined) divisions = this.arcLengthDivisions; + + if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { + return this.cacheArcLengths; + } + + this.needsUpdate = false; + var cache = []; + var current, + last = this.getPoint(0); + var sum = 0; + cache.push(0); + + for (var p = 1; p <= divisions; p++) { + current = this.getPoint(p / divisions); + sum += current.distanceTo(last); + cache.push(sum); + last = current; + } + + this.cacheArcLengths = cache; + return cache; // { sums: cache, sum: sum }; Sum is in the last element. + }, + updateArcLengths: function updateArcLengths() { + this.needsUpdate = true; + this.getLengths(); + }, + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + getUtoTmapping: function getUtoTmapping(u, distance) { + var arcLengths = this.getLengths(); + var i = 0; + var il = arcLengths.length; + var targetArcLength; // The targeted u distance value to get + + if (distance) { + targetArcLength = distance; + } else { + targetArcLength = u * arcLengths[il - 1]; + } // binary search for the index with largest value smaller than target u distance + + + var low = 0, + high = il - 1, + comparison; + + while (low <= high) { + i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[i] - targetArcLength; + + if (comparison < 0) { + low = i + 1; + } else if (comparison > 0) { + high = i - 1; + } else { + high = i; + break; // DONE + } + } + + i = high; + + if (arcLengths[i] === targetArcLength) { + return i / (il - 1); + } // we could get finer grain at lengths, or use simple interpolation between two points + + + var lengthBefore = arcLengths[i]; + var lengthAfter = arcLengths[i + 1]; + var segmentLength = lengthAfter - lengthBefore; // determine where we are between the 'before' and 'after' points + + var segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t + + var t = (i + segmentFraction) / (il - 1); + return t; + }, + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation + getTangent: function getTangent(t, optionalTarget) { + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; // Capping in case of danger + + if (t1 < 0) t1 = 0; + if (t2 > 1) t2 = 1; + var pt1 = this.getPoint(t1); + var pt2 = this.getPoint(t2); + var tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); + tangent.copy(pt2).sub(pt1).normalize(); + return tangent; + }, + getTangentAt: function getTangentAt(u, optionalTarget) { + var t = this.getUtoTmapping(u); + return this.getTangent(t, optionalTarget); + }, + computeFrenetFrames: function computeFrenetFrames(segments, closed) { + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf + var normal = new Vector3(); + var tangents = []; + var normals = []; + var binormals = []; + var vec = new Vector3(); + var mat = new Matrix4(); // compute the tangent vectors for each segment on the curve + + for (var i = 0; i <= segments; i++) { + var u = i / segments; + tangents[i] = this.getTangentAt(u, new Vector3()); + tangents[i].normalize(); + } // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component + + + normals[0] = new Vector3(); + binormals[0] = new Vector3(); + var min = Number.MAX_VALUE; + var tx = Math.abs(tangents[0].x); + var ty = Math.abs(tangents[0].y); + var tz = Math.abs(tangents[0].z); + + if (tx <= min) { + min = tx; + normal.set(1, 0, 0); + } + + if (ty <= min) { + min = ty; + normal.set(0, 1, 0); + } + + if (tz <= min) { + normal.set(0, 0, 1); + } + + vec.crossVectors(tangents[0], normal).normalize(); + normals[0].crossVectors(tangents[0], vec); + binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve + + for (var _i = 1; _i <= segments; _i++) { + normals[_i] = normals[_i - 1].clone(); + binormals[_i] = binormals[_i - 1].clone(); + vec.crossVectors(tangents[_i - 1], tangents[_i]); + + if (vec.length() > Number.EPSILON) { + vec.normalize(); + var theta = Math.acos(MathUtils.clamp(tangents[_i - 1].dot(tangents[_i]), -1, 1)); // clamp for floating pt errors + + normals[_i].applyMatrix4(mat.makeRotationAxis(vec, theta)); + } + + binormals[_i].crossVectors(tangents[_i], normals[_i]); + } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + + if (closed === true) { + var _theta = Math.acos(MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1)); + + _theta /= segments; + + if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { + _theta = -_theta; + } + + for (var _i2 = 1; _i2 <= segments; _i2++) { + // twist a little... + normals[_i2].applyMatrix4(mat.makeRotationAxis(tangents[_i2], _theta * _i2)); + + binormals[_i2].crossVectors(tangents[_i2], normals[_i2]); + } + } + + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; + }, + clone: function clone() { + return new this.constructor().copy(this); + }, + copy: function copy(source) { + this.arcLengthDivisions = source.arcLengthDivisions; + return this; + }, + toJSON: function toJSON() { + var data = { + metadata: { + version: 4.5, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; + return data; + }, + fromJSON: function fromJSON(json) { + this.arcLengthDivisions = json.arcLengthDivisions; + return this; + } + }); + + var EllipseCurve = /*#__PURE__*/function (_Curve) { + _inheritsLoose(EllipseCurve, _Curve); + + function EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { + var _this; + + if (aX === void 0) { + aX = 0; + } + + if (aY === void 0) { + aY = 0; + } + + if (xRadius === void 0) { + xRadius = 1; + } + + if (yRadius === void 0) { + yRadius = 1; + } + + if (aStartAngle === void 0) { + aStartAngle = 0; + } + + if (aEndAngle === void 0) { + aEndAngle = Math.PI * 2; + } + + if (aClockwise === void 0) { + aClockwise = false; + } + + if (aRotation === void 0) { + aRotation = 0; + } + + _this = _Curve.call(this) || this; + _this.type = 'EllipseCurve'; + _this.aX = aX; + _this.aY = aY; + _this.xRadius = xRadius; + _this.yRadius = yRadius; + _this.aStartAngle = aStartAngle; + _this.aEndAngle = aEndAngle; + _this.aClockwise = aClockwise; + _this.aRotation = aRotation; + return _this; + } + + var _proto = EllipseCurve.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + var point = optionalTarget || new Vector2(); + var twoPi = Math.PI * 2; + var deltaAngle = this.aEndAngle - this.aStartAngle; + var samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI + + while (deltaAngle < 0) { + deltaAngle += twoPi; + } + + while (deltaAngle > twoPi) { + deltaAngle -= twoPi; + } + + if (deltaAngle < Number.EPSILON) { + if (samePoints) { + deltaAngle = 0; + } else { + deltaAngle = twoPi; + } + } + + if (this.aClockwise === true && !samePoints) { + if (deltaAngle === twoPi) { + deltaAngle = -twoPi; + } else { + deltaAngle = deltaAngle - twoPi; + } + } + + var angle = this.aStartAngle + t * deltaAngle; + var x = this.aX + this.xRadius * Math.cos(angle); + var y = this.aY + this.yRadius * Math.sin(angle); + + if (this.aRotation !== 0) { + var cos = Math.cos(this.aRotation); + var sin = Math.sin(this.aRotation); + var tx = x - this.aX; + var ty = y - this.aY; // Rotate the point about the center of the ellipse. + + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; + } + + return point.set(x, y); + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.aX = source.aX; + this.aY = source.aY; + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; + this.aClockwise = source.aClockwise; + this.aRotation = source.aRotation; + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.aX = this.aX; + data.aY = this.aY; + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; + data.aClockwise = this.aClockwise; + data.aRotation = this.aRotation; + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.aX = json.aX; + this.aY = json.aY; + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; + this.aClockwise = json.aClockwise; + this.aRotation = json.aRotation; + return this; + }; + + return EllipseCurve; + }(Curve); + + EllipseCurve.prototype.isEllipseCurve = true; + + var ArcCurve = /*#__PURE__*/function (_EllipseCurve) { + _inheritsLoose(ArcCurve, _EllipseCurve); + + function ArcCurve(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { + var _this; + + _this = _EllipseCurve.call(this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise) || this; + _this.type = 'ArcCurve'; + return _this; + } + + return ArcCurve; + }(EllipseCurve); + + ArcCurve.prototype.isArcCurve = true; + + /** + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ + + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM + + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ + + function CubicPoly() { + var c0 = 0, + c1 = 0, + c2 = 0, + c3 = 0; + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + + function init(x0, x1, t0, t1) { + c0 = x0; + c1 = t0; + c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; + } + + return { + initCatmullRom: function initCatmullRom(x0, x1, x2, x3, tension) { + init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); + }, + initNonuniformCatmullRom: function initNonuniformCatmullRom(x0, x1, x2, x3, dt0, dt1, dt2) { + // compute tangents when parameterized in [t1,t2] + var t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; + var t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] + + t1 *= dt1; + t2 *= dt1; + init(x1, x2, t1, t2); + }, + calc: function calc(t) { + var t2 = t * t; + var t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; + } + }; + } // + + + var tmp = new Vector3(); + var px = new CubicPoly(), + py = new CubicPoly(), + pz = new CubicPoly(); + + var CatmullRomCurve3 = /*#__PURE__*/function (_Curve) { + _inheritsLoose(CatmullRomCurve3, _Curve); + + function CatmullRomCurve3(points, closed, curveType, tension) { + var _this; + + if (points === void 0) { + points = []; + } + + if (closed === void 0) { + closed = false; + } + + if (curveType === void 0) { + curveType = 'centripetal'; + } + + if (tension === void 0) { + tension = 0.5; + } + + _this = _Curve.call(this) || this; + _this.type = 'CatmullRomCurve3'; + _this.points = points; + _this.closed = closed; + _this.curveType = curveType; + _this.tension = tension; + return _this; + } + + var _proto = CatmullRomCurve3.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector3(); + } + + var point = optionalTarget; + var points = this.points; + var l = points.length; + var p = (l - (this.closed ? 0 : 1)) * t; + var intPoint = Math.floor(p); + var weight = p - intPoint; + + if (this.closed) { + intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; + } else if (weight === 0 && intPoint === l - 1) { + intPoint = l - 2; + weight = 1; + } + + var p0, p3; // 4 points (p1 & p2 defined below) + + if (this.closed || intPoint > 0) { + p0 = points[(intPoint - 1) % l]; + } else { + // extrapolate first point + tmp.subVectors(points[0], points[1]).add(points[0]); + p0 = tmp; + } + + var p1 = points[intPoint % l]; + var p2 = points[(intPoint + 1) % l]; + + if (this.closed || intPoint + 2 < l) { + p3 = points[(intPoint + 2) % l]; + } else { + // extrapolate last point + tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); + p3 = tmp; + } + + if (this.curveType === 'centripetal' || this.curveType === 'chordal') { + // init Centripetal / Chordal Catmull-Rom + var pow = this.curveType === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow(p0.distanceToSquared(p1), pow); + var dt1 = Math.pow(p1.distanceToSquared(p2), pow); + var dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points + + if (dt1 < 1e-4) dt1 = 1.0; + if (dt0 < 1e-4) dt0 = dt1; + if (dt2 < 1e-4) dt2 = dt1; + px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); + py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); + pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); + } else if (this.curveType === 'catmullrom') { + px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); + py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); + pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); + } + + point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.points = []; + + for (var i = 0, l = source.points.length; i < l; i++) { + var point = source.points[i]; + this.points.push(point.clone()); + } + + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.points = []; + + for (var i = 0, l = this.points.length; i < l; i++) { + var point = this.points[i]; + data.points.push(point.toArray()); + } + + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.points = []; + + for (var i = 0, l = json.points.length; i < l; i++) { + var point = json.points[i]; + this.points.push(new Vector3().fromArray(point)); + } + + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; + return this; + }; + + return CatmullRomCurve3; + }(Curve); + + CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + + /** + * Bezier Curves formulas obtained from + * http://en.wikipedia.org/wiki/Bézier_curve + */ + function CatmullRom(t, p0, p1, p2, p3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; + } // + + + function QuadraticBezierP0(t, p) { + var k = 1 - t; + return k * k * p; + } + + function QuadraticBezierP1(t, p) { + return 2 * (1 - t) * t * p; + } + + function QuadraticBezierP2(t, p) { + return t * t * p; + } + + function QuadraticBezier(t, p0, p1, p2) { + return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); + } // + + + function CubicBezierP0(t, p) { + var k = 1 - t; + return k * k * k * p; + } + + function CubicBezierP1(t, p) { + var k = 1 - t; + return 3 * k * k * t * p; + } + + function CubicBezierP2(t, p) { + return 3 * (1 - t) * t * t * p; + } + + function CubicBezierP3(t, p) { + return t * t * t * p; + } + + function CubicBezier(t, p0, p1, p2, p3) { + return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); + } + + var CubicBezierCurve = /*#__PURE__*/function (_Curve) { + _inheritsLoose(CubicBezierCurve, _Curve); + + function CubicBezierCurve(v0, v1, v2, v3) { + var _this; + + if (v0 === void 0) { + v0 = new Vector2(); + } + + if (v1 === void 0) { + v1 = new Vector2(); + } + + if (v2 === void 0) { + v2 = new Vector2(); + } + + if (v3 === void 0) { + v3 = new Vector2(); + } + + _this = _Curve.call(this) || this; + _this.type = 'CubicBezierCurve'; + _this.v0 = v0; + _this.v1 = v1; + _this.v2 = v2; + _this.v3 = v3; + return _this; + } + + var _proto = CubicBezierCurve.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector2(); + } + + var point = optionalTarget; + var v0 = this.v0, + v1 = this.v1, + v2 = this.v2, + v3 = this.v3; + point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v0.copy(source.v0); + this.v1.copy(source.v1); + this.v2.copy(source.v2); + this.v3.copy(source.v3); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v0.fromArray(json.v0); + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + this.v3.fromArray(json.v3); + return this; + }; + + return CubicBezierCurve; + }(Curve); + + CubicBezierCurve.prototype.isCubicBezierCurve = true; + + var CubicBezierCurve3 = /*#__PURE__*/function (_Curve) { + _inheritsLoose(CubicBezierCurve3, _Curve); + + function CubicBezierCurve3(v0, v1, v2, v3) { + var _this; + + if (v0 === void 0) { + v0 = new Vector3(); + } + + if (v1 === void 0) { + v1 = new Vector3(); + } + + if (v2 === void 0) { + v2 = new Vector3(); + } + + if (v3 === void 0) { + v3 = new Vector3(); + } + + _this = _Curve.call(this) || this; + _this.type = 'CubicBezierCurve3'; + _this.v0 = v0; + _this.v1 = v1; + _this.v2 = v2; + _this.v3 = v3; + return _this; + } + + var _proto = CubicBezierCurve3.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector3(); + } + + var point = optionalTarget; + var v0 = this.v0, + v1 = this.v1, + v2 = this.v2, + v3 = this.v3; + point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v0.copy(source.v0); + this.v1.copy(source.v1); + this.v2.copy(source.v2); + this.v3.copy(source.v3); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v0.fromArray(json.v0); + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + this.v3.fromArray(json.v3); + return this; + }; + + return CubicBezierCurve3; + }(Curve); + + CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; + + var LineCurve = /*#__PURE__*/function (_Curve) { + _inheritsLoose(LineCurve, _Curve); + + function LineCurve(v1, v2) { + var _this; + + if (v1 === void 0) { + v1 = new Vector2(); + } + + if (v2 === void 0) { + v2 = new Vector2(); + } + + _this = _Curve.call(this) || this; + _this.type = 'LineCurve'; + _this.v1 = v1; + _this.v2 = v2; + return _this; + } + + var _proto = LineCurve.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector2(); + } + + var point = optionalTarget; + + if (t === 1) { + point.copy(this.v2); + } else { + point.copy(this.v2).sub(this.v1); + point.multiplyScalar(t).add(this.v1); + } + + return point; + } // Line curve is linear, so we can overwrite default getPointAt + ; + + _proto.getPointAt = function getPointAt(u, optionalTarget) { + return this.getPoint(u, optionalTarget); + }; + + _proto.getTangent = function getTangent(t, optionalTarget) { + var tangent = optionalTarget || new Vector2(); + tangent.copy(this.v2).sub(this.v1).normalize(); + return tangent; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v1.copy(source.v1); + this.v2.copy(source.v2); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + return this; + }; + + return LineCurve; + }(Curve); + + LineCurve.prototype.isLineCurve = true; + + var LineCurve3 = /*#__PURE__*/function (_Curve) { + _inheritsLoose(LineCurve3, _Curve); + + function LineCurve3(v1, v2) { + var _this; + + if (v1 === void 0) { + v1 = new Vector3(); + } + + if (v2 === void 0) { + v2 = new Vector3(); + } + + _this = _Curve.call(this) || this; + _this.type = 'LineCurve3'; + _this.isLineCurve3 = true; + _this.v1 = v1; + _this.v2 = v2; + return _this; + } + + var _proto = LineCurve3.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector3(); + } + + var point = optionalTarget; + + if (t === 1) { + point.copy(this.v2); + } else { + point.copy(this.v2).sub(this.v1); + point.multiplyScalar(t).add(this.v1); + } + + return point; + } // Line curve is linear, so we can overwrite default getPointAt + ; + + _proto.getPointAt = function getPointAt(u, optionalTarget) { + return this.getPoint(u, optionalTarget); + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v1.copy(source.v1); + this.v2.copy(source.v2); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + return this; + }; + + return LineCurve3; + }(Curve); + + var QuadraticBezierCurve = /*#__PURE__*/function (_Curve) { + _inheritsLoose(QuadraticBezierCurve, _Curve); + + function QuadraticBezierCurve(v0, v1, v2) { + var _this; + + if (v0 === void 0) { + v0 = new Vector2(); + } + + if (v1 === void 0) { + v1 = new Vector2(); + } + + if (v2 === void 0) { + v2 = new Vector2(); + } + + _this = _Curve.call(this) || this; + _this.type = 'QuadraticBezierCurve'; + _this.v0 = v0; + _this.v1 = v1; + _this.v2 = v2; + return _this; + } + + var _proto = QuadraticBezierCurve.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector2(); + } + + var point = optionalTarget; + var v0 = this.v0, + v1 = this.v1, + v2 = this.v2; + point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v0.copy(source.v0); + this.v1.copy(source.v1); + this.v2.copy(source.v2); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v0.fromArray(json.v0); + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + return this; + }; + + return QuadraticBezierCurve; + }(Curve); + + QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; + + var QuadraticBezierCurve3 = /*#__PURE__*/function (_Curve) { + _inheritsLoose(QuadraticBezierCurve3, _Curve); + + function QuadraticBezierCurve3(v0, v1, v2) { + var _this; + + if (v0 === void 0) { + v0 = new Vector3(); + } + + if (v1 === void 0) { + v1 = new Vector3(); + } + + if (v2 === void 0) { + v2 = new Vector3(); + } + + _this = _Curve.call(this) || this; + _this.type = 'QuadraticBezierCurve3'; + _this.v0 = v0; + _this.v1 = v1; + _this.v2 = v2; + return _this; + } + + var _proto = QuadraticBezierCurve3.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector3(); + } + + var point = optionalTarget; + var v0 = this.v0, + v1 = this.v1, + v2 = this.v2; + point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.v0.copy(source.v0); + this.v1.copy(source.v1); + this.v2.copy(source.v2); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.v0.fromArray(json.v0); + this.v1.fromArray(json.v1); + this.v2.fromArray(json.v2); + return this; + }; + + return QuadraticBezierCurve3; + }(Curve); + + QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + + var SplineCurve = /*#__PURE__*/function (_Curve) { + _inheritsLoose(SplineCurve, _Curve); + + function SplineCurve(points) { + var _this; + + if (points === void 0) { + points = []; + } + + _this = _Curve.call(this) || this; + _this.type = 'SplineCurve'; + _this.points = points; + return _this; + } + + var _proto = SplineCurve.prototype; + + _proto.getPoint = function getPoint(t, optionalTarget) { + if (optionalTarget === void 0) { + optionalTarget = new Vector2(); + } + + var point = optionalTarget; + var points = this.points; + var p = (points.length - 1) * t; + var intPoint = Math.floor(p); + var weight = p - intPoint; + var p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; + var p1 = points[intPoint]; + var p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; + var p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; + point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); + return point; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.points = []; + + for (var i = 0, l = source.points.length; i < l; i++) { + var point = source.points[i]; + this.points.push(point.clone()); + } + + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.points = []; + + for (var i = 0, l = this.points.length; i < l; i++) { + var point = this.points[i]; + data.points.push(point.toArray()); + } + + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.points = []; + + for (var i = 0, l = json.points.length; i < l; i++) { + var point = json.points[i]; + this.points.push(new Vector2().fromArray(point)); + } + + return this; + }; + + return SplineCurve; + }(Curve); + + SplineCurve.prototype.isSplineCurve = true; + + var Curves = /*#__PURE__*/Object.freeze({ + __proto__: null, + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve + }); + + /************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + + var CurvePath = /*#__PURE__*/function (_Curve) { + _inheritsLoose(CurvePath, _Curve); + + function CurvePath() { + var _this; + + _this = _Curve.call(this) || this; + _this.type = 'CurvePath'; + _this.curves = []; + _this.autoClose = false; // Automatically closes the path + + return _this; + } + + var _proto = CurvePath.prototype; + + _proto.add = function add(curve) { + this.curves.push(curve); + }; + + _proto.closePath = function closePath() { + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length - 1].getPoint(1); + + if (!startPoint.equals(endPoint)) { + this.curves.push(new LineCurve(endPoint, startPoint)); + } + } // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') + ; + + _proto.getPoint = function getPoint(t) { + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0; // To think about boundaries points. + + while (i < curveLengths.length) { + if (curveLengths[i] >= d) { + var diff = curveLengths[i] - d; + var curve = this.curves[i]; + var segmentLength = curve.getLength(); + var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + return curve.getPointAt(u); + } + + i++; + } + + return null; // loop where sum != 0, sum > d , sum+1 1 && !points[points.length - 1].equals(points[0])) { + points.push(points[0]); + } + + return points; + }; + + _proto.copy = function copy(source) { + _Curve.prototype.copy.call(this, source); + + this.curves = []; + + for (var i = 0, l = source.curves.length; i < l; i++) { + var curve = source.curves[i]; + this.curves.push(curve.clone()); + } + + this.autoClose = source.autoClose; + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Curve.prototype.toJSON.call(this); + + data.autoClose = this.autoClose; + data.curves = []; + + for (var i = 0, l = this.curves.length; i < l; i++) { + var curve = this.curves[i]; + data.curves.push(curve.toJSON()); + } + + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Curve.prototype.fromJSON.call(this, json); + + this.autoClose = json.autoClose; + this.curves = []; + + for (var i = 0, l = json.curves.length; i < l; i++) { + var curve = json.curves[i]; + this.curves.push(new Curves[curve.type]().fromJSON(curve)); + } + + return this; + }; + + return CurvePath; + }(Curve); + + var Path = /*#__PURE__*/function (_CurvePath) { + _inheritsLoose(Path, _CurvePath); + + function Path(points) { + var _this; + + _this = _CurvePath.call(this) || this; + _this.type = 'Path'; + _this.currentPoint = new Vector2(); + + if (points) { + _this.setFromPoints(points); + } + + return _this; + } + + var _proto = Path.prototype; + + _proto.setFromPoints = function setFromPoints(points) { + this.moveTo(points[0].x, points[0].y); + + for (var i = 1, l = points.length; i < l; i++) { + this.lineTo(points[i].x, points[i].y); + } + + return this; + }; + + _proto.moveTo = function moveTo(x, y) { + this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? + + return this; + }; + + _proto.lineTo = function lineTo(x, y) { + var curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); + this.curves.push(curve); + this.currentPoint.set(x, y); + return this; + }; + + _proto.quadraticCurveTo = function quadraticCurveTo(aCPx, aCPy, aX, aY) { + var curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); + this.curves.push(curve); + this.currentPoint.set(aX, aY); + return this; + }; + + _proto.bezierCurveTo = function bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { + var curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); + this.curves.push(curve); + this.currentPoint.set(aX, aY); + return this; + }; + + _proto.splineThru = function splineThru(pts + /*Array of Vector*/ + ) { + var npts = [this.currentPoint.clone()].concat(pts); + var curve = new SplineCurve(npts); + this.curves.push(curve); + this.currentPoint.copy(pts[pts.length - 1]); + return this; + }; + + _proto.arc = function arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; + this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); + return this; + }; + + _proto.absarc = function absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + return this; + }; + + _proto.ellipse = function ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); + return this; + }; + + _proto.absellipse = function absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { + var curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); + + if (this.curves.length > 0) { + // if a previous curve is present, attempt to join + var firstPoint = curve.getPoint(0); + + if (!firstPoint.equals(this.currentPoint)) { + this.lineTo(firstPoint.x, firstPoint.y); + } + } + + this.curves.push(curve); + var lastPoint = curve.getPoint(1); + this.currentPoint.copy(lastPoint); + return this; + }; + + _proto.copy = function copy(source) { + _CurvePath.prototype.copy.call(this, source); + + this.currentPoint.copy(source.currentPoint); + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _CurvePath.prototype.toJSON.call(this); + + data.currentPoint = this.currentPoint.toArray(); + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _CurvePath.prototype.fromJSON.call(this, json); + + this.currentPoint.fromArray(json.currentPoint); + return this; + }; + + return Path; + }(CurvePath); + + var Shape = /*#__PURE__*/function (_Path) { + _inheritsLoose(Shape, _Path); + + function Shape(points) { + var _this; + + _this = _Path.call(this, points) || this; + _this.uuid = MathUtils.generateUUID(); + _this.type = 'Shape'; + _this.holes = []; + return _this; + } + + var _proto = Shape.prototype; + + _proto.getPointsHoles = function getPointsHoles(divisions) { + var holesPts = []; + + for (var i = 0, l = this.holes.length; i < l; i++) { + holesPts[i] = this.holes[i].getPoints(divisions); + } + + return holesPts; + } // get points of shape and holes (keypoints based on segments parameter) + ; + + _proto.extractPoints = function extractPoints(divisions) { + return { + shape: this.getPoints(divisions), + holes: this.getPointsHoles(divisions) + }; + }; + + _proto.copy = function copy(source) { + _Path.prototype.copy.call(this, source); + + this.holes = []; + + for (var i = 0, l = source.holes.length; i < l; i++) { + var hole = source.holes[i]; + this.holes.push(hole.clone()); + } + + return this; + }; + + _proto.toJSON = function toJSON() { + var data = _Path.prototype.toJSON.call(this); + + data.uuid = this.uuid; + data.holes = []; + + for (var i = 0, l = this.holes.length; i < l; i++) { + var hole = this.holes[i]; + data.holes.push(hole.toJSON()); + } + + return data; + }; + + _proto.fromJSON = function fromJSON(json) { + _Path.prototype.fromJSON.call(this, json); + + this.uuid = json.uuid; + this.holes = []; + + for (var i = 0, l = json.holes.length; i < l; i++) { + var hole = json.holes[i]; + this.holes.push(new Path().fromJSON(hole)); + } + + return this; + }; + + return Shape; + }(Path); + + var Light = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(Light, _Object3D); + + function Light(color, intensity) { + var _this; + + if (intensity === void 0) { + intensity = 1; + } + + _this = _Object3D.call(this) || this; + _this.type = 'Light'; + _this.color = new Color(color); + _this.intensity = intensity; + return _this; + } + + var _proto = Light.prototype; + + _proto.copy = function copy(source) { + _Object3D.prototype.copy.call(this, source); + + this.color.copy(source.color); + this.intensity = source.intensity; + return this; + }; + + _proto.toJSON = function toJSON(meta) { + var data = _Object3D.prototype.toJSON.call(this, meta); + + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; + if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); + if (this.distance !== undefined) data.object.distance = this.distance; + if (this.angle !== undefined) data.object.angle = this.angle; + if (this.decay !== undefined) data.object.decay = this.decay; + if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; + if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); + return data; + }; + + return Light; + }(Object3D); + + Light.prototype.isLight = true; + + var HemisphereLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(HemisphereLight, _Light); + + function HemisphereLight(skyColor, groundColor, intensity) { + var _this; + + _this = _Light.call(this, skyColor, intensity) || this; + _this.type = 'HemisphereLight'; + + _this.position.copy(Object3D.DefaultUp); + + _this.updateMatrix(); + + _this.groundColor = new Color(groundColor); + return _this; + } + + var _proto = HemisphereLight.prototype; + + _proto.copy = function copy(source) { + Light.prototype.copy.call(this, source); + this.groundColor.copy(source.groundColor); + return this; + }; + + return HemisphereLight; + }(Light); + + HemisphereLight.prototype.isHemisphereLight = true; + + var _projScreenMatrix = /*@__PURE__*/new Matrix4(); + + var _lightPositionWorld = /*@__PURE__*/new Vector3(); + + var _lookTarget = /*@__PURE__*/new Vector3(); + + var LightShadow = /*#__PURE__*/function () { + function LightShadow(camera) { + this.camera = camera; + this.bias = 0; + this.normalBias = 0; + this.radius = 1; + this.mapSize = new Vector2(512, 512); + this.map = null; + this.mapPass = null; + this.matrix = new Matrix4(); + this.autoUpdate = true; + this.needsUpdate = false; + this._frustum = new Frustum(); + this._frameExtents = new Vector2(1, 1); + this._viewportCount = 1; + this._viewports = [new Vector4(0, 0, 1, 1)]; + } + + var _proto = LightShadow.prototype; + + _proto.getViewportCount = function getViewportCount() { + return this._viewportCount; + }; + + _proto.getFrustum = function getFrustum() { + return this._frustum; + }; + + _proto.updateMatrices = function updateMatrices(light) { + var shadowCamera = this.camera; + var shadowMatrix = this.matrix; + + _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); + + shadowCamera.position.copy(_lightPositionWorld); + + _lookTarget.setFromMatrixPosition(light.target.matrixWorld); + + shadowCamera.lookAt(_lookTarget); + shadowCamera.updateMatrixWorld(); + + _projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); + + this._frustum.setFromProjectionMatrix(_projScreenMatrix); + + shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); + shadowMatrix.multiply(shadowCamera.projectionMatrix); + shadowMatrix.multiply(shadowCamera.matrixWorldInverse); + }; + + _proto.getViewport = function getViewport(viewportIndex) { + return this._viewports[viewportIndex]; + }; + + _proto.getFrameExtents = function getFrameExtents() { + return this._frameExtents; + }; + + _proto.copy = function copy(source) { + this.camera = source.camera.clone(); + this.bias = source.bias; + this.radius = source.radius; + this.mapSize.copy(source.mapSize); + return this; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.toJSON = function toJSON() { + var object = {}; + if (this.bias !== 0) object.bias = this.bias; + if (this.normalBias !== 0) object.normalBias = this.normalBias; + if (this.radius !== 1) object.radius = this.radius; + if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); + object.camera = this.camera.toJSON(false).object; + delete object.camera.matrix; + return object; + }; + + return LightShadow; + }(); + + var SpotLightShadow = /*#__PURE__*/function (_LightShadow) { + _inheritsLoose(SpotLightShadow, _LightShadow); + + function SpotLightShadow() { + var _this; + + _this = _LightShadow.call(this, new PerspectiveCamera(50, 1, 0.5, 500)) || this; + _this.focus = 1; + return _this; + } + + var _proto = SpotLightShadow.prototype; + + _proto.updateMatrices = function updateMatrices(light) { + var camera = this.camera; + var fov = MathUtils.RAD2DEG * 2 * light.angle * this.focus; + var aspect = this.mapSize.width / this.mapSize.height; + var far = light.distance || camera.far; + + if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); + } + + _LightShadow.prototype.updateMatrices.call(this, light); + }; + + return SpotLightShadow; + }(LightShadow); + + SpotLightShadow.prototype.isSpotLightShadow = true; + + var SpotLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(SpotLight, _Light); + + function SpotLight(color, intensity, distance, angle, penumbra, decay) { + var _this; + + if (distance === void 0) { + distance = 0; + } + + if (angle === void 0) { + angle = Math.PI / 3; + } + + if (penumbra === void 0) { + penumbra = 0; + } + + if (decay === void 0) { + decay = 1; + } + + _this = _Light.call(this, color, intensity) || this; + _this.type = 'SpotLight'; + + _this.position.copy(Object3D.DefaultUp); + + _this.updateMatrix(); + + _this.target = new Object3D(); + _this.distance = distance; + _this.angle = angle; + _this.penumbra = penumbra; + _this.decay = decay; // for physically correct lights, should be 2. + + _this.shadow = new SpotLightShadow(); + return _this; + } + + var _proto = SpotLight.prototype; + + _proto.copy = function copy(source) { + _Light.prototype.copy.call(this, source); + + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); + return this; + }; + + _createClass(SpotLight, [{ + key: "power", + get: function get() { + // intensity = power per solid angle. + // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + return this.intensity * Math.PI; + }, + set: function set(power) { + // intensity = power per solid angle. + // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + this.intensity = power / Math.PI; + } + }]); + + return SpotLight; + }(Light); + + SpotLight.prototype.isSpotLight = true; + + var _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); + + var _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); + + var _lookTarget$1 = /*@__PURE__*/new Vector3(); + + var PointLightShadow = /*#__PURE__*/function (_LightShadow) { + _inheritsLoose(PointLightShadow, _LightShadow); + + function PointLightShadow() { + var _this; + + _this = _LightShadow.call(this, new PerspectiveCamera(90, 1, 0.5, 500)) || this; + _this._frameExtents = new Vector2(4, 2); + _this._viewportCount = 6; + _this._viewports = [// These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + // positive X + new Vector4(2, 1, 1, 1), // negative X + new Vector4(0, 1, 1, 1), // positive Z + new Vector4(3, 1, 1, 1), // negative Z + new Vector4(1, 1, 1, 1), // positive Y + new Vector4(3, 0, 1, 1), // negative Y + new Vector4(1, 0, 1, 1)]; + _this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; + _this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; + return _this; + } + + var _proto = PointLightShadow.prototype; + + _proto.updateMatrices = function updateMatrices(light, viewportIndex) { + if (viewportIndex === void 0) { + viewportIndex = 0; + } + + var camera = this.camera; + var shadowMatrix = this.matrix; + + _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); + + camera.position.copy(_lightPositionWorld$1); + + _lookTarget$1.copy(camera.position); + + _lookTarget$1.add(this._cubeDirections[viewportIndex]); + + camera.up.copy(this._cubeUps[viewportIndex]); + camera.lookAt(_lookTarget$1); + camera.updateMatrixWorld(); + shadowMatrix.makeTranslation(-_lightPositionWorld$1.x, -_lightPositionWorld$1.y, -_lightPositionWorld$1.z); + + _projScreenMatrix$1.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + + this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); + }; + + return PointLightShadow; + }(LightShadow); + + PointLightShadow.prototype.isPointLightShadow = true; + + var PointLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(PointLight, _Light); + + function PointLight(color, intensity, distance, decay) { + var _this; + + if (distance === void 0) { + distance = 0; + } + + if (decay === void 0) { + decay = 1; + } + + _this = _Light.call(this, color, intensity) || this; + _this.type = 'PointLight'; + _this.distance = distance; + _this.decay = decay; // for physically correct lights, should be 2. + + _this.shadow = new PointLightShadow(); + return _this; + } + + var _proto = PointLight.prototype; + + _proto.copy = function copy(source) { + _Light.prototype.copy.call(this, source); + + this.distance = source.distance; + this.decay = source.decay; + this.shadow = source.shadow.clone(); + return this; + }; + + _createClass(PointLight, [{ + key: "power", + get: function get() { + // intensity = power per solid angle. + // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + return this.intensity * 4 * Math.PI; + }, + set: function set(power) { + // intensity = power per solid angle. + // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + this.intensity = power / (4 * Math.PI); + } + }]); + + return PointLight; + }(Light); + + PointLight.prototype.isPointLight = true; + + var OrthographicCamera = /*#__PURE__*/function (_Camera) { + _inheritsLoose(OrthographicCamera, _Camera); + + function OrthographicCamera(left, right, top, bottom, near, far) { + var _this; + + if (left === void 0) { + left = -1; + } + + if (right === void 0) { + right = 1; + } + + if (top === void 0) { + top = 1; + } + + if (bottom === void 0) { + bottom = -1; + } + + if (near === void 0) { + near = 0.1; + } + + if (far === void 0) { + far = 2000; + } + + _this = _Camera.call(this) || this; + _this.type = 'OrthographicCamera'; + _this.zoom = 1; + _this.view = null; + _this.left = left; + _this.right = right; + _this.top = top; + _this.bottom = bottom; + _this.near = near; + _this.far = far; + + _this.updateProjectionMatrix(); + + return _this; + } + + var _proto = OrthographicCamera.prototype; + + _proto.copy = function copy(source, recursive) { + _Camera.prototype.copy.call(this, source, recursive); + + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign({}, source.view); + return this; + }; + + _proto.setViewOffset = function setViewOffset(fullWidth, fullHeight, x, y, width, height) { + if (this.view === null) { + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; + } + + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; + this.updateProjectionMatrix(); + }; + + _proto.clearViewOffset = function clearViewOffset() { + if (this.view !== null) { + this.view.enabled = false; + } + + this.updateProjectionMatrix(); + }; + + _proto.updateProjectionMatrix = function updateProjectionMatrix() { + var dx = (this.right - this.left) / (2 * this.zoom); + var dy = (this.top - this.bottom) / (2 * this.zoom); + var cx = (this.right + this.left) / 2; + var cy = (this.top + this.bottom) / 2; + var left = cx - dx; + var right = cx + dx; + var top = cy + dy; + var bottom = cy - dy; + + if (this.view !== null && this.view.enabled) { + var scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; + var scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; + } + + this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); + this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); + }; + + _proto.toJSON = function toJSON(meta) { + var data = Object3D.prototype.toJSON.call(this, meta); + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; + if (this.view !== null) data.object.view = Object.assign({}, this.view); + return data; + }; + + return OrthographicCamera; + }(Camera); + + OrthographicCamera.prototype.isOrthographicCamera = true; + + var DirectionalLightShadow = /*#__PURE__*/function (_LightShadow) { + _inheritsLoose(DirectionalLightShadow, _LightShadow); + + function DirectionalLightShadow() { + return _LightShadow.call(this, new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)) || this; + } + + return DirectionalLightShadow; + }(LightShadow); + + DirectionalLightShadow.prototype.isDirectionalLightShadow = true; + + var DirectionalLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(DirectionalLight, _Light); + + function DirectionalLight(color, intensity) { + var _this; + + _this = _Light.call(this, color, intensity) || this; + _this.type = 'DirectionalLight'; + + _this.position.copy(Object3D.DefaultUp); + + _this.updateMatrix(); + + _this.target = new Object3D(); + _this.shadow = new DirectionalLightShadow(); + return _this; + } + + var _proto = DirectionalLight.prototype; + + _proto.copy = function copy(source) { + _Light.prototype.copy.call(this, source); + + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); + return this; + }; + + return DirectionalLight; + }(Light); + + DirectionalLight.prototype.isDirectionalLight = true; + + var AmbientLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(AmbientLight, _Light); + + function AmbientLight(color, intensity) { + var _this; + + _this = _Light.call(this, color, intensity) || this; + _this.type = 'AmbientLight'; + return _this; + } + + return AmbientLight; + }(Light); + + AmbientLight.prototype.isAmbientLight = true; + + var RectAreaLight = /*#__PURE__*/function (_Light) { + _inheritsLoose(RectAreaLight, _Light); + + function RectAreaLight(color, intensity, width, height) { + var _this; + + if (width === void 0) { + width = 10; + } + + if (height === void 0) { + height = 10; + } + + _this = _Light.call(this, color, intensity) || this; + _this.type = 'RectAreaLight'; + _this.width = width; + _this.height = height; + return _this; + } + + var _proto = RectAreaLight.prototype; + + _proto.copy = function copy(source) { + _Light.prototype.copy.call(this, source); + + this.width = source.width; + this.height = source.height; + return this; + }; + + _proto.toJSON = function toJSON(meta) { + var data = _Light.prototype.toJSON.call(this, meta); + + data.object.width = this.width; + data.object.height = this.height; + return data; + }; + + return RectAreaLight; + }(Light); + + RectAreaLight.prototype.isRectAreaLight = true; + + /** + * Primary reference: + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * Secondary reference: + * https://www.ppsloan.org/publications/StupidSH36.pdf + */ + // 3-band SH defined by 9 coefficients + + var SphericalHarmonics3 = /*#__PURE__*/function () { + function SphericalHarmonics3() { + this.coefficients = []; + + for (var i = 0; i < 9; i++) { + this.coefficients.push(new Vector3()); + } + } + + var _proto = SphericalHarmonics3.prototype; + + _proto.set = function set(coefficients) { + for (var i = 0; i < 9; i++) { + this.coefficients[i].copy(coefficients[i]); + } + + return this; + }; + + _proto.zero = function zero() { + for (var i = 0; i < 9; i++) { + this.coefficients[i].set(0, 0, 0); + } + + return this; + } // get the radiance in the direction of the normal + // target is a Vector3 + ; + + _proto.getAt = function getAt(normal, target) { + // normal is assumed to be unit length + var x = normal.x, + y = normal.y, + z = normal.z; + var coeff = this.coefficients; // band 0 + + target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 + + target.addScaledVector(coeff[1], 0.488603 * y); + target.addScaledVector(coeff[2], 0.488603 * z); + target.addScaledVector(coeff[3], 0.488603 * x); // band 2 + + target.addScaledVector(coeff[4], 1.092548 * (x * y)); + target.addScaledVector(coeff[5], 1.092548 * (y * z)); + target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); + target.addScaledVector(coeff[7], 1.092548 * (x * z)); + target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); + return target; + } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal + // target is a Vector3 + // https://graphics.stanford.edu/papers/envmap/envmap.pdf + ; + + _proto.getIrradianceAt = function getIrradianceAt(normal, target) { + // normal is assumed to be unit length + var x = normal.x, + y = normal.y, + z = normal.z; + var coeff = this.coefficients; // band 0 + + target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 + // band 1 + + target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 + + target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); + target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 + + target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 + + target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); + target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 + + target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); + target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 + + return target; + }; + + _proto.add = function add(sh) { + for (var i = 0; i < 9; i++) { + this.coefficients[i].add(sh.coefficients[i]); + } + + return this; + }; + + _proto.addScaledSH = function addScaledSH(sh, s) { + for (var i = 0; i < 9; i++) { + this.coefficients[i].addScaledVector(sh.coefficients[i], s); + } + + return this; + }; + + _proto.scale = function scale(s) { + for (var i = 0; i < 9; i++) { + this.coefficients[i].multiplyScalar(s); + } + + return this; + }; + + _proto.lerp = function lerp(sh, alpha) { + for (var i = 0; i < 9; i++) { + this.coefficients[i].lerp(sh.coefficients[i], alpha); + } + + return this; + }; + + _proto.equals = function equals(sh) { + for (var i = 0; i < 9; i++) { + if (!this.coefficients[i].equals(sh.coefficients[i])) { + return false; + } + } + + return true; + }; + + _proto.copy = function copy(sh) { + return this.set(sh.coefficients); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.fromArray = function fromArray(array, offset) { + if (offset === void 0) { + offset = 0; + } + + var coefficients = this.coefficients; + + for (var i = 0; i < 9; i++) { + coefficients[i].fromArray(array, offset + i * 3); + } + + return this; + }; + + _proto.toArray = function toArray(array, offset) { + if (array === void 0) { + array = []; + } + + if (offset === void 0) { + offset = 0; + } + + var coefficients = this.coefficients; + + for (var i = 0; i < 9; i++) { + coefficients[i].toArray(array, offset + i * 3); + } + + return array; + } // evaluate the basis functions + // shBasis is an Array[ 9 ] + ; + + SphericalHarmonics3.getBasisAt = function getBasisAt(normal, shBasis) { + // normal is assumed to be unit length + var x = normal.x, + y = normal.y, + z = normal.z; // band 0 + + shBasis[0] = 0.282095; // band 1 + + shBasis[1] = 0.488603 * y; + shBasis[2] = 0.488603 * z; + shBasis[3] = 0.488603 * x; // band 2 + + shBasis[4] = 1.092548 * x * y; + shBasis[5] = 1.092548 * y * z; + shBasis[6] = 0.315392 * (3 * z * z - 1); + shBasis[7] = 1.092548 * x * z; + shBasis[8] = 0.546274 * (x * x - y * y); + }; + + return SphericalHarmonics3; + }(); + + SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; + + var LightProbe = /*#__PURE__*/function (_Light) { + _inheritsLoose(LightProbe, _Light); + + function LightProbe(sh, intensity) { + var _this; + + if (sh === void 0) { + sh = new SphericalHarmonics3(); + } + + if (intensity === void 0) { + intensity = 1; + } + + _this = _Light.call(this, undefined, intensity) || this; + _this.sh = sh; + return _this; + } + + var _proto = LightProbe.prototype; + + _proto.copy = function copy(source) { + _Light.prototype.copy.call(this, source); + + this.sh.copy(source.sh); + return this; + }; + + _proto.fromJSON = function fromJSON(json) { + this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); + + this.sh.fromArray(json.sh); + return this; + }; + + _proto.toJSON = function toJSON(meta) { + var data = _Light.prototype.toJSON.call(this, meta); + + data.object.sh = this.sh.toArray(); + return data; + }; + + return LightProbe; + }(Light); + + LightProbe.prototype.isLightProbe = true; + + var MaterialLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(MaterialLoader, _Loader); + + function MaterialLoader(manager) { + var _this; + + _this = _Loader.call(this, manager) || this; + _this.textures = {}; + return _this; + } + + var _proto = MaterialLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new FileLoader(scope.manager); + loader.setPath(scope.path); + loader.setRequestHeader(scope.requestHeader); + loader.setWithCredentials(scope.withCredentials); + loader.load(url, function (text) { + try { + onLoad(scope.parse(JSON.parse(text))); + } catch (e) { + if (onError) { + onError(e); + } else { + console.error(e); + } + + scope.manager.itemError(url); + } + }, onProgress, onError); + }; + + _proto.parse = function parse(json) { + var textures = this.textures; + + function getTexture(name) { + if (textures[name] === undefined) { + console.warn('THREE.MaterialLoader: Undefined texture', name); + } + + return textures[name]; + } + + var material = new Materials[json.type](); + if (json.uuid !== undefined) material.uuid = json.uuid; + if (json.name !== undefined) material.name = json.name; + if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); + if (json.roughness !== undefined) material.roughness = json.roughness; + if (json.metalness !== undefined) material.metalness = json.metalness; + if (json.sheen !== undefined) material.sheen = new Color().setHex(json.sheen); + if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); + if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); + if (json.shininess !== undefined) material.shininess = json.shininess; + if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; + if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; + if (json.fog !== undefined) material.fog = json.fog; + if (json.flatShading !== undefined) material.flatShading = json.flatShading; + if (json.blending !== undefined) material.blending = json.blending; + if (json.combine !== undefined) material.combine = json.combine; + if (json.side !== undefined) material.side = json.side; + if (json.opacity !== undefined) material.opacity = json.opacity; + if (json.transparent !== undefined) material.transparent = json.transparent; + if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; + if (json.depthTest !== undefined) material.depthTest = json.depthTest; + if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; + if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; + if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; + if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; + if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; + if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; + if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; + if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; + if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; + if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; + if (json.wireframe !== undefined) material.wireframe = json.wireframe; + if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; + if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; + if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; + if (json.rotation !== undefined) material.rotation = json.rotation; + if (json.linewidth !== 1) material.linewidth = json.linewidth; + if (json.dashSize !== undefined) material.dashSize = json.dashSize; + if (json.gapSize !== undefined) material.gapSize = json.gapSize; + if (json.scale !== undefined) material.scale = json.scale; + if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; + if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; + if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; + if (json.skinning !== undefined) material.skinning = json.skinning; + if (json.morphTargets !== undefined) material.morphTargets = json.morphTargets; + if (json.morphNormals !== undefined) material.morphNormals = json.morphNormals; + if (json.dithering !== undefined) material.dithering = json.dithering; + if (json.vertexTangents !== undefined) material.vertexTangents = json.vertexTangents; + if (json.visible !== undefined) material.visible = json.visible; + if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; + if (json.userData !== undefined) material.userData = json.userData; + + if (json.vertexColors !== undefined) { + if (typeof json.vertexColors === 'number') { + material.vertexColors = json.vertexColors > 0 ? true : false; + } else { + material.vertexColors = json.vertexColors; + } + } // Shader Material + + + if (json.uniforms !== undefined) { + for (var name in json.uniforms) { + var uniform = json.uniforms[name]; + material.uniforms[name] = {}; + + switch (uniform.type) { + case 't': + material.uniforms[name].value = getTexture(uniform.value); + break; + + case 'c': + material.uniforms[name].value = new Color().setHex(uniform.value); + break; + + case 'v2': + material.uniforms[name].value = new Vector2().fromArray(uniform.value); + break; + + case 'v3': + material.uniforms[name].value = new Vector3().fromArray(uniform.value); + break; + + case 'v4': + material.uniforms[name].value = new Vector4().fromArray(uniform.value); + break; + + case 'm3': + material.uniforms[name].value = new Matrix3().fromArray(uniform.value); + break; + + case 'm4': + material.uniforms[name].value = new Matrix4().fromArray(uniform.value); + break; + + default: + material.uniforms[name].value = uniform.value; + } + } + } + + if (json.defines !== undefined) material.defines = json.defines; + if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; + if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; + + if (json.extensions !== undefined) { + for (var key in json.extensions) { + material.extensions[key] = json.extensions[key]; + } + } // Deprecated + + + if (json.shading !== undefined) material.flatShading = json.shading === 1; // THREE.FlatShading + // for PointsMaterial + + if (json.size !== undefined) material.size = json.size; + if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps + + if (json.map !== undefined) material.map = getTexture(json.map); + if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); + if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); + if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); + if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; + if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); + if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; + + if (json.normalScale !== undefined) { + var normalScale = json.normalScale; + + if (Array.isArray(normalScale) === false) { + // Blender exporter used to export a scalar. See #7459 + normalScale = [normalScale, normalScale]; + } + + material.normalScale = new Vector2().fromArray(normalScale); + } + + if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); + if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; + if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; + if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); + if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); + if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); + if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; + if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); + if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); + if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; + if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; + if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; + if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); + if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; + if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); + if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; + if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); + if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); + if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); + if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); + if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); + if (json.transmission !== undefined) material.transmission = json.transmission; + if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); + return material; + }; + + _proto.setTextures = function setTextures(value) { + this.textures = value; + return this; + }; + + return MaterialLoader; + }(Loader); + + var LoaderUtils = { + decodeText: function decodeText(array) { + if (typeof TextDecoder !== 'undefined') { + return new TextDecoder().decode(array); + } // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + + + var s = ''; + + for (var i = 0, il = array.length; i < il; i++) { + // Implicitly assumes little-endian. + s += String.fromCharCode(array[i]); + } + + try { + // merges multi-byte utf-8 characters. + return decodeURIComponent(escape(s)); + } catch (e) { + // see #16358 + return s; + } + }, + extractUrlBase: function extractUrlBase(url) { + var index = url.lastIndexOf('/'); + if (index === -1) return './'; + return url.substr(0, index + 1); + } + }; + + function InstancedBufferGeometry() { + BufferGeometry.call(this); + this.type = 'InstancedBufferGeometry'; + this.instanceCount = Infinity; + } + + InstancedBufferGeometry.prototype = Object.assign(Object.create(BufferGeometry.prototype), { + constructor: InstancedBufferGeometry, + isInstancedBufferGeometry: true, + copy: function copy(source) { + BufferGeometry.prototype.copy.call(this, source); + this.instanceCount = source.instanceCount; + return this; + }, + clone: function clone() { + return new this.constructor().copy(this); + }, + toJSON: function toJSON() { + var data = BufferGeometry.prototype.toJSON.call(this); + data.instanceCount = this.instanceCount; + data.isInstancedBufferGeometry = true; + return data; + } + }); + + function InstancedBufferAttribute(array, itemSize, normalized, meshPerAttribute) { + if (typeof normalized === 'number') { + meshPerAttribute = normalized; + normalized = false; + console.error('THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.'); + } + + BufferAttribute.call(this, array, itemSize, normalized); + this.meshPerAttribute = meshPerAttribute || 1; + } + + InstancedBufferAttribute.prototype = Object.assign(Object.create(BufferAttribute.prototype), { + constructor: InstancedBufferAttribute, + isInstancedBufferAttribute: true, + copy: function copy(source) { + BufferAttribute.prototype.copy.call(this, source); + this.meshPerAttribute = source.meshPerAttribute; + return this; + }, + toJSON: function toJSON() { + var data = BufferAttribute.prototype.toJSON.call(this); + data.meshPerAttribute = this.meshPerAttribute; + data.isInstancedBufferAttribute = true; + return data; + } + }); + + var BufferGeometryLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(BufferGeometryLoader, _Loader); + + function BufferGeometryLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = BufferGeometryLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new FileLoader(scope.manager); + loader.setPath(scope.path); + loader.setRequestHeader(scope.requestHeader); + loader.setWithCredentials(scope.withCredentials); + loader.load(url, function (text) { + try { + onLoad(scope.parse(JSON.parse(text))); + } catch (e) { + if (onError) { + onError(e); + } else { + console.error(e); + } + + scope.manager.itemError(url); + } + }, onProgress, onError); + }; + + _proto.parse = function parse(json) { + var interleavedBufferMap = {}; + var arrayBufferMap = {}; + + function getInterleavedBuffer(json, uuid) { + if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; + var interleavedBuffers = json.interleavedBuffers; + var interleavedBuffer = interleavedBuffers[uuid]; + var buffer = getArrayBuffer(json, interleavedBuffer.buffer); + var array = getTypedArray(interleavedBuffer.type, buffer); + var ib = new InterleavedBuffer(array, interleavedBuffer.stride); + ib.uuid = interleavedBuffer.uuid; + interleavedBufferMap[uuid] = ib; + return ib; + } + + function getArrayBuffer(json, uuid) { + if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; + var arrayBuffers = json.arrayBuffers; + var arrayBuffer = arrayBuffers[uuid]; + var ab = new Uint32Array(arrayBuffer).buffer; + arrayBufferMap[uuid] = ab; + return ab; + } + + var geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); + var index = json.data.index; + + if (index !== undefined) { + var typedArray = getTypedArray(index.type, index.array); + geometry.setIndex(new BufferAttribute(typedArray, 1)); + } + + var attributes = json.data.attributes; + + for (var key in attributes) { + var attribute = attributes[key]; + var bufferAttribute = void 0; + + if (attribute.isInterleavedBufferAttribute) { + var interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); + bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); + } else { + var _typedArray = getTypedArray(attribute.type, attribute.array); + + var bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; + bufferAttribute = new bufferAttributeConstr(_typedArray, attribute.itemSize, attribute.normalized); + } + + if (attribute.name !== undefined) bufferAttribute.name = attribute.name; + geometry.setAttribute(key, bufferAttribute); + } + + var morphAttributes = json.data.morphAttributes; + + if (morphAttributes) { + for (var _key in morphAttributes) { + var attributeArray = morphAttributes[_key]; + var array = []; + + for (var i = 0, il = attributeArray.length; i < il; i++) { + var _attribute = attributeArray[i]; + + var _bufferAttribute = void 0; + + if (_attribute.isInterleavedBufferAttribute) { + var _interleavedBuffer = getInterleavedBuffer(json.data, _attribute.data); + + _bufferAttribute = new InterleavedBufferAttribute(_interleavedBuffer, _attribute.itemSize, _attribute.offset, _attribute.normalized); + } else { + var _typedArray2 = getTypedArray(_attribute.type, _attribute.array); + + _bufferAttribute = new BufferAttribute(_typedArray2, _attribute.itemSize, _attribute.normalized); + } + + if (_attribute.name !== undefined) _bufferAttribute.name = _attribute.name; + array.push(_bufferAttribute); + } + + geometry.morphAttributes[_key] = array; + } + } + + var morphTargetsRelative = json.data.morphTargetsRelative; + + if (morphTargetsRelative) { + geometry.morphTargetsRelative = true; + } + + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; + + if (groups !== undefined) { + for (var _i = 0, n = groups.length; _i !== n; ++_i) { + var group = groups[_i]; + geometry.addGroup(group.start, group.count, group.materialIndex); + } + } + + var boundingSphere = json.data.boundingSphere; + + if (boundingSphere !== undefined) { + var center = new Vector3(); + + if (boundingSphere.center !== undefined) { + center.fromArray(boundingSphere.center); + } + + geometry.boundingSphere = new Sphere(center, boundingSphere.radius); + } + + if (json.name) geometry.name = json.name; + if (json.userData) geometry.userData = json.userData; + return geometry; + }; + + return BufferGeometryLoader; + }(Loader); + + var ObjectLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(ObjectLoader, _Loader); + + function ObjectLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = ObjectLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var path = this.path === '' ? LoaderUtils.extractUrlBase(url) : this.path; + this.resourcePath = this.resourcePath || path; + var loader = new FileLoader(this.manager); + loader.setPath(this.path); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(this.withCredentials); + loader.load(url, function (text) { + var json = null; + + try { + json = JSON.parse(text); + } catch (error) { + if (onError !== undefined) onError(error); + console.error('THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message); + return; + } + + var metadata = json.metadata; + + if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry') { + console.error('THREE.ObjectLoader: Can\'t load ' + url); + return; + } + + scope.parse(json, onLoad); + }, onProgress, onError); + }; + + _proto.parse = function parse(json, onLoad) { + var animations = this.parseAnimations(json.animations); + var shapes = this.parseShapes(json.shapes); + var geometries = this.parseGeometries(json.geometries, shapes); + var images = this.parseImages(json.images, function () { + if (onLoad !== undefined) onLoad(object); + }); + var textures = this.parseTextures(json.textures, images); + var materials = this.parseMaterials(json.materials, textures); + var object = this.parseObject(json.object, geometries, materials, animations); + var skeletons = this.parseSkeletons(json.skeletons, object); + this.bindSkeletons(object, skeletons); // + + if (onLoad !== undefined) { + var hasImages = false; + + for (var uuid in images) { + if (images[uuid] instanceof HTMLImageElement) { + hasImages = true; + break; + } + } + + if (hasImages === false) onLoad(object); + } + + return object; + }; + + _proto.parseShapes = function parseShapes(json) { + var shapes = {}; + + if (json !== undefined) { + for (var i = 0, l = json.length; i < l; i++) { + var shape = new Shape().fromJSON(json[i]); + shapes[shape.uuid] = shape; + } + } + + return shapes; + }; + + _proto.parseSkeletons = function parseSkeletons(json, object) { + var skeletons = {}; + var bones = {}; // generate bone lookup table + + object.traverse(function (child) { + if (child.isBone) bones[child.uuid] = child; + }); // create skeletons + + if (json !== undefined) { + for (var i = 0, l = json.length; i < l; i++) { + var skeleton = new Skeleton().fromJSON(json[i], bones); + skeletons[skeleton.uuid] = skeleton; + } + } + + return skeletons; + }; + + _proto.parseGeometries = function parseGeometries(json, shapes) { + var geometries = {}; + var geometryShapes; + + if (json !== undefined) { + var bufferGeometryLoader = new BufferGeometryLoader(); + + for (var i = 0, l = json.length; i < l; i++) { + var geometry = void 0; + var data = json[i]; + + switch (data.type) { + case 'PlaneGeometry': + case 'PlaneBufferGeometry': + geometry = new Geometries[data.type](data.width, data.height, data.widthSegments, data.heightSegments); + break; + + case 'BoxGeometry': + case 'BoxBufferGeometry': + geometry = new Geometries[data.type](data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); + break; + + case 'CircleGeometry': + case 'CircleBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.segments, data.thetaStart, data.thetaLength); + break; + + case 'CylinderGeometry': + case 'CylinderBufferGeometry': + geometry = new Geometries[data.type](data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); + break; + + case 'ConeGeometry': + case 'ConeBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); + break; + + case 'SphereGeometry': + case 'SphereBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); + break; + + case 'DodecahedronGeometry': + case 'DodecahedronBufferGeometry': + case 'IcosahedronGeometry': + case 'IcosahedronBufferGeometry': + case 'OctahedronGeometry': + case 'OctahedronBufferGeometry': + case 'TetrahedronGeometry': + case 'TetrahedronBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.detail); + break; + + case 'RingGeometry': + case 'RingBufferGeometry': + geometry = new Geometries[data.type](data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); + break; + + case 'TorusGeometry': + case 'TorusBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); + break; + + case 'TorusKnotGeometry': + case 'TorusKnotBufferGeometry': + geometry = new Geometries[data.type](data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); + break; + + case 'TubeGeometry': + case 'TubeBufferGeometry': + // This only works for built-in curves (e.g. CatmullRomCurve3). + // User defined curves or instances of CurvePath will not be deserialized. + geometry = new Geometries[data.type](new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); + break; + + case 'LatheGeometry': + case 'LatheBufferGeometry': + geometry = new Geometries[data.type](data.points, data.segments, data.phiStart, data.phiLength); + break; + + case 'PolyhedronGeometry': + case 'PolyhedronBufferGeometry': + geometry = new Geometries[data.type](data.vertices, data.indices, data.radius, data.details); + break; + + case 'ShapeGeometry': + case 'ShapeBufferGeometry': + geometryShapes = []; + + for (var j = 0, jl = data.shapes.length; j < jl; j++) { + var shape = shapes[data.shapes[j]]; + geometryShapes.push(shape); + } + + geometry = new Geometries[data.type](geometryShapes, data.curveSegments); + break; + + case 'ExtrudeGeometry': + case 'ExtrudeBufferGeometry': + geometryShapes = []; + + for (var _j = 0, _jl = data.shapes.length; _j < _jl; _j++) { + var _shape = shapes[data.shapes[_j]]; + geometryShapes.push(_shape); + } + + var extrudePath = data.options.extrudePath; + + if (extrudePath !== undefined) { + data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); + } + + geometry = new Geometries[data.type](geometryShapes, data.options); + break; + + case 'BufferGeometry': + case 'InstancedBufferGeometry': + geometry = bufferGeometryLoader.parse(data); + break; + + case 'Geometry': + console.error('THREE.ObjectLoader: Loading "Geometry" is not supported anymore.'); + break; + + default: + console.warn('THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"'); + continue; + } + + geometry.uuid = data.uuid; + if (data.name !== undefined) geometry.name = data.name; + if (geometry.isBufferGeometry === true && data.userData !== undefined) geometry.userData = data.userData; + geometries[data.uuid] = geometry; + } + } + + return geometries; + }; + + _proto.parseMaterials = function parseMaterials(json, textures) { + var cache = {}; // MultiMaterial + + var materials = {}; + + if (json !== undefined) { + var loader = new MaterialLoader(); + loader.setTextures(textures); + + for (var i = 0, l = json.length; i < l; i++) { + var data = json[i]; + + if (data.type === 'MultiMaterial') { + // Deprecated + var array = []; + + for (var j = 0; j < data.materials.length; j++) { + var material = data.materials[j]; + + if (cache[material.uuid] === undefined) { + cache[material.uuid] = loader.parse(material); + } + + array.push(cache[material.uuid]); + } + + materials[data.uuid] = array; + } else { + if (cache[data.uuid] === undefined) { + cache[data.uuid] = loader.parse(data); + } + + materials[data.uuid] = cache[data.uuid]; + } + } + } + + return materials; + }; + + _proto.parseAnimations = function parseAnimations(json) { + var animations = {}; + + if (json !== undefined) { + for (var i = 0; i < json.length; i++) { + var data = json[i]; + var clip = AnimationClip.parse(data); + animations[clip.uuid] = clip; + } + } + + return animations; + }; + + _proto.parseImages = function parseImages(json, onLoad) { + var scope = this; + var images = {}; + var loader; + + function loadImage(url) { + scope.manager.itemStart(url); + return loader.load(url, function () { + scope.manager.itemEnd(url); + }, undefined, function () { + scope.manager.itemError(url); + scope.manager.itemEnd(url); + }); + } + + function deserializeImage(image) { + if (typeof image === 'string') { + var url = image; + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test(url) ? url : scope.resourcePath + url; + return loadImage(path); + } else { + if (image.data) { + return { + data: getTypedArray(image.type, image.data), + width: image.width, + height: image.height + }; + } else { + return null; + } + } + } + + if (json !== undefined && json.length > 0) { + var manager = new LoadingManager(onLoad); + loader = new ImageLoader(manager); + loader.setCrossOrigin(this.crossOrigin); + + for (var i = 0, il = json.length; i < il; i++) { + var image = json[i]; + var url = image.url; + + if (Array.isArray(url)) { + // load array of images e.g CubeTexture + images[image.uuid] = []; + + for (var j = 0, jl = url.length; j < jl; j++) { + var currentUrl = url[j]; + var deserializedImage = deserializeImage(currentUrl); + + if (deserializedImage !== null) { + if (deserializedImage instanceof HTMLImageElement) { + images[image.uuid].push(deserializedImage); + } else { + // special case: handle array of data textures for cube textures + images[image.uuid].push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); + } + } + } + } else { + // load single image + var _deserializedImage = deserializeImage(image.url); + + if (_deserializedImage !== null) { + images[image.uuid] = _deserializedImage; + } + } + } + } + + return images; + }; + + _proto.parseTextures = function parseTextures(json, images) { + function parseConstant(value, type) { + if (typeof value === 'number') return value; + console.warn('THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value); + return type[value]; + } + + var textures = {}; + + if (json !== undefined) { + for (var i = 0, l = json.length; i < l; i++) { + var data = json[i]; + + if (data.image === undefined) { + console.warn('THREE.ObjectLoader: No "image" specified for', data.uuid); + } + + if (images[data.image] === undefined) { + console.warn('THREE.ObjectLoader: Undefined image', data.image); + } + + var texture = void 0; + var image = images[data.image]; + + if (Array.isArray(image)) { + texture = new CubeTexture(image); + if (image.length === 6) texture.needsUpdate = true; + } else { + if (image && image.data) { + texture = new DataTexture(image.data, image.width, image.height); + } else { + texture = new Texture(image); + } + + if (image) texture.needsUpdate = true; // textures can have undefined image data + } + + texture.uuid = data.uuid; + if (data.name !== undefined) texture.name = data.name; + if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); + if (data.offset !== undefined) texture.offset.fromArray(data.offset); + if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); + if (data.center !== undefined) texture.center.fromArray(data.center); + if (data.rotation !== undefined) texture.rotation = data.rotation; + + if (data.wrap !== undefined) { + texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); + texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); + } + + if (data.format !== undefined) texture.format = data.format; + if (data.type !== undefined) texture.type = data.type; + if (data.encoding !== undefined) texture.encoding = data.encoding; + if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); + if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); + if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; + if (data.flipY !== undefined) texture.flipY = data.flipY; + if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; + if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; + textures[data.uuid] = texture; + } + } + + return textures; + }; + + _proto.parseObject = function parseObject(data, geometries, materials, animations) { + var object; + + function getGeometry(name) { + if (geometries[name] === undefined) { + console.warn('THREE.ObjectLoader: Undefined geometry', name); + } + + return geometries[name]; + } + + function getMaterial(name) { + if (name === undefined) return undefined; + + if (Array.isArray(name)) { + var array = []; + + for (var i = 0, l = name.length; i < l; i++) { + var uuid = name[i]; + + if (materials[uuid] === undefined) { + console.warn('THREE.ObjectLoader: Undefined material', uuid); + } + + array.push(materials[uuid]); + } + + return array; + } + + if (materials[name] === undefined) { + console.warn('THREE.ObjectLoader: Undefined material', name); + } + + return materials[name]; + } + + var geometry, material; + + switch (data.type) { + case 'Scene': + object = new Scene(); + + if (data.background !== undefined) { + if (Number.isInteger(data.background)) { + object.background = new Color(data.background); + } + } + + if (data.fog !== undefined) { + if (data.fog.type === 'Fog') { + object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); + } else if (data.fog.type === 'FogExp2') { + object.fog = new FogExp2(data.fog.color, data.fog.density); + } + } + + break; + + case 'PerspectiveCamera': + object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); + if (data.focus !== undefined) object.focus = data.focus; + if (data.zoom !== undefined) object.zoom = data.zoom; + if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; + if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; + if (data.view !== undefined) object.view = Object.assign({}, data.view); + break; + + case 'OrthographicCamera': + object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); + if (data.zoom !== undefined) object.zoom = data.zoom; + if (data.view !== undefined) object.view = Object.assign({}, data.view); + break; + + case 'AmbientLight': + object = new AmbientLight(data.color, data.intensity); + break; + + case 'DirectionalLight': + object = new DirectionalLight(data.color, data.intensity); + break; + + case 'PointLight': + object = new PointLight(data.color, data.intensity, data.distance, data.decay); + break; + + case 'RectAreaLight': + object = new RectAreaLight(data.color, data.intensity, data.width, data.height); + break; + + case 'SpotLight': + object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); + break; + + case 'HemisphereLight': + object = new HemisphereLight(data.color, data.groundColor, data.intensity); + break; + + case 'LightProbe': + object = new LightProbe().fromJSON(data); + break; + + case 'SkinnedMesh': + geometry = getGeometry(data.geometry); + material = getMaterial(data.material); + object = new SkinnedMesh(geometry, material); + if (data.bindMode !== undefined) object.bindMode = data.bindMode; + if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); + if (data.skeleton !== undefined) object.skeleton = data.skeleton; + break; + + case 'Mesh': + geometry = getGeometry(data.geometry); + material = getMaterial(data.material); + object = new Mesh(geometry, material); + break; + + case 'InstancedMesh': + geometry = getGeometry(data.geometry); + material = getMaterial(data.material); + var count = data.count; + var instanceMatrix = data.instanceMatrix; + object = new InstancedMesh(geometry, material, count); + object.instanceMatrix = new BufferAttribute(new Float32Array(instanceMatrix.array), 16); + break; + + case 'LOD': + object = new LOD(); + break; + + case 'Line': + object = new Line(getGeometry(data.geometry), getMaterial(data.material)); + break; + + case 'LineLoop': + object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); + break; + + case 'LineSegments': + object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); + break; + + case 'PointCloud': + case 'Points': + object = new Points(getGeometry(data.geometry), getMaterial(data.material)); + break; + + case 'Sprite': + object = new Sprite(getMaterial(data.material)); + break; + + case 'Group': + object = new Group(); + break; + + case 'Bone': + object = new Bone(); + break; + + default: + object = new Object3D(); + } + + object.uuid = data.uuid; + if (data.name !== undefined) object.name = data.name; + + if (data.matrix !== undefined) { + object.matrix.fromArray(data.matrix); + if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; + if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); + } else { + if (data.position !== undefined) object.position.fromArray(data.position); + if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); + if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); + if (data.scale !== undefined) object.scale.fromArray(data.scale); + } + + if (data.castShadow !== undefined) object.castShadow = data.castShadow; + if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; + + if (data.shadow) { + if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; + if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; + if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; + if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); + if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); + } + + if (data.visible !== undefined) object.visible = data.visible; + if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; + if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; + if (data.userData !== undefined) object.userData = data.userData; + if (data.layers !== undefined) object.layers.mask = data.layers; + + if (data.children !== undefined) { + var children = data.children; + + for (var i = 0; i < children.length; i++) { + object.add(this.parseObject(children[i], geometries, materials, animations)); + } + } + + if (data.animations !== undefined) { + var objectAnimations = data.animations; + + for (var _i = 0; _i < objectAnimations.length; _i++) { + var uuid = objectAnimations[_i]; + object.animations.push(animations[uuid]); + } + } + + if (data.type === 'LOD') { + if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; + var levels = data.levels; + + for (var l = 0; l < levels.length; l++) { + var level = levels[l]; + var child = object.getObjectByProperty('uuid', level.object); + + if (child !== undefined) { + object.addLevel(child, level.distance); + } + } + } + + return object; + }; + + _proto.bindSkeletons = function bindSkeletons(object, skeletons) { + if (Object.keys(skeletons).length === 0) return; + object.traverse(function (child) { + if (child.isSkinnedMesh === true && child.skeleton !== undefined) { + var skeleton = skeletons[child.skeleton]; + + if (skeleton === undefined) { + console.warn('THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton); + } else { + child.bind(skeleton, child.bindMatrix); + } + } + }); + } + /* DEPRECATED */ + ; + + _proto.setTexturePath = function setTexturePath(value) { + console.warn('THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().'); + return this.setResourcePath(value); + }; + + return ObjectLoader; + }(Loader); + + var TEXTURE_MAPPING = { + UVMapping: UVMapping, + CubeReflectionMapping: CubeReflectionMapping, + CubeRefractionMapping: CubeRefractionMapping, + EquirectangularReflectionMapping: EquirectangularReflectionMapping, + EquirectangularRefractionMapping: EquirectangularRefractionMapping, + CubeUVReflectionMapping: CubeUVReflectionMapping, + CubeUVRefractionMapping: CubeUVRefractionMapping + }; + var TEXTURE_WRAPPING = { + RepeatWrapping: RepeatWrapping, + ClampToEdgeWrapping: ClampToEdgeWrapping, + MirroredRepeatWrapping: MirroredRepeatWrapping + }; + var TEXTURE_FILTER = { + NearestFilter: NearestFilter, + NearestMipmapNearestFilter: NearestMipmapNearestFilter, + NearestMipmapLinearFilter: NearestMipmapLinearFilter, + LinearFilter: LinearFilter, + LinearMipmapNearestFilter: LinearMipmapNearestFilter, + LinearMipmapLinearFilter: LinearMipmapLinearFilter + }; + + function ImageBitmapLoader(manager) { + if (typeof createImageBitmap === 'undefined') { + console.warn('THREE.ImageBitmapLoader: createImageBitmap() not supported.'); + } + + if (typeof fetch === 'undefined') { + console.warn('THREE.ImageBitmapLoader: fetch() not supported.'); + } + + Loader.call(this, manager); + this.options = { + premultiplyAlpha: 'none' + }; + } + + ImageBitmapLoader.prototype = Object.assign(Object.create(Loader.prototype), { + constructor: ImageBitmapLoader, + isImageBitmapLoader: true, + setOptions: function setOptions(options) { + this.options = options; + return this; + }, + load: function load(url, onLoad, onProgress, onError) { + if (url === undefined) url = ''; + if (this.path !== undefined) url = this.path + url; + url = this.manager.resolveURL(url); + var scope = this; + var cached = Cache.get(url); + + if (cached !== undefined) { + scope.manager.itemStart(url); + setTimeout(function () { + if (onLoad) onLoad(cached); + scope.manager.itemEnd(url); + }, 0); + return cached; + } + + var fetchOptions = {}; + fetchOptions.credentials = this.crossOrigin === 'anonymous' ? 'same-origin' : 'include'; + fetchOptions.headers = this.requestHeader; + fetch(url, fetchOptions).then(function (res) { + return res.blob(); + }).then(function (blob) { + return createImageBitmap(blob, Object.assign(scope.options, { + colorSpaceConversion: 'none' + })); + }).then(function (imageBitmap) { + Cache.add(url, imageBitmap); + if (onLoad) onLoad(imageBitmap); + scope.manager.itemEnd(url); + }).catch(function (e) { + if (onError) onError(e); + scope.manager.itemError(url); + scope.manager.itemEnd(url); + }); + scope.manager.itemStart(url); + } + }); + + var ShapePath = /*#__PURE__*/function () { + function ShapePath() { + this.type = 'ShapePath'; + this.color = new Color(); + this.subPaths = []; + this.currentPath = null; + } + + var _proto = ShapePath.prototype; + + _proto.moveTo = function moveTo(x, y) { + this.currentPath = new Path(); + this.subPaths.push(this.currentPath); + this.currentPath.moveTo(x, y); + return this; + }; + + _proto.lineTo = function lineTo(x, y) { + this.currentPath.lineTo(x, y); + return this; + }; + + _proto.quadraticCurveTo = function quadraticCurveTo(aCPx, aCPy, aX, aY) { + this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); + return this; + }; + + _proto.bezierCurveTo = function bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { + this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); + return this; + }; + + _proto.splineThru = function splineThru(pts) { + this.currentPath.splineThru(pts); + return this; + }; + + _proto.toShapes = function toShapes(isCCW, noHoles) { + function toShapesNoHoles(inSubpaths) { + var shapes = []; + + for (var i = 0, l = inSubpaths.length; i < l; i++) { + var _tmpPath = inSubpaths[i]; + + var _tmpShape = new Shape(); + + _tmpShape.curves = _tmpPath.curves; + shapes.push(_tmpShape); + } + + return shapes; + } + + function isPointInsidePolygon(inPt, inPolygon) { + var polyLen = inPolygon.length; // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + + var inside = false; + + for (var p = polyLen - 1, q = 0; q < polyLen; p = q++) { + var edgeLowPt = inPolygon[p]; + var edgeHighPt = inPolygon[q]; + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if (Math.abs(edgeDy) > Number.EPSILON) { + // not parallel + if (edgeDy < 0) { + edgeLowPt = inPolygon[q]; + edgeDx = -edgeDx; + edgeHighPt = inPolygon[p]; + edgeDy = -edgeDy; + } + + if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; + + if (inPt.y === edgeLowPt.y) { + if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + } else { + var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); + if (perpEdge === 0) return true; // inPt is on contour ? + + if (perpEdge < 0) continue; + inside = !inside; // true intersection left of inPt + } + } else { + // parallel or collinear + if (inPt.y !== edgeLowPt.y) continue; // parallel + // edge lies on the same horizontal line as inPt + + if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! + // continue; + } + } + + return inside; + } + + var isClockWise = ShapeUtils.isClockWise; + var subPaths = this.subPaths; + if (subPaths.length === 0) return []; + if (noHoles === true) return toShapesNoHoles(subPaths); + var solid, tmpPath, tmpShape; + var shapes = []; + + if (subPaths.length === 1) { + tmpPath = subPaths[0]; + tmpShape = new Shape(); + tmpShape.curves = tmpPath.curves; + shapes.push(tmpShape); + return shapes; + } + + var holesFirst = !isClockWise(subPaths[0].getPoints()); + holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + newShapes[mainIdx] = undefined; + newShapeHoles[mainIdx] = []; + + for (var i = 0, l = subPaths.length; i < l; i++) { + tmpPath = subPaths[i]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise(tmpPoints); + solid = isCCW ? !solid : solid; + + if (solid) { + if (!holesFirst && newShapes[mainIdx]) mainIdx++; + newShapes[mainIdx] = { + s: new Shape(), + p: tmpPoints + }; + newShapes[mainIdx].s.curves = tmpPath.curves; + if (holesFirst) mainIdx++; + newShapeHoles[mainIdx] = []; //console.log('cw', i); + } else { + newShapeHoles[mainIdx].push({ + h: tmpPath, + p: tmpPoints[0] + }); //console.log('ccw', i); + } + } // only Holes? -> probably all Shapes with wrong orientation + + + if (!newShapes[0]) return toShapesNoHoles(subPaths); + + if (newShapes.length > 1) { + var ambiguous = false; + var toChange = []; + + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { + betterShapeHoles[sIdx] = []; + } + + for (var _sIdx = 0, _sLen = newShapes.length; _sIdx < _sLen; _sIdx++) { + var sho = newShapeHoles[_sIdx]; + + for (var hIdx = 0; hIdx < sho.length; hIdx++) { + var ho = sho[hIdx]; + var hole_unassigned = true; + + for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { + if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { + if (_sIdx !== s2Idx) toChange.push({ + froms: _sIdx, + tos: s2Idx, + hole: hIdx + }); + + if (hole_unassigned) { + hole_unassigned = false; + betterShapeHoles[s2Idx].push(ho); + } else { + ambiguous = true; + } + } + } + + if (hole_unassigned) { + betterShapeHoles[_sIdx].push(ho); + } + } + } // console.log("ambiguous: ", ambiguous); + + + if (toChange.length > 0) { + // console.log("to change: ", toChange); + if (!ambiguous) newShapeHoles = betterShapeHoles; + } + } + + var tmpHoles; + + for (var _i = 0, il = newShapes.length; _i < il; _i++) { + tmpShape = newShapes[_i].s; + shapes.push(tmpShape); + tmpHoles = newShapeHoles[_i]; + + for (var j = 0, jl = tmpHoles.length; j < jl; j++) { + tmpShape.holes.push(tmpHoles[j].h); + } + } //console.log("shape", shapes); + + + return shapes; + }; + + return ShapePath; + }(); + + var Font = /*#__PURE__*/function () { + function Font(data) { + this.type = 'Font'; + this.data = data; + } + + var _proto = Font.prototype; + + _proto.generateShapes = function generateShapes(text, size) { + if (size === void 0) { + size = 100; + } + + var shapes = []; + var paths = createPaths(text, size, this.data); + + for (var p = 0, pl = paths.length; p < pl; p++) { + Array.prototype.push.apply(shapes, paths[p].toShapes()); + } + + return shapes; + }; + + return Font; + }(); + + function createPaths(text, size, data) { + var chars = Array.from(text); + var scale = size / data.resolution; + var line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale; + var paths = []; + var offsetX = 0, + offsetY = 0; + + for (var i = 0; i < chars.length; i++) { + var char = chars[i]; + + if (char === '\n') { + offsetX = 0; + offsetY -= line_height; + } else { + var ret = createPath(char, scale, offsetX, offsetY, data); + offsetX += ret.offsetX; + paths.push(ret.path); + } + } + + return paths; + } + + function createPath(char, scale, offsetX, offsetY, data) { + var glyph = data.glyphs[char] || data.glyphs['?']; + + if (!glyph) { + console.error('THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.'); + return; + } + + var path = new ShapePath(); + var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; + + if (glyph.o) { + var outline = glyph._cachedOutline || (glyph._cachedOutline = glyph.o.split(' ')); + + for (var i = 0, l = outline.length; i < l;) { + var action = outline[i++]; + + switch (action) { + case 'm': + // moveTo + x = outline[i++] * scale + offsetX; + y = outline[i++] * scale + offsetY; + path.moveTo(x, y); + break; + + case 'l': + // lineTo + x = outline[i++] * scale + offsetX; + y = outline[i++] * scale + offsetY; + path.lineTo(x, y); + break; + + case 'q': + // quadraticCurveTo + cpx = outline[i++] * scale + offsetX; + cpy = outline[i++] * scale + offsetY; + cpx1 = outline[i++] * scale + offsetX; + cpy1 = outline[i++] * scale + offsetY; + path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); + break; + + case 'b': + // bezierCurveTo + cpx = outline[i++] * scale + offsetX; + cpy = outline[i++] * scale + offsetY; + cpx1 = outline[i++] * scale + offsetX; + cpy1 = outline[i++] * scale + offsetY; + cpx2 = outline[i++] * scale + offsetX; + cpy2 = outline[i++] * scale + offsetY; + path.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, cpx, cpy); + break; + } + } + } + + return { + offsetX: glyph.ha * scale, + path: path + }; + } + + Font.prototype.isFont = true; + + var FontLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(FontLoader, _Loader); + + function FontLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = FontLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new FileLoader(this.manager); + loader.setPath(this.path); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(scope.withCredentials); + loader.load(url, function (text) { + var json; + + try { + json = JSON.parse(text); + } catch (e) { + console.warn('THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.'); + json = JSON.parse(text.substring(65, text.length - 2)); + } + + var font = scope.parse(json); + if (onLoad) onLoad(font); + }, onProgress, onError); + }; + + _proto.parse = function parse(json) { + return new Font(json); + }; + + return FontLoader; + }(Loader); + + var _context; + + var AudioContext = { + getContext: function getContext() { + if (_context === undefined) { + _context = new (window.AudioContext || window.webkitAudioContext)(); + } + + return _context; + }, + setContext: function setContext(value) { + _context = value; + } + }; + + var AudioLoader = /*#__PURE__*/function (_Loader) { + _inheritsLoose(AudioLoader, _Loader); + + function AudioLoader(manager) { + return _Loader.call(this, manager) || this; + } + + var _proto = AudioLoader.prototype; + + _proto.load = function load(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new FileLoader(this.manager); + loader.setResponseType('arraybuffer'); + loader.setPath(this.path); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(this.withCredentials); + loader.load(url, function (buffer) { + try { + // Create a copy of the buffer. The `decodeAudioData` method + // detaches the buffer when complete, preventing reuse. + var bufferCopy = buffer.slice(0); + var context = AudioContext.getContext(); + context.decodeAudioData(bufferCopy, function (audioBuffer) { + onLoad(audioBuffer); + }); + } catch (e) { + if (onError) { + onError(e); + } else { + console.error(e); + } + + scope.manager.itemError(url); + } + }, onProgress, onError); + }; + + return AudioLoader; + }(Loader); + + var HemisphereLightProbe = /*#__PURE__*/function (_LightProbe) { + _inheritsLoose(HemisphereLightProbe, _LightProbe); + + function HemisphereLightProbe(skyColor, groundColor, intensity) { + var _this; + + if (intensity === void 0) { + intensity = 1; + } + + _this = _LightProbe.call(this, undefined, intensity) || this; + var color1 = new Color().set(skyColor); + var color2 = new Color().set(groundColor); + var sky = new Vector3(color1.r, color1.g, color1.b); + var ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); + + var c0 = Math.sqrt(Math.PI); + var c1 = c0 * Math.sqrt(0.75); + + _this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); + + _this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); + + return _this; + } + + return HemisphereLightProbe; + }(LightProbe); + + HemisphereLightProbe.prototype.isHemisphereLightProbe = true; + + var AmbientLightProbe = /*#__PURE__*/function (_LightProbe) { + _inheritsLoose(AmbientLightProbe, _LightProbe); + + function AmbientLightProbe(color, intensity) { + var _this; + + if (intensity === void 0) { + intensity = 1; + } + + _this = _LightProbe.call(this, undefined, intensity) || this; + var color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); + + _this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); + + return _this; + } + + return AmbientLightProbe; + }(LightProbe); + + AmbientLightProbe.prototype.isAmbientLightProbe = true; + + var _eyeRight = new Matrix4(); + + var _eyeLeft = new Matrix4(); + + var StereoCamera = /*#__PURE__*/function () { + function StereoCamera() { + this.type = 'StereoCamera'; + this.aspect = 1; + this.eyeSep = 0.064; + this.cameraL = new PerspectiveCamera(); + this.cameraL.layers.enable(1); + this.cameraL.matrixAutoUpdate = false; + this.cameraR = new PerspectiveCamera(); + this.cameraR.layers.enable(2); + this.cameraR.matrixAutoUpdate = false; + this._cache = { + focus: null, + fov: null, + aspect: null, + near: null, + far: null, + zoom: null, + eyeSep: null + }; + } + + var _proto = StereoCamera.prototype; + + _proto.update = function update(camera) { + var cache = this._cache; + var needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; + + if (needsUpdate) { + cache.focus = camera.focus; + cache.fov = camera.fov; + cache.aspect = camera.aspect * this.aspect; + cache.near = camera.near; + cache.far = camera.far; + cache.zoom = camera.zoom; + cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ + + var projectionMatrix = camera.projectionMatrix.clone(); + var eyeSepHalf = cache.eyeSep / 2; + var eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; + var ymax = cache.near * Math.tan(MathUtils.DEG2RAD * cache.fov * 0.5) / cache.zoom; + var xmin, xmax; // translate xOffset + + _eyeLeft.elements[12] = -eyeSepHalf; + _eyeRight.elements[12] = eyeSepHalf; // for left eye + + xmin = -ymax * cache.aspect + eyeSepOnProjection; + xmax = ymax * cache.aspect + eyeSepOnProjection; + projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); + projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); + this.cameraL.projectionMatrix.copy(projectionMatrix); // for right eye + + xmin = -ymax * cache.aspect - eyeSepOnProjection; + xmax = ymax * cache.aspect - eyeSepOnProjection; + projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); + projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); + this.cameraR.projectionMatrix.copy(projectionMatrix); + } + + this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); + this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); + }; + + return StereoCamera; + }(); + + var Clock = /*#__PURE__*/function () { + function Clock(autoStart) { + this.autoStart = autoStart !== undefined ? autoStart : true; + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + this.running = false; + } + + var _proto = Clock.prototype; + + _proto.start = function start() { + this.startTime = now(); + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; + }; + + _proto.stop = function stop() { + this.getElapsedTime(); + this.running = false; + this.autoStart = false; + }; + + _proto.getElapsedTime = function getElapsedTime() { + this.getDelta(); + return this.elapsedTime; + }; + + _proto.getDelta = function getDelta() { + var diff = 0; + + if (this.autoStart && !this.running) { + this.start(); + return 0; + } + + if (this.running) { + var newTime = now(); + diff = (newTime - this.oldTime) / 1000; + this.oldTime = newTime; + this.elapsedTime += diff; + } + + return diff; + }; + + return Clock; + }(); + + function now() { + return (typeof performance === 'undefined' ? Date : performance).now(); // see #10732 + } + + var _position$2 = /*@__PURE__*/new Vector3(); + + var _quaternion$3 = /*@__PURE__*/new Quaternion(); + + var _scale$1 = /*@__PURE__*/new Vector3(); + + var _orientation = /*@__PURE__*/new Vector3(); + + var AudioListener = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(AudioListener, _Object3D); + + function AudioListener() { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'AudioListener'; + _this.context = AudioContext.getContext(); + _this.gain = _this.context.createGain(); + + _this.gain.connect(_this.context.destination); + + _this.filter = null; + _this.timeDelta = 0; // private + + _this._clock = new Clock(); + return _this; + } + + var _proto = AudioListener.prototype; + + _proto.getInput = function getInput() { + return this.gain; + }; + + _proto.removeFilter = function removeFilter() { + if (this.filter !== null) { + this.gain.disconnect(this.filter); + this.filter.disconnect(this.context.destination); + this.gain.connect(this.context.destination); + this.filter = null; + } + + return this; + }; + + _proto.getFilter = function getFilter() { + return this.filter; + }; + + _proto.setFilter = function setFilter(value) { + if (this.filter !== null) { + this.gain.disconnect(this.filter); + this.filter.disconnect(this.context.destination); + } else { + this.gain.disconnect(this.context.destination); + } + + this.filter = value; + this.gain.connect(this.filter); + this.filter.connect(this.context.destination); + return this; + }; + + _proto.getMasterVolume = function getMasterVolume() { + return this.gain.gain.value; + }; + + _proto.setMasterVolume = function setMasterVolume(value) { + this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); + return this; + }; + + _proto.updateMatrixWorld = function updateMatrixWorld(force) { + _Object3D.prototype.updateMatrixWorld.call(this, force); + + var listener = this.context.listener; + var up = this.up; + this.timeDelta = this._clock.getDelta(); + this.matrixWorld.decompose(_position$2, _quaternion$3, _scale$1); + + _orientation.set(0, 0, -1).applyQuaternion(_quaternion$3); + + if (listener.positionX) { + // code path for Chrome (see #14393) + var endTime = this.context.currentTime + this.timeDelta; + listener.positionX.linearRampToValueAtTime(_position$2.x, endTime); + listener.positionY.linearRampToValueAtTime(_position$2.y, endTime); + listener.positionZ.linearRampToValueAtTime(_position$2.z, endTime); + listener.forwardX.linearRampToValueAtTime(_orientation.x, endTime); + listener.forwardY.linearRampToValueAtTime(_orientation.y, endTime); + listener.forwardZ.linearRampToValueAtTime(_orientation.z, endTime); + listener.upX.linearRampToValueAtTime(up.x, endTime); + listener.upY.linearRampToValueAtTime(up.y, endTime); + listener.upZ.linearRampToValueAtTime(up.z, endTime); + } else { + listener.setPosition(_position$2.x, _position$2.y, _position$2.z); + listener.setOrientation(_orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z); + } + }; + + return AudioListener; + }(Object3D); + + var Audio = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(Audio, _Object3D); + + function Audio(listener) { + var _this; + + _this = _Object3D.call(this) || this; + _this.type = 'Audio'; + _this.listener = listener; + _this.context = listener.context; + _this.gain = _this.context.createGain(); + + _this.gain.connect(listener.getInput()); + + _this.autoplay = false; + _this.buffer = null; + _this.detune = 0; + _this.loop = false; + _this.loopStart = 0; + _this.loopEnd = 0; + _this.offset = 0; + _this.duration = undefined; + _this.playbackRate = 1; + _this.isPlaying = false; + _this.hasPlaybackControl = true; + _this.source = null; + _this.sourceType = 'empty'; + _this._startedAt = 0; + _this._progress = 0; + _this._connected = false; + _this.filters = []; + return _this; + } + + var _proto = Audio.prototype; + + _proto.getOutput = function getOutput() { + return this.gain; + }; + + _proto.setNodeSource = function setNodeSource(audioNode) { + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); + return this; + }; + + _proto.setMediaElementSource = function setMediaElementSource(mediaElement) { + this.hasPlaybackControl = false; + this.sourceType = 'mediaNode'; + this.source = this.context.createMediaElementSource(mediaElement); + this.connect(); + return this; + }; + + _proto.setMediaStreamSource = function setMediaStreamSource(mediaStream) { + this.hasPlaybackControl = false; + this.sourceType = 'mediaStreamNode'; + this.source = this.context.createMediaStreamSource(mediaStream); + this.connect(); + return this; + }; + + _proto.setBuffer = function setBuffer(audioBuffer) { + this.buffer = audioBuffer; + this.sourceType = 'buffer'; + if (this.autoplay) this.play(); + return this; + }; + + _proto.play = function play(delay) { + if (delay === void 0) { + delay = 0; + } + + if (this.isPlaying === true) { + console.warn('THREE.Audio: Audio is already playing.'); + return; + } + + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return; + } + + this._startedAt = this.context.currentTime + delay; + var source = this.context.createBufferSource(); + source.buffer = this.buffer; + source.loop = this.loop; + source.loopStart = this.loopStart; + source.loopEnd = this.loopEnd; + source.onended = this.onEnded.bind(this); + source.start(this._startedAt, this._progress + this.offset, this.duration); + this.isPlaying = true; + this.source = source; + this.setDetune(this.detune); + this.setPlaybackRate(this.playbackRate); + return this.connect(); + }; + + _proto.pause = function pause() { + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return; + } + + if (this.isPlaying === true) { + // update current progress + this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; + + if (this.loop === true) { + // ensure _progress does not exceed duration with looped audios + this._progress = this._progress % (this.duration || this.buffer.duration); + } + + this.source.stop(); + this.source.onended = null; + this.isPlaying = false; + } + + return this; + }; + + _proto.stop = function stop() { + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return; + } + + this._progress = 0; + this.source.stop(); + this.source.onended = null; + this.isPlaying = false; + return this; + }; + + _proto.connect = function connect() { + if (this.filters.length > 0) { + this.source.connect(this.filters[0]); + + for (var i = 1, l = this.filters.length; i < l; i++) { + this.filters[i - 1].connect(this.filters[i]); + } + + this.filters[this.filters.length - 1].connect(this.getOutput()); + } else { + this.source.connect(this.getOutput()); + } + + this._connected = true; + return this; + }; + + _proto.disconnect = function disconnect() { + if (this.filters.length > 0) { + this.source.disconnect(this.filters[0]); + + for (var i = 1, l = this.filters.length; i < l; i++) { + this.filters[i - 1].disconnect(this.filters[i]); + } + + this.filters[this.filters.length - 1].disconnect(this.getOutput()); + } else { + this.source.disconnect(this.getOutput()); + } + + this._connected = false; + return this; + }; + + _proto.getFilters = function getFilters() { + return this.filters; + }; + + _proto.setFilters = function setFilters(value) { + if (!value) value = []; + + if (this._connected === true) { + this.disconnect(); + this.filters = value.slice(); + this.connect(); + } else { + this.filters = value.slice(); + } + + return this; + }; + + _proto.setDetune = function setDetune(value) { + this.detune = value; + if (this.source.detune === undefined) return; // only set detune when available + + if (this.isPlaying === true) { + this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); + } + + return this; + }; + + _proto.getDetune = function getDetune() { + return this.detune; + }; + + _proto.getFilter = function getFilter() { + return this.getFilters()[0]; + }; + + _proto.setFilter = function setFilter(filter) { + return this.setFilters(filter ? [filter] : []); + }; + + _proto.setPlaybackRate = function setPlaybackRate(value) { + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return; + } + + this.playbackRate = value; + + if (this.isPlaying === true) { + this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); + } + + return this; + }; + + _proto.getPlaybackRate = function getPlaybackRate() { + return this.playbackRate; + }; + + _proto.onEnded = function onEnded() { + this.isPlaying = false; + }; + + _proto.getLoop = function getLoop() { + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return false; + } + + return this.loop; + }; + + _proto.setLoop = function setLoop(value) { + if (this.hasPlaybackControl === false) { + console.warn('THREE.Audio: this Audio has no playback control.'); + return; + } + + this.loop = value; + + if (this.isPlaying === true) { + this.source.loop = this.loop; + } + + return this; + }; + + _proto.setLoopStart = function setLoopStart(value) { + this.loopStart = value; + return this; + }; + + _proto.setLoopEnd = function setLoopEnd(value) { + this.loopEnd = value; + return this; + }; + + _proto.getVolume = function getVolume() { + return this.gain.gain.value; + }; + + _proto.setVolume = function setVolume(value) { + this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); + return this; + }; + + return Audio; + }(Object3D); + + var _position$3 = /*@__PURE__*/new Vector3(); + + var _quaternion$4 = /*@__PURE__*/new Quaternion(); + + var _scale$2 = /*@__PURE__*/new Vector3(); + + var _orientation$1 = /*@__PURE__*/new Vector3(); + + var PositionalAudio = /*#__PURE__*/function (_Audio) { + _inheritsLoose(PositionalAudio, _Audio); + + function PositionalAudio(listener) { + var _this; + + _this = _Audio.call(this, listener) || this; + _this.panner = _this.context.createPanner(); + _this.panner.panningModel = 'HRTF'; + + _this.panner.connect(_this.gain); + + return _this; + } + + var _proto = PositionalAudio.prototype; + + _proto.getOutput = function getOutput() { + return this.panner; + }; + + _proto.getRefDistance = function getRefDistance() { + return this.panner.refDistance; + }; + + _proto.setRefDistance = function setRefDistance(value) { + this.panner.refDistance = value; + return this; + }; + + _proto.getRolloffFactor = function getRolloffFactor() { + return this.panner.rolloffFactor; + }; + + _proto.setRolloffFactor = function setRolloffFactor(value) { + this.panner.rolloffFactor = value; + return this; + }; + + _proto.getDistanceModel = function getDistanceModel() { + return this.panner.distanceModel; + }; + + _proto.setDistanceModel = function setDistanceModel(value) { + this.panner.distanceModel = value; + return this; + }; + + _proto.getMaxDistance = function getMaxDistance() { + return this.panner.maxDistance; + }; + + _proto.setMaxDistance = function setMaxDistance(value) { + this.panner.maxDistance = value; + return this; + }; + + _proto.setDirectionalCone = function setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { + this.panner.coneInnerAngle = coneInnerAngle; + this.panner.coneOuterAngle = coneOuterAngle; + this.panner.coneOuterGain = coneOuterGain; + return this; + }; + + _proto.updateMatrixWorld = function updateMatrixWorld(force) { + _Audio.prototype.updateMatrixWorld.call(this, force); + + if (this.hasPlaybackControl === true && this.isPlaying === false) return; + this.matrixWorld.decompose(_position$3, _quaternion$4, _scale$2); + + _orientation$1.set(0, 0, 1).applyQuaternion(_quaternion$4); + + var panner = this.panner; + + if (panner.positionX) { + // code path for Chrome and Firefox (see #14393) + var endTime = this.context.currentTime + this.listener.timeDelta; + panner.positionX.linearRampToValueAtTime(_position$3.x, endTime); + panner.positionY.linearRampToValueAtTime(_position$3.y, endTime); + panner.positionZ.linearRampToValueAtTime(_position$3.z, endTime); + panner.orientationX.linearRampToValueAtTime(_orientation$1.x, endTime); + panner.orientationY.linearRampToValueAtTime(_orientation$1.y, endTime); + panner.orientationZ.linearRampToValueAtTime(_orientation$1.z, endTime); + } else { + panner.setPosition(_position$3.x, _position$3.y, _position$3.z); + panner.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z); + } + }; + + return PositionalAudio; + }(Audio); + + var AudioAnalyser = /*#__PURE__*/function () { + function AudioAnalyser(audio, fftSize) { + if (fftSize === void 0) { + fftSize = 2048; + } + + this.analyser = audio.context.createAnalyser(); + this.analyser.fftSize = fftSize; + this.data = new Uint8Array(this.analyser.frequencyBinCount); + audio.getOutput().connect(this.analyser); + } + + var _proto = AudioAnalyser.prototype; + + _proto.getFrequencyData = function getFrequencyData() { + this.analyser.getByteFrequencyData(this.data); + return this.data; + }; + + _proto.getAverageFrequency = function getAverageFrequency() { + var value = 0; + var data = this.getFrequencyData(); + + for (var i = 0; i < data.length; i++) { + value += data[i]; + } + + return value / data.length; + }; + + return AudioAnalyser; + }(); + + var PropertyMixer = /*#__PURE__*/function () { + function PropertyMixer(binding, typeName, valueSize) { + this.binding = binding; + this.valueSize = valueSize; + var mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + // + // 'add' is used for additive cumulative results + // + // 'work' is optional and is only present for quaternion types. It is used + // to store intermediate quaternion multiplication results + + switch (typeName) { + case 'quaternion': + mixFunction = this._slerp; + mixFunctionAdditive = this._slerpAdditive; + setIdentity = this._setAdditiveIdentityQuaternion; + this.buffer = new Float64Array(valueSize * 6); + this._workIndex = 5; + break; + + case 'string': + case 'bool': + mixFunction = this._select; // Use the regular mix function and for additive on these types, + // additive is not relevant for non-numeric types + + mixFunctionAdditive = this._select; + setIdentity = this._setAdditiveIdentityOther; + this.buffer = new Array(valueSize * 5); + break; + + default: + mixFunction = this._lerp; + mixFunctionAdditive = this._lerpAdditive; + setIdentity = this._setAdditiveIdentityNumeric; + this.buffer = new Float64Array(valueSize * 5); + } + + this._mixBufferRegion = mixFunction; + this._mixBufferRegionAdditive = mixFunctionAdditive; + this._setIdentity = setIdentity; + this._origIndex = 3; + this._addIndex = 4; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; + this.useCount = 0; + this.referenceCount = 0; + } // accumulate data in the 'incoming' region into 'accu' + + + var _proto = PropertyMixer.prototype; + + _proto.accumulate = function accumulate(accuIndex, weight) { + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place + var buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride; + var currentWeight = this.cumulativeWeight; + + if (currentWeight === 0) { + // accuN := incoming * weight + for (var i = 0; i !== stride; ++i) { + buffer[offset + i] = buffer[i]; + } + + currentWeight = weight; + } else { + // accuN := accuN + incoming * weight + currentWeight += weight; + var mix = weight / currentWeight; + + this._mixBufferRegion(buffer, offset, 0, mix, stride); + } + + this.cumulativeWeight = currentWeight; + } // accumulate data in the 'incoming' region into 'add' + ; + + _proto.accumulateAdditive = function accumulateAdditive(weight) { + var buffer = this.buffer, + stride = this.valueSize, + offset = stride * this._addIndex; + + if (this.cumulativeWeightAdditive === 0) { + // add = identity + this._setIdentity(); + } // add := add + incoming * weight + + + this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); + + this.cumulativeWeightAdditive += weight; + } // apply the state of 'accu' to the binding when accus differ + ; + + _proto.apply = function apply(accuIndex) { + var stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, + weight = this.cumulativeWeight, + weightAdditive = this.cumulativeWeightAdditive, + binding = this.binding; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; + + if (weight < 1) { + // accuN := accuN + original * ( 1 - cumulativeWeight ) + var originalValueOffset = stride * this._origIndex; + + this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); + } + + if (weightAdditive > 0) { + // accuN := accuN + additive accuN + this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); + } + + for (var i = stride, e = stride + stride; i !== e; ++i) { + if (buffer[i] !== buffer[i + stride]) { + // value has changed -> update scene graph + binding.setValue(buffer, offset); + break; + } + } + } // remember the state of the bound property and copy it to both accus + ; + + _proto.saveOriginalState = function saveOriginalState() { + var binding = this.binding; + var buffer = this.buffer, + stride = this.valueSize, + originalValueOffset = stride * this._origIndex; + binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original + + for (var i = stride, e = originalValueOffset; i !== e; ++i) { + buffer[i] = buffer[originalValueOffset + i % stride]; + } // Add to identity for additive + + + this._setIdentity(); + + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; + } // apply the state previously taken via 'saveOriginalState' to the binding + ; + + _proto.restoreOriginalState = function restoreOriginalState() { + var originalValueOffset = this.valueSize * 3; + this.binding.setValue(this.buffer, originalValueOffset); + }; + + _proto._setAdditiveIdentityNumeric = function _setAdditiveIdentityNumeric() { + var startIndex = this._addIndex * this.valueSize; + var endIndex = startIndex + this.valueSize; + + for (var i = startIndex; i < endIndex; i++) { + this.buffer[i] = 0; + } + }; + + _proto._setAdditiveIdentityQuaternion = function _setAdditiveIdentityQuaternion() { + this._setAdditiveIdentityNumeric(); + + this.buffer[this._addIndex * this.valueSize + 3] = 1; + }; + + _proto._setAdditiveIdentityOther = function _setAdditiveIdentityOther() { + var startIndex = this._origIndex * this.valueSize; + var targetIndex = this._addIndex * this.valueSize; + + for (var i = 0; i < this.valueSize; i++) { + this.buffer[targetIndex + i] = this.buffer[startIndex + i]; + } + } // mix functions + ; + + _proto._select = function _select(buffer, dstOffset, srcOffset, t, stride) { + if (t >= 0.5) { + for (var i = 0; i !== stride; ++i) { + buffer[dstOffset + i] = buffer[srcOffset + i]; + } + } + }; + + _proto._slerp = function _slerp(buffer, dstOffset, srcOffset, t) { + Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); + }; + + _proto._slerpAdditive = function _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { + var workOffset = this._workIndex * stride; // Store result in intermediate buffer offset + + Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result + + Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); + }; + + _proto._lerp = function _lerp(buffer, dstOffset, srcOffset, t, stride) { + var s = 1 - t; + + for (var i = 0; i !== stride; ++i) { + var j = dstOffset + i; + buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; + } + }; + + _proto._lerpAdditive = function _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { + for (var i = 0; i !== stride; ++i) { + var j = dstOffset + i; + buffer[j] = buffer[j] + buffer[srcOffset + i] * t; + } + }; + + return PropertyMixer; + }(); + + // Characters [].:/ are reserved for track binding syntax. + var _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; + + var _reservedRe = new RegExp('[' + _RESERVED_CHARS_RE + ']', 'g'); // Attempts to allow node names from any language. ES5's `\w` regexp matches + // only latin characters, and the unicode \p{L} is not yet supported. So + // instead, we exclude reserved characters and match everything else. + + + var _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; + + var _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace('\\.', '') + ']'; // Parent directories, delimited by '/' or ':'. Currently unused, but must + // be matched to parse the rest of the track name. + + + var _directoryRe = /((?:WC+[\/:])*)/.source.replace('WC', _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. + + + var _nodeRe = /(WCOD+)?/.source.replace('WCOD', _wordCharOrDot); // Object on target node, and accessor. May not contain reserved + // characters. Accessor may contain any character except closing bracket. + + + var _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace('WC', _wordChar); // Property and accessor. May not contain reserved characters. Accessor may + // contain any non-bracket characters. + + + var _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace('WC', _wordChar); + + var _trackRe = new RegExp('' + '^' + _directoryRe + _nodeRe + _objectRe + _propertyRe + '$'); + + var _supportedObjectNames = ['material', 'materials', 'bones']; + + function Composite(targetGroup, path, optionalParsedPath) { + var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_(path, parsedPath); + } + + Object.assign(Composite.prototype, { + getValue: function getValue(array, offset) { + this.bind(); // bind all binding + + var firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[firstValidIndex]; // and only call .getValue on the first + + if (binding !== undefined) binding.getValue(array, offset); + }, + setValue: function setValue(array, offset) { + var bindings = this._bindings; + + for (var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { + bindings[i].setValue(array, offset); + } + }, + bind: function bind() { + var bindings = this._bindings; + + for (var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { + bindings[i].bind(); + } + }, + unbind: function unbind() { + var bindings = this._bindings; + + for (var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { + bindings[i].unbind(); + } + } + }); + + function PropertyBinding(rootNode, path, parsedPath) { + this.path = path; + this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); + this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName) || rootNode; + this.rootNode = rootNode; + } + + Object.assign(PropertyBinding, { + Composite: Composite, + create: function create(root, path, parsedPath) { + if (!(root && root.isAnimationObjectGroup)) { + return new PropertyBinding(root, path, parsedPath); + } else { + return new PropertyBinding.Composite(root, path, parsedPath); + } + }, + + /** + * Replaces spaces with underscores and removes unsupported characters from + * node names, to ensure compatibility with parseTrackName(). + * + * @param {string} name Node name to be sanitized. + * @return {string} + */ + sanitizeNodeName: function sanitizeNodeName(name) { + return name.replace(/\s/g, '_').replace(_reservedRe, ''); + }, + parseTrackName: function parseTrackName(trackName) { + var matches = _trackRe.exec(trackName); + + if (!matches) { + throw new Error('PropertyBinding: Cannot parse trackName: ' + trackName); + } + + var results = { + // directoryName: matches[ 1 ], // (tschw) currently unused + nodeName: matches[2], + objectName: matches[3], + objectIndex: matches[4], + propertyName: matches[5], + // required + propertyIndex: matches[6] + }; + var lastDot = results.nodeName && results.nodeName.lastIndexOf('.'); + + if (lastDot !== undefined && lastDot !== -1) { + var objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there + // is no way to parse 'foo.bar.baz': 'baz' must be a property, but + // 'bar' could be the objectName, or part of a nodeName (which can + // include '.' characters). + + if (_supportedObjectNames.indexOf(objectName) !== -1) { + results.nodeName = results.nodeName.substring(0, lastDot); + results.objectName = objectName; + } + } + + if (results.propertyName === null || results.propertyName.length === 0) { + throw new Error('PropertyBinding: can not parse propertyName from trackName: ' + trackName); + } + + return results; + }, + findNode: function findNode(root, nodeName) { + if (!nodeName || nodeName === '' || nodeName === '.' || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { + return root; + } // search into skeleton bones. + + + if (root.skeleton) { + var bone = root.skeleton.getBoneByName(nodeName); + + if (bone !== undefined) { + return bone; + } + } // search into node subtree. + + + if (root.children) { + var searchNodeSubtree = function searchNodeSubtree(children) { + for (var i = 0; i < children.length; i++) { + var childNode = children[i]; + + if (childNode.name === nodeName || childNode.uuid === nodeName) { + return childNode; + } + + var result = searchNodeSubtree(childNode.children); + if (result) return result; + } + + return null; + }; + + var subTreeNode = searchNodeSubtree(root.children); + + if (subTreeNode) { + return subTreeNode; + } + } + + return null; + } + }); + Object.assign(PropertyBinding.prototype, { + // prototype, continued + // these are used to "bind" a nonexistent property + _getValue_unavailable: function _getValue_unavailable() {}, + _setValue_unavailable: function _setValue_unavailable() {}, + BindingType: { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 + }, + Versioning: { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 + }, + GetterByBindingType: [function getValue_direct(buffer, offset) { + buffer[offset] = this.node[this.propertyName]; + }, function getValue_array(buffer, offset) { + var source = this.resolvedProperty; + + for (var i = 0, n = source.length; i !== n; ++i) { + buffer[offset++] = source[i]; + } + }, function getValue_arrayElement(buffer, offset) { + buffer[offset] = this.resolvedProperty[this.propertyIndex]; + }, function getValue_toArray(buffer, offset) { + this.resolvedProperty.toArray(buffer, offset); + }], + SetterByBindingTypeAndVersioning: [[// Direct + function setValue_direct(buffer, offset) { + this.targetObject[this.propertyName] = buffer[offset]; + }, function setValue_direct_setNeedsUpdate(buffer, offset) { + this.targetObject[this.propertyName] = buffer[offset]; + this.targetObject.needsUpdate = true; + }, function setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { + this.targetObject[this.propertyName] = buffer[offset]; + this.targetObject.matrixWorldNeedsUpdate = true; + }], [// EntireArray + function setValue_array(buffer, offset) { + var dest = this.resolvedProperty; + + for (var i = 0, n = dest.length; i !== n; ++i) { + dest[i] = buffer[offset++]; + } + }, function setValue_array_setNeedsUpdate(buffer, offset) { + var dest = this.resolvedProperty; + + for (var i = 0, n = dest.length; i !== n; ++i) { + dest[i] = buffer[offset++]; + } + + this.targetObject.needsUpdate = true; + }, function setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { + var dest = this.resolvedProperty; + + for (var i = 0, n = dest.length; i !== n; ++i) { + dest[i] = buffer[offset++]; + } + + this.targetObject.matrixWorldNeedsUpdate = true; + }], [// ArrayElement + function setValue_arrayElement(buffer, offset) { + this.resolvedProperty[this.propertyIndex] = buffer[offset]; + }, function setValue_arrayElement_setNeedsUpdate(buffer, offset) { + this.resolvedProperty[this.propertyIndex] = buffer[offset]; + this.targetObject.needsUpdate = true; + }, function setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { + this.resolvedProperty[this.propertyIndex] = buffer[offset]; + this.targetObject.matrixWorldNeedsUpdate = true; + }], [// HasToFromArray + function setValue_fromArray(buffer, offset) { + this.resolvedProperty.fromArray(buffer, offset); + }, function setValue_fromArray_setNeedsUpdate(buffer, offset) { + this.resolvedProperty.fromArray(buffer, offset); + this.targetObject.needsUpdate = true; + }, function setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { + this.resolvedProperty.fromArray(buffer, offset); + this.targetObject.matrixWorldNeedsUpdate = true; + }]], + getValue: function getValue_unbound(targetArray, offset) { + this.bind(); + this.getValue(targetArray, offset); // Note: This class uses a State pattern on a per-method basis: + // 'bind' sets 'this.getValue' / 'setValue' and shadows the + // prototype version of these methods with one that represents + // the bound state. When the property is not found, the methods + // become no-ops. + }, + setValue: function getValue_unbound(sourceArray, offset) { + this.bind(); + this.setValue(sourceArray, offset); + }, + // create getter / setter pair for a property in the scene graph + bind: function bind() { + var targetObject = this.node; + var parsedPath = this.parsedPath; + var objectName = parsedPath.objectName; + var propertyName = parsedPath.propertyName; + var propertyIndex = parsedPath.propertyIndex; + + if (!targetObject) { + targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName) || this.rootNode; + this.node = targetObject; + } // set fail state so we can just 'return' on error + + + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; // ensure there is a value node + + if (!targetObject) { + console.error('THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.'); + return; + } + + if (objectName) { + var objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... + + switch (objectName) { + case 'materials': + if (!targetObject.material) { + console.error('THREE.PropertyBinding: Can not bind to material as node does not have a material.', this); + return; + } + + if (!targetObject.material.materials) { + console.error('THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this); + return; + } + + targetObject = targetObject.material.materials; + break; + + case 'bones': + if (!targetObject.skeleton) { + console.error('THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this); + return; + } // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. + + + targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. + + for (var i = 0; i < targetObject.length; i++) { + if (targetObject[i].name === objectIndex) { + objectIndex = i; + break; + } + } + + break; + + default: + if (targetObject[objectName] === undefined) { + console.error('THREE.PropertyBinding: Can not bind to objectName of node undefined.', this); + return; + } + + targetObject = targetObject[objectName]; + } + + if (objectIndex !== undefined) { + if (targetObject[objectIndex] === undefined) { + console.error('THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject); + return; + } + + targetObject = targetObject[objectIndex]; + } + } // resolve property + + + var nodeProperty = targetObject[propertyName]; + + if (nodeProperty === undefined) { + var nodeName = parsedPath.nodeName; + console.error('THREE.PropertyBinding: Trying to update property for track: ' + nodeName + '.' + propertyName + ' but it wasn\'t found.', targetObject); + return; + } // determine versioning scheme + + + var versioning = this.Versioning.None; + this.targetObject = targetObject; + + if (targetObject.needsUpdate !== undefined) { + // material + versioning = this.Versioning.NeedsUpdate; + } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { + // node transform + versioning = this.Versioning.MatrixWorldNeedsUpdate; + } // determine how the property gets bound + + + var bindingType = this.BindingType.Direct; + + if (propertyIndex !== undefined) { + // access a sub element of the property array (only primitives are supported right now) + if (propertyName === 'morphTargetInfluences') { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + // support resolving morphTarget names into indices. + if (!targetObject.geometry) { + console.error('THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this); + return; + } + + if (targetObject.geometry.isBufferGeometry) { + if (!targetObject.geometry.morphAttributes) { + console.error('THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this); + return; + } + + if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { + propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; + } + } else { + console.error('THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this); + return; + } + } + + bindingType = this.BindingType.ArrayElement; + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; + } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { + // must use copy for Object3D.Euler/Quaternion + bindingType = this.BindingType.HasFromToArray; + this.resolvedProperty = nodeProperty; + } else if (Array.isArray(nodeProperty)) { + bindingType = this.BindingType.EntireArray; + this.resolvedProperty = nodeProperty; + } else { + this.propertyName = propertyName; + } // select getter / setter + + + this.getValue = this.GetterByBindingType[bindingType]; + this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; + }, + unbind: function unbind() { + this.node = null; // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; + } + }); // DECLARE ALIAS AFTER assign prototype + + Object.assign(PropertyBinding.prototype, { + // initial state of these methods that calls 'bind' + _getValue_unbound: PropertyBinding.prototype.getValue, + _setValue_unbound: PropertyBinding.prototype.setValue + }); + + /** + * + * A group of objects that receives a shared animation state. + * + * Usage: + * + * - Add objects you would otherwise pass as 'root' to the + * constructor or the .clipAction method of AnimationMixer. + * + * - Instead pass this object as 'root'. + * + * - You can also add and remove objects later when the mixer + * is running. + * + * Note: + * + * Objects of this class appear as one object to the mixer, + * so cache control of the individual objects must be done + * on the group. + * + * Limitation: + * + * - The animated properties must be compatible among the + * all objects in the group. + * + * - A single property can either be controlled through a + * target group or directly, but not both. + */ + + var AnimationObjectGroup = /*#__PURE__*/function () { + function AnimationObjectGroup() { + this.uuid = MathUtils.generateUUID(); // cached objects followed by the active ones + + this._objects = Array.prototype.slice.call(arguments); + this.nCachedObjects_ = 0; // threshold + // note: read by PropertyBinding.Composite + + var indices = {}; + this._indicesByUUID = indices; // for bookkeeping + + for (var i = 0, n = arguments.length; i !== n; ++i) { + indices[arguments[i].uuid] = i; + } + + this._paths = []; // inside: string + + this._parsedPaths = []; // inside: { we don't care, here } + + this._bindings = []; // inside: Array< PropertyBinding > + + this._bindingsIndicesByPath = {}; // inside: indices in these arrays + + var scope = this; + this.stats = { + objects: { + get total() { + return scope._objects.length; + }, + + get inUse() { + return this.total - scope.nCachedObjects_; + } + + }, + + get bindingsPerObject() { + return scope._bindings.length; + } + + }; + } + + var _proto = AnimationObjectGroup.prototype; + + _proto.add = function add() { + var objects = this._objects, + indicesByUUID = this._indicesByUUID, + paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + nBindings = bindings.length; + var knownObject = undefined, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_; + + for (var i = 0, n = arguments.length; i !== n; ++i) { + var object = arguments[i], + uuid = object.uuid; + var index = indicesByUUID[uuid]; + + if (index === undefined) { + // unknown object -> add it to the ACTIVE region + index = nObjects++; + indicesByUUID[uuid] = index; + objects.push(object); // accounting is done, now do the same for all bindings + + for (var j = 0, m = nBindings; j !== m; ++j) { + bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); + } + } else if (index < nCachedObjects) { + knownObject = objects[index]; // move existing object to the ACTIVE region + + var firstActiveIndex = --nCachedObjects, + lastCachedObject = objects[firstActiveIndex]; + indicesByUUID[lastCachedObject.uuid] = index; + objects[index] = lastCachedObject; + indicesByUUID[uuid] = firstActiveIndex; + objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings + + for (var _j = 0, _m = nBindings; _j !== _m; ++_j) { + var bindingsForPath = bindings[_j], + lastCached = bindingsForPath[firstActiveIndex]; + var binding = bindingsForPath[index]; + bindingsForPath[index] = lastCached; + + if (binding === undefined) { + // since we do not bother to create new bindings + // for objects that are cached, the binding may + // or may not exist + binding = new PropertyBinding(object, paths[_j], parsedPaths[_j]); + } + + bindingsForPath[firstActiveIndex] = binding; + } + } else if (objects[index] !== knownObject) { + console.error('THREE.AnimationObjectGroup: Different objects with the same UUID ' + 'detected. Clean the caches or recreate your infrastructure when reloading scenes.'); + } // else the object is already where we want it to be + + } // for arguments + + + this.nCachedObjects_ = nCachedObjects; + }; + + _proto.remove = function remove() { + var objects = this._objects, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; + var nCachedObjects = this.nCachedObjects_; + + for (var i = 0, n = arguments.length; i !== n; ++i) { + var object = arguments[i], + uuid = object.uuid, + index = indicesByUUID[uuid]; + + if (index !== undefined && index >= nCachedObjects) { + // move existing object into the CACHED region + var lastCachedIndex = nCachedObjects++, + firstActiveObject = objects[lastCachedIndex]; + indicesByUUID[firstActiveObject.uuid] = index; + objects[index] = firstActiveObject; + indicesByUUID[uuid] = lastCachedIndex; + objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings + + for (var j = 0, m = nBindings; j !== m; ++j) { + var bindingsForPath = bindings[j], + firstActive = bindingsForPath[lastCachedIndex], + binding = bindingsForPath[index]; + bindingsForPath[index] = firstActive; + bindingsForPath[lastCachedIndex] = binding; + } + } + } // for arguments + + + this.nCachedObjects_ = nCachedObjects; + } // remove & forget + ; + + _proto.uncache = function uncache() { + var objects = this._objects, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; + var nCachedObjects = this.nCachedObjects_, + nObjects = objects.length; + + for (var i = 0, n = arguments.length; i !== n; ++i) { + var object = arguments[i], + uuid = object.uuid, + index = indicesByUUID[uuid]; + + if (index !== undefined) { + delete indicesByUUID[uuid]; + + if (index < nCachedObjects) { + // object is cached, shrink the CACHED region + var firstActiveIndex = --nCachedObjects, + lastCachedObject = objects[firstActiveIndex], + lastIndex = --nObjects, + lastObject = objects[lastIndex]; // last cached object takes this object's place + + indicesByUUID[lastCachedObject.uuid] = index; + objects[index] = lastCachedObject; // last object goes to the activated slot and pop + + indicesByUUID[lastObject.uuid] = firstActiveIndex; + objects[firstActiveIndex] = lastObject; + objects.pop(); // accounting is done, now do the same for all bindings + + for (var j = 0, m = nBindings; j !== m; ++j) { + var bindingsForPath = bindings[j], + lastCached = bindingsForPath[firstActiveIndex], + last = bindingsForPath[lastIndex]; + bindingsForPath[index] = lastCached; + bindingsForPath[firstActiveIndex] = last; + bindingsForPath.pop(); + } + } else { + // object is active, just swap with the last and pop + var _lastIndex = --nObjects, + _lastObject = objects[_lastIndex]; + + if (_lastIndex > 0) { + indicesByUUID[_lastObject.uuid] = index; + } + + objects[index] = _lastObject; + objects.pop(); // accounting is done, now do the same for all bindings + + for (var _j2 = 0, _m2 = nBindings; _j2 !== _m2; ++_j2) { + var _bindingsForPath = bindings[_j2]; + _bindingsForPath[index] = _bindingsForPath[_lastIndex]; + + _bindingsForPath.pop(); + } + } // cached or active + + } // if object is known + + } // for arguments + + + this.nCachedObjects_ = nCachedObjects; + } // Internal interface used by befriended PropertyBinding.Composite: + ; + + _proto.subscribe_ = function subscribe_(path, parsedPath) { + // returns an array of bindings for the given path that is changed + // according to the contained objects in the group + var indicesByPath = this._bindingsIndicesByPath; + var index = indicesByPath[path]; + var bindings = this._bindings; + if (index !== undefined) return bindings[index]; + var paths = this._paths, + parsedPaths = this._parsedPaths, + objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + bindingsForPath = new Array(nObjects); + index = bindings.length; + indicesByPath[path] = index; + paths.push(path); + parsedPaths.push(parsedPath); + bindings.push(bindingsForPath); + + for (var i = nCachedObjects, n = objects.length; i !== n; ++i) { + var object = objects[i]; + bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); + } + + return bindingsForPath; + }; + + _proto.unsubscribe_ = function unsubscribe_(path) { + // tells the group to forget about a property path and no longer + // update the array previously obtained with 'subscribe_' + var indicesByPath = this._bindingsIndicesByPath, + index = indicesByPath[path]; + + if (index !== undefined) { + var paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + lastBindingsIndex = bindings.length - 1, + lastBindings = bindings[lastBindingsIndex], + lastBindingsPath = path[lastBindingsIndex]; + indicesByPath[lastBindingsPath] = index; + bindings[index] = lastBindings; + bindings.pop(); + parsedPaths[index] = parsedPaths[lastBindingsIndex]; + parsedPaths.pop(); + paths[index] = paths[lastBindingsIndex]; + paths.pop(); + } + }; + + return AnimationObjectGroup; + }(); + + AnimationObjectGroup.prototype.isAnimationObjectGroup = true; + + var AnimationAction = /*#__PURE__*/function () { + function AnimationAction(mixer, clip, localRoot, blendMode) { + if (localRoot === void 0) { + localRoot = null; + } + + if (blendMode === void 0) { + blendMode = clip.blendMode; + } + + this._mixer = mixer; + this._clip = clip; + this._localRoot = localRoot; + this.blendMode = blendMode; + var tracks = clip.tracks, + nTracks = tracks.length, + interpolants = new Array(nTracks); + var interpolantSettings = { + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + }; + + for (var i = 0; i !== nTracks; ++i) { + var interpolant = tracks[i].createInterpolant(null); + interpolants[i] = interpolant; + interpolant.settings = interpolantSettings; + } + + this._interpolantSettings = interpolantSettings; + this._interpolants = interpolants; // bound by the mixer + // inside: PropertyMixer (managed by the mixer) + + this._propertyBindings = new Array(nTracks); + this._cacheIndex = null; // for the memory manager + + this._byClipCacheIndex = null; // for the memory manager + + this._timeScaleInterpolant = null; + this._weightInterpolant = null; + this.loop = LoopRepeat; + this._loopCount = -1; // global mixer time when the action is to be started + // it's set back to 'null' upon start of the action + + this._startTime = null; // scaled local time of the action + // gets clamped or wrapped to 0..clip.duration according to loop + + this.time = 0; + this.timeScale = 1; + this._effectiveTimeScale = 1; + this.weight = 1; + this._effectiveWeight = 1; + this.repetitions = Infinity; // no. of repetitions when looping + + this.paused = false; // true -> zero effective time scale + + this.enabled = true; // false -> zero effective weight + + this.clampWhenFinished = false; // keep feeding the last frame? + + this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate + + this.zeroSlopeAtEnd = true; // clips for start, loop and end + } // State & Scheduling + + + var _proto = AnimationAction.prototype; + + _proto.play = function play() { + this._mixer._activateAction(this); + + return this; + }; + + _proto.stop = function stop() { + this._mixer._deactivateAction(this); + + return this.reset(); + }; + + _proto.reset = function reset() { + this.paused = false; + this.enabled = true; + this.time = 0; // restart clip + + this._loopCount = -1; // forget previous loops + + this._startTime = null; // forget scheduling + + return this.stopFading().stopWarping(); + }; + + _proto.isRunning = function isRunning() { + return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); + } // return true when play has been called + ; + + _proto.isScheduled = function isScheduled() { + return this._mixer._isActiveAction(this); + }; + + _proto.startAt = function startAt(time) { + this._startTime = time; + return this; + }; + + _proto.setLoop = function setLoop(mode, repetitions) { + this.loop = mode; + this.repetitions = repetitions; + return this; + } // Weight + // set the weight stopping any scheduled fading + // although .enabled = false yields an effective weight of zero, this + // method does *not* change .enabled, because it would be confusing + ; + + _proto.setEffectiveWeight = function setEffectiveWeight(weight) { + this.weight = weight; // note: same logic as when updated at runtime + + this._effectiveWeight = this.enabled ? weight : 0; + return this.stopFading(); + } // return the weight considering fading and .enabled + ; + + _proto.getEffectiveWeight = function getEffectiveWeight() { + return this._effectiveWeight; + }; + + _proto.fadeIn = function fadeIn(duration) { + return this._scheduleFading(duration, 0, 1); + }; + + _proto.fadeOut = function fadeOut(duration) { + return this._scheduleFading(duration, 1, 0); + }; + + _proto.crossFadeFrom = function crossFadeFrom(fadeOutAction, duration, warp) { + fadeOutAction.fadeOut(duration); + this.fadeIn(duration); + + if (warp) { + var fadeInDuration = this._clip.duration, + fadeOutDuration = fadeOutAction._clip.duration, + startEndRatio = fadeOutDuration / fadeInDuration, + endStartRatio = fadeInDuration / fadeOutDuration; + fadeOutAction.warp(1.0, startEndRatio, duration); + this.warp(endStartRatio, 1.0, duration); + } + + return this; + }; + + _proto.crossFadeTo = function crossFadeTo(fadeInAction, duration, warp) { + return fadeInAction.crossFadeFrom(this, duration, warp); + }; + + _proto.stopFading = function stopFading() { + var weightInterpolant = this._weightInterpolant; + + if (weightInterpolant !== null) { + this._weightInterpolant = null; + + this._mixer._takeBackControlInterpolant(weightInterpolant); + } + + return this; + } // Time Scale Control + // set the time scale stopping any scheduled warping + // although .paused = true yields an effective time scale of zero, this + // method does *not* change .paused, because it would be confusing + ; + + _proto.setEffectiveTimeScale = function setEffectiveTimeScale(timeScale) { + this.timeScale = timeScale; + this._effectiveTimeScale = this.paused ? 0 : timeScale; + return this.stopWarping(); + } // return the time scale considering warping and .paused + ; + + _proto.getEffectiveTimeScale = function getEffectiveTimeScale() { + return this._effectiveTimeScale; + }; + + _proto.setDuration = function setDuration(duration) { + this.timeScale = this._clip.duration / duration; + return this.stopWarping(); + }; + + _proto.syncWith = function syncWith(action) { + this.time = action.time; + this.timeScale = action.timeScale; + return this.stopWarping(); + }; + + _proto.halt = function halt(duration) { + return this.warp(this._effectiveTimeScale, 0, duration); + }; + + _proto.warp = function warp(startTimeScale, endTimeScale, duration) { + var mixer = this._mixer, + now = mixer.time, + timeScale = this.timeScale; + var interpolant = this._timeScaleInterpolant; + + if (interpolant === null) { + interpolant = mixer._lendControlInterpolant(); + this._timeScaleInterpolant = interpolant; + } + + var times = interpolant.parameterPositions, + values = interpolant.sampleValues; + times[0] = now; + times[1] = now + duration; + values[0] = startTimeScale / timeScale; + values[1] = endTimeScale / timeScale; + return this; + }; + + _proto.stopWarping = function stopWarping() { + var timeScaleInterpolant = this._timeScaleInterpolant; + + if (timeScaleInterpolant !== null) { + this._timeScaleInterpolant = null; + + this._mixer._takeBackControlInterpolant(timeScaleInterpolant); + } + + return this; + } // Object Accessors + ; + + _proto.getMixer = function getMixer() { + return this._mixer; + }; + + _proto.getClip = function getClip() { + return this._clip; + }; + + _proto.getRoot = function getRoot() { + return this._localRoot || this._mixer._root; + } // Interna + ; + + _proto._update = function _update(time, deltaTime, timeDirection, accuIndex) { + // called by the mixer + if (!this.enabled) { + // call ._updateWeight() to update ._effectiveWeight + this._updateWeight(time); + + return; + } + + var startTime = this._startTime; + + if (startTime !== null) { + // check for scheduled start of action + var timeRunning = (time - startTime) * timeDirection; + + if (timeRunning < 0 || timeDirection === 0) { + return; // yet to come / don't decide when delta = 0 + } // start + + + this._startTime = null; // unschedule + + deltaTime = timeDirection * timeRunning; + } // apply time scale and advance time + + + deltaTime *= this._updateTimeScale(time); + + var clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in + // an effective weight of 0 + + + var weight = this._updateWeight(time); + + if (weight > 0) { + var _interpolants = this._interpolants; + var propertyMixers = this._propertyBindings; + + switch (this.blendMode) { + case AdditiveAnimationBlendMode: + for (var j = 0, m = _interpolants.length; j !== m; ++j) { + _interpolants[j].evaluate(clipTime); + + propertyMixers[j].accumulateAdditive(weight); + } + + break; + + case NormalAnimationBlendMode: + default: + for (var _j = 0, _m = _interpolants.length; _j !== _m; ++_j) { + _interpolants[_j].evaluate(clipTime); + + propertyMixers[_j].accumulate(accuIndex, weight); + } + + } + } + }; + + _proto._updateWeight = function _updateWeight(time) { + var weight = 0; + + if (this.enabled) { + weight = this.weight; + var interpolant = this._weightInterpolant; + + if (interpolant !== null) { + var interpolantValue = interpolant.evaluate(time)[0]; + weight *= interpolantValue; + + if (time > interpolant.parameterPositions[1]) { + this.stopFading(); + + if (interpolantValue === 0) { + // faded out, disable + this.enabled = false; + } + } + } + } + + this._effectiveWeight = weight; + return weight; + }; + + _proto._updateTimeScale = function _updateTimeScale(time) { + var timeScale = 0; + + if (!this.paused) { + timeScale = this.timeScale; + var interpolant = this._timeScaleInterpolant; + + if (interpolant !== null) { + var interpolantValue = interpolant.evaluate(time)[0]; + timeScale *= interpolantValue; + + if (time > interpolant.parameterPositions[1]) { + this.stopWarping(); + + if (timeScale === 0) { + // motion has halted, pause + this.paused = true; + } else { + // warp done - apply final time scale + this.timeScale = timeScale; + } + } + } + } + + this._effectiveTimeScale = timeScale; + return timeScale; + }; + + _proto._updateTime = function _updateTime(deltaTime) { + var duration = this._clip.duration; + var loop = this.loop; + var time = this.time + deltaTime; + var loopCount = this._loopCount; + var pingPong = loop === LoopPingPong; + + if (deltaTime === 0) { + if (loopCount === -1) return time; + return pingPong && (loopCount & 1) === 1 ? duration - time : time; + } + + if (loop === LoopOnce) { + if (loopCount === -1) { + // just started + this._loopCount = 0; + + this._setEndings(true, true, false); + } + + handle_stop: { + if (time >= duration) { + time = duration; + } else if (time < 0) { + time = 0; + } else { + this.time = time; + break handle_stop; + } + + if (this.clampWhenFinished) this.paused = true;else this.enabled = false; + this.time = time; + + this._mixer.dispatchEvent({ + type: 'finished', + action: this, + direction: deltaTime < 0 ? -1 : 1 + }); + } + } else { + // repetitive Repeat or PingPong + if (loopCount === -1) { + // just started + if (deltaTime >= 0) { + loopCount = 0; + + this._setEndings(true, this.repetitions === 0, pingPong); + } else { + // when looping in reverse direction, the initial + // transition through zero counts as a repetition, + // so leave loopCount at -1 + this._setEndings(this.repetitions === 0, true, pingPong); + } + } + + if (time >= duration || time < 0) { + // wrap around + var loopDelta = Math.floor(time / duration); // signed + + time -= duration * loopDelta; + loopCount += Math.abs(loopDelta); + var pending = this.repetitions - loopCount; + + if (pending <= 0) { + // have to stop (switch state, clamp time, fire event) + if (this.clampWhenFinished) this.paused = true;else this.enabled = false; + time = deltaTime > 0 ? duration : 0; + this.time = time; + + this._mixer.dispatchEvent({ + type: 'finished', + action: this, + direction: deltaTime > 0 ? 1 : -1 + }); + } else { + // keep running + if (pending === 1) { + // entering the last round + var atStart = deltaTime < 0; + + this._setEndings(atStart, !atStart, pingPong); + } else { + this._setEndings(false, false, pingPong); + } + + this._loopCount = loopCount; + this.time = time; + + this._mixer.dispatchEvent({ + type: 'loop', + action: this, + loopDelta: loopDelta + }); + } + } else { + this.time = time; + } + + if (pingPong && (loopCount & 1) === 1) { + // invert time for the "pong round" + return duration - time; + } + } + + return time; + }; + + _proto._setEndings = function _setEndings(atStart, atEnd, pingPong) { + var settings = this._interpolantSettings; + + if (pingPong) { + settings.endingStart = ZeroSlopeEnding; + settings.endingEnd = ZeroSlopeEnding; + } else { + // assuming for LoopOnce atStart == atEnd == true + if (atStart) { + settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; + } else { + settings.endingStart = WrapAroundEnding; + } + + if (atEnd) { + settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; + } else { + settings.endingEnd = WrapAroundEnding; + } + } + }; + + _proto._scheduleFading = function _scheduleFading(duration, weightNow, weightThen) { + var mixer = this._mixer, + now = mixer.time; + var interpolant = this._weightInterpolant; + + if (interpolant === null) { + interpolant = mixer._lendControlInterpolant(); + this._weightInterpolant = interpolant; + } + + var times = interpolant.parameterPositions, + values = interpolant.sampleValues; + times[0] = now; + values[0] = weightNow; + times[1] = now + duration; + values[1] = weightThen; + return this; + }; + + return AnimationAction; + }(); + + var AnimationMixer = /*#__PURE__*/function (_EventDispatcher) { + _inheritsLoose(AnimationMixer, _EventDispatcher); + + function AnimationMixer(root) { + var _this; + + _this = _EventDispatcher.call(this) || this; + _this._root = root; + + _this._initMemoryManager(); + + _this._accuIndex = 0; + _this.time = 0; + _this.timeScale = 1.0; + return _this; + } + + var _proto = AnimationMixer.prototype; + + _proto._bindAction = function _bindAction(action, prototypeAction) { + var root = action._localRoot || this._root, + tracks = action._clip.tracks, + nTracks = tracks.length, + bindings = action._propertyBindings, + interpolants = action._interpolants, + rootUuid = root.uuid, + bindingsByRoot = this._bindingsByRootAndName; + var bindingsByName = bindingsByRoot[rootUuid]; + + if (bindingsByName === undefined) { + bindingsByName = {}; + bindingsByRoot[rootUuid] = bindingsByName; + } + + for (var i = 0; i !== nTracks; ++i) { + var track = tracks[i], + trackName = track.name; + var binding = bindingsByName[trackName]; + + if (binding !== undefined) { + bindings[i] = binding; + } else { + binding = bindings[i]; + + if (binding !== undefined) { + // existing binding, make sure the cache knows + if (binding._cacheIndex === null) { + ++binding.referenceCount; + + this._addInactiveBinding(binding, rootUuid, trackName); + } + + continue; + } + + var path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; + binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); + ++binding.referenceCount; + + this._addInactiveBinding(binding, rootUuid, trackName); + + bindings[i] = binding; + } + + interpolants[i].resultBuffer = binding.buffer; + } + }; + + _proto._activateAction = function _activateAction(action) { + if (!this._isActiveAction(action)) { + if (action._cacheIndex === null) { + // this action has been forgotten by the cache, but the user + // appears to be still using it -> rebind + var rootUuid = (action._localRoot || this._root).uuid, + clipUuid = action._clip.uuid, + actionsForClip = this._actionsByClip[clipUuid]; + + this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); + + this._addInactiveAction(action, clipUuid, rootUuid); + } + + var bindings = action._propertyBindings; // increment reference counts / sort out state + + for (var i = 0, n = bindings.length; i !== n; ++i) { + var binding = bindings[i]; + + if (binding.useCount++ === 0) { + this._lendBinding(binding); + + binding.saveOriginalState(); + } + } + + this._lendAction(action); + } + }; + + _proto._deactivateAction = function _deactivateAction(action) { + if (this._isActiveAction(action)) { + var bindings = action._propertyBindings; // decrement reference counts / sort out state + + for (var i = 0, n = bindings.length; i !== n; ++i) { + var binding = bindings[i]; + + if (--binding.useCount === 0) { + binding.restoreOriginalState(); + + this._takeBackBinding(binding); + } + } + + this._takeBackAction(action); + } + } // Memory manager + ; + + _proto._initMemoryManager = function _initMemoryManager() { + this._actions = []; // 'nActiveActions' followed by inactive ones + + this._nActiveActions = 0; + this._actionsByClip = {}; // inside: + // { + // knownActions: Array< AnimationAction > - used as prototypes + // actionByRoot: AnimationAction - lookup + // } + + this._bindings = []; // 'nActiveBindings' followed by inactive ones + + this._nActiveBindings = 0; + this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > + + this._controlInterpolants = []; // same game as above + + this._nActiveControlInterpolants = 0; + var scope = this; + this.stats = { + actions: { + get total() { + return scope._actions.length; + }, + + get inUse() { + return scope._nActiveActions; + } + + }, + bindings: { + get total() { + return scope._bindings.length; + }, + + get inUse() { + return scope._nActiveBindings; + } + + }, + controlInterpolants: { + get total() { + return scope._controlInterpolants.length; + }, + + get inUse() { + return scope._nActiveControlInterpolants; + } + + } + }; + } // Memory management for AnimationAction objects + ; + + _proto._isActiveAction = function _isActiveAction(action) { + var index = action._cacheIndex; + return index !== null && index < this._nActiveActions; + }; + + _proto._addInactiveAction = function _addInactiveAction(action, clipUuid, rootUuid) { + var actions = this._actions, + actionsByClip = this._actionsByClip; + var actionsForClip = actionsByClip[clipUuid]; + + if (actionsForClip === undefined) { + actionsForClip = { + knownActions: [action], + actionByRoot: {} + }; + action._byClipCacheIndex = 0; + actionsByClip[clipUuid] = actionsForClip; + } else { + var knownActions = actionsForClip.knownActions; + action._byClipCacheIndex = knownActions.length; + knownActions.push(action); + } + + action._cacheIndex = actions.length; + actions.push(action); + actionsForClip.actionByRoot[rootUuid] = action; + }; + + _proto._removeInactiveAction = function _removeInactiveAction(action) { + var actions = this._actions, + lastInactiveAction = actions[actions.length - 1], + cacheIndex = action._cacheIndex; + lastInactiveAction._cacheIndex = cacheIndex; + actions[cacheIndex] = lastInactiveAction; + actions.pop(); + action._cacheIndex = null; + var clipUuid = action._clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[clipUuid], + knownActionsForClip = actionsForClip.knownActions, + lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], + byClipCacheIndex = action._byClipCacheIndex; + lastKnownAction._byClipCacheIndex = byClipCacheIndex; + knownActionsForClip[byClipCacheIndex] = lastKnownAction; + knownActionsForClip.pop(); + action._byClipCacheIndex = null; + var actionByRoot = actionsForClip.actionByRoot, + rootUuid = (action._localRoot || this._root).uuid; + delete actionByRoot[rootUuid]; + + if (knownActionsForClip.length === 0) { + delete actionsByClip[clipUuid]; + } + + this._removeInactiveBindingsForAction(action); + }; + + _proto._removeInactiveBindingsForAction = function _removeInactiveBindingsForAction(action) { + var bindings = action._propertyBindings; + + for (var i = 0, n = bindings.length; i !== n; ++i) { + var binding = bindings[i]; + + if (--binding.referenceCount === 0) { + this._removeInactiveBinding(binding); + } + } + }; + + _proto._lendAction = function _lendAction(action) { + // [ active actions | inactive actions ] + // [ active actions >| inactive actions ] + // s a + // <-swap-> + // a s + var actions = this._actions, + prevIndex = action._cacheIndex, + lastActiveIndex = this._nActiveActions++, + firstInactiveAction = actions[lastActiveIndex]; + action._cacheIndex = lastActiveIndex; + actions[lastActiveIndex] = action; + firstInactiveAction._cacheIndex = prevIndex; + actions[prevIndex] = firstInactiveAction; + }; + + _proto._takeBackAction = function _takeBackAction(action) { + // [ active actions | inactive actions ] + // [ active actions |< inactive actions ] + // a s + // <-swap-> + // s a + var actions = this._actions, + prevIndex = action._cacheIndex, + firstInactiveIndex = --this._nActiveActions, + lastActiveAction = actions[firstInactiveIndex]; + action._cacheIndex = firstInactiveIndex; + actions[firstInactiveIndex] = action; + lastActiveAction._cacheIndex = prevIndex; + actions[prevIndex] = lastActiveAction; + } // Memory management for PropertyMixer objects + ; + + _proto._addInactiveBinding = function _addInactiveBinding(binding, rootUuid, trackName) { + var bindingsByRoot = this._bindingsByRootAndName, + bindings = this._bindings; + var bindingByName = bindingsByRoot[rootUuid]; + + if (bindingByName === undefined) { + bindingByName = {}; + bindingsByRoot[rootUuid] = bindingByName; + } + + bindingByName[trackName] = binding; + binding._cacheIndex = bindings.length; + bindings.push(binding); + }; + + _proto._removeInactiveBinding = function _removeInactiveBinding(binding) { + var bindings = this._bindings, + propBinding = binding.binding, + rootUuid = propBinding.rootNode.uuid, + trackName = propBinding.path, + bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[rootUuid], + lastInactiveBinding = bindings[bindings.length - 1], + cacheIndex = binding._cacheIndex; + lastInactiveBinding._cacheIndex = cacheIndex; + bindings[cacheIndex] = lastInactiveBinding; + bindings.pop(); + delete bindingByName[trackName]; + + if (Object.keys(bindingByName).length === 0) { + delete bindingsByRoot[rootUuid]; + } + }; + + _proto._lendBinding = function _lendBinding(binding) { + var bindings = this._bindings, + prevIndex = binding._cacheIndex, + lastActiveIndex = this._nActiveBindings++, + firstInactiveBinding = bindings[lastActiveIndex]; + binding._cacheIndex = lastActiveIndex; + bindings[lastActiveIndex] = binding; + firstInactiveBinding._cacheIndex = prevIndex; + bindings[prevIndex] = firstInactiveBinding; + }; + + _proto._takeBackBinding = function _takeBackBinding(binding) { + var bindings = this._bindings, + prevIndex = binding._cacheIndex, + firstInactiveIndex = --this._nActiveBindings, + lastActiveBinding = bindings[firstInactiveIndex]; + binding._cacheIndex = firstInactiveIndex; + bindings[firstInactiveIndex] = binding; + lastActiveBinding._cacheIndex = prevIndex; + bindings[prevIndex] = lastActiveBinding; + } // Memory management of Interpolants for weight and time scale + ; + + _proto._lendControlInterpolant = function _lendControlInterpolant() { + var interpolants = this._controlInterpolants, + lastActiveIndex = this._nActiveControlInterpolants++; + var interpolant = interpolants[lastActiveIndex]; + + if (interpolant === undefined) { + interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, this._controlInterpolantsResultBuffer); + interpolant.__cacheIndex = lastActiveIndex; + interpolants[lastActiveIndex] = interpolant; + } + + return interpolant; + }; + + _proto._takeBackControlInterpolant = function _takeBackControlInterpolant(interpolant) { + var interpolants = this._controlInterpolants, + prevIndex = interpolant.__cacheIndex, + firstInactiveIndex = --this._nActiveControlInterpolants, + lastActiveInterpolant = interpolants[firstInactiveIndex]; + interpolant.__cacheIndex = firstInactiveIndex; + interpolants[firstInactiveIndex] = interpolant; + lastActiveInterpolant.__cacheIndex = prevIndex; + interpolants[prevIndex] = lastActiveInterpolant; + } // return an action for a clip optionally using a custom root target + // object (this method allocates a lot of dynamic memory in case a + // previously unknown clip/root combination is specified) + ; + + _proto.clipAction = function clipAction(clip, optionalRoot, blendMode) { + var root = optionalRoot || this._root, + rootUuid = root.uuid; + var clipObject = typeof clip === 'string' ? AnimationClip.findByName(root, clip) : clip; + var clipUuid = clipObject !== null ? clipObject.uuid : clip; + var actionsForClip = this._actionsByClip[clipUuid]; + var prototypeAction = null; + + if (blendMode === undefined) { + if (clipObject !== null) { + blendMode = clipObject.blendMode; + } else { + blendMode = NormalAnimationBlendMode; + } + } + + if (actionsForClip !== undefined) { + var existingAction = actionsForClip.actionByRoot[rootUuid]; + + if (existingAction !== undefined && existingAction.blendMode === blendMode) { + return existingAction; + } // we know the clip, so we don't have to parse all + // the bindings again but can just copy + + + prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action + + if (clipObject === null) clipObject = prototypeAction._clip; + } // clip must be known when specified via string + + + if (clipObject === null) return null; // allocate all resources required to run it + + var newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); + + this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager + + + this._addInactiveAction(newAction, clipUuid, rootUuid); + + return newAction; + } // get an existing action + ; + + _proto.existingAction = function existingAction(clip, optionalRoot) { + var root = optionalRoot || this._root, + rootUuid = root.uuid, + clipObject = typeof clip === 'string' ? AnimationClip.findByName(root, clip) : clip, + clipUuid = clipObject ? clipObject.uuid : clip, + actionsForClip = this._actionsByClip[clipUuid]; + + if (actionsForClip !== undefined) { + return actionsForClip.actionByRoot[rootUuid] || null; + } + + return null; + } // deactivates all previously scheduled actions + ; + + _proto.stopAllAction = function stopAllAction() { + var actions = this._actions, + nActions = this._nActiveActions; + + for (var i = nActions - 1; i >= 0; --i) { + actions[i].stop(); + } + + return this; + } // advance the time and update apply the animation + ; + + _proto.update = function update(deltaTime) { + deltaTime *= this.timeScale; + var actions = this._actions, + nActions = this._nActiveActions, + time = this.time += deltaTime, + timeDirection = Math.sign(deltaTime), + accuIndex = this._accuIndex ^= 1; // run active actions + + for (var i = 0; i !== nActions; ++i) { + var action = actions[i]; + + action._update(time, deltaTime, timeDirection, accuIndex); + } // update scene graph + + + var bindings = this._bindings, + nBindings = this._nActiveBindings; + + for (var _i = 0; _i !== nBindings; ++_i) { + bindings[_i].apply(accuIndex); + } + + return this; + } // Allows you to seek to a specific time in an animation. + ; + + _proto.setTime = function setTime(timeInSeconds) { + this.time = 0; // Zero out time attribute for AnimationMixer object; + + for (var i = 0; i < this._actions.length; i++) { + this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. + } + + return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. + } // return this mixer's root target object + ; + + _proto.getRoot = function getRoot() { + return this._root; + } // free all resources specific to a particular clip + ; + + _proto.uncacheClip = function uncacheClip(clip) { + var actions = this._actions, + clipUuid = clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[clipUuid]; + + if (actionsForClip !== undefined) { + // note: just calling _removeInactiveAction would mess up the + // iteration state and also require updating the state we can + // just throw away + var actionsToRemove = actionsForClip.knownActions; + + for (var i = 0, n = actionsToRemove.length; i !== n; ++i) { + var action = actionsToRemove[i]; + + this._deactivateAction(action); + + var cacheIndex = action._cacheIndex, + lastInactiveAction = actions[actions.length - 1]; + action._cacheIndex = null; + action._byClipCacheIndex = null; + lastInactiveAction._cacheIndex = cacheIndex; + actions[cacheIndex] = lastInactiveAction; + actions.pop(); + + this._removeInactiveBindingsForAction(action); + } + + delete actionsByClip[clipUuid]; + } + } // free all resources specific to a particular root target object + ; + + _proto.uncacheRoot = function uncacheRoot(root) { + var rootUuid = root.uuid, + actionsByClip = this._actionsByClip; + + for (var clipUuid in actionsByClip) { + var actionByRoot = actionsByClip[clipUuid].actionByRoot, + action = actionByRoot[rootUuid]; + + if (action !== undefined) { + this._deactivateAction(action); + + this._removeInactiveAction(action); + } + } + + var bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[rootUuid]; + + if (bindingByName !== undefined) { + for (var trackName in bindingByName) { + var binding = bindingByName[trackName]; + binding.restoreOriginalState(); + + this._removeInactiveBinding(binding); + } + } + } // remove a targeted clip from the cache + ; + + _proto.uncacheAction = function uncacheAction(clip, optionalRoot) { + var action = this.existingAction(clip, optionalRoot); + + if (action !== null) { + this._deactivateAction(action); + + this._removeInactiveAction(action); + } + }; + + return AnimationMixer; + }(EventDispatcher); + + AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array(1); + + var Uniform = /*#__PURE__*/function () { + function Uniform(value) { + if (typeof value === 'string') { + console.warn('THREE.Uniform: Type parameter is no longer needed.'); + value = arguments[1]; + } + + this.value = value; + } + + var _proto = Uniform.prototype; + + _proto.clone = function clone() { + return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); + }; + + return Uniform; + }(); + + function InstancedInterleavedBuffer(array, stride, meshPerAttribute) { + InterleavedBuffer.call(this, array, stride); + this.meshPerAttribute = meshPerAttribute || 1; + } + + InstancedInterleavedBuffer.prototype = Object.assign(Object.create(InterleavedBuffer.prototype), { + constructor: InstancedInterleavedBuffer, + isInstancedInterleavedBuffer: true, + copy: function copy(source) { + InterleavedBuffer.prototype.copy.call(this, source); + this.meshPerAttribute = source.meshPerAttribute; + return this; + }, + clone: function clone(data) { + var ib = InterleavedBuffer.prototype.clone.call(this, data); + ib.meshPerAttribute = this.meshPerAttribute; + return ib; + }, + toJSON: function toJSON(data) { + var json = InterleavedBuffer.prototype.toJSON.call(this, data); + json.isInstancedInterleavedBuffer = true; + json.meshPerAttribute = this.meshPerAttribute; + return json; + } + }); + + function GLBufferAttribute(buffer, type, itemSize, elementSize, count) { + this.buffer = buffer; + this.type = type; + this.itemSize = itemSize; + this.elementSize = elementSize; + this.count = count; + this.version = 0; + } + + Object.defineProperty(GLBufferAttribute.prototype, 'needsUpdate', { + set: function set(value) { + if (value === true) this.version++; + } + }); + Object.assign(GLBufferAttribute.prototype, { + isGLBufferAttribute: true, + setBuffer: function setBuffer(buffer) { + this.buffer = buffer; + return this; + }, + setType: function setType(type, elementSize) { + this.type = type; + this.elementSize = elementSize; + return this; + }, + setItemSize: function setItemSize(itemSize) { + this.itemSize = itemSize; + return this; + }, + setCount: function setCount(count) { + this.count = count; + return this; + } + }); + + function Raycaster(origin, direction, near, far) { + if (near === void 0) { + near = 0; + } + + if (far === void 0) { + far = Infinity; + } + + this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near; + this.far = far; + this.camera = null; + this.layers = new Layers(); + this.params = { + Mesh: {}, + Line: { + threshold: 1 + }, + LOD: {}, + Points: { + threshold: 1 + }, + Sprite: {} + }; + Object.defineProperties(this.params, { + PointCloud: { + get: function get() { + console.warn('THREE.Raycaster: params.PointCloud has been renamed to params.Points.'); + return this.Points; + } + } + }); + } + + function ascSort(a, b) { + return a.distance - b.distance; + } + + function _intersectObject(object, raycaster, intersects, recursive) { + if (object.layers.test(raycaster.layers)) { + object.raycast(raycaster, intersects); + } + + if (recursive === true) { + var children = object.children; + + for (var i = 0, l = children.length; i < l; i++) { + _intersectObject(children[i], raycaster, intersects, true); + } + } + } + + Object.assign(Raycaster.prototype, { + set: function set(origin, direction) { + // direction is assumed to be normalized (for accurate distance calculations) + this.ray.set(origin, direction); + }, + setFromCamera: function setFromCamera(coords, camera) { + if (camera && camera.isPerspectiveCamera) { + this.ray.origin.setFromMatrixPosition(camera.matrixWorld); + this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); + this.camera = camera; + } else if (camera && camera.isOrthographicCamera) { + this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera + + this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); + this.camera = camera; + } else { + console.error('THREE.Raycaster: Unsupported camera type: ' + camera.type); + } + }, + intersectObject: function intersectObject(object, recursive, intersects) { + if (recursive === void 0) { + recursive = false; + } + + if (intersects === void 0) { + intersects = []; + } + + _intersectObject(object, this, intersects, recursive); + + intersects.sort(ascSort); + return intersects; + }, + intersectObjects: function intersectObjects(objects, recursive, intersects) { + if (recursive === void 0) { + recursive = false; + } + + if (intersects === void 0) { + intersects = []; + } + + for (var i = 0, l = objects.length; i < l; i++) { + _intersectObject(objects[i], this, intersects, recursive); + } + + intersects.sort(ascSort); + return intersects; + } + }); + + /** + * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system + * + * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. + * The azimuthal angle (theta) is measured from the positive z-axis. + */ + + var Spherical = /*#__PURE__*/function () { + function Spherical(radius, phi, theta) { + if (radius === void 0) { + radius = 1; + } + + if (phi === void 0) { + phi = 0; + } + + if (theta === void 0) { + theta = 0; + } + + this.radius = radius; + this.phi = phi; // polar angle + + this.theta = theta; // azimuthal angle + + return this; + } + + var _proto = Spherical.prototype; + + _proto.set = function set(radius, phi, theta) { + this.radius = radius; + this.phi = phi; + this.theta = theta; + return this; + }; + + _proto.copy = function copy(other) { + this.radius = other.radius; + this.phi = other.phi; + this.theta = other.theta; + return this; + } // restrict phi to be betwee EPS and PI-EPS + ; + + _proto.makeSafe = function makeSafe() { + var EPS = 0.000001; + this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); + return this; + }; + + _proto.setFromVector3 = function setFromVector3(v) { + return this.setFromCartesianCoords(v.x, v.y, v.z); + }; + + _proto.setFromCartesianCoords = function setFromCartesianCoords(x, y, z) { + this.radius = Math.sqrt(x * x + y * y + z * z); + + if (this.radius === 0) { + this.theta = 0; + this.phi = 0; + } else { + this.theta = Math.atan2(x, z); + this.phi = Math.acos(MathUtils.clamp(y / this.radius, -1, 1)); + } + + return this; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Spherical; + }(); + + /** + * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system + */ + var Cylindrical = /*#__PURE__*/function () { + function Cylindrical(radius, theta, y) { + if (radius === void 0) { + radius = 1; + } + + if (theta === void 0) { + theta = 0; + } + + if (y === void 0) { + y = 0; + } + + this.radius = radius; // distance from the origin to a point in the x-z plane + + this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis + + this.y = y; // height above the x-z plane + + return this; + } + + var _proto = Cylindrical.prototype; + + _proto.set = function set(radius, theta, y) { + this.radius = radius; + this.theta = theta; + this.y = y; + return this; + }; + + _proto.copy = function copy(other) { + this.radius = other.radius; + this.theta = other.theta; + this.y = other.y; + return this; + }; + + _proto.setFromVector3 = function setFromVector3(v) { + return this.setFromCartesianCoords(v.x, v.y, v.z); + }; + + _proto.setFromCartesianCoords = function setFromCartesianCoords(x, y, z) { + this.radius = Math.sqrt(x * x + z * z); + this.theta = Math.atan2(x, z); + this.y = y; + return this; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Cylindrical; + }(); + + var _vector$8 = /*@__PURE__*/new Vector2(); + + var Box2 = /*#__PURE__*/function () { + function Box2(min, max) { + if (min === void 0) { + min = new Vector2(+Infinity, +Infinity); + } + + if (max === void 0) { + max = new Vector2(-Infinity, -Infinity); + } + + this.min = min; + this.max = max; + } + + var _proto = Box2.prototype; + + _proto.set = function set(min, max) { + this.min.copy(min); + this.max.copy(max); + return this; + }; + + _proto.setFromPoints = function setFromPoints(points) { + this.makeEmpty(); + + for (var i = 0, il = points.length; i < il; i++) { + this.expandByPoint(points[i]); + } + + return this; + }; + + _proto.setFromCenterAndSize = function setFromCenterAndSize(center, size) { + var halfSize = _vector$8.copy(size).multiplyScalar(0.5); + + this.min.copy(center).sub(halfSize); + this.max.copy(center).add(halfSize); + return this; + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + _proto.copy = function copy(box) { + this.min.copy(box.min); + this.max.copy(box.max); + return this; + }; + + _proto.makeEmpty = function makeEmpty() { + this.min.x = this.min.y = +Infinity; + this.max.x = this.max.y = -Infinity; + return this; + }; + + _proto.isEmpty = function isEmpty() { + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + return this.max.x < this.min.x || this.max.y < this.min.y; + }; + + _proto.getCenter = function getCenter(target) { + if (target === undefined) { + console.warn('THREE.Box2: .getCenter() target is now required'); + target = new Vector2(); + } + + return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); + }; + + _proto.getSize = function getSize(target) { + if (target === undefined) { + console.warn('THREE.Box2: .getSize() target is now required'); + target = new Vector2(); + } + + return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); + }; + + _proto.expandByPoint = function expandByPoint(point) { + this.min.min(point); + this.max.max(point); + return this; + }; + + _proto.expandByVector = function expandByVector(vector) { + this.min.sub(vector); + this.max.add(vector); + return this; + }; + + _proto.expandByScalar = function expandByScalar(scalar) { + this.min.addScalar(-scalar); + this.max.addScalar(scalar); + return this; + }; + + _proto.containsPoint = function containsPoint(point) { + return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; + }; + + _proto.containsBox = function containsBox(box) { + return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; + }; + + _proto.getParameter = function getParameter(point, target) { + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + if (target === undefined) { + console.warn('THREE.Box2: .getParameter() target is now required'); + target = new Vector2(); + } + + return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); + }; + + _proto.intersectsBox = function intersectsBox(box) { + // using 4 splitting planes to rule out intersections + return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; + }; + + _proto.clampPoint = function clampPoint(point, target) { + if (target === undefined) { + console.warn('THREE.Box2: .clampPoint() target is now required'); + target = new Vector2(); + } + + return target.copy(point).clamp(this.min, this.max); + }; + + _proto.distanceToPoint = function distanceToPoint(point) { + var clampedPoint = _vector$8.copy(point).clamp(this.min, this.max); + + return clampedPoint.sub(point).length(); + }; + + _proto.intersect = function intersect(box) { + this.min.max(box.min); + this.max.min(box.max); + return this; + }; + + _proto.union = function union(box) { + this.min.min(box.min); + this.max.max(box.max); + return this; + }; + + _proto.translate = function translate(offset) { + this.min.add(offset); + this.max.add(offset); + return this; + }; + + _proto.equals = function equals(box) { + return box.min.equals(this.min) && box.max.equals(this.max); + }; + + return Box2; + }(); + + Box2.prototype.isBox2 = true; + + var _startP = /*@__PURE__*/new Vector3(); + + var _startEnd = /*@__PURE__*/new Vector3(); + + var Line3 = /*#__PURE__*/function () { + function Line3(start, end) { + if (start === void 0) { + start = new Vector3(); + } + + if (end === void 0) { + end = new Vector3(); + } + + this.start = start; + this.end = end; + } + + var _proto = Line3.prototype; + + _proto.set = function set(start, end) { + this.start.copy(start); + this.end.copy(end); + return this; + }; + + _proto.copy = function copy(line) { + this.start.copy(line.start); + this.end.copy(line.end); + return this; + }; + + _proto.getCenter = function getCenter(target) { + if (target === undefined) { + console.warn('THREE.Line3: .getCenter() target is now required'); + target = new Vector3(); + } + + return target.addVectors(this.start, this.end).multiplyScalar(0.5); + }; + + _proto.delta = function delta(target) { + if (target === undefined) { + console.warn('THREE.Line3: .delta() target is now required'); + target = new Vector3(); + } + + return target.subVectors(this.end, this.start); + }; + + _proto.distanceSq = function distanceSq() { + return this.start.distanceToSquared(this.end); + }; + + _proto.distance = function distance() { + return this.start.distanceTo(this.end); + }; + + _proto.at = function at(t, target) { + if (target === undefined) { + console.warn('THREE.Line3: .at() target is now required'); + target = new Vector3(); + } + + return this.delta(target).multiplyScalar(t).add(this.start); + }; + + _proto.closestPointToPointParameter = function closestPointToPointParameter(point, clampToLine) { + _startP.subVectors(point, this.start); + + _startEnd.subVectors(this.end, this.start); + + var startEnd2 = _startEnd.dot(_startEnd); + + var startEnd_startP = _startEnd.dot(_startP); + + var t = startEnd_startP / startEnd2; + + if (clampToLine) { + t = MathUtils.clamp(t, 0, 1); + } + + return t; + }; + + _proto.closestPointToPoint = function closestPointToPoint(point, clampToLine, target) { + var t = this.closestPointToPointParameter(point, clampToLine); + + if (target === undefined) { + console.warn('THREE.Line3: .closestPointToPoint() target is now required'); + target = new Vector3(); + } + + return this.delta(target).multiplyScalar(t).add(this.start); + }; + + _proto.applyMatrix4 = function applyMatrix4(matrix) { + this.start.applyMatrix4(matrix); + this.end.applyMatrix4(matrix); + return this; + }; + + _proto.equals = function equals(line) { + return line.start.equals(this.start) && line.end.equals(this.end); + }; + + _proto.clone = function clone() { + return new this.constructor().copy(this); + }; + + return Line3; + }(); + + function ImmediateRenderObject(material) { + Object3D.call(this); + this.material = material; + + this.render = function () + /* renderCallback */ + {}; + + this.hasPositions = false; + this.hasNormals = false; + this.hasColors = false; + this.hasUvs = false; + this.positionArray = null; + this.normalArray = null; + this.colorArray = null; + this.uvArray = null; + this.count = 0; + } + + ImmediateRenderObject.prototype = Object.create(Object3D.prototype); + ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; + ImmediateRenderObject.prototype.isImmediateRenderObject = true; + + var _vector$9 = /*@__PURE__*/new Vector3(); + + var SpotLightHelper = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(SpotLightHelper, _Object3D); + + function SpotLightHelper(light, color) { + var _this; + + _this = _Object3D.call(this) || this; + _this.light = light; + + _this.light.updateMatrixWorld(); + + _this.matrix = light.matrixWorld; + _this.matrixAutoUpdate = false; + _this.color = color; + var geometry = new BufferGeometry(); + var positions = [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, 1]; + + for (var i = 0, j = 1, l = 32; i < l; i++, j++) { + var p1 = i / l * Math.PI * 2; + var p2 = j / l * Math.PI * 2; + positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); + } + + geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); + var material = new LineBasicMaterial({ + fog: false, + toneMapped: false + }); + _this.cone = new LineSegments(geometry, material); + + _this.add(_this.cone); + + _this.update(); + + return _this; + } + + var _proto = SpotLightHelper.prototype; + + _proto.dispose = function dispose() { + this.cone.geometry.dispose(); + this.cone.material.dispose(); + }; + + _proto.update = function update() { + this.light.updateMatrixWorld(); + var coneLength = this.light.distance ? this.light.distance : 1000; + var coneWidth = coneLength * Math.tan(this.light.angle); + this.cone.scale.set(coneWidth, coneWidth, coneLength); + + _vector$9.setFromMatrixPosition(this.light.target.matrixWorld); + + this.cone.lookAt(_vector$9); + + if (this.color !== undefined) { + this.cone.material.color.set(this.color); + } else { + this.cone.material.color.copy(this.light.color); + } + }; + + return SpotLightHelper; + }(Object3D); + + var _vector$a = /*@__PURE__*/new Vector3(); + + var _boneMatrix = /*@__PURE__*/new Matrix4(); + + var _matrixWorldInv = /*@__PURE__*/new Matrix4(); + + var SkeletonHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(SkeletonHelper, _LineSegments); + + function SkeletonHelper(object) { + var _this; + + var bones = getBoneList(object); + var geometry = new BufferGeometry(); + var vertices = []; + var colors = []; + var color1 = new Color(0, 0, 1); + var color2 = new Color(0, 1, 0); + + for (var i = 0; i < bones.length; i++) { + var bone = bones[i]; + + if (bone.parent && bone.parent.isBone) { + vertices.push(0, 0, 0); + vertices.push(0, 0, 0); + colors.push(color1.r, color1.g, color1.b); + colors.push(color2.r, color2.g, color2.b); + } + } + + geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); + var material = new LineBasicMaterial({ + vertexColors: true, + depthTest: false, + depthWrite: false, + toneMapped: false, + transparent: true + }); + _this = _LineSegments.call(this, geometry, material) || this; + _this.type = 'SkeletonHelper'; + _this.isSkeletonHelper = true; + _this.root = object; + _this.bones = bones; + _this.matrix = object.matrixWorld; + _this.matrixAutoUpdate = false; + return _this; + } + + var _proto = SkeletonHelper.prototype; + + _proto.updateMatrixWorld = function updateMatrixWorld(force) { + var bones = this.bones; + var geometry = this.geometry; + var position = geometry.getAttribute('position'); + + _matrixWorldInv.copy(this.root.matrixWorld).invert(); + + for (var i = 0, j = 0; i < bones.length; i++) { + var bone = bones[i]; + + if (bone.parent && bone.parent.isBone) { + _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); + + _vector$a.setFromMatrixPosition(_boneMatrix); + + position.setXYZ(j, _vector$a.x, _vector$a.y, _vector$a.z); + + _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); + + _vector$a.setFromMatrixPosition(_boneMatrix); + + position.setXYZ(j + 1, _vector$a.x, _vector$a.y, _vector$a.z); + j += 2; + } + } + + geometry.getAttribute('position').needsUpdate = true; + + _LineSegments.prototype.updateMatrixWorld.call(this, force); + }; + + return SkeletonHelper; + }(LineSegments); + + function getBoneList(object) { + var boneList = []; + + if (object && object.isBone) { + boneList.push(object); + } + + for (var i = 0; i < object.children.length; i++) { + boneList.push.apply(boneList, getBoneList(object.children[i])); + } + + return boneList; + } + + var PointLightHelper = /*#__PURE__*/function (_Mesh) { + _inheritsLoose(PointLightHelper, _Mesh); + + function PointLightHelper(light, sphereSize, color) { + var _this; + + var geometry = new SphereGeometry(sphereSize, 4, 2); + var material = new MeshBasicMaterial({ + wireframe: true, + fog: false, + toneMapped: false + }); + _this = _Mesh.call(this, geometry, material) || this; + _this.light = light; + + _this.light.updateMatrixWorld(); + + _this.color = color; + _this.type = 'PointLightHelper'; + _this.matrix = _this.light.matrixWorld; + _this.matrixAutoUpdate = false; + + _this.update(); + /* + // TODO: delete this comment? + const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); + const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + const d = light.distance; + if ( d === 0.0 ) { + this.lightDistance.visible = false; + } else { + this.lightDistance.scale.set( d, d, d ); + } + this.add( this.lightDistance ); + */ + + + return _this; + } + + var _proto = PointLightHelper.prototype; + + _proto.dispose = function dispose() { + this.geometry.dispose(); + this.material.dispose(); + }; + + _proto.update = function update() { + if (this.color !== undefined) { + this.material.color.set(this.color); + } else { + this.material.color.copy(this.light.color); + } + /* + const d = this.light.distance; + if ( d === 0.0 ) { + this.lightDistance.visible = false; + } else { + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + } + */ + + }; + + return PointLightHelper; + }(Mesh); + + var _vector$b = /*@__PURE__*/new Vector3(); + + var _color1 = /*@__PURE__*/new Color(); + + var _color2 = /*@__PURE__*/new Color(); + + var HemisphereLightHelper = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(HemisphereLightHelper, _Object3D); + + function HemisphereLightHelper(light, size, color) { + var _this; + + _this = _Object3D.call(this) || this; + _this.light = light; + + _this.light.updateMatrixWorld(); + + _this.matrix = light.matrixWorld; + _this.matrixAutoUpdate = false; + _this.color = color; + var geometry = new OctahedronGeometry(size); + geometry.rotateY(Math.PI * 0.5); + _this.material = new MeshBasicMaterial({ + wireframe: true, + fog: false, + toneMapped: false + }); + if (_this.color === undefined) _this.material.vertexColors = true; + var position = geometry.getAttribute('position'); + var colors = new Float32Array(position.count * 3); + geometry.setAttribute('color', new BufferAttribute(colors, 3)); + + _this.add(new Mesh(geometry, _this.material)); + + _this.update(); + + return _this; + } + + var _proto = HemisphereLightHelper.prototype; + + _proto.dispose = function dispose() { + this.children[0].geometry.dispose(); + this.children[0].material.dispose(); + }; + + _proto.update = function update() { + var mesh = this.children[0]; + + if (this.color !== undefined) { + this.material.color.set(this.color); + } else { + var colors = mesh.geometry.getAttribute('color'); + + _color1.copy(this.light.color); + + _color2.copy(this.light.groundColor); + + for (var i = 0, l = colors.count; i < l; i++) { + var color = i < l / 2 ? _color1 : _color2; + colors.setXYZ(i, color.r, color.g, color.b); + } + + colors.needsUpdate = true; + } + + mesh.lookAt(_vector$b.setFromMatrixPosition(this.light.matrixWorld).negate()); + }; + + return HemisphereLightHelper; + }(Object3D); + + var GridHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(GridHelper, _LineSegments); + + function GridHelper(size, divisions, color1, color2) { + var _this; + + if (size === void 0) { + size = 10; + } + + if (divisions === void 0) { + divisions = 10; + } + + if (color1 === void 0) { + color1 = 0x444444; + } + + if (color2 === void 0) { + color2 = 0x888888; + } + + color1 = new Color(color1); + color2 = new Color(color2); + var center = divisions / 2; + var step = size / divisions; + var halfSize = size / 2; + var vertices = [], + colors = []; + + for (var i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { + vertices.push(-halfSize, 0, k, halfSize, 0, k); + vertices.push(k, 0, -halfSize, k, 0, halfSize); + var color = i === center ? color1 : color2; + color.toArray(colors, j); + j += 3; + color.toArray(colors, j); + j += 3; + color.toArray(colors, j); + j += 3; + color.toArray(colors, j); + j += 3; + } + + var geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); + var material = new LineBasicMaterial({ + vertexColors: true, + toneMapped: false + }); + _this = _LineSegments.call(this, geometry, material) || this; + _this.type = 'GridHelper'; + return _this; + } + + return GridHelper; + }(LineSegments); + + var PolarGridHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(PolarGridHelper, _LineSegments); + + function PolarGridHelper(radius, radials, circles, divisions, color1, color2) { + var _this; + + if (radius === void 0) { + radius = 10; + } + + if (radials === void 0) { + radials = 16; + } + + if (circles === void 0) { + circles = 8; + } + + if (divisions === void 0) { + divisions = 64; + } + + if (color1 === void 0) { + color1 = 0x444444; + } + + if (color2 === void 0) { + color2 = 0x888888; + } + + color1 = new Color(color1); + color2 = new Color(color2); + var vertices = []; + var colors = []; // create the radials + + for (var i = 0; i <= radials; i++) { + var v = i / radials * (Math.PI * 2); + var x = Math.sin(v) * radius; + var z = Math.cos(v) * radius; + vertices.push(0, 0, 0); + vertices.push(x, 0, z); + var color = i & 1 ? color1 : color2; + colors.push(color.r, color.g, color.b); + colors.push(color.r, color.g, color.b); + } // create the circles + + + for (var _i = 0; _i <= circles; _i++) { + var _color = _i & 1 ? color1 : color2; + + var r = radius - radius / circles * _i; + + for (var j = 0; j < divisions; j++) { + // first vertex + var _v = j / divisions * (Math.PI * 2); + + var _x = Math.sin(_v) * r; + + var _z = Math.cos(_v) * r; + + vertices.push(_x, 0, _z); + colors.push(_color.r, _color.g, _color.b); // second vertex + + _v = (j + 1) / divisions * (Math.PI * 2); + _x = Math.sin(_v) * r; + _z = Math.cos(_v) * r; + vertices.push(_x, 0, _z); + colors.push(_color.r, _color.g, _color.b); + } + } + + var geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); + var material = new LineBasicMaterial({ + vertexColors: true, + toneMapped: false + }); + _this = _LineSegments.call(this, geometry, material) || this; + _this.type = 'PolarGridHelper'; + return _this; + } + + return PolarGridHelper; + }(LineSegments); + + var _v1$6 = /*@__PURE__*/new Vector3(); + + var _v2$3 = /*@__PURE__*/new Vector3(); + + var _v3$1 = /*@__PURE__*/new Vector3(); + + var DirectionalLightHelper = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(DirectionalLightHelper, _Object3D); + + function DirectionalLightHelper(light, size, color) { + var _this; + + _this = _Object3D.call(this) || this; + _this.light = light; + + _this.light.updateMatrixWorld(); + + _this.matrix = light.matrixWorld; + _this.matrixAutoUpdate = false; + _this.color = color; + if (size === undefined) size = 1; + var geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); + var material = new LineBasicMaterial({ + fog: false, + toneMapped: false + }); + _this.lightPlane = new Line(geometry, material); + + _this.add(_this.lightPlane); + + geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); + _this.targetLine = new Line(geometry, material); + + _this.add(_this.targetLine); + + _this.update(); + + return _this; + } + + var _proto = DirectionalLightHelper.prototype; + + _proto.dispose = function dispose() { + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); + }; + + _proto.update = function update() { + _v1$6.setFromMatrixPosition(this.light.matrixWorld); + + _v2$3.setFromMatrixPosition(this.light.target.matrixWorld); + + _v3$1.subVectors(_v2$3, _v1$6); + + this.lightPlane.lookAt(_v2$3); + + if (this.color !== undefined) { + this.lightPlane.material.color.set(this.color); + this.targetLine.material.color.set(this.color); + } else { + this.lightPlane.material.color.copy(this.light.color); + this.targetLine.material.color.copy(this.light.color); + } + + this.targetLine.lookAt(_v2$3); + this.targetLine.scale.z = _v3$1.length(); + }; + + return DirectionalLightHelper; + }(Object3D); + + var _vector$c = /*@__PURE__*/new Vector3(); + + var _camera = /*@__PURE__*/new Camera(); + /** + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + + + var CameraHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(CameraHelper, _LineSegments); + + function CameraHelper(camera) { + var _this; + + var geometry = new BufferGeometry(); + var material = new LineBasicMaterial({ + color: 0xffffff, + vertexColors: true, + toneMapped: false + }); + var vertices = []; + var colors = []; + var pointMap = {}; // colors + + var colorFrustum = new Color(0xffaa00); + var colorCone = new Color(0xff0000); + var colorUp = new Color(0x00aaff); + var colorTarget = new Color(0xffffff); + var colorCross = new Color(0x333333); // near + + addLine('n1', 'n2', colorFrustum); + addLine('n2', 'n4', colorFrustum); + addLine('n4', 'n3', colorFrustum); + addLine('n3', 'n1', colorFrustum); // far + + addLine('f1', 'f2', colorFrustum); + addLine('f2', 'f4', colorFrustum); + addLine('f4', 'f3', colorFrustum); + addLine('f3', 'f1', colorFrustum); // sides + + addLine('n1', 'f1', colorFrustum); + addLine('n2', 'f2', colorFrustum); + addLine('n3', 'f3', colorFrustum); + addLine('n4', 'f4', colorFrustum); // cone + + addLine('p', 'n1', colorCone); + addLine('p', 'n2', colorCone); + addLine('p', 'n3', colorCone); + addLine('p', 'n4', colorCone); // up + + addLine('u1', 'u2', colorUp); + addLine('u2', 'u3', colorUp); + addLine('u3', 'u1', colorUp); // target + + addLine('c', 't', colorTarget); + addLine('p', 'c', colorCross); // cross + + addLine('cn1', 'cn2', colorCross); + addLine('cn3', 'cn4', colorCross); + addLine('cf1', 'cf2', colorCross); + addLine('cf3', 'cf4', colorCross); + + function addLine(a, b, color) { + addPoint(a, color); + addPoint(b, color); + } + + function addPoint(id, color) { + vertices.push(0, 0, 0); + colors.push(color.r, color.g, color.b); + + if (pointMap[id] === undefined) { + pointMap[id] = []; + } + + pointMap[id].push(vertices.length / 3 - 1); + } + + geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); + _this = _LineSegments.call(this, geometry, material) || this; + _this.type = 'CameraHelper'; + _this.camera = camera; + if (_this.camera.updateProjectionMatrix) _this.camera.updateProjectionMatrix(); + _this.matrix = camera.matrixWorld; + _this.matrixAutoUpdate = false; + _this.pointMap = pointMap; + + _this.update(); + + return _this; + } + + var _proto = CameraHelper.prototype; + + _proto.update = function update() { + var geometry = this.geometry; + var pointMap = this.pointMap; + var w = 1, + h = 1; // we need just camera projection matrix inverse + // world matrix must be identity + + _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target + + + setPoint('c', pointMap, geometry, _camera, 0, 0, -1); + setPoint('t', pointMap, geometry, _camera, 0, 0, 1); // near + + setPoint('n1', pointMap, geometry, _camera, -w, -h, -1); + setPoint('n2', pointMap, geometry, _camera, w, -h, -1); + setPoint('n3', pointMap, geometry, _camera, -w, h, -1); + setPoint('n4', pointMap, geometry, _camera, w, h, -1); // far + + setPoint('f1', pointMap, geometry, _camera, -w, -h, 1); + setPoint('f2', pointMap, geometry, _camera, w, -h, 1); + setPoint('f3', pointMap, geometry, _camera, -w, h, 1); + setPoint('f4', pointMap, geometry, _camera, w, h, 1); // up + + setPoint('u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); + setPoint('u2', pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); + setPoint('u3', pointMap, geometry, _camera, 0, h * 2, -1); // cross + + setPoint('cf1', pointMap, geometry, _camera, -w, 0, 1); + setPoint('cf2', pointMap, geometry, _camera, w, 0, 1); + setPoint('cf3', pointMap, geometry, _camera, 0, -h, 1); + setPoint('cf4', pointMap, geometry, _camera, 0, h, 1); + setPoint('cn1', pointMap, geometry, _camera, -w, 0, -1); + setPoint('cn2', pointMap, geometry, _camera, w, 0, -1); + setPoint('cn3', pointMap, geometry, _camera, 0, -h, -1); + setPoint('cn4', pointMap, geometry, _camera, 0, h, -1); + geometry.getAttribute('position').needsUpdate = true; + }; + + return CameraHelper; + }(LineSegments); + + function setPoint(point, pointMap, geometry, camera, x, y, z) { + _vector$c.set(x, y, z).unproject(camera); + + var points = pointMap[point]; + + if (points !== undefined) { + var position = geometry.getAttribute('position'); + + for (var i = 0, l = points.length; i < l; i++) { + position.setXYZ(points[i], _vector$c.x, _vector$c.y, _vector$c.z); + } + } + } + + var _box$3 = /*@__PURE__*/new Box3(); + + var BoxHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(BoxHelper, _LineSegments); + + function BoxHelper(object, color) { + var _this; + + if (color === void 0) { + color = 0xffff00; + } + + var indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); + var positions = new Float32Array(8 * 3); + var geometry = new BufferGeometry(); + geometry.setIndex(new BufferAttribute(indices, 1)); + geometry.setAttribute('position', new BufferAttribute(positions, 3)); + _this = _LineSegments.call(this, geometry, new LineBasicMaterial({ + color: color, + toneMapped: false + })) || this; + _this.object = object; + _this.type = 'BoxHelper'; + _this.matrixAutoUpdate = false; + + _this.update(); + + return _this; + } + + var _proto = BoxHelper.prototype; + + _proto.update = function update(object) { + if (object !== undefined) { + console.warn('THREE.BoxHelper: .update() has no longer arguments.'); + } + + if (this.object !== undefined) { + _box$3.setFromObject(this.object); + } + + if (_box$3.isEmpty()) return; + var min = _box$3.min; + var max = _box$3.max; + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + + var position = this.geometry.attributes.position; + var array = position.array; + array[0] = max.x; + array[1] = max.y; + array[2] = max.z; + array[3] = min.x; + array[4] = max.y; + array[5] = max.z; + array[6] = min.x; + array[7] = min.y; + array[8] = max.z; + array[9] = max.x; + array[10] = min.y; + array[11] = max.z; + array[12] = max.x; + array[13] = max.y; + array[14] = min.z; + array[15] = min.x; + array[16] = max.y; + array[17] = min.z; + array[18] = min.x; + array[19] = min.y; + array[20] = min.z; + array[21] = max.x; + array[22] = min.y; + array[23] = min.z; + position.needsUpdate = true; + this.geometry.computeBoundingSphere(); + }; + + _proto.setFromObject = function setFromObject(object) { + this.object = object; + this.update(); + return this; + }; + + _proto.copy = function copy(source) { + LineSegments.prototype.copy.call(this, source); + this.object = source.object; + return this; + }; + + return BoxHelper; + }(LineSegments); + + var Box3Helper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(Box3Helper, _LineSegments); + + function Box3Helper(box, color) { + var _this; + + if (color === void 0) { + color = 0xffff00; + } + + var indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); + var positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; + var geometry = new BufferGeometry(); + geometry.setIndex(new BufferAttribute(indices, 1)); + geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); + _this = _LineSegments.call(this, geometry, new LineBasicMaterial({ + color: color, + toneMapped: false + })) || this; + _this.box = box; + _this.type = 'Box3Helper'; + + _this.geometry.computeBoundingSphere(); + + return _this; + } + + var _proto = Box3Helper.prototype; + + _proto.updateMatrixWorld = function updateMatrixWorld(force) { + var box = this.box; + if (box.isEmpty()) return; + box.getCenter(this.position); + box.getSize(this.scale); + this.scale.multiplyScalar(0.5); + + _LineSegments.prototype.updateMatrixWorld.call(this, force); + }; + + return Box3Helper; + }(LineSegments); + + var PlaneHelper = /*#__PURE__*/function (_Line) { + _inheritsLoose(PlaneHelper, _Line); + + function PlaneHelper(plane, size, hex) { + var _this; + + if (size === void 0) { + size = 1; + } + + if (hex === void 0) { + hex = 0xffff00; + } + + var color = hex; + var positions = [1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0]; + var geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); + geometry.computeBoundingSphere(); + _this = _Line.call(this, geometry, new LineBasicMaterial({ + color: color, + toneMapped: false + })) || this; + _this.type = 'PlaneHelper'; + _this.plane = plane; + _this.size = size; + var positions2 = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1]; + var geometry2 = new BufferGeometry(); + geometry2.setAttribute('position', new Float32BufferAttribute(positions2, 3)); + geometry2.computeBoundingSphere(); + + _this.add(new Mesh(geometry2, new MeshBasicMaterial({ + color: color, + opacity: 0.2, + transparent: true, + depthWrite: false, + toneMapped: false + }))); + + return _this; + } + + var _proto = PlaneHelper.prototype; + + _proto.updateMatrixWorld = function updateMatrixWorld(force) { + var scale = -this.plane.constant; + if (Math.abs(scale) < 1e-8) scale = 1e-8; // sign does not matter + + this.scale.set(0.5 * this.size, 0.5 * this.size, scale); + this.children[0].material.side = scale < 0 ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here + + this.lookAt(this.plane.normal); + + _Line.prototype.updateMatrixWorld.call(this, force); + }; + + return PlaneHelper; + }(Line); + + var _axis = /*@__PURE__*/new Vector3(); + + var _lineGeometry, _coneGeometry; + + var ArrowHelper = /*#__PURE__*/function (_Object3D) { + _inheritsLoose(ArrowHelper, _Object3D); + + // dir is assumed to be normalized + function ArrowHelper(dir, origin, length, color, headLength, headWidth) { + var _this; + + if (dir === void 0) { + dir = new Vector3(0, 0, 1); + } + + if (origin === void 0) { + origin = new Vector3(0, 0, 0); + } + + if (length === void 0) { + length = 1; + } + + if (color === void 0) { + color = 0xffff00; + } + + if (headLength === void 0) { + headLength = length * 0.2; + } + + if (headWidth === void 0) { + headWidth = headLength * 0.2; + } + + _this = _Object3D.call(this) || this; + _this.type = 'ArrowHelper'; + + if (_lineGeometry === undefined) { + _lineGeometry = new BufferGeometry(); + + _lineGeometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); + + _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); + + _coneGeometry.translate(0, -0.5, 0); + } + + _this.position.copy(origin); + + _this.line = new Line(_lineGeometry, new LineBasicMaterial({ + color: color, + toneMapped: false + })); + _this.line.matrixAutoUpdate = false; + + _this.add(_this.line); + + _this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ + color: color, + toneMapped: false + })); + _this.cone.matrixAutoUpdate = false; + + _this.add(_this.cone); + + _this.setDirection(dir); + + _this.setLength(length, headLength, headWidth); + + return _this; + } + + var _proto = ArrowHelper.prototype; + + _proto.setDirection = function setDirection(dir) { + // dir is assumed to be normalized + if (dir.y > 0.99999) { + this.quaternion.set(0, 0, 0, 1); + } else if (dir.y < -0.99999) { + this.quaternion.set(1, 0, 0, 0); + } else { + _axis.set(dir.z, 0, -dir.x).normalize(); + + var radians = Math.acos(dir.y); + this.quaternion.setFromAxisAngle(_axis, radians); + } + }; + + _proto.setLength = function setLength(length, headLength, headWidth) { + if (headLength === void 0) { + headLength = length * 0.2; + } + + if (headWidth === void 0) { + headWidth = headLength * 0.2; + } + + this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 + + this.line.updateMatrix(); + this.cone.scale.set(headWidth, headLength, headWidth); + this.cone.position.y = length; + this.cone.updateMatrix(); + }; + + _proto.setColor = function setColor(color) { + this.line.material.color.set(color); + this.cone.material.color.set(color); + }; + + _proto.copy = function copy(source) { + _Object3D.prototype.copy.call(this, source, false); + + this.line.copy(source.line); + this.cone.copy(source.cone); + return this; + }; + + return ArrowHelper; + }(Object3D); + + var AxesHelper = /*#__PURE__*/function (_LineSegments) { + _inheritsLoose(AxesHelper, _LineSegments); + + function AxesHelper(size) { + var _this; + + if (size === void 0) { + size = 1; + } + + var vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; + var colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; + var geometry = new BufferGeometry(); + geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); + var material = new LineBasicMaterial({ + vertexColors: true, + toneMapped: false + }); + _this = _LineSegments.call(this, geometry, material) || this; + _this.type = 'AxesHelper'; + return _this; + } + + return AxesHelper; + }(LineSegments); + + var _floatView = new Float32Array(1); + + var _int32View = new Int32Array(_floatView.buffer); + + var DataUtils = { + // Converts float32 to float16 (stored as uint16 value). + toHalfFloat: function toHalfFloat(val) { + // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 + + /* This method is faster than the OpenEXR implementation (very often + * used, eg. in Ogre), with the additional benefit of rounding, inspired + * by James Tursa?s half-precision code. */ + _floatView[0] = val; + var x = _int32View[0]; + var bits = x >> 16 & 0x8000; + /* Get the sign */ + + var m = x >> 12 & 0x07ff; + /* Keep one extra bit for rounding */ + + var e = x >> 23 & 0xff; + /* Using int is faster here */ + + /* If zero, or denormal, or exponent underflows too much for a denormal + * half, return signed zero. */ + + if (e < 103) return bits; + /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ + + if (e > 142) { + bits |= 0x7c00; + /* If exponent was 0xff and one mantissa bit was set, it means NaN, + * not Inf, so make sure we set one mantissa bit too. */ + + bits |= (e == 255 ? 0 : 1) && x & 0x007fffff; + return bits; + } + /* If exponent underflows but not too much, return a denormal */ + + + if (e < 113) { + m |= 0x0800; + /* Extra rounding may overflow and set mantissa to 0 and exponent + * to 1, which is OK. */ + + bits |= (m >> 114 - e) + (m >> 113 - e & 1); + return bits; + } + + bits |= e - 112 << 10 | m >> 1; + /* Extra rounding. An overflow will set mantissa to 0 and increment + * the exponent, which is OK. */ + + bits += m & 1; + return bits; + } + }; + + var _ENCODINGS; + var LOD_MIN = 4; + var LOD_MAX = 8; + var SIZE_MAX = Math.pow(2, LOD_MAX); // The standard deviations (radians) associated with the extra mips. These are + // chosen to approximate a Trowbridge-Reitz distribution function times the + // geometric shadowing function. These sigma values squared must match the + // variance #defines in cube_uv_reflection_fragment.glsl.js. + + var EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; + var TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // The maximum length of the blur for loop. Smaller sigmas will use fewer + // samples and exit early, but not recompile the shader. + + var MAX_SAMPLES = 20; + var ENCODINGS = (_ENCODINGS = {}, _ENCODINGS[LinearEncoding] = 0, _ENCODINGS[sRGBEncoding] = 1, _ENCODINGS[RGBEEncoding] = 2, _ENCODINGS[RGBM7Encoding] = 3, _ENCODINGS[RGBM16Encoding] = 4, _ENCODINGS[RGBDEncoding] = 5, _ENCODINGS[GammaEncoding] = 6, _ENCODINGS); + var backgroundMaterial = new MeshBasicMaterial({ + side: BackSide, + depthWrite: false, + depthTest: false + }); + var backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); + + var _flatCamera = /*@__PURE__*/new OrthographicCamera(); + + var _createPlanes2 = /*@__PURE__*/_createPlanes(), + _lodPlanes = _createPlanes2._lodPlanes, + _sizeLods = _createPlanes2._sizeLods, + _sigmas = _createPlanes2._sigmas; + + var _clearColor = /*@__PURE__*/new Color(); + + var _oldTarget = null; // Golden Ratio + + var PHI = (1 + Math.sqrt(5)) / 2; + var INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the + // same axis), used as axis directions evenly spread on a sphere. + + var _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; + /** + * This class generates a Prefiltered, Mipmapped Radiance Environment Map + * (PMREM) from a cubeMap environment texture. This allows different levels of + * blur to be quickly accessed based on material roughness. It is packed into a + * special CubeUV format that allows us to perform custom interpolation so that + * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap + * chain, it only goes down to the LOD_MIN level (above), and then creates extra + * even more filtered 'mips' at the same LOD_MIN resolution, associated with + * higher roughness levels. In this way we maintain resolution to smoothly + * interpolate diffuse lighting while limiting sampling computation. + */ + + function convertLinearToRGBE(color) { + var maxComponent = Math.max(color.r, color.g, color.b); + var fExp = Math.min(Math.max(Math.ceil(Math.log2(maxComponent)), -128.0), 127.0); + color.multiplyScalar(Math.pow(2.0, -fExp)); + var alpha = (fExp + 128.0) / 255.0; + return alpha; + } + + var PMREMGenerator = /*#__PURE__*/function () { + function PMREMGenerator(renderer) { + this._renderer = renderer; + this._pingPongRenderTarget = null; + this._blurMaterial = _getBlurShader(MAX_SAMPLES); + this._equirectShader = null; + this._cubemapShader = null; + + this._compileMaterial(this._blurMaterial); + } + /** + * Generates a PMREM from a supplied Scene, which can be faster than using an + * image if networking bandwidth is low. Optional sigma specifies a blur radius + * in radians to be applied to the scene before PMREM generation. Optional near + * and far planes ensure the scene is rendered in its entirety (the cubeCamera + * is placed at the origin). + */ + + + var _proto = PMREMGenerator.prototype; + + _proto.fromScene = function fromScene(scene, sigma, near, far) { + if (sigma === void 0) { + sigma = 0; + } + + if (near === void 0) { + near = 0.1; + } + + if (far === void 0) { + far = 100; + } + + _oldTarget = this._renderer.getRenderTarget(); + + var cubeUVRenderTarget = this._allocateTargets(); + + this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); + + if (sigma > 0) { + this._blur(cubeUVRenderTarget, 0, 0, sigma); + } + + this._applyPMREM(cubeUVRenderTarget); + + this._cleanup(cubeUVRenderTarget); + + return cubeUVRenderTarget; + } + /** + * Generates a PMREM from an equirectangular texture, which can be either LDR + * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512), + * as this matches best with the 256 x 256 cubemap output. + */ + ; + + _proto.fromEquirectangular = function fromEquirectangular(equirectangular) { + return this._fromTexture(equirectangular); + } + /** + * Generates a PMREM from an cubemap texture, which can be either LDR + * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256, + * as this matches best with the 256 x 256 cubemap output. + */ + ; + + _proto.fromCubemap = function fromCubemap(cubemap) { + return this._fromTexture(cubemap); + } + /** + * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + ; + + _proto.compileCubemapShader = function compileCubemapShader() { + if (this._cubemapShader === null) { + this._cubemapShader = _getCubemapShader(); + + this._compileMaterial(this._cubemapShader); + } + } + /** + * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + ; + + _proto.compileEquirectangularShader = function compileEquirectangularShader() { + if (this._equirectShader === null) { + this._equirectShader = _getEquirectShader(); + + this._compileMaterial(this._equirectShader); + } + } + /** + * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, + * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on + * one of them will cause any others to also become unusable. + */ + ; + + _proto.dispose = function dispose() { + this._blurMaterial.dispose(); + + if (this._cubemapShader !== null) this._cubemapShader.dispose(); + if (this._equirectShader !== null) this._equirectShader.dispose(); + + for (var i = 0; i < _lodPlanes.length; i++) { + _lodPlanes[i].dispose(); + } + } // private interface + ; + + _proto._cleanup = function _cleanup(outputTarget) { + this._pingPongRenderTarget.dispose(); + + this._renderer.setRenderTarget(_oldTarget); + + outputTarget.scissorTest = false; + + _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); + }; + + _proto._fromTexture = function _fromTexture(texture) { + _oldTarget = this._renderer.getRenderTarget(); + + var cubeUVRenderTarget = this._allocateTargets(texture); + + this._textureToCubeUV(texture, cubeUVRenderTarget); + + this._applyPMREM(cubeUVRenderTarget); + + this._cleanup(cubeUVRenderTarget); + + return cubeUVRenderTarget; + }; + + _proto._allocateTargets = function _allocateTargets(texture) { + // warning: null texture is valid + var params = { + magFilter: NearestFilter, + minFilter: NearestFilter, + generateMipmaps: false, + type: UnsignedByteType, + format: RGBEFormat, + encoding: _isLDR(texture) ? texture.encoding : RGBEEncoding, + depthBuffer: false + }; + + var cubeUVRenderTarget = _createRenderTarget(params); + + cubeUVRenderTarget.depthBuffer = texture ? false : true; + this._pingPongRenderTarget = _createRenderTarget(params); + return cubeUVRenderTarget; + }; + + _proto._compileMaterial = function _compileMaterial(material) { + var tmpMesh = new Mesh(_lodPlanes[0], material); + + this._renderer.compile(tmpMesh, _flatCamera); + }; + + _proto._sceneToCubeUV = function _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { + var fov = 90; + var aspect = 1; + var cubeCamera = new PerspectiveCamera(fov, aspect, near, far); + var upSign = [1, -1, 1, 1, 1, 1]; + var forwardSign = [1, 1, 1, -1, -1, -1]; + var renderer = this._renderer; + var originalAutoClear = renderer.autoClear; + var outputEncoding = renderer.outputEncoding; + var toneMapping = renderer.toneMapping; + renderer.getClearColor(_clearColor); + renderer.toneMapping = NoToneMapping; + renderer.outputEncoding = LinearEncoding; + renderer.autoClear = false; + var useSolidColor = false; + var background = scene.background; + + if (background) { + if (background.isColor) { + backgroundMaterial.color.copy(background).convertSRGBToLinear(); + scene.background = null; + var alpha = convertLinearToRGBE(backgroundMaterial.color); + backgroundMaterial.opacity = alpha; + useSolidColor = true; + } + } else { + backgroundMaterial.color.copy(_clearColor).convertSRGBToLinear(); + + var _alpha = convertLinearToRGBE(backgroundMaterial.color); + + backgroundMaterial.opacity = _alpha; + useSolidColor = true; + } + + for (var i = 0; i < 6; i++) { + var col = i % 3; + + if (col == 0) { + cubeCamera.up.set(0, upSign[i], 0); + cubeCamera.lookAt(forwardSign[i], 0, 0); + } else if (col == 1) { + cubeCamera.up.set(0, 0, upSign[i]); + cubeCamera.lookAt(0, forwardSign[i], 0); + } else { + cubeCamera.up.set(0, upSign[i], 0); + cubeCamera.lookAt(0, 0, forwardSign[i]); + } + + _setViewport(cubeUVRenderTarget, col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX); + + renderer.setRenderTarget(cubeUVRenderTarget); + + if (useSolidColor) { + renderer.render(backgroundBox, cubeCamera); + } + + renderer.render(scene, cubeCamera); + } + + renderer.toneMapping = toneMapping; + renderer.outputEncoding = outputEncoding; + renderer.autoClear = originalAutoClear; + }; + + _proto._textureToCubeUV = function _textureToCubeUV(texture, cubeUVRenderTarget) { + var renderer = this._renderer; + + if (texture.isCubeTexture) { + if (this._cubemapShader == null) { + this._cubemapShader = _getCubemapShader(); + } + } else { + if (this._equirectShader == null) { + this._equirectShader = _getEquirectShader(); + } + } + + var material = texture.isCubeTexture ? this._cubemapShader : this._equirectShader; + var mesh = new Mesh(_lodPlanes[0], material); + var uniforms = material.uniforms; + uniforms['envMap'].value = texture; + + if (!texture.isCubeTexture) { + uniforms['texelSize'].value.set(1.0 / texture.image.width, 1.0 / texture.image.height); + } + + uniforms['inputEncoding'].value = ENCODINGS[texture.encoding]; + uniforms['outputEncoding'].value = ENCODINGS[cubeUVRenderTarget.texture.encoding]; + + _setViewport(cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX); + + renderer.setRenderTarget(cubeUVRenderTarget); + renderer.render(mesh, _flatCamera); + }; + + _proto._applyPMREM = function _applyPMREM(cubeUVRenderTarget) { + var renderer = this._renderer; + var autoClear = renderer.autoClear; + renderer.autoClear = false; + + for (var i = 1; i < TOTAL_LODS; i++) { + var sigma = Math.sqrt(_sigmas[i] * _sigmas[i] - _sigmas[i - 1] * _sigmas[i - 1]); + var poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; + + this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); + } + + renderer.autoClear = autoClear; + } + /** + * This is a two-pass Gaussian blur for a cubemap. Normally this is done + * vertically and horizontally, but this breaks down on a cube. Here we apply + * the blur latitudinally (around the poles), and then longitudinally (towards + * the poles) to approximate the orthogonally-separable blur. It is least + * accurate at the poles, but still does a decent job. + */ + ; + + _proto._blur = function _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { + var pingPongRenderTarget = this._pingPongRenderTarget; + + this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, 'latitudinal', poleAxis); + + this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, 'longitudinal', poleAxis); + }; + + _proto._halfBlur = function _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { + var renderer = this._renderer; + var blurMaterial = this._blurMaterial; + + if (direction !== 'latitudinal' && direction !== 'longitudinal') { + console.error('blur direction must be either latitudinal or longitudinal!'); + } // Number of standard deviations at which to cut off the discrete approximation. + + + var STANDARD_DEVIATIONS = 3; + var blurMesh = new Mesh(_lodPlanes[lodOut], blurMaterial); + var blurUniforms = blurMaterial.uniforms; + var pixels = _sizeLods[lodIn] - 1; + var radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); + var sigmaPixels = sigmaRadians / radiansPerPixel; + var samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; + + if (samples > MAX_SAMPLES) { + console.warn("sigmaRadians, " + sigmaRadians + ", is too large and will clip, as it requested " + samples + " samples when the maximum is set to " + MAX_SAMPLES); + } + + var weights = []; + var sum = 0; + + for (var i = 0; i < MAX_SAMPLES; ++i) { + var _x = i / sigmaPixels; + + var weight = Math.exp(-_x * _x / 2); + weights.push(weight); + + if (i == 0) { + sum += weight; + } else if (i < samples) { + sum += 2 * weight; + } + } + + for (var _i = 0; _i < weights.length; _i++) { + weights[_i] = weights[_i] / sum; + } + + blurUniforms['envMap'].value = targetIn.texture; + blurUniforms['samples'].value = samples; + blurUniforms['weights'].value = weights; + blurUniforms['latitudinal'].value = direction === 'latitudinal'; + + if (poleAxis) { + blurUniforms['poleAxis'].value = poleAxis; + } + + blurUniforms['dTheta'].value = radiansPerPixel; + blurUniforms['mipInt'].value = LOD_MAX - lodIn; + blurUniforms['inputEncoding'].value = ENCODINGS[targetIn.texture.encoding]; + blurUniforms['outputEncoding'].value = ENCODINGS[targetIn.texture.encoding]; + var outputSize = _sizeLods[lodOut]; + var x = 3 * Math.max(0, SIZE_MAX - 2 * outputSize); + var y = (lodOut === 0 ? 0 : 2 * SIZE_MAX) + 2 * outputSize * (lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0); + + _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); + + renderer.setRenderTarget(targetOut); + renderer.render(blurMesh, _flatCamera); + }; + + return PMREMGenerator; + }(); + + function _isLDR(texture) { + if (texture === undefined || texture.type !== UnsignedByteType) return false; + return texture.encoding === LinearEncoding || texture.encoding === sRGBEncoding || texture.encoding === GammaEncoding; + } + + function _createPlanes() { + var _lodPlanes = []; + var _sizeLods = []; + var _sigmas = []; + var lod = LOD_MAX; + + for (var i = 0; i < TOTAL_LODS; i++) { + var sizeLod = Math.pow(2, lod); + + _sizeLods.push(sizeLod); + + var sigma = 1.0 / sizeLod; + + if (i > LOD_MAX - LOD_MIN) { + sigma = EXTRA_LOD_SIGMA[i - LOD_MAX + LOD_MIN - 1]; + } else if (i == 0) { + sigma = 0; + } + + _sigmas.push(sigma); + + var texelSize = 1.0 / (sizeLod - 1); + var min = -texelSize / 2; + var max = 1 + texelSize / 2; + var uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; + var cubeFaces = 6; + var vertices = 6; + var positionSize = 3; + var uvSize = 2; + var faceIndexSize = 1; + var position = new Float32Array(positionSize * vertices * cubeFaces); + var uv = new Float32Array(uvSize * vertices * cubeFaces); + var faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); + + for (var face = 0; face < cubeFaces; face++) { + var x = face % 3 * 2 / 3 - 1; + var y = face > 2 ? 0 : -1; + var coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; + position.set(coordinates, positionSize * vertices * face); + uv.set(uv1, uvSize * vertices * face); + var fill = [face, face, face, face, face, face]; + faceIndex.set(fill, faceIndexSize * vertices * face); + } + + var planes = new BufferGeometry(); + planes.setAttribute('position', new BufferAttribute(position, positionSize)); + planes.setAttribute('uv', new BufferAttribute(uv, uvSize)); + planes.setAttribute('faceIndex', new BufferAttribute(faceIndex, faceIndexSize)); + + _lodPlanes.push(planes); + + if (lod > LOD_MIN) { + lod--; + } + } + + return { + _lodPlanes: _lodPlanes, + _sizeLods: _sizeLods, + _sigmas: _sigmas + }; + } + + function _createRenderTarget(params) { + var cubeUVRenderTarget = new WebGLRenderTarget(3 * SIZE_MAX, 3 * SIZE_MAX, params); + cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; + cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; + cubeUVRenderTarget.scissorTest = true; + return cubeUVRenderTarget; + } + + function _setViewport(target, x, y, width, height) { + target.viewport.set(x, y, width, height); + target.scissor.set(x, y, width, height); + } + + function _getBlurShader(maxSamples) { + var weights = new Float32Array(maxSamples); + var poleAxis = new Vector3(0, 1, 0); + var shaderMaterial = new RawShaderMaterial({ + name: 'SphericalGaussianBlur', + defines: { + 'n': maxSamples + }, + uniforms: { + 'envMap': { + value: null + }, + 'samples': { + value: 1 + }, + 'weights': { + value: weights + }, + 'latitudinal': { + value: false + }, + 'dTheta': { + value: 0 + }, + 'mipInt': { + value: 0 + }, + 'poleAxis': { + value: poleAxis + }, + 'inputEncoding': { + value: ENCODINGS[LinearEncoding] + }, + 'outputEncoding': { + value: ENCODINGS[LinearEncoding] + } + }, + vertexShader: _getCommonVertexShader(), + fragmentShader: + /* glsl */ + "\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t" + _getEncodings() + "\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t", + blending: NoBlending, + depthTest: false, + depthWrite: false + }); + return shaderMaterial; + } + + function _getEquirectShader() { + var texelSize = new Vector2(1, 1); + var shaderMaterial = new RawShaderMaterial({ + name: 'EquirectangularToCubeUV', + uniforms: { + 'envMap': { + value: null + }, + 'texelSize': { + value: texelSize + }, + 'inputEncoding': { + value: ENCODINGS[LinearEncoding] + }, + 'outputEncoding': { + value: ENCODINGS[LinearEncoding] + } + }, + vertexShader: _getCommonVertexShader(), + fragmentShader: + /* glsl */ + "\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform vec2 texelSize;\n\n\t\t\t" + _getEncodings() + "\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tvec2 f = fract( uv / texelSize - 0.5 );\n\t\t\t\tuv -= f * texelSize;\n\t\t\t\tvec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x += texelSize.x;\n\t\t\t\tvec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.y += texelSize.y;\n\t\t\t\tvec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x -= texelSize.x;\n\t\t\t\tvec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\n\t\t\t\tvec3 tm = mix( tl, tr, f.x );\n\t\t\t\tvec3 bm = mix( bl, br, f.x );\n\t\t\t\tgl_FragColor.rgb = mix( tm, bm, f.y );\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t", + blending: NoBlending, + depthTest: false, + depthWrite: false + }); + return shaderMaterial; + } + + function _getCubemapShader() { + var shaderMaterial = new RawShaderMaterial({ + name: 'CubemapToCubeUV', + uniforms: { + 'envMap': { + value: null + }, + 'inputEncoding': { + value: ENCODINGS[LinearEncoding] + }, + 'outputEncoding': { + value: ENCODINGS[LinearEncoding] + } + }, + vertexShader: _getCommonVertexShader(), + fragmentShader: + /* glsl */ + "\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\t" + _getEncodings() + "\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb;\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t", + blending: NoBlending, + depthTest: false, + depthWrite: false + }); + return shaderMaterial; + } + + function _getCommonVertexShader() { + return ( + /* glsl */ + "\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t" + ); + } + + function _getEncodings() { + return ( + /* glsl */ + "\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t" + ); + } + + var LineStrip = 0; + var LinePieces = 1; + var NoColors = 0; + var FaceColors = 1; + var VertexColors = 2; + function MeshFaceMaterial(materials) { + console.warn('THREE.MeshFaceMaterial has been removed. Use an Array instead.'); + return materials; + } + function MultiMaterial(materials) { + if (materials === void 0) { + materials = []; + } + + console.warn('THREE.MultiMaterial has been removed. Use an Array instead.'); + materials.isMultiMaterial = true; + materials.materials = materials; + + materials.clone = function () { + return materials.slice(); + }; + + return materials; + } + function PointCloud(geometry, material) { + console.warn('THREE.PointCloud has been renamed to THREE.Points.'); + return new Points(geometry, material); + } + function Particle(material) { + console.warn('THREE.Particle has been renamed to THREE.Sprite.'); + return new Sprite(material); + } + function ParticleSystem(geometry, material) { + console.warn('THREE.ParticleSystem has been renamed to THREE.Points.'); + return new Points(geometry, material); + } + function PointCloudMaterial(parameters) { + console.warn('THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.'); + return new PointsMaterial(parameters); + } + function ParticleBasicMaterial(parameters) { + console.warn('THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.'); + return new PointsMaterial(parameters); + } + function ParticleSystemMaterial(parameters) { + console.warn('THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.'); + return new PointsMaterial(parameters); + } + function Vertex(x, y, z) { + console.warn('THREE.Vertex has been removed. Use THREE.Vector3 instead.'); + return new Vector3(x, y, z); + } // + + function DynamicBufferAttribute(array, itemSize) { + console.warn('THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead.'); + return new BufferAttribute(array, itemSize).setUsage(DynamicDrawUsage); + } + function Int8Attribute(array, itemSize) { + console.warn('THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.'); + return new Int8BufferAttribute(array, itemSize); + } + function Uint8Attribute(array, itemSize) { + console.warn('THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.'); + return new Uint8BufferAttribute(array, itemSize); + } + function Uint8ClampedAttribute(array, itemSize) { + console.warn('THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.'); + return new Uint8ClampedBufferAttribute(array, itemSize); + } + function Int16Attribute(array, itemSize) { + console.warn('THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.'); + return new Int16BufferAttribute(array, itemSize); + } + function Uint16Attribute(array, itemSize) { + console.warn('THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.'); + return new Uint16BufferAttribute(array, itemSize); + } + function Int32Attribute(array, itemSize) { + console.warn('THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.'); + return new Int32BufferAttribute(array, itemSize); + } + function Uint32Attribute(array, itemSize) { + console.warn('THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.'); + return new Uint32BufferAttribute(array, itemSize); + } + function Float32Attribute(array, itemSize) { + console.warn('THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.'); + return new Float32BufferAttribute(array, itemSize); + } + function Float64Attribute(array, itemSize) { + console.warn('THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.'); + return new Float64BufferAttribute(array, itemSize); + } // + + Curve.create = function (construct, getPoint) { + console.log('THREE.Curve.create() has been deprecated'); + construct.prototype = Object.create(Curve.prototype); + construct.prototype.constructor = construct; + construct.prototype.getPoint = getPoint; + return construct; + }; // + + + Path.prototype.fromPoints = function (points) { + console.warn('THREE.Path: .fromPoints() has been renamed to .setFromPoints().'); + return this.setFromPoints(points); + }; // + + + function AxisHelper(size) { + console.warn('THREE.AxisHelper has been renamed to THREE.AxesHelper.'); + return new AxesHelper(size); + } + function BoundingBoxHelper(object, color) { + console.warn('THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.'); + return new BoxHelper(object, color); + } + function EdgesHelper(object, hex) { + console.warn('THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.'); + return new LineSegments(new EdgesGeometry(object.geometry), new LineBasicMaterial({ + color: hex !== undefined ? hex : 0xffffff + })); + } + + GridHelper.prototype.setColors = function () { + console.error('THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.'); + }; + + SkeletonHelper.prototype.update = function () { + console.error('THREE.SkeletonHelper: update() no longer needs to be called.'); + }; + + function WireframeHelper(object, hex) { + console.warn('THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.'); + return new LineSegments(new WireframeGeometry(object.geometry), new LineBasicMaterial({ + color: hex !== undefined ? hex : 0xffffff + })); + } // + + Loader.prototype.extractUrlBase = function (url) { + console.warn('THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.'); + return LoaderUtils.extractUrlBase(url); + }; + + Loader.Handlers = { + add: function add() + /* regex, loader */ + { + console.error('THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.'); + }, + get: function get() + /* file */ + { + console.error('THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.'); + } + }; + function XHRLoader(manager) { + console.warn('THREE.XHRLoader has been renamed to THREE.FileLoader.'); + return new FileLoader(manager); + } + function BinaryTextureLoader(manager) { + console.warn('THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.'); + return new DataTextureLoader(manager); + } // + + Box2.prototype.center = function (optionalTarget) { + console.warn('THREE.Box2: .center() has been renamed to .getCenter().'); + return this.getCenter(optionalTarget); + }; + + Box2.prototype.empty = function () { + console.warn('THREE.Box2: .empty() has been renamed to .isEmpty().'); + return this.isEmpty(); + }; + + Box2.prototype.isIntersectionBox = function (box) { + console.warn('THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().'); + return this.intersectsBox(box); + }; + + Box2.prototype.size = function (optionalTarget) { + console.warn('THREE.Box2: .size() has been renamed to .getSize().'); + return this.getSize(optionalTarget); + }; // + + + Box3.prototype.center = function (optionalTarget) { + console.warn('THREE.Box3: .center() has been renamed to .getCenter().'); + return this.getCenter(optionalTarget); + }; + + Box3.prototype.empty = function () { + console.warn('THREE.Box3: .empty() has been renamed to .isEmpty().'); + return this.isEmpty(); + }; + + Box3.prototype.isIntersectionBox = function (box) { + console.warn('THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().'); + return this.intersectsBox(box); + }; + + Box3.prototype.isIntersectionSphere = function (sphere) { + console.warn('THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().'); + return this.intersectsSphere(sphere); + }; + + Box3.prototype.size = function (optionalTarget) { + console.warn('THREE.Box3: .size() has been renamed to .getSize().'); + return this.getSize(optionalTarget); + }; // + + + Sphere.prototype.empty = function () { + console.warn('THREE.Sphere: .empty() has been renamed to .isEmpty().'); + return this.isEmpty(); + }; // + + + Frustum.prototype.setFromMatrix = function (m) { + console.warn('THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().'); + return this.setFromProjectionMatrix(m); + }; // + + + Line3.prototype.center = function (optionalTarget) { + console.warn('THREE.Line3: .center() has been renamed to .getCenter().'); + return this.getCenter(optionalTarget); + }; // + + + MathUtils.random16 = function () { + console.warn('THREE.Math: .random16() has been deprecated. Use Math.random() instead.'); + return Math.random(); + }; + + MathUtils.nearestPowerOfTwo = function (value) { + console.warn('THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().'); + return MathUtils.floorPowerOfTwo(value); + }; + + MathUtils.nextPowerOfTwo = function (value) { + console.warn('THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().'); + return MathUtils.ceilPowerOfTwo(value); + }; // + + + Matrix3.prototype.flattenToArrayOffset = function (array, offset) { + console.warn('THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.'); + return this.toArray(array, offset); + }; + + Matrix3.prototype.multiplyVector3 = function (vector) { + console.warn('THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.'); + return vector.applyMatrix3(this); + }; + + Matrix3.prototype.multiplyVector3Array = function () + /* a */ + { + console.error('THREE.Matrix3: .multiplyVector3Array() has been removed.'); + }; + + Matrix3.prototype.applyToBufferAttribute = function (attribute) { + console.warn('THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.'); + return attribute.applyMatrix3(this); + }; + + Matrix3.prototype.applyToVector3Array = function () + /* array, offset, length */ + { + console.error('THREE.Matrix3: .applyToVector3Array() has been removed.'); + }; + + Matrix3.prototype.getInverse = function (matrix) { + console.warn('THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.'); + return this.copy(matrix).invert(); + }; // + + + Matrix4.prototype.extractPosition = function (m) { + console.warn('THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().'); + return this.copyPosition(m); + }; + + Matrix4.prototype.flattenToArrayOffset = function (array, offset) { + console.warn('THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.'); + return this.toArray(array, offset); + }; + + Matrix4.prototype.getPosition = function () { + console.warn('THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.'); + return new Vector3().setFromMatrixColumn(this, 3); + }; + + Matrix4.prototype.setRotationFromQuaternion = function (q) { + console.warn('THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().'); + return this.makeRotationFromQuaternion(q); + }; + + Matrix4.prototype.multiplyToArray = function () { + console.warn('THREE.Matrix4: .multiplyToArray() has been removed.'); + }; + + Matrix4.prototype.multiplyVector3 = function (vector) { + console.warn('THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.'); + return vector.applyMatrix4(this); + }; + + Matrix4.prototype.multiplyVector4 = function (vector) { + console.warn('THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.'); + return vector.applyMatrix4(this); + }; + + Matrix4.prototype.multiplyVector3Array = function () + /* a */ + { + console.error('THREE.Matrix4: .multiplyVector3Array() has been removed.'); + }; + + Matrix4.prototype.rotateAxis = function (v) { + console.warn('THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.'); + v.transformDirection(this); + }; + + Matrix4.prototype.crossVector = function (vector) { + console.warn('THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.'); + return vector.applyMatrix4(this); + }; + + Matrix4.prototype.translate = function () { + console.error('THREE.Matrix4: .translate() has been removed.'); + }; + + Matrix4.prototype.rotateX = function () { + console.error('THREE.Matrix4: .rotateX() has been removed.'); + }; + + Matrix4.prototype.rotateY = function () { + console.error('THREE.Matrix4: .rotateY() has been removed.'); + }; + + Matrix4.prototype.rotateZ = function () { + console.error('THREE.Matrix4: .rotateZ() has been removed.'); + }; + + Matrix4.prototype.rotateByAxis = function () { + console.error('THREE.Matrix4: .rotateByAxis() has been removed.'); + }; + + Matrix4.prototype.applyToBufferAttribute = function (attribute) { + console.warn('THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.'); + return attribute.applyMatrix4(this); + }; + + Matrix4.prototype.applyToVector3Array = function () + /* array, offset, length */ + { + console.error('THREE.Matrix4: .applyToVector3Array() has been removed.'); + }; + + Matrix4.prototype.makeFrustum = function (left, right, bottom, top, near, far) { + console.warn('THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.'); + return this.makePerspective(left, right, top, bottom, near, far); + }; + + Matrix4.prototype.getInverse = function (matrix) { + console.warn('THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.'); + return this.copy(matrix).invert(); + }; // + + + Plane.prototype.isIntersectionLine = function (line) { + console.warn('THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().'); + return this.intersectsLine(line); + }; // + + + Quaternion.prototype.multiplyVector3 = function (vector) { + console.warn('THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.'); + return vector.applyQuaternion(this); + }; + + Quaternion.prototype.inverse = function () { + console.warn('THREE.Quaternion: .inverse() has been renamed to invert().'); + return this.invert(); + }; // + + + Ray.prototype.isIntersectionBox = function (box) { + console.warn('THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().'); + return this.intersectsBox(box); + }; + + Ray.prototype.isIntersectionPlane = function (plane) { + console.warn('THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().'); + return this.intersectsPlane(plane); + }; + + Ray.prototype.isIntersectionSphere = function (sphere) { + console.warn('THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().'); + return this.intersectsSphere(sphere); + }; // + + + Triangle.prototype.area = function () { + console.warn('THREE.Triangle: .area() has been renamed to .getArea().'); + return this.getArea(); + }; + + Triangle.prototype.barycoordFromPoint = function (point, target) { + console.warn('THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().'); + return this.getBarycoord(point, target); + }; + + Triangle.prototype.midpoint = function (target) { + console.warn('THREE.Triangle: .midpoint() has been renamed to .getMidpoint().'); + return this.getMidpoint(target); + }; + + Triangle.prototypenormal = function (target) { + console.warn('THREE.Triangle: .normal() has been renamed to .getNormal().'); + return this.getNormal(target); + }; + + Triangle.prototype.plane = function (target) { + console.warn('THREE.Triangle: .plane() has been renamed to .getPlane().'); + return this.getPlane(target); + }; + + Triangle.barycoordFromPoint = function (point, a, b, c, target) { + console.warn('THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().'); + return Triangle.getBarycoord(point, a, b, c, target); + }; + + Triangle.normal = function (a, b, c, target) { + console.warn('THREE.Triangle: .normal() has been renamed to .getNormal().'); + return Triangle.getNormal(a, b, c, target); + }; // + + + Shape.prototype.extractAllPoints = function (divisions) { + console.warn('THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.'); + return this.extractPoints(divisions); + }; + + Shape.prototype.extrude = function (options) { + console.warn('THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.'); + return new ExtrudeGeometry(this, options); + }; + + Shape.prototype.makeGeometry = function (options) { + console.warn('THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.'); + return new ShapeGeometry(this, options); + }; // + + + Vector2.prototype.fromAttribute = function (attribute, index, offset) { + console.warn('THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().'); + return this.fromBufferAttribute(attribute, index, offset); + }; + + Vector2.prototype.distanceToManhattan = function (v) { + console.warn('THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().'); + return this.manhattanDistanceTo(v); + }; + + Vector2.prototype.lengthManhattan = function () { + console.warn('THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().'); + return this.manhattanLength(); + }; // + + + Vector3.prototype.setEulerFromRotationMatrix = function () { + console.error('THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.'); + }; + + Vector3.prototype.setEulerFromQuaternion = function () { + console.error('THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.'); + }; + + Vector3.prototype.getPositionFromMatrix = function (m) { + console.warn('THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().'); + return this.setFromMatrixPosition(m); + }; + + Vector3.prototype.getScaleFromMatrix = function (m) { + console.warn('THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().'); + return this.setFromMatrixScale(m); + }; + + Vector3.prototype.getColumnFromMatrix = function (index, matrix) { + console.warn('THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().'); + return this.setFromMatrixColumn(matrix, index); + }; + + Vector3.prototype.applyProjection = function (m) { + console.warn('THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.'); + return this.applyMatrix4(m); + }; + + Vector3.prototype.fromAttribute = function (attribute, index, offset) { + console.warn('THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().'); + return this.fromBufferAttribute(attribute, index, offset); + }; + + Vector3.prototype.distanceToManhattan = function (v) { + console.warn('THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().'); + return this.manhattanDistanceTo(v); + }; + + Vector3.prototype.lengthManhattan = function () { + console.warn('THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().'); + return this.manhattanLength(); + }; // + + + Vector4.prototype.fromAttribute = function (attribute, index, offset) { + console.warn('THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().'); + return this.fromBufferAttribute(attribute, index, offset); + }; + + Vector4.prototype.lengthManhattan = function () { + console.warn('THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().'); + return this.manhattanLength(); + }; // + + + Object3D.prototype.getChildByName = function (name) { + console.warn('THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().'); + return this.getObjectByName(name); + }; + + Object3D.prototype.renderDepth = function () { + console.warn('THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.'); + }; + + Object3D.prototype.translate = function (distance, axis) { + console.warn('THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.'); + return this.translateOnAxis(axis, distance); + }; + + Object3D.prototype.getWorldRotation = function () { + console.error('THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.'); + }; + + Object3D.prototype.applyMatrix = function (matrix) { + console.warn('THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().'); + return this.applyMatrix4(matrix); + }; + + Object.defineProperties(Object3D.prototype, { + eulerOrder: { + get: function get() { + console.warn('THREE.Object3D: .eulerOrder is now .rotation.order.'); + return this.rotation.order; + }, + set: function set(value) { + console.warn('THREE.Object3D: .eulerOrder is now .rotation.order.'); + this.rotation.order = value; + } + }, + useQuaternion: { + get: function get() { + console.warn('THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.'); + }, + set: function set() { + console.warn('THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.'); + } + } + }); + + Mesh.prototype.setDrawMode = function () { + console.error('THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.'); + }; + + Object.defineProperties(Mesh.prototype, { + drawMode: { + get: function get() { + console.error('THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.'); + return TrianglesDrawMode; + }, + set: function set() { + console.error('THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.'); + } + } + }); + Object.defineProperties(LOD.prototype, { + objects: { + get: function get() { + console.warn('THREE.LOD: .objects has been renamed to .levels.'); + return this.levels; + } + } + }); + Object.defineProperty(Skeleton.prototype, 'useVertexTexture', { + get: function get() { + console.warn('THREE.Skeleton: useVertexTexture has been removed.'); + }, + set: function set() { + console.warn('THREE.Skeleton: useVertexTexture has been removed.'); + } + }); + + SkinnedMesh.prototype.initBones = function () { + console.error('THREE.SkinnedMesh: initBones() has been removed.'); + }; + + Object.defineProperty(Curve.prototype, '__arcLengthDivisions', { + get: function get() { + console.warn('THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.'); + return this.arcLengthDivisions; + }, + set: function set(value) { + console.warn('THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.'); + this.arcLengthDivisions = value; + } + }); // + + PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) { + console.warn('THREE.PerspectiveCamera.setLens is deprecated. ' + 'Use .setFocalLength and .filmGauge for a photographic setup.'); + if (filmGauge !== undefined) this.filmGauge = filmGauge; + this.setFocalLength(focalLength); + }; // + + + Object.defineProperties(Light.prototype, { + onlyShadow: { + set: function set() { + console.warn('THREE.Light: .onlyShadow has been removed.'); + } + }, + shadowCameraFov: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraFov is now .shadow.camera.fov.'); + this.shadow.camera.fov = value; + } + }, + shadowCameraLeft: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraLeft is now .shadow.camera.left.'); + this.shadow.camera.left = value; + } + }, + shadowCameraRight: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraRight is now .shadow.camera.right.'); + this.shadow.camera.right = value; + } + }, + shadowCameraTop: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraTop is now .shadow.camera.top.'); + this.shadow.camera.top = value; + } + }, + shadowCameraBottom: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.'); + this.shadow.camera.bottom = value; + } + }, + shadowCameraNear: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraNear is now .shadow.camera.near.'); + this.shadow.camera.near = value; + } + }, + shadowCameraFar: { + set: function set(value) { + console.warn('THREE.Light: .shadowCameraFar is now .shadow.camera.far.'); + this.shadow.camera.far = value; + } + }, + shadowCameraVisible: { + set: function set() { + console.warn('THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.'); + } + }, + shadowBias: { + set: function set(value) { + console.warn('THREE.Light: .shadowBias is now .shadow.bias.'); + this.shadow.bias = value; + } + }, + shadowDarkness: { + set: function set() { + console.warn('THREE.Light: .shadowDarkness has been removed.'); + } + }, + shadowMapWidth: { + set: function set(value) { + console.warn('THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.'); + this.shadow.mapSize.width = value; + } + }, + shadowMapHeight: { + set: function set(value) { + console.warn('THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.'); + this.shadow.mapSize.height = value; + } + } + }); // + + Object.defineProperties(BufferAttribute.prototype, { + length: { + get: function get() { + console.warn('THREE.BufferAttribute: .length has been deprecated. Use .count instead.'); + return this.array.length; + } + }, + dynamic: { + get: function get() { + console.warn('THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.'); + return this.usage === DynamicDrawUsage; + }, + set: function set() + /* value */ + { + console.warn('THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.'); + this.setUsage(DynamicDrawUsage); + } + } + }); + + BufferAttribute.prototype.setDynamic = function (value) { + console.warn('THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.'); + this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); + return this; + }; + + BufferAttribute.prototype.copyIndicesArray = function () + /* indices */ + { + console.error('THREE.BufferAttribute: .copyIndicesArray() has been removed.'); + }, BufferAttribute.prototype.setArray = function () + /* array */ + { + console.error('THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers'); + }; // + + BufferGeometry.prototype.addIndex = function (index) { + console.warn('THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().'); + this.setIndex(index); + }; + + BufferGeometry.prototype.addAttribute = function (name, attribute) { + console.warn('THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().'); + + if (!(attribute && attribute.isBufferAttribute) && !(attribute && attribute.isInterleavedBufferAttribute)) { + console.warn('THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).'); + return this.setAttribute(name, new BufferAttribute(arguments[1], arguments[2])); + } + + if (name === 'index') { + console.warn('THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.'); + this.setIndex(attribute); + return this; + } + + return this.setAttribute(name, attribute); + }; + + BufferGeometry.prototype.addDrawCall = function (start, count, indexOffset) { + if (indexOffset !== undefined) { + console.warn('THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.'); + } + + console.warn('THREE.BufferGeometry: .addDrawCall() is now .addGroup().'); + this.addGroup(start, count); + }; + + BufferGeometry.prototype.clearDrawCalls = function () { + console.warn('THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().'); + this.clearGroups(); + }; + + BufferGeometry.prototype.computeOffsets = function () { + console.warn('THREE.BufferGeometry: .computeOffsets() has been removed.'); + }; + + BufferGeometry.prototype.removeAttribute = function (name) { + console.warn('THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().'); + return this.deleteAttribute(name); + }; + + BufferGeometry.prototype.applyMatrix = function (matrix) { + console.warn('THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().'); + return this.applyMatrix4(matrix); + }; + + Object.defineProperties(BufferGeometry.prototype, { + drawcalls: { + get: function get() { + console.error('THREE.BufferGeometry: .drawcalls has been renamed to .groups.'); + return this.groups; + } + }, + offsets: { + get: function get() { + console.warn('THREE.BufferGeometry: .offsets has been renamed to .groups.'); + return this.groups; + } + } + }); + Object.defineProperties(InstancedBufferGeometry.prototype, { + maxInstancedCount: { + get: function get() { + console.warn('THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.'); + return this.instanceCount; + }, + set: function set(value) { + console.warn('THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.'); + this.instanceCount = value; + } + } + }); + Object.defineProperties(Raycaster.prototype, { + linePrecision: { + get: function get() { + console.warn('THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.'); + return this.params.Line.threshold; + }, + set: function set(value) { + console.warn('THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.'); + this.params.Line.threshold = value; + } + } + }); + Object.defineProperties(InterleavedBuffer.prototype, { + dynamic: { + get: function get() { + console.warn('THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.'); + return this.usage === DynamicDrawUsage; + }, + set: function set(value) { + console.warn('THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.'); + this.setUsage(value); + } + } + }); + + InterleavedBuffer.prototype.setDynamic = function (value) { + console.warn('THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.'); + this.setUsage(value === true ? DynamicDrawUsage : StaticDrawUsage); + return this; + }; + + InterleavedBuffer.prototype.setArray = function () + /* array */ + { + console.error('THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers'); + }; // + + + ExtrudeGeometry.prototype.getArrays = function () { + console.error('THREE.ExtrudeGeometry: .getArrays() has been removed.'); + }; + + ExtrudeGeometry.prototype.addShapeList = function () { + console.error('THREE.ExtrudeGeometry: .addShapeList() has been removed.'); + }; + + ExtrudeGeometry.prototype.addShape = function () { + console.error('THREE.ExtrudeGeometry: .addShape() has been removed.'); + }; // + + + Scene.prototype.dispose = function () { + console.error('THREE.Scene: .dispose() has been removed.'); + }; // + + + Object.defineProperties(Uniform.prototype, { + dynamic: { + set: function set() { + console.warn('THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.'); + } + }, + onUpdate: { + value: function value() { + console.warn('THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.'); + return this; + } + } + }); // + + Object.defineProperties(Material.prototype, { + wrapAround: { + get: function get() { + console.warn('THREE.Material: .wrapAround has been removed.'); + }, + set: function set() { + console.warn('THREE.Material: .wrapAround has been removed.'); + } + }, + overdraw: { + get: function get() { + console.warn('THREE.Material: .overdraw has been removed.'); + }, + set: function set() { + console.warn('THREE.Material: .overdraw has been removed.'); + } + }, + wrapRGB: { + get: function get() { + console.warn('THREE.Material: .wrapRGB has been removed.'); + return new Color(); + } + }, + shading: { + get: function get() { + console.error('THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.'); + }, + set: function set(value) { + console.warn('THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.'); + this.flatShading = value === FlatShading; + } + }, + stencilMask: { + get: function get() { + console.warn('THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.'); + return this.stencilFuncMask; + }, + set: function set(value) { + console.warn('THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.'); + this.stencilFuncMask = value; + } + } + }); + Object.defineProperties(MeshPhongMaterial.prototype, { + metal: { + get: function get() { + console.warn('THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.'); + return false; + }, + set: function set() { + console.warn('THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead'); + } + } + }); + Object.defineProperties(MeshPhysicalMaterial.prototype, { + transparency: { + get: function get() { + console.warn('THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.'); + return this.transmission; + }, + set: function set(value) { + console.warn('THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.'); + this.transmission = value; + } + } + }); + Object.defineProperties(ShaderMaterial.prototype, { + derivatives: { + get: function get() { + console.warn('THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.'); + return this.extensions.derivatives; + }, + set: function set(value) { + console.warn('THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.'); + this.extensions.derivatives = value; + } + } + }); // + + WebGLRenderer.prototype.clearTarget = function (renderTarget, color, depth, stencil) { + console.warn('THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.'); + this.setRenderTarget(renderTarget); + this.clear(color, depth, stencil); + }; + + WebGLRenderer.prototype.animate = function (callback) { + console.warn('THREE.WebGLRenderer: .animate() is now .setAnimationLoop().'); + this.setAnimationLoop(callback); + }; + + WebGLRenderer.prototype.getCurrentRenderTarget = function () { + console.warn('THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().'); + return this.getRenderTarget(); + }; + + WebGLRenderer.prototype.getMaxAnisotropy = function () { + console.warn('THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().'); + return this.capabilities.getMaxAnisotropy(); + }; + + WebGLRenderer.prototype.getPrecision = function () { + console.warn('THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.'); + return this.capabilities.precision; + }; + + WebGLRenderer.prototype.resetGLState = function () { + console.warn('THREE.WebGLRenderer: .resetGLState() is now .state.reset().'); + return this.state.reset(); + }; + + WebGLRenderer.prototype.supportsFloatTextures = function () { + console.warn('THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).'); + return this.extensions.get('OES_texture_float'); + }; + + WebGLRenderer.prototype.supportsHalfFloatTextures = function () { + console.warn('THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).'); + return this.extensions.get('OES_texture_half_float'); + }; + + WebGLRenderer.prototype.supportsStandardDerivatives = function () { + console.warn('THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).'); + return this.extensions.get('OES_standard_derivatives'); + }; + + WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { + console.warn('THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).'); + return this.extensions.get('WEBGL_compressed_texture_s3tc'); + }; + + WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { + console.warn('THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).'); + return this.extensions.get('WEBGL_compressed_texture_pvrtc'); + }; + + WebGLRenderer.prototype.supportsBlendMinMax = function () { + console.warn('THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).'); + return this.extensions.get('EXT_blend_minmax'); + }; + + WebGLRenderer.prototype.supportsVertexTextures = function () { + console.warn('THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.'); + return this.capabilities.vertexTextures; + }; + + WebGLRenderer.prototype.supportsInstancedArrays = function () { + console.warn('THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).'); + return this.extensions.get('ANGLE_instanced_arrays'); + }; + + WebGLRenderer.prototype.enableScissorTest = function (boolean) { + console.warn('THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().'); + this.setScissorTest(boolean); + }; + + WebGLRenderer.prototype.initMaterial = function () { + console.warn('THREE.WebGLRenderer: .initMaterial() has been removed.'); + }; + + WebGLRenderer.prototype.addPrePlugin = function () { + console.warn('THREE.WebGLRenderer: .addPrePlugin() has been removed.'); + }; + + WebGLRenderer.prototype.addPostPlugin = function () { + console.warn('THREE.WebGLRenderer: .addPostPlugin() has been removed.'); + }; + + WebGLRenderer.prototype.updateShadowMap = function () { + console.warn('THREE.WebGLRenderer: .updateShadowMap() has been removed.'); + }; + + WebGLRenderer.prototype.setFaceCulling = function () { + console.warn('THREE.WebGLRenderer: .setFaceCulling() has been removed.'); + }; + + WebGLRenderer.prototype.allocTextureUnit = function () { + console.warn('THREE.WebGLRenderer: .allocTextureUnit() has been removed.'); + }; + + WebGLRenderer.prototype.setTexture = function () { + console.warn('THREE.WebGLRenderer: .setTexture() has been removed.'); + }; + + WebGLRenderer.prototype.setTexture2D = function () { + console.warn('THREE.WebGLRenderer: .setTexture2D() has been removed.'); + }; + + WebGLRenderer.prototype.setTextureCube = function () { + console.warn('THREE.WebGLRenderer: .setTextureCube() has been removed.'); + }; + + WebGLRenderer.prototype.getActiveMipMapLevel = function () { + console.warn('THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().'); + return this.getActiveMipmapLevel(); + }; + + Object.defineProperties(WebGLRenderer.prototype, { + shadowMapEnabled: { + get: function get() { + return this.shadowMap.enabled; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.'); + this.shadowMap.enabled = value; + } + }, + shadowMapType: { + get: function get() { + return this.shadowMap.type; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.'); + this.shadowMap.type = value; + } + }, + shadowMapCullFace: { + get: function get() { + console.warn('THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.'); + return undefined; + }, + set: function set() + /* value */ + { + console.warn('THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.'); + } + }, + context: { + get: function get() { + console.warn('THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.'); + return this.getContext(); + } + }, + vr: { + get: function get() { + console.warn('THREE.WebGLRenderer: .vr has been renamed to .xr'); + return this.xr; + } + }, + gammaInput: { + get: function get() { + console.warn('THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.'); + return false; + }, + set: function set() { + console.warn('THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.'); + } + }, + gammaOutput: { + get: function get() { + console.warn('THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.'); + return false; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.'); + this.outputEncoding = value === true ? sRGBEncoding : LinearEncoding; + } + }, + toneMappingWhitePoint: { + get: function get() { + console.warn('THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.'); + return 1.0; + }, + set: function set() { + console.warn('THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.'); + } + } + }); + Object.defineProperties(WebGLShadowMap.prototype, { + cullFace: { + get: function get() { + console.warn('THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.'); + return undefined; + }, + set: function set() + /* cullFace */ + { + console.warn('THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.'); + } + }, + renderReverseSided: { + get: function get() { + console.warn('THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.'); + return undefined; + }, + set: function set() { + console.warn('THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.'); + } + }, + renderSingleSided: { + get: function get() { + console.warn('THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.'); + return undefined; + }, + set: function set() { + console.warn('THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.'); + } + } + }); + function WebGLRenderTargetCube(width, height, options) { + console.warn('THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options ).'); + return new WebGLCubeRenderTarget(width, options); + } // + + Object.defineProperties(WebGLRenderTarget.prototype, { + wrapS: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.'); + return this.texture.wrapS; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.'); + this.texture.wrapS = value; + } + }, + wrapT: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.'); + return this.texture.wrapT; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.'); + this.texture.wrapT = value; + } + }, + magFilter: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.'); + return this.texture.magFilter; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.'); + this.texture.magFilter = value; + } + }, + minFilter: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.'); + return this.texture.minFilter; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.'); + this.texture.minFilter = value; + } + }, + anisotropy: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.'); + return this.texture.anisotropy; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.'); + this.texture.anisotropy = value; + } + }, + offset: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .offset is now .texture.offset.'); + return this.texture.offset; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .offset is now .texture.offset.'); + this.texture.offset = value; + } + }, + repeat: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .repeat is now .texture.repeat.'); + return this.texture.repeat; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .repeat is now .texture.repeat.'); + this.texture.repeat = value; + } + }, + format: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .format is now .texture.format.'); + return this.texture.format; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .format is now .texture.format.'); + this.texture.format = value; + } + }, + type: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .type is now .texture.type.'); + return this.texture.type; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .type is now .texture.type.'); + this.texture.type = value; + } + }, + generateMipmaps: { + get: function get() { + console.warn('THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.'); + return this.texture.generateMipmaps; + }, + set: function set(value) { + console.warn('THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.'); + this.texture.generateMipmaps = value; + } + } + }); // + + Object.defineProperties(Audio.prototype, { + load: { + value: function value(file) { + console.warn('THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.'); + var scope = this; + var audioLoader = new AudioLoader(); + audioLoader.load(file, function (buffer) { + scope.setBuffer(buffer); + }); + return this; + } + }, + startTime: { + set: function set() { + console.warn('THREE.Audio: .startTime is now .play( delay ).'); + } + } + }); + + AudioAnalyser.prototype.getData = function () { + console.warn('THREE.AudioAnalyser: .getData() is now .getFrequencyData().'); + return this.getFrequencyData(); + }; // + + + CubeCamera.prototype.updateCubeMap = function (renderer, scene) { + console.warn('THREE.CubeCamera: .updateCubeMap() is now .update().'); + return this.update(renderer, scene); + }; + + CubeCamera.prototype.clear = function (renderer, color, depth, stencil) { + console.warn('THREE.CubeCamera: .clear() is now .renderTarget.clear().'); + return this.renderTarget.clear(renderer, color, depth, stencil); + }; + + ImageUtils.crossOrigin = undefined; + + ImageUtils.loadTexture = function (url, mapping, onLoad, onError) { + console.warn('THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.'); + var loader = new TextureLoader(); + loader.setCrossOrigin(this.crossOrigin); + var texture = loader.load(url, onLoad, undefined, onError); + if (mapping) texture.mapping = mapping; + return texture; + }; + + ImageUtils.loadTextureCube = function (urls, mapping, onLoad, onError) { + console.warn('THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.'); + var loader = new CubeTextureLoader(); + loader.setCrossOrigin(this.crossOrigin); + var texture = loader.load(urls, onLoad, undefined, onError); + if (mapping) texture.mapping = mapping; + return texture; + }; + + ImageUtils.loadCompressedTexture = function () { + console.error('THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.'); + }; + + ImageUtils.loadCompressedTextureCube = function () { + console.error('THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.'); + }; // + + + function CanvasRenderer() { + console.error('THREE.CanvasRenderer has been removed'); + } // + + function JSONLoader() { + console.error('THREE.JSONLoader has been removed.'); + } // + + var SceneUtils = { + createMultiMaterialObject: function createMultiMaterialObject() + /* geometry, materials */ + { + console.error('THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js'); + }, + detach: function detach() + /* child, parent, scene */ + { + console.error('THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js'); + }, + attach: function attach() + /* child, scene, parent */ + { + console.error('THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js'); + } + }; // + + function LensFlare() { + console.error('THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js'); + } + + if (typeof __THREE_DEVTOOLS__ !== 'undefined') { + /* eslint-disable no-undef */ + __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('register', { + detail: { + revision: REVISION + } + })); + /* eslint-enable no-undef */ + + } + + if (typeof window !== 'undefined') { + if (window.__THREE__) { + console.warn('WARNING: Multiple instances of Three.js being imported.'); + } else { + window.__THREE__ = REVISION; + } + } + + exports.ACESFilmicToneMapping = ACESFilmicToneMapping; + exports.AddEquation = AddEquation; + exports.AddOperation = AddOperation; + exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; + exports.AdditiveBlending = AdditiveBlending; + exports.AlphaFormat = AlphaFormat; + exports.AlwaysDepth = AlwaysDepth; + exports.AlwaysStencilFunc = AlwaysStencilFunc; + exports.AmbientLight = AmbientLight; + exports.AmbientLightProbe = AmbientLightProbe; + exports.AnimationClip = AnimationClip; + exports.AnimationLoader = AnimationLoader; + exports.AnimationMixer = AnimationMixer; + exports.AnimationObjectGroup = AnimationObjectGroup; + exports.AnimationUtils = AnimationUtils; + exports.ArcCurve = ArcCurve; + exports.ArrayCamera = ArrayCamera; + exports.ArrowHelper = ArrowHelper; + exports.Audio = Audio; + exports.AudioAnalyser = AudioAnalyser; + exports.AudioContext = AudioContext; + exports.AudioListener = AudioListener; + exports.AudioLoader = AudioLoader; + exports.AxesHelper = AxesHelper; + exports.AxisHelper = AxisHelper; + exports.BackSide = BackSide; + exports.BasicDepthPacking = BasicDepthPacking; + exports.BasicShadowMap = BasicShadowMap; + exports.BinaryTextureLoader = BinaryTextureLoader; + exports.Bone = Bone; + exports.BooleanKeyframeTrack = BooleanKeyframeTrack; + exports.BoundingBoxHelper = BoundingBoxHelper; + exports.Box2 = Box2; + exports.Box3 = Box3; + exports.Box3Helper = Box3Helper; + exports.BoxBufferGeometry = BoxGeometry; + exports.BoxGeometry = BoxGeometry; + exports.BoxHelper = BoxHelper; + exports.BufferAttribute = BufferAttribute; + exports.BufferGeometry = BufferGeometry; + exports.BufferGeometryLoader = BufferGeometryLoader; + exports.ByteType = ByteType; + exports.Cache = Cache; + exports.Camera = Camera; + exports.CameraHelper = CameraHelper; + exports.CanvasRenderer = CanvasRenderer; + exports.CanvasTexture = CanvasTexture; + exports.CatmullRomCurve3 = CatmullRomCurve3; + exports.CineonToneMapping = CineonToneMapping; + exports.CircleBufferGeometry = CircleGeometry; + exports.CircleGeometry = CircleGeometry; + exports.ClampToEdgeWrapping = ClampToEdgeWrapping; + exports.Clock = Clock; + exports.Color = Color; + exports.ColorKeyframeTrack = ColorKeyframeTrack; + exports.CompressedTexture = CompressedTexture; + exports.CompressedTextureLoader = CompressedTextureLoader; + exports.ConeBufferGeometry = ConeGeometry; + exports.ConeGeometry = ConeGeometry; + exports.CubeCamera = CubeCamera; + exports.CubeReflectionMapping = CubeReflectionMapping; + exports.CubeRefractionMapping = CubeRefractionMapping; + exports.CubeTexture = CubeTexture; + exports.CubeTextureLoader = CubeTextureLoader; + exports.CubeUVReflectionMapping = CubeUVReflectionMapping; + exports.CubeUVRefractionMapping = CubeUVRefractionMapping; + exports.CubicBezierCurve = CubicBezierCurve; + exports.CubicBezierCurve3 = CubicBezierCurve3; + exports.CubicInterpolant = CubicInterpolant; + exports.CullFaceBack = CullFaceBack; + exports.CullFaceFront = CullFaceFront; + exports.CullFaceFrontBack = CullFaceFrontBack; + exports.CullFaceNone = CullFaceNone; + exports.Curve = Curve; + exports.CurvePath = CurvePath; + exports.CustomBlending = CustomBlending; + exports.CustomToneMapping = CustomToneMapping; + exports.CylinderBufferGeometry = CylinderGeometry; + exports.CylinderGeometry = CylinderGeometry; + exports.Cylindrical = Cylindrical; + exports.DataTexture = DataTexture; + exports.DataTexture2DArray = DataTexture2DArray; + exports.DataTexture3D = DataTexture3D; + exports.DataTextureLoader = DataTextureLoader; + exports.DataUtils = DataUtils; + exports.DecrementStencilOp = DecrementStencilOp; + exports.DecrementWrapStencilOp = DecrementWrapStencilOp; + exports.DefaultLoadingManager = DefaultLoadingManager; + exports.DepthFormat = DepthFormat; + exports.DepthStencilFormat = DepthStencilFormat; + exports.DepthTexture = DepthTexture; + exports.DirectionalLight = DirectionalLight; + exports.DirectionalLightHelper = DirectionalLightHelper; + exports.DiscreteInterpolant = DiscreteInterpolant; + exports.DodecahedronBufferGeometry = DodecahedronGeometry; + exports.DodecahedronGeometry = DodecahedronGeometry; + exports.DoubleSide = DoubleSide; + exports.DstAlphaFactor = DstAlphaFactor; + exports.DstColorFactor = DstColorFactor; + exports.DynamicBufferAttribute = DynamicBufferAttribute; + exports.DynamicCopyUsage = DynamicCopyUsage; + exports.DynamicDrawUsage = DynamicDrawUsage; + exports.DynamicReadUsage = DynamicReadUsage; + exports.EdgesGeometry = EdgesGeometry; + exports.EdgesHelper = EdgesHelper; + exports.EllipseCurve = EllipseCurve; + exports.EqualDepth = EqualDepth; + exports.EqualStencilFunc = EqualStencilFunc; + exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; + exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; + exports.Euler = Euler; + exports.EventDispatcher = EventDispatcher; + exports.ExtrudeBufferGeometry = ExtrudeGeometry; + exports.ExtrudeGeometry = ExtrudeGeometry; + exports.FaceColors = FaceColors; + exports.FileLoader = FileLoader; + exports.FlatShading = FlatShading; + exports.Float16BufferAttribute = Float16BufferAttribute; + exports.Float32Attribute = Float32Attribute; + exports.Float32BufferAttribute = Float32BufferAttribute; + exports.Float64Attribute = Float64Attribute; + exports.Float64BufferAttribute = Float64BufferAttribute; + exports.FloatType = FloatType; + exports.Fog = Fog; + exports.FogExp2 = FogExp2; + exports.Font = Font; + exports.FontLoader = FontLoader; + exports.FrontSide = FrontSide; + exports.Frustum = Frustum; + exports.GLBufferAttribute = GLBufferAttribute; + exports.GLSL1 = GLSL1; + exports.GLSL3 = GLSL3; + exports.GammaEncoding = GammaEncoding; + exports.GreaterDepth = GreaterDepth; + exports.GreaterEqualDepth = GreaterEqualDepth; + exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; + exports.GreaterStencilFunc = GreaterStencilFunc; + exports.GridHelper = GridHelper; + exports.Group = Group; + exports.HalfFloatType = HalfFloatType; + exports.HemisphereLight = HemisphereLight; + exports.HemisphereLightHelper = HemisphereLightHelper; + exports.HemisphereLightProbe = HemisphereLightProbe; + exports.IcosahedronBufferGeometry = IcosahedronGeometry; + exports.IcosahedronGeometry = IcosahedronGeometry; + exports.ImageBitmapLoader = ImageBitmapLoader; + exports.ImageLoader = ImageLoader; + exports.ImageUtils = ImageUtils; + exports.ImmediateRenderObject = ImmediateRenderObject; + exports.IncrementStencilOp = IncrementStencilOp; + exports.IncrementWrapStencilOp = IncrementWrapStencilOp; + exports.InstancedBufferAttribute = InstancedBufferAttribute; + exports.InstancedBufferGeometry = InstancedBufferGeometry; + exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; + exports.InstancedMesh = InstancedMesh; + exports.Int16Attribute = Int16Attribute; + exports.Int16BufferAttribute = Int16BufferAttribute; + exports.Int32Attribute = Int32Attribute; + exports.Int32BufferAttribute = Int32BufferAttribute; + exports.Int8Attribute = Int8Attribute; + exports.Int8BufferAttribute = Int8BufferAttribute; + exports.IntType = IntType; + exports.InterleavedBuffer = InterleavedBuffer; + exports.InterleavedBufferAttribute = InterleavedBufferAttribute; + exports.Interpolant = Interpolant; + exports.InterpolateDiscrete = InterpolateDiscrete; + exports.InterpolateLinear = InterpolateLinear; + exports.InterpolateSmooth = InterpolateSmooth; + exports.InvertStencilOp = InvertStencilOp; + exports.JSONLoader = JSONLoader; + exports.KeepStencilOp = KeepStencilOp; + exports.KeyframeTrack = KeyframeTrack; + exports.LOD = LOD; + exports.LatheBufferGeometry = LatheGeometry; + exports.LatheGeometry = LatheGeometry; + exports.Layers = Layers; + exports.LensFlare = LensFlare; + exports.LessDepth = LessDepth; + exports.LessEqualDepth = LessEqualDepth; + exports.LessEqualStencilFunc = LessEqualStencilFunc; + exports.LessStencilFunc = LessStencilFunc; + exports.Light = Light; + exports.LightProbe = LightProbe; + exports.Line = Line; + exports.Line3 = Line3; + exports.LineBasicMaterial = LineBasicMaterial; + exports.LineCurve = LineCurve; + exports.LineCurve3 = LineCurve3; + exports.LineDashedMaterial = LineDashedMaterial; + exports.LineLoop = LineLoop; + exports.LinePieces = LinePieces; + exports.LineSegments = LineSegments; + exports.LineStrip = LineStrip; + exports.LinearEncoding = LinearEncoding; + exports.LinearFilter = LinearFilter; + exports.LinearInterpolant = LinearInterpolant; + exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; + exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; + exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; + exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; + exports.LinearToneMapping = LinearToneMapping; + exports.Loader = Loader; + exports.LoaderUtils = LoaderUtils; + exports.LoadingManager = LoadingManager; + exports.LogLuvEncoding = LogLuvEncoding; + exports.LoopOnce = LoopOnce; + exports.LoopPingPong = LoopPingPong; + exports.LoopRepeat = LoopRepeat; + exports.LuminanceAlphaFormat = LuminanceAlphaFormat; + exports.LuminanceFormat = LuminanceFormat; + exports.MOUSE = MOUSE; + exports.Material = Material; + exports.MaterialLoader = MaterialLoader; + exports.Math = MathUtils; + exports.MathUtils = MathUtils; + exports.Matrix3 = Matrix3; + exports.Matrix4 = Matrix4; + exports.MaxEquation = MaxEquation; + exports.Mesh = Mesh; + exports.MeshBasicMaterial = MeshBasicMaterial; + exports.MeshDepthMaterial = MeshDepthMaterial; + exports.MeshDistanceMaterial = MeshDistanceMaterial; + exports.MeshFaceMaterial = MeshFaceMaterial; + exports.MeshLambertMaterial = MeshLambertMaterial; + exports.MeshMatcapMaterial = MeshMatcapMaterial; + exports.MeshNormalMaterial = MeshNormalMaterial; + exports.MeshPhongMaterial = MeshPhongMaterial; + exports.MeshPhysicalMaterial = MeshPhysicalMaterial; + exports.MeshStandardMaterial = MeshStandardMaterial; + exports.MeshToonMaterial = MeshToonMaterial; + exports.MinEquation = MinEquation; + exports.MirroredRepeatWrapping = MirroredRepeatWrapping; + exports.MixOperation = MixOperation; + exports.MultiMaterial = MultiMaterial; + exports.MultiplyBlending = MultiplyBlending; + exports.MultiplyOperation = MultiplyOperation; + exports.NearestFilter = NearestFilter; + exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; + exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; + exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; + exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; + exports.NeverDepth = NeverDepth; + exports.NeverStencilFunc = NeverStencilFunc; + exports.NoBlending = NoBlending; + exports.NoColors = NoColors; + exports.NoToneMapping = NoToneMapping; + exports.NormalAnimationBlendMode = NormalAnimationBlendMode; + exports.NormalBlending = NormalBlending; + exports.NotEqualDepth = NotEqualDepth; + exports.NotEqualStencilFunc = NotEqualStencilFunc; + exports.NumberKeyframeTrack = NumberKeyframeTrack; + exports.Object3D = Object3D; + exports.ObjectLoader = ObjectLoader; + exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; + exports.OctahedronBufferGeometry = OctahedronGeometry; + exports.OctahedronGeometry = OctahedronGeometry; + exports.OneFactor = OneFactor; + exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; + exports.OneMinusDstColorFactor = OneMinusDstColorFactor; + exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; + exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; + exports.OrthographicCamera = OrthographicCamera; + exports.PCFShadowMap = PCFShadowMap; + exports.PCFSoftShadowMap = PCFSoftShadowMap; + exports.PMREMGenerator = PMREMGenerator; + exports.ParametricBufferGeometry = ParametricGeometry; + exports.ParametricGeometry = ParametricGeometry; + exports.Particle = Particle; + exports.ParticleBasicMaterial = ParticleBasicMaterial; + exports.ParticleSystem = ParticleSystem; + exports.ParticleSystemMaterial = ParticleSystemMaterial; + exports.Path = Path; + exports.PerspectiveCamera = PerspectiveCamera; + exports.Plane = Plane; + exports.PlaneBufferGeometry = PlaneGeometry; + exports.PlaneGeometry = PlaneGeometry; + exports.PlaneHelper = PlaneHelper; + exports.PointCloud = PointCloud; + exports.PointCloudMaterial = PointCloudMaterial; + exports.PointLight = PointLight; + exports.PointLightHelper = PointLightHelper; + exports.Points = Points; + exports.PointsMaterial = PointsMaterial; + exports.PolarGridHelper = PolarGridHelper; + exports.PolyhedronBufferGeometry = PolyhedronGeometry; + exports.PolyhedronGeometry = PolyhedronGeometry; + exports.PositionalAudio = PositionalAudio; + exports.PropertyBinding = PropertyBinding; + exports.PropertyMixer = PropertyMixer; + exports.QuadraticBezierCurve = QuadraticBezierCurve; + exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; + exports.Quaternion = Quaternion; + exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; + exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; + exports.REVISION = REVISION; + exports.RGBADepthPacking = RGBADepthPacking; + exports.RGBAFormat = RGBAFormat; + exports.RGBAIntegerFormat = RGBAIntegerFormat; + exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; + exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; + exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; + exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; + exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; + exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; + exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; + exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; + exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; + exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; + exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; + exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; + exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; + exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; + exports.RGBA_BPTC_Format = RGBA_BPTC_Format; + exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; + exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; + exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; + exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; + exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; + exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; + exports.RGBDEncoding = RGBDEncoding; + exports.RGBEEncoding = RGBEEncoding; + exports.RGBEFormat = RGBEFormat; + exports.RGBFormat = RGBFormat; + exports.RGBIntegerFormat = RGBIntegerFormat; + exports.RGBM16Encoding = RGBM16Encoding; + exports.RGBM7Encoding = RGBM7Encoding; + exports.RGB_ETC1_Format = RGB_ETC1_Format; + exports.RGB_ETC2_Format = RGB_ETC2_Format; + exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; + exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; + exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; + exports.RGFormat = RGFormat; + exports.RGIntegerFormat = RGIntegerFormat; + exports.RawShaderMaterial = RawShaderMaterial; + exports.Ray = Ray; + exports.Raycaster = Raycaster; + exports.RectAreaLight = RectAreaLight; + exports.RedFormat = RedFormat; + exports.RedIntegerFormat = RedIntegerFormat; + exports.ReinhardToneMapping = ReinhardToneMapping; + exports.RepeatWrapping = RepeatWrapping; + exports.ReplaceStencilOp = ReplaceStencilOp; + exports.ReverseSubtractEquation = ReverseSubtractEquation; + exports.RingBufferGeometry = RingGeometry; + exports.RingGeometry = RingGeometry; + exports.SRGB8_ALPHA8_ASTC_10x10_Format = SRGB8_ALPHA8_ASTC_10x10_Format; + exports.SRGB8_ALPHA8_ASTC_10x5_Format = SRGB8_ALPHA8_ASTC_10x5_Format; + exports.SRGB8_ALPHA8_ASTC_10x6_Format = SRGB8_ALPHA8_ASTC_10x6_Format; + exports.SRGB8_ALPHA8_ASTC_10x8_Format = SRGB8_ALPHA8_ASTC_10x8_Format; + exports.SRGB8_ALPHA8_ASTC_12x10_Format = SRGB8_ALPHA8_ASTC_12x10_Format; + exports.SRGB8_ALPHA8_ASTC_12x12_Format = SRGB8_ALPHA8_ASTC_12x12_Format; + exports.SRGB8_ALPHA8_ASTC_4x4_Format = SRGB8_ALPHA8_ASTC_4x4_Format; + exports.SRGB8_ALPHA8_ASTC_5x4_Format = SRGB8_ALPHA8_ASTC_5x4_Format; + exports.SRGB8_ALPHA8_ASTC_5x5_Format = SRGB8_ALPHA8_ASTC_5x5_Format; + exports.SRGB8_ALPHA8_ASTC_6x5_Format = SRGB8_ALPHA8_ASTC_6x5_Format; + exports.SRGB8_ALPHA8_ASTC_6x6_Format = SRGB8_ALPHA8_ASTC_6x6_Format; + exports.SRGB8_ALPHA8_ASTC_8x5_Format = SRGB8_ALPHA8_ASTC_8x5_Format; + exports.SRGB8_ALPHA8_ASTC_8x6_Format = SRGB8_ALPHA8_ASTC_8x6_Format; + exports.SRGB8_ALPHA8_ASTC_8x8_Format = SRGB8_ALPHA8_ASTC_8x8_Format; + exports.Scene = Scene; + exports.SceneUtils = SceneUtils; + exports.ShaderChunk = ShaderChunk; + exports.ShaderLib = ShaderLib; + exports.ShaderMaterial = ShaderMaterial; + exports.ShadowMaterial = ShadowMaterial; + exports.Shape = Shape; + exports.ShapeBufferGeometry = ShapeGeometry; + exports.ShapeGeometry = ShapeGeometry; + exports.ShapePath = ShapePath; + exports.ShapeUtils = ShapeUtils; + exports.ShortType = ShortType; + exports.Skeleton = Skeleton; + exports.SkeletonHelper = SkeletonHelper; + exports.SkinnedMesh = SkinnedMesh; + exports.SmoothShading = SmoothShading; + exports.Sphere = Sphere; + exports.SphereBufferGeometry = SphereGeometry; + exports.SphereGeometry = SphereGeometry; + exports.Spherical = Spherical; + exports.SphericalHarmonics3 = SphericalHarmonics3; + exports.SplineCurve = SplineCurve; + exports.SpotLight = SpotLight; + exports.SpotLightHelper = SpotLightHelper; + exports.Sprite = Sprite; + exports.SpriteMaterial = SpriteMaterial; + exports.SrcAlphaFactor = SrcAlphaFactor; + exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; + exports.SrcColorFactor = SrcColorFactor; + exports.StaticCopyUsage = StaticCopyUsage; + exports.StaticDrawUsage = StaticDrawUsage; + exports.StaticReadUsage = StaticReadUsage; + exports.StereoCamera = StereoCamera; + exports.StreamCopyUsage = StreamCopyUsage; + exports.StreamDrawUsage = StreamDrawUsage; + exports.StreamReadUsage = StreamReadUsage; + exports.StringKeyframeTrack = StringKeyframeTrack; + exports.SubtractEquation = SubtractEquation; + exports.SubtractiveBlending = SubtractiveBlending; + exports.TOUCH = TOUCH; + exports.TangentSpaceNormalMap = TangentSpaceNormalMap; + exports.TetrahedronBufferGeometry = TetrahedronGeometry; + exports.TetrahedronGeometry = TetrahedronGeometry; + exports.TextBufferGeometry = TextGeometry; + exports.TextGeometry = TextGeometry; + exports.Texture = Texture; + exports.TextureLoader = TextureLoader; + exports.TorusBufferGeometry = TorusGeometry; + exports.TorusGeometry = TorusGeometry; + exports.TorusKnotBufferGeometry = TorusKnotGeometry; + exports.TorusKnotGeometry = TorusKnotGeometry; + exports.Triangle = Triangle; + exports.TriangleFanDrawMode = TriangleFanDrawMode; + exports.TriangleStripDrawMode = TriangleStripDrawMode; + exports.TrianglesDrawMode = TrianglesDrawMode; + exports.TubeBufferGeometry = TubeGeometry; + exports.TubeGeometry = TubeGeometry; + exports.UVMapping = UVMapping; + exports.Uint16Attribute = Uint16Attribute; + exports.Uint16BufferAttribute = Uint16BufferAttribute; + exports.Uint32Attribute = Uint32Attribute; + exports.Uint32BufferAttribute = Uint32BufferAttribute; + exports.Uint8Attribute = Uint8Attribute; + exports.Uint8BufferAttribute = Uint8BufferAttribute; + exports.Uint8ClampedAttribute = Uint8ClampedAttribute; + exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; + exports.Uniform = Uniform; + exports.UniformsLib = UniformsLib; + exports.UniformsUtils = UniformsUtils; + exports.UnsignedByteType = UnsignedByteType; + exports.UnsignedInt248Type = UnsignedInt248Type; + exports.UnsignedIntType = UnsignedIntType; + exports.UnsignedShort4444Type = UnsignedShort4444Type; + exports.UnsignedShort5551Type = UnsignedShort5551Type; + exports.UnsignedShort565Type = UnsignedShort565Type; + exports.UnsignedShortType = UnsignedShortType; + exports.VSMShadowMap = VSMShadowMap; + exports.Vector2 = Vector2; + exports.Vector3 = Vector3; + exports.Vector4 = Vector4; + exports.VectorKeyframeTrack = VectorKeyframeTrack; + exports.Vertex = Vertex; + exports.VertexColors = VertexColors; + exports.VideoTexture = VideoTexture; + exports.WebGL1Renderer = WebGL1Renderer; + exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; + exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; + exports.WebGLRenderTarget = WebGLRenderTarget; + exports.WebGLRenderTargetCube = WebGLRenderTargetCube; + exports.WebGLRenderer = WebGLRenderer; + exports.WebGLUtils = WebGLUtils; + exports.WireframeGeometry = WireframeGeometry; + exports.WireframeHelper = WireframeHelper; + exports.WrapAroundEnding = WrapAroundEnding; + exports.XHRLoader = XHRLoader; + exports.ZeroCurvatureEnding = ZeroCurvatureEnding; + exports.ZeroFactor = ZeroFactor; + exports.ZeroSlopeEnding = ZeroSlopeEnding; + exports.ZeroStencilOp = ZeroStencilOp; + exports.sRGBEncoding = sRGBEncoding; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/activities/3DVolume.activity/lib/domReady.js b/activities/3DVolume.activity/lib/domReady.js new file mode 100644 index 000000000..2b5412209 --- /dev/null +++ b/activities/3DVolume.activity/lib/domReady.js @@ -0,0 +1,129 @@ +/** + * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/domReady for details + */ +/*jslint */ +/*global require: false, define: false, requirejs: false, + window: false, clearInterval: false, document: false, + self: false, setInterval: false */ + + +define(function () { + 'use strict'; + + var isTop, testDiv, scrollIntervalId, + isBrowser = typeof window !== "undefined" && window.document, + isPageLoaded = !isBrowser, + doc = isBrowser ? document : null, + readyCalls = []; + + function runCallbacks(callbacks) { + var i; + for (i = 0; i < callbacks.length; i += 1) { + callbacks[i](doc); + } + } + + function callReady() { + var callbacks = readyCalls; + + if (isPageLoaded) { + //Call the DOM ready callbacks + if (callbacks.length) { + readyCalls = []; + runCallbacks(callbacks); + } + } + } + + /** + * Sets the page as loaded. + */ + function pageLoaded() { + if (!isPageLoaded) { + isPageLoaded = true; + if (scrollIntervalId) { + clearInterval(scrollIntervalId); + } + + callReady(); + } + } + + if (isBrowser) { + if (document.addEventListener) { + //Standards. Hooray! Assumption here that if standards based, + //it knows about DOMContentLoaded. + document.addEventListener("DOMContentLoaded", pageLoaded, false); + window.addEventListener("load", pageLoaded, false); + } else if (window.attachEvent) { + window.attachEvent("onload", pageLoaded); + + testDiv = document.createElement('div'); + try { + isTop = window.frameElement === null; + } catch (e) {} + + //DOMContentLoaded approximation that uses a doScroll, as found by + //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/, + //but modified by other contributors, including jdalton + if (testDiv.doScroll && isTop && window.external) { + scrollIntervalId = setInterval(function () { + try { + testDiv.doScroll(); + pageLoaded(); + } catch (e) {} + }, 30); + } + } + + //Check if document already complete, and if so, just trigger page load + //listeners. Latest webkit browsers also use "interactive", and + //will fire the onDOMContentLoaded before "interactive" but not after + //entering "interactive" or "complete". More details: + //http://dev.w3.org/html5/spec/the-end.html#the-end + //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded + //Hmm, this is more complicated on further use, see "firing too early" + //bug: https://github.com/requirejs/domReady/issues/1 + //so removing the || document.readyState === "interactive" test. + //There is still a window.onload binding that should get fired if + //DOMContentLoaded is missed. + if (document.readyState === "complete") { + pageLoaded(); + } + } + + /** START OF PUBLIC API **/ + + /** + * Registers a callback for DOM ready. If DOM is already ready, the + * callback is called immediately. + * @param {Function} callback + */ + function domReady(callback) { + if (isPageLoaded) { + callback(doc); + } else { + readyCalls.push(callback); + } + return domReady; + } + + domReady.version = '2.0.1'; + + /** + * Loader Plugin API method + */ + domReady.load = function (name, req, onLoad, config) { + if (config.isBuild) { + onLoad(null); + } else { + domReady(onLoad); + } + }; + + /** END OF PUBLIC API **/ + + return domReady; +}); diff --git a/activities/3DVolume.activity/lib/intro.js b/activities/3DVolume.activity/lib/intro.js new file mode 100644 index 000000000..88c49dc90 --- /dev/null +++ b/activities/3DVolume.activity/lib/intro.js @@ -0,0 +1,11 @@ +/*! + * Intro.js v5.1.0 + * https://introjs.com + * + * Copyright (C) 2012-2022 Afshin Mehrabani (@afshinmeh). + * https://introjs.com + * + * Date: Mon, 04 Apr 2022 21:20:28 GMT + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).introJs=e()}(this,(function(){"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null==n)return;var i,o,r=[],a=!0,s=!1;try{for(n=n.call(t);!(a=(i=n.next()).done)&&(r.push(i.value),!e||r.length!==e);a=!0);}catch(t){s=!0,o=t}finally{try{a||null==n.return||n.return()}finally{if(s)throw o}}return r}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return i(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n1&&void 0!==arguments[1]?arguments[1]:"introjs-stamp";return t[n]=t[n]||0,void 0===e[n]&&(e[n]=t[n]++),e[n]}}();function r(t,e,n){if(t)for(var i=0,o=t.length;i0&&c[0]<4?1:+(c[0]+c[1])),!u&&F&&(!(c=F.match(/Edge\/(\d+)/))||c[1]>=74)&&(c=F.match(/Chrome\/(\d+)/))&&(u=+c[1]);var U=u,W=!!Object.getOwnPropertySymbols&&!p((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&U&&U<41})),K=W&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,Y=f.Object,X=K?function(t){return"symbol"==typeof t}:function(t){var e=H("Symbol");return M(e)&&D(e.prototype,Y(t))},J=f.String,Q=function(t){try{return J(t)}catch(t){return"Object"}},Z=f.TypeError,tt=function(t){if(M(t))return t;throw Z(Q(t)+" is not a function")},et=function(t,e){var n=t[e];return null==n?void 0:tt(n)},nt=f.TypeError,it=Object.defineProperty,ot=function(t,e){try{it(f,t,{value:e,configurable:!0,writable:!0})}catch(n){f[t]=e}return e},rt="__core-js_shared__",at=f[rt]||ot(rt,{}),st=l((function(t){(t.exports=function(t,e){return at[t]||(at[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.21.1",mode:"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.21.1/LICENSE",source:"https://github.com/zloirock/core-js"})})),lt=f.Object,ct=function(t){return lt(P(t))},ut=A({}.hasOwnProperty),ht=Object.hasOwn||function(t,e){return ut(ct(t),e)},ft=0,pt=Math.random(),dt=A(1..toString),gt=function(t){return"Symbol("+(void 0===t?"":t)+")_"+dt(++ft+pt,36)},vt=st("wks"),mt=f.Symbol,bt=mt&&mt.for,yt=K?mt:mt&&mt.withoutSetter||gt,wt=function(t){if(!ht(vt,t)||!W&&"string"!=typeof vt[t]){var e="Symbol."+t;W&&ht(mt,t)?vt[t]=mt[t]:vt[t]=K&&bt?bt(e):yt(e)}return vt[t]},_t=f.TypeError,St=wt("toPrimitive"),xt=function(t,e){if(!q(t)||X(t))return t;var n,i=et(t,St);if(i){if(void 0===e&&(e="default"),n=m(i,t,e),!q(n)||X(n))return n;throw _t("Can't convert object to primitive value")}return void 0===e&&(e="number"),function(t,e){var n,i;if("string"===e&&M(n=t.toString)&&!q(i=m(n,t)))return i;if(M(n=t.valueOf)&&!q(i=m(n,t)))return i;if("string"!==e&&M(n=t.toString)&&!q(i=m(n,t)))return i;throw nt("Can't convert object to primitive value")}(t,e)},jt=function(t){var e=xt(t,"string");return X(e)?e:e+""},Ct=f.document,At=q(Ct)&&q(Ct.createElement),kt=function(t){return At?Ct.createElement(t):{}},Et=!d&&!p((function(){return 7!=Object.defineProperty(kt("div"),"a",{get:function(){return 7}}).a})),Tt=Object.getOwnPropertyDescriptor,It={f:d?Tt:function(t,e){if(t=R(t),e=jt(e),Et)try{return Tt(t,e)}catch(t){}if(ht(t,e))return _(!m(w.f,t,e),t[e])}},Nt=d&&p((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),Lt=f.String,Ot=f.TypeError,Pt=function(t){if(q(t))return t;throw Ot(Lt(t)+" is not an object")},Rt=f.TypeError,Mt=Object.defineProperty,qt=Object.getOwnPropertyDescriptor,Bt="enumerable",Ht="configurable",Dt="writable",Ft={f:d?Nt?function(t,e,n){if(Pt(t),e=jt(e),Pt(n),"function"==typeof t&&"prototype"===e&&"value"in n&&Dt in n&&!n.writable){var i=qt(t,e);i&&i.writable&&(t[e]=n.value,n={configurable:Ht in n?n.configurable:i.configurable,enumerable:Bt in n?n.enumerable:i.enumerable,writable:!1})}return Mt(t,e,n)}:Mt:function(t,e,n){if(Pt(t),e=jt(e),Pt(n),Et)try{return Mt(t,e,n)}catch(t){}if("get"in n||"set"in n)throw Rt("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},$t=d?function(t,e,n){return Ft.f(t,e,_(1,n))}:function(t,e,n){return t[e]=n,t},Gt=A(Function.toString);M(at.inspectSource)||(at.inspectSource=function(t){return Gt(t)});var Vt,zt,Ut,Wt=at.inspectSource,Kt=f.WeakMap,Yt=M(Kt)&&/native code/.test(Wt(Kt)),Xt=st("keys"),Jt=function(t){return Xt[t]||(Xt[t]=gt(t))},Qt={},Zt="Object already initialized",te=f.TypeError,ee=f.WeakMap;if(Yt||at.state){var ne=at.state||(at.state=new ee),ie=A(ne.get),oe=A(ne.has),re=A(ne.set);Vt=function(t,e){if(oe(ne,t))throw new te(Zt);return e.facade=t,re(ne,t,e),e},zt=function(t){return ie(ne,t)||{}},Ut=function(t){return oe(ne,t)}}else{var ae=Jt("state");Qt[ae]=!0,Vt=function(t,e){if(ht(t,ae))throw new te(Zt);return e.facade=t,$t(t,ae,e),e},zt=function(t){return ht(t,ae)?t[ae]:{}},Ut=function(t){return ht(t,ae)}}var se={set:Vt,get:zt,has:Ut,enforce:function(t){return Ut(t)?zt(t):Vt(t,{})},getterFor:function(t){return function(e){var n;if(!q(e)||(n=zt(e)).type!==t)throw te("Incompatible receiver, "+t+" required");return n}}},le=Function.prototype,ce=d&&Object.getOwnPropertyDescriptor,ue=ht(le,"name"),he={EXISTS:ue,PROPER:ue&&"something"===function(){}.name,CONFIGURABLE:ue&&(!d||d&&ce(le,"name").configurable)},fe=l((function(t){var e=he.CONFIGURABLE,n=se.get,i=se.enforce,o=String(String).split("String");(t.exports=function(t,n,r,a){var s,l=!!a&&!!a.unsafe,c=!!a&&!!a.enumerable,u=!!a&&!!a.noTargetGet,h=a&&void 0!==a.name?a.name:n;M(r)&&("Symbol("===String(h).slice(0,7)&&(h="["+String(h).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!ht(r,"name")||e&&r.name!==h)&&$t(r,"name",h),(s=i(r)).source||(s.source=o.join("string"==typeof h?h:""))),t!==f?(l?!u&&t[n]&&(c=!0):delete t[n],c?t[n]=r:$t(t,n,r)):c?t[n]=r:ot(n,r)})(Function.prototype,"toString",(function(){return M(this)&&n(this).source||Wt(this)}))})),pe=Math.ceil,de=Math.floor,ge=function(t){var e=+t;return e!=e||0===e?0:(e>0?de:pe)(e)},ve=Math.max,me=Math.min,be=function(t,e){var n=ge(t);return n<0?ve(n+e,0):me(n,e)},ye=Math.min,we=function(t){return t>0?ye(ge(t),9007199254740991):0},_e=function(t){return we(t.length)},Se=function(t){return function(e,n,i){var o,r=R(e),a=_e(r),s=be(i,a);if(t&&n!=n){for(;a>s;)if((o=r[s++])!=o)return!0}else for(;a>s;s++)if((t||s in r)&&r[s]===n)return t||s||0;return!t&&-1}},xe={includes:Se(!0),indexOf:Se(!1)},je=xe.indexOf,Ce=A([].push),Ae=function(t,e){var n,i=R(t),o=0,r=[];for(n in i)!ht(Qt,n)&&ht(i,n)&&Ce(r,n);for(;e.length>o;)ht(i,n=e[o++])&&(~je(r,n)||Ce(r,n));return r},ke=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Ee=ke.concat("length","prototype"),Te={f:Object.getOwnPropertyNames||function(t){return Ae(t,Ee)}},Ie={f:Object.getOwnPropertySymbols},Ne=A([].concat),Le=H("Reflect","ownKeys")||function(t){var e=Te.f(Pt(t)),n=Ie.f;return n?Ne(e,n(t)):e},Oe=function(t,e,n){for(var i=Le(e),o=Ft.f,r=It.f,a=0;aa;)Ft.f(t,n=o[a++],i[n]);return t},an={f:rn},sn=H("document","documentElement"),ln=Jt("IE_PROTO"),cn=function(){},un=function(t){return" - - - - + + + + + @@ -33,32 +33,35 @@ -
+
+ - -
+
-
+
- + + + + -
+
@@ -96,7 +99,7 @@ -

: Last Roll

+

diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index b06c2b57d..87cf9cc8a 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -3,7 +3,8 @@ define([ 'sugar-web/env', './palettes/bgpalette', './palettes/volumepalette', - './palettes/colorpalette', + './palettes/colorpalettefill', + './palettes/colorpalettetext', './palettes/zoompalette', 'sugar-web/graphics/presencepalette', 'tutorial', @@ -14,7 +15,8 @@ define([ env, bgpalette, volumepalette, - colorpalette, + colorpaletteFill, + colorpaletteText, zoompalette, presencepalette, tutorial, @@ -48,10 +50,16 @@ define([ document.getElementById('zoom-button'), undefined, ) - var paletteColor = new colorpalette.ColorPalette( - document.getElementById('color-button'), + var paletteColorFill = new colorpaletteFill.ColorPalette( + document.getElementById('color-button-fill'), undefined, ) + + var paletteColorText = new colorpaletteText.ColorPalette( + document.getElementById('color-button-text'), + undefined, + ) + let presentScore = 0 let lastRoll = '' let diceArray = [] @@ -62,6 +70,13 @@ define([ let textColor = '#ffffff' var currentenv let removeVolume = false + let transparent = false + let toggleTransparent = false + let defaultVolume = true + + var defaultButton = document.getElementById('default-button') + defaultButton.classList.toggle('active') + env.getEnvironment(function (err, environment) { currentenv = environment @@ -70,15 +85,18 @@ define([ ? currentenv.user.colorvalue.fill : presentColor - scene.background = new THREE.Color(presentColor) + scene.background = new THREE.Color("#A9A9A9") + console.log(presentColor) textColor = currentenv.user.colorvalue.stroke != null ? currentenv.user.colorvalue.stroke : textColor - document.getElementById('color-button').style.backgroundColor = - presentColor + + document.getElementById('color-button-fill').style.backgroundColor = presentColor + document.getElementById('color-button-text').style.backgroundColor = textColor + if (environment.sharedId) { @@ -91,7 +109,6 @@ define([ var onNetworkDataReceived = function (msg) { if (presence.getUserInfo().networkId === msg.user.networkId) { - console.log('returbign') return } if (msg.action == 'throw') { @@ -152,6 +169,17 @@ define([ msg.content.sharedImageData ) break + case 'deca': + createDecahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ) + break case 'icosa': createIcosahedron( msg.content.color, @@ -173,18 +201,123 @@ define([ tutorial.start() }) - document.addEventListener('color-selected', function (event) { - const selectedColor = event.detail.color - // Update the presentColor variable with the selected color - presentColor = selectedColor - document.getElementById('color-button').style.backgroundColor = - presentColor - console.log('Present color updated:', presentColor) - // changeColors(); - }) + document.addEventListener('color-selected-fill', function (event) { + const selectedColor = event.detail.color; + presentColor = selectedColor; + document.getElementById('color-button-fill').style.backgroundColor = presentColor; + updateSliders(selectedColor); + }); + + const redSliderFill = document.getElementById("red-slider-fill"); + const greenSliderFill = document.getElementById("green-slider-fill"); + const blueSliderFill = document.getElementById("blue-slider-fill"); + + let sliderColorFill = { r: 0, g: 0, b: 0 }; + + function rgbToHex(r, g, b) { + return ( + "#" + + ((1 << 24) + (r << 16) + (g << 8) + b) + .toString(16) + .slice(1) + .toUpperCase() + ); + } + + function hexToRgb(hex) { + let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; + } + + function updateColorDisplayFill() { + const hexColor = rgbToHex(sliderColorFill.r, sliderColorFill.g, sliderColorFill.b); + presentColor = hexColor; + document.getElementById('color-button-fill').style.backgroundColor = presentColor; + } + + function updateSlidersFill(color) { + const rgb = color.match(/\d+/g).map(num => parseInt(num, 10)); + redSliderFill.value = rgb[0]; + greenSliderFill.value = rgb[1]; + blueSliderFill.value = rgb[2]; + } + + function handleSliderChangeFill() { + sliderColorText = { + r: parseInt(redSliderFill.value), + g: parseInt(greenSliderFill.value), + b: parseInt(blueSliderFill.value), + }; + updateColorDisplayFill(); + } + + redSliderFill.addEventListener("input", handleSliderChangeFill); + greenSliderFill.addEventListener("input", handleSliderChangeFill); + blueSliderFill.addEventListener("input", handleSliderChangeFill); + + + document.addEventListener('color-selected-fill', function (event) { + const selectedColorFill = event.detail.color; + presentColor = selectedColorFill; + document.getElementById('color-button-fill').style.backgroundColor = presentColor; + updateSlidersFill(selectedColorFill); + }); + + + + + + + const redSliderText = document.getElementById("red-slider-text"); + const greenSliderText = document.getElementById("green-slider-text"); + const blueSliderText = document.getElementById("blue-slider-text"); + + let sliderColorText = { r: 0, g: 0, b: 0 }; + + + function updateColorDisplayText() { + const hexColor = rgbToHex(sliderColorText.r, sliderColorText.g, sliderColorText.b); + textColor = hexColor; + document.getElementById('color-button-text').style.backgroundColor = textColor; + } + + function updateSlidersText(color) { + const rgb = color.match(/\d+/g).map(num => parseInt(num, 10)); + redSliderText.value = rgb[0]; + greenSliderText.value = rgb[1]; + blueSliderText.value = rgb[2]; + } + + function handleSliderChangeText() { + sliderColorText = { + r: parseInt(redSliderText.value), + g: parseInt(greenSliderText.value), + b: parseInt(blueSliderText.value), + }; + updateColorDisplayText(); + } + + redSliderText.addEventListener("input", handleSliderChangeText); + greenSliderText.addEventListener("input", handleSliderChangeText); + blueSliderText.addEventListener("input", handleSliderChangeText); + + document.addEventListener('color-selected-text', function (event) { + const selectedColorText = event.detail.color; + textColor = selectedColorText; + document.getElementById('color-button-text').style.backgroundColor = textColor; + updateSlidersText(selectedColorFill); +}); + + + + - let transparent = false - let toggleTransparent = false function updateDice(type, value) { dices[type] += value document.getElementById(type).innerHTML = '
' + dices[type] @@ -218,7 +351,12 @@ define([ document.querySelector('#number-button').addEventListener('click', () => { var numberButton = document.getElementById('number-button') numberButton.classList.toggle('active') - document.getElementById("volume-button").style.backgroundImage = 'url(icons/number_on.svg)' + document.getElementById("volume-button").style.backgroundImage = 'url(icons/number_volume.svg)' + if (defaultVolume) { + var defaultButton = document.getElementById('default-button') + defaultButton.classList.toggle('active') + defaultVolume = !defaultVolume + } if (toggleTransparent) { var transparentButton = document.getElementById('transparent-button') transparentButton.classList.toggle('active') @@ -232,32 +370,23 @@ define([ showNumbers = !showNumbers // toggleNumbers(); }) - document.querySelector('#clear-button').addEventListener('click', () => { - var clearButton = document.getElementById('clear-button') - // Toggle the 'active' class on the clear button - clearButton.classList.toggle('active') - removeVolume = !removeVolume - addCube = false - addTetra = false - addOcta = false - addDodeca = false - addIcosa = false - cube.classList.remove('active') - tetra.classList.remove('active') - octa.classList.remove('active') - dodeca.classList.remove('active') - icosa.classList.remove('active') - - }) - const remove_button = document.querySelector('#clear-button') document .querySelector('#transparent-button') .addEventListener('click', () => { var transparentButton = document.getElementById('transparent-button') // Toggle the 'active' class on the clear button transparentButton.classList.toggle('active') - document.getElementById("volume-button").style.backgroundImage = 'url(icons/tess.png)' + document.getElementById("volume-button").style.backgroundImage = 'url(icons/transparent_volume.svg)' + console.log("it is beign clicked now!!") + console.log(defaultVolume) + + if (defaultVolume) { + console.log("it is true") + var defaultButton = document.getElementById('default-button') + defaultButton.classList.toggle('active') + defaultVolume = !defaultVolume + } if (showNumbers) { var numberButton = document.getElementById('number-button') @@ -272,19 +401,100 @@ define([ toggleTransparent = !toggleTransparent }) - let addCube = false - let addTetra = false - let addOcta = false - let addDodeca = false - let addIcosa = false - var cube = document.getElementById('cube-button') - var tetra = document.getElementById('tetra-button') - var octa = document.getElementById('octa-button') - var dodeca = document.getElementById('dodeca-button') - var icosa = document.getElementById('icosa-button') + document + .querySelector('#default-button') + .addEventListener('click', () => { + var defaultButton = document.getElementById('default-button') + // Toggle the 'active' class on the clear button + defaultButton.classList.toggle('active') + document.getElementById("volume-button").style.backgroundImage = 'url(icons/cube_solid.svg)' + + if (toggleTransparent) { + var transparentButton = document.getElementById('transparent-button') + transparentButton.classList.toggle('active') + toggleTransparent = !toggleTransparent + } + + if (showNumbers) { + var numberButton = document.getElementById('number-button') + numberButton.classList.toggle('active') + showNumbers = !showNumbers + } + if (showImage) { + var imageButton1 = document.getElementById('image-button') + imageButton1.classList.toggle('active') + showImage = !showImage + } + defaultVolume = !defaultVolume + }) + let addShape = { + cube: false, + tetra: false, + octa: false, + dodeca: false, + deca: false, + icosa: false + }; + + const buttons = ['cube', 'tetra', 'octa', 'dodeca', 'deca', 'icosa']; + const clearButton = document.getElementById('clear-button'); + const removeButton = document.querySelector('#clear-button'); + const solidButton = document.querySelector('#solid-button'); + + const toggleShape = (shape) => { + buttons.forEach(btn => { + addShape[btn] = btn === shape; + document.getElementById(`${btn}-button`).classList.toggle('active', btn === shape); + }); + removeVolume = false; + removeButton.classList.remove('active'); + + if (transparent) { + transparent = false; + solidButton.style.backgroundImage = 'url(icons/cube_solid.svg)'; + toggleTransparency(); + } + }; + + clearButton.addEventListener('click', () => { + clearButton.classList.toggle('active'); + removeVolume = !removeVolume; + buttons.forEach(btn => { + addShape[btn] = false; + document.getElementById(`${btn}-button`).classList.remove('active'); + }); + }); + + removeButton.addEventListener('click', () => { + if (!removeButton.classList.contains('active')) { + buttons.forEach(btn => { + addShape[btn] = false; + document.getElementById(`${btn}-button`).classList.remove('active'); + }); + removeVolume = true; + removeButton.classList.add('active'); + + if (transparent) { + transparent = false; + solidButton.style.backgroundImage = 'url(icons/cube_solid.svg)'; + toggleTransparency(); + } + } + }); + + buttons.forEach(shape => { + document.getElementById(`${shape}-button`).addEventListener('click', () => { + if (!document.getElementById(`${shape}-button`).classList.contains('active')) { + toggleShape(shape); + } + }); + }); + + + // const imageButton = document.getElementById('image-button') // document // .getElementById('image-button') @@ -339,146 +549,6 @@ define([ // ) // }) - // Add click event listeners to each div - cube.addEventListener('click', function () { - if (!cube.classList.contains('active')) { - cube.classList.add('active') - addCube = true - addTetra = false - addOcta = false - addDodeca = false - addIcosa = false - removeVolume = false - remove_button.classList.remove('active') - tetra.classList.remove('active') - octa.classList.remove('active') - dodeca.classList.remove('active') - icosa.classList.remove('active') - - - - if (transparent) { - transparent = false - document.querySelector('#solid-button').style.backgroundImage = - 'url(icons/cube_solid.svg)' - toggleTransparency() - } - } else { - cube.classList.remove('active') - addCube = !addCube - } - }) - - tetra.addEventListener('click', function () { - if (!tetra.classList.contains('active')) { - addCube = false - addTetra = true - addOcta = false - addDodeca = false - addIcosa = false - tetra.classList.add('active') - cube.classList.remove('active') - octa.classList.remove('active') - dodeca.classList.remove('active') - icosa.classList.remove('active') - - - removeVolume = false - remove_button.classList.remove('active') - if (transparent) { - transparent = false - document.querySelector('#solid-button').style.backgroundImage = - 'url(icons/cube_solid.svg)' - toggleTransparency() - } - } else { - tetra.classList.remove('active') - addTetra = !addTetra - } - }) - - octa.addEventListener('click', function () { - if (!octa.classList.contains('active')) { - addCube = false - addTetra = false - addOcta = true - addDodeca = false - addIcosa = false - octa.classList.add('active') - cube.classList.remove('active') - tetra.classList.remove('active') - icosa.classList.remove('active') - dodeca.classList.remove('active') - - removeVolume = false - remove_button.classList.remove('active') - if (transparent) { - transparent = false - document.querySelector('#solid-button').style.backgroundImage = - 'url(icons/cube_solid.svg)' - toggleTransparency() - } - } else { - octa.classList.remove('active') - addOcta = !addOcta - } - }) - - dodeca.addEventListener('click', function () { - if (!dodeca.classList.contains('active')) { - addCube = false - addTetra = false - addOcta = false - addIcosa = false - addDodeca = true - octa.classList.remove('active') - cube.classList.remove('active') - tetra.classList.remove('active') - icosa.classList.remove('active') - dodeca.classList.add('active') - - removeVolume = false - remove_button.classList.remove('active') - if (transparent) { - transparent = false - document.querySelector('#solid-button').style.backgroundImage = - 'url(icons/cube_solid.svg)' - toggleTransparency() - } - } else { - dodeca.classList.remove('active') - addDodeca = !addDodeca - } - }) - - icosa.addEventListener('click', function () { - if (!icosa.classList.contains('active')) { - addCube = false - addTetra = false - addOcta = false - addDodeca = false - addIcosa = true - octa.classList.remove('active') - cube.classList.remove('active') - tetra.classList.remove('active') - dodeca.classList.remove('active') - - icosa.classList.add('active') - - removeVolume = false - remove_button.classList.remove('active') - if (transparent) { - transparent = false - document.querySelector('#solid-button').style.backgroundImage = - 'url(icons/cube_solid.svg)' - toggleTransparency() - } - } else { - icosa.classList.remove('active') - addIcosa = !addIcosa - } - }) - // Event listeners // document // .querySelector('.cube .plus-button') @@ -540,7 +610,7 @@ define([ // Function to update the elements function updateElements() { // totalScoreElement.textContent = presentScore - lastRollElement.textContent = lastRoll + '=' + presentScore; + lastRollElement.textContent = lastRoll.substring(0, lastRoll.length - 2) + '= ' + presentScore; } const renderer = new THREE.WebGLRenderer({ @@ -556,7 +626,7 @@ define([ document.querySelector('body').addEventListener('click', onAddClick) function onAddClick(event) { - if (addCube || addTetra || addOcta || addDodeca || addIcosa) { + if (!removeVolume) { var rect = renderer.domElement.getBoundingClientRect() mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 @@ -570,11 +640,10 @@ define([ for (let i = 0; i < intersects.length; i++) { var intersectedObject = intersects[i]?.object if (intersectedObject.geometry.type == 'PlaneGeometry') { - console.log(intersects[i].point) xCoordinate = intersects[i].point.x zCoordinate = intersects[i].point.z - if (addCube) { + if (addShape['cube']) { createCube() if (presence) { presence.sendMessage(presence.getSharedInfo().id, { @@ -591,7 +660,7 @@ define([ }, }) } - } else if (addTetra) { + } else if (addShape['tetra']) { createTetrahedron() if (presence) { presence.sendMessage(presence.getSharedInfo().id, { @@ -608,7 +677,24 @@ define([ }, }) } - } else if (addDodeca) { + } else if (addShape['deca']) { + createDecahedron() + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + content: { + shape: 'deca', + color: currentenv.user.colorvalue.fill, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData + }, + }) + } + } else if (addShape['dodeca']) { createDodecahedron() if (presence) { presence.sendMessage(presence.getSharedInfo().id, { @@ -625,7 +711,7 @@ define([ }, }) } - } else if (addIcosa) { + } else if (addShape['icosa']) { createIcosahedron() if (presence) { presence.sendMessage(presence.getSharedInfo().id, { @@ -644,7 +730,7 @@ define([ } } else { createOctahedron() - if (presence) { + if (addShape['octa']) { console.log(showImage); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), @@ -759,6 +845,7 @@ define([ scene.add(bottomLight) scene.add(topLight) + const ambientLight = new THREE.AmbientLight(0x222222) // Soft ambient lighting scene.add(ambientLight) @@ -783,7 +870,7 @@ define([ const groundMesh = new THREE.Mesh(groundGeo, groundMat) groundMesh.receiveShadow = true - groundMesh.material.color.setHex(0x425eff) + groundMesh.material.color.setHex(0x363636) scene.add(groundMesh) const groundPhysMat = new CANNON.Material() const groundWidth = 0 // Desired width of the ground @@ -803,8 +890,9 @@ define([ material: groundPhysMat, }) groundBody.material.friction = 1 - world.addBody(groundBody) groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) + world.addBody(groundBody) + const leftWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), @@ -891,6 +979,11 @@ define([ const zoomInButton = document.getElementById('zoom-in-button') const zoomOutButton = document.getElementById('zoom-out-button') + const zoomEqualButton = document.getElementById('zoom-equal-button') + const zoomToButton = document.getElementById('zoom-to-button') + + + const zoomInFunction = e => { const fov = getFov() @@ -904,6 +997,18 @@ define([ camera.updateProjectionMatrix() } + const zoomEqualFunction = e => { + const fov = getFov() + camera.fov = 29; + camera.updateProjectionMatrix() + } + + const zoomToFunction = e => { + const fov = getFov() + camera.fov = 35; + camera.updateProjectionMatrix() + } + const clickZoom = (value, zoomType) => { if (value >= 20 && zoomType === 'zoomIn') { return value - 5 @@ -923,8 +1028,18 @@ define([ ) } + const fov = getFov() + camera.fov = 29 + camera.updateProjectionMatrix() + + + zoomInButton.addEventListener('click', zoomInFunction) zoomOutButton.addEventListener('click', zoomOutFunction) + zoomEqualButton.addEventListener('click', zoomEqualFunction); + zoomToButton.addEventListener('click', zoomToFunction); + + function createTetrahedron( sharedColor, @@ -1338,6 +1453,7 @@ define([ }) const line = new THREE.LineSegments(wireframe, lineMaterial) boxMesh = line + } else if (tempImage) { const boxGeo = new THREE.BoxGeometry(2, 2, 2) @@ -1361,6 +1477,8 @@ define([ scene.add(boxMesh) const boxPhysmat = new CANNON.Material() + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared const boxBody = new CANNON.Body({ @@ -1391,6 +1509,7 @@ define([ boxBody.applyImpulse(offset, rollingForce) // what will happen when the two bodies touch + const groundBoxContactMat = new CANNON.ContactMaterial( groundPhysMat, boxPhysmat, @@ -1404,7 +1523,8 @@ define([ const cannonDebugger = new CannonDebugger(scene, world, { color: 0xadd8e6, }) - function createDodecahedron( + + function createDecahedron( sharedColor, ifNumbers, ifTransparent, @@ -1413,19 +1533,57 @@ define([ ifImage, sharedImageData ) { - console.log("creating a dodecahedron") - let dodecahedron + let decahedron let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers let tempTransparent = ifTransparent == null ? toggleTransparent : ifTransparent let tempImage = ifImage == null ? showImage : ifImage + + const sides = 10; + const radius = 1; + const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], + ].flat(); + + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); + } + console.log(verticesGeo); + + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ].flat(); + const args = [verticesGeo, facesGeo, radius, 0]; + let decaGeometry = new THREE.PolyhedronGeometry(...args); + + if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 5) let tileSize = 512 - let g = new THREE.DodecahedronGeometry(2) + let g = decaGeometry let c = document.createElement('canvas') - let div = document.createElement('div') c.width = tileSize * tileDimension.x c.height = tileSize * tileDimension.y let ctx = c.getContext('2d') @@ -1434,6 +1592,8 @@ define([ let uvs = [] + + let baseUVs = [ [0.067, 0.25], [0.933, 0.25], @@ -1441,13 +1601,8 @@ define([ ].map(p => { return new THREE.Vector2(...p) }) - let arrOfNums = [ - [2, 1, 3], - [1, 2, 4], - [3, 1, 4], - [2, 3, 4], - ] - for (let i = 0; i < 4; i++) { + + for (let i = 0; i < 10; i++) { let u = i % tileDimension.x let v = Math.floor(i / tileDimension.x) uvs.push( @@ -1461,28 +1616,13 @@ define([ ctx.textAlign = 'center' ctx.textBaseline = 'middle' - ctx.font = `bold 150px Arial` + ctx.font = `bold 200px Arial` ctx.fillStyle = textColor - // ctx.fillText( - // i + 1, - // (u + 0.5) * tileSize, - // c.height - (v + 0.5) * tileSize - // ); - let aStep = (Math.PI * 2) / 3 - let yAlign = Math.PI * 0.5 - let tileQuarter = tileSize * 0.25 - for (let j = 0; j < 3; j++) { - ctx.save() - ctx.translate( - (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, - c.height - - (v + 0.5) * tileSize + - Math.sin(j * aStep - yAlign) * tileQuarter, - ) - ctx.rotate((j * Math.PI * 2) / 3) - ctx.fillText(arrOfNums[i][j], 0, 0) - ctx.restore() - } + ctx.fillText( + i + 1 + (i == 5 || i == 8 ? '' : ''), + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize, + ) } g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) @@ -1493,11 +1633,179 @@ define([ map: tex, }) - dodecahedron = new THREE.Mesh(g, m) + decahedron = new THREE.Mesh(g, m) + + + } else if (tempTransparent) { + const decahedronTransaprentGeometry = decaGeometry + const wireframe = new THREE.WireframeGeometry( + decahedronTransaprentGeometry, + ) + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }) + const line = new THREE.LineSegments(wireframe, lineMaterial) + decahedron = line + } else if (tempImage) { + const decaGeo = decaGeometry + + const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) + + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }) + + // Create cube mesh with the material + decahedron = new THREE.Mesh(decaGeo, material) + } else { + const decahedronGeometry = decaGeometry + + const decaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }) + + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial) + } + + decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true + scene.add(decahedron) + + const t = (1 + Math.sqrt(5)) / 2; +const r = 1 / t; + +const verticesCannon = []; + for (let i = 0; i < verticesGeo.length; i += 3) { + verticesCannon.push(new CANNON.Vec3(verticesGeo[i], verticesGeo[i+1], verticesGeo[i+2])); + } + + const facesCannon = []; + for (let i = 0; i < facesGeo.length; i += 3) { + facesCannon.push([facesGeo[i], facesGeo[i+1], facesGeo[i+2]]); + } + + + // Create a ConvexPolyhedron shape from the vertices and faces + const decahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesCannon, + faces: facesCannon, + }) + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + + const decahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: decahedronShape, + position: new CANNON.Vec3(x, 10, z), + friction: -1, + restitution: 5, + }) + if (tempShowNumbers) { + decahedronBody.addEventListener('sleep', () => { + console.log("going to sleep now deca") + sleepCounter++; + getTetraScore(decahedron) + }) + } + world.addBody(decahedronBody) + decahedronBody.angularVelocity.set( + Math.random() - 0.5, + Math.random(), + Math.random() - 0.5, + ) + decahedronBody.applyImpulse(offset, rollingForce) + decahedron.position.copy(decahedronBody.position) // this merges the physics body to threejs mesh + decahedron.quaternion.copy(decahedronBody.quaternion) + diceArray.push([decahedron, decahedronBody, 'deca']) + } + + function createDodecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData + ) { + let dodecahedron + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent + let tempImage = ifImage == null ? showImage : ifImage + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid + let tileSize = 512; + let g = new THREE.DodecahedronGeometry(2); + + let c = document.createElement('canvas'); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext('2d'); + ctx.fillStyle = presentColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + const base = new THREE.Vector2(0, 0.5); + const center = new THREE.Vector2(); + const angle = THREE.MathUtils.degToRad(72); + let baseUVs = [ + base.clone().rotateAround(center, angle * 1).addScalar(0.5), + base.clone().rotateAround(center, angle * 2).addScalar(0.5), + base.clone().rotateAround(center, angle * 3).addScalar(0.5), + base.clone().rotateAround(center, angle * 4).addScalar(0.5), + base.clone().rotateAround(center, angle * 0).addScalar(0.5) + ]; + + for (let i = 0; i < 12; i++) { // 12 faces for a dodecahedron + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[4].x + u) / tileDimension.x, (baseUVs[4].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y + ); + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.font = `bold ${tileSize / 3}px Arial`; + ctx.fillStyle = textColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + + g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + dodecahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { - const dodecahedronTransparentGeometry = new THREE.DodecahedronGeometry(2) // Size of the dodecahedron + const dodedodecahedronTransaprentGeometry = new THREE.DodecahedronGeometry(2) // Size of the tetrahedron const wireframe = new THREE.WireframeGeometry( - dodecahedronTransparentGeometry, + dodedodecahedronTransaprentGeometry, ) const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, @@ -1508,7 +1816,7 @@ define([ const line = new THREE.LineSegments(wireframe, lineMaterial) dodecahedron = line } else if (tempImage) { - const boxGeo = new THREE.DodecahedronGeometry(2) + const dodecaGeo = new THREE.DodecahedronGeometry(2) const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) @@ -1517,60 +1825,69 @@ define([ const material = new THREE.MeshPhongMaterial({ map: texture }) // Create cube mesh with the material - dodecahedron = new THREE.Mesh(boxGeo, material) + dodecahedron = new THREE.Mesh(dodecaGeo, material) } else { - const dodecahedronGeometry = new THREE.DodecahedronGeometry(2) // Size of the dodecahedron + const dodecahedronGeometry = new THREE.DodecahedronGeometry(2) // Size of the tetrahedron - const tetraMaterial = new THREE.MeshStandardMaterial({ + const dodecaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, }) - dodecahedron = new THREE.Mesh(dodecahedronGeometry, tetraMaterial) + dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial) } dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y dodecahedron.castShadow = true scene.add(dodecahedron) - // Vertices -const r = (1 + Math.sqrt(5)) / 2; -const t = 1 / r; -const verticesDodeca = [ - new CANNON.Vec3(-1, -1, -1), new CANNON.Vec3(-1, -1, 1), - new CANNON.Vec3(-1, 1, -1), new CANNON.Vec3(-1, 1, 1), - new CANNON.Vec3(1, -1, -1), new CANNON.Vec3(1, -1, 1), - new CANNON.Vec3(1, 1, -1), new CANNON.Vec3(1, 1, 1), - new CANNON.Vec3(0, -t, -r), new CANNON.Vec3(0, -t, r), - new CANNON.Vec3(0, t, -r), new CANNON.Vec3(0, t, r), - new CANNON.Vec3(-t, -r, 0), new CANNON.Vec3(-t, r, 0), - new CANNON.Vec3(t, -r, 0), new CANNON.Vec3(t, r, 0), - new CANNON.Vec3(-r, 0, -t), new CANNON.Vec3(r, 0, -t), - new CANNON.Vec3(-r, 0, t), new CANNON.Vec3(r, 0, t) + const t = (1 + Math.sqrt(5)) / 2; +const r = 1 / t; +const scaleFactor = 1; + +const vertices = [ + new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(0, -r, -t).scale(scaleFactor), + new CANNON.Vec3(0, -r, t).scale(scaleFactor), + new CANNON.Vec3(0, r, -t).scale(scaleFactor), + new CANNON.Vec3(0, r, t).scale(scaleFactor), + new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(-r, t, 0).scale(scaleFactor), + new CANNON.Vec3(r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(r, t, 0).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(-t, 0, r).scale(scaleFactor), + new CANNON.Vec3(t, 0, r).scale(scaleFactor) ]; -// Faces -const facesDodeca = [ - [3, 11, 7], [3, 7, 15], [3, 15, 13], - [7, 19, 17], [7, 17, 6], [7, 6, 15], - [17, 4, 8], [17, 8, 10], [17, 10, 6], - [8, 0, 16], [8, 16, 2], [8, 2, 10], - [0, 12, 1], [0, 1, 18], [0, 18, 16], - [6, 10, 2], [6, 2, 13], [6, 13, 15], - [2, 16, 18], [2, 18, 3], [2, 3, 13], - [18, 1, 9], [18, 9, 11], [18, 11, 3], - [4, 14, 12], [4, 12, 0], [4, 0, 8], - [11, 9, 5], [11, 5, 19], [11, 19, 7], - [19, 5, 14], [19, 14, 4], [19, 4, 17], - [1, 12, 14], [1, 14, 5], [1, 5, 9] +const indices = [ + [3, 11, 7], [3, 7, 15], [3, 15, 13], + [7, 19, 17], [7, 17, 6], [7, 6, 15], + [17, 4, 8], [17, 8, 10], [17, 10, 6], + [8, 0, 16], [8, 16, 2], [8, 2, 10], + [0, 12, 1], [0, 1, 18], [0, 18, 16], + [6, 10, 2], [6, 2, 13], [6, 13, 15], + [2, 16, 18], [2, 18, 3], [2, 3, 13], + [18, 1, 9], [18, 9, 11], [18, 11, 3], + [4, 14, 12], [4, 12, 0], [4, 0, 8], + [11, 9, 5], [11, 5, 19], [11, 19, 7], + [19, 5, 14], [19, 14, 4], [19, 4, 17], + [1, 12, 14], [1, 14, 5], [1, 5, 9] ]; -// Create a ConvexPolyhedron shape from the vertices and faces -const dodecahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesDodeca, - faces: facesDodeca -}); - + // Create a ConvexPolyhedron shape from the vertices and faces + const dodecahedronShape = new CANNON.ConvexPolyhedron({ + vertices: vertices, + faces: indices, + }) let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared @@ -1600,6 +1917,10 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ diceArray.push([dodecahedron, dodecahedronBody, 'dodeca']) } + + + + function createIcosahedron( sharedColor, ifNumbers, @@ -1611,8 +1932,7 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ ) { let icosahedron let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent + let tempTransparent = ifTransparent == null ? toggleTransparent : ifTransparent let tempImage = ifImage == null ? showImage : ifImage if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 5) @@ -1642,7 +1962,7 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ [3, 1, 4], [2, 3, 4], ] - for (let i = 0; i < 4; i++) { + for (let i = 0; i < 20; i++) { let u = i % tileDimension.x let v = Math.floor(i / tileDimension.x) uvs.push( @@ -1656,7 +1976,7 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ ctx.textAlign = 'center' ctx.textBaseline = 'middle' - ctx.font = `bold 150px Arial` + ctx.font = `bold 175px Arial` ctx.fillStyle = textColor // ctx.fillText( // i + 1, @@ -1666,18 +1986,11 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ let aStep = (Math.PI * 2) / 3 let yAlign = Math.PI * 0.5 let tileQuarter = tileSize * 0.25 - for (let j = 0; j < 3; j++) { - ctx.save() - ctx.translate( - (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, - c.height - - (v + 0.5) * tileSize + - Math.sin(j * aStep - yAlign) * tileQuarter, - ) - ctx.rotate((j * Math.PI * 2) / 3) - ctx.fillText(arrOfNums[i][j], 0, 0) - ctx.restore() - } + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize, + ) } g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) @@ -1716,14 +2029,13 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ } else { const icosahedronGeometry = new THREE.IcosahedronGeometry(2) // Size of the icosahedron - const tetraMaterial = new THREE.MeshStandardMaterial({ + const icosaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, }) - icosahedron = new THREE.Mesh(icosahedronGeometry, tetraMaterial) + icosahedron = new THREE.Mesh(icosahedronGeometry, icosaMaterial) } - icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y icosahedron.castShadow = true scene.add(icosahedron) @@ -1731,13 +2043,14 @@ const dodecahedronShape = new CANNON.ConvexPolyhedron({ // Vertices // Vertices const t = (1 + Math.sqrt(5)) / 2; +const scaleFactor = 0.7; const verticesIcosa = [ - new CANNON.Vec3(-1, t, 0), new CANNON.Vec3(1, t, 0), - new CANNON.Vec3(-1, -t, 0), new CANNON.Vec3(1, -t, 0), - new CANNON.Vec3(0, -1, t), new CANNON.Vec3(0, 1, t), - new CANNON.Vec3(0, -1, -t), new CANNON.Vec3(0, 1, -t), - new CANNON.Vec3(t, 0, -1), new CANNON.Vec3(t, 0, 1), - new CANNON.Vec3(-t, 0, -1), new CANNON.Vec3(-t, 0, 1) + new CANNON.Vec3(-1, t, 0).scale(scaleFactor), new CANNON.Vec3(1, t, 0).scale(scaleFactor), + new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), new CANNON.Vec3(1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(0, -1, t).scale(scaleFactor), new CANNON.Vec3(0, 1, t).scale(scaleFactor), + new CANNON.Vec3(0, -1, -t).scale(scaleFactor), new CANNON.Vec3(0, 1, -t).scale(scaleFactor), + new CANNON.Vec3(t, 0, -1).scale(scaleFactor), new CANNON.Vec3(t, 0, 1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), new CANNON.Vec3(-t, 0, 1).scale(scaleFactor) ]; // Faces @@ -1767,6 +2080,7 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ }) if (tempShowNumbers) { icosahedronBody.addEventListener('sleep', () => { + console.log("icosa going to sleep"); sleepCounter++; getTetraScore(icosahedron) }) @@ -1822,6 +2136,9 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ for (let i = 0; i < dices.dodeca; i++) { createDodecahedron() } + for (let i = 0; i < dices.deca; i++) { + createDecahedron() + } for (let i = 0; i < dices.icosa; i++) { createIcosahedron() } @@ -1884,7 +2201,7 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ // break // } } - lastRoll += faceVectors[minInd].face + ' +' + lastRoll += faceVectors[minInd].face + ' + ' presentScore += faceVectors[minInd].face updateElements() } @@ -1919,7 +2236,7 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ faceVector.vector.applyEuler(body.rotation) if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + ' +' + lastRoll += faceVector.face + ' + ' presentScore += faceVector.face updateElements() break @@ -1948,9 +2265,8 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ for (const faceVector of faceVectors) { faceVector.vector.applyEuler(body.rotation) - console.log(faceVector.vector.y) if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + ' +' + lastRoll += faceVector.face + ' + ' presentScore += faceVector.face updateElements() break @@ -2011,7 +2327,8 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ break case 'default': groundMesh.material.needsUpdate = true - groundMesh.material.color.setHex(0x425eff) + groundMesh.material.color.setHex(0xD3D3D3) + console.log(groundMesh.material.color) groundMesh.material.wireframe = false groundMesh.material.map = null groundBody.material.friction = 1 diff --git a/activities/3DVolume.activity/js/palettes/colorpalette.html b/activities/3DVolume.activity/js/palettes/colorpalette.html deleted file mode 100644 index 93e4e1f56..000000000 --- a/activities/3DVolume.activity/js/palettes/colorpalette.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/colorpalettefill.html b/activities/3DVolume.activity/js/palettes/colorpalettefill.html new file mode 100644 index 000000000..a3506c8f0 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.html @@ -0,0 +1,28 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ \ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/colorpalette.js b/activities/3DVolume.activity/js/palettes/colorpalettefill.js similarity index 90% rename from activities/3DVolume.activity/js/palettes/colorpalette.js rename to activities/3DVolume.activity/js/palettes/colorpalettefill.js index f50203215..ee6f4f194 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalette.js +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.js @@ -1,6 +1,6 @@ define([ "sugar-web/graphics/palette", - "text!activity/palettes/colorpalette.html", + "text!activity/palettes/colorpalettefill.html", ], function (palette, template) { var colorpalette = {}; colorpalette.ColorPalette = function (invoker, primaryText) { @@ -8,17 +8,18 @@ define([ this.getPalette().id = "color-palette"; var containerElem = document.createElement("div"); + containerElem.innerHTML = template; this.setContent([containerElem]); let that = this; - const colors = document.querySelectorAll(".color"); + const colors = document.querySelectorAll(".color-fill"); colors.forEach((color) => { color.addEventListener("click", function () { const selectedColor = this.style.backgroundColor; - const colorChangeEvent = new CustomEvent("color-selected", { + const colorChangeEvent = new CustomEvent("color-selected-fill", { detail: { color: selectedColor }, }); document.dispatchEvent(colorChangeEvent); diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.html b/activities/3DVolume.activity/js/palettes/colorpalettetext.html new file mode 100644 index 000000000..0599bcb21 --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.html @@ -0,0 +1,30 @@ + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.js b/activities/3DVolume.activity/js/palettes/colorpalettetext.js new file mode 100644 index 000000000..40fb4d57e --- /dev/null +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.js @@ -0,0 +1,51 @@ +define([ + "sugar-web/graphics/palette", + "text!activity/palettes/colorpalettetext.html", + ], function (palette, template) { + var colorpalette = {}; + colorpalette.ColorPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "color-palette"; + + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; + + this.setContent([containerElem]); + + let that = this; + + const colors = document.querySelectorAll(".color-text"); + colors.forEach((color) => { + color.addEventListener("click", function () { + const selectedColor = this.style.backgroundColor; + const colorChangeEvent = new CustomEvent("color-selected-text", { + detail: { color: selectedColor }, + }); + document.dispatchEvent(colorChangeEvent); + that.popDown(); + }); + }); + }; + + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + colorpalette.ColorPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); + + return colorpalette; + }); + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html index 19fcfeeee..7ebe78215 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.html +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -1,4 +1,6 @@
- + + +
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.html b/activities/3DVolume.activity/js/palettes/zoompalette.html index aa562e92e..14cb9cd30 100644 --- a/activities/3DVolume.activity/js/palettes/zoompalette.html +++ b/activities/3DVolume.activity/js/palettes/zoompalette.html @@ -1,4 +1,7 @@
- + + + +
\ No newline at end of file diff --git a/activities/3DVolume.activity/js/buffer.js b/activities/3DVolume.activity/lib/buffer.js similarity index 100% rename from activities/3DVolume.activity/js/buffer.js rename to activities/3DVolume.activity/lib/buffer.js diff --git a/activities/3DVolume.activity/js/cannon.js b/activities/3DVolume.activity/lib/cannon.js similarity index 100% rename from activities/3DVolume.activity/js/cannon.js rename to activities/3DVolume.activity/lib/cannon.js diff --git a/activities/3DVolume.activity/js/debug.js b/activities/3DVolume.activity/lib/debug.js similarity index 100% rename from activities/3DVolume.activity/js/debug.js rename to activities/3DVolume.activity/lib/debug.js diff --git a/activities/3DVolume.activity/js/orbit.js b/activities/3DVolume.activity/lib/orbit.js similarity index 100% rename from activities/3DVolume.activity/js/orbit.js rename to activities/3DVolume.activity/lib/orbit.js diff --git a/activities/3DVolume.activity/js/three.js b/activities/3DVolume.activity/lib/three.js similarity index 100% rename from activities/3DVolume.activity/js/three.js rename to activities/3DVolume.activity/lib/three.js From f0e750c617e60edd2cbc5309b6136833a1b3e11b Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sat, 15 Jun 2024 23:45:51 +0530 Subject: [PATCH 03/60] changes --- activities.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activities.json b/activities.json index e7cf5803e..9cd7fc751 100644 --- a/activities.json +++ b/activities.json @@ -1,5 +1,5 @@ [ - {"id": "org.sugarlabs.3DVolume", "name": "3D Volume Activity", "version": 1, "directory": "activities/3DVolume.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, + {"id": "org.sugarlabs.3DVolume", "name": "3D Volume", "version": 1, "directory": "activities/3DVolume.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, {"id": "org.olpcfrance.XmasLights", "name": "Xmas Lights", "version": 1, "directory": "activities/XmasLights.activity", "icon": "activity/activity-icon.svg", "favorite": false, "activityId": null}, {"id": "org.olpcfrance.DollarStreet", "name": "Dollar Street", "version": 1, "directory": "activities/DollarStreet.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, {"id": "org.sugarlabs.Tangram", "name": "Tangram", "version": 1, "directory": "activities/Tangram.activity", "icon": "activity/activity-icon.svg", "favorite": true, "activityId": null}, From 62f008e79d435baf8c593118b71f6d07dbf376c0 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 23 Jun 2024 00:38:42 +0530 Subject: [PATCH 04/60] latest --- activities/3DVolume.activity/css/activity.css | 181 +- activities/3DVolume.activity/icons/minus.svg | 7 +- activities/3DVolume.activity/images/1.png | Bin 15332 -> 0 bytes activities/3DVolume.activity/images/1.svg | 12 - activities/3DVolume.activity/images/2.png | Bin 18232 -> 0 bytes activities/3DVolume.activity/images/2.svg | 12 - activities/3DVolume.activity/images/3.png | Bin 17334 -> 0 bytes activities/3DVolume.activity/images/4.png | Bin 15513 -> 0 bytes activities/3DVolume.activity/images/5.png | Bin 16851 -> 0 bytes activities/3DVolume.activity/images/6.png | Bin 18101 -> 0 bytes activities/3DVolume.activity/images/7.png | Bin 16196 -> 0 bytes activities/3DVolume.activity/images/8.png | Bin 19142 -> 0 bytes activities/3DVolume.activity/images/image.png | Bin 169571 -> 0 bytes .../3DVolume.activity/images/red-board.jpeg | Bin 1526 -> 0 bytes activities/3DVolume.activity/index.html | 264 +- activities/3DVolume.activity/js/activity.js | 2919 ++++++++++------- .../3DVolume.activity/js/fonts/robot.json | 1 - 17 files changed, 1943 insertions(+), 1453 deletions(-) delete mode 100644 activities/3DVolume.activity/images/1.png delete mode 100644 activities/3DVolume.activity/images/1.svg delete mode 100644 activities/3DVolume.activity/images/2.png delete mode 100644 activities/3DVolume.activity/images/2.svg delete mode 100644 activities/3DVolume.activity/images/3.png delete mode 100644 activities/3DVolume.activity/images/4.png delete mode 100644 activities/3DVolume.activity/images/5.png delete mode 100644 activities/3DVolume.activity/images/6.png delete mode 100644 activities/3DVolume.activity/images/7.png delete mode 100644 activities/3DVolume.activity/images/8.png delete mode 100644 activities/3DVolume.activity/images/image.png delete mode 100644 activities/3DVolume.activity/images/red-board.jpeg delete mode 100644 activities/3DVolume.activity/js/fonts/robot.json diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index 080a2de2f..72f1d5c36 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -2,7 +2,7 @@ background-image: url(../activity/activity-icon.svg); } #main-toolbar #network-button { - background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); } .toolbar hr { vertical-align: bottom; @@ -18,33 +18,33 @@ } #private-button { - background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); - width: 47px; - height: 47px; - margin: 4px 2px; - color: white; - color: transparent; - background-color: transparent; - background-position: center; - background-repeat: no-repeat; - background-size: contain; - border: 0; - border-radius: 5.5px; + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-home.svg); + width: 47px; + height: 47px; + margin: 4px 2px; + color: white; + color: transparent; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + background-size: contain; + border: 0; + border-radius: 5.5px; } #shared-button { - background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg); - width: 47px; - height: 47px; - margin: 4px 2px; - color: white; - color: transparent; - background-color: transparent; - background-position: center; - background-repeat: no-repeat; - background-size: contain; - border: 0; - border-radius: 5.5px; + background-image: url(../lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg); + width: 47px; + height: 47px; + margin: 4px 2px; + color: white; + color: transparent; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + background-size: contain; + border: 0; + border-radius: 5.5px; } body { @@ -55,25 +55,24 @@ body { outline: none; } #volume-button { - background-image:url(../icons/cube_solid.svg); - outline:none; + background-image: url(../icons/cube_solid.svg); + outline: none; } #main-toolbar #clear-button { background-image: url(../icons/minus.svg); - outline:none; + outline: none; } #clear-button.active { - background-color: #D3D3D3; + background-color: #d3d3d3; } - #main-toolbar #solid-button { background-image: url(../icons/cube_solid.svg); height: 100px; width: 65px; background-size: cover; background-repeat: no-repeat; - outline:none; + outline: none; } #number-button { height: 65px; @@ -90,7 +89,7 @@ body { background-size: cover; background-repeat: no-repeat; background-image: url(../icons/glass.svg); - outline:none; + outline: none; } #throw-button { height: 45px; @@ -98,7 +97,7 @@ body { background-size: cover; background-repeat: no-repeat; background-image: url(../icons/throw.svg); - outline:none; + outline: none; } #transparent-button { margin: 0; @@ -119,71 +118,71 @@ body { background-size: contain; background-repeat: no-repeat; background-image: url(../icons/default_volume.svg); - outline:none; + outline: none; border-radius: 5px; } -#colors-button-fill{ +#colors-button-fill { display: none; } .button-background { - background-color:#000; + background-color: #000; } #cube-button { - background-image:url(../icons/cube.svg); - outline:none; + background-image: url(../icons/cube.svg); + outline: none; background-repeat: no-repeat; background-size: cover; } #tetra-button { - background-image:url(../icons/tetra.svg); - outline:none; + background-image: url(../icons/tetra.svg); + outline: none; background-repeat: no-repeat; background-size: cover; } #octa-button { - background-image:url(../icons/octa.svg); - outline:none; + background-image: url(../icons/octa.svg); + outline: none; background-repeat: no-repeat; background-size: cover; } #dodeca-button { - background-image:url(../icons/dodeca.svg); - outline:none; + background-image: url(../icons/dodeca.svg); + outline: none; background-repeat: no-repeat; background-size: cover; } #deca-button { - background-image:url(../icons/deca.svg); - outline:none; + background-image: url(../icons/deca.svg); + outline: none; background-repeat: no-repeat; background-size: cover; } #icosa-button { - background-image:url(../icons/icosa.svg); - outline:none; + background-image: url(../icons/icosa.svg); + outline: none; margin-left: 10px; background-repeat: no-repeat; background-size: cover; } .active { - background-color: #656565 !important; + background-color: #656565 !important; } #main-toolbar #help-button { - background-image: url(../icons/help.svg); + background-image: url(../icons/help.svg); } .vertical-line { - width: 5px; - height: 55px; - background-color: white; + width: 5px; + height: 55px; + background-color: white; transform: translateX(-50%); margin-left: 5px; margin-right: 5px; } .selector { - display:flex; + display: flex; } .bg-select { /* padding: 10px; */ @@ -217,12 +216,13 @@ body { background-image: url(../icons/image.svg); } body { - margin: 0; - color: var(--color-text); - background-color: var(--color-bg); - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + color: var(--color-text); + background-color: var(--color-bg); + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } body { margin: 0; @@ -251,7 +251,7 @@ body { #score-container { position: absolute; - height:70vh; + height: 70vh; width: 70vh; top: 70px; left: 20px; @@ -275,7 +275,6 @@ body { font-size: 1.5rem; } - .color-palette { display: flex; width: 370px; /* ugly hack */ @@ -283,24 +282,23 @@ body { border: #808080 2px solid; } - #color-grid-fill { display: grid; - grid-template-columns: repeat(3, 50px); + grid-template-columns: repeat(3, 50px); grid-gap: 10px; - margin-left:5px; - margin-bottom:5px; - margin-top:5px; - margin-right:5px; + margin-left: 5px; + margin-bottom: 5px; + margin-top: 5px; + margin-right: 5px; } #color-grid-text { display: grid; grid-template-columns: repeat(3, 50px); grid-gap: 10px; - margin-left:5px; - margin-bottom:5px; - margin-top:5px; - margin-right:5px; + margin-left: 5px; + margin-bottom: 5px; + margin-top: 5px; + margin-right: 5px; } .slider-container { @@ -346,21 +344,23 @@ body { } #right-button { - background-image: url(../icons/right-arrow.svg); + background-image: url(../icons/go-right.svg); } #up-button { - background-image: url(../icons/up-arrow.svg); + background-image: url(../icons/go-right.svg); + transform: rotate(-90deg); } #down-button { - background-image: url(../icons/down-arrow.svg); + background-image: url(../icons/go-left.svg); + transform: rotate(-90deg); } #left-button { - background-image: url(../icons/left-arrow.svg); + background-image: url(../icons/go-left.svg); } .arrow-container { - width:50%; + width: 50%; display: flex; flex-direction: column; align-items: center; @@ -376,19 +376,17 @@ body { margin: 5px 0; } - .left-right-buttons { display: flex; justify-content: space-between; /* background-color:pink; */ - width:80%; + width: 80%; } .left-right-buttons .arrow-button { margin: 0 5px; } - .color-fill { width: 45px; /* Decreased circle size */ height: 45px; /* Decreased circle size */ @@ -397,7 +395,6 @@ body { border: white solid 2px; } - .color-text { width: 45px; /* Decreased circle size */ height: 45px; /* Decreased circle size */ @@ -410,24 +407,24 @@ body { .introjs-overlay { background-color: #000 !important; - opacity: .8 !important; + opacity: 0.8 !important; } .introjs-tooltip { - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif!important; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; border-radius: 6px !important; padding: 2px 1px !important; font-size: 14px !important; } -.introjs-helperLayer{ +.introjs-helperLayer { background: inherit !important; opacity: 0.6 !important; } .customTooltip .introjs-tooltip-header { - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; - color : #ffffff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #ffffff; text-shadow: none; /* background-color: #808080; */ margin: 0; @@ -481,7 +478,7 @@ body { .customTooltip .introjs-tooltipbuttons { display: flex; - flex-wrap:wrap; + flex-wrap: wrap; justify-content: center; align-items: center; cursor: pointer; @@ -505,8 +502,8 @@ body { } .customTooltip .introjs-button:focus { - -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); - box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); border: 0px; /* background-color: #808080 !important; */ } @@ -514,11 +511,11 @@ body { .customTooltip .introjs-disabled { color: black !important; border: 0px; - opacity: .65; + opacity: 0.65; } .customTooltip .introjs-disabled:focus { - -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); - box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); border: 0px; } diff --git a/activities/3DVolume.activity/icons/minus.svg b/activities/3DVolume.activity/icons/minus.svg index 0ecba4fb5..2964a6483 100644 --- a/activities/3DVolume.activity/icons/minus.svg +++ b/activities/3DVolume.activity/icons/minus.svg @@ -1 +1,6 @@ - \ No newline at end of file + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/images/1.png b/activities/3DVolume.activity/images/1.png deleted file mode 100644 index e838f2420fc6e7a4e115b64eb636455354746f30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15332 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 - - - - - - - - - - - \ No newline at end of file diff --git a/activities/3DVolume.activity/images/2.png b/activities/3DVolume.activity/images/2.png deleted file mode 100644 index 4462d6b8fdf9140036b09e05981401dcaad09566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18232 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 U5@58#N&o-=07*qoM6N<$f*(-5;{X5v diff --git a/activities/3DVolume.activity/images/2.svg b/activities/3DVolume.activity/images/2.svg deleted file mode 100644 index 6c1eeaf5a..000000000 --- a/activities/3DVolume.activity/images/2.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/activities/3DVolume.activity/images/3.png b/activities/3DVolume.activity/images/3.png deleted file mode 100644 index f9a747f91cbc4aaca9a4d6c7c0cfba49c98a193a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17334 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 diff --git a/activities/3DVolume.activity/images/4.png b/activities/3DVolume.activity/images/4.png deleted file mode 100644 index 7f594fef50263388c9f4e5e468b42d75a62c25cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15513 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 diff --git a/activities/3DVolume.activity/images/5.png b/activities/3DVolume.activity/images/5.png deleted file mode 100644 index 8b84d7cde52b7cf78d61a4eb4fd288ffaa660b73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16851 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 R0y^M92s2yLnoI7F{||P|6wd$v diff --git a/activities/3DVolume.activity/images/8.png b/activities/3DVolume.activity/images/8.png deleted file mode 100644 index 845302e342f84973f2904cffd575a521c213b17c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19142 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 z 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

yp2?;!-q$B|p%HG#u)ahNuxS|F!{+ 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 F?w>>v|J?up diff --git a/activities/3DVolume.activity/images/red-board.jpeg b/activities/3DVolume.activity/images/red-board.jpeg deleted file mode 100644 index dd9cc16c72e91a53a8c6f5890f7dd2597ebe06fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1526 zcmYjRe@qj16u(kwYu8eWz_@OmP$~9WAhvgcRB@uv(t~!iMfu`QCf) z-sipd`MhrZvh^4$#cd87BoaZ82z;QeU!h@WyF{u~N@X&+Oe$AOrG>9brj*L7)s>YM z>K!|(w1(;`jed8<&Kh%#9x*jGHom?S-G|mU8yXr-BDq|yR4Pl9DwPSYfKA{uH-N)m zO#E*Hc;sz@K`_0%HWw^g-$H5`bW`-ESfqoB)FQE3v~>^CK#*7j4)p&K6^S89@iySA zD&;+|xIjPZA~XM%;CR(1f{Kc_NyWfdseaWiqyz^{&HCwZmyVXPNY+EOB?DSPv8AiH@o3H}$Yn3gh=WFk%T$j^xRYKYH$< zjFPgA3FVyokoWd@AmB(Q>jM4BWc-8qD4kFIF{ryp;geA&o%Mq(i>xmm&1^DQhhFO< z@RR!^xOJLB1UP*{k7e*sPsHrkVj2Rs)n}y+12=pyN^1|TIh$s-g{u~nbIQzXXmu`k z5w*B{1Wr;n&>MHI$F3qw{oPIG9b2z%KAH(J^-oO(ZkkP?<)q)`CiCUw;#C9p(}^>V zc2K|+%_ds4Iz$MOovEeR`Lj-=uieemvda&jhlh-IA7>p#gXRGI;h-eYKRrzu2U2~B zMk#C2i;eI`6g25?b2u{^k1ie= z09Bcpx=86?V-ip9U@T5lpD$n6w%y-&a?bKiY%Ly(yU7I=gGaAYifVYz7U6c`2y0kaJeX1?Z^jfX$Wh6e$aft^m{008&Ho+aG7O7KsD zqFQX8g3L||Jx=<-fR5HYhz%WxJ!H&pc=mDL4%w+P#kH#NZ15AX3zl)3qRE~Sz^&EG z>3O3_CY%<;o8=bMZ?= z;@0RhZ}M-eBKHuqAnbUzIaz~Ry6Ydv7Sx{?Pd%;*^h*@ayy#rOw|C~T(Rf%*FX;d diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 9a645660d..c808c8351 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -1,112 +1,164 @@ + + + 3D Volume Activity + + + + + + + + + + + + + + + + + + +

+
+ + + +
+ + + + + + + + +
+ + + +
+ + + + + + + + + + + +
+ + + + +
- - -3D Volume Activity - - - - - - - - - - - - - - - - - - - - - - -
-
- - - -
- - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
- - - - - - -
- - - - - - - - - -
- - -
- - -
-
- -
- -
- -
- - + + + + + + +
+ +
-
- -
-

- - -
- - - - - - +
+ +
+ +
+ +
+ + +
+ +
+
+

+
+ diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 87cf9cc8a..68a0fdffc 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1,14 +1,14 @@ define([ - 'sugar-web/activity/activity', - 'sugar-web/env', - './palettes/bgpalette', - './palettes/volumepalette', - './palettes/colorpalettefill', - './palettes/colorpalettetext', - './palettes/zoompalette', - 'sugar-web/graphics/presencepalette', - 'tutorial', - 'sugar-web/graphics/journalchooser', + "sugar-web/activity/activity", + "sugar-web/env", + "./palettes/bgpalette", + "./palettes/volumepalette", + "./palettes/colorpalettefill", + "./palettes/colorpalettetext", + "./palettes/zoompalette", + "sugar-web/graphics/presencepalette", + "tutorial", + "sugar-web/graphics/journalchooser", "sugar-web/datastore", ], function ( activity, @@ -24,107 +24,107 @@ define([ datastore ) { // Function to change the background color based on the provided string - requirejs(['domReady!'], function (doc) { - activity.setup() + requirejs(["domReady!"], function (doc) { + activity.setup(); // Link presence palette var paletteBg = new bgpalette.BgPalette( - document.getElementById('bg-button'), - undefined, - ) - paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper) + document.getElementById("bg-button"), + undefined + ); + paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper); function changeBoardBackgroundHelper(selectedBoard) { if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), - action: 'changeBg', + action: "changeBg", content: selectedBoard, - }) + }); } - changeBoardBackground(selectedBoard) + changeBoardBackground(selectedBoard); } var paletteVolume = new volumepalette.VolumePalette( - document.getElementById('volume-button'), - undefined, - ) + document.getElementById("volume-button"), + undefined + ); var paletteZoom = new zoompalette.ZoomPalette( - document.getElementById('zoom-button'), - undefined, - ) + document.getElementById("zoom-button"), + undefined + ); var paletteColorFill = new colorpaletteFill.ColorPalette( - document.getElementById('color-button-fill'), - undefined, - ) + document.getElementById("color-button-fill"), + undefined + ); var paletteColorText = new colorpaletteText.ColorPalette( - document.getElementById('color-button-text'), - undefined, - ) - - let presentScore = 0 - let lastRoll = '' - let diceArray = [] - let showNumbers = false - let showImage = false + document.getElementById("color-button-text"), + undefined + ); + + let presentScore = 0; + let lastRoll = ""; + let diceArray = []; + let journalDiceArray = []; + let showNumbers = false; + let showImage = false; let imageData; let presentColor; - let textColor = '#ffffff' - var currentenv - let removeVolume = false - let transparent = false - let toggleTransparent = false - let defaultVolume = true + let textColor = "#ffffff"; + var currentenv; + let removeVolume = false; + let transparent = false; + let toggleTransparent = false; + let defaultVolume = true; - var defaultButton = document.getElementById('default-button') - defaultButton.classList.toggle('active') + var defaultButton = document.getElementById("default-button"); + defaultButton.classList.toggle("active"); env.getEnvironment(function (err, environment) { - currentenv = environment + currentenv = environment; presentColor = currentenv.user.colorvalue.fill != null ? currentenv.user.colorvalue.fill - : presentColor - - scene.background = new THREE.Color("#A9A9A9") - console.log(presentColor) + : presentColor; + + scene.background = new THREE.Color("#A9A9A9"); + console.log(presentColor); - textColor = currentenv.user.colorvalue.stroke != null ? currentenv.user.colorvalue.stroke - : textColor + : textColor; - document.getElementById('color-button-fill').style.backgroundColor = presentColor - document.getElementById('color-button-text').style.backgroundColor = textColor - - + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; + document.getElementById("color-button-text").style.backgroundColor = + textColor; if (environment.sharedId) { - console.log('Shared instance') + console.log("Shared instance"); presence = activity.getPresenceObject(function (error, network) { - network.onDataReceived(onNetworkDataReceived) - }) + network.onDataReceived(onNetworkDataReceived); + }); } - }) + }); var onNetworkDataReceived = function (msg) { if (presence.getUserInfo().networkId === msg.user.networkId) { - return + return; } - if (msg.action == 'throw') { - throwDice() + if (msg.action == "throw") { + throwDice(); } - if (msg.action == 'changeBg') { - changeBoardBackground(msg.content) + if (msg.action == "changeBg") { + changeBoardBackground(msg.content); } - if (msg.action == 'resetScore') { - presentScore = 0 - totalScoreElement.textContent = 0 - lastRoll = '' - lastRollElement.textContent = '' + if (msg.action == "resetScore") { + presentScore = 0; + totalScoreElement.textContent = 0; + lastRoll = ""; + lastRollElement.textContent = ""; } switch (msg.content.shape) { - case 'cube': + case "cube": createCube( msg.content.color, msg.content.ifNumbers, @@ -133,9 +133,9 @@ define([ msg.content.zCoordinateShared, msg.content.ifImage, msg.content.sharedImageData - ) - break - case 'octa': + ); + break; + case "octa": createOctahedron( msg.content.color, msg.content.ifNumbers, @@ -144,10 +144,10 @@ define([ msg.content.zCoordinateShared, msg.content.ifImage, msg.content.sharedImageData - ) - break - case 'tetra': - console.log(msg.content.ifImage) + ); + break; + case "tetra": + console.log(msg.content.ifImage); createTetrahedron( msg.content.color, msg.content.ifNumbers, @@ -156,182 +156,322 @@ define([ msg.content.zCoordinateShared, msg.content.ifImage, msg.content.sharedImageData - ) - break - case 'dodeca': - createDodecahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData - ) - break - case 'deca': - createDecahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData - ) - break - case 'icosa': - createIcosahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData - ) - break + ); + break; + case "dodeca": + createDodecahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ); + break; + case "deca": + createDecahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ); + break; + + case "icosa": + createIcosahedron( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData + ); + break; } - } + }; - // Launch tutorial document - .getElementById('help-button') - .addEventListener('click', function (e) { - tutorial.start() - }) - - document.addEventListener('color-selected-fill', function (event) { - const selectedColor = event.detail.color; - presentColor = selectedColor; - document.getElementById('color-button-fill').style.backgroundColor = presentColor; - updateSliders(selectedColor); + .getElementById("stop-button") + .addEventListener("click", function (event) { + for (let i = 0; i < diceArray.length; i++) { + journalDiceArray.push([ + diceArray[i][2], + diceArray[i][1].position, + diceArray[i][1].quaternion, + diceArray[i][5], + diceArray[i][6], + diceArray[i][3], + diceArray[i][4], + ]); + } + console.log("writing..."); + var jsonData = JSON.stringify(journalDiceArray); + console.log(jsonData); + activity.getDatastoreObject().setDataAsText(jsonData); + activity.getDatastoreObject().save(function (error) { + if (error === null) { + console.log("write done."); + } else { + console.log("write failed."); + } + }); + }); + + env.getEnvironment(function (err, environment) { + currentenv = environment; + + // Load from datastore + if (!environment.objectId) { + console.log("New instance"); + } else { + activity + .getDatastoreObject() + .loadAsText(function (error, metadata, data) { + if (error == null && data != null) { + data = JSON.parse(data); + for (let i = 0; i < data.length; i++) { + let fillColorStored = data[i][3]; + let textColorStored = data[i][4]; + switch (data[i][0]) { + case "cube": + createCube( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "octa": + createOctahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "tetra": + createTetrahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "deca": + createDecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "dodeca": + createDodecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "icosa": + createIcosahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + default: + // Default case (optional): Handle unexpected values + console.log(`Unexpected shape: ${data[i][0]}`); + break; + } + } + } + }); + } }); - + + // Launch tutorial + document + .getElementById("help-button") + .addEventListener("click", function (e) { + tutorial.start(); + }); + const redSliderFill = document.getElementById("red-slider-fill"); const greenSliderFill = document.getElementById("green-slider-fill"); const blueSliderFill = document.getElementById("blue-slider-fill"); - + let sliderColorFill = { r: 0, g: 0, b: 0 }; - + function rgbToHex(r, g, b) { - return ( - "#" + - ((1 << 24) + (r << 16) + (g << 8) + b) - .toString(16) - .slice(1) - .toUpperCase() - ); + return ( + "#" + + ((1 << 24) + (r << 16) + (g << 8) + b) + .toString(16) + .slice(1) + .toUpperCase() + ); } - + function hexToRgb(hex) { - let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null; + let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; } - + function updateColorDisplayFill() { - const hexColor = rgbToHex(sliderColorFill.r, sliderColorFill.g, sliderColorFill.b); - presentColor = hexColor; - document.getElementById('color-button-fill').style.backgroundColor = presentColor; + const hexColor = rgbToHex( + sliderColorFill.r, + sliderColorFill.g, + sliderColorFill.b + ); + presentColor = hexColor; + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; } - + function updateSlidersFill(color) { - const rgb = color.match(/\d+/g).map(num => parseInt(num, 10)); - redSliderFill.value = rgb[0]; - greenSliderFill.value = rgb[1]; - blueSliderFill.value = rgb[2]; + const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); + redSliderFill.value = rgb[0]; + greenSliderFill.value = rgb[1]; + blueSliderFill.value = rgb[2]; } - + function handleSliderChangeFill() { - sliderColorText = { - r: parseInt(redSliderFill.value), - g: parseInt(greenSliderFill.value), - b: parseInt(blueSliderFill.value), - }; - updateColorDisplayFill(); + sliderColorFill = { + r: parseInt(redSliderFill.value), + g: parseInt(greenSliderFill.value), + b: parseInt(blueSliderFill.value), + }; + updateColorDisplayFill(); } - + redSliderFill.addEventListener("input", handleSliderChangeFill); greenSliderFill.addEventListener("input", handleSliderChangeFill); blueSliderFill.addEventListener("input", handleSliderChangeFill); - - document.addEventListener('color-selected-fill', function (event) { + document.addEventListener("color-selected-fill", function (event) { const selectedColorFill = event.detail.color; presentColor = selectedColorFill; - document.getElementById('color-button-fill').style.backgroundColor = presentColor; + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; updateSlidersFill(selectedColorFill); - }); - - + }); + const redSliderText = document.getElementById("red-slider-text"); + const greenSliderText = document.getElementById("green-slider-text"); + const blueSliderText = document.getElementById("blue-slider-text"); + let sliderColorText = { r: 0, g: 0, b: 0 }; - - const redSliderText = document.getElementById("red-slider-text"); - const greenSliderText = document.getElementById("green-slider-text"); - const blueSliderText = document.getElementById("blue-slider-text"); - - let sliderColorText = { r: 0, g: 0, b: 0 }; - - - function updateColorDisplayText() { - const hexColor = rgbToHex(sliderColorText.r, sliderColorText.g, sliderColorText.b); + function updateColorDisplayText() { + const hexColor = rgbToHex( + sliderColorText.r, + sliderColorText.g, + sliderColorText.b + ); textColor = hexColor; - document.getElementById('color-button-text').style.backgroundColor = textColor; - } - - function updateSlidersText(color) { - const rgb = color.match(/\d+/g).map(num => parseInt(num, 10)); + document.getElementById("color-button-text").style.backgroundColor = + textColor; + } + + function updateSlidersText(color) { + const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); redSliderText.value = rgb[0]; greenSliderText.value = rgb[1]; blueSliderText.value = rgb[2]; - } - - function handleSliderChangeText() { + } + + function handleSliderChangeText() { sliderColorText = { - r: parseInt(redSliderText.value), - g: parseInt(greenSliderText.value), - b: parseInt(blueSliderText.value), + r: parseInt(redSliderText.value), + g: parseInt(greenSliderText.value), + b: parseInt(blueSliderText.value), }; updateColorDisplayText(); - } - - redSliderText.addEventListener("input", handleSliderChangeText); - greenSliderText.addEventListener("input", handleSliderChangeText); - blueSliderText.addEventListener("input", handleSliderChangeText); - - document.addEventListener('color-selected-text', function (event) { - const selectedColorText = event.detail.color; - textColor = selectedColorText; - document.getElementById('color-button-text').style.backgroundColor = textColor; - updateSlidersText(selectedColorFill); -}); - + } - + redSliderText.addEventListener("input", handleSliderChangeText); + greenSliderText.addEventListener("input", handleSliderChangeText); + blueSliderText.addEventListener("input", handleSliderChangeText); + document.addEventListener("color-selected-text", function (event) { + const selectedColorText = event.detail.color; + textColor = selectedColorText; + document.getElementById("color-button-text").style.backgroundColor = + textColor; + updateSlidersText(selectedColorText); + }); + + // document.addEventListener('color-selected-fill', function (event) { + // const selectedColor = event.detail.color; + // presentColor = selectedColor; + // document.getElementById('color-button-fill').style.backgroundColor = presentColor; + // updateSlidersFill(selectedColor); + // }); function updateDice(type, value) { - dices[type] += value - document.getElementById(type).innerHTML = '
' + dices[type] + dices[type] += value; + document.getElementById(type).innerHTML = "
" + dices[type]; } - document.querySelector('#throw-button').addEventListener('click', () => { - throwDice() + document.querySelector("#throw-button").addEventListener("click", () => { + throwDice(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), - action: 'throw', - }) + action: "throw", + }); } - }) + }); // Toggles the dice's transparency // document.querySelector('#solid-button').addEventListener('click', () => { @@ -348,152 +488,157 @@ define([ // Toggles showing numbers on dice - document.querySelector('#number-button').addEventListener('click', () => { - var numberButton = document.getElementById('number-button') - numberButton.classList.toggle('active') - document.getElementById("volume-button").style.backgroundImage = 'url(icons/number_volume.svg)' + document.querySelector("#number-button").addEventListener("click", () => { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/number_volume.svg)"; if (defaultVolume) { - var defaultButton = document.getElementById('default-button') - defaultButton.classList.toggle('active') - defaultVolume = !defaultVolume + var defaultButton = document.getElementById("default-button"); + defaultButton.classList.toggle("active"); + defaultVolume = !defaultVolume; } if (toggleTransparent) { - var transparentButton = document.getElementById('transparent-button') - transparentButton.classList.toggle('active') - toggleTransparent = !toggleTransparent + var transparentButton = document.getElementById("transparent-button"); + transparentButton.classList.toggle("active"); + toggleTransparent = !toggleTransparent; } if (showImage) { - var imageButton1 = document.getElementById('image-button') - imageButton1.classList.toggle('active') - showImage = !showImage + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; } - showNumbers = !showNumbers + showNumbers = !showNumbers; // toggleNumbers(); - }) + }); document - .querySelector('#transparent-button') - .addEventListener('click', () => { - var transparentButton = document.getElementById('transparent-button') + .querySelector("#transparent-button") + .addEventListener("click", () => { + var transparentButton = document.getElementById("transparent-button"); // Toggle the 'active' class on the clear button - transparentButton.classList.toggle('active') - document.getElementById("volume-button").style.backgroundImage = 'url(icons/transparent_volume.svg)' - console.log("it is beign clicked now!!") - console.log(defaultVolume) + transparentButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/transparent_volume.svg)"; + console.log(defaultVolume); if (defaultVolume) { - console.log("it is true") - var defaultButton = document.getElementById('default-button') - defaultButton.classList.toggle('active') - defaultVolume = !defaultVolume + console.log("it is true"); + var defaultButton = document.getElementById("default-button"); + defaultButton.classList.toggle("active"); + defaultVolume = !defaultVolume; } if (showNumbers) { - var numberButton = document.getElementById('number-button') - numberButton.classList.toggle('active') - showNumbers = !showNumbers + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + showNumbers = !showNumbers; } if (showImage) { - var imageButton1 = document.getElementById('image-button') - imageButton1.classList.toggle('active') - showImage = !showImage + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; } - toggleTransparent = !toggleTransparent - }) + toggleTransparent = !toggleTransparent; + }); - document - .querySelector('#default-button') - .addEventListener('click', () => { - var defaultButton = document.getElementById('default-button') - // Toggle the 'active' class on the clear button - defaultButton.classList.toggle('active') - document.getElementById("volume-button").style.backgroundImage = 'url(icons/cube_solid.svg)' + document.querySelector("#default-button").addEventListener("click", () => { + var defaultButton = document.getElementById("default-button"); + // Toggle the 'active' class on the clear button + defaultButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/cube_solid.svg)"; - if (toggleTransparent) { - var transparentButton = document.getElementById('transparent-button') - transparentButton.classList.toggle('active') - toggleTransparent = !toggleTransparent - } + if (toggleTransparent) { + var transparentButton = document.getElementById("transparent-button"); + transparentButton.classList.toggle("active"); + toggleTransparent = !toggleTransparent; + } - if (showNumbers) { - var numberButton = document.getElementById('number-button') - numberButton.classList.toggle('active') - showNumbers = !showNumbers - } - if (showImage) { - var imageButton1 = document.getElementById('image-button') - imageButton1.classList.toggle('active') - showImage = !showImage - } - defaultVolume = !defaultVolume - }) + if (showNumbers) { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + showNumbers = !showNumbers; + } + if (showImage) { + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; + } + defaultVolume = !defaultVolume; + }); + let addShape = { + cube: true, + tetra: false, + octa: false, + dodeca: false, + deca: false, + icosa: false, + }; + document.getElementById("cube-button").classList.toggle("active"); + + const buttons = ["cube", "tetra", "octa", "dodeca", "deca", "icosa"]; + const clearButton = document.getElementById("clear-button"); + const removeButton = document.querySelector("#clear-button"); + const solidButton = document.querySelector("#solid-button"); + + const toggleShape = (shape) => { + buttons.forEach((btn) => { + addShape[btn] = btn === shape; + document + .getElementById(`${btn}-button`) + .classList.toggle("active", btn === shape); + }); + removeVolume = false; + removeButton.classList.remove("active"); + if (transparent) { + transparent = false; + solidButton.style.backgroundImage = "url(icons/cube_solid.svg)"; + toggleTransparency(); + } + }; + + clearButton.addEventListener("click", () => { + clearButton.classList.toggle("active"); + removeVolume = !removeVolume; + buttons.forEach((btn) => { + addShape[btn] = false; + document.getElementById(`${btn}-button`).classList.remove("active"); + }); + }); - let addShape = { - cube: false, - tetra: false, - octa: false, - dodeca: false, - deca: false, - icosa: false - }; - - const buttons = ['cube', 'tetra', 'octa', 'dodeca', 'deca', 'icosa']; - const clearButton = document.getElementById('clear-button'); - const removeButton = document.querySelector('#clear-button'); - const solidButton = document.querySelector('#solid-button'); - - const toggleShape = (shape) => { - buttons.forEach(btn => { - addShape[btn] = btn === shape; - document.getElementById(`${btn}-button`).classList.toggle('active', btn === shape); + removeButton.addEventListener("click", () => { + if (!removeButton.classList.contains("active")) { + buttons.forEach((btn) => { + addShape[btn] = false; + document.getElementById(`${btn}-button`).classList.remove("active"); }); - removeVolume = false; - removeButton.classList.remove('active'); - + removeVolume = true; + removeButton.classList.add("active"); + if (transparent) { transparent = false; - solidButton.style.backgroundImage = 'url(icons/cube_solid.svg)'; + solidButton.style.backgroundImage = "url(icons/cube_solid.svg)"; toggleTransparency(); } - }; - - clearButton.addEventListener('click', () => { - clearButton.classList.toggle('active'); - removeVolume = !removeVolume; - buttons.forEach(btn => { - addShape[btn] = false; - document.getElementById(`${btn}-button`).classList.remove('active'); - }); - }); - - removeButton.addEventListener('click', () => { - if (!removeButton.classList.contains('active')) { - buttons.forEach(btn => { - addShape[btn] = false; - document.getElementById(`${btn}-button`).classList.remove('active'); - }); - removeVolume = true; - removeButton.classList.add('active'); - - if (transparent) { - transparent = false; - solidButton.style.backgroundImage = 'url(icons/cube_solid.svg)'; - toggleTransparency(); - } - } - }); - - buttons.forEach(shape => { - document.getElementById(`${shape}-button`).addEventListener('click', () => { - if (!document.getElementById(`${shape}-button`).classList.contains('active')) { + } + }); + + buttons.forEach((shape) => { + document + .getElementById(`${shape}-button`) + .addEventListener("click", () => { + if ( + !document + .getElementById(`${shape}-button`) + .classList.contains("active") + ) { toggleShape(shape); } }); - }); - - + }); // const imageButton = document.getElementById('image-button') // document @@ -605,146 +750,147 @@ define([ // }) // const totalScoreElement = document.getElementById('score') - const lastRollElement = document.getElementById('roll') + const lastRollElement = document.getElementById("roll"); // Function to update the elements function updateElements() { // totalScoreElement.textContent = presentScore - lastRollElement.textContent = lastRoll.substring(0, lastRoll.length - 2) + '= ' + presentScore; + lastRollElement.textContent = + lastRoll.substring(0, lastRoll.length - 2) + "= " + presentScore; } const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, - }) - renderer.shadowMap.enabled = true + }); + renderer.shadowMap.enabled = true; - let xCoordinate, zCoordinate - const raycaster = new THREE.Raycaster() - const mouse = new THREE.Vector2() - document.querySelector('body').addEventListener('click', onRemoveClick) - document.querySelector('body').addEventListener('click', onAddClick) + let xCoordinate, zCoordinate, yCoordinate; + const raycaster = new THREE.Raycaster(); + const mouse = new THREE.Vector2(); + document.querySelector("body").addEventListener("click", onRemoveClick); + document.querySelector("body").addEventListener("click", onAddClick); function onAddClick(event) { if (!removeVolume) { - var rect = renderer.domElement.getBoundingClientRect() - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera) + raycaster.setFromCamera(mouse, camera); // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children) + var intersects = raycaster.intersectObjects(scene.children); for (let i = 0; i < intersects.length; i++) { - var intersectedObject = intersects[i]?.object - if (intersectedObject.geometry.type == 'PlaneGeometry') { - xCoordinate = intersects[i].point.x - zCoordinate = intersects[i].point.z + var intersectedObject = intersects[i]?.object; + if (intersectedObject.geometry.type == "PlaneGeometry") { + xCoordinate = intersects[i].point.x; + zCoordinate = intersects[i].point.z; - if (addShape['cube']) { - createCube() + if (addShape["cube"]) { + createCube(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'cube', + shape: "cube", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } - } else if (addShape['tetra']) { - createTetrahedron() + } else if (addShape["tetra"]) { + createTetrahedron(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'tetra', + shape: "tetra", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } - } else if (addShape['deca']) { - createDecahedron() + } else if (addShape["deca"]) { + createDecahedron(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'deca', + shape: "deca", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } - } else if (addShape['dodeca']) { - createDodecahedron() + } else if (addShape["dodeca"]) { + createDodecahedron(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'dodeca', + shape: "dodeca", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } - } else if (addShape['icosa']) { - createIcosahedron() + } else if (addShape["icosa"]) { + createIcosahedron(); if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'icosa', + shape: "icosa", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } } else { - createOctahedron() - if (addShape['octa']) { + createOctahedron(); + if (addShape["octa"]) { console.log(showImage); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: 'octa', + shape: "octa", color: currentenv.user.colorvalue.fill, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, - sharedImageData: imageData + sharedImageData: imageData, }, - }) + }); } } } @@ -754,52 +900,64 @@ define([ function onRemoveClick(event) { if (removeVolume) { // Calculate mouse position in normalized device coordinates - var rect = renderer.domElement.getBoundingClientRect() - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera) + raycaster.setFromCamera(mouse, camera); // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children) + var intersects = raycaster.intersectObjects(scene.children); - var intersectedObject = intersects[0]?.object - if (intersectedObject.geometry.type == 'PlaneGeometry') { - return + var intersectedObject = intersects[0]?.object; + if (intersectedObject.geometry.type == "PlaneGeometry") { + return; + } + let num = 0; + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][3]) { + num++; + } } for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][0] == intersectedObject) { - world.removeBody(diceArray[i][1]) - scene.remove(diceArray[i][0]) - diceArray.splice(i, 1) + if (diceArray[i][3]) { + num--; + } + world.removeBody(diceArray[i][1]); + scene.remove(diceArray[i][0]); + diceArray.splice(i, 1); } } + if (num == 0) { + lastRollElement.textContent = ""; + } } } - var presence = null + var presence = null; var palette = new presencepalette.PresencePalette( - document.getElementById('network-button'), - undefined, - ) - palette.addEventListener('shared', function () { - palette.popDown() - console.log('Want to share') + document.getElementById("network-button"), + undefined + ); + palette.addEventListener("shared", function () { + palette.popDown(); + console.log("Want to share"); presence = activity.getPresenceObject(function (error, network) { if (error) { - console.log('Sharing error') - return + console.log("Sharing error"); + return; } network.createSharedActivity( - 'org.sugarlabs.Volume', + "org.sugarlabs.Volume", function (groupId) { - console.log('Activity shared') - }, - ) - network.onDataReceived(onNetworkDataReceived) - }) - }) + console.log("Activity shared"); + } + ); + network.onDataReceived(onNetworkDataReceived); + }); + }); // document // .querySelector('#reset-button') @@ -816,83 +974,82 @@ define([ // } // }) let sleepCounter = 0; - renderer.setSize(window.innerWidth, window.innerHeight) - const canvas = document.getElementById('game-container') - document.getElementById('game-container').appendChild(renderer.domElement) - - const scene = new THREE.Scene() - scene.background = new THREE.Color(presentColor) - const light = new THREE.DirectionalLight(0xffffff, 0.7) - light.castShadow = true - const leftLight = new THREE.DirectionalLight(0xffffff, 0.25) - leftLight.castShadow = true - const rightLight = new THREE.DirectionalLight(0xffffff, 0.2) - rightLight.castShadow = true - const backLight = new THREE.DirectionalLight(0xffffff, 0.2) - const bottomLight = new THREE.DirectionalLight(0xffffff, 0.1) - const topLight = new THREE.DirectionalLight(0xffffff, 0.7) - topLight.castShadow = true - leftLight.position.set(-30, 20, -30) - rightLight.position.set(30, 20, -30) - backLight.position.set(0, 20, 30) - light.position.set(0, 20, -30) - bottomLight.position.set(0, -20, -30) - topLight.position.set(0, 10, 0) - scene.add(backLight) - scene.add(rightLight) - scene.add(leftLight) - scene.add(light) - scene.add(bottomLight) - scene.add(topLight) - - - const ambientLight = new THREE.AmbientLight(0x222222) // Soft ambient lighting - scene.add(ambientLight) + renderer.setSize(window.innerWidth, window.innerHeight); + const canvas = document.getElementById("game-container"); + document.getElementById("game-container").appendChild(renderer.domElement); + + const scene = new THREE.Scene(); + scene.background = new THREE.Color(presentColor); + const light = new THREE.DirectionalLight(0xffffff, 0.4); + light.castShadow = true; + const leftLight = new THREE.DirectionalLight(0xffffff, 0.25); + leftLight.castShadow = true; + const rightLight = new THREE.DirectionalLight(0xffffff, 0.1); + rightLight.castShadow = true; + const backLight = new THREE.DirectionalLight(0xffffff, 0.1); + const bottomLight = new THREE.DirectionalLight(0xffffff, 0.1); + const topLight = new THREE.DirectionalLight(0xffffff, 0.2); + topLight.castShadow = true; + leftLight.position.set(-30, 20, -30); + rightLight.position.set(30, 20, -30); + backLight.position.set(0, 20, 30); + light.position.set(0, 20, -30); + bottomLight.position.set(0, -20, -30); + topLight.position.set(0, 10, 0); + scene.add(backLight); + scene.add(rightLight); + scene.add(leftLight); + scene.add(light); + scene.add(bottomLight); + scene.add(topLight); + + const ambientLight = new THREE.AmbientLight(0x222222); // Soft ambient lighting + scene.add(ambientLight); const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, - 1000, - ) + 1000 + ); const world = new CANNON.World({ gravity: new CANNON.Vec3(0, -9.81, 0), - }) - world.allowSleep = true + }); + world.allowSleep = true; - const groundGeo = new THREE.PlaneGeometry(30, 30) + const groundGeo = new THREE.PlaneGeometry(30, 30); const groundMat = new THREE.MeshPhongMaterial({ side: THREE.DoubleSide, wireframe: false, - }) - groundMat.needsUpdate = true - const groundMesh = new THREE.Mesh(groundGeo, groundMat) - groundMesh.receiveShadow = true - - groundMesh.material.color.setHex(0x363636) - scene.add(groundMesh) - const groundPhysMat = new CANNON.Material() - const groundWidth = 0 // Desired width of the ground - const groundDepth = 0 // Desired depth of the ground - const groundThickness = 0 - const boxWidth = groundWidth / 2 - const boxDepth = groundDepth / 2 - const boxHeight = 10 // Adjust this for desired box height + }); + groundMat.needsUpdate = true; + const groundMesh = new THREE.Mesh(groundGeo, groundMat); + groundMesh.receiveShadow = true; + + groundMesh.material.color.setHex(0xd3d3d3); + + scene.add(groundMesh); + const groundPhysMat = new CANNON.Material(); + const groundWidth = 0; // Desired width of the ground + const groundDepth = 0; // Desired depth of the ground + const groundThickness = 0; + const boxWidth = groundWidth / 2; + const boxDepth = groundDepth / 2; + const boxHeight = 10; // Adjust this for desired box height const boxShape = new CANNON.Box( - new CANNON.Vec3(boxWidth, boxHeight / 2, boxDepth), - ) + new CANNON.Vec3(boxWidth, boxHeight / 2, boxDepth) + ); const groundBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 15, 0.1)), type: CANNON.Body.STATIC, material: groundPhysMat, - }) - groundBody.material.friction = 1 - groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) - world.addBody(groundBody) - + }); + groundBody.material.friction = 1; + groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); + world.addBody(groundBody); const leftWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), @@ -900,10 +1057,10 @@ define([ material: groundPhysMat, friction: -10, restitution: 10, - }) - world.addBody(leftWallBody) - leftWallBody.position.set(15, 0, 0) - leftWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0) + }); + world.addBody(leftWallBody); + leftWallBody.position.set(15, 0, 0); + leftWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); const rightWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), @@ -911,10 +1068,10 @@ define([ material: groundPhysMat, friction: -10, restitution: 10, - }) - world.addBody(rightWallBody) - rightWallBody.position.set(-15, 0, 0) - rightWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0) + }); + world.addBody(rightWallBody); + rightWallBody.position.set(-15, 0, 0); + rightWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); const backWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), @@ -922,10 +1079,10 @@ define([ material: groundPhysMat, friction: -10, restitution: 10, - }) - world.addBody(backWallBody) - backWallBody.position.set(0, 0, 15) - backWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2) + }); + world.addBody(backWallBody); + backWallBody.position.set(0, 0, 15); + backWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); const frontWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), @@ -933,113 +1090,110 @@ define([ material: groundPhysMat, friction: -10, restitution: 10, - }) - world.addBody(frontWallBody) - frontWallBody.position.set(0, 0, -15) - frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2) + }); + world.addBody(frontWallBody); + frontWallBody.position.set(0, 0, -15); + frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); - const rollingForceMagnitude = 2 // Adjust for desired intensity + const rollingForceMagnitude = 2; // Adjust for desired intensity const randomDirection = new CANNON.Vec3( Math.random() - 0.5, // Random x-axis value between -0.5 and 0.5 Math.random() * 0.2 - 0.1, // Random y-axis value between -0.1 and 0.1 (slightly tilted) - Math.random() - 0.5, // Random z-axis value between -0.5 and 0.5 - ) - randomDirection.normalize() // Normalize to unit vector + Math.random() - 0.5 // Random z-axis value between -0.5 and 0.5 + ); + randomDirection.normalize(); // Normalize to unit vector - const rollingForce = randomDirection.scale(rollingForceMagnitude) + const rollingForce = randomDirection.scale(rollingForceMagnitude); - const offset = new CANNON.Vec3(0, 0.1, 0) + const offset = new CANNON.Vec3(0, 0.1, 0); - const orbit = new OrbitControls.OrbitControls(camera, renderer.domElement) - camera.position.set(0, 25, -30) - orbit.update() - orbit.listenToKeyEvents(document.querySelector('body')) + const orbit = new OrbitControls.OrbitControls(camera, renderer.domElement); + camera.position.set(0, 25, -30); + orbit.update(); + orbit.listenToKeyEvents(document.querySelector("body")); - const goRightButton = document.querySelector('#right-button') - const goLeftButton = document.querySelector('#left-button') - const goUpButton = document.querySelector('#up-button') - const goDownButton = document.querySelector('#down-button') + const goRightButton = document.querySelector("#right-button"); + const goLeftButton = document.querySelector("#left-button"); + const goUpButton = document.querySelector("#up-button"); + const goDownButton = document.querySelector("#down-button"); // Add click event listener to the button - goRightButton.addEventListener('click', function () { - orbit.rotateRight() - }) - - goLeftButton.addEventListener('click', function () { - orbit.rotateLeft() - }) - goUpButton.addEventListener('click', function () { - orbit.rotateUp() - }) - goDownButton.addEventListener('click', function () { - orbit.rotateDown() - }) - // Zoom code - const evt = new Event('wheel', { bubbles: true, cancelable: true }) - - const zoomInButton = document.getElementById('zoom-in-button') - const zoomOutButton = document.getElementById('zoom-out-button') - const zoomEqualButton = document.getElementById('zoom-equal-button') - const zoomToButton = document.getElementById('zoom-to-button') - - - - - const zoomInFunction = e => { - const fov = getFov() - camera.fov = clickZoom(fov, 'zoomIn') - camera.updateProjectionMatrix() - } - - const zoomOutFunction = e => { - const fov = getFov() - camera.fov = clickZoom(fov, 'zoomOut') - camera.updateProjectionMatrix() - } + goRightButton.addEventListener("click", function (event) { + orbit.rotateRight(); + event.stopPropagation(); + }); - const zoomEqualFunction = e => { - const fov = getFov() + goLeftButton.addEventListener("click", function (event) { + orbit.rotateLeft(); + event.stopPropagation(); + }); + goUpButton.addEventListener("click", function (event) { + orbit.rotateUp(); + event.stopPropagation(); + }); + goDownButton.addEventListener("click", function (event) { + orbit.rotateDown(); + event.stopPropagation(); + }); + // Zoom code + const evt = new Event("wheel", { bubbles: true, cancelable: true }); + + const zoomInButton = document.getElementById("zoom-in-button"); + const zoomOutButton = document.getElementById("zoom-out-button"); + const zoomEqualButton = document.getElementById("zoom-equal-button"); + const zoomToButton = document.getElementById("zoom-to-button"); + + const zoomInFunction = (e) => { + const fov = getFov(); + camera.fov = clickZoom(fov, "zoomIn"); + camera.updateProjectionMatrix(); + }; + + const zoomOutFunction = (e) => { + const fov = getFov(); + camera.fov = clickZoom(fov, "zoomOut"); + camera.updateProjectionMatrix(); + }; + + const zoomEqualFunction = (e) => { + const fov = getFov(); camera.fov = 29; - camera.updateProjectionMatrix() - } + camera.updateProjectionMatrix(); + }; - const zoomToFunction = e => { - const fov = getFov() + const zoomToFunction = (e) => { + const fov = getFov(); camera.fov = 35; - camera.updateProjectionMatrix() - } + camera.updateProjectionMatrix(); + }; const clickZoom = (value, zoomType) => { - if (value >= 20 && zoomType === 'zoomIn') { - return value - 5 - } else if (value <= 75 && zoomType === 'zoomOut') { - return value + 5 + if (value >= 20 && zoomType === "zoomIn") { + return value - 5; + } else if (value <= 75 && zoomType === "zoomOut") { + return value + 5; } else { - return value + return value; } - } + }; const getFov = () => { return Math.floor( (2 * Math.atan(camera.getFilmHeight() / 2 / camera.getFocalLength()) * 180) / - Math.PI, - ) - } - - const fov = getFov() - camera.fov = 29 - camera.updateProjectionMatrix() - - - - zoomInButton.addEventListener('click', zoomInFunction) - zoomOutButton.addEventListener('click', zoomOutFunction) - zoomEqualButton.addEventListener('click', zoomEqualFunction); - zoomToButton.addEventListener('click', zoomToFunction); + Math.PI + ); + }; + const fov = getFov(); + camera.fov = 29; + camera.updateProjectionMatrix(); + zoomInButton.addEventListener("click", zoomInFunction); + zoomOutButton.addEventListener("click", zoomOutFunction); + zoomEqualButton.addEventListener("click", zoomEqualFunction); + zoomToButton.addEventListener("click", zoomToFunction); function createTetrahedron( sharedColor, @@ -1048,171 +1202,190 @@ define([ xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let tetrahedron - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let tetrahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent - let tempImage = ifImage == null ? showImage : ifImage + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5) - let tileSize = 512 - let g = new THREE.TetrahedronGeometry(2) + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.TetrahedronGeometry(2); - let c = document.createElement('canvas') - let div = document.createElement('div') - c.width = tileSize * tileDimension.x - c.height = tileSize * tileDimension.y - let ctx = c.getContext('2d') - ctx.fillStyle = presentColor - ctx.fillRect(0, 0, c.width, c.height) + let c = document.createElement("canvas"); + let div = document.createElement("div"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); - let uvs = [] + let uvs = []; let baseUVs = [ [0.067, 0.25], [0.933, 0.25], [0.5, 1], - ].map(p => { - return new THREE.Vector2(...p) - }) + ].map((p) => { + return new THREE.Vector2(...p); + }); let arrOfNums = [ [2, 1, 3], [1, 2, 4], [3, 1, 4], [2, 3, 4], - ] + ]; for (let i = 0; i < 4; i++) { - let u = i % tileDimension.x - let v = Math.floor(i / tileDimension.x) + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); uvs.push( (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, (baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y, (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - ) + (baseUVs[2].y + v) / tileDimension.y + ); - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.font = `bold 150px Arial` - ctx.fillStyle = textColor + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 150px Arial`; + ctx.fillStyle = tempTextColor; // ctx.fillText( // i + 1, // (u + 0.5) * tileSize, // c.height - (v + 0.5) * tileSize // ); - let aStep = (Math.PI * 2) / 3 - let yAlign = Math.PI * 0.5 - let tileQuarter = tileSize * 0.25 + let aStep = (Math.PI * 2) / 3; + let yAlign = Math.PI * 0.5; + let tileQuarter = tileSize * 0.25; for (let j = 0; j < 3; j++) { - ctx.save() + ctx.save(); ctx.translate( (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, c.height - (v + 0.5) * tileSize + - Math.sin(j * aStep - yAlign) * tileQuarter, - ) - ctx.rotate((j * Math.PI * 2) / 3) - ctx.fillText(arrOfNums[i][j], 0, 0) - ctx.restore() + Math.sin(j * aStep - yAlign) * tileQuarter + ); + ctx.rotate((j * Math.PI * 2) / 3); + ctx.fillText(arrOfNums[i][j], 0, 0); + ctx.restore(); } } - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - let tex = new THREE.CanvasTexture(c) - tex.colorSpace = THREE.SRGBColorSpace + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; let m = new THREE.MeshPhongMaterial({ map: tex, - }) + }); - tetrahedron = new THREE.Mesh(g, m) + tetrahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry(2) // Size of the tetrahedron + const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry(2); // Size of the tetrahedron const wireframe = new THREE.WireframeGeometry( - tetrahedronTransparentGeometry, - ) + tetrahedronTransparentGeometry + ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - tetrahedron = line + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + tetrahedron = line; } else if (tempImage) { - const boxGeo = new THREE.TetrahedronGeometry(2) + const boxGeo = new THREE.TetrahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - tetrahedron = new THREE.Mesh(boxGeo, material) + tetrahedron = new THREE.Mesh(boxGeo, material); } else { - const tetrahedronGeometry = new THREE.TetrahedronGeometry(2) // Size of the tetrahedron + const tetrahedronGeometry = new THREE.TetrahedronGeometry(2); // Size of the tetrahedron const tetraMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) + }); - tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial) + tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial); } - tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y - tetrahedron.castShadow = true - scene.add(tetrahedron) + tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + tetrahedron.castShadow = true; + scene.add(tetrahedron); const verticesTetra = [ new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) - ] + ]; const facesTetra = [ [2, 1, 0], // Triangle 1 (right, top, left) [0, 3, 2], // Triangle 2 (right, front, top) [1, 3, 0], // Triangle 3 (top, front, left) [2, 3, 1], // Triangle 4 (left, right, front) - ] + ]; // Create a ConvexPolyhedron shape from the vertices and faces const tetrahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesTetra, faces: facesTetra, - }) + }); - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; const tetrahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: tetrahedronShape, - position: new CANNON.Vec3(x, 10, z), + position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, - }) + }); if (tempShowNumbers) { - tetrahedronBody.addEventListener('sleep', () => { + tetrahedronBody.addEventListener("sleep", () => { sleepCounter++; - getTetraScore(tetrahedron) - }) + getTetraScore(tetrahedron); + }); } - world.addBody(tetrahedronBody) + world.addBody(tetrahedronBody); tetrahedronBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - tetrahedronBody.applyImpulse(offset, rollingForce) - tetrahedron.position.copy(tetrahedronBody.position) // this merges the physics body to threejs mesh - tetrahedron.quaternion.copy(tetrahedronBody.quaternion) - diceArray.push([tetrahedron, tetrahedronBody, 'tetra']) + Math.random() - 0.5 + ); + tetrahedronBody.applyImpulse(offset, rollingForce); + tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh + tetrahedron.quaternion.copy(tetrahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + tetrahedron.quaternion.copy(quaternionShared); + tetrahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + tetrahedron, + tetrahedronBody, + "tetra", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); } function createOctahedron( @@ -1222,102 +1395,109 @@ define([ xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let octahedron - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let octahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent - let tempImage = ifImage == null ? showImage : ifImage + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; + if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5) - let tileSize = 512 - let g = new THREE.OctahedronGeometry(2) + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.OctahedronGeometry(2); - let c = document.createElement('canvas') - c.width = tileSize * tileDimension.x - c.height = tileSize * tileDimension.y - let ctx = c.getContext('2d') - ctx.fillStyle = presentColor - ctx.fillRect(0, 0, c.width, c.height) + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); - let uvs = [] + let uvs = []; let baseUVs = [ [0.067, 0.25], [0.933, 0.25], [0.5, 1], - ].map(p => { - return new THREE.Vector2(...p) - }) + ].map((p) => { + return new THREE.Vector2(...p); + }); for (let i = 0; i < 9; i++) { - let u = i % tileDimension.x - let v = Math.floor(i / tileDimension.x) + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); uvs.push( (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, (baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y, (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - ) + (baseUVs[2].y + v) / tileDimension.y + ); - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.font = `bold 200px Arial` - ctx.fillStyle = textColor + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 200px Arial`; + ctx.fillStyle = tempTextColor; ctx.fillText( - i + 1 + (i == 5 || i == 8 ? '' : ''), + i + 1 + (i == 5 || i == 8 ? "" : ""), (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize, - ) + c.height - (v + 0.5) * tileSize + ); } - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - let tex = new THREE.CanvasTexture(c) - tex.colorSpace = THREE.SRGBColorSpace + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; let m = new THREE.MeshPhongMaterial({ map: tex, - }) + }); - octahedron = new THREE.Mesh(g, m) + octahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const octahedronTransparentGeometry = new THREE.OctahedronGeometry(2) // Size of the octahedron + const octahedronTransparentGeometry = new THREE.OctahedronGeometry(2); // Size of the octahedron const wireframe = new THREE.WireframeGeometry( - octahedronTransparentGeometry, - ) + octahedronTransparentGeometry + ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - octahedron = line + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + octahedron = line; } else if (tempImage) { - const octahedronGeometry = new THREE.OctahedronGeometry(2) + const octahedronGeometry = new THREE.OctahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - octahedron = new THREE.Mesh(octahedronGeometry, material) + octahedron = new THREE.Mesh(octahedronGeometry, material); } else { - const octahedronGeometry = new THREE.OctahedronGeometry(2) // Size of the octahedron + const octahedronGeometry = new THREE.OctahedronGeometry(2); // Size of the octahedron const octaMaterial = new THREE.MeshPhongMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) - octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial) + }); + octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial); } - octahedron.castShadow = true - scene.add(octahedron) + octahedron.castShadow = true; + scene.add(octahedron); const verticesOcta = [ new CANNON.Vec3(2, 0, 0), // Vertex 1 (right) @@ -1326,7 +1506,7 @@ define([ new CANNON.Vec3(0, -2, 0), // Vertex 4 (front) new CANNON.Vec3(0, 0, 2), // Vertex 4 (front) new CANNON.Vec3(0, 0, -2), // Vertex 4 (front) - ] + ]; // Define the faces of the tetrahedron (counter-clockwise order) const facesOcta = [ @@ -1338,39 +1518,52 @@ define([ [1, 5, 3], // Triangle 1 (right, top, left) [1, 3, 4], // Triangle 1 (right, top, left) [1, 4, 2], // Triangle 1 (right, top, left) - ] + ]; const octahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesOcta, faces: facesOcta, - }) - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + }); + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; const octahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: octahedronShape, - position: new CANNON.Vec3(x, 10, z), + position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, - }) + }); if (tempShowNumbers) { - octahedronBody.addEventListener('sleep', () => { + octahedronBody.addEventListener("sleep", () => { sleepCounter++; - getOctaScore(octahedron) - }) + getOctaScore(octahedron); + }); } - world.addBody(octahedronBody) + world.addBody(octahedronBody); octahedronBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - octahedronBody.applyImpulse(offset, rollingForce) - octahedron.position.copy(octahedronBody.position) // this merges the physics body to threejs mesh - octahedron.quaternion.copy(octahedronBody.quaternion) - diceArray.push([octahedron, octahedronBody, 'octa']) + Math.random() - 0.5 + ); + octahedronBody.applyImpulse(offset, rollingForce); + octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh + octahedron.quaternion.copy(octahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + octahedron.quaternion.copy(quaternionShared); + octahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + octahedron, + octahedronBody, + "octa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); } function createCube( @@ -1380,149 +1573,165 @@ define([ xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let boxMesh - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let boxMesh; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent + ifTransparent == null ? toggleTransparent : ifTransparent; let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 2) - let tileSize = 512 - let g = new THREE.BoxGeometry(2, 2, 2) + let tileDimension = new THREE.Vector2(4, 2); + let tileSize = 512; + let g = new THREE.BoxGeometry(2, 2, 2); - let c = document.createElement('canvas') - c.width = tileSize * tileDimension.x - c.height = tileSize * tileDimension.y - let ctx = c.getContext('2d') - ctx.fillStyle = sharedColor != null ? sharedColor : presentColor - ctx.fillRect(0, 0, c.width, c.height) + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); let baseUVs = [ [0, 1], [1, 1], [0, 0], [1, 0], - ].map(p => { - return new THREE.Vector2(...p) - }) - let uvs = [] - let vTemp = new THREE.Vector2() - let vCenter = new THREE.Vector2(0.5, 0.5) + ].map((p) => { + return new THREE.Vector2(...p); + }); + let uvs = []; + let vTemp = new THREE.Vector2(); + let vCenter = new THREE.Vector2(0.5, 0.5); for (let i = 0; i < 6; i++) { - let u = i % tileDimension.x - let v = Math.floor(i / tileDimension.x) - baseUVs.forEach(buv => { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + baseUVs.forEach((buv) => { uvs.push( (buv.x + u) / tileDimension.x, - (buv.y + v) / tileDimension.y, - ) - }) - - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.font = `bold 300px Arial` - ctx.fillStyle = textColor + (buv.y + v) / tileDimension.y + ); + }); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 300px Arial`; + ctx.fillStyle = tempTextColor; ctx.fillText( i + 1, (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize, - ) + c.height - (v + 0.5) * tileSize + ); } - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - let tex = new THREE.CanvasTexture(c) - tex.colorSpace = THREE.SRGBColorSpace + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; let m = new THREE.MeshPhongMaterial({ map: tex, // metalness: 0.75, // roughness: 0.25, - }) + }); - boxMesh = new THREE.Mesh(g, m) + boxMesh = new THREE.Mesh(g, m); } else if (tempTransparent) { - const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2) - const wireframe = new THREE.WireframeGeometry(boxTransparentGeometry) + const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); + const wireframe = new THREE.WireframeGeometry(boxTransparentGeometry); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - boxMesh = line - + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + boxMesh = line; } else if (tempImage) { - const boxGeo = new THREE.BoxGeometry(2, 2, 2) + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - boxMesh = new THREE.Mesh(boxGeo, material) - } else { - const boxGeo = new THREE.BoxGeometry(2, 2, 2) + boxMesh = new THREE.Mesh(boxGeo, material); + } else { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); const boxMat = new THREE.MeshPhongMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) - boxMesh = new THREE.Mesh(boxGeo, boxMat) + }); + boxMesh = new THREE.Mesh(boxGeo, boxMat); } - boxMesh.castShadow = true - scene.add(boxMesh) + boxMesh.castShadow = true; + scene.add(boxMesh); - const boxPhysmat = new CANNON.Material() + const boxPhysmat = new CANNON.Material(); + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared const boxBody = new CANNON.Body({ mass: 1, shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), - position: new CANNON.Vec3(xCoordinate, 10, zCoordinate), + position: new CANNON.Vec3(x, y, z), material: boxPhysmat, friction: 0.1, restitution: 5, - }) - boxBody.addEventListener('click', function (boxBody) { - console.log('clicked a box') - }) + }); + + world.addBody(boxBody); - world.addBody(boxBody) if (tempShowNumbers) { - boxBody.addEventListener('sleep', () => { + boxBody.addEventListener("sleep", () => { sleepCounter++; - getCubeScore(boxMesh) - }) + getCubeScore(boxMesh); + }); } boxBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - boxBody.applyImpulse(offset, rollingForce) + Math.random() - 0.5 + ); + boxBody.applyImpulse(offset, rollingForce); // what will happen when the two bodies touch const groundBoxContactMat = new CANNON.ContactMaterial( groundPhysMat, boxPhysmat, - { friction: 0.5 }, - ) + { friction: 0.5 } + ); - world.addContactMaterial(groundBoxContactMat) - diceArray.push([boxMesh, boxBody, 'cube']) + world.addContactMaterial(groundBoxContactMat); + if (quaternionShared != null && quaternionShared != undefined) { + boxMesh.quaternion.copy(quaternionShared); + boxBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + boxMesh, + boxBody, + "cube", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); } const cannonDebugger = new CannonDebugger(scene, world, { color: 0xadd8e6, - }) + }); function createDecahedron( sharedColor, @@ -1531,80 +1740,85 @@ define([ xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let decahedron - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let decahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent - let tempImage = ifImage == null ? showImage : ifImage + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; const sides = 10; - const radius = 1; - const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], - ].flat(); - - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); - } - console.log(verticesGeo); - - const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], - ].flat(); - const args = [verticesGeo, facesGeo, radius, 0]; - let decaGeometry = new THREE.PolyhedronGeometry(...args); - + const radius = 1; + const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], + ].flat(); + + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); + } + console.log(verticesGeo); + + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ].flat(); + const args = [verticesGeo, facesGeo, radius, 0]; + let decaGeometry = new THREE.PolyhedronGeometry(...args); if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5) - let tileSize = 512 - let g = decaGeometry - - let c = document.createElement('canvas') - c.width = tileSize * tileDimension.x - c.height = tileSize * tileDimension.y - let ctx = c.getContext('2d') - ctx.fillStyle = presentColor - ctx.fillRect(0, 0, c.width, c.height) - - let uvs = [] - + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = decaGeometry; + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map(p => { - return new THREE.Vector2(...p) - }) + [0.1, 0.25], // bt + [0.933, 0.25], //br + [0.5, 1], // tl + [0.5, 0.15], + ].map((p) => { + return new THREE.Vector2(...p); + }); + + // Initialize UVs array + let uvs = []; + // Loop to generate UVs and draw numbers on the canvas for (let i = 0; i < 10; i++) { - let u = i % tileDimension.x - let v = Math.floor(i / tileDimension.x) + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); uvs.push( (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, @@ -1612,116 +1826,159 @@ define([ (baseUVs[1].y + v) / tileDimension.y, (baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y, - ) - - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.font = `bold 200px Arial` - ctx.fillStyle = textColor + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 200px Arial`; + ctx.fillStyle = tempTextColor; ctx.fillText( - i + 1 + (i == 5 || i == 8 ? '' : ''), + i + 1, (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize, - ) + c.height - (v + 0.5) * tileSize + ); } - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - let tex = new THREE.CanvasTexture(c) - tex.colorSpace = THREE.SRGBColorSpace + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; let m = new THREE.MeshPhongMaterial({ map: tex, - }) - - decahedron = new THREE.Mesh(g, m) + }); - + decahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry + const decahedronTransaprentGeometry = decaGeometry; const wireframe = new THREE.WireframeGeometry( - decahedronTransaprentGeometry, - ) + decahedronTransaprentGeometry + ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - decahedron = line + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + decahedron = line; } else if (tempImage) { - const decaGeo = decaGeometry + const decaGeo = decaGeometry; + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - decahedron = new THREE.Mesh(decaGeo, material) + decahedron = new THREE.Mesh(decaGeo, material); } else { - const decahedronGeometry = decaGeometry + const decahedronGeometry = decaGeometry; const decaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) + }); - decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial) + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); } - decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y - decahedron.castShadow = true - scene.add(decahedron) + decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true; + scene.add(decahedron); + console.log(decahedron); const t = (1 + Math.sqrt(5)) / 2; -const r = 1 / t; - -const verticesCannon = []; - for (let i = 0; i < verticesGeo.length; i += 3) { - verticesCannon.push(new CANNON.Vec3(verticesGeo[i], verticesGeo[i+1], verticesGeo[i+2])); - } - - const facesCannon = []; - for (let i = 0; i < facesGeo.length; i += 3) { - facesCannon.push([facesGeo[i], facesGeo[i+1], facesGeo[i+2]]); - } + const r = 1 / t; + + const verticesCannon = []; + for (let i = 0; i < verticesGeo.length; i += 3) { + verticesCannon.push( + new CANNON.Vec3( + verticesGeo[i], + verticesGeo[i + 1], + verticesGeo[i + 2] + ) + ); + } + const facesCannon = []; + for (let i = 0; i < facesGeo.length; i += 3) { + facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); + } // Create a ConvexPolyhedron shape from the vertices and faces const decahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesCannon, faces: facesCannon, - }) + }); - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; const decahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: decahedronShape, - position: new CANNON.Vec3(x, 10, z), + position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, - }) + }); if (tempShowNumbers) { - decahedronBody.addEventListener('sleep', () => { - console.log("going to sleep now deca") + decahedronBody.addEventListener("sleep", () => { sleepCounter++; - getTetraScore(decahedron) - }) + getDecaScore(decahedron); + }); } - world.addBody(decahedronBody) + world.addBody(decahedronBody); decahedronBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - decahedronBody.applyImpulse(offset, rollingForce) - decahedron.position.copy(decahedronBody.position) // this merges the physics body to threejs mesh - decahedron.quaternion.copy(decahedronBody.quaternion) - diceArray.push([decahedron, decahedronBody, 'deca']) + Math.random() - 0.5 + ); + decahedronBody.applyImpulse(offset, rollingForce); + decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh + decahedron.quaternion.copy(decahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + decahedron.quaternion.copy(quaternionShared); + decahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + decahedron, + decahedronBody, + "deca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + function makeNumbers() { + let c = document.createElement("canvas"); + c.width = 1024; + c.height = 1024; + let ctx = c.getContext("2d"); + ctx.fillStyle = "#f0f"; + ctx.fillRect(0, 0, c.width, c.height); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = "#0f8"; + ctx.font = "bold 60px Arial"; + let step = 1024 / 10; + let start = step * 0.5; + + for (let i = 0; i < 10; i++) { + ctx.fillText(i + 1, start + step * i, 512); + } + + return new THREE.CanvasTexture(c); } function createDodecahedron( @@ -1731,196 +1988,260 @@ const verticesCannon = []; xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let dodecahedron - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers + let dodecahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent - let tempImage = ifImage == null ? showImage : ifImage + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid let tileSize = 512; - let g = new THREE.DodecahedronGeometry(2); - - let c = document.createElement('canvas'); + let g = new THREE.DodecahedronGeometry(1.25); + + let c = document.createElement("canvas"); c.width = tileSize * tileDimension.x; c.height = tileSize * tileDimension.y; - let ctx = c.getContext('2d'); - ctx.fillStyle = presentColor; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; ctx.fillRect(0, 0, c.width, c.height); - + let uvs = []; const base = new THREE.Vector2(0, 0.5); const center = new THREE.Vector2(); const angle = THREE.MathUtils.degToRad(72); let baseUVs = [ - base.clone().rotateAround(center, angle * 1).addScalar(0.5), - base.clone().rotateAround(center, angle * 2).addScalar(0.5), - base.clone().rotateAround(center, angle * 3).addScalar(0.5), - base.clone().rotateAround(center, angle * 4).addScalar(0.5), - base.clone().rotateAround(center, angle * 0).addScalar(0.5) + base + .clone() + .rotateAround(center, angle * 1) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 2) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 3) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 4) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 0) + .addScalar(0.5), ]; - - for (let i = 0; i < 12; i++) { // 12 faces for a dodecahedron - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[4].x + u) / tileDimension.x, (baseUVs[4].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y - ); - - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.font = `bold ${tileSize / 3}px Arial`; - ctx.fillStyle = textColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); + + for (let i = 0; i < 12; i++) { + // 12 faces for a dodecahedron + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[4].x + u) / tileDimension.x, + (baseUVs[4].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold ${tileSize / 3}px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); } - - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); - + + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + let tex = new THREE.CanvasTexture(c); tex.colorSpace = THREE.SRGBColorSpace; tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); - + let m = new THREE.MeshPhongMaterial({ - map: tex, + map: tex, }); - + dodecahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const dodedodecahedronTransaprentGeometry = new THREE.DodecahedronGeometry(2) // Size of the tetrahedron + const dodedodecahedronTransaprentGeometry = + new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron const wireframe = new THREE.WireframeGeometry( - dodedodecahedronTransaprentGeometry, - ) + dodedodecahedronTransaprentGeometry + ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - dodecahedron = line + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + dodecahedron = line; } else if (tempImage) { - const dodecaGeo = new THREE.DodecahedronGeometry(2) + const dodecaGeo = new THREE.DodecahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - dodecahedron = new THREE.Mesh(dodecaGeo, material) + dodecahedron = new THREE.Mesh(dodecaGeo, material); } else { - const dodecahedronGeometry = new THREE.DodecahedronGeometry(2) // Size of the tetrahedron + const dodecahedronGeometry = new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron const dodecaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) + }); - dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial) + dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial); } - dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y - dodecahedron.castShadow = true - scene.add(dodecahedron) - - const t = (1 + Math.sqrt(5)) / 2; -const r = 1 / t; -const scaleFactor = 1; - -const vertices = [ - new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(0, -r, -t).scale(scaleFactor), - new CANNON.Vec3(0, -r, t).scale(scaleFactor), - new CANNON.Vec3(0, r, -t).scale(scaleFactor), - new CANNON.Vec3(0, r, t).scale(scaleFactor), - new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(-r, t, 0).scale(scaleFactor), - new CANNON.Vec3(r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(r, t, 0).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(-t, 0, r).scale(scaleFactor), - new CANNON.Vec3(t, 0, r).scale(scaleFactor) -]; - -const indices = [ - [3, 11, 7], [3, 7, 15], [3, 15, 13], - [7, 19, 17], [7, 17, 6], [7, 6, 15], - [17, 4, 8], [17, 8, 10], [17, 10, 6], - [8, 0, 16], [8, 16, 2], [8, 2, 10], - [0, 12, 1], [0, 1, 18], [0, 18, 16], - [6, 10, 2], [6, 2, 13], [6, 13, 15], - [2, 16, 18], [2, 18, 3], [2, 3, 13], - [18, 1, 9], [18, 9, 11], [18, 11, 3], - [4, 14, 12], [4, 12, 0], [4, 0, 8], - [11, 9, 5], [11, 5, 19], [11, 19, 7], - [19, 5, 14], [19, 14, 4], [19, 4, 17], - [1, 12, 14], [1, 14, 5], [1, 5, 9] -]; + dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + dodecahedron.castShadow = true; + scene.add(dodecahedron); + + const t = 1.618; + const r = 0.618; + const scaleFactor = 0.75; + + const vertices = [ + new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(0, -r, -t).scale(scaleFactor), + new CANNON.Vec3(0, -r, t).scale(scaleFactor), + new CANNON.Vec3(0, r, -t).scale(scaleFactor), + new CANNON.Vec3(0, r, t).scale(scaleFactor), + new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(-r, t, 0).scale(scaleFactor), + new CANNON.Vec3(r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(r, t, 0).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(-t, 0, r).scale(scaleFactor), + new CANNON.Vec3(t, 0, r).scale(scaleFactor), + ]; + + const indices = [ + [3, 11, 7], + [3, 7, 15], + [3, 15, 13], + [7, 19, 17], + [7, 17, 6], + [7, 6, 15], + [17, 4, 8], + [17, 8, 10], + [17, 10, 6], + [8, 0, 16], + [8, 16, 2], + [8, 2, 10], + [0, 12, 1], + [0, 1, 18], + [0, 18, 16], + [6, 10, 2], + [6, 2, 13], + [6, 13, 15], + [2, 16, 18], + [2, 18, 3], + [2, 3, 13], + [18, 1, 9], + [18, 9, 11], + [18, 11, 3], + [4, 14, 12], + [4, 12, 0], + [4, 0, 8], + [11, 9, 5], + [11, 5, 19], + [11, 19, 7], + [19, 5, 14], + [19, 14, 4], + [19, 4, 17], + [1, 12, 14], + [1, 14, 5], + [1, 5, 9], + ]; // Create a ConvexPolyhedron shape from the vertices and faces const dodecahedronShape = new CANNON.ConvexPolyhedron({ vertices: vertices, faces: indices, - }) + }); - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; const dodecahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: dodecahedronShape, - position: new CANNON.Vec3(x, 10, z), + position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, - }) + }); if (tempShowNumbers) { - dodecahedronBody.addEventListener('sleep', () => { + dodecahedronBody.addEventListener("sleep", () => { sleepCounter++; - getTetraScore(dodecahedron) - }) + getDodecaScore(dodecahedron); + }); } - world.addBody(dodecahedronBody) + world.addBody(dodecahedronBody); dodecahedronBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - dodecahedronBody.applyImpulse(offset, rollingForce) - dodecahedron.position.copy(dodecahedronBody.position) // this merges the physics body to threejs mesh - dodecahedron.quaternion.copy(dodecahedronBody.quaternion) - diceArray.push([dodecahedron, dodecahedronBody, 'dodeca']) + Math.random() - 0.5 + ); + dodecahedronBody.applyImpulse(offset, rollingForce); + dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh + dodecahedron.quaternion.copy(dodecahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + dodecahedron.quaternion.copy(quaternionShared); + dodecahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + dodecahedron, + dodecahedronBody, + "dodeca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); } - - - - function createIcosahedron( sharedColor, ifNumbers, @@ -1928,221 +2249,254 @@ const indices = [ xCoordinateShared, zCoordinateShared, ifImage, - sharedImageData + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor ) { - let icosahedron - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers - let tempTransparent = ifTransparent == null ? toggleTransparent : ifTransparent - let tempImage = ifImage == null ? showImage : ifImage + let icosahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : presentColor; + let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; + if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5) - let tileSize = 512 - let g = new THREE.IcosahedronGeometry(2) + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.IcosahedronGeometry(1.2); - let c = document.createElement('canvas') - let div = document.createElement('div') - c.width = tileSize * tileDimension.x - c.height = tileSize * tileDimension.y - let ctx = c.getContext('2d') - ctx.fillStyle = presentColor - ctx.fillRect(0, 0, c.width, c.height) + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); - let uvs = [] + let uvs = []; let baseUVs = [ [0.067, 0.25], [0.933, 0.25], [0.5, 1], - ].map(p => { - return new THREE.Vector2(...p) - }) - let arrOfNums = [ - [2, 1, 3], - [1, 2, 4], - [3, 1, 4], - [2, 3, 4], - ] + ].map((p) => { + return new THREE.Vector2(...p); + }); for (let i = 0; i < 20; i++) { - let u = i % tileDimension.x - let v = Math.floor(i / tileDimension.x) + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); uvs.push( (baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y, (baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y, (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - ) - - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.font = `bold 175px Arial` - ctx.fillStyle = textColor - // ctx.fillText( - // i + 1, - // (u + 0.5) * tileSize, - // c.height - (v + 0.5) * tileSize - // ); - let aStep = (Math.PI * 2) / 3 - let yAlign = Math.PI * 0.5 - let tileQuarter = tileSize * 0.25 + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 175px Arial`; + ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i == 5) { + text + "."; + } ctx.fillText( i + 1, (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize, - ) + c.height - (v + 0.5) * tileSize + ); } - g.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - let tex = new THREE.CanvasTexture(c) - tex.colorSpace = THREE.SRGBColorSpace + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; let m = new THREE.MeshPhongMaterial({ map: tex, - }) + }); - icosahedron = new THREE.Mesh(g, m) + icosahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry(2) // Size of the Icosahedron + const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry( + 1.15 + ); // Size of the Icosahedron const wireframe = new THREE.WireframeGeometry( - icosahedronTransparentGeometry, - ) + icosahedronTransparentGeometry + ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : presentColor, depthTest: true, opacity: 1, transparent: false, - }) - const line = new THREE.LineSegments(wireframe, lineMaterial) - icosahedron = line + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + icosahedron = line; } else if (tempImage) { - const boxGeo = new THREE.IcosahedronGeometry(2) + const boxGeo = new THREE.IcosahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); - const texture = new THREE.TextureLoader().load(sharedImageData != null ? sharedImageData : imageData) - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }) + const material = new THREE.MeshPhongMaterial({ map: texture }); // Create cube mesh with the material - icosahedron = new THREE.Mesh(boxGeo, material) + icosahedron = new THREE.Mesh(boxGeo, material); } else { - const icosahedronGeometry = new THREE.IcosahedronGeometry(2) // Size of the icosahedron + const icosahedronGeometry = new THREE.IcosahedronGeometry(1.2); // Size of the icosahedron const icosaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, wireframe: false, - }) + }); - icosahedron = new THREE.Mesh(icosahedronGeometry, icosaMaterial) + icosahedron = new THREE.Mesh(icosahedronGeometry, icosaMaterial); } - icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0) // Rotates 90 degrees on X, 45 degrees on Y - icosahedron.castShadow = true - scene.add(icosahedron) + icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + icosahedron.castShadow = true; + scene.add(icosahedron); // Vertices -// Vertices -const t = (1 + Math.sqrt(5)) / 2; -const scaleFactor = 0.7; -const verticesIcosa = [ - new CANNON.Vec3(-1, t, 0).scale(scaleFactor), new CANNON.Vec3(1, t, 0).scale(scaleFactor), - new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), new CANNON.Vec3(1, -t, 0).scale(scaleFactor), - new CANNON.Vec3(0, -1, t).scale(scaleFactor), new CANNON.Vec3(0, 1, t).scale(scaleFactor), - new CANNON.Vec3(0, -1, -t).scale(scaleFactor), new CANNON.Vec3(0, 1, -t).scale(scaleFactor), - new CANNON.Vec3(t, 0, -1).scale(scaleFactor), new CANNON.Vec3(t, 0, 1).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), new CANNON.Vec3(-t, 0, 1).scale(scaleFactor) -]; - -// Faces -const facesIcosa = [ - [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], - [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], - [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], - [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1] -]; - -// Create a ConvexPolyhedron shape from the vertices and faces -const icosahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesIcosa, - faces: facesIcosa -}); + // Vertices + const t = (1 + Math.sqrt(5)) / 2; + const scaleFactor = 0.6; + const verticesIcosa = [ + new CANNON.Vec3(-1, t, 0).scale(scaleFactor), + new CANNON.Vec3(1, t, 0).scale(scaleFactor), + new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(0, -1, t).scale(scaleFactor), + new CANNON.Vec3(0, 1, t).scale(scaleFactor), + new CANNON.Vec3(0, -1, -t).scale(scaleFactor), + new CANNON.Vec3(0, 1, -t).scale(scaleFactor), + new CANNON.Vec3(t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(t, 0, 1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, 1).scale(scaleFactor), + ]; + + // Faces + const facesIcosa = [ + [0, 11, 5], + [0, 5, 1], + [0, 1, 7], + [0, 7, 10], + [0, 10, 11], + [1, 5, 9], + [5, 11, 4], + [11, 10, 2], + [10, 7, 6], + [7, 1, 8], + [3, 9, 4], + [3, 4, 2], + [3, 2, 6], + [3, 6, 8], + [3, 8, 9], + [4, 9, 5], + [2, 4, 11], + [6, 2, 10], + [8, 6, 7], + [9, 8, 1], + ]; + // Create a ConvexPolyhedron shape from the vertices and faces + const icosahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesIcosa, + faces: facesIcosa, + }); - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; const icosahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: icosahedronShape, - position: new CANNON.Vec3(x, 10, z), + position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, - }) + }); if (tempShowNumbers) { - icosahedronBody.addEventListener('sleep', () => { - console.log("icosa going to sleep"); + icosahedronBody.addEventListener("sleep", () => { + console.log("icosa going to sleeep"); sleepCounter++; - getTetraScore(icosahedron) - }) + getIcosaScore(icosahedron); + }); } - world.addBody(icosahedronBody) + icosahedronBody.sleepSpeedLimit = 0.2; // ugly hack to bypass the vibration issue in the ConvextPolyhedron class of cannon-es, port phyiscs engine to rapier before removing this :) + world.addBody(icosahedronBody); icosahedronBody.angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - icosahedronBody.applyImpulse(offset, rollingForce) - icosahedron.position.copy(icosahedronBody.position) // this merges the physics body to threejs mesh - icosahedron.quaternion.copy(icosahedronBody.quaternion) - diceArray.push([icosahedron, icosahedronBody, 'icosa']) - } - - + Math.random() - 0.5 + ); + icosahedronBody.applyImpulse(offset, rollingForce); + icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh + icosahedron.quaternion.copy(icosahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + icosahedron.quaternion.copy(quaternionShared); + icosahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + icosahedron, + icosahedronBody, + "icosa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } - const timeStep = 1 / 20 + const timeStep = 1 / 20; function throwDice() { for (let i = 0; i < diceArray.length; i++) { - scene.remove(diceArray[i][0]) - world.removeBody(diceArray[i][1]) + scene.remove(diceArray[i][0]); + world.removeBody(diceArray[i][1]); } if (diceArray.length > 0) { - lastRoll = '' - presentScore = 0 + lastRoll = ""; + presentScore = 0; for (let i = 0; i < diceArray.length; i++) { diceArray[i][1].angularVelocity.set( Math.random() - 0.5, Math.random(), - Math.random() - 0.5, - ) - diceArray[i][1].applyImpulse(offset, rollingForce) - diceArray[i][1].position.set(0, 10, 0) + Math.random() - 0.5 + ); + diceArray[i][1].applyImpulse(offset, rollingForce); + diceArray[i][1].position.set(0, 10, 0); } for (let i = 0; i < diceArray.length; i++) { - scene.add(diceArray[i][0]) - world.addBody(diceArray[i][1]) + scene.add(diceArray[i][0]); + world.addBody(diceArray[i][1]); } } else { for (let i = 0; i < dices.cube; i++) { - createCube() + createCube(); } for (let i = 0; i < dices.tetra; i++) { - createTetrahedron() + createTetrahedron(); } for (let i = 0; i < dices.octa; i++) { - createOctahedron() + createOctahedron(); } for (let i = 0; i < dices.dodeca; i++) { - createDodecahedron() + createDodecahedron(); } for (let i = 0; i < dices.deca; i++) { - createDecahedron() + createDecahedron(); } for (let i = 0; i < dices.icosa; i++) { - createIcosahedron() + createIcosahedron(); } - lastRoll = '' + lastRoll = ""; // if (showNumbers) { // getScore(); // } @@ -2182,17 +2536,17 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) face: 7, }, - ] + ]; - let minValue = 1000000 - let minInd + let minValue = 1000000; + let minInd; for (let i = 0; i < faceVectors.length; i++) { - let faceVector = faceVectors[i] - faceVector.vector.applyEuler(body.rotation) - console.log(Math.abs(faceVector.vector.y)) + let faceVector = faceVectors[i]; + faceVector.vector.applyEuler(body.rotation); + console.log(Math.abs(faceVector.vector.y)); if (minValue > Math.abs(1 - faceVector.vector.y)) { - minValue = Math.abs(1 - faceVector.vector.y) - minInd = i + minValue = Math.abs(1 - faceVector.vector.y); + minInd = i; } // if (Math.abs(faceVector.vector.y).toString().substring(0, 1) == '1') { // lastRoll += faceVectors[i].face + ' +' @@ -2201,9 +2555,9 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ // break // } } - lastRoll += faceVectors[minInd].face + ' + ' - presentScore += faceVectors[minInd].face - updateElements() + lastRoll += faceVectors[minInd].face + " + "; + presentScore += faceVectors[minInd].face; + updateElements(); } function getCubeScore(body) { const faceVectors = [ @@ -2231,15 +2585,15 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ vector: new THREE.Vector3(0, 0, -1), face: 6, }, - ] + ]; for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation) + faceVector.vector.applyEuler(body.rotation); if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + ' + ' - presentScore += faceVector.face - updateElements() - break + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; } } } @@ -2261,37 +2615,144 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) face: 4, }, - ] + ]; for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation) + faceVector.vector.applyEuler(body.rotation); if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + ' + ' - presentScore += faceVector.face - updateElements() - break + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; } } } + + function getDecaScore(body) { + console.log("getting deca"); + } + + function getIcosaScore(body) { + // Define the golden ratio + const phi = (1 + Math.sqrt(5)) / 2; + + // Icosahedron face vectors + const faceVectors = [ + { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 14 }, + { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 13 }, + { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 18 }, + { vector: new THREE.Vector3(0, -1, -phi).normalize(), face: 19 }, + { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, + { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, + { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 7 }, + { vector: new THREE.Vector3(-1, -phi, 0).normalize(), face: 17 }, + { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 9 }, + { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, + { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 4 }, + { vector: new THREE.Vector3(-phi, 0, -1).normalize(), face: 12 }, + { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, + { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 1 }, + { vector: new THREE.Vector3(1, -phi, phi).normalize(), face: 15 }, + { vector: new THREE.Vector3(-1, -phi, phi).normalize(), face: 16 }, + { vector: new THREE.Vector3(1, phi, -phi).normalize(), face: 10 }, + { vector: new THREE.Vector3(-1, phi, -phi).normalize(), face: 3 }, + { vector: new THREE.Vector3(1, -phi, -phi).normalize(), face: 11 }, + { vector: new THREE.Vector3(-1, -phi, -phi).normalize(), face: 20 }, + ]; + + let closestFaces = []; + let minDifference = Infinity; + + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation); + console.log(faceVector.vector.y); + const difference = Math.abs(Math.abs(faceVector.vector.y) - 1); + console.log(difference + ", " + faceVector.face); + + if (difference < minDifference) { + closestFaces = [faceVector]; + minDifference = difference; + } else if (difference === minDifference) { + closestFaces.push(faceVector); + } + } + + if (closestFaces.length === 1) { + const closestFace = closestFaces[0]; + console.log(`Closest face: ${closestFace.face}`); + lastRoll += closestFace.face + " + "; + presentScore += closestFace.face; + } else { + console.log("Multiple faces are equally close to being on top:"); + closestFaces.forEach((faceVector) => + console.log(`Face: ${faceVector.face}, y: ${faceVector.vector.y}`) + ); + } + + updateElements(); + } + + // Function to calculate a face vector based on spherical coordinates + function calculateFaceVector(theta, phi) { + return new THREE.Vector3( + Math.cos(theta) * Math.sin(phi), + Math.sin(theta) * Math.sin(phi), + Math.cos(phi) + ); + } + + function getDodecaScore(body) { + // Define the golden ratio + const phi = (1 + Math.sqrt(5)) / 2; + + // Dodecahedron face vectors + const faceVectors = [ + { vector: new THREE.Vector3(1, 1, 1), face: 1 }, + { vector: new THREE.Vector3(1, 1, -1), face: 6 }, + { vector: new THREE.Vector3(1, -1, 1), face: 11 }, + { vector: new THREE.Vector3(1, -1, -1), face: 4 }, + { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, + { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, + { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, + { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, + { vector: new THREE.Vector3(0, phi, 1 / phi), face: 9 }, + { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, + { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, + { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, + ]; + + for (const faceVector of faceVectors) { + faceVector.vector.normalize().applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) === 1) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + } + } + function toggleTransparency() { for (let i = 0; i < diceArray.length; i++) { if (transparent) { - leftLight.intensity = 5 - rightLight.intensity = 5 - backLight.intensity = 5 - bottomLight.intensity = 5 + leftLight.intensity = 5; + rightLight.intensity = 5; + backLight.intensity = 5; + bottomLight.intensity = 5; } else { - leftLight.intensity = 0.1 - rightLight.intensity = 0.1 - backLight.intensity = 0.5 - bottomLight.intensity = 0.1 + leftLight.intensity = 0.1; + rightLight.intensity = 0.1; + backLight.intensity = 0.5; + bottomLight.intensity = 0.1; } - diceArray[i][0].material.wireframe = !diceArray[i][0].material.wireframe + diceArray[i][0].material.wireframe = + !diceArray[i][0].material.wireframe; diceArray[i][0].material.transparent = - !diceArray[i][0].material.transparent - diceArray[i][0].material.needsUpdate = true + !diceArray[i][0].material.transparent; + diceArray[i][0].material.needsUpdate = true; } - groundMesh.material.wireframe = !groundMesh.material.wireframe + groundMesh.material.wireframe = !groundMesh.material.wireframe; } // function changeColors() { // for (let i = 0; i < diceArray.length; i++) { @@ -2301,64 +2762,64 @@ const icosahedronShape = new CANNON.ConvexPolyhedron({ // } function changeBoardBackground(selectedBoard) { - let textureLoader = new THREE.TextureLoader() + let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { - case 'green-board': - console.log('now changing bg to green') + case "green-board": + console.log("now changing bg to green"); textureLoader.load( - 'images/grass_background.png', + "images/grass_background.png", function (groundTexture) { - groundMesh.material.wireframe = false - groundMesh.material.map = groundTexture - groundMesh.material.needsUpdate = true - groundBody.material.friction = 5 - }, - ) - break - case 'wood': - console.log('wood changing') - textureLoader.load('images/wood.png', function (groundTexture) { - groundMesh.material.wireframe = false - groundMesh.material.color.setHex(0xf0c592) - groundMesh.material.map = groundTexture - groundMesh.material.needsUpdate = true - groundBody.material.friction = 3 - }) - break - case 'default': - groundMesh.material.needsUpdate = true - groundMesh.material.color.setHex(0xD3D3D3) - console.log(groundMesh.material.color) - groundMesh.material.wireframe = false - groundMesh.material.map = null - groundBody.material.friction = 1 - break + groundMesh.material.wireframe = false; + groundMesh.material.map = groundTexture; + groundMesh.material.needsUpdate = true; + groundBody.material.friction = 5; + } + ); + break; + case "wood": + console.log("wood changing"); + textureLoader.load("images/wood.png", function (groundTexture) { + groundMesh.material.wireframe = false; + groundMesh.material.color.setHex(0xf0c592); + groundMesh.material.map = groundTexture; + groundMesh.material.needsUpdate = true; + groundBody.material.friction = 3; + }); + break; + case "default": + groundMesh.material.needsUpdate = true; + groundMesh.material.color.setHex(0xd3d3d3); + console.log(groundMesh.material.color); + groundMesh.material.wireframe = false; + groundMesh.material.map = null; + groundBody.material.friction = 1; + break; } } - animate() + animate(); function animate() { - world.step(timeStep) + world.step(timeStep); // cannonDebugger.update(); - groundMesh.position.copy(groundBody.position) - groundMesh.quaternion.copy(groundBody.quaternion) + groundMesh.position.copy(groundBody.position); + groundMesh.quaternion.copy(groundBody.quaternion); // Loop to merge the cannon bodies to the threejs meshes for (let i = 0; i < diceArray.length; i++) { - diceArray[i][0]?.position?.copy(diceArray[i][1].position) - diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion) + diceArray[i][0]?.position?.copy(diceArray[i][1].position); + diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); } - renderer.render(scene, camera) + renderer.render(scene, camera); } - renderer.setAnimationLoop(animate) + renderer.setAnimationLoop(animate); - window.addEventListener('resize', function () { - camera.aspect = window.innerWidth / window.innerHeight - camera.updateProjectionMatrix() - renderer.setSize(window.innerWidth, window.innerHeight) - }) - }) -}) + window.addEventListener("resize", function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }); + }); +}); diff --git a/activities/3DVolume.activity/js/fonts/robot.json b/activities/3DVolume.activity/js/fonts/robot.json deleted file mode 100644 index 4749dd566..000000000 --- a/activities/3DVolume.activity/js/fonts/robot.json +++ /dev/null @@ -1 +0,0 @@ -{"glyphs":{"0":{"ha":783,"x_min":77,"x_max":705,"o":"m 705 376 q 622 86 705 187 q 392 -14 538 -14 q 161 87 246 -14 q 77 376 77 188 l 77 610 q 161 900 77 799 q 391 1002 245 1002 q 621 900 536 1002 q 705 610 705 799 l 705 376 m 572 639 q 525 832 572 767 q 391 897 479 897 q 256 832 302 897 q 210 639 210 767 l 210 349 q 257 156 210 221 q 392 90 304 90 q 526 155 480 90 q 572 349 572 220 l 572 639 z "},"1":{"ha":782,"x_min":126,"x_max":462,"o":"m 462 0 l 328 0 l 328 857 l 126 854 l 126 951 l 462 987 l 462 0 z "},"2":{"ha":782,"x_min":62,"x_max":720,"o":"m 720 0 l 80 0 l 80 92 l 404 451 q 521 605 490 548 q 552 722 552 662 q 509 846 552 795 q 395 897 466 897 q 244 844 292 897 q 195 696 195 791 l 67 696 l 66 701 q 151 915 62 828 q 395 1002 239 1002 q 607 924 528 1002 q 686 726 686 846 q 638 565 686 646 q 504 391 590 484 l 244 108 l 245 104 l 720 104 l 720 0 z "},"3":{"ha":782,"x_min":64,"x_max":691,"o":"m 263 555 l 380 555 q 506 600 469 555 q 543 724 543 644 q 500 852 543 808 q 376 897 458 897 q 251 851 298 897 q 204 729 204 805 l 76 729 l 75 733 q 156 923 71 845 q 376 1002 240 1002 q 595 929 513 1002 q 676 721 676 856 q 639 599 676 660 q 528 507 602 538 q 654 415 617 477 q 691 275 691 353 q 603 63 691 140 q 376 -14 514 -14 q 153 59 241 -14 q 68 256 64 132 l 70 260 l 197 260 q 245 136 197 182 q 376 90 293 90 q 509 136 461 90 q 557 273 557 182 q 515 408 557 364 q 380 451 472 451 l 263 451 l 263 555 z "},"4":{"ha":782,"x_min":37,"x_max":750,"o":"m 614 332 l 750 332 l 750 228 l 614 228 l 614 0 l 481 0 l 481 228 l 37 228 l 37 303 l 473 987 l 614 987 l 614 332 m 180 332 l 481 332 l 481 795 l 477 796 l 464 762 l 180 332 z "},"5":{"ha":782,"x_min":103,"x_max":707,"o":"m 119 446 l 176 987 l 670 987 l 670 869 l 289 869 l 256 591 q 326 630 288 614 q 414 646 364 645 q 629 557 551 648 q 707 315 707 467 q 628 76 707 166 q 395 -14 548 -14 q 186 54 270 -14 q 106 256 103 123 l 108 260 l 229 260 q 275 135 229 179 q 395 90 321 90 q 527 150 480 90 q 574 313 574 210 q 526 469 574 408 q 395 530 479 530 q 281 507 317 530 q 230 434 246 483 l 119 446 z "},"6":{"ha":782,"x_min":90,"x_max":732,"o":"m 458 1002 q 558 990 509 1002 q 641 961 608 979 l 612 859 q 543 886 579 876 q 458 897 507 897 q 287 812 352 897 q 222 591 222 727 l 222 575 q 322 634 266 613 q 442 656 378 656 q 653 564 574 656 q 732 332 732 473 q 648 82 732 179 q 425 -14 564 -14 q 184 91 279 -14 q 90 387 90 196 l 90 576 q 195 884 90 766 q 458 1002 301 1002 m 412 556 q 296 528 344 556 q 222 454 248 500 l 222 374 q 280 164 222 238 q 425 90 338 90 q 552 160 507 90 q 598 332 598 229 q 549 493 598 430 q 412 556 500 556 z "},"7":{"ha":782,"x_min":52,"x_max":720,"o":"m 720 882 q 478 505 541 669 q 388 106 416 340 l 377 0 l 243 0 l 254 106 q 365 524 283 340 q 589 882 447 707 l 52 882 l 52 987 l 720 987 l 720 882 z "},"8":{"ha":782,"x_min":69,"x_max":712,"o":"m 685 730 q 642 594 685 652 q 524 508 598 536 q 661 414 610 478 q 712 267 712 351 q 623 58 712 130 q 391 -14 534 -14 q 158 58 246 -14 q 69 267 69 130 q 119 414 69 351 q 255 508 170 478 q 139 594 181 536 q 97 730 97 652 q 177 931 97 860 q 390 1002 258 1002 q 603 931 520 1002 q 685 730 685 860 m 579 270 q 526 404 579 351 q 390 456 472 456 q 255 404 307 456 q 203 270 203 351 q 254 138 203 186 q 391 90 306 90 q 527 138 475 90 q 579 270 579 186 m 551 727 q 506 848 551 800 q 390 897 460 897 q 275 850 319 897 q 231 727 231 804 q 275 606 231 652 q 391 561 319 561 q 507 606 462 561 q 551 727 551 652 z "},"9":{"ha":782,"x_min":56,"x_max":695,"o":"m 347 90 q 503 164 444 90 q 562 384 562 238 l 562 429 q 478 356 528 380 q 368 331 429 331 q 140 419 224 331 q 56 663 56 508 q 145 907 56 812 q 362 1002 235 1002 q 606 905 517 1002 q 695 621 695 808 l 695 385 q 598 89 695 192 q 347 -14 502 -14 q 240 -4 294 -14 q 144 26 186 5 l 164 128 q 247 99 204 107 q 347 90 290 90 m 368 435 q 491 467 441 435 q 562 549 540 499 l 562 634 q 512 830 562 764 q 366 897 462 897 q 241 831 293 897 q 190 663 190 766 q 238 498 190 562 q 368 435 286 435 z "},"\r":{"ha":345,"x_min":0,"x_max":0,"o":""}," ":{"ha":345,"x_min":0,"x_max":0,"o":""},"!":{"ha":366,"x_min":116,"x_max":250,"o":"m 250 324 l 116 324 l 116 987 l 250 987 l 250 324 m 250 0 l 116 0 l 116 138 l 250 138 l 250 0 z "},"\"":{"ha":453,"x_min":54,"x_max":398,"o":"m 189 875 l 120 705 l 54 705 l 55 868 l 55 1058 l 189 1058 l 189 875 m 398 875 l 330 705 l 264 705 l 264 873 l 264 1058 l 398 1058 l 398 875 z "},"#":{"ha":865,"x_min":47,"x_max":804,"o":"m 483 278 l 310 278 l 256 0 l 153 0 l 208 278 l 47 278 l 47 373 l 227 373 l 273 609 l 99 609 l 99 705 l 292 705 l 347 987 l 450 987 l 394 705 l 567 705 l 623 987 l 725 987 l 669 705 l 804 705 l 804 609 l 651 609 l 605 373 l 753 373 l 753 278 l 586 278 l 532 0 l 429 0 l 483 278 m 329 373 l 502 373 l 548 609 l 375 609 l 329 373 z "},"$":{"ha":782,"x_min":75,"x_max":709,"o":"m 575 255 q 532 359 575 315 q 389 436 488 402 q 182 547 252 477 q 113 731 113 616 q 177 913 113 843 q 353 998 241 984 l 353 1148 l 460 1148 l 460 998 q 637 900 574 981 q 700 683 700 819 l 567 683 q 524 832 567 775 q 406 889 481 889 q 286 847 326 889 q 246 733 246 806 q 287 627 246 667 q 435 549 328 586 q 642 438 574 505 q 709 256 709 371 q 639 71 709 140 q 447 -11 569 2 l 447 -141 l 340 -141 l 340 -12 q 149 74 224 1 q 77 288 75 146 l 79 291 l 208 291 q 260 142 208 186 q 389 98 313 98 q 526 139 477 98 q 575 255 575 181 z "},"%":{"ha":1016,"x_min":71,"x_max":957,"o":"m 71 798 q 126 943 71 884 q 275 1002 182 1002 q 423 943 368 1002 q 479 798 479 884 l 479 745 q 424 601 479 659 q 276 543 368 543 q 126 601 182 543 q 71 745 71 659 l 71 798 m 170 745 q 197 660 170 695 q 276 625 224 625 q 353 660 326 625 q 380 745 380 694 l 380 798 q 352 883 380 848 q 275 919 325 919 q 197 883 224 919 q 170 798 170 848 l 170 745 m 549 242 q 604 387 549 328 q 753 446 660 446 q 901 387 845 446 q 957 242 957 328 l 957 189 q 901 44 957 102 q 754 -14 846 -14 q 605 44 661 -14 q 549 189 549 102 l 549 242 m 648 189 q 675 103 648 138 q 754 68 703 68 q 831 103 804 68 q 858 189 858 138 l 858 242 q 830 328 858 292 q 753 363 802 363 q 675 328 703 363 q 648 242 648 292 l 648 189 m 311 75 l 237 120 l 719 892 l 793 846 l 311 75 z "},"&":{"ha":865,"x_min":43,"x_max":836,"o":"m 43 266 q 91 411 43 349 q 234 535 139 473 q 155 655 181 602 q 130 763 130 707 q 196 940 130 878 q 378 1002 262 1002 q 552 940 485 1002 q 619 791 619 878 q 584 676 619 725 q 478 577 548 628 l 404 523 l 635 245 q 678 343 663 290 q 694 456 694 397 l 813 456 q 787 291 813 367 q 710 155 760 215 l 836 3 l 834 0 l 679 0 l 621 69 q 501 7 567 28 q 365 -14 435 -14 q 131 64 218 -14 q 43 266 43 142 m 365 90 q 461 107 414 90 q 551 155 509 123 l 306 450 l 279 430 q 197 342 217 384 q 177 266 177 300 q 225 140 177 189 q 365 90 273 90 m 263 764 q 281 689 263 728 q 336 604 300 650 l 430 669 q 483 725 469 694 q 496 791 496 755 q 464 865 496 833 q 378 897 431 897 q 293 858 323 897 q 263 764 263 820 z "},"'":{"ha":243,"x_min":54,"x_max":189,"o":"m 189 907 l 120 715 l 54 715 l 55 895 l 55 1058 l 189 1058 l 189 907 z "},"(":{"ha":460,"x_min":90,"x_max":454,"o":"m 90 401 q 197 857 90 668 q 424 1109 305 1046 l 428 1109 l 454 1030 q 292 798 361 958 q 223 402 223 637 l 223 393 q 292 -2 223 158 q 454 -241 360 -162 l 428 -314 l 424 -314 q 197 -62 305 -251 q 90 395 90 127 l 90 401 z "},")":{"ha":466,"x_min":4,"x_max":368,"o":"m 368 395 q 260 -62 368 127 q 34 -314 152 -251 l 30 -314 l 4 -241 q 165 -6 96 -170 q 235 393 235 157 l 235 402 q 163 798 235 634 q 4 1036 92 962 l 30 1109 l 34 1109 q 260 857 152 1046 q 368 401 368 668 l 368 395 z "},"*":{"ha":600,"x_min":19,"x_max":580,"o":"m 220 666 l 19 726 l 52 831 l 253 755 l 246 987 l 355 987 l 349 751 l 547 826 l 580 720 l 375 660 l 506 477 l 417 412 l 294 606 l 175 417 l 85 480 l 220 666 z "},"+":{"ha":788,"x_min":53,"x_max":730,"o":"m 459 531 l 730 531 l 730 410 l 459 410 l 459 99 l 326 99 l 326 410 l 53 410 l 53 531 l 326 531 l 326 818 l 459 818 l 459 531 z "},",":{"ha":274,"x_min":33,"x_max":214,"o":"m 214 33 l 112 -175 l 33 -175 l 80 39 l 80 150 l 214 150 l 214 33 z "},"-":{"ha":380,"x_min":24,"x_max":356,"o":"m 356 365 l 24 365 l 24 469 l 356 469 l 356 365 z "},".":{"ha":372,"x_min":109,"x_max":243,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 z "},"/":{"ha":576,"x_min":11,"x_max":536,"o":"m 125 -85 l 11 -85 l 423 987 l 536 987 l 125 -85 z "},":":{"ha":351,"x_min":109,"x_max":243,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 243 594 l 109 594 l 109 731 l 243 731 l 243 594 z "},";":{"ha":356,"x_min":67,"x_max":248,"o":"m 243 594 l 110 594 l 110 731 l 243 731 l 243 594 m 248 33 l 146 -175 l 67 -175 l 115 39 l 115 150 l 248 150 l 248 33 z "},"<":{"ha":705,"x_min":48,"x_max":602,"o":"m 222 379 l 165 367 l 165 363 l 222 350 l 602 195 l 602 59 l 48 316 l 48 417 l 602 673 l 602 537 l 222 379 z "},"=":{"ha":782,"x_min":103,"x_max":669,"o":"m 669 558 l 103 558 l 103 669 l 669 669 l 669 558 m 669 276 l 103 276 l 103 387 l 669 387 l 669 276 z "},">":{"ha":727,"x_min":92,"x_max":673,"o":"m 92 541 l 92 673 l 673 417 l 673 316 l 92 59 l 92 192 l 498 353 l 555 365 l 555 369 l 498 382 l 92 541 z "},"?":{"ha":661,"x_min":39,"x_max":601,"o":"m 241 278 q 258 414 241 376 q 343 515 274 452 q 439 637 410 594 q 467 740 467 680 q 429 850 467 812 q 319 889 391 889 q 214 856 258 889 q 171 757 171 822 l 43 757 l 41 761 q 118 936 39 870 q 319 1002 197 1002 q 527 933 453 1002 q 601 743 601 865 q 553 583 601 656 q 427 435 505 511 q 382 369 390 398 q 374 278 374 340 l 241 278 m 379 0 l 239 0 l 239 141 l 379 141 l 379 0 z "},"@":{"ha":1243,"x_min":65,"x_max":1186,"o":"m 1175 340 q 1093 91 1168 195 q 868 -14 1018 -14 q 783 14 819 -14 q 731 94 747 42 q 648 13 697 39 q 534 -14 600 -14 q 403 67 450 -14 q 368 282 356 149 q 461 564 384 458 q 651 670 539 670 q 766 652 722 670 q 860 598 809 635 l 857 595 l 861 595 l 827 199 q 841 96 821 124 q 897 68 862 68 q 1030 145 980 68 q 1086 340 1080 222 q 988 744 1097 600 q 652 889 880 889 q 316 732 443 889 q 178 324 189 576 q 280 -79 166 69 q 607 -227 394 -227 q 728 -213 667 -227 q 831 -174 789 -198 l 857 -247 q 742 -291 812 -275 q 604 -307 671 -307 q 206 -138 347 -307 q 77 324 65 31 q 246 791 90 611 q 654 970 402 970 q 1049 801 911 970 q 1175 340 1186 631 m 488 282 q 503 136 481 186 q 575 85 525 85 q 654 102 618 85 q 720 161 690 119 q 720 179 720 170 q 722 199 720 188 l 753 564 q 716 576 736 572 q 676 581 697 581 q 547 507 591 581 q 488 282 502 433 z "},"A":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"B":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"C":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 z "},"D":{"ha":915,"x_min":122,"x_max":854,"o":"m 122 0 l 122 987 l 425 987 q 736 868 619 987 q 854 561 854 749 l 854 426 q 736 118 854 237 q 425 0 619 0 l 122 0 m 256 882 l 256 104 l 425 104 q 641 195 562 104 q 720 426 720 285 l 720 562 q 641 792 720 702 q 425 882 562 882 l 256 882 z "},"E":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"F":{"ha":809,"x_min":122,"x_max":775,"o":"m 706 437 l 256 437 l 256 0 l 122 0 l 122 987 l 775 987 l 775 882 l 256 882 l 256 542 l 706 542 l 706 437 z "},"G":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 z "},"H":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"I":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"J":{"ha":766,"x_min":41,"x_max":653,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 z "},"K":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"L":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 z "},"M":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"N":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 z "},"O":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"P":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"Q":{"ha":947,"x_min":77,"x_max":908,"o":"m 869 406 q 836 227 869 309 q 741 88 802 145 l 908 -70 l 817 -157 l 629 17 q 550 -6 591 1 q 466 -14 509 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"R":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 z "},"S":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 z "},"T":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"U":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 z "},"V":{"ha":878,"x_min":15,"x_max":863,"o":"m 415 245 l 437 168 l 441 168 l 464 245 l 719 987 l 863 987 l 496 0 l 382 0 l 15 987 l 160 987 l 415 245 z "},"W":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 z "},"X":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Y":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"Z":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 z "},"[":{"ha":374,"x_min":97,"x_max":358,"o":"m 358 1023 l 231 1023 l 231 -106 l 358 -106 l 358 -212 l 97 -212 l 97 1128 l 358 1128 l 358 1023 z "},"\\":{"ha":574,"x_min":26,"x_max":565,"o":"m 26 987 l 153 987 l 565 -85 l 439 -85 l 26 987 z "},"]":{"ha":374,"x_min":7,"x_max":269,"o":"m 7 1128 l 269 1128 l 269 -212 l 7 -212 l 7 -106 l 136 -106 l 136 1023 l 7 1023 l 7 1128 z "},"^":{"ha":581,"x_min":41,"x_max":537,"o":"m 165 494 l 41 494 l 244 987 l 335 987 l 537 494 l 414 494 l 302 779 l 291 826 l 287 826 l 276 779 l 165 494 z "},"_":{"ha":631,"x_min":3,"x_max":629,"o":"m 629 -104 l 3 -104 l 3 0 l 629 0 l 629 -104 z "},"`":{"ha":435,"x_min":56,"x_max":332,"o":"m 332 821 l 225 821 l 56 998 l 58 1002 l 214 1002 l 332 821 z "},"a":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 z "},"b":{"ha":789,"x_min":97,"x_max":724,"o":"m 724 339 q 647 83 724 180 q 437 -14 571 -14 q 311 14 365 -14 q 222 97 258 42 l 206 0 l 97 0 l 97 1058 l 231 1058 l 231 647 q 316 722 265 696 q 436 747 368 747 q 648 639 572 747 q 724 353 724 530 l 724 339 m 590 353 q 544 559 590 479 q 402 639 497 639 q 298 609 340 639 q 231 528 256 578 l 231 210 q 298 125 256 155 q 404 94 340 94 q 544 161 498 94 q 590 339 590 229 l 590 353 z "},"c":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 z "},"d":{"ha":789,"x_min":66,"x_max":687,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"e":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 z "},"f":{"ha":479,"x_min":38,"x_max":484,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 z "},"g":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 z "},"h":{"ha":789,"x_min":97,"x_max":694,"o":"m 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 1058 l 231 1058 l 231 635 z "},"i":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 z "},"j":{"ha":359,"x_min":-45,"x_max":251,"o":"m 251 734 l 251 -60 q 193 -235 251 -174 q 31 -296 134 -296 q -8 -293 9 -296 q -45 -284 -25 -290 l -35 -179 q -8 -185 -26 -182 q 21 -187 9 -187 q 91 -157 65 -187 q 117 -60 117 -127 l 117 734 l 251 734 m 247 924 l 113 924 l 113 1058 l 247 1058 l 247 924 z "},"k":{"ha":712,"x_min":98,"x_max":713,"o":"m 318 338 l 231 338 l 231 0 l 98 0 l 98 1058 l 231 1058 l 231 445 l 317 445 l 517 734 l 677 734 l 427 400 l 713 0 l 556 0 l 318 338 z "},"l":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 z "},"m":{"ha":1214,"x_min":97,"x_max":1117,"o":"m 216 734 l 226 637 q 317 719 262 690 q 446 747 372 747 q 571 713 519 747 q 650 612 624 680 q 742 711 685 674 q 875 747 799 747 q 1052 670 987 747 q 1117 439 1117 593 l 1117 0 l 983 0 l 983 440 q 946 594 983 549 q 835 639 909 639 q 724 591 766 639 q 674 471 682 544 l 674 466 l 674 0 l 540 0 l 540 440 q 502 591 540 543 q 391 639 463 639 q 291 614 330 639 q 231 543 252 589 l 231 0 l 97 0 l 97 734 l 216 734 z "},"n":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 z "},"o":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"p":{"ha":789,"x_min":97,"x_max":722,"o":"m 722 339 q 646 83 722 180 q 437 -14 570 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 199 734 l 220 639 q 310 720 256 692 q 435 747 363 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 540 160 491 90 q 589 339 589 229 l 589 353 z "},"q":{"ha":789,"x_min":66,"x_max":680,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 472 722 421 747 q 558 648 523 696 l 578 734 l 680 734 l 680 -282 l 546 -282 l 546 69 q 463 7 511 28 q 353 -14 414 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 115 441 90 q 546 183 519 139 l 546 553 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 z "},"r":{"ha":476,"x_min":97,"x_max":463,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 z "},"s":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 z "},"t":{"ha":480,"x_min":23,"x_max":419,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 z "},"u":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 z "},"v":{"ha":699,"x_min":31,"x_max":675,"o":"m 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 406 0 l 304 0 l 31 734 l 168 734 l 342 216 z "},"w":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 z "},"x":{"ha":699,"x_min":31,"x_max":665,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 z "},"y":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 z "},"z":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 z "},"{":{"ha":472,"x_min":43,"x_max":455,"o":"m 428 -247 q 240 -128 296 -210 q 184 68 184 -47 l 184 208 q 150 326 184 283 q 43 368 115 368 l 43 468 q 150 510 115 468 q 184 628 184 551 l 184 768 q 240 965 184 884 q 428 1083 296 1046 l 455 1004 q 349 919 380 980 q 318 768 318 857 l 318 628 q 289 503 318 557 q 203 418 260 448 q 289 332 260 387 q 318 208 318 277 l 318 68 q 349 -82 318 -21 q 455 -167 380 -142 l 428 -247 z "},"|":{"ha":344,"x_min":119,"x_max":226,"o":"m 226 -183 l 119 -183 l 119 987 l 226 987 l 226 -183 z "},"}":{"ha":472,"x_min":14,"x_max":427,"o":"m 14 -167 q 120 -82 88 -142 q 152 68 152 -21 l 152 208 q 182 334 152 280 q 277 418 213 389 q 182 500 213 446 q 152 628 152 554 l 152 768 q 120 919 152 857 q 14 1004 88 980 l 42 1083 q 230 965 174 1046 q 286 768 286 884 l 286 628 q 320 510 286 551 q 427 468 354 468 l 427 368 q 320 326 354 368 q 286 208 286 283 l 286 68 q 230 -128 286 -47 q 42 -247 174 -210 l 14 -167 z "},"~":{"ha":943,"x_min":87,"x_max":857,"o":"m 857 502 q 798 340 857 409 q 651 272 739 272 q 540 294 590 272 q 433 367 490 317 q 359 416 393 399 q 292 432 326 432 q 215 396 247 432 q 182 309 182 360 l 87 321 q 145 479 87 414 q 292 545 203 545 q 403 521 352 545 q 511 450 454 498 q 583 400 551 416 q 651 385 616 385 q 729 423 696 385 q 762 514 762 462 l 857 502 z "}," ":{"ha":345,"x_min":0,"x_max":0,"o":""},"¡":{"ha":344,"x_min":98,"x_max":231,"o":"m 231 -254 l 98 -254 l 98 410 l 231 410 l 231 -254 m 231 594 l 98 594 l 98 734 l 231 734 l 231 594 z "},"¢":{"ha":761,"x_min":73,"x_max":694,"o":"m 402 90 q 519 131 469 90 q 570 232 570 172 l 690 232 l 692 228 q 626 77 694 145 q 458 -9 557 8 l 458 -166 l 324 -166 l 324 -6 q 137 117 201 18 q 73 353 73 216 l 73 381 q 137 615 73 516 q 324 739 201 713 l 324 894 l 458 894 l 458 743 q 630 654 565 726 q 692 484 694 582 l 691 481 l 570 481 q 522 595 570 548 q 402 642 475 642 q 252 567 297 642 q 207 381 207 491 l 207 353 q 252 165 207 240 q 402 90 296 90 z "},"£":{"ha":810,"x_min":47,"x_max":753,"o":"m 292 417 l 296 316 q 285 200 296 255 q 255 104 275 144 l 753 104 l 753 0 l 91 0 l 91 104 l 98 104 q 146 180 130 113 q 163 316 163 246 l 159 417 l 47 417 l 47 522 l 155 522 l 148 705 q 224 923 148 844 q 427 1002 300 1002 q 637 931 563 1002 q 709 743 712 860 l 708 739 l 579 739 q 536 858 579 819 q 427 897 494 897 q 321 846 360 897 q 281 705 281 795 l 288 522 l 572 522 l 572 417 l 292 417 z "},"¤":{"ha":987,"x_min":71,"x_max":929,"o":"m 744 73 q 629 9 692 31 q 498 -14 566 -14 q 367 8 429 -14 q 253 72 304 31 l 165 -18 l 71 76 l 164 171 q 112 284 130 222 q 94 412 94 345 q 114 545 94 481 q 170 662 133 609 l 71 763 l 165 857 l 262 758 q 372 816 312 795 q 498 837 433 837 q 624 816 564 837 q 735 757 684 795 l 834 858 l 929 763 l 827 659 q 882 544 863 606 q 901 412 901 481 q 883 285 901 346 q 833 173 865 224 l 929 76 l 834 -18 l 744 73 m 218 412 q 299 195 218 285 q 498 105 381 105 q 695 195 613 105 q 777 412 777 285 q 695 628 777 538 q 498 718 613 718 q 299 628 381 718 q 218 412 218 538 z "},"¥":{"ha":843,"x_min":20,"x_max":813,"o":"m 417 538 l 661 987 l 813 987 l 530 500 l 741 500 l 741 395 l 481 395 l 481 304 l 741 304 l 741 199 l 481 199 l 481 0 l 348 0 l 348 199 l 94 199 l 94 304 l 348 304 l 348 395 l 94 395 l 94 500 l 304 500 l 20 987 l 174 987 l 417 538 z "},"¦":{"ha":338,"x_min":98,"x_max":232,"o":"m 98 -183 l 98 354 l 232 354 l 232 -183 l 98 -183 m 232 473 l 98 473 l 98 987 l 232 987 l 232 473 z "},"§":{"ha":854,"x_min":61,"x_max":779,"o":"m 779 292 q 746 181 779 229 q 654 106 713 134 q 725 26 701 72 q 749 -87 749 -20 q 659 -270 749 -203 q 416 -336 568 -336 q 163 -266 266 -336 q 64 -47 61 -197 l 66 -43 l 193 -42 q 259 -185 193 -139 q 416 -231 324 -231 q 562 -190 509 -231 q 615 -88 615 -150 q 566 8 615 -26 q 379 85 517 42 q 140 191 217 127 q 64 374 64 254 q 96 483 64 435 q 186 559 127 531 q 117 640 140 593 q 94 753 94 687 q 185 933 94 865 q 428 1002 276 1002 q 674 926 586 1002 q 760 713 762 850 l 758 709 l 630 709 q 576 843 630 789 q 428 897 523 897 q 279 856 330 897 q 228 754 228 816 q 273 653 228 687 q 462 581 319 620 q 703 472 627 534 q 779 292 779 411 m 409 467 q 349 484 377 475 q 296 503 321 492 q 222 456 247 490 q 197 375 197 422 q 243 272 197 307 q 432 197 290 237 q 495 178 470 186 q 543 163 520 171 q 618 210 591 176 q 646 290 646 243 q 596 388 646 352 q 409 467 546 425 z "},"¨":{"ha":692,"x_min":115,"x_max":583,"o":"m 583 852 l 434 852 l 434 987 l 583 987 l 583 852 m 264 852 l 115 852 l 115 987 l 264 987 l 264 852 z "},"©":{"ha":1088,"x_min":60,"x_max":1022,"o":"m 753 404 l 755 400 q 698 244 758 298 q 532 191 638 191 q 359 263 423 191 q 296 454 296 336 l 296 534 q 359 724 296 652 q 532 797 423 797 q 698 743 638 797 q 755 589 758 690 l 754 585 l 655 585 q 624 679 655 649 q 532 708 593 708 q 433 660 468 708 q 399 535 399 612 l 399 454 q 433 327 399 374 q 532 279 468 279 q 624 309 593 279 q 654 404 654 338 l 753 404 m 142 494 q 258 191 142 315 q 541 68 374 68 q 824 191 707 68 q 941 494 941 315 q 824 795 941 673 q 541 918 707 918 q 258 795 374 918 q 142 494 142 673 m 60 494 q 200 854 60 708 q 541 1001 340 1001 q 882 854 741 1001 q 1022 494 1022 708 q 881 133 1022 280 q 541 -14 741 -14 q 200 133 340 -14 q 60 494 60 280 z "},"ª":{"ha":622,"x_min":81,"x_max":534,"o":"m 416 479 q 407 509 410 493 q 401 543 404 526 q 341 490 379 511 q 250 469 302 469 q 125 511 170 469 q 81 624 81 552 q 139 739 81 699 q 305 780 196 780 l 399 780 l 399 815 q 379 881 399 858 q 319 904 359 904 q 249 886 274 904 q 224 834 224 867 l 115 843 l 114 847 q 167 958 110 913 q 319 1002 224 1002 q 463 954 410 1002 q 516 814 516 905 l 516 602 q 520 538 516 568 q 534 479 524 508 l 416 479 m 279 564 q 350 584 313 564 q 399 628 387 604 l 399 703 l 306 703 q 227 680 255 703 q 199 627 199 658 q 218 580 199 596 q 279 564 238 564 z "},"«":{"ha":655,"x_min":66,"x_max":593,"o":"m 194 350 l 367 80 l 267 80 l 66 344 l 66 357 l 267 621 l 367 621 l 194 350 m 420 350 l 593 80 l 493 80 l 293 344 l 293 357 l 493 621 l 593 621 l 420 350 z "},"¬":{"ha":771,"x_min":86,"x_max":652,"o":"m 652 254 l 518 254 l 518 432 l 86 432 l 86 544 l 652 544 l 652 254 z "},"­":{"ha":380,"x_min":24,"x_max":356,"o":"m 356 365 l 24 365 l 24 469 l 356 469 l 356 365 z "},"®":{"ha":1088,"x_min":60,"x_max":1022,"o":"m 60 494 q 200 854 60 708 q 541 1001 340 1001 q 882 854 741 1001 q 1022 494 1022 708 q 881 133 1022 280 q 541 -14 741 -14 q 200 133 340 -14 q 60 494 60 280 m 142 494 q 258 191 142 314 q 541 68 374 68 q 823 191 707 68 q 940 494 940 315 q 824 796 940 673 q 541 918 707 918 q 258 796 374 918 q 142 494 142 673 m 443 444 l 443 214 l 342 214 l 342 791 l 532 791 q 693 746 635 791 q 752 616 752 702 q 730 543 752 574 q 665 490 707 511 q 729 436 709 472 q 749 349 749 400 l 749 311 q 752 261 749 283 q 761 225 754 239 l 761 214 l 657 214 q 650 256 651 229 q 648 312 648 283 l 648 349 q 626 420 648 397 q 551 444 603 444 l 443 444 m 443 532 l 546 532 q 621 554 590 532 q 652 613 652 575 q 625 683 652 663 q 532 703 598 703 l 443 703 l 443 532 z "},"¯":{"ha":644,"x_min":83,"x_max":572,"o":"m 572 888 l 83 888 l 83 987 l 572 987 l 572 888 z "},"°":{"ha":517,"x_min":87,"x_max":432,"o":"m 87 825 q 138 949 87 897 q 261 1002 190 1002 q 381 949 331 1002 q 432 825 432 897 q 382 701 432 751 q 261 650 332 650 q 138 701 189 650 q 87 825 87 751 m 176 825 q 200 763 176 787 q 261 739 225 739 q 320 763 296 739 q 345 825 345 787 q 320 887 345 862 q 261 913 296 913 q 200 887 225 913 q 176 825 176 862 z "},"±":{"ha":744,"x_min":67,"x_max":688,"o":"m 446 581 l 688 581 l 688 476 l 446 476 l 446 196 l 326 196 l 326 476 l 67 476 l 67 581 l 326 581 l 326 859 l 446 859 l 446 581 m 659 3 l 92 3 l 92 108 l 659 108 l 659 3 z "},"²":{"ha":589,"x_min":77,"x_max":484,"o":"m 484 452 l 83 452 l 83 541 l 288 718 q 349 784 334 759 q 364 838 364 810 q 345 893 364 872 q 286 914 326 914 q 216 892 241 914 q 191 837 191 871 l 82 837 l 81 841 q 134 956 77 908 q 286 1003 191 1003 q 429 960 378 1003 q 481 838 481 916 q 451 745 481 783 q 342 636 420 707 l 238 545 l 239 541 l 484 541 l 484 452 z "},"³":{"ha":594,"x_min":72,"x_max":502,"o":"m 288 771 q 355 790 333 771 q 377 845 377 810 q 352 894 377 874 q 281 913 328 913 q 219 897 243 913 q 196 854 196 881 l 86 854 l 85 858 q 138 962 81 922 q 281 1002 195 1002 q 436 962 379 1002 q 493 847 493 922 q 469 779 493 810 q 403 730 445 748 q 476 682 450 715 q 502 604 502 650 q 441 486 502 528 q 281 444 380 444 q 133 484 195 444 q 76 598 72 523 l 77 602 l 187 602 q 213 552 187 571 q 281 532 238 532 q 358 552 330 532 q 387 605 387 572 q 362 666 387 647 q 288 686 337 686 l 198 686 l 198 771 l 288 771 z "},"´":{"ha":444,"x_min":89,"x_max":372,"o":"m 214 1002 l 370 1002 l 372 998 l 189 821 l 89 821 l 214 1002 z "},"µ":{"ha":789,"x_min":104,"x_max":685,"o":"m 237 734 l 237 298 q 276 134 238 178 q 381 90 313 90 q 489 115 448 90 q 551 186 530 139 l 551 734 l 685 734 l 685 0 l 565 0 l 559 73 q 486 8 529 31 q 387 -14 443 -14 q 301 -3 338 -14 q 237 32 264 8 l 237 -282 l 104 -282 l 104 734 l 237 734 z "},"¶":{"ha":682,"x_min":43,"x_max":567,"o":"m 433 0 l 433 353 l 376 353 q 131 440 218 353 q 43 670 43 528 q 131 899 43 810 q 376 987 218 987 l 567 987 l 567 0 l 433 0 z "},"·":{"ha":366,"x_min":109,"x_max":243,"o":"m 243 423 l 109 423 l 109 567 l 243 567 l 243 423 z "},"¸":{"ha":345,"x_min":81,"x_max":292,"o":"m 198 0 l 190 -35 q 263 -71 234 -43 q 292 -153 292 -98 q 239 -256 292 -218 q 85 -295 185 -295 l 81 -221 q 159 -204 130 -221 q 189 -155 189 -188 q 165 -109 189 -122 q 81 -92 140 -96 l 103 0 l 198 0 z "},"¹":{"ha":378,"x_min":64,"x_max":269,"o":"m 269 451 l 151 451 l 151 895 l 64 895 l 64 986 l 269 1002 l 269 451 z "},"º":{"ha":633,"x_min":81,"x_max":550,"o":"m 81 774 q 145 938 81 875 q 315 1002 209 1002 q 486 938 422 1002 q 550 774 550 875 l 550 695 q 487 531 550 594 q 317 469 423 469 q 145 531 210 469 q 81 695 81 594 l 81 774 m 199 695 q 229 600 199 635 q 317 564 258 564 q 403 600 373 564 q 433 695 433 636 l 433 774 q 403 868 433 831 q 315 904 372 904 q 229 868 258 904 q 199 774 199 831 l 199 695 z "},"»":{"ha":655,"x_min":75,"x_max":610,"o":"m 175 644 l 375 380 l 375 367 l 175 103 l 75 103 l 248 373 l 75 644 l 175 644 m 410 644 l 610 380 l 610 367 l 410 103 l 309 103 l 482 373 l 309 644 l 410 644 z "},"¼":{"ha":1082,"x_min":125,"x_max":1021,"o":"m 329 450 l 211 450 l 211 895 l 125 895 l 125 985 l 329 1001 l 329 450 m 304 80 l 230 125 l 712 897 l 786 852 l 304 80 m 936 191 l 1021 191 l 1021 103 l 936 103 l 936 0 l 821 0 l 821 103 l 538 103 l 532 172 l 819 543 l 936 543 l 936 191 m 656 191 l 821 191 l 821 401 l 817 402 l 808 387 l 656 191 z "},"½":{"ha":1158,"x_min":125,"x_max":1034,"o":"m 313 80 l 239 125 l 721 897 l 795 852 l 313 80 m 329 450 l 211 450 l 211 895 l 125 895 l 125 985 l 329 1001 l 329 450 m 1034 0 l 633 0 l 633 88 l 838 266 q 899 332 884 307 q 914 386 914 357 q 895 441 914 420 q 836 462 876 462 q 766 440 791 462 q 741 385 741 418 l 632 385 l 631 389 q 684 503 627 456 q 836 551 741 551 q 979 507 928 551 q 1031 385 1031 464 q 1001 293 1031 331 q 892 184 970 255 l 788 92 l 789 88 l 1034 88 l 1034 0 z "},"¾":{"ha":1208,"x_min":83,"x_max":1149,"o":"m 428 80 l 354 125 l 836 897 l 910 852 l 428 80 m 1064 191 l 1149 191 l 1149 103 l 1064 103 l 1064 0 l 949 0 l 949 103 l 666 103 l 661 172 l 947 543 l 1064 543 l 1064 191 m 784 191 l 949 191 l 949 401 l 945 402 l 936 387 l 784 191 m 298 772 q 366 791 344 772 q 388 846 388 810 q 363 895 388 875 q 292 914 338 914 q 230 898 254 914 q 207 854 207 882 l 97 854 l 96 859 q 149 963 92 922 q 292 1003 206 1003 q 447 963 390 1003 q 504 848 504 922 q 480 780 504 810 q 414 731 456 749 q 487 683 461 715 q 513 604 513 650 q 452 487 513 529 q 292 445 391 445 q 144 484 205 445 q 87 599 83 524 l 87 603 l 198 603 q 223 552 198 572 q 292 533 249 533 q 369 553 340 533 q 397 606 397 573 q 373 667 397 648 q 298 686 348 686 l 209 686 l 209 772 l 298 772 z "},"¿":{"ha":687,"x_min":77,"x_max":638,"o":"m 437 456 q 420 319 436 357 q 335 219 404 281 q 239 96 267 139 q 211 -5 211 54 q 249 -116 211 -77 q 359 -155 287 -155 q 464 -121 420 -155 q 507 -22 507 -87 l 635 -22 l 637 -26 q 559 -201 638 -136 q 359 -267 480 -267 q 151 -199 224 -267 q 77 -9 77 -131 q 125 150 77 77 q 252 299 172 222 q 296 364 288 335 q 304 456 304 393 l 437 456 m 299 734 l 439 734 l 439 593 l 299 593 l 299 734 z "},"À":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 533 1058 l 426 1058 l 256 1234 l 258 1238 l 414 1238 l 533 1058 z "},"Á":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 541 1236 l 697 1236 l 699 1232 l 515 1055 l 416 1055 l 541 1236 z "},"Â":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 658 1103 l 658 1086 l 549 1086 l 449 1186 l 350 1086 l 241 1086 l 241 1103 l 408 1264 l 490 1264 l 658 1103 z "},"Ã":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 685 1251 q 645 1143 685 1187 q 543 1099 604 1099 q 443 1130 495 1099 q 356 1162 391 1162 q 307 1140 327 1162 q 288 1086 288 1118 l 214 1104 q 254 1214 214 1167 q 356 1260 294 1260 q 451 1228 394 1260 q 543 1196 508 1196 q 591 1218 571 1196 q 612 1272 612 1240 l 685 1251 z "},"Ä":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 684 1088 l 535 1088 l 535 1224 l 684 1224 l 684 1088 m 365 1088 l 216 1088 l 216 1224 l 365 1224 l 365 1088 z "},"Å":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 310 1176 q 351 1272 310 1233 q 451 1310 392 1310 q 549 1272 509 1310 q 590 1176 590 1234 q 550 1081 590 1118 q 451 1044 509 1044 q 351 1081 392 1044 q 310 1176 310 1118 m 380 1176 q 401 1126 380 1147 q 451 1105 422 1105 q 500 1125 479 1105 q 520 1176 520 1145 q 500 1227 520 1206 q 451 1249 479 1249 q 401 1227 422 1249 q 380 1176 380 1206 z "},"Æ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 z "},"Ç":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 511 -5 l 503 -41 q 576 -76 547 -48 q 606 -158 606 -104 q 552 -262 606 -223 q 399 -300 498 -300 l 394 -227 q 473 -210 443 -227 q 503 -160 503 -193 q 478 -115 503 -127 q 395 -97 454 -102 l 416 -5 l 511 -5 z "},"È":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 482 1058 l 375 1058 l 205 1234 l 208 1238 l 363 1238 l 482 1058 z "},"É":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 490 1236 l 646 1236 l 648 1232 l 465 1055 l 365 1055 l 490 1236 z "},"Ê":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 635 1103 l 635 1086 l 526 1086 l 426 1186 l 327 1086 l 218 1086 l 218 1103 l 385 1264 l 467 1264 l 635 1103 z "},"Ë":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 660 1088 l 511 1088 l 511 1224 l 660 1224 l 660 1088 m 341 1088 l 193 1088 l 193 1224 l 341 1224 l 341 1088 z "},"Ì":{"ha":393,"x_min":-23,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 254 1058 l 146 1058 l -23 1234 l -21 1238 l 135 1238 l 254 1058 z "},"Í":{"ha":393,"x_min":129,"x_max":418,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 260 1236 l 416 1236 l 418 1232 l 235 1055 l 135 1055 l 260 1236 z "},"Î":{"ha":393,"x_min":-10,"x_max":406,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 406 1103 l 406 1086 l 297 1086 l 197 1186 l 98 1086 l -10 1086 l -10 1103 l 157 1264 l 238 1264 l 406 1103 z "},"Ï":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ð":{"ha":935,"x_min":28,"x_max":874,"o":"m 142 0 l 142 450 l 28 450 l 28 555 l 142 555 l 142 987 l 446 987 q 757 868 640 987 q 874 561 874 749 l 874 426 q 757 118 874 237 q 446 0 640 0 l 142 0 m 463 450 l 276 450 l 276 104 l 446 104 q 662 195 583 104 q 741 426 741 285 l 741 562 q 662 792 741 702 q 446 882 583 882 l 276 882 l 276 555 l 463 555 l 463 450 z "},"Ñ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 729 1251 q 689 1143 729 1187 q 587 1099 648 1099 q 487 1130 539 1099 q 400 1162 435 1162 q 351 1140 371 1162 q 332 1086 332 1118 l 258 1104 q 298 1214 258 1167 q 400 1260 338 1260 q 495 1228 438 1260 q 587 1196 552 1196 q 635 1218 615 1196 q 656 1272 656 1240 l 729 1251 z "},"Ò":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 530 1072 l 422 1072 l 253 1249 l 255 1253 l 411 1253 l 530 1072 z "},"Ó":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 538 1250 l 694 1250 l 695 1246 l 512 1069 l 412 1069 l 538 1250 z "},"Ô":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 682 1117 l 682 1100 l 573 1100 l 473 1200 l 374 1100 l 266 1100 l 266 1118 l 433 1278 l 514 1278 l 682 1117 z "},"Õ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 709 1265 q 668 1157 709 1201 q 567 1113 628 1113 q 467 1144 519 1113 q 380 1176 414 1176 q 331 1154 351 1176 q 311 1101 311 1132 l 238 1118 q 278 1228 238 1181 q 380 1274 318 1274 q 475 1242 418 1274 q 567 1211 532 1211 q 615 1233 595 1211 q 635 1286 635 1255 l 709 1265 z "},"Ö":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 707 1103 l 559 1103 l 559 1238 l 707 1238 l 707 1103 m 389 1103 l 240 1103 l 240 1238 l 389 1238 l 389 1103 z "},"×":{"ha":743,"x_min":60,"x_max":673,"o":"m 60 238 l 281 465 l 60 691 l 145 777 l 366 551 l 588 777 l 673 691 l 451 465 l 673 238 l 588 153 l 366 378 l 145 153 l 60 238 z "},"Ø":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 357 0 408 -14 q 262 41 306 14 l 201 -64 l 100 -64 l 194 95 q 107 232 137 152 q 77 406 77 312 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 587 984 530 1002 q 693 936 644 967 l 748 1029 l 849 1029 l 760 879 q 841 745 812 822 q 869 581 869 669 l 869 406 m 210 406 q 223 298 210 348 q 260 214 236 249 l 264 213 l 633 834 q 557 877 599 862 q 466 892 515 892 q 280 805 349 892 q 210 583 210 718 l 210 406 m 736 583 q 725 679 736 633 q 693 758 713 724 l 689 759 l 322 141 q 388 106 352 118 q 466 95 424 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Ù":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 529 1058 l 422 1058 l 252 1234 l 254 1238 l 410 1238 l 529 1058 z "},"Ú":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 537 1236 l 693 1236 l 694 1232 l 511 1055 l 412 1055 l 537 1236 z "},"Û":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 682 1103 l 682 1086 l 572 1086 l 473 1186 l 374 1086 l 265 1086 l 265 1103 l 432 1264 l 513 1264 l 682 1103 z "},"Ü":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 707 1088 l 558 1088 l 558 1224 l 707 1224 l 707 1088 m 388 1088 l 239 1088 l 239 1224 l 388 1224 l 388 1088 z "},"Ý":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 488 1235 l 644 1235 l 646 1231 l 463 1055 l 363 1055 l 488 1235 z "},"Þ":{"ha":820,"x_min":111,"x_max":760,"o":"m 244 987 l 244 789 l 427 789 q 672 709 584 789 q 760 500 760 629 q 672 292 760 372 q 427 212 584 212 l 244 212 l 244 0 l 111 0 l 111 987 l 244 987 m 244 684 l 244 317 l 427 317 q 576 370 526 317 q 626 499 626 422 q 576 630 626 576 q 427 684 526 684 l 244 684 z "},"ß":{"ha":828,"x_min":93,"x_max":770,"o":"m 226 0 l 93 0 l 93 734 q 173 970 93 885 q 376 1055 252 1055 q 554 996 486 1055 q 623 825 623 938 q 579 670 623 745 q 535 557 535 595 q 653 405 535 501 q 770 214 770 309 q 700 43 770 101 q 508 -14 629 -14 q 391 0 451 -14 q 306 34 331 14 l 336 142 q 409 106 365 123 q 494 90 453 90 q 602 122 568 90 q 637 208 637 155 q 519 362 637 264 q 401 558 401 459 q 449 687 401 612 q 497 814 497 762 q 463 913 497 877 q 383 950 428 950 q 269 893 313 950 q 226 734 226 837 l 226 0 z "},"à":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 433 840 l 326 840 l 156 1016 l 158 1020 l 314 1020 l 433 840 z "},"á":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 441 1017 l 597 1017 l 598 1013 l 415 837 l 315 837 l 441 1017 z "},"â":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 585 884 l 585 867 l 476 867 l 376 968 l 277 867 l 169 867 l 169 885 l 336 1046 l 417 1046 l 585 884 z "},"ã":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 612 1032 q 571 924 612 968 q 470 880 531 880 q 370 912 422 880 q 283 943 317 943 q 234 921 254 943 q 214 868 214 899 l 141 886 q 181 995 141 949 q 283 1042 221 1042 q 378 1010 321 1042 q 470 978 435 978 q 518 1000 498 978 q 538 1054 538 1022 l 612 1032 z "},"ä":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 610 870 l 462 870 l 462 1006 l 610 1006 l 610 870 m 292 870 l 143 870 l 143 1006 l 292 1006 l 292 870 z "},"å":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 235 958 q 276 1053 235 1015 q 376 1092 317 1092 q 474 1054 433 1092 q 515 958 515 1015 q 474 863 515 899 q 376 826 434 826 q 276 863 317 826 q 235 958 235 899 m 304 958 q 326 908 304 928 q 376 887 347 887 q 425 907 404 887 q 445 958 445 927 q 425 1009 445 988 q 376 1030 404 1030 q 326 1009 347 1030 q 304 958 304 988 z "},"æ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 z "},"ç":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 416 -5 l 408 -41 q 481 -76 452 -48 q 510 -158 510 -104 q 456 -262 510 -223 q 303 -300 403 -300 l 298 -227 q 377 -210 347 -227 q 407 -160 407 -193 q 382 -115 407 -127 q 299 -97 358 -102 l 321 -5 l 416 -5 z "},"è":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 435 840 l 328 840 l 158 1017 l 160 1021 l 316 1021 l 435 840 z "},"é":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 443 1018 l 599 1018 l 600 1014 l 417 838 l 317 838 l 443 1018 z "},"ê":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 587 885 l 587 868 l 478 868 l 378 968 l 279 868 l 171 868 l 171 886 l 338 1046 l 419 1046 l 587 885 z "},"ë":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 612 871 l 464 871 l 464 1006 l 612 1006 l 612 871 m 294 871 l 145 871 l 145 1006 l 294 1006 l 294 871 z "},"ì":{"ha":349,"x_min":-48,"x_max":237,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 229 825 l 121 825 l -48 1002 l -46 1006 l 110 1006 l 229 825 z "},"í":{"ha":349,"x_min":104,"x_max":393,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 235 1003 l 391 1003 l 393 999 l 210 823 l 110 823 l 235 1003 z "},"î":{"ha":349,"x_min":-35,"x_max":381,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 381 870 l 381 853 l 272 853 l 172 954 l 73 853 l -35 853 l -35 871 l 132 1031 l 213 1031 l 381 870 z "},"ï":{"ha":349,"x_min":-61,"x_max":406,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 406 856 l 258 856 l 258 991 l 406 991 l 406 856 m 87 856 l -61 856 l -61 991 l 87 991 l 87 856 z "},"ð":{"ha":815,"x_min":49,"x_max":727,"o":"m 592 880 q 692 716 656 809 q 727 512 727 623 l 727 363 q 629 91 727 197 q 386 -14 532 -14 q 143 81 238 -14 q 49 316 49 176 q 142 567 49 471 q 380 663 236 663 q 495 638 441 663 q 585 572 549 613 l 587 576 q 552 709 581 650 q 477 814 524 769 l 281 702 l 229 771 l 402 870 q 348 897 376 885 q 290 918 319 908 l 330 1029 q 433 994 384 1016 q 524 940 481 971 l 672 1025 l 724 956 l 592 880 m 389 90 q 536 168 479 90 q 593 363 593 245 l 593 463 q 515 531 570 503 q 380 558 460 558 q 237 488 292 558 q 182 316 182 417 q 238 158 182 227 q 389 90 294 90 z "},"ñ":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 627 1032 q 587 924 627 968 q 486 880 547 880 q 385 912 437 880 q 298 943 333 943 q 250 921 269 943 q 230 868 230 899 l 157 886 q 197 995 157 949 q 298 1042 237 1042 q 393 1010 336 1042 q 486 978 450 978 q 534 1000 513 978 q 554 1054 554 1022 l 627 1032 z "},"ò":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 451 840 l 344 840 l 174 1016 l 176 1020 l 332 1020 l 451 840 z "},"ó":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 459 1017 l 615 1017 l 616 1013 l 433 837 l 334 837 l 459 1017 z "},"ô":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 604 884 l 604 867 l 494 867 l 395 968 l 296 867 l 187 867 l 187 885 l 354 1046 l 435 1046 l 604 884 z "},"õ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 630 1032 q 590 924 630 968 q 488 880 549 880 q 388 912 440 880 q 301 943 336 943 q 252 921 272 943 q 233 868 233 899 l 159 886 q 199 995 159 949 q 301 1042 239 1042 q 396 1010 339 1042 q 488 978 453 978 q 536 1000 516 978 q 557 1054 557 1022 l 630 1032 z "},"ö":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 629 870 l 480 870 l 480 1006 l 629 1006 l 629 870 m 310 870 l 161 870 l 161 1006 l 310 1006 l 310 870 z "},"÷":{"ha":793,"x_min":48,"x_max":725,"o":"m 725 405 l 48 405 l 48 532 l 725 532 l 725 405 m 454 677 l 320 677 l 320 815 l 454 815 l 454 677 m 454 122 l 320 122 l 320 260 l 454 260 l 454 122 z "},"ø":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 466 740 431 747 q 532 719 501 732 l 583 820 l 670 820 l 600 677 q 691 548 659 627 q 723 374 723 469 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 329 -8 360 -14 q 269 8 298 -3 l 220 -92 l 133 -92 l 201 47 q 101 176 136 95 q 66 359 66 258 l 66 374 m 199 359 q 213 246 199 297 q 254 162 227 195 l 258 162 l 484 619 q 441 636 464 630 q 393 642 418 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 589 374 q 577 476 589 428 q 544 555 566 524 l 540 555 l 318 106 q 353 94 334 98 q 395 90 372 90 q 540 166 490 90 q 589 359 589 242 l 589 374 z "},"ù":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 450 825 l 342 825 l 173 1002 l 175 1006 l 331 1006 l 450 825 z "},"ú":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 458 1003 l 614 1003 l 615 999 l 432 823 l 332 823 l 458 1003 z "},"û":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 602 870 l 602 853 l 493 853 l 393 954 l 294 853 l 186 853 l 186 871 l 353 1031 l 434 1031 l 602 870 z "},"ü":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 627 856 l 479 856 l 479 991 l 627 991 l 627 856 m 309 856 l 160 856 l 160 991 l 309 991 l 309 856 z "},"ý":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 413 1003 l 569 1003 l 570 999 l 387 823 l 288 823 l 413 1003 z "},"þ":{"ha":804,"x_min":104,"x_max":729,"o":"m 729 339 q 653 83 729 180 q 444 -14 577 -14 q 325 8 376 -14 q 237 77 273 31 l 237 -282 l 104 -282 l 104 1058 l 237 1058 l 237 652 q 324 723 273 699 q 441 747 375 747 q 653 639 578 747 q 729 353 729 531 l 729 339 m 595 353 q 546 558 595 478 q 401 639 497 639 q 303 613 344 639 q 237 541 262 587 l 237 186 q 303 116 262 141 q 403 90 344 90 q 547 160 498 90 q 595 339 595 229 l 595 353 z "},"ÿ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 583 856 l 434 856 l 434 991 l 583 991 l 583 856 m 264 856 l 115 856 l 115 991 l 264 991 l 264 856 z "},"Ā":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 694 1112 l 205 1112 l 205 1211 l 694 1211 l 694 1112 z "},"ā":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 625 894 l 136 894 l 136 993 l 625 993 l 625 894 z "},"Ă":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 z "},"ă":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 z "},"Ą":{"ha":899,"x_min":14,"x_max":923,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 869 0 q 795 -67 822 -35 q 768 -136 768 -100 q 784 -181 768 -165 q 833 -197 799 -197 q 867 -192 852 -197 q 901 -180 883 -187 l 923 -263 q 869 -284 899 -276 q 799 -293 840 -293 q 693 -256 734 -293 q 652 -151 652 -218 q 693 -48 652 -97 q 821 39 734 1 l 869 0 z "},"ą":{"ha":764,"x_min":72,"x_max":723,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 669 0 q 595 -67 622 -35 q 568 -136 568 -100 q 584 -181 568 -165 q 633 -197 599 -197 q 667 -192 652 -197 q 701 -180 683 -187 l 723 -263 q 669 -284 699 -276 q 599 -293 640 -293 q 493 -256 534 -293 q 452 -151 452 -218 q 493 -48 452 -97 q 621 39 534 1 l 669 0 z "},"Ć":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 524 1250 l 680 1250 l 682 1246 l 498 1069 l 399 1069 l 524 1250 z "},"ć":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 429 1017 l 585 1017 l 586 1013 l 403 837 l 303 837 l 429 1017 z "},"Ĉ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 669 1117 l 669 1100 l 559 1100 l 460 1200 l 361 1100 l 252 1100 l 252 1118 l 419 1278 l 500 1278 l 669 1117 z "},"ĉ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 573 884 l 573 867 l 464 867 l 364 968 l 265 867 l 157 867 l 157 885 l 323 1046 l 405 1046 l 573 884 z "},"Ċ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 534 1102 l 386 1102 l 386 1238 l 534 1238 l 534 1102 z "},"ċ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 438 869 l 290 869 l 290 1006 l 438 1006 l 438 869 z "},"Č":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 459 1179 l 559 1279 l 674 1279 l 674 1267 l 500 1101 l 419 1101 l 247 1265 l 247 1279 l 359 1279 l 459 1179 z "},"č":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 363 946 l 463 1046 l 578 1046 l 578 1034 l 404 868 l 323 868 l 151 1033 l 151 1046 l 264 1046 l 363 946 z "},"Ď":{"ha":915,"x_min":122,"x_max":854,"o":"m 122 0 l 122 987 l 425 987 q 736 868 619 987 q 854 561 854 749 l 854 426 q 736 118 854 237 q 425 0 619 0 l 122 0 m 256 882 l 256 104 l 425 104 q 641 195 562 104 q 720 426 720 285 l 720 562 q 641 792 720 702 q 425 882 562 882 l 256 882 m 420 1164 l 519 1265 l 635 1265 l 635 1253 l 460 1086 l 380 1086 l 208 1251 l 208 1265 l 320 1265 l 420 1164 z "},"ď":{"ha":891,"x_min":66,"x_max":888,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 m 888 963 l 805 802 l 739 802 l 781 969 l 781 1058 l 888 1058 l 888 963 z "},"Đ":{"ha":935,"x_min":28,"x_max":874,"o":"m 142 0 l 142 450 l 28 450 l 28 555 l 142 555 l 142 987 l 446 987 q 757 868 640 987 q 874 561 874 749 l 874 426 q 757 118 874 237 q 446 0 640 0 l 142 0 m 463 450 l 276 450 l 276 104 l 446 104 q 662 195 583 104 q 741 426 741 285 l 741 562 q 662 792 741 702 q 446 882 583 882 l 276 882 l 276 555 l 463 555 l 463 450 z "},"đ":{"ha":810,"x_min":66,"x_max":821,"o":"m 821 835 l 687 835 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 835 l 386 835 l 386 940 l 553 940 l 553 1058 l 687 1058 l 687 940 l 821 940 l 821 835 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"Ē":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 674 1112 l 185 1112 l 185 1211 l 674 1211 l 674 1112 z "},"ē":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 627 895 l 138 895 l 138 994 l 627 994 l 627 895 z "},"Ĕ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 625 1268 l 627 1264 q 574 1136 629 1185 q 427 1086 519 1086 q 278 1136 333 1086 q 227 1264 223 1185 l 228 1268 l 330 1268 q 353 1196 330 1223 q 427 1168 376 1168 q 499 1196 475 1168 q 523 1268 523 1224 l 625 1268 z "},"ĕ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 578 1050 l 579 1046 q 527 918 582 968 q 379 869 472 869 q 231 918 286 869 q 179 1046 176 968 l 180 1050 l 283 1050 q 306 978 283 1006 q 379 950 329 950 q 452 978 428 950 q 475 1050 475 1006 l 578 1050 z "},"Ė":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 500 1088 l 352 1088 l 352 1224 l 500 1224 l 500 1088 z "},"ė":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 452 870 l 304 870 l 304 1006 l 452 1006 l 452 870 z "},"Ę":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 488 0 q 413 -67 440 -35 q 387 -136 387 -100 q 402 -181 387 -165 q 452 -197 417 -197 q 486 -192 470 -197 q 519 -180 501 -187 l 541 -263 q 487 -284 517 -276 q 417 -293 458 -293 q 312 -256 353 -293 q 271 -151 271 -218 q 312 -48 271 -97 q 439 39 353 1 l 488 0 z "},"ę":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 597 42 q 523 -25 549 7 q 496 -94 496 -58 q 511 -139 496 -123 q 561 -155 526 -155 q 595 -150 579 -155 q 628 -138 610 -145 l 650 -221 q 596 -242 626 -234 q 526 -251 567 -251 q 421 -214 462 -251 q 380 -109 380 -176 q 421 -6 380 -55 q 548 81 462 43 l 597 42 z "},"Ě":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 425 1164 l 525 1265 l 640 1265 l 640 1253 l 466 1086 l 385 1086 l 213 1251 l 213 1265 l 326 1265 l 425 1164 z "},"ě":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 378 947 l 477 1047 l 593 1047 l 593 1035 l 418 869 l 338 869 l 165 1034 l 165 1047 l 278 1047 l 378 947 z "},"Ĝ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 662 1117 l 662 1100 l 553 1100 l 453 1200 l 354 1100 l 245 1100 l 245 1118 l 412 1278 l 494 1278 l 662 1117 z "},"ĝ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 591 884 l 591 867 l 482 867 l 382 968 l 283 867 l 175 867 l 175 885 l 342 1046 l 423 1046 l 591 884 z "},"Ğ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 652 1282 l 654 1278 q 602 1150 656 1200 q 454 1101 547 1101 q 305 1150 360 1101 q 254 1278 250 1200 l 255 1282 l 357 1282 q 380 1210 357 1238 q 454 1182 404 1182 q 526 1210 503 1182 q 550 1282 550 1238 l 652 1282 z "},"ğ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 582 1050 l 583 1046 q 531 918 586 967 q 383 868 476 868 q 235 918 290 868 q 183 1046 180 967 l 184 1050 l 287 1050 q 310 977 287 1005 q 383 949 333 949 q 456 978 432 949 q 479 1050 479 1006 l 582 1050 z "},"Ġ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 527 1102 l 379 1102 l 379 1238 l 527 1238 l 527 1102 z "},"ġ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 456 869 l 309 869 l 309 1006 l 456 1006 l 456 869 z "},"Ģ":{"ha":947,"x_min":81,"x_max":838,"o":"m 838 131 q 715 31 802 77 q 490 -14 628 -14 q 197 99 313 -14 q 81 392 81 212 l 81 595 q 189 888 81 775 q 467 1002 297 1002 q 734 918 637 1002 q 833 703 831 835 l 831 699 l 704 699 q 638 841 698 785 q 467 897 579 897 q 285 812 354 897 q 216 597 216 727 l 216 392 q 293 175 216 260 q 490 90 370 90 q 627 113 574 90 q 704 163 681 135 l 704 388 l 488 388 l 488 493 l 838 493 l 838 131 m 502 -174 l 419 -335 l 353 -335 l 395 -168 l 395 -79 l 502 -79 l 502 -174 z "},"ģ":{"ha":789,"x_min":68,"x_max":692,"o":"m 68 353 q 145 639 68 530 q 359 747 222 747 q 481 719 429 747 q 570 638 534 691 l 586 734 l 692 734 l 692 -4 q 610 -221 692 -145 q 374 -296 528 -296 q 259 -282 321 -296 q 151 -242 198 -267 l 185 -138 q 272 -171 221 -159 q 372 -184 323 -184 q 514 -139 470 -184 q 559 -4 559 -95 l 559 79 q 473 9 523 33 q 357 -14 422 -14 q 145 83 222 -14 q 68 339 68 181 l 68 353 m 201 339 q 248 161 201 229 q 391 94 295 94 q 492 122 451 94 q 559 199 532 149 l 559 537 q 492 611 533 584 q 392 639 450 639 q 249 559 296 639 q 201 353 201 479 l 201 339 m 311 950 l 393 1118 l 459 1118 l 445 943 l 445 850 l 311 850 l 311 950 z "},"Ĥ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 698 1103 l 698 1086 l 589 1086 l 489 1186 l 390 1086 l 281 1086 l 281 1103 l 448 1264 l 530 1264 l 698 1103 z "},"ĥ":{"ha":789,"x_min":97,"x_max":694,"o":"m 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 1058 l 231 1058 l 231 635 m 554 1102 l 554 1085 l 445 1085 l 345 1185 l 246 1085 l 138 1085 l 138 1103 l 304 1263 l 386 1263 l 554 1102 z "},"Ħ":{"ha":976,"x_min":21,"x_max":965,"o":"m 867 793 l 965 793 l 965 694 l 867 694 l 867 0 l 734 0 l 734 436 l 254 436 l 254 0 l 121 0 l 121 694 l 21 694 l 21 793 l 121 793 l 121 987 l 254 987 l 254 793 l 734 793 l 734 987 l 867 987 l 867 793 m 254 541 l 734 541 l 734 694 l 254 694 l 254 541 z "},"ħ":{"ha":810,"x_min":1,"x_max":715,"o":"m 435 835 l 251 835 l 251 635 q 344 718 289 688 q 467 747 399 747 q 649 677 584 747 q 715 460 715 606 l 715 0 l 581 0 l 581 461 q 542 595 581 552 q 426 639 503 639 q 325 613 370 639 q 251 542 280 587 l 251 0 l 117 0 l 117 835 l 1 835 l 1 940 l 117 940 l 117 1058 l 251 1058 l 251 940 l 435 940 l 435 835 z "},"Ĩ":{"ha":393,"x_min":-38,"x_max":433,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 433 1251 q 392 1143 433 1187 q 291 1099 352 1099 q 191 1130 243 1099 q 104 1162 138 1162 q 55 1140 75 1162 q 35 1086 35 1118 l -38 1104 q 2 1214 -38 1167 q 104 1260 42 1260 q 199 1228 142 1260 q 291 1196 256 1196 q 339 1218 319 1196 q 359 1272 359 1240 l 433 1251 z "},"ĩ":{"ha":349,"x_min":-63,"x_max":408,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 408 1018 q 367 910 408 954 q 266 866 327 866 q 165 898 218 866 q 79 929 113 929 q 30 907 50 929 q 10 854 10 885 l -63 871 q -23 981 -63 935 q 79 1027 17 1027 q 174 996 117 1027 q 266 964 231 964 q 314 986 294 964 q 334 1040 334 1008 l 408 1018 z "},"Ī":{"ha":393,"x_min":-43,"x_max":446,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 446 1112 l -43 1112 l -43 1211 l 446 1211 l 446 1112 z "},"ī":{"ha":349,"x_min":-68,"x_max":420,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 420 881 l -68 881 l -68 980 l 420 980 l 420 881 z "},"Ĭ":{"ha":393,"x_min":-5,"x_max":401,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 397 1268 l 398 1264 q 346 1136 401 1185 q 198 1086 291 1086 q 50 1136 104 1086 q -2 1264 -5 1185 l -1 1268 l 102 1268 q 125 1196 102 1223 q 198 1168 148 1168 q 271 1196 247 1168 q 294 1268 294 1224 l 397 1268 z "},"ĭ":{"ha":349,"x_min":-31,"x_max":376,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 372 1036 l 373 1031 q 321 903 376 953 q 173 854 266 854 q 24 903 79 854 q -27 1031 -31 953 l -26 1036 l 77 1036 q 100 963 77 991 q 173 935 123 935 q 245 963 222 935 q 269 1036 269 991 l 372 1036 z "},"Į":{"ha":393,"x_min":31,"x_max":302,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 248 0 q 174 -67 201 -35 q 147 -136 147 -100 q 162 -181 147 -165 q 212 -197 178 -197 q 246 -192 231 -197 q 279 -180 262 -187 l 302 -263 q 248 -284 277 -276 q 178 -293 218 -293 q 72 -256 113 -293 q 31 -151 31 -218 q 72 -48 31 -97 q 199 39 113 1 l 248 0 z "},"į":{"ha":350,"x_min":9,"x_max":280,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 227 0 q 152 -67 179 -35 q 125 -136 125 -100 q 141 -181 125 -165 q 191 -197 156 -197 q 224 -192 209 -197 q 258 -180 240 -187 l 280 -263 q 226 -284 256 -276 q 156 -293 197 -293 q 51 -256 92 -293 q 9 -151 9 -218 q 51 -48 9 -97 q 178 39 92 1 l 227 0 z "},"İ":{"ha":393,"x_min":122,"x_max":270,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 270 1088 l 122 1088 l 122 1224 l 270 1224 l 270 1088 z "},"ı":{"ha":349,"x_min":104,"x_max":237,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 z "},"IJ":{"ha":1159,"x_min":129,"x_max":1046,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 912 987 l 1046 987 l 1046 273 q 962 63 1046 141 q 745 -14 878 -14 q 518 58 602 -14 q 437 268 434 130 l 439 272 l 567 272 q 613 135 567 180 q 745 90 660 90 q 865 140 819 90 q 912 273 912 190 l 912 987 z "},"ij":{"ha":709,"x_min":108,"x_max":601,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 601 734 l 601 -60 q 543 -235 601 -174 q 380 -296 484 -296 q 342 -293 359 -296 q 305 -284 325 -290 l 315 -179 q 342 -185 324 -182 q 371 -187 359 -187 q 441 -157 415 -187 q 467 -60 467 -127 l 467 734 l 601 734 m 597 924 l 463 924 l 463 1058 l 597 1058 l 597 924 z "},"Ĵ":{"ha":766,"x_min":41,"x_max":789,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 m 789 1095 l 789 1078 l 680 1078 l 581 1178 l 481 1078 l 373 1078 l 373 1095 l 540 1256 l 621 1256 l 789 1095 z "},"ĵ":{"ha":355,"x_min":-46,"x_max":393,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 m 393 858 l 393 841 l 284 841 l 184 941 l 85 841 l -23 841 l -23 859 l 144 1019 l 225 1019 l 393 858 z "},"Ķ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 479 -163 l 396 -323 l 330 -323 l 372 -157 l 372 -68 l 479 -68 l 479 -163 z "},"ķ":{"ha":712,"x_min":98,"x_max":713,"o":"m 318 338 l 231 338 l 231 0 l 98 0 l 98 1058 l 231 1058 l 231 445 l 317 445 l 517 734 l 677 734 l 427 400 l 713 0 l 556 0 l 318 338 m 412 -161 l 329 -322 l 263 -322 l 304 -155 l 304 -66 l 412 -66 l 412 -161 z "},"ĸ":{"ha":776,"x_min":104,"x_max":738,"o":"m 306 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 714 734 l 715 730 l 410 382 l 738 3 l 736 0 l 573 0 l 306 311 z "},"Ĺ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 252 1236 l 408 1236 l 409 1232 l 226 1055 l 126 1055 l 252 1236 z "},"ĺ":{"ha":350,"x_min":108,"x_max":397,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 239 1261 l 395 1261 l 397 1257 l 214 1081 l 114 1081 l 239 1261 z "},"Ļ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 474 -161 l 391 -322 l 326 -322 l 367 -155 l 367 -66 l 474 -66 l 474 -161 z "},"ļ":{"ha":350,"x_min":75,"x_max":241,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 223 -161 l 140 -322 l 75 -322 l 116 -155 l 116 -66 l 223 -66 l 223 -161 z "},"Ľ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 544 893 l 461 732 l 395 732 l 437 899 l 437 988 l 544 988 l 544 893 z "},"ľ":{"ha":452,"x_min":108,"x_max":456,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 456 963 l 373 802 l 307 802 l 349 969 l 349 1058 l 456 1058 l 456 963 z "},"Ŀ":{"ha":750,"x_min":122,"x_max":723,"o":"m 256 104 l 723 104 l 723 0 l 122 0 l 122 987 l 256 987 l 256 104 m 564 467 l 416 467 l 416 603 l 564 603 l 564 467 z "},"ŀ":{"ha":499,"x_min":108,"x_max":477,"o":"m 241 0 l 108 0 l 108 1058 l 241 1058 l 241 0 m 477 455 l 329 455 l 329 591 l 477 591 l 477 455 z "},"Ł":{"ha":730,"x_min":27,"x_max":711,"o":"m 244 572 l 427 630 l 427 517 l 244 459 l 244 104 l 711 104 l 711 0 l 111 0 l 111 417 l 27 391 l 27 503 l 111 530 l 111 987 l 244 987 l 244 572 z "},"ł":{"ha":378,"x_min":25,"x_max":357,"o":"m 255 591 l 357 631 l 357 519 l 255 479 l 255 0 l 121 0 l 121 429 l 25 392 l 25 504 l 121 541 l 121 1058 l 255 1058 l 255 591 z "},"Ń":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 558 1236 l 714 1236 l 715 1232 l 532 1055 l 433 1055 l 558 1236 z "},"ń":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 456 1017 l 612 1017 l 614 1013 l 431 837 l 331 837 l 456 1017 z "},"Ņ":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 542 -161 l 459 -322 l 393 -322 l 435 -155 l 435 -66 l 542 -66 l 542 -161 z "},"ņ":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 440 -161 l 357 -322 l 292 -322 l 333 -155 l 333 -66 l 440 -66 l 440 -161 z "},"Ň":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 m 493 1164 l 593 1265 l 708 1265 l 708 1253 l 534 1086 l 453 1086 l 281 1251 l 281 1265 l 393 1265 l 493 1164 z "},"ň":{"ha":789,"x_min":97,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 391 946 l 491 1046 l 606 1046 l 606 1034 l 432 868 l 351 868 l 179 1033 l 179 1046 l 292 1046 l 391 946 z "},"ʼn":{"ha":789,"x_min":-22,"x_max":692,"o":"m 216 734 l 226 625 q 318 715 262 683 q 444 747 373 747 q 627 678 562 747 q 692 463 692 608 l 692 0 l 559 0 l 559 460 q 520 598 559 557 q 404 639 482 639 q 301 611 346 639 q 231 535 257 583 l 231 0 l 97 0 l 97 734 l 216 734 m 127 963 l 44 802 l -22 802 l 20 969 l 20 1058 l 127 1058 l 127 963 z "},"Ŋ":{"ha":960,"x_min":109,"x_max":856,"o":"m 856 987 l 856 -60 q 798 -235 856 -174 q 635 -296 739 -296 q 596 -293 614 -296 q 559 -284 578 -290 l 568 -182 q 600 -189 577 -186 q 635 -191 623 -191 q 699 -156 675 -191 q 722 -60 722 -121 l 722 0 l 247 754 l 243 753 l 243 0 l 109 0 l 109 987 l 243 987 l 718 233 l 722 235 l 722 987 l 856 987 z "},"ŋ":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 622 678 557 747 q 687 463 687 608 l 687 -60 q 629 -235 687 -174 q 466 -296 570 -296 q 427 -293 445 -296 q 390 -284 409 -290 l 399 -176 q 431 -181 408 -179 q 466 -184 454 -184 q 530 -152 507 -184 q 553 -60 553 -120 l 553 460 q 515 598 553 557 q 397 639 476 639 q 298 616 340 639 q 231 554 256 594 l 231 0 l 97 0 l 97 734 l 216 734 z "},"Ō":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 722 1126 l 233 1126 l 233 1225 l 722 1225 l 722 1126 z "},"ō":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 643 894 l 154 894 l 154 993 l 643 993 l 643 894 z "},"Ŏ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 673 1282 l 674 1278 q 622 1150 677 1200 q 474 1101 567 1101 q 326 1150 380 1101 q 274 1278 271 1200 l 275 1282 l 378 1282 q 401 1210 378 1238 q 474 1182 424 1182 q 547 1210 523 1182 q 570 1282 570 1238 l 673 1282 z "},"ŏ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 594 1050 l 595 1046 q 543 918 598 967 q 395 868 488 868 q 247 918 302 868 q 195 1046 192 967 l 197 1050 l 299 1050 q 322 977 299 1005 q 395 949 345 949 q 468 978 444 949 q 492 1050 492 1006 l 594 1050 z "},"Ő":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 659 1280 l 814 1280 l 817 1276 l 613 1100 l 497 1100 l 496 1103 l 659 1280 m 438 1280 l 581 1280 l 583 1277 l 420 1100 l 313 1100 l 438 1280 z "},"ő":{"ha":789,"x_min":66,"x_max":738,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 581 1048 l 736 1048 l 738 1044 l 534 867 l 418 867 l 417 871 l 581 1048 m 359 1048 l 503 1048 l 504 1044 l 341 867 l 235 867 l 359 1048 z "},"Œ":{"ha":1329,"x_min":71,"x_max":1269,"o":"m 1269 0 l 646 0 q 540 -11 584 -7 q 450 -14 496 -14 q 176 98 281 -14 q 71 391 71 211 l 71 597 q 175 889 71 777 q 449 1002 280 1002 q 544 998 496 1002 q 646 987 593 994 l 1261 987 l 1261 882 l 747 882 l 747 563 l 1200 563 l 1200 458 l 747 458 l 747 104 l 1269 104 l 1269 0 m 450 91 q 533 94 492 91 q 614 103 574 97 l 614 884 q 531 893 572 889 q 449 897 490 897 q 269 820 334 897 q 204 598 204 743 l 204 391 q 270 168 204 245 q 450 91 336 91 z "},"œ":{"ha":1257,"x_min":66,"x_max":1209,"o":"m 929 -14 q 771 21 840 -14 q 659 122 703 57 q 548 21 615 57 q 395 -14 481 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 154 642 66 536 q 393 747 242 747 q 549 710 481 747 q 661 607 617 673 q 770 710 705 673 q 909 747 836 747 q 1133 658 1057 747 q 1209 419 1209 568 l 1209 336 l 733 336 l 731 332 q 785 158 733 227 q 929 90 836 90 q 1048 110 997 90 q 1137 163 1099 129 l 1189 76 q 1085 12 1149 37 q 929 -14 1021 -14 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 909 642 q 794 586 842 642 q 736 444 747 529 l 737 440 l 1076 440 l 1076 458 q 1034 589 1076 536 q 909 642 991 642 z "},"Ŕ":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 485 1236 l 641 1236 l 642 1232 l 459 1055 l 359 1055 l 485 1236 z "},"ŕ":{"ha":476,"x_min":97,"x_max":502,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 345 1017 l 500 1017 l 502 1013 l 319 837 l 219 837 l 345 1017 z "},"Ŗ":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 469 -161 l 386 -322 l 320 -322 l 361 -155 l 361 -66 l 469 -66 l 469 -161 z "},"ŗ":{"ha":476,"x_min":73,"x_max":463,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 221 -161 l 138 -322 l 73 -322 l 114 -155 l 114 -66 l 221 -66 l 221 -161 z "},"Ř":{"ha":920,"x_min":122,"x_max":855,"o":"m 256 428 l 256 0 l 122 0 l 122 987 l 479 987 q 727 915 642 987 q 812 706 812 843 q 773 574 812 630 q 657 484 733 517 q 774 398 739 458 q 810 252 810 339 l 810 159 q 820 76 810 113 q 855 16 830 39 l 855 0 l 718 0 q 684 68 691 23 q 676 160 676 113 l 676 250 q 629 379 676 330 q 504 428 583 428 l 256 428 m 256 533 l 466 533 q 629 576 579 533 q 679 707 679 619 q 630 837 679 791 q 479 882 582 882 l 256 882 l 256 533 m 420 1164 l 519 1265 l 635 1265 l 635 1253 l 460 1086 l 380 1086 l 208 1251 l 208 1265 l 320 1265 l 420 1164 z "},"ř":{"ha":476,"x_min":68,"x_max":496,"o":"m 444 616 l 376 620 q 287 595 323 620 q 231 524 250 570 l 231 0 l 97 0 l 97 734 l 216 734 l 229 627 q 306 715 260 684 q 412 747 352 747 q 440 745 427 747 q 463 740 454 743 l 444 616 m 281 946 l 380 1046 l 496 1046 l 496 1034 l 321 868 l 241 868 l 68 1033 l 68 1046 l 181 1046 l 281 946 z "},"Ś":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 480 1250 l 636 1250 l 637 1246 l 454 1069 l 355 1069 l 480 1250 z "},"ś":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 427 1017 l 583 1017 l 585 1013 l 401 837 l 302 837 l 427 1017 z "},"Ŝ":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 625 1117 l 625 1100 l 515 1100 l 416 1200 l 317 1100 l 208 1100 l 208 1118 l 375 1278 l 456 1278 l 625 1117 z "},"ŝ":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 572 884 l 572 867 l 463 867 l 363 968 l 264 867 l 155 867 l 155 885 l 322 1046 l 404 1046 l 572 884 z "},"Ş":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 467 -5 l 459 -41 q 532 -76 503 -48 q 562 -158 562 -104 q 508 -262 562 -223 q 355 -300 454 -300 l 350 -227 q 429 -210 399 -227 q 458 -160 458 -193 q 434 -115 458 -127 q 351 -97 410 -102 l 372 -5 l 467 -5 z "},"ş":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 414 -5 l 406 -40 q 479 -75 450 -47 q 509 -157 509 -103 q 455 -261 509 -222 q 302 -300 401 -300 l 297 -226 q 376 -209 346 -226 q 406 -159 406 -193 q 381 -114 406 -127 q 298 -96 357 -101 l 319 -5 l 414 -5 z "},"Š":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 415 1179 l 515 1279 l 630 1279 l 630 1267 l 456 1101 l 375 1101 l 203 1265 l 203 1279 l 315 1279 l 415 1179 z "},"š":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 362 946 l 462 1046 l 577 1046 l 577 1034 l 403 868 l 322 868 l 150 1033 l 150 1046 l 262 1046 l 362 946 z "},"Ţ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 457 -161 l 374 -322 l 309 -322 l 350 -155 l 350 -66 l 457 -66 l 457 -161 z "},"ţ":{"ha":480,"x_min":23,"x_max":419,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 m 357 -168 l 275 -329 l 209 -329 l 250 -162 l 250 -73 l 357 -73 l 357 -168 z "},"Ť":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 408 1164 l 508 1264 l 623 1264 l 623 1252 l 449 1086 l 368 1086 l 196 1251 l 196 1264 l 309 1264 l 408 1164 z "},"ť":{"ha":507,"x_min":23,"x_max":518,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 m 518 981 l 435 821 l 370 821 l 411 987 l 411 1076 l 518 1076 l 518 981 z "},"Ŧ":{"ha":814,"x_min":23,"x_max":791,"o":"m 623 556 l 473 556 l 473 0 l 340 0 l 340 556 l 188 556 l 188 661 l 340 661 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 l 473 882 l 473 661 l 623 661 l 623 556 z "},"ŧ":{"ha":480,"x_min":-5,"x_max":429,"o":"m 273 911 l 273 734 l 412 734 l 412 635 l 273 635 l 273 512 l 429 512 l 429 407 l 273 407 l 273 189 q 295 117 273 138 q 351 96 316 96 q 377 98 363 96 q 401 105 391 101 l 419 14 q 375 -6 404 1 q 317 -14 347 -14 q 188 35 236 -14 q 140 189 140 84 l 140 407 l -5 407 l -5 512 l 140 512 l 140 635 l 23 635 l 23 734 l 140 734 l 140 911 l 273 911 z "},"Ũ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 708 1251 q 668 1143 708 1187 q 566 1099 627 1099 q 466 1130 518 1099 q 379 1162 414 1162 q 330 1140 350 1162 q 311 1086 311 1118 l 237 1104 q 277 1214 237 1167 q 379 1260 317 1260 q 474 1228 417 1260 q 566 1196 531 1196 q 614 1218 594 1196 q 635 1272 635 1240 l 708 1251 z "},"ũ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 629 1018 q 588 910 629 954 q 487 866 548 866 q 387 898 439 866 q 300 929 334 929 q 251 907 271 929 q 231 854 231 885 l 158 871 q 198 981 158 935 q 300 1027 238 1027 q 395 996 338 1027 q 487 964 452 964 q 535 986 515 964 q 555 1040 555 1008 l 629 1018 z "},"Ū":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 721 1112 l 232 1112 l 232 1211 l 721 1211 l 721 1112 z "},"ū":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 642 881 l 153 881 l 153 980 l 642 980 l 642 881 z "},"Ŭ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 672 1268 l 673 1264 q 621 1136 676 1185 q 473 1086 566 1086 q 325 1136 380 1086 q 273 1264 270 1185 l 275 1268 l 377 1268 q 400 1196 377 1223 q 473 1168 423 1168 q 546 1196 522 1168 q 570 1268 570 1224 l 672 1268 z "},"ŭ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 593 1036 l 594 1031 q 542 903 597 953 q 394 854 487 854 q 245 903 300 854 q 194 1031 191 953 l 195 1036 l 298 1036 q 321 963 298 991 q 394 935 344 935 q 467 963 443 935 q 490 1036 490 991 l 593 1036 z "},"Ů":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 331 1176 q 372 1272 331 1233 q 472 1310 413 1310 q 570 1272 530 1310 q 611 1176 611 1234 q 571 1081 611 1118 q 472 1044 530 1044 q 372 1081 413 1044 q 331 1176 331 1118 m 401 1176 q 422 1126 401 1147 q 472 1105 443 1105 q 521 1125 500 1105 q 541 1176 541 1145 q 521 1227 541 1206 q 472 1249 500 1249 q 422 1227 443 1249 q 401 1176 401 1206 z "},"ů":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 252 943 q 293 1039 252 1000 q 393 1078 334 1078 q 491 1039 450 1078 q 532 943 532 1001 q 491 848 532 885 q 393 812 451 812 q 293 848 334 812 q 252 943 252 885 m 321 943 q 342 893 321 914 q 393 873 363 873 q 441 893 421 873 q 462 943 462 913 q 441 995 462 974 q 393 1016 421 1016 q 342 995 363 1016 q 321 943 321 974 z "},"Ű":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 659 1266 l 814 1266 l 816 1262 l 612 1086 l 496 1086 l 495 1089 l 659 1266 m 437 1266 l 581 1266 l 582 1263 l 419 1086 l 313 1086 l 437 1266 z "},"ű":{"ha":789,"x_min":94,"x_max":736,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 579 1034 l 734 1034 l 736 1029 l 533 853 l 417 853 l 416 857 l 579 1034 m 358 1034 l 501 1034 l 503 1030 l 340 853 l 233 853 l 358 1034 z "},"Ų":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 551 0 q 477 -67 504 -35 q 450 -136 450 -100 q 466 -181 450 -165 q 515 -197 481 -197 q 549 -192 534 -197 q 583 -180 565 -187 l 605 -263 q 551 -284 581 -276 q 481 -293 522 -293 q 375 -256 416 -293 q 334 -151 334 -218 q 375 -48 334 -97 q 503 39 416 1 l 551 0 z "},"ų":{"ha":789,"x_min":94,"x_max":728,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 675 0 q 601 -67 627 -35 q 574 -136 574 -100 q 589 -181 574 -165 q 639 -197 604 -197 q 673 -192 657 -197 q 706 -180 688 -187 l 728 -263 q 674 -284 704 -276 q 604 -293 645 -293 q 499 -256 540 -293 q 458 -151 458 -218 q 499 -48 458 -97 q 626 39 540 1 l 675 0 z "},"Ŵ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 818 1103 l 818 1086 l 709 1086 l 609 1186 l 510 1086 l 401 1086 l 401 1103 l 568 1264 l 650 1264 l 818 1103 z "},"ŵ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 733 870 l 733 853 l 624 853 l 524 954 l 425 853 l 317 853 l 317 871 l 484 1031 l 565 1031 l 733 870 z "},"Ŷ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 633 1102 l 633 1085 l 524 1085 l 424 1185 l 325 1085 l 216 1085 l 216 1103 l 383 1263 l 465 1263 l 633 1102 z "},"ŷ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 557 870 l 557 853 l 448 853 l 349 954 l 250 853 l 141 853 l 141 871 l 308 1031 l 389 1031 l 557 870 z "},"Ÿ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 658 1088 l 509 1088 l 509 1223 l 658 1223 l 658 1088 m 339 1088 l 191 1088 l 191 1223 l 339 1223 l 339 1088 z "},"Ź":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 477 1236 l 633 1236 l 635 1232 l 452 1055 l 352 1055 l 477 1236 z "},"ź":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 420 1003 l 576 1003 l 577 999 l 394 823 l 294 823 l 420 1003 z "},"Ż":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 487 1088 l 339 1088 l 339 1224 l 487 1224 l 487 1088 z "},"ż":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 429 855 l 281 855 l 281 991 l 429 991 l 429 855 z "},"Ž":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 m 412 1164 l 512 1265 l 627 1265 l 627 1253 l 453 1086 l 372 1086 l 200 1251 l 200 1265 l 313 1265 l 412 1164 z "},"ž":{"ha":699,"x_min":64,"x_max":647,"o":"m 223 104 l 647 104 l 647 0 l 64 0 l 64 94 l 460 628 l 68 628 l 68 734 l 624 734 l 624 643 l 223 104 m 355 932 l 454 1032 l 570 1032 l 570 1020 l 395 854 l 315 854 l 142 1019 l 142 1032 l 255 1032 l 355 932 z "},"ſ":{"ha":350,"x_min":108,"x_max":439,"o":"m 108 0 l 108 827 q 170 1008 108 944 q 341 1072 231 1072 q 387 1068 363 1072 q 439 1058 410 1065 l 422 960 q 394 965 409 963 q 362 967 379 967 q 272 930 303 967 q 241 827 241 893 l 241 0 l 108 0 z "},"ƒ":{"ha":476,"x_min":-16,"x_max":477,"o":"m 413 635 l 280 635 l 280 -60 q 222 -235 280 -174 q 60 -296 164 -296 q 20 -293 38 -296 q -16 -284 3 -290 l -6 -182 q 25 -189 2 -186 q 60 -191 47 -191 q 123 -156 100 -191 q 146 -60 146 -121 l 146 635 l 32 635 l 32 734 l 146 734 l 146 827 q 208 1008 146 944 q 379 1072 269 1072 q 426 1068 402 1072 q 477 1058 449 1065 l 461 956 q 432 961 449 959 q 396 963 414 963 q 309 928 337 963 q 280 827 280 893 l 280 734 l 413 734 l 413 635 z "},"Ơ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 z "},"ơ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"Ư":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 z "},"ư":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 z "},"ǰ":{"ha":355,"x_min":-46,"x_max":399,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 m 184 920 l 283 1020 l 399 1020 l 399 1008 l 224 842 l 144 842 l -28 1006 l -28 1020 l 84 1020 l 184 920 z "},"Ǻ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 515 1414 l 650 1414 l 651 1410 l 505 1289 l 417 1289 l 515 1414 m 330 1142 q 365 1223 330 1191 q 449 1255 399 1255 q 530 1223 496 1255 q 564 1142 564 1191 q 531 1062 564 1093 q 449 1031 497 1031 q 365 1062 399 1031 q 330 1142 330 1093 m 390 1142 q 407 1102 390 1118 q 449 1085 425 1085 q 489 1101 472 1085 q 505 1142 505 1118 q 489 1184 505 1167 q 449 1202 472 1202 q 407 1184 425 1202 q 390 1142 390 1167 z "},"ǻ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 444 1196 l 579 1196 l 581 1192 l 434 1070 l 347 1070 l 444 1196 m 260 924 q 294 1005 260 972 q 378 1037 329 1037 q 460 1005 426 1037 q 494 924 494 972 q 460 844 494 875 q 378 812 427 812 q 294 844 329 812 q 260 924 260 875 m 319 924 q 337 883 319 900 q 378 867 354 867 q 418 883 401 867 q 435 924 435 899 q 418 966 435 949 q 378 983 401 983 q 337 966 354 983 q 319 924 319 949 z "},"Ǽ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 m 699 1236 l 854 1236 l 856 1232 l 673 1055 l 573 1055 l 699 1236 z "},"ǽ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 m 647 1018 l 803 1018 l 804 1014 l 621 838 l 522 838 l 647 1018 z "},"Ǿ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 357 0 408 -14 q 262 41 306 14 l 201 -64 l 100 -64 l 194 95 q 107 232 137 152 q 77 406 77 312 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 587 984 530 1002 q 693 936 644 967 l 748 1029 l 849 1029 l 760 879 q 841 745 812 822 q 869 581 869 669 l 869 406 m 210 406 q 223 298 210 348 q 260 214 236 249 l 264 213 l 633 834 q 557 877 599 862 q 466 892 515 892 q 280 805 349 892 q 210 583 210 718 l 210 406 m 736 583 q 725 679 736 633 q 693 758 713 724 l 689 759 l 322 141 q 388 106 352 118 q 466 95 424 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 538 1278 l 694 1278 l 695 1274 l 512 1097 l 412 1097 l 538 1278 z "},"ǿ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 466 740 431 747 q 532 719 501 732 l 583 820 l 670 820 l 600 677 q 691 548 659 627 q 723 374 723 469 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 329 -8 360 -14 q 269 8 298 -3 l 220 -92 l 133 -92 l 201 47 q 101 176 136 95 q 66 359 66 258 l 66 374 m 199 359 q 213 246 199 297 q 254 162 227 195 l 258 162 l 484 619 q 441 636 464 630 q 393 642 418 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 589 374 q 577 476 589 428 q 544 555 566 524 l 540 555 l 318 106 q 353 94 334 98 q 395 90 372 90 q 540 166 490 90 q 589 359 589 242 l 589 374 m 430 1017 l 586 1017 l 587 1013 l 404 836 l 304 836 l 430 1017 z "},"Ș":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 m 464 -175 l 381 -336 l 315 -336 l 357 -169 l 357 -80 l 464 -80 l 464 -175 z "},"ș":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 m 411 -174 l 328 -335 l 262 -335 l 304 -168 l 304 -79 l 411 -79 l 411 -174 z "},"ȷ":{"ha":355,"x_min":-46,"x_max":250,"o":"m 250 734 l 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 734 l 250 734 z "},"ʼ":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 z "},"ˆ":{"ha":664,"x_min":116,"x_max":532,"o":"m 532 866 l 532 849 l 423 849 l 323 949 l 224 849 l 116 849 l 116 867 l 283 1027 l 364 1027 l 532 866 z "},"ˇ":{"ha":625,"x_min":95,"x_max":522,"o":"m 307 927 l 407 1027 l 522 1027 l 522 1015 l 348 849 l 267 849 l 95 1014 l 95 1027 l 208 1027 l 307 927 z "},"˘":{"ha":594,"x_min":87,"x_max":494,"o":"m 490 987 l 491 983 q 439 855 494 905 q 291 806 384 806 q 142 855 197 806 q 91 983 87 905 l 92 987 l 195 987 q 218 915 195 943 q 291 887 241 887 q 363 915 340 887 q 387 987 387 943 l 490 987 z "},"˙":{"ha":377,"x_min":109,"x_max":256,"o":"m 256 851 l 109 851 l 109 987 l 256 987 l 256 851 z "},"˚":{"ha":472,"x_min":90,"x_max":370,"o":"m 90 887 q 131 983 90 944 q 231 1021 172 1021 q 330 983 289 1021 q 370 887 370 945 q 330 792 370 829 q 231 755 290 755 q 131 792 172 755 q 90 887 90 829 m 160 887 q 181 837 160 858 q 231 817 202 817 q 280 837 260 817 q 300 887 300 857 q 280 939 300 918 q 231 960 260 960 q 181 939 202 960 q 160 887 160 918 z "},"˛":{"ha":381,"x_min":46,"x_max":317,"o":"m 263 0 q 189 -67 216 -35 q 162 -136 162 -100 q 177 -181 162 -165 q 227 -197 193 -197 q 261 -192 245 -197 q 294 -180 277 -187 l 317 -263 q 263 -284 292 -276 q 193 -293 233 -293 q 87 -256 128 -293 q 46 -151 46 -218 q 87 -48 46 -97 q 214 39 128 1 l 263 0 z "},"˜":{"ha":664,"x_min":92,"x_max":562,"o":"m 562 1011 q 522 903 562 947 q 420 859 481 859 q 320 891 372 859 q 233 922 268 922 q 184 900 204 922 q 165 847 165 878 l 92 865 q 132 974 92 928 q 233 1021 172 1021 q 328 989 271 1021 q 420 957 385 957 q 469 979 448 957 q 489 1033 489 1001 l 562 1011 z "},"˝":{"ha":517,"x_min":68,"x_max":571,"o":"m 414 1029 l 569 1029 l 571 1025 l 368 849 l 252 849 l 250 852 l 414 1029 m 193 1029 l 336 1029 l 337 1026 l 174 849 l 68 849 l 193 1029 z "},"˳":{"ha":456,"x_min":123,"x_max":334,"o":"m 123 -159 q 154 -86 123 -115 q 230 -57 185 -57 q 303 -86 273 -57 q 334 -159 334 -115 q 303 -229 334 -201 q 230 -257 273 -257 q 154 -229 185 -257 q 123 -159 123 -201 m 184 -159 q 197 -189 184 -176 q 230 -202 211 -202 q 260 -190 248 -202 q 273 -159 273 -177 q 260 -125 273 -138 q 230 -112 248 -112 q 197 -125 211 -112 q 184 -159 184 -139 z "},"̀":{"ha":0,"x_min":-553,"x_max":-345,"o":"m -345 821 l -433 821 l -553 1057 l -427 1057 l -345 821 z "},"́":{"ha":0,"x_min":-446,"x_max":-236,"o":"m -359 1057 l -236 1057 l -363 821 l -446 821 l -359 1057 z "},"̃":{"ha":0,"x_min":-599,"x_max":-128,"o":"m -128 1011 q -169 903 -128 947 q -270 859 -209 859 q -370 891 -318 859 q -457 922 -422 922 q -506 900 -486 922 q -526 847 -526 878 l -599 865 q -559 974 -599 928 q -457 1021 -519 1021 q -362 989 -419 1021 q -270 957 -305 957 q -222 979 -242 957 q -201 1033 -201 1001 l -128 1011 z "},"̉":{"ha":0,"x_min":-459,"x_max":-248,"o":"m -446 842 l -446 947 q -373 960 -395 949 q -351 996 -351 971 q -380 1036 -351 1023 q -459 1048 -410 1048 l -454 1122 q -301 1087 -355 1122 q -248 994 -248 1052 q -277 921 -248 946 q -350 890 -306 897 l -351 842 l -446 842 z "},"̏":{"ha":0,"x_min":-665,"x_max":-161,"o":"m -344 852 l -345 849 l -461 849 l -665 1025 l -663 1029 l -507 1029 l -344 852 m -161 849 l -268 849 l -431 1026 l -430 1029 l -286 1029 l -161 849 z "},"̣":{"ha":0,"x_min":-480,"x_max":-332,"o":"m -332 -229 l -480 -229 l -480 -93 l -332 -93 l -332 -229 z "},"΄":{"ha":357,"x_min":132,"x_max":315,"o":"m 174 1119 l 315 1119 l 196 861 l 132 861 l 174 1119 z "},"΅":{"ha":709,"x_min":109,"x_max":589,"o":"m 589 852 l 454 852 l 454 987 l 589 987 l 589 852 m 243 852 l 109 852 l 109 987 l 243 987 l 243 852 m 337 1173 l 485 1173 l 399 996 l 302 996 l 337 1173 z "},"Ά":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 205 1119 l 346 1119 l 227 861 l 163 861 l 205 1119 z "},"·":{"ha":367,"x_min":109,"x_max":243,"o":"m 243 423 l 109 423 l 109 567 l 243 567 l 243 423 z "},"Έ":{"ha":880,"x_min":-20,"x_max":845,"o":"m 776 458 l 323 458 l 323 104 l 845 104 l 845 0 l 190 0 l 190 987 l 838 987 l 838 882 l 323 882 l 323 563 l 776 563 l 776 458 m 22 1120 l 163 1120 l 44 863 l -20 863 l 22 1120 z "},"Ή":{"ha":1059,"x_min":14,"x_max":937,"o":"m 937 0 l 803 0 l 803 436 l 323 436 l 323 0 l 190 0 l 190 987 l 323 987 l 323 541 l 803 541 l 803 987 l 937 987 l 937 0 m 55 1120 l 196 1120 l 77 863 l 14 863 l 55 1120 z "},"Ί":{"ha":460,"x_min":16,"x_max":331,"o":"m 331 0 l 197 0 l 197 987 l 331 987 l 331 0 m 58 1120 l 199 1120 l 80 862 l 16 862 l 58 1120 z "},"Ό":{"ha":960,"x_min":46,"x_max":883,"o":"m 883 406 q 771 105 883 224 q 479 -14 659 -14 q 198 105 307 -14 q 90 406 90 224 l 90 581 q 198 882 90 762 q 479 1002 307 1002 q 771 882 659 1002 q 883 581 883 762 l 883 406 m 749 583 q 676 805 749 718 q 479 892 603 892 q 293 805 363 892 q 224 583 224 718 l 224 406 q 293 182 224 269 q 479 95 363 95 q 676 181 604 95 q 749 406 749 268 l 749 583 m 87 1119 l 229 1119 l 110 861 l 46 861 l 87 1119 z "},"Ύ":{"ha":916,"x_min":-86,"x_max":902,"o":"m 492 486 l 749 987 l 902 987 l 555 347 l 555 0 l 422 0 l 422 356 l 81 987 l 234 987 l 492 486 m -45 1119 l 96 1119 l -22 861 l -86 861 l -45 1119 z "},"Ώ":{"ha":940,"x_min":41,"x_max":849,"o":"m 512 108 q 661 222 608 125 q 714 490 714 319 l 714 570 q 649 812 714 728 q 468 897 583 897 q 288 812 353 897 q 223 570 223 728 l 223 490 q 278 222 223 319 q 429 108 332 124 l 429 0 l 94 0 l 94 104 l 252 104 q 133 274 176 170 q 90 490 90 378 l 90 568 q 194 881 90 761 q 468 1002 299 1002 q 742 881 637 1002 q 848 568 848 761 l 848 490 q 804 274 848 378 q 686 104 761 170 l 849 104 l 849 0 l 512 0 l 512 108 m 83 1119 l 224 1119 l 105 861 l 41 861 l 83 1119 z "},"ΐ":{"ha":456,"x_min":-35,"x_max":446,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 446 803 l 311 803 l 311 939 l 446 939 l 446 803 m 100 803 l -35 803 l -35 939 l 100 939 l 100 803 m 193 1124 l 341 1124 l 255 947 l 159 947 l 193 1124 z "},"Α":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"Β":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"Γ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"Δ":{"ha":981,"x_min":20,"x_max":944,"o":"m 439 987 l 553 987 l 944 0 l 20 0 l 439 987 m 199 104 l 768 104 l 496 817 l 492 817 l 199 104 z "},"Ε":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"Ζ":{"ha":831,"x_min":66,"x_max":768,"o":"m 216 104 l 768 104 l 768 0 l 66 0 l 66 99 l 593 882 l 73 882 l 73 987 l 746 987 l 746 892 l 216 104 z "},"Η":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"Θ":{"ha":947,"x_min":77,"x_max":869,"o":"m 650 447 l 305 447 l 305 551 l 650 551 l 650 447 m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Ι":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Κ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"Λ":{"ha":912,"x_min":33,"x_max":873,"o":"m 455 794 l 451 794 l 170 0 l 33 0 l 395 987 l 511 987 l 873 0 l 736 0 l 455 794 z "},"Μ":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"Ν":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 260 764 l 256 762 l 256 0 l 122 0 l 122 987 l 256 987 l 731 225 l 735 227 l 735 987 l 869 987 l 869 0 z "},"Ξ":{"ha":795,"x_min":83,"x_max":719,"o":"m 83 104 l 719 104 l 719 0 l 83 0 l 83 104 m 140 565 l 656 565 l 656 460 l 140 460 l 140 565 m 84 987 l 711 987 l 711 882 l 84 882 l 84 987 z "},"Ο":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"Π":{"ha":992,"x_min":122,"x_max":870,"o":"m 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 0 z "},"Ρ":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"Σ":{"ha":795,"x_min":47,"x_max":743,"o":"m 514 485 l 212 109 l 214 105 l 743 105 l 743 0 l 47 0 l 47 100 l 373 494 l 47 888 l 47 987 l 709 987 l 709 882 l 214 882 l 212 879 l 514 501 l 514 485 z "},"Τ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"Υ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"Φ":{"ha":976,"x_min":57,"x_max":920,"o":"m 556 833 q 816 734 712 830 q 920 490 920 638 q 816 244 920 340 q 556 144 712 147 l 556 0 l 422 0 l 422 144 q 161 243 265 146 q 57 489 57 339 q 161 734 57 637 q 422 834 265 831 l 422 987 l 556 987 l 556 833 m 189 489 q 249 316 189 380 q 418 254 309 253 l 422 255 l 422 724 l 418 725 q 250 662 310 726 q 189 489 189 597 m 787 490 q 727 662 787 598 q 560 725 667 725 l 556 724 l 556 255 l 560 254 q 727 317 667 253 q 787 490 787 381 z "},"Χ":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Ψ":{"ha":960,"x_min":59,"x_max":886,"o":"m 534 329 l 538 328 q 694 412 635 340 q 753 600 753 484 l 753 987 l 886 987 l 886 600 q 789 333 886 434 q 534 216 692 233 l 534 0 l 399 0 l 399 217 q 152 334 245 233 q 59 600 59 434 l 59 987 l 192 987 l 192 600 q 248 414 192 485 q 395 329 303 342 l 399 330 l 399 987 l 534 987 l 534 329 z "},"Ω":{"ha":926,"x_min":76,"x_max":836,"o":"m 498 108 q 647 222 594 125 q 701 490 701 319 l 701 570 q 635 812 701 728 q 454 897 570 897 q 275 812 340 897 q 210 570 210 728 l 210 490 q 264 222 210 319 q 416 108 319 124 l 416 0 l 80 0 l 80 104 l 239 104 q 119 274 163 170 q 76 490 76 378 l 76 568 q 181 881 76 761 q 454 1002 286 1002 q 729 881 623 1002 q 834 568 834 761 l 834 490 q 791 274 834 378 q 673 104 747 170 l 836 104 l 836 0 l 498 0 l 498 108 z "},"Ϊ":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ϋ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 658 1088 l 509 1088 l 509 1223 l 658 1223 l 658 1088 m 339 1088 l 191 1088 l 191 1223 l 339 1223 l 339 1088 z "},"ά":{"ha":785,"x_min":66,"x_max":781,"o":"m 679 733 l 679 187 q 693 119 679 138 q 730 100 707 100 q 749 101 741 100 q 766 105 758 102 l 781 9 q 741 -9 762 -5 q 696 -14 721 -14 q 611 7 645 -14 q 561 76 577 29 q 472 8 524 31 q 353 -14 420 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 475 723 422 747 q 565 652 528 699 l 600 733 l 679 733 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 114 441 90 q 545 182 519 138 l 545 187 l 545 554 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 m 427 1124 l 568 1124 l 449 866 l 385 866 l 427 1124 z "},"έ":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 m 376 1123 l 517 1123 l 399 865 l 335 865 l 376 1123 z "},"ή":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 623 681 559 747 q 687 463 687 615 l 687 -281 l 553 -281 l 553 460 q 515 600 553 557 q 397 642 476 642 q 298 620 340 642 q 231 556 256 597 l 231 0 l 97 0 l 97 734 l 216 734 m 395 1124 l 536 1124 l 417 866 l 353 866 l 395 1124 z "},"ί":{"ha":456,"x_min":134,"x_max":425,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 208 1111 l 349 1111 l 230 853 l 166 853 l 208 1111 z "},"ΰ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 646 803 l 511 803 l 511 939 l 646 939 l 646 803 m 300 803 l 166 803 l 166 939 l 300 939 l 300 803 m 394 1124 l 542 1124 l 456 947 l 359 947 l 394 1124 z "},"α":{"ha":785,"x_min":66,"x_max":781,"o":"m 679 733 l 679 187 q 693 119 679 138 q 730 100 707 100 q 749 101 741 100 q 766 105 758 102 l 781 9 q 741 -9 762 -5 q 696 -14 721 -14 q 611 7 645 -14 q 561 76 577 29 q 472 8 524 31 q 353 -14 420 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 475 723 422 747 q 565 652 528 699 l 600 733 l 679 733 m 200 339 q 246 159 200 228 q 387 90 292 90 q 480 114 441 90 q 545 182 519 138 l 545 187 l 545 554 q 480 618 519 595 q 388 642 441 642 q 246 561 292 642 q 200 353 200 480 l 200 339 z "},"β":{"ha":823,"x_min":106,"x_max":748,"o":"m 412 1002 q 624 927 544 1002 q 704 734 704 852 q 670 621 704 673 q 577 538 636 569 q 703 442 658 509 q 748 288 748 376 q 662 66 748 146 q 439 -14 576 -14 q 332 1 384 -14 q 240 47 280 16 l 240 -260 l 106 -260 l 106 728 q 197 921 106 840 q 412 1002 287 1002 m 409 583 q 531 624 492 583 q 570 737 570 665 q 528 849 570 802 q 412 897 486 897 q 290 848 339 897 q 240 728 240 800 l 240 165 q 318 110 270 130 q 426 90 366 90 q 564 144 513 90 q 614 286 614 199 q 570 420 614 361 q 450 478 526 478 l 353 478 l 353 583 l 409 583 z "},"γ":{"ha":699,"x_min":31,"x_max":675,"o":"m 539 734 l 675 734 l 421 42 l 421 -283 l 288 -283 l 288 45 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 z "},"δ":{"ha":789,"x_min":66,"x_max":723,"o":"m 636 987 l 636 885 l 328 885 l 327 881 l 578 693 q 685 559 647 642 q 723 374 723 475 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 137 621 66 519 q 334 743 209 722 l 339 743 l 340 747 l 139 907 l 139 987 l 636 987 m 393 642 q 249 566 298 642 q 199 374 199 489 l 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 z "},"ε":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 z "},"ζ":{"ha":726,"x_min":78,"x_max":658,"o":"m 658 987 l 658 911 l 426 618 q 300 438 338 515 q 262 262 262 361 q 297 144 262 184 q 402 104 333 104 l 424 104 q 593 61 532 104 q 654 -68 654 18 q 600 -200 652 -144 q 473 -288 547 -256 l 418 -201 q 495 -151 463 -180 q 527 -75 527 -122 q 499 -20 527 -39 q 410 0 471 0 l 375 0 q 191 73 254 0 q 128 259 128 145 q 175 449 128 340 q 320 673 222 558 l 502 879 l 500 882 l 78 882 l 78 987 l 658 987 z "},"η":{"ha":789,"x_min":97,"x_max":687,"o":"m 216 734 l 225 632 q 316 717 262 687 q 439 747 370 747 q 623 681 559 747 q 687 463 687 615 l 687 -281 l 553 -281 l 553 460 q 515 600 553 557 q 397 642 476 642 q 298 620 340 642 q 231 556 256 597 l 231 0 l 97 0 l 97 734 l 216 734 z "},"θ":{"ha":790,"x_min":81,"x_max":709,"o":"m 709 376 q 626 86 709 187 q 396 -14 542 -14 q 165 87 250 -14 q 81 376 81 188 l 81 610 q 165 900 81 799 q 395 1002 250 1002 q 625 900 541 1002 q 709 610 709 799 l 709 376 m 214 545 l 576 545 l 576 639 q 529 832 576 767 q 395 897 483 897 q 260 832 307 897 q 214 639 214 767 l 214 545 m 576 439 l 214 439 l 214 349 q 261 156 214 221 q 396 90 308 90 q 530 155 484 90 q 576 349 576 220 l 576 439 z "},"ι":{"ha":456,"x_min":134,"x_max":425,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 z "},"κ":{"ha":776,"x_min":104,"x_max":738,"o":"m 306 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 714 734 l 715 730 l 410 382 l 738 3 l 736 0 l 573 0 l 306 311 z "},"λ":{"ha":789,"x_min":38,"x_max":758,"o":"m 186 0 l 38 0 l 303 697 l 266 795 q 215 888 244 852 q 144 924 185 924 q 119 923 138 924 q 98 921 101 921 l 98 1019 q 133 1026 112 1023 q 171 1029 155 1029 q 310 975 258 1029 q 389 846 362 920 l 633 205 q 672 128 648 157 q 726 100 696 100 q 739 100 736 100 q 758 103 742 100 l 756 -1 q 736 -8 749 -5 q 716 -12 724 -12 q 588 33 636 -12 q 503 172 539 77 l 366 532 l 362 531 l 342 454 l 186 0 z "},"μ":{"ha":789,"x_min":104,"x_max":685,"o":"m 237 734 l 237 298 q 276 134 238 178 q 381 90 313 90 q 489 115 448 90 q 551 186 530 139 l 551 734 l 685 734 l 685 0 l 565 0 l 559 73 q 486 8 529 31 q 387 -14 443 -14 q 301 -3 338 -14 q 237 32 264 8 l 237 -282 l 104 -282 l 104 734 l 237 734 z "},"ν":{"ha":699,"x_min":31,"x_max":675,"o":"m 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 406 0 l 304 0 l 31 734 l 168 734 l 342 216 z "},"ξ":{"ha":707,"x_min":58,"x_max":675,"o":"m 625 987 l 625 882 l 425 882 q 299 833 341 877 q 256 724 256 789 q 306 624 256 660 q 459 589 355 589 l 556 589 l 556 484 l 459 484 q 266 431 332 484 q 200 274 200 378 q 254 142 200 193 q 404 91 309 91 l 445 91 q 614 48 554 91 q 675 -83 675 5 q 621 -214 673 -158 q 494 -301 568 -269 l 441 -215 q 517 -165 485 -194 q 549 -89 549 -136 q 525 -33 549 -54 q 450 -13 501 -13 l 404 -13 q 160 64 254 -13 q 66 277 66 141 q 120 442 66 375 q 277 538 174 509 q 163 613 204 566 q 123 721 123 661 q 143 814 123 773 q 204 882 164 855 l 58 882 l 58 987 l 625 987 z "},"ο":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"π":{"ha":828,"x_min":54,"x_max":833,"o":"m 758 628 l 675 628 l 675 182 q 693 113 675 132 q 741 94 711 94 q 774 99 758 94 q 803 112 791 104 l 833 22 q 772 -7 802 1 q 706 -14 742 -14 q 583 34 625 -14 q 541 189 541 82 l 541 628 l 273 628 l 273 0 l 140 0 l 140 628 l 54 628 l 54 734 l 758 734 l 758 628 z "},"ρ":{"ha":789,"x_min":97,"x_max":719,"o":"m 719 339 q 645 83 719 180 q 437 -14 570 -14 q 319 6 371 -14 q 231 66 268 26 l 231 -282 l 97 -282 l 97 391 l 97 391 q 184 653 97 559 q 401 747 271 747 q 637 640 555 747 q 719 353 719 532 l 719 339 m 585 353 q 541 562 585 481 q 401 642 496 642 q 274 564 318 642 q 231 391 231 486 l 231 186 q 296 116 256 141 q 396 90 337 90 q 538 159 490 90 q 585 339 585 229 l 585 353 z "},"ς":{"ha":747,"x_min":66,"x_max":673,"o":"m 389 747 q 594 676 515 747 q 671 485 673 606 l 669 481 l 549 481 q 507 596 549 551 q 389 642 466 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 256 188 200 256 q 424 103 311 119 q 603 50 546 85 q 660 -70 660 14 q 606 -201 659 -145 q 479 -288 553 -256 l 425 -202 q 501 -152 469 -181 q 533 -76 533 -123 q 503 -26 533 -43 q 409 0 473 -9 q 153 115 239 24 q 66 353 66 207 l 66 381 q 153 641 66 535 q 389 747 239 747 z "},"σ":{"ha":789,"x_min":66,"x_max":779,"o":"m 779 628 l 591 628 q 689 507 654 579 q 723 347 723 435 l 723 332 q 633 88 723 190 q 395 -14 544 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 q 154 632 66 530 q 393 734 241 733 l 779 734 l 779 628 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 555 589 483 q 393 628 490 628 q 249 555 298 628 q 199 374 199 483 l 199 359 z "},"τ":{"ha":727,"x_min":55,"x_max":670,"o":"m 670 630 l 427 630 l 427 0 l 294 0 l 294 630 l 55 630 l 55 734 l 670 734 l 670 630 z "},"υ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 z "},"φ":{"ha":983,"x_min":56,"x_max":927,"o":"m 552 734 q 817 625 707 734 q 927 355 927 516 q 835 106 927 208 q 552 -11 743 5 l 552 -324 l 418 -324 l 418 -11 q 144 115 233 6 q 56 382 56 224 q 78 573 56 486 q 135 734 100 661 l 275 734 q 214 555 236 643 q 190 382 191 468 q 245 197 190 277 q 414 100 300 116 l 418 102 l 418 734 l 552 734 m 793 355 q 728 546 791 466 q 556 624 665 627 l 552 623 l 552 100 l 556 98 q 735 186 676 113 q 793 355 793 260 z "},"χ":{"ha":775,"x_min":64,"x_max":745,"o":"m 137 744 q 276 690 224 744 q 355 562 327 635 l 418 412 l 422 412 l 580 734 l 714 734 l 483 254 l 621 -79 q 664 -154 643 -129 q 714 -180 685 -180 q 727 -179 724 -180 q 745 -176 730 -178 l 743 -286 q 724 -293 736 -290 q 703 -296 711 -296 q 577 -252 624 -296 q 494 -112 530 -208 l 408 92 l 404 92 l 224 -281 l 83 -281 l 342 249 l 232 511 q 180 601 210 566 q 111 635 151 635 q 85 633 104 635 q 64 631 67 631 l 64 734 q 99 741 78 737 q 137 744 121 744 z "},"ψ":{"ha":977,"x_min":62,"x_max":920,"o":"m 546 733 l 546 101 l 550 100 q 728 197 670 115 q 787 385 787 279 q 762 556 785 469 q 702 734 740 644 l 842 734 q 899 575 877 662 q 920 385 920 488 q 830 113 920 222 q 546 -12 740 3 l 546 -321 l 412 -321 l 412 -10 q 153 109 244 7 q 62 404 62 212 l 62 734 l 195 734 l 195 403 q 253 188 195 262 q 408 101 310 114 l 412 102 l 412 733 l 546 733 z "},"ω":{"ha":1181,"x_min":73,"x_max":1107,"o":"m 318 734 q 239 555 268 643 q 207 381 210 467 q 245 171 207 252 q 364 90 283 90 q 480 145 437 90 q 523 319 523 200 l 523 522 l 657 522 l 657 319 q 700 145 657 200 q 816 90 743 90 q 935 171 897 90 q 973 381 973 251 q 941 555 970 467 q 862 734 911 643 l 1002 734 q 1078 574 1048 662 q 1107 381 1107 486 q 1038 97 1107 208 q 823 -14 970 -14 q 678 27 737 -14 q 590 149 618 68 q 502 27 561 68 q 357 -14 442 -14 q 142 97 210 -14 q 73 381 73 208 q 102 574 73 486 q 178 734 131 663 l 318 734 z "},"ϊ":{"ha":456,"x_min":-27,"x_max":440,"o":"m 267 733 l 267 182 q 285 113 267 132 q 334 94 303 94 q 367 99 351 94 q 395 112 383 104 l 425 22 q 365 -7 395 1 q 298 -14 334 -14 q 176 34 218 -14 q 134 189 134 82 l 134 733 l 267 733 m 440 856 l 292 856 l 292 991 l 440 991 l 440 856 m 121 856 l -27 856 l -27 991 l 121 991 l 121 856 z "},"ϋ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 641 856 l 492 856 l 492 991 l 641 991 l 641 856 m 322 856 l 174 856 l 174 991 l 322 991 l 322 856 z "},"ό":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 397 1124 l 538 1124 l 420 866 l 356 866 l 397 1124 z "},"ύ":{"ha":789,"x_min":96,"x_max":720,"o":"m 229 734 l 229 319 q 276 145 229 200 q 402 90 323 90 q 541 176 495 90 q 587 381 587 262 q 562 554 585 467 q 503 734 540 642 l 642 734 q 699 573 677 661 q 720 381 720 486 q 645 97 720 208 q 409 -14 570 -14 q 177 68 259 -14 q 96 320 96 150 l 96 734 l 229 734 m 410 1111 l 551 1111 l 432 853 l 368 853 l 410 1111 z "},"ώ":{"ha":1181,"x_min":73,"x_max":1107,"o":"m 318 734 q 239 555 268 643 q 207 381 210 467 q 245 171 207 252 q 364 90 283 90 q 480 145 437 90 q 523 319 523 200 l 523 522 l 657 522 l 657 319 q 700 145 657 200 q 816 90 743 90 q 935 171 897 90 q 973 381 973 251 q 941 555 970 467 q 862 734 911 643 l 1002 734 q 1078 574 1048 662 q 1107 381 1107 486 q 1038 97 1107 208 q 823 -14 970 -14 q 678 27 737 -14 q 590 149 618 68 q 502 27 561 68 q 357 -14 442 -14 q 142 97 210 -14 q 73 381 73 208 q 102 574 73 486 q 178 734 131 663 l 318 734 m 593 1111 l 734 1111 l 615 853 l 551 853 l 593 1111 z "},"ϑ":{"ha":858,"x_min":77,"x_max":814,"o":"m 353 703 l 353 745 q 417 931 353 861 q 584 1002 481 1002 q 752 933 690 1002 q 814 745 814 865 l 814 401 q 710 99 814 213 q 439 -14 607 -14 q 177 104 277 -14 q 77 401 77 222 l 77 849 l 210 850 l 210 401 q 272 180 210 266 q 439 95 334 95 q 615 175 551 95 q 681 397 680 256 q 443 487 533 402 q 353 703 353 572 m 681 745 q 656 855 681 818 q 584 892 631 892 q 513 855 539 892 q 487 745 487 818 l 487 701 q 538 566 487 620 q 677 510 589 513 l 681 511 l 681 745 z "},"ϒ":{"ha":739,"x_min":-15,"x_max":751,"o":"m 503 840 q 579 960 538 924 q 676 995 621 995 q 716 991 699 995 q 751 979 733 988 l 735 876 q 724 879 732 878 q 708 881 717 881 q 667 869 686 881 q 637 831 648 857 l 434 392 l 434 0 l 301 0 l 301 392 l 98 831 q 67 869 86 857 q 27 881 49 881 q 11 879 18 881 q 0 876 3 878 l -15 979 q 18 991 1 988 q 59 995 35 995 q 155 960 113 995 q 232 840 197 924 l 353 565 l 366 517 l 370 517 l 382 565 l 503 840 z "},"ϖ":{"ha":1090,"x_min":53,"x_max":1059,"o":"m 1059 628 l 968 628 q 999 511 988 573 q 1010 381 1010 450 q 951 97 1010 209 q 768 -14 892 -14 q 631 27 687 -14 q 548 146 575 68 q 465 26 520 67 q 329 -14 409 -14 q 145 97 203 -14 q 86 381 86 209 q 97 511 86 450 q 128 628 108 573 l 53 628 l 53 734 l 1059 734 l 1059 628 m 877 381 q 861 503 875 441 q 825 628 848 565 l 272 628 q 235 503 248 564 q 220 381 221 441 q 248 171 220 252 q 336 90 276 90 q 442 145 403 90 q 481 319 481 200 l 481 488 l 616 488 l 616 319 q 655 145 616 200 q 761 90 694 90 q 848 171 820 90 q 877 381 877 252 z "},"Ѐ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 482 1058 l 375 1058 l 205 1234 l 208 1238 l 363 1238 l 482 1058 z "},"Ё":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 660 1088 l 511 1088 l 511 1224 l 660 1224 l 660 1088 m 341 1088 l 193 1088 l 193 1224 l 341 1224 l 341 1088 z "},"Ђ":{"ha":1042,"x_min":28,"x_max":990,"o":"m 795 882 l 444 882 l 444 570 q 549 595 498 585 q 637 605 601 605 q 897 523 804 605 q 990 294 990 441 q 904 71 990 147 q 656 -4 818 -6 l 653 -3 l 652 93 q 809 144 762 93 q 857 294 857 196 q 801 442 855 391 q 637 493 746 493 q 544 485 595 493 q 444 462 492 476 l 444 0 l 311 0 l 311 882 l 28 882 l 28 987 l 795 987 l 795 882 z "},"Ѓ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 m 489 1236 l 645 1236 l 646 1232 l 463 1055 l 363 1055 l 489 1236 z "},"Є":{"ha":940,"x_min":92,"x_max":836,"o":"m 831 317 l 833 313 q 735 80 836 174 q 470 -14 635 -14 q 197 105 302 -14 q 92 406 92 224 l 92 582 q 197 883 92 764 q 470 1002 302 1002 q 736 913 637 1002 q 833 677 836 824 l 831 673 l 703 673 q 642 837 703 777 q 470 897 581 897 q 292 807 358 897 q 225 583 225 717 l 225 551 l 606 551 l 606 447 l 225 447 l 225 406 q 292 181 225 271 q 470 91 358 91 q 642 151 581 91 q 703 317 703 211 l 831 317 z "},"Ѕ":{"ha":866,"x_min":66,"x_max":793,"o":"m 660 249 q 609 362 660 317 q 429 439 558 408 q 184 549 272 477 q 96 732 96 622 q 191 924 96 847 q 436 1002 286 1002 q 694 913 598 1002 q 787 707 790 824 l 785 703 l 658 703 q 601 843 658 789 q 436 897 543 897 q 283 851 336 897 q 230 734 230 806 q 288 626 230 669 q 475 551 345 583 q 712 437 631 512 q 793 250 793 361 q 696 58 793 131 q 441 -14 598 -14 q 179 66 293 -14 q 69 280 66 146 l 71 284 l 198 284 q 268 140 198 189 q 441 90 338 90 q 601 133 542 90 q 660 249 660 176 z "},"І":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ї":{"ha":393,"x_min":-36,"x_max":431,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 431 1088 l 283 1088 l 283 1224 l 431 1224 l 431 1088 m 113 1088 l -36 1088 l -36 1224 l 113 1224 l 113 1088 z "},"Ј":{"ha":766,"x_min":41,"x_max":653,"o":"m 519 987 l 653 987 l 653 273 q 569 63 653 141 q 352 -14 486 -14 q 125 58 210 -14 q 45 268 41 130 l 46 272 l 174 272 q 221 135 174 180 q 352 90 267 90 q 473 140 426 90 q 519 273 519 190 l 519 987 z "},"Љ":{"ha":1493,"x_min":34,"x_max":1438,"o":"m 862 987 l 862 603 l 1105 603 q 1351 519 1263 603 q 1438 301 1438 435 q 1351 83 1438 167 q 1105 0 1263 0 l 728 0 l 728 882 l 387 882 l 386 522 q 312 125 385 250 q 69 0 239 0 l 34 0 l 34 104 l 61 104 q 208 201 163 104 q 253 522 252 298 l 253 987 l 862 987 m 862 498 l 862 104 l 1105 104 q 1255 162 1205 104 q 1305 302 1305 219 q 1255 441 1305 385 q 1105 498 1205 498 l 862 498 z "},"Њ":{"ha":1500,"x_min":122,"x_max":1446,"o":"m 256 561 l 736 561 l 736 987 l 869 987 l 869 570 l 1114 570 q 1359 492 1272 570 q 1446 288 1446 415 q 1358 80 1446 159 q 1114 0 1271 0 l 736 0 l 736 456 l 256 456 l 256 0 l 122 0 l 122 987 l 256 987 l 256 561 m 869 465 l 869 112 l 1114 112 q 1263 162 1213 112 q 1313 289 1313 212 q 1263 415 1313 365 q 1114 465 1213 465 l 869 465 z "},"Ћ":{"ha":1133,"x_min":45,"x_max":1016,"o":"m 812 882 l 454 882 l 454 589 q 559 609 506 602 q 684 616 613 616 q 931 544 847 616 q 1016 312 1016 472 l 1016 0 l 882 0 l 882 312 q 835 467 882 423 q 684 510 788 510 q 567 503 623 510 q 454 481 510 495 l 454 0 l 321 0 l 321 882 l 45 882 l 45 987 l 812 987 l 812 882 z "},"Ќ":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 481 1236 l 637 1236 l 638 1232 l 455 1055 l 355 1055 l 481 1236 z "},"Ѝ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 553 1058 l 446 1058 l 277 1234 l 279 1238 l 435 1238 l 553 1058 z "},"Ў":{"ha":880,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 637 1268 l 639 1264 q 587 1136 642 1185 q 439 1086 532 1086 q 290 1136 345 1086 q 239 1264 235 1185 l 240 1268 l 342 1268 q 366 1196 342 1223 q 439 1168 389 1168 q 511 1196 488 1168 q 535 1268 535 1224 l 637 1268 z "},"Џ":{"ha":992,"x_min":122,"x_max":869,"o":"m 122 987 l 256 987 l 256 104 l 736 104 l 736 987 l 869 987 l 869 0 l 568 0 l 568 -243 l 434 -243 l 434 0 l 122 0 l 122 987 z "},"А":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 z "},"Б":{"ha":876,"x_min":111,"x_max":821,"o":"m 716 882 l 244 882 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 987 l 716 987 l 716 882 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"В":{"ha":888,"x_min":122,"x_max":813,"o":"m 122 0 l 122 987 l 444 987 q 686 921 599 987 q 774 720 774 854 q 732 603 774 654 q 621 526 690 551 q 762 438 711 507 q 813 279 813 370 q 725 72 813 143 q 487 0 637 0 l 122 0 m 256 463 l 256 104 l 487 104 q 629 150 578 104 q 680 277 680 195 q 637 414 680 364 q 507 463 595 463 l 256 463 m 256 568 l 472 568 q 593 609 547 568 q 640 723 640 650 q 590 843 640 803 q 444 882 539 882 l 256 882 l 256 568 z "},"Г":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"Д":{"ha":1051,"x_min":33,"x_max":1013,"o":"m 877 104 l 1013 104 l 999 -242 l 879 -242 l 879 0 l 166 0 l 166 -243 l 53 -243 l 33 104 l 117 104 q 213 278 170 163 q 260 588 256 392 l 283 987 l 877 987 l 877 104 m 395 588 q 355 304 389 427 q 266 104 320 181 l 743 104 l 743 882 l 411 882 l 395 588 z "},"Е":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 z "},"Ж":{"ha":1267,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 z "},"З":{"ha":939,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 z "},"И":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 z "},"Й":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 696 1268 l 698 1264 q 646 1136 701 1185 q 498 1086 591 1086 q 349 1136 404 1086 q 298 1264 294 1185 l 299 1268 l 401 1268 q 425 1196 401 1223 q 498 1168 448 1168 q 570 1196 547 1168 q 594 1268 594 1224 l 696 1268 z "},"К":{"ha":893,"x_min":122,"x_max":890,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 z "},"Л":{"ha":984,"x_min":33,"x_max":862,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 z "},"М":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 z "},"Н":{"ha":991,"x_min":122,"x_max":869,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 z "},"О":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 z "},"П":{"ha":992,"x_min":122,"x_max":870,"o":"m 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 0 z "},"Р":{"ha":890,"x_min":122,"x_max":833,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 745 476 833 557 q 500 396 658 396 l 256 396 m 256 500 l 500 500 q 650 554 600 500 q 699 690 699 608 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"С":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 z "},"Т":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 z "},"У":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 z "},"Ф":{"ha":1079,"x_min":58,"x_max":1029,"o":"m 610 889 l 628 889 q 914 778 799 889 q 1029 492 1029 666 q 914 204 1029 316 q 628 92 799 92 l 610 92 l 610 -41 l 477 -41 l 477 92 l 457 92 q 172 203 286 92 q 58 490 58 315 q 172 777 58 665 q 457 889 286 889 l 477 889 l 477 1028 l 610 1028 l 610 889 m 457 784 q 259 705 329 784 q 190 490 190 625 q 259 275 190 354 q 457 197 329 197 l 477 197 l 477 784 l 457 784 m 610 784 l 610 197 l 629 197 q 826 276 755 197 q 896 492 896 355 q 826 705 896 626 q 629 784 755 784 l 610 784 z "},"Х":{"ha":878,"x_min":45,"x_max":840,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 z "},"Ц":{"ha":1069,"x_min":123,"x_max":1015,"o":"m 1015 -243 l 881 -243 l 881 109 l 1015 109 l 1015 -243 m 123 987 l 257 987 l 257 105 l 737 105 l 737 987 l 871 987 l 871 0 l 123 0 l 123 987 z "},"Ч":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 z "},"Ш":{"ha":1310,"x_min":122,"x_max":1188,"o":"m 256 987 l 256 104 l 589 104 l 589 987 l 722 987 l 722 104 l 1055 104 l 1055 987 l 1188 987 l 1188 0 l 122 0 l 122 987 l 256 987 z "},"Щ":{"ha":1352,"x_min":122,"x_max":1293,"o":"m 256 987 l 256 104 l 589 104 l 589 987 l 722 987 l 722 104 l 1055 104 l 1055 987 l 1188 987 l 1188 105 l 1293 105 l 1280 -238 l 1161 -238 l 1161 0 l 122 0 l 122 987 l 256 987 z "},"Ъ":{"ha":1050,"x_min":15,"x_max":996,"o":"m 15 987 l 418 987 l 418 583 l 662 583 q 908 505 821 583 q 996 294 996 427 q 908 81 996 161 q 662 0 820 0 l 285 0 l 285 882 l 15 882 l 15 987 m 418 478 l 418 104 l 662 104 q 812 159 762 104 q 862 296 862 213 q 812 427 862 376 q 662 478 762 478 l 418 478 z "},"Ы":{"ha":1208,"x_min":122,"x_max":1087,"o":"m 256 597 l 499 597 q 745 516 657 597 q 833 301 833 435 q 745 83 833 167 q 499 0 656 0 l 122 0 l 122 987 l 256 987 l 256 597 m 256 492 l 256 104 l 499 104 q 649 162 599 104 q 699 302 699 219 q 649 437 699 383 q 499 492 600 492 l 256 492 m 1087 0 l 953 0 l 953 987 l 1087 987 l 1087 0 z "},"Ь":{"ha":875,"x_min":111,"x_max":821,"o":"m 244 583 l 488 583 q 734 505 646 583 q 821 294 821 427 q 733 81 821 161 q 488 0 646 0 l 111 0 l 111 987 l 244 987 l 244 583 m 244 478 l 244 104 l 488 104 q 638 159 588 104 q 688 296 688 213 q 638 427 688 376 q 488 478 588 478 l 244 478 z "},"Э":{"ha":939,"x_min":123,"x_max":867,"o":"m 127 671 l 126 675 q 223 908 123 813 q 488 1002 324 1002 q 762 883 656 1002 q 867 582 867 764 l 867 406 q 762 105 867 223 q 488 -14 656 -14 q 222 75 321 -14 q 126 311 123 163 l 127 315 l 256 315 q 316 151 256 211 q 488 91 376 91 q 666 181 600 91 q 733 405 733 271 l 733 460 l 344 460 l 344 565 l 733 565 l 733 582 q 666 807 733 716 q 488 897 600 897 q 316 837 376 897 q 256 671 256 777 l 127 671 z "},"Ю":{"ha":1238,"x_min":129,"x_max":1195,"o":"m 1195 406 q 1083 105 1195 224 q 791 -14 970 -14 q 510 105 618 -14 q 402 406 402 224 l 402 429 l 263 429 l 263 0 l 129 0 l 129 987 l 263 987 l 263 533 l 402 533 l 402 581 q 510 882 402 762 q 791 1002 618 1002 q 1083 882 970 1002 q 1195 581 1195 762 l 1195 406 m 1061 583 q 988 805 1061 718 q 791 892 915 892 q 605 805 675 892 q 536 583 536 718 l 536 406 q 605 182 536 269 q 791 95 675 95 q 988 181 916 95 q 1061 406 1061 268 l 1061 583 z "},"Я":{"ha":890,"x_min":67,"x_max":769,"o":"m 209 0 l 67 0 l 298 422 q 153 526 203 458 q 103 690 103 594 q 196 909 103 831 q 453 987 290 987 l 769 987 l 769 0 l 635 0 l 635 386 l 414 386 l 209 0 m 635 882 l 453 882 q 292 831 347 882 q 237 692 237 781 q 292 547 237 603 q 452 492 348 492 l 635 492 l 635 882 z "},"а":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 z "},"б":{"ha":768,"x_min":66,"x_max":723,"o":"m 420 692 q 642 598 560 692 q 723 359 723 505 l 723 345 q 635 86 723 186 q 395 -14 547 -14 q 154 86 243 -14 q 66 345 66 186 l 66 439 q 157 785 66 656 q 401 945 248 914 q 525 982 487 960 q 564 1053 564 1004 l 667 1053 l 668 1049 q 612 892 670 930 q 427 833 553 855 q 269 762 334 818 q 188 599 203 707 l 191 595 q 291 665 231 638 q 420 692 351 692 m 393 587 q 249 522 298 587 q 200 359 200 457 l 200 345 q 249 162 200 234 q 395 90 298 90 q 540 162 490 90 q 589 345 589 234 l 589 359 q 539 522 589 457 q 393 587 490 587 z "},"в":{"ha":789,"x_min":98,"x_max":718,"o":"m 98 0 l 98 734 l 387 734 q 612 683 532 734 q 692 532 692 633 q 661 440 692 480 q 573 378 630 399 q 680 315 642 361 q 718 209 718 269 q 643 53 718 106 q 434 0 568 0 l 98 0 m 231 321 l 231 103 l 434 103 q 546 131 508 103 q 584 212 584 159 q 546 293 584 264 q 434 321 508 321 l 231 321 m 231 424 l 388 424 q 516 449 473 424 q 559 525 559 474 q 515 604 559 577 q 387 630 472 630 l 231 630 l 231 424 z "},"г":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 z "},"д":{"ha":846,"x_min":31,"x_max":803,"o":"m 89 104 q 175 240 145 170 q 215 458 205 311 l 226 734 l 705 734 l 705 104 l 803 104 l 790 -216 l 669 -216 l 669 0 l 165 0 l 165 -216 l 44 -216 l 31 104 l 89 104 m 349 458 q 314 250 341 336 q 243 104 286 163 l 572 104 l 572 615 l 355 615 l 349 458 z "},"е":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 z "},"ж":{"ha":1063,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 z "},"з":{"ha":705,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 z "},"и":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 z "},"й":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 593 1036 l 595 1031 q 543 903 597 953 q 395 854 488 854 q 246 903 301 854 q 195 1031 191 953 l 196 1036 l 298 1036 q 321 963 298 991 q 395 935 345 935 q 467 963 444 935 q 491 1036 491 991 l 593 1036 z "},"к":{"ha":736,"x_min":104,"x_max":739,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 z "},"л":{"ha":768,"x_min":18,"x_max":692,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 z "},"м":{"ha":1036,"x_min":104,"x_max":926,"o":"m 515 175 l 519 175 l 759 734 l 926 734 l 926 0 l 792 0 l 792 509 l 788 511 l 563 0 l 471 0 l 241 522 l 237 521 l 237 0 l 104 0 l 104 734 l 276 734 l 515 175 z "},"н":{"ha":789,"x_min":97,"x_max":691,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 z "},"о":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 z "},"п":{"ha":789,"x_min":97,"x_max":692,"o":"m 692 0 l 558 0 l 558 628 l 231 628 l 231 0 l 97 0 l 97 734 l 692 734 l 692 0 z "},"р":{"ha":789,"x_min":97,"x_max":722,"o":"m 722 339 q 646 83 722 180 q 437 -14 570 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 199 734 l 220 639 q 310 720 256 692 q 435 747 363 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 540 160 491 90 q 589 339 589 229 l 589 353 z "},"с":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 z "},"т":{"ha":711,"x_min":48,"x_max":663,"o":"m 663 630 l 420 630 l 420 0 l 287 0 l 287 630 l 48 630 l 48 734 l 663 734 l 663 630 z "},"у":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 z "},"ф":{"ha":1007,"x_min":66,"x_max":941,"o":"m 66 353 q 136 640 66 532 q 334 747 205 747 q 385 743 361 747 q 431 730 409 738 l 431 1058 l 564 1058 l 564 726 q 614 742 587 736 q 673 747 642 747 q 871 640 802 747 q 941 353 941 532 l 941 339 q 871 82 941 179 q 675 -14 802 -14 q 615 -9 643 -14 q 564 6 587 -3 l 564 -282 l 431 -282 l 431 3 q 385 -10 409 -5 q 332 -14 360 -14 q 136 82 205 -14 q 66 339 66 179 l 66 353 m 807 353 q 764 562 807 481 q 633 642 721 642 q 595 639 612 642 q 564 631 578 636 l 564 100 q 595 93 578 95 q 634 90 612 90 q 765 159 722 90 q 807 339 807 227 l 807 353 m 200 339 q 239 158 200 226 q 366 90 278 90 q 401 93 385 90 q 431 100 417 95 l 431 633 q 401 640 417 637 q 368 642 386 642 q 240 562 279 642 q 200 353 200 482 l 200 339 z "},"х":{"ha":699,"x_min":31,"x_max":665,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 z "},"ц":{"ha":826,"x_min":97,"x_max":781,"o":"m 97 734 l 231 734 l 231 104 l 558 104 l 558 734 l 692 734 l 692 104 l 781 104 l 781 -240 l 648 -240 l 648 0 l 97 0 l 97 734 z "},"ч":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 z "},"ш":{"ha":1116,"x_min":97,"x_max":1015,"o":"m 231 734 l 231 104 l 489 104 l 489 734 l 623 734 l 623 104 l 881 104 l 881 734 l 1015 734 l 1015 0 l 97 0 l 97 734 l 231 734 z "},"щ":{"ha":1175,"x_min":97,"x_max":1134,"o":"m 231 734 l 231 104 l 489 104 l 489 734 l 623 734 l 623 104 l 881 104 l 881 734 l 1015 734 l 1015 98 l 1134 98 l 1134 -229 l 1002 -229 l 1002 0 l 97 0 l 97 734 l 231 734 z "},"ъ":{"ha":860,"x_min":20,"x_max":825,"o":"m 368 481 l 551 481 q 753 415 681 481 q 825 243 825 349 q 752 68 825 137 q 551 0 680 0 l 235 0 l 235 629 l 20 629 l 20 734 l 368 734 l 368 481 m 368 377 l 368 104 l 551 104 q 657 143 623 104 q 691 239 691 181 q 656 336 691 294 q 551 377 622 377 l 368 377 z "},"ы":{"ha":1097,"x_min":117,"x_max":971,"o":"m 251 454 l 433 454 q 636 392 564 454 q 708 230 708 330 q 635 65 708 130 q 433 0 562 0 l 117 0 l 117 734 l 251 734 l 251 454 m 971 0 l 838 0 l 838 734 l 971 734 l 971 0 m 251 350 l 251 104 l 433 104 q 539 139 505 104 q 574 225 574 174 q 539 313 574 275 q 433 350 505 350 l 251 350 z "},"ь":{"ha":755,"x_min":104,"x_max":694,"o":"m 237 454 l 420 454 q 622 392 550 454 q 694 230 694 330 q 622 65 694 130 q 420 0 549 0 l 104 0 l 104 734 l 237 734 l 237 454 m 237 350 l 237 104 l 420 104 q 526 139 492 104 q 560 225 560 174 q 526 313 560 275 q 420 350 491 350 l 237 350 z "},"э":{"ha":747,"x_min":67,"x_max":675,"o":"m 353 642 q 242 601 290 642 q 193 500 193 559 l 73 500 l 71 505 q 154 673 67 599 q 353 747 240 747 q 588 641 502 747 q 675 381 675 535 l 675 353 q 588 92 675 198 q 353 -14 501 -14 q 148 61 229 -14 q 71 248 67 137 l 72 252 l 193 252 q 239 137 193 184 q 353 90 285 90 q 485 157 438 90 q 537 318 532 223 l 535 321 l 274 321 l 274 426 l 534 426 l 535 429 q 482 580 528 518 q 353 642 435 642 z "},"ю":{"ha":1133,"x_min":104,"x_max":1066,"o":"m 237 424 l 411 424 q 511 657 425 568 q 736 747 597 747 q 978 642 889 747 q 1066 374 1066 537 l 1066 359 q 978 90 1066 195 q 738 -14 890 -14 q 508 78 595 -14 q 410 319 421 171 l 237 319 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 m 543 359 q 592 166 543 242 q 738 90 641 90 q 883 166 833 90 q 932 359 932 242 l 932 374 q 883 566 932 489 q 736 642 833 642 q 592 566 641 642 q 543 374 543 489 l 543 359 z "},"я":{"ha":789,"x_min":53,"x_max":685,"o":"m 685 734 l 685 0 l 551 0 l 551 285 l 370 285 l 197 0 l 53 0 l 239 304 q 131 384 169 330 q 94 509 94 438 q 169 671 94 608 q 375 734 243 734 l 685 734 m 228 508 q 261 425 228 459 q 361 390 294 390 l 551 390 l 551 630 l 375 630 q 265 594 302 630 q 228 508 228 557 z "},"ѐ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 435 840 l 328 840 l 158 1017 l 160 1021 l 316 1021 l 435 840 z "},"ё":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 612 871 l 464 871 l 464 1006 l 612 1006 l 612 871 m 294 871 l 145 871 l 145 1006 l 294 1006 l 294 871 z "},"ђ":{"ha":789,"x_min":-17,"x_max":695,"o":"m 418 819 l 231 819 l 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 309 l 695 309 l 695 -60 q 637 -235 695 -174 q 474 -296 578 -296 q 434 -294 452 -296 q 397 -285 416 -291 l 408 -182 q 439 -189 416 -186 q 474 -191 463 -191 q 538 -156 515 -191 q 561 -60 561 -121 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 819 l -17 819 l -17 924 l 97 924 l 97 1058 l 231 1058 l 231 924 l 418 924 l 418 819 z "},"ѓ":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 m 351 1003 l 507 1003 l 508 999 l 325 823 l 225 823 l 351 1003 z "},"є":{"ha":748,"x_min":73,"x_max":694,"o":"m 402 90 q 519 131 469 90 q 570 232 570 172 l 690 232 l 691 228 q 607 59 694 133 q 402 -14 519 -14 q 157 90 242 -14 q 73 353 73 195 l 73 381 q 158 643 73 538 q 402 747 243 747 q 613 671 531 747 q 692 485 694 595 l 690 481 l 570 481 q 522 595 570 548 q 402 642 475 642 q 261 579 307 642 q 212 422 215 515 l 213 418 l 468 418 l 468 314 l 213 314 l 212 311 q 260 153 215 215 q 402 90 306 90 z "},"ѕ":{"ha":726,"x_min":70,"x_max":657,"o":"m 524 195 q 490 267 524 239 q 363 317 456 296 q 162 396 229 346 q 94 532 94 446 q 170 684 94 621 q 368 747 246 747 q 572 682 496 747 q 644 522 648 616 l 643 517 l 515 517 q 475 604 515 566 q 368 642 435 642 q 262 611 297 642 q 228 536 228 580 q 258 467 228 492 q 382 422 289 442 q 589 341 522 393 q 657 204 657 290 q 578 46 657 106 q 372 -14 499 -14 q 151 57 231 -14 q 74 223 70 128 l 75 227 l 203 227 q 256 123 206 156 q 372 90 305 90 q 483 119 443 90 q 524 195 524 148 z "},"і":{"ha":350,"x_min":108,"x_max":241,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 z "},"ї":{"ha":349,"x_min":-61,"x_max":406,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 406 856 l 258 856 l 258 991 l 406 991 l 406 856 m 87 856 l -61 856 l -61 991 l 87 991 l 87 856 z "},"ј":{"ha":359,"x_min":-45,"x_max":251,"o":"m 251 734 l 251 -60 q 193 -235 251 -174 q 31 -296 134 -296 q -8 -293 9 -296 q -45 -284 -25 -290 l -35 -179 q -8 -185 -26 -182 q 21 -187 9 -187 q 91 -157 65 -187 q 117 -60 117 -127 l 117 734 l 251 734 m 247 924 l 113 924 l 113 1058 l 247 1058 l 247 924 z "},"љ":{"ha":1208,"x_min":44,"x_max":1149,"o":"m 692 734 l 692 455 l 874 455 q 1076 392 1004 455 q 1149 230 1149 330 q 1076 65 1149 130 q 874 0 1003 0 l 558 0 l 558 628 l 342 628 l 342 420 q 282 102 342 205 q 82 0 222 0 l 44 0 l 47 107 l 75 108 q 178 182 146 108 q 209 420 209 256 l 209 734 l 692 734 m 692 351 l 692 103 l 874 103 q 980 141 945 103 q 1015 231 1015 179 q 980 316 1015 281 q 874 351 946 351 l 692 351 z "},"њ":{"ha":1208,"x_min":97,"x_max":1148,"o":"m 231 457 l 557 457 l 557 734 l 691 734 l 691 454 l 873 454 q 1076 392 1004 454 q 1148 230 1148 330 q 1075 65 1148 130 q 873 0 1002 0 l 557 0 l 557 353 l 231 353 l 231 0 l 97 0 l 97 734 l 231 734 l 231 457 m 691 351 l 691 103 l 873 103 q 979 141 945 103 q 1014 231 1014 179 q 980 316 1014 281 q 873 351 945 351 l 691 351 z "},"ћ":{"ha":789,"x_min":13,"x_max":694,"o":"m 448 821 l 231 821 l 231 635 q 324 718 269 688 q 446 747 379 747 q 629 677 564 747 q 694 460 694 606 l 694 0 l 561 0 l 561 461 q 522 595 561 552 q 406 639 483 639 q 305 613 350 639 q 231 542 260 587 l 231 0 l 97 0 l 97 821 l 13 821 l 13 926 l 97 926 l 97 1058 l 231 1058 l 231 926 l 448 926 l 448 821 z "},"ќ":{"ha":736,"x_min":104,"x_max":739,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 m 430 1002 l 586 1002 l 587 998 l 404 822 l 304 822 l 430 1002 z "},"ѝ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 450 825 l 343 825 l 174 1002 l 176 1006 l 332 1006 l 450 825 z "},"ў":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 548 1036 l 549 1031 q 497 903 552 953 q 349 854 442 854 q 201 903 256 854 q 149 1031 146 953 l 151 1036 l 253 1036 q 276 963 253 991 q 349 935 299 935 q 422 963 398 935 q 446 1036 446 991 l 548 1036 z "},"џ":{"ha":789,"x_min":97,"x_max":692,"o":"m 231 734 l 231 104 l 558 104 l 558 734 l 692 734 l 692 0 l 463 0 l 463 -241 l 330 -241 l 330 0 l 97 0 l 97 734 l 231 734 z "},"Ѡ":{"ha":1219,"x_min":106,"x_max":1113,"o":"m 1113 987 l 1113 265 q 1035 57 1113 129 q 829 -14 956 -14 q 698 15 755 -14 q 608 103 640 45 q 514 15 574 45 q 378 -14 454 -14 q 181 57 256 -14 q 106 265 106 129 l 106 987 l 240 987 l 240 265 q 278 135 240 179 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 987 l 679 987 l 679 265 q 720 135 679 180 q 829 90 762 90 q 938 135 897 90 q 979 265 979 180 l 979 987 l 1113 987 z "},"ѡ":{"ha":1058,"x_min":84,"x_max":979,"o":"m 979 734 l 979 239 q 908 51 979 115 q 722 -14 837 -14 q 610 10 659 -14 q 530 82 560 34 q 446 10 498 34 q 328 -14 394 -14 q 151 50 218 -14 q 84 239 84 115 l 84 734 l 218 734 l 218 239 q 248 128 218 165 q 328 90 277 90 q 427 128 389 90 q 465 239 465 166 l 465 734 l 599 734 l 599 239 q 632 128 599 165 q 722 90 666 90 q 811 128 777 90 q 846 239 846 166 l 846 734 l 979 734 z "},"Ѣ":{"ha":875,"x_min":-35,"x_max":821,"o":"m 400 747 l 244 747 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 747 l -35 747 l -35 852 l 111 852 l 111 987 l 244 987 l 244 852 l 400 852 l 400 747 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"ѣ":{"ha":755,"x_min":-25,"x_max":694,"o":"m 455 734 l 237 734 l 237 507 l 420 507 q 622 438 550 507 q 694 256 694 368 q 622 72 694 144 q 420 0 549 0 l 104 0 l 104 734 l -25 734 l -25 838 l 104 838 l 104 1058 l 237 1058 l 237 838 l 455 838 l 455 734 m 237 403 l 237 104 l 420 104 q 526 147 492 104 q 560 252 560 189 q 526 358 560 313 q 420 403 491 403 l 237 403 z "},"Ѥ":{"ha":1246,"x_min":130,"x_max":1138,"o":"m 264 566 l 397 566 l 397 582 q 502 883 397 764 q 775 1002 608 1002 q 1032 915 939 1002 q 1138 677 1125 828 l 1137 673 l 1008 673 q 937 839 992 781 q 775 897 882 897 q 597 807 663 897 q 530 583 530 717 l 530 566 l 896 566 l 896 460 l 530 460 l 530 406 q 597 181 530 271 q 775 91 663 91 q 938 149 883 91 q 1008 317 992 206 l 1137 317 l 1138 313 q 1031 78 1125 170 q 775 -14 937 -14 q 502 105 608 -14 q 397 406 397 224 l 397 460 l 264 460 l 264 0 l 130 0 l 130 987 l 264 987 l 264 566 z "},"ѥ":{"ha":1038,"x_min":102,"x_max":983,"o":"m 236 418 l 363 418 q 457 654 372 561 q 691 747 541 747 q 902 671 820 747 q 981 485 983 595 l 979 481 l 859 481 q 811 595 859 548 q 691 642 764 642 q 550 579 595 642 q 500 421 504 515 l 502 418 l 786 418 l 786 313 l 502 313 l 500 310 q 549 153 504 215 q 691 90 595 90 q 808 131 758 90 q 859 232 859 172 l 979 232 l 980 228 q 896 59 983 133 q 691 -14 808 -14 q 457 78 541 -14 q 363 313 373 170 l 236 313 l 236 0 l 102 0 l 102 734 l 236 734 l 236 418 z "},"Ѧ":{"ha":838,"x_min":29,"x_max":848,"o":"m 611 297 l 502 297 l 502 0 l 369 0 l 369 297 l 269 297 l 165 0 l 29 0 l 385 987 l 500 987 l 848 0 l 712 0 l 611 297 m 307 408 l 573 408 l 444 790 l 439 790 l 307 408 z "},"ѧ":{"ha":744,"x_min":9,"x_max":722,"o":"m 505 199 l 433 199 l 433 0 l 300 0 l 300 199 l 224 199 l 145 0 l 9 0 l 307 734 l 422 734 l 722 0 l 586 0 l 505 199 m 265 304 l 463 304 l 378 516 l 366 562 l 361 562 l 349 516 l 265 304 z "},"Ѩ":{"ha":1224,"x_min":142,"x_max":1207,"o":"m 276 409 l 535 409 l 744 987 l 859 987 l 1207 0 l 1071 0 l 970 297 l 861 297 l 861 0 l 728 0 l 728 297 l 627 297 l 524 0 l 388 0 l 495 298 l 276 298 l 276 0 l 142 0 l 142 987 l 276 987 l 276 409 m 666 408 l 932 408 l 802 790 l 798 790 l 666 408 z "},"ѩ":{"ha":1050,"x_min":126,"x_max":1025,"o":"m 260 304 l 435 304 l 610 734 l 725 734 l 1025 0 l 889 0 l 808 199 l 736 199 l 736 0 l 604 0 l 604 199 l 527 199 l 448 0 l 312 0 l 393 199 l 260 199 l 260 0 l 126 0 l 126 734 l 260 734 l 260 304 m 568 304 l 766 304 l 681 516 l 669 562 l 665 562 l 652 516 l 568 304 z "},"Ѫ":{"ha":1193,"x_min":101,"x_max":1093,"o":"m 746 552 l 761 552 q 1008 481 923 552 q 1093 252 1093 410 l 1093 0 l 959 0 l 959 252 q 912 404 959 361 q 761 447 865 447 l 682 447 l 665 418 l 665 0 l 532 0 l 532 429 l 520 447 l 431 447 q 281 404 328 447 q 235 252 235 361 l 235 0 l 101 0 l 101 252 q 185 481 101 410 q 431 552 269 552 l 456 552 l 191 987 l 1011 987 l 746 552 m 598 552 l 605 552 l 798 882 l 406 882 l 598 552 z "},"ѫ":{"ha":1028,"x_min":101,"x_max":927,"o":"m 762 734 l 845 734 l 652 409 q 857 331 786 402 q 927 110 927 260 l 927 0 l 793 0 l 793 110 q 752 263 793 220 q 623 305 712 305 l 591 305 l 583 292 l 583 0 l 450 0 l 450 298 l 446 305 l 405 305 q 275 263 315 305 q 235 110 235 220 l 235 0 l 101 0 l 101 110 q 173 334 101 263 q 384 409 245 404 l 191 734 l 298 734 l 298 734 l 762 734 l 762 734 m 518 412 l 637 629 l 399 629 l 518 412 z "},"Ѭ":{"ha":1577,"x_min":129,"x_max":1477,"o":"m 1131 552 q 1392 481 1308 552 q 1477 252 1477 410 l 1477 0 l 1343 0 l 1343 252 q 1296 404 1343 361 q 1145 447 1249 447 l 1067 447 l 1050 418 l 1050 0 l 916 0 l 916 429 l 905 447 l 816 447 q 666 404 712 447 q 619 252 619 361 l 619 0 l 486 0 l 486 252 q 538 448 486 377 l 263 448 l 263 0 l 129 0 l 129 987 l 263 987 l 263 552 l 841 552 l 576 987 l 1396 987 l 1131 552 m 983 552 l 989 552 l 1182 882 l 790 882 l 983 552 z "},"ѭ":{"ha":1369,"x_min":104,"x_max":1270,"o":"m 1188 734 l 995 409 q 1200 331 1129 402 q 1270 110 1270 260 l 1270 0 l 1136 0 l 1136 110 q 1096 263 1136 220 q 966 305 1055 305 l 934 305 l 926 292 l 926 0 l 793 0 l 793 298 l 789 305 l 748 305 q 618 263 658 305 q 578 110 578 220 l 578 0 l 444 0 l 444 110 q 492 305 444 235 l 237 305 l 237 0 l 104 0 l 104 734 l 237 734 l 237 410 l 726 410 l 534 734 l 1188 734 m 861 412 l 981 629 l 742 629 l 861 412 z "},"Ѯ":{"ha":720,"x_min":50,"x_max":632,"o":"m 279 559 q 433 603 384 559 q 483 723 483 646 q 434 837 483 792 q 293 882 386 882 l 87 882 l 87 987 l 293 987 q 527 915 436 987 q 617 720 617 843 q 573 595 617 650 q 451 509 529 539 q 585 422 538 481 q 632 276 632 362 q 542 63 632 140 q 309 -14 452 -14 l 275 -14 q 200 -34 224 -14 q 177 -90 177 -54 q 209 -166 177 -137 q 285 -215 240 -195 l 231 -301 q 103 -214 156 -269 q 50 -83 51 -158 q 111 48 50 5 q 281 92 172 92 l 309 92 q 447 142 397 92 q 498 273 498 193 q 442 409 498 363 q 279 454 387 454 l 182 454 l 182 559 l 279 559 m 363 1192 l 463 1293 l 578 1293 l 578 1280 l 404 1114 l 323 1114 l 151 1279 l 151 1293 l 263 1293 l 363 1192 z "},"ѯ":{"ha":669,"x_min":50,"x_max":603,"o":"m 278 419 q 411 447 368 419 q 454 526 454 475 q 412 599 454 570 q 292 628 370 628 l 86 628 l 86 734 l 292 734 q 505 677 422 734 q 588 524 588 620 q 553 434 588 474 q 456 370 518 393 q 565 306 528 347 q 603 206 603 264 q 520 45 603 104 q 308 -14 438 -14 l 275 -14 q 200 -34 223 -14 q 176 -90 176 -54 q 208 -166 176 -137 q 285 -215 240 -195 l 231 -301 q 103 -214 155 -269 q 50 -83 50 -158 q 110 48 50 5 q 280 92 171 92 l 308 92 q 426 122 382 92 q 469 203 469 153 q 420 287 469 259 q 278 314 372 314 l 181 314 l 181 419 l 278 419 m 314 959 l 414 1059 l 529 1059 l 529 1047 l 355 881 l 274 881 l 102 1046 l 102 1059 l 214 1059 l 314 959 z "},"Ѱ":{"ha":960,"x_min":59,"x_max":886,"o":"m 534 329 l 538 328 q 694 412 635 340 q 753 600 753 484 l 753 987 l 886 987 l 886 600 q 789 333 886 434 q 534 216 692 233 l 534 0 l 399 0 l 399 217 q 152 334 245 233 q 59 600 59 434 l 59 987 l 192 987 l 192 600 q 248 414 192 485 q 395 329 303 342 l 399 330 l 399 987 l 534 987 l 534 329 z "},"ѱ":{"ha":977,"x_min":62,"x_max":920,"o":"m 546 733 l 546 101 l 550 100 q 728 197 670 115 q 787 385 787 279 q 762 556 785 469 q 702 734 740 644 l 842 734 q 899 575 877 662 q 920 385 920 488 q 830 113 920 222 q 546 -12 740 3 l 546 -321 l 412 -321 l 412 -10 q 153 109 244 7 q 62 404 62 212 l 62 734 l 195 734 l 195 403 q 253 188 195 262 q 408 101 310 114 l 412 102 l 412 733 l 546 733 z "},"Ѳ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 z "},"ѳ":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 z "},"Ѵ":{"ha":878,"x_min":15,"x_max":844,"o":"m 394 259 l 416 177 l 420 177 l 443 259 l 614 805 q 692 956 648 911 q 810 1001 736 1001 l 844 1000 l 843 886 l 835 886 q 780 864 801 886 q 741 789 760 843 l 475 0 l 361 0 l 15 987 l 160 987 l 394 259 z "},"ѵ":{"ha":699,"x_min":31,"x_max":704,"o":"m 328 216 l 339 165 l 343 165 l 356 216 l 457 579 q 530 706 485 665 q 630 747 574 747 q 670 743 653 747 q 704 730 688 740 l 689 627 q 678 631 686 629 q 662 633 671 633 q 620 615 640 633 q 591 569 600 597 l 391 0 l 290 0 l 31 734 l 168 734 l 328 216 z "},"Ѷ":{"ha":878,"x_min":15,"x_max":844,"o":"m 394 259 l 416 177 l 420 177 l 443 259 l 614 805 q 692 956 648 911 q 810 1001 736 1001 l 844 1000 l 843 886 l 835 886 q 780 864 801 886 q 741 789 760 843 l 475 0 l 361 0 l 15 987 l 160 987 l 394 259 m 389 1087 l 387 1084 l 271 1084 l 68 1260 l 70 1264 l 225 1264 l 389 1087 m 571 1084 l 465 1084 l 301 1261 l 302 1264 l 446 1264 l 571 1084 z "},"ѷ":{"ha":699,"x_min":-1,"x_max":704,"o":"m 328 216 l 339 165 l 343 165 l 356 216 l 457 579 q 530 706 485 665 q 630 747 574 747 q 670 743 653 747 q 704 730 688 740 l 689 627 q 678 631 686 629 q 662 633 671 633 q 620 615 640 633 q 591 569 600 597 l 391 0 l 290 0 l 31 734 l 168 734 l 328 216 m 319 886 l 318 883 l 202 883 l -1 1059 l 1 1063 l 156 1063 l 319 886 m 502 883 l 395 883 l 232 1060 l 233 1063 l 377 1063 l 502 883 z "},"Ѹ":{"ha":1645,"x_min":77,"x_max":1625,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 1268 272 l 1292 180 l 1296 180 l 1476 734 l 1625 734 l 1316 -113 q 1237 -241 1289 -186 q 1097 -296 1185 -296 q 1055 -293 1080 -296 q 1017 -286 1030 -289 l 1030 -180 q 1054 -182 1026 -180 q 1090 -184 1082 -184 q 1160 -146 1133 -184 q 1205 -62 1187 -108 l 1237 15 l 964 734 l 1114 734 l 1268 272 z "},"ѹ":{"ha":1488,"x_min":66,"x_max":1468,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 1111 272 l 1135 180 l 1139 180 l 1319 734 l 1468 734 l 1159 -113 q 1079 -241 1131 -186 q 939 -296 1027 -296 q 898 -293 923 -296 q 859 -286 873 -289 l 873 -180 q 897 -182 869 -180 q 932 -184 925 -184 q 1002 -146 975 -184 q 1048 -62 1029 -108 l 1080 15 l 807 734 l 956 734 l 1111 272 z "},"Ѻ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 541 -8 669 12 l 541 -96 l 407 -96 l 407 -10 q 184 105 274 7 q 77 406 77 224 l 77 581 q 184 882 77 762 q 407 998 274 981 l 407 1078 l 541 1078 l 541 996 q 757 882 669 975 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 541 882 613 863 l 541 814 l 407 814 l 407 886 q 279 805 330 869 q 210 583 210 718 l 210 406 q 279 182 210 269 q 407 101 330 118 l 407 174 l 541 174 l 541 104 q 663 181 613 123 q 736 406 736 268 l 736 583 z "},"ѻ":{"ha":789,"x_min":66,"x_max":723,"o":"m 462 -80 l 328 -80 l 328 -9 q 154 90 222 9 q 66 359 66 195 l 66 374 q 154 642 66 536 q 328 742 222 724 l 328 818 l 462 818 l 462 741 q 634 642 567 722 q 723 374 723 537 l 723 359 q 635 90 723 195 q 462 -8 567 10 l 462 -80 m 199 359 q 248 166 199 242 q 328 100 279 119 l 328 170 l 462 170 l 462 101 q 540 166 509 119 q 589 359 589 242 l 589 374 q 539 566 589 489 q 462 630 509 612 l 462 567 l 328 567 l 328 631 q 248 566 279 613 q 199 374 199 489 l 199 359 z "},"Ѽ":{"ha":1214,"x_min":106,"x_max":1109,"o":"m 838 981 l 842 983 q 1035 907 960 985 q 1109 682 1109 829 l 1109 291 q 1035 64 1109 142 q 838 -14 960 -14 q 701 15 761 -14 q 608 101 642 43 q 513 15 573 43 q 378 -14 454 -14 q 181 64 256 -14 q 106 291 106 142 l 106 682 q 181 907 106 829 q 374 983 255 985 l 378 981 l 378 882 q 278 831 315 882 q 240 682 240 779 l 240 291 q 278 141 240 193 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 558 l 675 558 l 675 265 q 720 135 675 179 q 838 90 764 90 q 938 141 899 90 q 976 291 976 193 l 976 682 q 938 831 976 779 q 838 882 899 882 l 838 981 m 907 1191 l 907 1103 l 876 1103 q 686 1145 787 1103 q 556 1187 585 1187 q 503 1168 522 1187 q 483 1115 483 1150 l 483 1103 l 392 1103 l 392 1127 q 434 1238 392 1200 q 553 1275 476 1275 q 701 1233 602 1275 q 878 1191 800 1191 l 907 1191 m 578 894 l 524 939 l 564 1017 l 563 1112 l 690 1112 l 690 1028 l 578 894 z "},"ѽ":{"ha":1058,"x_min":83,"x_max":978,"o":"m 734 734 l 738 736 q 911 668 844 739 q 978 462 978 597 l 978 264 q 911 57 978 128 q 734 -14 844 -14 q 614 10 667 -14 q 530 83 562 35 q 446 10 498 35 q 328 -14 394 -14 q 150 57 217 -14 q 83 264 83 128 l 83 462 q 150 668 83 597 q 323 736 216 739 l 328 734 l 328 635 q 247 591 277 635 q 217 462 217 547 l 217 264 q 247 134 217 178 q 328 90 277 90 q 426 128 388 90 q 464 239 464 166 l 464 397 l 597 397 l 597 239 q 635 128 597 166 q 734 90 673 90 q 814 134 784 90 q 844 264 844 178 l 844 462 q 814 591 844 547 q 734 635 784 635 l 734 734 m 852 947 l 852 860 l 821 860 q 630 902 732 860 q 500 943 529 943 q 447 925 467 943 q 427 871 427 907 l 427 859 l 336 859 l 336 884 q 378 994 336 957 q 498 1031 420 1031 q 646 989 547 1031 q 822 947 745 947 l 852 947 m 524 655 l 471 704 l 508 778 l 507 863 l 633 859 l 633 780 l 524 655 z "},"Ѿ":{"ha":1219,"x_min":106,"x_max":1113,"o":"m 318 1145 l 318 1219 l 875 1219 l 876 1145 l 651 1145 l 651 1059 l 530 1059 l 530 1145 l 318 1145 m 1113 987 l 1113 265 q 1035 57 1113 129 q 829 -14 956 -14 q 698 15 755 -14 q 608 103 640 45 q 514 15 574 45 q 378 -14 454 -14 q 181 57 256 -14 q 106 265 106 129 l 106 987 l 240 987 l 240 265 q 278 135 240 179 q 378 90 315 90 q 496 135 451 90 q 541 265 541 179 l 541 987 l 679 987 l 679 265 q 720 135 679 180 q 829 90 762 90 q 938 135 897 90 q 979 265 979 180 l 979 987 l 1113 987 z "},"ѿ":{"ha":1059,"x_min":84,"x_max":979,"o":"m 258 914 l 258 988 l 814 988 l 817 914 l 591 914 l 591 827 l 470 827 l 470 914 l 258 914 m 979 734 l 979 239 q 908 51 979 115 q 722 -14 837 -14 q 610 10 659 -14 q 530 82 560 34 q 446 10 498 34 q 328 -14 394 -14 q 151 50 218 -14 q 84 239 84 115 l 84 734 l 218 734 l 218 239 q 248 128 218 165 q 328 90 277 90 q 427 128 389 90 q 465 239 465 166 l 465 734 l 599 734 l 599 239 q 632 128 599 165 q 722 90 666 90 q 811 128 777 90 q 846 239 846 166 l 846 734 l 979 734 z "},"Ҁ":{"ha":906,"x_min":80,"x_max":824,"o":"m 536 -260 l 404 -260 l 404 -10 q 169 124 258 9 q 80 406 80 239 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 l 536 90 l 536 -260 z "},"ҁ":{"ha":745,"x_min":66,"x_max":673,"o":"m 460 -260 l 327 -260 l 327 -9 q 135 114 204 13 q 66 353 66 215 l 66 381 q 153 641 66 535 q 389 747 239 747 q 593 671 512 747 q 671 485 673 595 l 669 481 l 549 481 q 502 595 549 547 q 389 642 456 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 247 168 200 245 q 389 90 294 90 l 460 90 l 460 -260 z "},"҂":{"ha":869,"x_min":76,"x_max":795,"o":"m 410 303 l 606 186 l 557 102 l 360 217 l 237 0 l 119 0 l 272 270 l 76 386 l 124 470 l 323 354 l 460 601 l 263 717 l 313 802 l 510 686 l 635 910 l 751 910 l 596 634 l 795 518 l 744 434 l 549 549 l 410 303 z "},"҃":{"ha":790,"x_min":144,"x_max":631,"o":"m 262 891 l 262 806 l 144 806 l 144 966 l 513 966 l 513 1039 l 631 1038 l 631 891 l 262 891 z "},"҄":{"ha":820,"x_min":170,"x_max":686,"o":"m 200 972 q 376 1014 277 972 q 524 1056 475 1056 q 643 1019 601 1056 q 686 908 686 981 l 686 884 l 595 884 l 595 896 q 575 949 595 931 q 522 968 555 968 q 391 926 492 968 q 201 884 290 884 l 170 884 l 170 972 l 200 972 z "},"҅":{"ha":534,"x_min":174,"x_max":340,"o":"m 174 1017 l 174 1101 l 300 1101 l 300 1006 l 340 927 l 285 882 l 174 1017 z "},"҆":{"ha":574,"x_min":203,"x_max":370,"o":"m 258 882 l 203 927 l 243 1006 l 242 1101 l 370 1101 l 370 1017 l 258 882 z "},"҈":{"ha":1408,"x_min":40,"x_max":1359,"o":"m 553 859 l 551 863 q 586 952 548 917 q 690 987 624 987 q 793 952 755 987 q 829 863 831 917 l 827 859 l 757 859 q 740 908 757 888 q 690 928 724 928 q 640 908 656 928 q 624 859 624 888 l 553 859 m 954 671 l 952 675 q 987 764 949 728 q 1090 799 1025 799 q 1194 764 1156 799 q 1230 675 1233 729 l 1229 671 l 1158 671 q 1141 721 1158 701 q 1090 740 1125 740 q 1041 720 1057 740 q 1025 671 1025 701 l 954 671 m 1080 330 l 1079 334 q 1114 422 1076 387 q 1217 458 1152 458 q 1321 423 1282 458 q 1356 334 1359 388 l 1355 330 l 1284 330 q 1268 379 1284 360 q 1217 399 1252 399 q 1167 379 1183 399 q 1152 330 1152 359 l 1080 330 m 947 -22 l 945 -18 q 980 71 942 35 q 1084 106 1019 106 q 1187 71 1149 106 q 1223 -18 1225 36 l 1221 -22 l 1151 -22 q 1135 27 1151 8 q 1084 47 1118 47 q 1034 27 1050 47 q 1018 -22 1018 7 l 947 -22 m 556 -214 l 555 -210 q 589 -121 551 -156 q 693 -87 627 -87 q 797 -121 758 -87 q 832 -210 835 -156 l 831 -214 l 760 -214 q 744 -165 760 -184 q 693 -146 727 -146 q 643 -165 659 -146 q 627 -214 627 -184 l 556 -214 m 164 671 l 163 675 q 198 764 159 729 q 302 799 236 799 q 405 764 367 799 q 441 675 444 729 l 439 671 l 369 671 q 352 720 369 701 q 302 740 336 740 q 252 720 268 740 q 236 671 236 701 l 164 671 m 45 330 l 43 334 q 78 422 40 387 q 182 458 117 458 q 285 423 247 458 q 321 334 323 388 l 319 330 l 249 330 q 233 379 249 360 q 182 399 216 399 q 132 379 148 399 q 116 330 116 359 l 45 330 m 157 -22 l 156 -18 q 191 71 153 35 q 294 106 229 106 q 398 71 359 106 q 434 -18 437 36 l 433 -22 l 361 -22 q 345 27 361 8 q 294 47 329 47 q 245 27 260 47 q 229 -22 229 7 l 157 -22 z "},"҉":{"ha":1362,"x_min":52,"x_max":1311,"o":"m 749 -41 l 756 -50 l 673 -280 l 608 -280 l 656 -41 l 749 -41 m 616 764 l 608 773 l 691 1002 l 756 1002 l 709 764 l 616 764 m 1076 428 l 1085 436 l 1311 352 l 1311 286 l 1076 334 l 1076 428 m 287 294 l 278 286 l 52 370 l 52 436 l 287 388 l 287 294 m 913 680 l 915 690 l 1133 794 l 1179 748 q 979 612 1154 732 l 913 680 m 450 14 l 448 3 l 231 -101 l 184 -54 l 384 81 l 450 14 m 355 583 l 343 585 l 243 806 l 287 852 l 421 650 l 355 583 m 1007 109 l 1019 108 l 1120 -113 l 1075 -161 l 941 42 l 1007 109 z "},"Ҋ":{"ha":991,"x_min":122,"x_max":980,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 696 1268 l 698 1264 q 646 1136 701 1185 q 498 1086 591 1086 q 349 1136 404 1086 q 298 1264 294 1185 l 299 1268 l 401 1268 q 425 1196 401 1223 q 498 1168 448 1168 q 570 1196 547 1168 q 594 1268 594 1224 l 696 1268 m 980 7 l 878 -201 l 799 -201 l 846 14 l 846 124 l 980 124 l 980 7 z "},"ҋ":{"ha":789,"x_min":97,"x_max":803,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 593 1036 l 595 1031 q 543 903 597 953 q 395 854 488 854 q 246 903 301 854 q 195 1031 191 953 l 196 1036 l 298 1036 q 321 963 298 991 q 395 935 345 935 q 467 963 444 935 q 491 1036 491 991 l 593 1036 m 803 7 l 701 -201 l 622 -201 l 669 14 l 669 124 l 803 124 l 803 7 z "},"Ҍ":{"ha":875,"x_min":-35,"x_max":821,"o":"m 400 747 l 244 747 l 244 597 l 488 597 q 733 516 646 597 q 821 301 821 435 q 733 83 821 167 q 488 0 645 0 l 111 0 l 111 747 l -35 747 l -35 852 l 111 852 l 111 987 l 244 987 l 244 852 l 400 852 l 400 747 m 244 492 l 244 104 l 488 104 q 637 162 587 104 q 688 302 688 219 q 638 437 688 383 q 488 492 588 492 l 244 492 z "},"ҍ":{"ha":755,"x_min":-25,"x_max":694,"o":"m 455 882 l 237 882 l 237 507 l 420 507 q 622 438 550 507 q 694 256 694 368 q 622 72 694 144 q 420 0 549 0 l 104 0 l 104 882 l -25 882 l -25 987 l 104 987 l 104 1119 l 237 1119 l 237 987 l 455 987 l 455 882 m 237 403 l 237 104 l 420 104 q 526 147 492 104 q 560 252 560 189 q 526 358 560 313 q 420 403 491 403 l 237 403 z "},"Ҏ":{"ha":888,"x_min":122,"x_max":850,"o":"m 256 396 l 256 0 l 122 0 l 122 987 l 500 987 q 745 906 658 987 q 833 692 833 825 q 759 490 833 568 l 850 391 l 772 320 l 672 429 q 500 396 600 396 l 256 396 m 256 500 l 500 500 q 592 515 553 500 l 519 595 l 597 666 l 672 584 q 699 690 699 629 q 649 827 699 772 q 500 882 600 882 l 256 882 l 256 500 z "},"ҏ":{"ha":789,"x_min":97,"x_max":724,"o":"m 722 339 q 652 92 722 187 l 724 12 l 646 -59 l 574 20 q 437 -14 515 -14 q 318 8 369 -14 q 231 77 267 31 l 231 -282 l 97 -282 l 97 734 l 212 734 l 224 644 q 313 721 260 694 q 435 747 366 747 q 647 639 571 747 q 722 353 722 531 l 722 339 m 589 353 q 539 558 589 478 q 395 639 490 639 q 296 613 337 639 q 231 541 256 587 l 231 186 q 296 116 256 141 q 396 90 337 90 q 490 113 451 90 l 417 195 l 496 266 l 560 194 q 589 339 589 255 l 589 353 z "},"Ґ":{"ha":762,"x_min":111,"x_max":719,"o":"m 719 882 l 244 882 l 244 0 l 111 0 l 111 987 l 585 987 l 585 1214 l 719 1214 l 719 882 z "},"ґ":{"ha":621,"x_min":97,"x_max":567,"o":"m 567 628 l 231 628 l 231 0 l 97 0 l 97 734 l 433 734 l 433 947 l 567 947 l 567 628 z "},"Ғ":{"ha":773,"x_min":-3,"x_max":728,"o":"m 432 462 l 256 462 l 256 0 l 122 0 l 122 462 l -3 462 l -3 567 l 122 567 l 122 987 l 728 987 l 728 882 l 256 882 l 256 567 l 432 567 l 432 462 z "},"ғ":{"ha":576,"x_min":-9,"x_max":564,"o":"m 426 323 l 231 323 l 231 0 l 97 0 l 97 323 l -9 323 l -9 428 l 97 428 l 97 734 l 564 734 l 564 628 l 231 628 l 231 428 l 426 428 l 426 323 z "},"Ҕ":{"ha":850,"x_min":122,"x_max":785,"o":"m 728 882 l 256 882 l 256 565 l 378 565 q 677 461 570 565 q 785 173 785 357 q 702 -100 785 -5 q 466 -193 620 -195 l 462 -191 l 460 -95 q 607 -26 563 -95 q 651 173 651 43 q 582 381 650 310 q 378 453 514 453 l 256 453 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 z "},"ҕ":{"ha":700,"x_min":97,"x_max":652,"o":"m 564 628 l 231 628 l 231 439 l 306 439 q 556 352 459 439 q 652 115 652 264 q 585 -74 651 22 q 391 -193 519 -169 l 355 -92 q 481 -15 443 -68 q 519 115 519 38 q 459 271 517 215 q 306 327 401 327 l 231 327 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 z "},"Җ":{"ha":1267,"x_min":19,"x_max":1297,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 1297 -243 l 1164 -243 l 1164 106 l 1297 106 l 1297 -243 z "},"җ":{"ha":1063,"x_min":14,"x_max":1069,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 1069 -243 l 935 -243 l 935 106 l 1069 106 l 1069 -243 z "},"Ҙ":{"ha":824,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 m 477 -292 l 344 -292 l 344 58 l 477 58 l 477 -292 z "},"ҙ":{"ha":705,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 m 419 -291 l 286 -291 l 286 59 l 419 59 l 419 -291 z "},"Қ":{"ha":893,"x_min":122,"x_max":937,"o":"m 371 446 l 256 446 l 256 0 l 122 0 l 122 987 l 256 987 l 256 551 l 359 551 l 712 987 l 860 987 l 862 984 l 479 510 l 890 3 l 888 0 l 728 0 l 371 446 m 937 -243 l 804 -243 l 804 106 l 937 106 l 937 -243 z "},"қ":{"ha":736,"x_min":104,"x_max":772,"o":"m 325 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 315 424 l 557 734 l 713 734 l 715 730 l 428 382 l 739 3 l 736 0 l 572 0 l 325 311 m 772 -243 l 638 -243 l 638 106 l 772 106 l 772 -243 z "},"Ҝ":{"ha":878,"x_min":111,"x_max":867,"o":"m 839 987 l 569 520 l 867 0 l 701 0 l 470 446 l 417 446 l 417 281 l 311 281 l 311 446 l 244 446 l 244 0 l 111 0 l 111 987 l 244 987 l 244 551 l 311 551 l 311 724 l 417 724 l 417 551 l 469 551 l 684 987 l 839 987 z "},"ҝ":{"ha":765,"x_min":104,"x_max":762,"o":"m 738 734 l 508 387 l 762 0 l 593 0 l 410 311 l 395 311 l 395 182 l 288 182 l 288 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 288 424 l 288 567 l 395 567 l 395 424 l 405 424 l 578 734 l 738 734 z "},"Ҟ":{"ha":907,"x_min":-7,"x_max":903,"o":"m 385 446 l 269 446 l 269 0 l 136 0 l 136 783 l -7 783 l -7 888 l 136 888 l 136 987 l 269 987 l 269 888 l 427 888 l 427 783 l 269 783 l 269 551 l 372 551 l 726 987 l 873 987 l 876 984 l 492 510 l 903 3 l 901 0 l 742 0 l 385 446 z "},"ҟ":{"ha":726,"x_min":-33,"x_max":727,"o":"m 332 338 l 245 338 l 245 0 l 111 0 l 111 823 l -33 823 l -33 928 l 111 928 l 111 1058 l 245 1058 l 245 928 l 401 928 l 401 823 l 245 823 l 245 445 l 330 445 l 531 734 l 691 734 l 440 400 l 727 0 l 570 0 l 332 338 z "},"Ҡ":{"ha":1139,"x_min":45,"x_max":1136,"o":"m 617 446 l 502 446 l 502 0 l 368 0 l 368 882 l 45 882 l 45 987 l 502 987 l 502 551 l 605 551 l 958 987 l 1106 987 l 1108 984 l 725 510 l 1136 3 l 1134 0 l 975 0 l 617 446 z "},"ҡ":{"ha":964,"x_min":44,"x_max":959,"o":"m 545 311 l 458 311 l 458 0 l 324 0 l 324 628 l 44 628 l 44 734 l 458 734 l 458 424 l 535 424 l 777 734 l 934 734 l 936 730 l 648 382 l 959 3 l 957 0 l 793 0 l 545 311 z "},"Ң":{"ha":991,"x_min":122,"x_max":967,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 967 -243 l 833 -243 l 833 106 l 967 106 l 967 -243 z "},"ң":{"ha":789,"x_min":97,"x_max":789,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 m 789 -243 l 656 -243 l 656 106 l 789 106 l 789 -243 z "},"Ҥ":{"ha":1348,"x_min":122,"x_max":1298,"o":"m 256 541 l 735 541 l 735 987 l 1298 987 l 1298 882 l 869 882 l 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 z "},"ҥ":{"ha":987,"x_min":97,"x_max":937,"o":"m 231 416 l 557 416 l 557 734 l 937 734 l 937 628 l 691 628 l 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 z "},"Ҧ":{"ha":1423,"x_min":122,"x_max":1354,"o":"m 870 565 l 947 565 q 1247 461 1140 565 q 1354 173 1354 357 q 1272 -100 1354 -5 q 1036 -193 1190 -195 l 1031 -191 l 1030 -95 q 1177 -26 1133 -95 q 1221 173 1221 43 q 1152 381 1219 310 q 947 453 1084 453 l 870 453 l 870 0 l 736 0 l 736 882 l 256 882 l 256 0 l 122 0 l 122 987 l 870 987 l 870 565 z "},"ҧ":{"ha":1211,"x_min":97,"x_max":1166,"o":"m 692 439 l 806 439 q 1065 352 965 439 q 1166 115 1166 264 q 1099 -74 1164 22 q 904 -193 1033 -169 l 869 -92 q 994 -15 956 -68 q 1032 115 1032 38 q 969 271 1031 216 q 806 327 907 327 l 692 327 l 692 0 l 558 0 l 558 628 l 231 628 l 231 0 l 97 0 l 97 734 l 692 734 l 692 439 z "},"Ҩ":{"ha":1029,"x_min":77,"x_max":973,"o":"m 973 -20 q 832 -8 899 -20 q 705 30 764 5 q 604 -3 657 8 q 494 -14 551 -14 q 193 121 309 -14 q 77 459 77 256 l 77 572 q 162 878 77 755 q 381 996 248 1000 l 385 995 l 385 892 q 257 802 304 892 q 210 573 210 712 l 210 459 q 287 197 210 300 q 494 95 364 95 q 538 97 517 95 q 579 104 559 99 q 452 265 496 170 q 408 473 408 360 l 408 627 q 485 893 408 787 q 687 1000 563 1000 q 888 896 810 1000 q 965 627 965 791 l 965 459 q 927 262 965 353 q 819 106 888 171 q 892 93 854 97 q 973 88 930 88 l 973 -20 m 541 472 q 581 289 541 368 q 696 163 621 209 l 700 163 q 797 286 762 208 q 831 459 831 363 l 831 629 q 792 819 831 746 q 687 891 753 891 q 581 817 621 891 q 541 629 541 742 l 541 472 z "},"ҩ":{"ha":840,"x_min":73,"x_max":804,"o":"m 804 -8 q 688 1 743 -8 q 586 31 633 11 q 501 -3 546 8 q 408 -14 456 -14 q 166 100 260 -14 q 73 387 73 214 l 73 425 q 139 655 73 564 q 309 742 205 745 l 313 741 l 313 638 q 235 579 264 638 q 207 427 207 519 l 207 387 q 261 177 207 259 q 408 95 315 95 q 437 97 422 95 q 466 102 452 98 q 370 229 403 156 q 336 389 336 302 l 336 458 q 395 667 336 587 q 553 748 454 748 q 711 662 651 748 q 770 444 770 576 l 770 373 q 748 232 770 298 q 685 115 726 165 q 741 104 711 107 q 804 100 770 100 l 804 -8 m 637 373 l 637 446 q 614 584 637 530 q 555 635 592 638 l 551 635 q 491 590 512 638 q 470 460 470 541 l 470 387 q 495 263 470 318 q 568 174 521 208 l 572 174 q 620 259 603 208 q 637 373 637 311 z "},"Ҫ":{"ha":880,"x_min":80,"x_max":824,"o":"m 820 316 l 821 312 q 724 79 824 173 q 458 -14 623 -14 q 185 104 291 -14 q 80 406 80 223 l 80 581 q 185 883 80 764 q 458 1002 291 1002 q 725 912 626 1002 q 821 676 824 823 l 820 672 l 692 672 q 631 836 692 776 q 458 897 570 897 q 280 806 347 897 q 214 583 214 716 l 214 406 q 280 180 214 271 q 458 90 347 90 q 631 150 570 90 q 692 316 692 210 l 820 316 m 528 -292 l 394 -292 l 394 58 l 528 58 l 528 -292 z "},"ҫ":{"ha":737,"x_min":66,"x_max":688,"o":"m 395 90 q 512 131 462 90 q 563 232 563 172 l 683 232 l 684 228 q 600 59 688 133 q 395 -14 512 -14 q 151 90 235 -14 q 66 353 66 195 l 66 381 q 151 643 66 538 q 395 747 236 747 q 606 671 524 747 q 685 485 688 595 l 684 481 l 563 481 q 515 595 563 548 q 395 642 468 642 q 245 567 290 642 q 200 381 200 491 l 200 353 q 245 165 200 240 q 395 90 290 90 m 432 -292 l 298 -292 l 298 58 l 432 58 l 432 -292 z "},"Ҭ":{"ha":814,"x_min":23,"x_max":791,"o":"m 791 882 l 473 882 l 473 0 l 340 0 l 340 882 l 23 882 l 23 987 l 791 987 l 791 882 m 572 -243 l 438 -243 l 438 106 l 572 106 l 572 -243 z "},"ҭ":{"ha":711,"x_min":48,"x_max":663,"o":"m 663 630 l 420 630 l 420 0 l 287 0 l 287 630 l 48 630 l 48 734 l 663 734 l 663 630 m 519 -243 l 385 -243 l 385 106 l 519 106 l 519 -243 z "},"Ү":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 z "},"ү":{"ha":699,"x_min":31,"x_max":675,"o":"m 539 734 l 675 734 l 421 42 l 421 -283 l 288 -283 l 288 45 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 z "},"Ұ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 550 463 l 631 463 l 631 357 l 493 357 l 488 347 l 488 0 l 355 0 l 355 356 l 354 357 l 197 357 l 197 463 l 297 463 l 14 987 l 166 987 l 424 486 z "},"ұ":{"ha":699,"x_min":31,"x_max":675,"o":"m 576 -9 l 421 -9 l 421 -283 l 288 -283 l 288 -9 l 141 -9 l 141 96 l 269 96 l 31 734 l 168 734 l 342 216 l 353 165 l 357 165 l 370 216 l 539 734 l 675 734 l 441 96 l 576 96 l 576 -9 z "},"Ҳ":{"ha":878,"x_min":45,"x_max":857,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 m 857 -243 l 724 -243 l 724 106 l 857 106 l 857 -243 z "},"ҳ":{"ha":699,"x_min":31,"x_max":702,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 m 702 -243 l 568 -243 l 568 106 l 702 106 l 702 -243 z "},"Ҵ":{"ha":1253,"x_min":37,"x_max":1147,"o":"m 269 881 l 37 881 l 37 986 l 684 986 l 684 881 l 403 881 l 403 105 l 883 105 l 883 987 l 1017 987 l 1017 109 l 1147 109 l 1147 -241 l 1014 -241 l 1014 0 l 269 0 l 269 881 z "},"ҵ":{"ha":968,"x_min":22,"x_max":888,"o":"m 203 629 l 22 629 l 22 734 l 502 734 l 502 629 l 337 629 l 337 104 l 665 104 l 665 734 l 798 734 l 798 104 l 888 104 l 888 -240 l 754 -240 l 754 0 l 203 0 l 203 629 z "},"Ҷ":{"ha":956,"x_min":100,"x_max":931,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 931 -243 l 798 -243 l 798 106 l 931 106 l 931 -243 z "},"ҷ":{"ha":767,"x_min":86,"x_max":768,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 768 -243 l 635 -243 l 635 106 l 768 106 l 768 -243 z "},"Ҹ":{"ha":948,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 541 376 558 378 l 541 214 l 434 214 l 434 372 l 431 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 l 434 477 l 434 690 l 541 690 l 541 484 q 567 487 554 485 q 699 515 632 496 l 699 987 l 833 987 z "},"ҹ":{"ha":768,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 439 245 448 246 l 439 155 l 332 155 l 332 243 q 161 312 226 252 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 332 351 285 359 l 332 539 l 439 539 l 439 351 l 456 353 q 536 368 496 358 l 536 734 l 670 734 l 670 0 z "},"Һ":{"ha":948,"x_min":94,"x_max":827,"o":"m 94 0 l 94 987 l 227 987 l 227 578 q 352 606 292 597 q 495 616 412 616 q 742 544 658 616 q 827 312 827 472 l 827 0 l 692 0 l 692 312 q 645 467 692 423 q 495 510 598 510 q 359 500 424 510 q 227 472 294 490 l 227 0 l 94 0 z "},"һ":{"ha":768,"x_min":100,"x_max":684,"o":"m 100 735 l 234 735 l 234 473 q 313 488 273 483 q 400 493 354 493 q 609 423 533 493 q 684 216 684 353 l 684 1 l 551 1 l 551 216 q 513 346 551 305 q 400 387 475 387 q 315 382 355 387 q 234 367 274 377 l 234 1 l 100 1 l 100 735 z "},"Ҽ":{"ha":1179,"x_min":52,"x_max":1072,"o":"m 743 -16 q 394 119 519 -16 q 270 475 270 254 l 270 479 q 107 559 163 493 q 55 730 52 626 l 56 734 l 159 734 q 187 628 159 671 q 273 574 215 586 q 405 882 291 763 q 677 1000 519 1000 q 970 880 869 1000 q 1072 549 1072 760 l 1072 475 l 408 475 l 407 472 q 487 196 404 303 q 743 89 571 89 q 867 104 815 89 q 970 146 920 120 l 1003 52 q 904 6 969 27 q 743 -16 839 -16 m 677 895 q 490 804 562 895 q 409 570 419 712 l 411 567 l 938 567 l 938 588 q 877 811 938 728 q 677 895 815 895 z "},"ҽ":{"ha":824,"x_min":-22,"x_max":755,"o":"m 475 -14 q 232 88 322 -14 q 141 353 141 190 l 141 358 q 19 437 60 378 q -22 588 -22 496 l 84 588 q 99 508 84 541 q 147 458 115 475 q 257 667 170 586 q 455 747 345 747 q 680 658 604 747 q 755 419 755 568 l 755 336 l 279 336 l 277 332 q 331 158 279 227 q 475 90 382 90 q 594 110 543 90 q 683 163 646 129 l 735 76 q 631 12 696 37 q 475 -14 567 -14 m 455 642 q 340 586 388 642 q 282 444 293 529 l 283 440 l 622 440 l 622 458 q 580 589 622 536 q 455 642 538 642 z "},"Ҿ":{"ha":1078,"x_min":52,"x_max":1072,"o":"m 743 -16 q 394 119 519 -16 q 270 475 270 254 l 270 479 q 107 559 163 493 q 55 730 52 626 l 56 734 l 159 734 q 187 628 159 671 q 273 574 215 586 q 405 882 291 763 q 677 1000 519 1000 q 970 880 869 1000 q 1072 549 1072 760 l 1072 475 l 408 475 l 407 472 q 487 196 404 303 q 743 89 571 89 q 867 104 815 89 q 970 146 920 120 l 1003 52 q 904 6 969 27 q 743 -16 839 -16 m 677 895 q 490 804 562 895 q 409 570 419 712 l 411 567 l 938 567 l 938 588 q 877 811 938 728 q 677 895 815 895 m 730 -288 l 596 -288 l 596 62 l 730 62 l 730 -288 z "},"ҿ":{"ha":824,"x_min":-22,"x_max":755,"o":"m 475 -14 q 232 88 322 -14 q 141 353 141 190 l 141 358 q 19 437 60 378 q -22 588 -22 496 l 84 588 q 99 508 84 541 q 147 458 115 475 q 257 667 170 586 q 455 747 345 747 q 680 658 604 747 q 755 419 755 568 l 755 336 l 279 336 l 277 332 q 331 158 279 227 q 475 90 382 90 q 594 110 543 90 q 683 163 646 129 l 735 76 q 631 12 696 37 q 475 -14 567 -14 m 455 642 q 340 586 388 642 q 282 444 293 529 l 283 440 l 622 440 l 622 458 q 580 589 622 536 q 455 642 538 642 m 549 -287 l 415 -287 l 415 63 l 549 63 l 549 -287 z "},"Ӏ":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ӂ":{"ha":1267,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 836 1268 l 837 1264 q 785 1136 840 1185 q 637 1086 730 1086 q 488 1136 543 1086 q 437 1264 433 1185 l 438 1268 l 541 1268 q 564 1196 541 1223 q 637 1168 587 1168 q 709 1196 686 1168 q 733 1268 733 1224 l 836 1268 z "},"ӂ":{"ha":1063,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 731 1036 l 732 1031 q 680 903 735 953 q 532 854 625 854 q 384 903 439 854 q 332 1031 329 953 l 334 1036 l 436 1036 q 459 963 436 991 q 532 935 482 935 q 605 963 581 935 q 629 1036 629 991 l 731 1036 z "},"Ӄ":{"ha":878,"x_min":111,"x_max":836,"o":"m 481 560 q 739 443 648 545 q 831 171 831 340 q 748 -102 831 -7 q 512 -195 666 -197 l 508 -193 l 507 -97 q 653 -28 609 -97 q 697 171 697 41 q 628 379 696 308 q 424 450 560 450 l 244 450 l 244 0 l 111 0 l 111 987 l 244 987 l 244 563 l 331 563 l 684 987 l 834 987 l 836 984 l 481 560 z "},"ӄ":{"ha":760,"x_min":104,"x_max":715,"o":"m 440 416 q 638 320 565 395 q 711 119 711 245 q 644 -61 709 29 q 449 -175 578 -152 l 414 -73 q 539 -2 501 -52 q 578 119 578 47 q 516 264 576 217 q 351 311 456 311 l 237 311 l 237 0 l 104 0 l 104 734 l 237 734 l 237 424 l 294 424 l 557 734 l 713 734 l 715 730 l 440 416 z "},"Ӆ":{"ha":984,"x_min":33,"x_max":973,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 m 973 7 l 871 -201 l 792 -201 l 840 14 l 840 124 l 973 124 l 973 7 z "},"ӆ":{"ha":768,"x_min":18,"x_max":803,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 m 803 7 l 701 -201 l 622 -201 l 669 14 l 669 124 l 803 124 l 803 7 z "},"Ӈ":{"ha":991,"x_min":122,"x_max":869,"o":"m 256 987 l 256 541 l 734 541 l 734 987 l 869 987 l 869 -60 q 810 -235 869 -174 q 647 -296 751 -296 q 608 -293 626 -296 q 571 -284 590 -290 l 581 -182 q 612 -189 589 -186 q 647 -191 635 -191 q 711 -156 688 -191 q 734 -60 734 -121 l 734 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 z "},"ӈ":{"ha":789,"x_min":97,"x_max":691,"o":"m 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 -60 q 632 -235 691 -174 q 470 -296 574 -296 q 431 -293 449 -296 q 394 -284 413 -290 l 404 -182 q 436 -189 413 -186 q 470 -191 458 -191 q 534 -156 511 -191 q 557 -60 557 -121 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 z "},"Ӊ":{"ha":991,"x_min":122,"x_max":980,"o":"m 869 0 l 735 0 l 735 436 l 256 436 l 256 0 l 122 0 l 122 987 l 256 987 l 256 541 l 735 541 l 735 987 l 869 987 l 869 0 m 980 7 l 878 -201 l 799 -201 l 846 14 l 846 124 l 980 124 l 980 7 z "},"ӊ":{"ha":789,"x_min":97,"x_max":802,"o":"m 691 0 l 557 0 l 557 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 557 416 l 557 734 l 691 734 l 691 0 m 802 7 l 701 -201 l 621 -201 l 669 14 l 669 124 l 802 124 l 802 7 z "},"Ӌ":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 734 -243 l 600 -243 l 600 106 l 734 106 l 734 -243 z "},"ӌ":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 570 -243 l 437 -243 l 437 106 l 570 106 l 570 -243 z "},"Ӎ":{"ha":1220,"x_min":122,"x_max":1208,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 m 1208 7 l 1107 -201 l 1027 -201 l 1075 14 l 1075 124 l 1208 124 l 1208 7 z "},"ӎ":{"ha":1036,"x_min":104,"x_max":1037,"o":"m 515 175 l 519 175 l 759 734 l 926 734 l 926 0 l 792 0 l 792 509 l 788 511 l 563 0 l 471 0 l 241 522 l 237 521 l 237 0 l 104 0 l 104 734 l 276 734 l 515 175 m 1037 7 l 935 -201 l 856 -201 l 903 14 l 903 124 l 1037 124 l 1037 7 z "},"ӏ":{"ha":393,"x_min":129,"x_max":263,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 z "},"Ӑ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 z "},"ӑ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 z "},"Ӓ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 684 1088 l 535 1088 l 535 1224 l 684 1224 l 684 1088 m 365 1088 l 216 1088 l 216 1224 l 365 1224 l 365 1088 z "},"ӓ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 610 870 l 462 870 l 462 1006 l 610 1006 l 610 870 m 292 870 l 143 870 l 143 1006 l 292 1006 l 292 870 z "},"Ӕ":{"ha":1303,"x_min":-14,"x_max":1281,"o":"m 1281 0 l 674 0 l 664 237 l 286 237 l 149 0 l -14 0 l 583 987 l 1239 987 l 1239 882 l 770 882 l 784 566 l 1184 566 l 1184 461 l 788 461 l 803 104 l 1281 104 l 1281 0 m 356 359 l 659 359 l 638 840 l 635 842 l 356 359 z "},"ӕ":{"ha":1173,"x_min":39,"x_max":1126,"o":"m 856 -14 q 693 19 764 -14 q 578 113 623 52 q 464 22 540 59 q 280 -14 389 -14 q 102 45 165 -14 q 39 206 39 104 q 117 372 39 313 q 345 431 195 431 l 500 431 l 500 488 q 465 601 500 560 q 363 642 430 642 q 252 605 294 642 q 211 515 211 568 l 83 527 l 82 531 q 156 686 79 625 q 363 747 234 747 q 500 720 441 747 q 593 640 559 692 q 696 719 637 691 q 824 747 755 747 q 1047 659 969 747 q 1126 416 1126 571 l 1126 336 l 645 336 l 644 332 q 697 157 644 224 q 856 90 751 90 q 971 109 926 90 q 1069 162 1016 127 l 1114 68 q 1015 12 1078 39 q 856 -14 951 -14 m 307 90 q 414 120 358 90 q 500 188 471 149 l 500 334 l 346 334 q 219 296 264 334 q 173 203 173 258 q 207 122 173 153 q 307 90 241 90 m 824 642 q 701 585 747 642 q 646 437 654 528 l 648 434 l 992 434 l 992 455 q 951 590 992 538 q 824 642 911 642 z "},"Ӗ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 625 1268 l 627 1264 q 574 1136 629 1185 q 427 1086 519 1086 q 278 1136 333 1086 q 227 1264 223 1185 l 228 1268 l 330 1268 q 353 1196 330 1223 q 427 1168 376 1168 q 499 1196 475 1168 q 523 1268 523 1224 l 625 1268 z "},"ӗ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 578 1050 l 579 1046 q 527 918 582 968 q 379 869 472 869 q 231 918 286 869 q 179 1046 176 968 l 180 1050 l 283 1050 q 306 978 283 1006 q 379 950 329 950 q 452 978 428 950 q 475 1050 475 1006 l 578 1050 z "},"Ә":{"ha":966,"x_min":60,"x_max":890,"o":"m 416 1002 q 765 866 640 1002 q 890 510 890 731 l 890 477 q 772 124 890 262 q 482 -14 653 -14 q 168 105 276 -14 q 60 437 60 225 l 60 510 l 752 510 l 753 513 q 672 789 756 682 q 416 897 587 897 q 291 881 343 897 q 189 840 239 865 l 156 934 q 255 980 190 958 q 416 1002 319 1002 m 482 90 q 669 181 597 90 q 751 415 740 273 l 749 418 l 194 418 l 194 397 q 261 174 194 257 q 482 90 329 90 z "},"ә":{"ha":739,"x_min":79,"x_max":693,"o":"m 359 748 q 603 646 512 748 q 693 381 693 544 l 693 351 q 600 91 693 195 q 379 -13 507 -13 q 155 76 231 -13 q 79 315 79 166 l 79 398 l 555 398 l 557 402 q 503 575 555 507 q 359 644 452 644 q 240 624 291 644 q 151 571 189 605 l 99 658 q 203 722 138 696 q 359 748 267 748 m 379 92 q 494 148 446 92 q 552 290 541 205 l 551 294 l 212 294 l 212 276 q 254 145 212 197 q 379 92 296 92 z "},"Ӛ":{"ha":966,"x_min":60,"x_max":890,"o":"m 416 1002 q 765 866 640 1002 q 890 510 890 731 l 890 477 q 772 124 890 262 q 482 -14 653 -14 q 168 105 276 -14 q 60 437 60 225 l 60 510 l 752 510 l 753 513 q 672 789 756 682 q 416 897 587 897 q 291 881 343 897 q 189 840 239 865 l 156 934 q 255 980 190 958 q 416 1002 319 1002 m 482 90 q 669 181 597 90 q 751 415 740 273 l 749 418 l 194 418 l 194 397 q 261 174 194 257 q 482 90 329 90 m 661 1057 l 512 1057 l 512 1193 l 661 1193 l 661 1057 m 342 1057 l 193 1057 l 193 1193 l 342 1193 l 342 1057 z "},"ӛ":{"ha":739,"x_min":79,"x_max":693,"o":"m 359 748 q 603 646 512 748 q 693 381 693 544 l 693 351 q 600 91 693 195 q 379 -13 507 -13 q 155 76 231 -13 q 79 315 79 166 l 79 398 l 555 398 l 557 402 q 503 575 555 507 q 359 644 452 644 q 240 624 291 644 q 151 571 189 605 l 99 658 q 203 722 138 696 q 359 748 267 748 m 379 92 q 494 148 446 92 q 552 290 541 205 l 551 294 l 212 294 l 212 276 q 254 145 212 197 q 379 92 296 92 m 619 870 l 471 870 l 471 1006 l 619 1006 l 619 870 m 300 870 l 152 870 l 152 1006 l 300 1006 l 300 870 z "},"Ӝ":{"ha":1144,"x_min":19,"x_max":1249,"o":"m 808 453 l 704 453 l 704 0 l 570 0 l 570 453 l 460 453 l 186 0 l 19 0 l 349 522 l 47 987 l 203 987 l 459 558 l 570 558 l 570 987 l 704 987 l 704 558 l 810 558 l 1066 987 l 1222 987 l 919 523 l 1249 0 l 1083 0 l 808 453 m 873 1088 l 725 1088 l 725 1224 l 873 1224 l 873 1088 m 555 1088 l 406 1088 l 406 1224 l 555 1224 l 555 1088 z "},"ӝ":{"ha":999,"x_min":14,"x_max":1044,"o":"m 682 318 l 596 318 l 596 0 l 463 0 l 463 318 l 376 318 l 182 0 l 14 0 l 276 389 l 37 734 l 199 734 l 379 431 l 463 431 l 463 734 l 596 734 l 596 431 l 680 431 l 861 734 l 1022 734 l 783 389 l 1044 0 l 876 0 l 682 318 m 733 856 l 585 856 l 585 991 l 733 991 l 733 856 m 414 856 l 266 856 l 266 991 l 414 991 l 414 856 z "},"Ӟ":{"ha":939,"x_min":81,"x_max":846,"o":"m 831 720 q 785 595 831 652 q 657 507 739 538 q 797 419 749 478 q 846 275 846 360 q 738 62 846 139 q 454 -14 629 -14 q 191 58 301 -14 q 85 270 81 131 l 86 274 l 214 274 q 281 143 214 197 q 454 90 348 90 q 644 141 575 90 q 712 273 712 191 q 650 407 712 363 q 465 450 587 450 l 341 450 l 341 556 l 465 556 q 640 601 583 556 q 697 723 697 646 q 636 848 697 799 q 454 897 574 897 q 293 848 359 897 q 228 729 228 800 l 100 729 l 100 733 q 199 927 96 852 q 454 1002 302 1002 q 730 928 629 1002 q 831 720 831 855 m 697 1103 l 549 1103 l 549 1238 l 697 1238 l 697 1103 m 378 1103 l 230 1103 l 230 1238 l 378 1238 l 378 1103 z "},"ӟ":{"ha":748,"x_min":60,"x_max":639,"o":"m 360 429 q 461 455 431 429 q 492 533 492 482 q 457 611 492 579 q 351 643 422 643 q 245 610 288 643 q 203 529 203 576 l 76 529 l 75 533 q 152 687 71 627 q 351 746 233 746 q 553 691 480 746 q 625 533 625 635 q 595 443 625 484 q 512 378 566 401 q 606 312 574 355 q 639 207 639 269 q 560 45 639 104 q 351 -13 481 -13 q 146 45 232 -13 q 63 213 60 103 l 64 217 l 191 217 q 236 127 191 165 q 351 90 281 90 q 464 123 424 90 q 505 207 505 157 q 470 293 505 266 q 360 320 435 320 l 233 320 l 233 429 l 360 429 m 608 869 l 459 869 l 459 1005 l 608 1005 l 608 869 m 289 869 l 140 869 l 140 1005 l 289 1005 l 289 869 z "},"Ӡ":{"ha":810,"x_min":71,"x_max":725,"o":"m 530 879 l 528 882 l 100 882 l 100 987 l 690 987 l 690 906 l 413 573 q 643 487 562 564 q 725 275 725 410 q 632 63 725 140 q 389 -14 539 -14 q 165 58 259 -14 q 75 270 71 131 l 76 274 l 204 274 q 256 143 204 197 q 389 90 307 90 q 537 141 484 90 q 591 273 591 191 q 537 425 591 376 q 374 473 482 473 l 275 473 l 275 578 l 530 879 z "},"ӡ":{"ha":810,"x_min":71,"x_max":725,"o":"m 515 625 l 513 628 l 100 628 l 100 734 l 690 734 l 690 652 l 422 318 q 646 231 567 307 q 725 21 725 154 q 632 -191 725 -113 q 389 -268 538 -268 q 165 -195 260 -268 q 75 16 71 -122 l 76 20 l 204 20 q 256 -110 204 -56 q 389 -163 307 -163 q 537 -112 484 -163 q 591 18 591 -62 q 536 171 591 123 q 373 219 481 219 l 273 219 l 273 323 l 515 625 z "},"Ӣ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 745 1112 l 256 1112 l 256 1211 l 745 1211 l 745 1112 z "},"ӣ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 642 881 l 153 881 l 153 980 l 642 980 l 642 881 z "},"Ӥ":{"ha":991,"x_min":122,"x_max":869,"o":"m 735 987 l 869 987 l 869 0 l 735 0 l 735 753 l 731 754 l 256 0 l 122 0 l 122 987 l 256 987 l 256 235 l 260 234 l 735 987 m 731 1088 l 583 1088 l 583 1224 l 731 1224 l 731 1088 m 412 1088 l 264 1088 l 264 1224 l 412 1224 l 412 1088 z "},"ӥ":{"ha":789,"x_min":97,"x_max":692,"o":"m 558 734 l 692 734 l 692 0 l 558 0 l 558 521 l 554 522 l 230 0 l 97 0 l 97 734 l 230 734 l 230 213 l 234 212 l 558 734 m 628 856 l 479 856 l 479 991 l 628 991 l 628 856 m 309 856 l 161 856 l 161 991 l 309 991 l 309 856 z "},"Ӧ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 707 1103 l 559 1103 l 559 1238 l 707 1238 l 707 1103 m 389 1103 l 240 1103 l 240 1238 l 389 1238 l 389 1103 z "},"ӧ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 629 870 l 480 870 l 480 1006 l 629 1006 l 629 870 m 310 870 l 161 870 l 161 1006 l 310 1006 l 310 870 z "},"Ө":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 z "},"ө":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 z "},"Ӫ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 210 540 l 736 540 l 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 540 m 736 435 l 210 435 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 435 m 722 1085 l 574 1085 l 574 1221 l 722 1221 l 722 1085 m 404 1085 l 255 1085 l 255 1221 l 404 1221 l 404 1085 z "},"ӫ":{"ha":790,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 395 90 q 530 153 481 90 q 584 315 578 216 l 583 318 l 205 318 l 204 315 q 258 153 210 216 q 395 90 307 90 m 393 642 q 260 582 309 642 q 205 427 212 521 l 206 423 l 582 423 l 583 427 q 527 581 576 520 q 393 642 478 642 m 613 889 l 465 889 l 465 1025 l 613 1025 l 613 889 m 294 889 l 146 889 l 146 1025 l 294 1025 l 294 889 z "},"Ӭ":{"ha":939,"x_min":123,"x_max":867,"o":"m 127 671 l 126 675 q 223 908 123 813 q 488 1002 324 1002 q 762 883 656 1002 q 867 582 867 764 l 867 406 q 762 105 867 223 q 488 -14 656 -14 q 222 75 321 -14 q 126 311 123 163 l 127 315 l 256 315 q 316 151 256 211 q 488 91 376 91 q 666 181 600 91 q 733 405 733 271 l 733 460 l 344 460 l 344 565 l 733 565 l 733 582 q 666 807 733 716 q 488 897 600 897 q 316 837 376 897 q 256 671 256 777 l 127 671 m 703 1103 l 555 1103 l 555 1239 l 703 1239 l 703 1103 m 385 1103 l 236 1103 l 236 1239 l 385 1239 l 385 1103 z "},"ӭ":{"ha":747,"x_min":67,"x_max":675,"o":"m 353 642 q 242 601 290 642 q 193 500 193 559 l 73 500 l 71 505 q 154 673 67 599 q 353 747 240 747 q 588 641 502 747 q 675 381 675 535 l 675 353 q 588 92 675 198 q 353 -14 501 -14 q 148 61 229 -14 q 71 248 67 137 l 72 252 l 193 252 q 239 137 193 184 q 353 90 285 90 q 485 157 438 90 q 537 318 532 223 l 535 321 l 274 321 l 274 426 l 534 426 l 535 429 q 482 580 528 518 q 353 642 435 642 m 605 870 l 456 870 l 456 1006 l 605 1006 l 605 870 m 286 870 l 138 870 l 138 1006 l 286 1006 l 286 870 z "},"Ӯ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 686 1112 l 197 1112 l 197 1211 l 686 1211 l 686 1112 z "},"ӯ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 597 881 l 108 881 l 108 980 l 597 980 l 597 881 z "},"Ӱ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 672 1088 l 524 1088 l 524 1224 l 672 1224 l 672 1088 m 353 1088 l 205 1088 l 205 1224 l 353 1224 l 353 1088 z "},"ӱ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 583 856 l 434 856 l 434 991 l 583 991 l 583 856 m 264 856 l 115 856 l 115 991 l 264 991 l 264 856 z "},"Ӳ":{"ha":873,"x_min":58,"x_max":830,"o":"m 399 526 l 448 398 l 452 398 l 675 987 l 830 987 l 485 162 q 398 30 445 73 q 248 -14 351 -14 q 208 -12 231 -14 q 179 -8 185 -10 l 182 93 q 212 91 189 92 q 246 90 235 90 q 315 115 294 90 q 361 193 336 139 l 389 250 l 58 987 l 208 987 l 399 526 m 624 1266 l 779 1266 l 781 1262 l 578 1086 l 462 1086 l 460 1089 l 624 1266 m 403 1266 l 546 1266 l 547 1263 l 385 1086 l 278 1086 l 403 1266 z "},"ӳ":{"ha":699,"x_min":18,"x_max":692,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 534 1034 l 690 1034 l 692 1029 l 488 853 l 372 853 l 371 857 l 534 1034 m 313 1034 l 456 1034 l 458 1030 l 295 853 l 189 853 l 313 1034 z "},"Ӵ":{"ha":956,"x_min":100,"x_max":833,"o":"m 833 987 l 833 0 l 699 0 l 699 409 q 574 380 635 389 q 431 372 514 372 q 184 443 268 372 q 100 675 100 515 l 100 987 l 234 987 l 234 675 q 281 520 234 564 q 431 477 328 477 q 567 487 502 477 q 699 515 632 496 l 699 987 l 833 987 m 701 1088 l 552 1088 l 552 1224 l 701 1224 l 701 1088 m 382 1088 l 233 1088 l 233 1224 l 382 1224 l 382 1088 z "},"ӵ":{"ha":767,"x_min":86,"x_max":670,"o":"m 670 0 l 536 0 l 536 262 q 457 247 498 252 q 370 242 416 242 q 162 312 237 242 q 86 519 86 382 l 86 734 l 220 734 l 220 519 q 258 389 220 431 q 370 348 296 348 q 456 353 415 348 q 536 368 496 358 l 536 734 l 670 734 l 670 0 m 608 856 l 460 856 l 460 991 l 608 991 l 608 856 m 290 856 l 141 856 l 141 991 l 290 991 l 290 856 z "},"Ӷ":{"ha":773,"x_min":122,"x_max":728,"o":"m 728 882 l 256 882 l 256 0 l 122 0 l 122 987 l 728 987 l 728 882 m 354 -243 l 220 -243 l 220 106 l 354 106 l 354 -243 z "},"ӷ":{"ha":576,"x_min":97,"x_max":564,"o":"m 564 628 l 231 628 l 231 0 l 97 0 l 97 734 l 564 734 l 564 628 m 307 -243 l 173 -243 l 173 106 l 307 106 l 307 -243 z "},"Ӹ":{"ha":1208,"x_min":122,"x_max":1087,"o":"m 256 583 l 499 583 q 745 505 658 583 q 833 294 833 427 q 745 81 833 161 q 499 0 657 0 l 122 0 l 122 987 l 256 987 l 256 583 m 256 478 l 256 104 l 499 104 q 649 159 600 104 q 699 296 699 213 q 649 427 699 376 q 499 478 600 478 l 256 478 m 1087 0 l 953 0 l 953 987 l 1087 987 l 1087 0 m 839 1088 l 690 1088 l 690 1224 l 839 1224 l 839 1088 m 520 1088 l 372 1088 l 372 1224 l 520 1224 l 520 1088 z "},"ӹ":{"ha":1097,"x_min":117,"x_max":971,"o":"m 251 454 l 433 454 q 636 392 564 454 q 708 230 708 330 q 635 65 708 130 q 433 0 562 0 l 117 0 l 117 734 l 251 734 l 251 454 m 251 350 l 251 104 l 433 104 q 539 139 505 104 q 574 225 574 174 q 539 313 574 275 q 433 350 505 350 l 251 350 m 971 0 l 838 0 l 838 734 l 971 734 l 971 0 m 779 856 l 631 856 l 631 991 l 779 991 l 779 856 m 460 856 l 312 856 l 312 991 l 460 991 l 460 856 z "},"Ӻ":{"ha":824,"x_min":44,"x_max":779,"o":"m 779 882 l 307 882 l 307 0 l 174 0 l 174 987 l 779 987 l 779 882 m 479 482 l 44 482 l 44 587 l 479 587 l 479 482 m 429 104 l 429 -60 q 370 -235 429 -174 q 208 -296 312 -296 q 170 -293 187 -296 q 133 -284 153 -290 l 142 -176 q 174 -181 152 -179 q 208 -184 195 -184 q 272 -152 249 -184 q 295 -60 295 -120 l 295 104 l 429 104 z "},"ӻ":{"ha":570,"x_min":45,"x_max":630,"o":"m 630 628 l 297 628 l 297 0 l 163 0 l 163 734 l 630 734 l 630 628 m 479 364 l 45 364 l 45 469 l 479 469 l 479 364 m 418 104 l 418 -60 q 359 -235 418 -174 q 197 -296 301 -296 q 159 -293 176 -296 q 122 -284 142 -290 l 132 -176 q 163 -181 141 -179 q 197 -184 184 -184 q 261 -152 238 -184 q 284 -60 284 -120 l 284 104 l 418 104 z "},"Ӽ":{"ha":878,"x_min":45,"x_max":892,"o":"m 441 602 l 671 987 l 833 987 l 519 498 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 365 498 l 52 987 l 212 987 l 441 602 m 892 104 l 892 -60 q 833 -235 892 -174 q 671 -296 775 -296 q 633 -293 650 -296 q 596 -284 616 -290 l 606 -176 q 637 -181 615 -179 q 671 -184 659 -184 q 735 -152 712 -184 q 758 -60 758 -120 l 758 104 l 892 104 z "},"ӽ":{"ha":699,"x_min":31,"x_max":736,"o":"m 346 463 l 502 734 l 658 734 l 420 371 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 276 371 l 38 734 l 192 734 l 346 463 m 736 104 l 736 -60 q 678 -235 736 -174 q 516 -296 620 -296 q 478 -293 495 -296 q 441 -284 460 -290 l 450 -176 q 481 -181 460 -179 q 516 -184 503 -184 q 580 -152 557 -184 q 603 -60 603 -120 l 603 104 l 736 104 z "},"Ӿ":{"ha":878,"x_min":45,"x_max":840,"o":"m 665 452 l 549 452 l 840 0 l 680 0 l 444 392 l 206 0 l 45 0 l 336 452 l 231 452 l 231 557 l 326 557 l 52 987 l 212 987 l 441 602 l 671 987 l 833 987 l 557 557 l 665 557 l 665 452 z "},"ӿ":{"ha":699,"x_min":31,"x_max":665,"o":"m 559 324 l 450 324 l 665 0 l 511 0 l 349 277 l 186 0 l 31 0 l 245 324 l 124 324 l 124 429 l 237 429 l 38 734 l 192 734 l 346 463 l 502 734 l 658 734 l 458 429 l 559 429 l 559 324 z "},"Ԁ":{"ha":873,"x_min":62,"x_max":772,"o":"m 638 597 l 638 987 l 772 987 l 772 0 l 395 0 q 149 83 237 0 q 62 301 62 167 q 149 516 62 435 q 395 597 236 597 l 638 597 m 638 104 l 638 492 l 395 492 q 245 437 294 492 q 195 302 195 383 q 245 162 195 219 q 395 104 294 104 l 638 104 z "},"ԁ":{"ha":789,"x_min":66,"x_max":687,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 1058 l 687 1058 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"Ԃ":{"ha":1153,"x_min":62,"x_max":1116,"o":"m 395 0 q 149 83 237 0 q 62 301 62 167 q 149 516 62 435 q 395 597 236 597 l 638 597 l 638 987 l 772 987 l 772 104 l 828 104 q 939 153 900 105 q 981 286 979 201 q 971 395 982 338 q 941 505 961 452 l 1069 505 q 1104 386 1092 437 q 1115 286 1116 335 q 1030 74 1112 148 q 828 0 947 0 l 395 0 m 638 104 l 638 492 l 395 492 q 245 437 294 492 q 195 302 195 383 q 245 162 195 219 q 395 104 294 104 l 638 104 z "},"ԃ":{"ha":1197,"x_min":66,"x_max":1120,"o":"m 66 353 q 142 639 66 530 q 354 747 218 747 q 462 726 414 747 q 545 665 509 705 l 545 1058 l 678 1058 l 678 234 q 708 130 677 170 q 791 90 739 90 q 932 162 882 91 q 985 359 983 233 q 976 493 986 425 q 945 631 965 562 l 1074 632 q 1108 485 1096 549 q 1119 359 1120 421 q 1023 82 1116 178 q 791 -14 929 -14 q 655 13 710 -16 q 572 105 600 42 q 481 16 536 46 q 353 -14 427 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 m 545 554 q 479 616 518 593 q 388 639 441 639 q 246 559 292 639 q 200 353 200 480 l 200 339 q 245 161 200 228 q 387 94 291 94 q 483 119 444 94 q 549 189 522 144 q 546 210 547 199 q 545 234 545 222 l 545 554 z "},"Ԅ":{"ha":1101,"x_min":37,"x_max":1011,"o":"m 477 251 q 430 380 477 331 q 305 429 384 429 l 176 429 l 176 534 l 268 534 q 430 577 380 534 q 480 709 480 620 q 432 837 480 791 q 280 882 383 882 l 37 882 l 37 987 l 280 987 q 528 916 442 987 q 614 707 614 844 q 574 575 614 631 q 459 486 534 519 q 575 399 539 458 q 610 252 610 339 l 610 206 q 636 122 610 155 q 709 90 663 90 q 831 163 787 91 q 877 359 874 235 q 867 494 878 425 q 836 632 857 563 l 965 632 q 1000 480 988 541 q 1010 359 1011 420 q 922 82 1008 179 q 709 -14 836 -14 q 541 37 600 -16 q 477 206 482 90 l 477 251 z "},"ԅ":{"ha":898,"x_min":33,"x_max":854,"o":"m 507 144 q 520 101 507 116 q 565 86 533 86 q 676 139 637 87 q 719 284 716 191 q 709 391 720 336 q 677 500 698 447 l 806 500 q 842 382 829 431 q 852 284 854 332 q 767 60 849 138 q 565 -18 685 -18 q 425 19 471 -20 q 374 145 378 58 l 374 196 q 338 271 374 244 q 235 298 302 298 l 92 298 l 91 402 l 216 402 q 335 430 296 402 q 373 511 373 458 q 334 598 373 566 q 215 630 296 630 l 37 630 l 33 734 l 215 734 q 431 676 355 734 q 508 515 508 618 q 475 422 508 460 q 380 359 443 383 q 477 298 447 340 q 507 197 507 257 l 507 144 z "},"Ԇ":{"ha":745,"x_min":54,"x_max":663,"o":"m 663 31 l 561 -176 l 481 -176 l 520 0 l 479 0 q 444 68 451 24 q 437 161 437 113 l 437 250 q 390 379 437 330 q 265 429 344 429 l 116 429 l 116 533 l 226 533 q 390 576 339 533 q 440 707 440 619 q 392 836 440 790 q 240 882 343 882 l 54 882 l 54 987 l 240 987 q 488 916 402 987 q 573 706 573 844 q 533 574 573 630 q 418 485 494 517 q 535 398 499 458 q 570 252 570 338 l 570 159 l 570 149 l 663 149 l 663 31 z "},"ԇ":{"ha":701,"x_min":83,"x_max":652,"o":"m 652 18 l 550 -190 l 471 -190 l 513 0 l 470 0 q 438 49 445 16 q 431 117 431 83 l 431 182 q 395 267 431 236 q 293 298 359 298 l 131 298 l 131 402 l 273 402 q 393 430 355 402 q 431 511 431 458 q 392 598 431 566 q 273 630 354 630 l 83 630 l 83 734 l 273 734 q 489 676 412 734 q 565 515 565 619 q 532 421 565 460 q 435 357 499 382 q 535 293 505 338 q 565 182 565 248 l 565 135 l 652 135 l 652 18 z "},"Ԉ":{"ha":1347,"x_min":47,"x_max":1295,"o":"m 719 882 l 399 882 l 399 521 q 326 125 399 251 q 83 0 252 0 l 47 0 l 47 104 l 75 104 q 221 202 176 104 q 266 521 266 300 l 266 987 l 852 987 l 852 234 q 883 130 852 170 q 966 90 913 90 q 1107 162 1057 91 q 1160 359 1157 233 q 1150 493 1160 425 q 1119 631 1139 562 l 1248 632 q 1282 485 1270 549 q 1293 359 1295 421 q 1197 82 1291 178 q 966 -14 1103 -14 q 787 44 850 -16 q 719 234 724 104 l 719 882 z "},"ԉ":{"ha":1122,"x_min":44,"x_max":1086,"o":"m 551 628 l 343 628 l 343 422 q 283 103 343 207 q 83 0 222 0 l 44 0 l 47 114 l 75 115 q 178 186 147 115 q 210 422 210 258 l 210 734 l 685 734 l 685 234 q 715 130 684 170 q 797 90 745 90 q 909 154 869 91 q 951 329 948 218 q 941 456 951 391 q 909 587 930 522 l 1039 587 q 1074 447 1061 506 q 1084 329 1086 387 q 999 75 1082 163 q 797 -14 917 -14 q 620 44 683 -16 q 551 234 557 104 l 551 628 z "},"Ԋ":{"ha":1391,"x_min":115,"x_max":1301,"o":"m 859 987 l 859 234 q 888 130 858 170 q 971 90 919 90 q 1113 162 1063 91 q 1166 359 1163 233 q 1156 493 1166 425 q 1124 631 1145 562 l 1253 632 q 1289 485 1276 548 q 1299 359 1301 421 q 1202 82 1296 178 q 971 -14 1109 -14 q 793 44 857 -16 q 725 234 730 104 l 725 436 l 248 436 l 248 0 l 115 0 l 115 987 l 248 987 l 248 541 l 725 541 l 725 987 l 859 987 z "},"ԋ":{"ha":1152,"x_min":97,"x_max":1099,"o":"m 564 312 l 231 312 l 231 0 l 97 0 l 97 734 l 231 734 l 231 416 l 564 416 l 564 734 l 698 734 l 698 234 q 728 130 697 170 q 810 90 759 90 q 922 154 882 91 q 964 329 962 218 q 955 456 965 391 q 924 587 944 522 l 1052 587 q 1087 446 1075 504 q 1098 329 1099 387 q 1013 75 1095 163 q 810 -14 931 -14 q 633 44 696 -16 q 564 234 570 104 l 564 312 z "},"Ԍ":{"ha":862,"x_min":80,"x_max":802,"o":"m 473 -14 q 190 104 299 -14 q 80 405 80 223 l 80 583 q 190 883 80 764 q 473 1002 299 1002 q 610 986 549 1002 q 716 942 670 971 l 673 850 q 579 885 628 873 q 473 897 530 897 q 284 806 354 897 q 214 584 214 716 l 214 405 q 284 180 214 271 q 473 90 354 90 q 615 143 565 91 q 668 288 665 195 q 660 406 669 345 q 634 533 650 467 l 763 533 q 797 370 791 399 q 802 288 802 341 q 705 64 799 142 q 473 -14 611 -14 z "},"ԍ":{"ha":715,"x_min":66,"x_max":655,"o":"m 402 90 q 493 119 467 91 q 522 204 519 147 q 518 285 522 243 q 509 364 515 326 l 637 364 q 650 279 646 318 q 655 204 655 240 q 583 41 652 97 q 402 -14 514 -14 q 156 92 246 -14 q 66 353 66 199 l 66 381 q 153 641 66 534 q 388 747 239 747 q 500 735 452 747 q 580 703 548 724 l 549 603 q 475 632 517 621 q 388 642 434 642 q 248 564 295 642 q 200 381 200 486 l 200 353 q 251 168 200 245 q 402 90 301 90 z "},"Ԏ":{"ha":986,"x_min":24,"x_max":920,"o":"m 345 882 l 24 882 l 24 987 l 806 987 l 806 882 l 478 882 l 478 234 q 508 130 477 170 q 591 90 539 90 q 733 162 682 91 q 786 359 783 233 q 776 492 787 424 q 744 631 765 561 l 873 632 q 908 485 897 548 q 919 359 920 421 q 823 82 916 178 q 591 -14 729 -14 q 413 44 476 -16 q 345 234 350 104 l 345 882 z "},"ԏ":{"ha":890,"x_min":47,"x_max":823,"o":"m 288 630 l 47 630 l 47 734 l 663 734 l 663 630 l 421 630 l 421 234 q 451 130 420 170 q 534 90 481 90 q 645 143 606 91 q 688 289 685 196 q 677 397 688 340 q 647 508 667 454 l 775 508 q 810 388 798 438 q 821 289 823 338 q 736 64 819 142 q 534 -14 654 -14 q 356 44 419 -16 q 288 234 293 104 l 288 630 z "},"Ԑ":{"ha":939,"x_min":106,"x_max":869,"o":"m 486 450 q 301 407 363 450 q 239 273 239 363 q 308 141 239 191 q 497 90 376 90 q 670 143 603 90 q 738 274 738 197 l 865 274 l 867 270 q 760 58 869 131 q 497 -14 650 -14 q 214 63 322 -14 q 106 275 106 140 q 154 419 106 361 q 294 507 202 478 q 166 595 212 539 q 121 720 121 652 q 222 928 121 855 q 497 1002 323 1002 q 752 927 648 1002 q 852 733 856 852 l 852 729 l 724 729 q 658 848 724 800 q 497 897 592 897 q 315 848 376 897 q 254 723 254 799 q 311 601 254 646 q 486 556 368 556 l 611 556 l 611 450 l 486 450 z "},"ԑ":{"ha":748,"x_min":66,"x_max":679,"o":"m 365 323 q 242 295 283 323 q 201 207 201 266 q 247 123 201 157 q 374 90 294 90 q 499 127 450 90 q 548 217 548 165 l 674 217 l 675 213 q 589 45 679 103 q 374 -13 498 -13 q 152 46 237 -13 q 66 207 66 104 q 101 312 66 269 q 203 377 136 355 q 112 442 144 401 q 79 533 79 484 q 158 690 79 635 q 374 746 237 746 q 582 687 498 746 q 663 533 667 627 l 662 529 l 536 529 q 489 610 536 576 q 374 643 441 643 q 254 611 295 643 q 213 533 213 578 q 250 454 213 483 q 365 425 287 425 l 507 425 l 507 323 l 365 323 z "},"Ԓ":{"ha":984,"x_min":33,"x_max":995,"o":"m 862 987 l 862 0 l 728 0 l 728 882 l 372 882 l 371 522 q 301 125 371 251 q 69 0 231 0 l 33 0 l 33 104 l 61 104 q 196 201 155 104 q 238 522 237 298 l 239 987 l 862 987 m 995 104 l 995 -60 q 937 -235 995 -174 q 774 -296 878 -296 q 736 -293 753 -296 q 699 -284 719 -290 l 709 -176 q 740 -181 718 -179 q 774 -184 762 -184 q 838 -152 815 -184 q 861 -60 861 -120 l 861 104 l 995 104 z "},"ԓ":{"ha":768,"x_min":18,"x_max":825,"o":"m 692 734 l 692 0 l 558 0 l 558 628 l 310 628 l 310 420 q 252 102 310 203 q 56 0 195 0 l 18 0 l 20 114 l 48 115 q 148 184 119 115 q 176 420 176 254 l 176 734 l 692 734 m 825 104 l 825 -60 q 766 -235 825 -174 q 604 -296 708 -296 q 566 -293 583 -296 q 529 -284 549 -290 l 538 -176 q 570 -181 548 -179 q 604 -184 591 -184 q 668 -152 645 -184 q 691 -60 691 -120 l 691 104 l 825 104 z "},"Ḁ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 355 -159 q 386 -86 355 -115 q 461 -57 416 -57 q 534 -86 504 -57 q 565 -159 565 -115 q 535 -229 565 -201 q 461 -257 505 -257 q 386 -229 416 -257 q 355 -159 355 -201 m 415 -159 q 429 -189 415 -176 q 461 -202 442 -202 q 492 -190 479 -202 q 505 -159 505 -177 q 492 -125 505 -138 q 461 -112 479 -112 q 429 -125 442 -112 q 415 -159 415 -139 z "},"ḁ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 229 -159 q 260 -86 229 -115 q 336 -57 291 -57 q 409 -86 378 -57 q 439 -159 439 -115 q 409 -229 439 -201 q 336 -257 379 -257 q 260 -229 291 -257 q 229 -159 229 -201 m 290 -159 q 303 -189 290 -176 q 336 -202 317 -202 q 366 -190 353 -202 q 379 -159 379 -177 q 366 -125 379 -138 q 336 -112 353 -112 q 303 -125 317 -112 q 290 -159 290 -139 z "},"Ḿ":{"ha":1220,"x_min":122,"x_max":1097,"o":"m 293 987 l 608 185 l 612 185 l 926 987 l 1097 987 l 1097 0 l 964 0 l 964 391 l 977 792 l 974 793 l 654 0 l 565 0 l 246 791 l 243 790 l 256 391 l 256 0 l 122 0 l 122 987 l 293 987 m 674 1236 l 830 1236 l 831 1232 l 648 1055 l 549 1055 l 674 1236 z "},"ḿ":{"ha":1214,"x_min":97,"x_max":1117,"o":"m 216 734 l 226 637 q 317 719 262 690 q 446 747 372 747 q 571 713 519 747 q 650 612 624 680 q 742 711 685 674 q 875 747 799 747 q 1052 670 987 747 q 1117 439 1117 593 l 1117 0 l 983 0 l 983 440 q 946 594 983 549 q 835 639 909 639 q 724 591 766 639 q 674 471 682 544 l 674 466 l 674 0 l 540 0 l 540 440 q 502 591 540 543 q 391 639 463 639 q 291 614 330 639 q 231 543 252 589 l 231 0 l 97 0 l 97 734 l 216 734 m 686 1017 l 842 1017 l 843 1013 l 660 837 l 560 837 l 686 1017 z "},"Ẁ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 665 1058 l 558 1058 l 389 1234 l 391 1238 l 547 1238 l 665 1058 z "},"ẁ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 581 825 l 473 825 l 304 1002 l 306 1006 l 462 1006 l 581 825 z "},"Ẃ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 673 1236 l 829 1236 l 831 1232 l 648 1055 l 548 1055 l 673 1236 z "},"ẃ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 589 1003 l 745 1003 l 746 999 l 563 823 l 463 823 l 589 1003 z "},"Ẅ":{"ha":1227,"x_min":37,"x_max":1182,"o":"m 320 342 l 338 218 l 342 218 l 369 342 l 550 987 l 668 987 l 850 342 l 878 215 l 882 215 l 901 342 l 1048 987 l 1182 987 l 944 0 l 825 0 l 630 685 l 612 774 l 608 774 l 591 685 l 393 0 l 274 0 l 37 987 l 170 987 l 320 342 m 843 1088 l 694 1088 l 694 1224 l 843 1224 l 843 1088 m 524 1088 l 376 1088 l 376 1224 l 524 1224 l 524 1088 z "},"ẅ":{"ha":1051,"x_min":31,"x_max":1017,"o":"m 285 267 l 300 178 l 304 178 l 323 267 l 470 734 l 577 734 l 724 267 l 745 168 l 749 168 l 769 267 l 884 734 l 1017 734 l 804 0 l 696 0 l 555 447 l 524 572 l 520 571 l 491 447 l 351 0 l 243 0 l 31 734 l 163 734 l 285 267 m 758 856 l 610 856 l 610 991 l 758 991 l 758 856 m 439 856 l 291 856 l 291 991 l 439 991 l 439 856 z "},"Ạ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ạ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ả":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 406 1072 l 406 1176 q 479 1190 457 1179 q 501 1225 501 1200 q 472 1265 501 1252 q 393 1278 442 1278 l 397 1351 q 551 1316 497 1351 q 604 1223 604 1281 q 575 1151 604 1175 q 502 1120 546 1126 l 501 1072 l 406 1072 z "},"ả":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 332 853 l 331 958 q 405 971 382 960 q 427 1007 427 982 q 397 1046 427 1034 q 318 1059 368 1059 l 323 1133 q 476 1098 422 1133 q 530 1004 530 1063 q 500 932 530 957 q 427 901 471 907 l 427 853 l 332 853 z "},"Ấ":{"ha":899,"x_min":14,"x_max":892,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 399 1261 l 506 1261 l 701 1087 l 700 1083 l 566 1083 l 452 1190 l 339 1083 l 204 1083 l 203 1087 l 399 1261 m 749 1379 l 892 1379 l 754 1202 l 654 1202 l 749 1379 z "},"ấ":{"ha":764,"x_min":72,"x_max":812,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 319 1043 l 426 1043 l 621 869 l 620 865 l 486 865 l 372 971 l 259 865 l 124 865 l 123 869 l 319 1043 m 669 1160 l 812 1160 l 674 984 l 574 984 l 669 1160 z "},"Ầ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 706 1076 l 705 1072 l 570 1072 l 456 1178 l 343 1072 l 209 1072 l 208 1076 l 403 1250 l 510 1250 l 706 1076 m 254 1191 l 155 1191 l 17 1367 l 159 1367 l 254 1191 z "},"ầ":{"ha":764,"x_min":-63,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 626 857 l 625 853 l 490 853 l 376 960 l 263 853 l 129 853 l 127 857 l 323 1031 l 430 1031 l 626 857 m 174 972 l 75 972 l -63 1149 l 79 1149 l 174 972 z "},"Ẩ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 701 1062 l 699 1058 l 584 1058 l 451 1184 l 318 1058 l 203 1058 l 202 1062 l 384 1236 l 518 1236 l 701 1062 m 684 1152 l 683 1242 q 746 1253 728 1244 q 765 1284 765 1262 q 740 1318 765 1307 q 672 1329 714 1329 l 676 1392 q 809 1363 762 1392 q 855 1282 855 1333 q 830 1219 855 1241 q 766 1193 804 1198 l 765 1152 l 684 1152 z "},"ẩ":{"ha":764,"x_min":72,"x_max":775,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 621 845 l 619 841 l 504 841 l 371 967 l 238 841 l 123 841 l 122 845 l 304 1019 l 438 1019 l 621 845 m 604 935 l 603 1025 q 666 1036 648 1027 q 685 1067 685 1045 q 660 1101 685 1090 q 592 1112 634 1112 l 596 1175 q 729 1146 682 1175 q 775 1065 775 1116 q 750 1002 775 1024 q 686 976 724 981 l 685 935 l 604 935 z "},"Ẫ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 699 1063 l 698 1059 l 583 1059 l 450 1179 l 317 1059 l 202 1059 l 201 1063 l 397 1238 l 503 1238 l 699 1063 m 627 1407 q 594 1324 627 1360 q 516 1288 561 1288 q 442 1312 480 1288 q 378 1336 404 1336 q 339 1316 357 1336 q 322 1273 322 1296 l 268 1286 q 300 1370 268 1333 q 378 1408 333 1408 q 448 1384 406 1408 q 516 1361 490 1361 q 555 1380 536 1361 q 573 1423 573 1399 l 627 1407 z "},"ẫ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 623 846 l 622 842 l 507 842 l 374 962 l 241 842 l 126 842 l 125 846 l 321 1021 l 427 1021 l 623 846 m 551 1190 q 518 1107 551 1143 q 440 1071 485 1071 q 366 1095 404 1071 q 302 1119 328 1119 q 263 1099 281 1119 q 246 1056 246 1079 l 192 1069 q 224 1153 192 1116 q 302 1191 257 1191 q 372 1167 330 1191 q 440 1144 414 1144 q 479 1163 460 1144 q 497 1206 497 1182 l 551 1190 z "},"Ậ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 658 1103 l 658 1086 l 549 1086 l 449 1186 l 350 1086 l 241 1086 l 241 1103 l 408 1264 l 490 1264 l 658 1103 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ậ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 585 884 l 585 867 l 476 867 l 376 968 l 277 867 l 169 867 l 169 885 l 336 1046 l 417 1046 l 585 884 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ắ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 650 1218 l 652 1214 q 600 1115 656 1154 q 450 1076 545 1076 q 300 1115 355 1076 q 249 1214 244 1154 l 250 1218 l 353 1218 q 376 1162 353 1183 q 450 1141 399 1141 q 524 1162 500 1141 q 547 1218 547 1183 l 650 1218 m 484 1367 l 589 1367 l 591 1363 l 483 1233 l 411 1233 l 484 1367 z "},"ắ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 573 1000 l 574 996 q 523 897 578 935 q 373 858 467 858 q 222 897 278 858 q 172 996 167 935 l 173 1000 l 275 1000 q 299 944 275 965 q 373 922 322 922 q 446 944 422 922 q 470 1000 470 965 l 573 1000 m 407 1148 l 512 1148 l 513 1145 l 406 1015 l 334 1015 l 407 1148 z "},"Ằ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 495 1233 l 414 1233 l 279 1362 l 280 1367 l 401 1367 l 495 1233 m 650 1218 l 652 1214 q 600 1115 656 1154 q 450 1076 545 1076 q 300 1115 355 1076 q 249 1214 244 1154 l 250 1218 l 353 1218 q 376 1162 353 1183 q 450 1141 399 1141 q 524 1162 500 1141 q 547 1218 547 1183 l 650 1218 z "},"ằ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 418 1015 l 337 1015 l 201 1144 l 203 1148 l 324 1148 l 418 1015 m 573 1000 l 574 996 q 523 897 578 935 q 373 858 467 858 q 222 897 278 858 q 172 996 167 935 l 173 1000 l 275 1000 q 299 944 275 965 q 373 922 322 922 q 446 944 422 922 q 470 1000 470 965 l 573 1000 z "},"Ẳ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 643 1213 l 644 1209 q 595 1111 648 1149 q 450 1072 541 1072 q 305 1111 359 1072 q 256 1209 252 1149 l 258 1213 l 355 1213 q 378 1158 355 1179 q 450 1136 401 1136 q 522 1157 498 1136 q 545 1213 545 1179 l 643 1213 m 417 1251 l 416 1337 q 486 1347 466 1339 q 503 1372 507 1354 l 503 1376 q 479 1404 507 1395 q 404 1413 452 1413 l 409 1470 q 557 1443 505 1470 q 608 1372 608 1417 q 580 1317 608 1336 q 509 1294 552 1299 l 509 1251 l 417 1251 z "},"ẳ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 561 992 l 562 988 q 513 889 566 928 q 368 851 459 851 q 223 889 277 851 q 174 988 170 928 l 176 992 l 273 992 q 296 937 273 958 q 368 915 319 915 q 439 936 416 915 q 463 992 463 958 l 561 992 m 335 1030 l 334 1116 q 404 1125 384 1118 q 421 1151 425 1133 l 421 1155 q 397 1183 425 1174 q 322 1192 370 1192 l 327 1249 q 475 1222 423 1249 q 526 1151 526 1196 q 498 1096 526 1115 q 427 1073 470 1078 l 427 1030 l 335 1030 z "},"Ẵ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 646 1216 l 647 1212 q 597 1111 651 1150 q 450 1072 543 1072 q 303 1111 357 1072 q 253 1212 248 1150 l 254 1216 l 355 1216 q 377 1159 355 1181 q 450 1137 400 1137 q 522 1159 498 1137 q 545 1216 545 1181 l 646 1216 m 646 1398 q 613 1311 646 1347 q 533 1274 581 1274 q 452 1300 494 1274 q 382 1326 410 1326 q 343 1308 359 1326 q 328 1265 328 1291 l 269 1280 q 300 1368 269 1330 q 382 1405 332 1405 q 458 1379 412 1405 q 533 1354 504 1354 q 571 1371 555 1354 q 587 1414 587 1388 l 646 1398 z "},"ẵ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 567 998 l 568 994 q 518 892 572 932 q 372 853 464 853 q 224 892 279 853 q 174 994 170 932 l 176 998 l 276 998 q 299 940 276 962 q 372 918 321 918 q 443 940 420 918 q 467 998 467 962 l 567 998 m 567 1179 q 535 1092 567 1128 q 454 1056 503 1056 q 373 1082 415 1056 q 303 1107 331 1107 q 265 1090 280 1107 q 250 1047 250 1072 l 190 1061 q 222 1149 190 1112 q 303 1187 254 1187 q 379 1161 334 1187 q 454 1135 425 1135 q 492 1153 476 1135 q 509 1196 509 1170 l 567 1179 z "},"Ặ":{"ha":899,"x_min":14,"x_max":886,"o":"m 657 254 l 243 254 l 150 0 l 14 0 l 395 987 l 510 987 l 886 0 l 749 0 l 657 254 m 285 366 l 617 366 l 454 816 l 450 816 l 285 366 m 649 1268 l 650 1264 q 598 1136 653 1185 q 450 1086 543 1086 q 302 1136 357 1086 q 250 1264 247 1185 l 252 1268 l 354 1268 q 377 1196 354 1223 q 450 1168 400 1168 q 523 1196 499 1168 q 547 1268 547 1224 l 649 1268 m 524 -229 l 376 -229 l 376 -93 l 524 -93 l 524 -229 z "},"ặ":{"ha":764,"x_min":72,"x_max":686,"o":"m 548 0 q 537 59 541 33 q 533 110 534 84 q 436 21 496 57 q 307 -14 376 -14 q 132 44 192 -14 q 72 206 72 103 q 157 371 72 311 q 388 430 242 430 l 533 430 l 533 503 q 494 605 533 567 q 382 642 454 642 q 278 609 318 642 q 237 530 237 576 l 110 530 l 109 534 q 184 681 104 614 q 390 747 264 747 q 591 684 515 747 q 667 501 667 621 l 667 148 q 671 72 667 109 q 686 0 675 35 l 548 0 m 326 98 q 457 135 399 98 q 533 220 515 172 l 533 340 l 383 340 q 254 299 302 340 q 205 203 205 258 q 236 126 205 155 q 326 98 267 98 m 576 1050 l 577 1046 q 525 918 580 967 q 377 868 470 868 q 229 918 283 868 q 177 1046 174 967 l 178 1050 l 281 1050 q 304 977 281 1005 q 377 949 327 949 q 450 978 426 949 q 473 1050 473 1006 l 576 1050 m 418 -229 l 270 -229 l 270 -93 l 418 -93 l 418 -229 z "},"Ẹ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 500 -222 l 352 -222 l 352 -86 l 500 -86 l 500 -222 z "},"ẹ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 463 -229 l 315 -229 l 315 -93 l 463 -93 l 463 -229 z "},"Ẻ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 381 1072 l 380 1176 q 454 1190 432 1179 q 476 1225 476 1200 q 447 1265 476 1252 q 368 1278 417 1278 l 372 1351 q 526 1316 472 1351 q 579 1223 579 1281 q 550 1151 579 1175 q 477 1120 521 1126 l 476 1072 l 381 1072 z "},"ẻ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 334 854 l 333 958 q 407 972 385 961 q 429 1008 429 983 q 399 1047 429 1034 q 320 1060 370 1060 l 325 1133 q 478 1098 425 1133 q 532 1005 532 1063 q 503 933 532 958 q 429 902 473 908 l 429 854 l 334 854 z "},"Ẽ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 661 1251 q 621 1143 661 1187 q 519 1099 581 1099 q 419 1130 471 1099 q 332 1162 367 1162 q 283 1140 303 1162 q 264 1086 264 1118 l 191 1104 q 231 1214 191 1167 q 332 1260 271 1260 q 427 1228 370 1260 q 519 1196 484 1196 q 568 1218 547 1196 q 588 1272 588 1240 l 661 1251 z "},"ẽ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 614 1033 q 573 925 614 969 q 472 881 533 881 q 372 912 424 881 q 285 944 319 944 q 236 922 256 944 q 216 869 216 900 l 143 886 q 183 996 143 949 q 285 1042 223 1042 q 380 1010 323 1042 q 472 979 437 979 q 520 1001 500 979 q 541 1055 541 1023 l 614 1033 z "},"Ế":{"ha":812,"x_min":122,"x_max":862,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 368 1261 l 475 1261 l 671 1087 l 669 1083 l 535 1083 l 422 1190 l 309 1083 l 174 1083 l 172 1087 l 368 1261 m 719 1379 l 862 1379 l 724 1202 l 624 1202 l 719 1379 z "},"ế":{"ha":734,"x_min":67,"x_max":814,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 321 1044 l 428 1044 l 623 869 l 622 865 l 488 865 l 374 972 l 261 865 l 126 865 l 125 869 l 321 1044 m 671 1161 l 814 1161 l 676 985 l 576 985 l 671 1161 z "},"Ề":{"ha":812,"x_min":-14,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 675 1076 l 674 1072 l 539 1072 l 426 1178 l 313 1072 l 178 1072 l 177 1076 l 372 1250 l 479 1250 l 675 1076 m 224 1191 l 124 1191 l -14 1367 l 129 1367 l 224 1191 z "},"ề":{"ha":734,"x_min":-61,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 628 858 l 627 854 l 492 854 l 378 960 l 265 854 l 131 854 l 130 858 l 325 1032 l 432 1032 l 628 858 m 176 973 l 77 973 l -61 1149 l 81 1149 l 176 973 z "},"Ể":{"ha":812,"x_min":122,"x_max":825,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 670 1062 l 669 1058 l 553 1058 l 420 1184 l 288 1058 l 173 1058 l 172 1062 l 353 1236 l 488 1236 l 670 1062 m 653 1152 l 652 1242 q 716 1253 697 1244 q 734 1284 734 1262 q 709 1318 734 1307 q 642 1329 684 1329 l 646 1392 q 778 1363 732 1392 q 825 1282 825 1333 q 799 1219 825 1241 q 735 1193 774 1198 l 734 1152 l 653 1152 z "},"ể":{"ha":734,"x_min":67,"x_max":777,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 623 846 l 621 842 l 506 842 l 373 968 l 240 842 l 125 842 l 124 846 l 306 1020 l 440 1020 l 623 846 m 606 935 l 605 1025 q 668 1037 650 1028 q 687 1067 687 1046 q 662 1101 687 1090 q 594 1112 636 1112 l 598 1176 q 731 1146 684 1176 q 777 1066 777 1117 q 752 1003 777 1025 q 688 977 726 981 l 687 935 l 606 935 z "},"Ễ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 673 1063 l 671 1059 l 556 1059 l 423 1179 l 290 1059 l 176 1059 l 174 1063 l 370 1238 l 477 1238 l 673 1063 m 600 1407 q 567 1324 600 1360 q 490 1288 534 1288 q 415 1312 454 1288 q 351 1336 377 1336 q 313 1316 330 1336 q 296 1273 296 1296 l 241 1286 q 274 1370 241 1333 q 351 1408 307 1408 q 421 1384 380 1408 q 490 1361 463 1361 q 528 1380 510 1361 q 547 1423 547 1399 l 600 1407 z "},"ễ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 625 847 l 624 843 l 509 843 l 376 962 l 243 843 l 128 843 l 127 847 l 323 1021 l 429 1021 l 625 847 m 553 1191 q 520 1108 553 1144 q 442 1072 487 1072 q 368 1096 406 1072 q 304 1120 330 1120 q 266 1100 283 1120 q 248 1057 248 1080 l 194 1069 q 227 1154 194 1116 q 304 1192 259 1192 q 374 1168 332 1192 q 442 1145 416 1145 q 481 1164 463 1145 q 499 1206 499 1183 l 553 1191 z "},"Ệ":{"ha":812,"x_min":122,"x_max":777,"o":"m 708 458 l 256 458 l 256 104 l 777 104 l 777 0 l 122 0 l 122 987 l 770 987 l 770 882 l 256 882 l 256 563 l 708 563 l 708 458 m 635 1103 l 635 1086 l 526 1086 l 426 1186 l 327 1086 l 218 1086 l 218 1103 l 385 1264 l 467 1264 l 635 1103 m 500 -222 l 352 -222 l 352 -86 l 500 -86 l 500 -222 z "},"ệ":{"ha":734,"x_min":67,"x_max":682,"o":"m 401 -14 q 158 88 248 -14 q 67 353 67 190 l 67 382 q 160 643 67 539 q 381 747 254 747 q 606 658 530 747 q 682 419 682 568 l 682 336 l 205 336 l 203 332 q 257 158 205 227 q 401 90 309 90 q 520 110 469 90 q 609 163 572 129 l 661 76 q 557 12 622 37 q 401 -14 493 -14 m 381 642 q 267 586 314 642 q 208 444 219 529 l 210 440 l 548 440 l 548 458 q 506 589 548 536 q 381 642 464 642 m 587 885 l 587 868 l 478 868 l 378 968 l 279 868 l 171 868 l 171 886 l 338 1046 l 419 1046 l 587 885 m 463 -229 l 315 -229 l 315 -93 l 463 -93 l 463 -229 z "},"Ỉ":{"ha":393,"x_min":129,"x_max":349,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 151 1072 l 151 1176 q 224 1190 202 1179 q 246 1225 246 1200 q 217 1265 246 1252 q 138 1278 187 1278 l 142 1351 q 296 1316 242 1351 q 349 1223 349 1281 q 320 1151 349 1175 q 247 1120 291 1126 l 246 1072 l 151 1072 z "},"ỉ":{"ha":349,"x_min":104,"x_max":324,"o":"m 237 0 l 104 0 l 104 734 l 237 734 l 237 0 m 126 840 l 125 945 q 199 958 177 947 q 221 994 221 969 q 192 1034 221 1021 q 113 1046 162 1046 l 117 1120 q 271 1085 217 1120 q 324 991 324 1050 q 295 919 324 944 q 222 888 266 895 l 221 840 l 126 840 z "},"Ị":{"ha":393,"x_min":122,"x_max":270,"o":"m 263 0 l 129 0 l 129 987 l 263 987 l 263 0 m 270 -222 l 122 -222 l 122 -87 l 270 -87 l 270 -222 z "},"ị":{"ha":350,"x_min":101,"x_max":249,"o":"m 241 0 l 108 0 l 108 734 l 241 734 l 241 0 m 241 922 l 108 922 l 108 1058 l 241 1058 l 241 922 m 249 -222 l 101 -222 l 101 -86 l 249 -86 l 249 -222 z "},"Ọ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 547 -233 l 399 -233 l 399 -98 l 547 -98 l 547 -233 z "},"ọ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ỏ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 429 1086 l 428 1190 q 502 1204 479 1193 q 524 1240 524 1215 q 494 1279 524 1266 q 415 1292 465 1292 l 420 1365 q 573 1330 519 1365 q 627 1237 627 1295 q 597 1165 627 1190 q 524 1134 568 1140 l 524 1086 l 429 1086 z "},"ỏ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 350 853 l 349 958 q 423 971 401 960 q 445 1007 445 982 q 415 1046 445 1034 q 336 1059 386 1059 l 341 1133 q 494 1098 441 1133 q 548 1004 548 1063 q 519 932 548 957 q 446 901 490 907 l 445 853 l 350 853 z "},"Ố":{"ha":947,"x_min":77,"x_max":909,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 416 1276 l 523 1276 l 718 1101 l 717 1097 l 583 1097 l 469 1204 l 356 1097 l 221 1097 l 220 1101 l 416 1276 m 766 1393 l 909 1393 l 771 1217 l 671 1217 l 766 1393 z "},"ố":{"ha":789,"x_min":66,"x_max":831,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 337 1043 l 444 1043 l 640 869 l 638 865 l 504 865 l 391 971 l 277 865 l 142 865 l 141 869 l 337 1043 m 688 1160 l 831 1160 l 692 984 l 593 984 l 688 1160 z "},"Ồ":{"ha":947,"x_min":34,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 723 1090 l 722 1086 l 587 1086 l 473 1192 l 360 1086 l 226 1086 l 224 1090 l 420 1264 l 527 1264 l 723 1090 m 271 1205 l 172 1205 l 34 1381 l 176 1381 l 271 1205 z "},"ồ":{"ha":789,"x_min":-45,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 644 857 l 643 853 l 508 853 l 395 960 l 281 853 l 147 853 l 146 857 l 341 1031 l 448 1031 l 644 857 m 193 972 l 93 972 l -45 1149 l 98 1149 l 193 972 z "},"Ổ":{"ha":947,"x_min":77,"x_max":872,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 718 1076 l 716 1072 l 601 1072 l 468 1198 l 335 1072 l 220 1072 l 219 1076 l 401 1251 l 535 1251 l 718 1076 m 701 1166 l 700 1256 q 763 1267 745 1259 q 782 1298 782 1276 q 756 1332 782 1321 q 689 1343 731 1343 l 693 1407 q 826 1377 779 1407 q 872 1297 872 1348 q 847 1234 872 1255 q 783 1207 821 1212 l 782 1166 l 701 1166 z "},"ổ":{"ha":789,"x_min":66,"x_max":793,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 639 845 l 637 841 l 522 841 l 389 967 l 256 841 l 142 841 l 140 845 l 322 1019 l 456 1019 l 639 845 m 622 935 l 621 1025 q 685 1036 666 1027 q 703 1067 703 1045 q 678 1101 703 1090 q 610 1112 652 1112 l 614 1175 q 747 1146 701 1175 q 793 1065 793 1116 q 768 1002 793 1024 q 704 976 743 981 l 703 935 l 622 935 z "},"Ỗ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 720 1078 l 719 1074 l 604 1074 l 471 1193 l 338 1074 l 223 1074 l 222 1078 l 418 1252 l 524 1252 l 720 1078 m 648 1421 q 615 1338 648 1375 q 537 1302 582 1302 q 463 1326 501 1302 q 399 1350 425 1350 q 360 1330 378 1350 q 343 1287 343 1310 l 289 1300 q 321 1384 289 1347 q 399 1422 354 1422 q 469 1399 427 1422 q 537 1375 511 1375 q 576 1394 557 1375 q 594 1437 594 1413 l 648 1421 z "},"ỗ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 642 846 l 640 842 l 525 842 l 392 962 l 259 842 l 144 842 l 143 846 l 339 1021 l 446 1021 l 642 846 m 569 1190 q 536 1107 569 1143 q 458 1071 503 1071 q 384 1095 422 1071 q 320 1119 346 1119 q 282 1099 299 1119 q 264 1056 264 1079 l 210 1069 q 243 1153 210 1116 q 320 1191 275 1191 q 390 1167 349 1191 q 458 1144 432 1144 q 497 1163 479 1144 q 515 1206 515 1182 l 569 1190 z "},"Ộ":{"ha":947,"x_min":77,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 682 1117 l 682 1100 l 573 1100 l 473 1200 l 374 1100 l 266 1100 l 266 1118 l 433 1278 l 514 1278 l 682 1117 m 547 -233 l 399 -233 l 399 -98 l 547 -98 l 547 -233 z "},"ộ":{"ha":789,"x_min":66,"x_max":723,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 634 642 546 747 q 723 374 723 537 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 604 884 l 604 867 l 494 867 l 395 968 l 296 867 l 187 867 l 187 885 l 354 1046 l 435 1046 l 604 884 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ớ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 540 1223 l 696 1223 l 697 1219 l 514 1043 l 414 1043 l 540 1223 z "},"ớ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 459 1017 l 615 1017 l 616 1013 l 433 837 l 334 837 l 459 1017 z "},"Ờ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 532 1046 l 425 1046 l 255 1222 l 257 1226 l 413 1226 l 532 1046 z "},"ờ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 451 840 l 344 840 l 174 1016 l 176 1020 l 332 1020 l 451 840 z "},"Ở":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 431 1086 l 430 1190 q 504 1204 481 1193 q 526 1240 526 1215 q 496 1279 526 1266 q 417 1292 467 1292 l 422 1365 q 575 1330 522 1365 q 629 1237 629 1295 q 600 1165 629 1190 q 526 1134 570 1140 l 526 1086 l 431 1086 z "},"ở":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 350 853 l 349 958 q 423 971 401 960 q 445 1007 445 982 q 415 1046 445 1034 q 336 1059 386 1059 l 341 1133 q 494 1098 441 1133 q 548 1004 548 1063 q 519 932 548 957 q 446 901 490 907 l 445 853 l 350 853 z "},"Ỡ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 711 1238 q 670 1131 711 1175 q 569 1086 630 1086 q 469 1118 521 1086 q 382 1149 416 1149 q 333 1127 353 1149 q 313 1074 313 1105 l 240 1092 q 280 1201 240 1155 q 382 1248 320 1248 q 477 1216 420 1248 q 569 1184 534 1184 q 617 1206 597 1184 q 637 1260 637 1228 l 711 1238 z "},"ỡ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 630 1032 q 590 924 630 968 q 488 880 549 880 q 388 912 440 880 q 301 943 336 943 q 252 921 272 943 q 233 868 233 899 l 159 886 q 199 995 159 949 q 301 1042 239 1042 q 396 1010 339 1042 q 488 978 453 978 q 536 1000 516 978 q 557 1054 557 1022 l 630 1032 z "},"Ợ":{"ha":951,"x_min":73,"x_max":1041,"o":"m 866 406 q 754 105 866 224 q 463 -14 642 -14 q 181 105 290 -14 q 73 406 73 224 l 73 581 q 181 882 73 762 q 463 1002 290 1002 q 614 975 544 1002 q 737 898 684 947 q 864 963 821 905 q 907 1121 907 1021 l 1041 1121 q 981 908 1041 991 q 812 802 922 826 q 852 697 838 753 q 866 581 866 641 l 866 406 m 732 583 q 659 805 732 718 q 463 892 586 892 q 276 805 346 892 q 207 583 207 718 l 207 406 q 276 182 207 269 q 463 95 346 95 q 660 181 587 95 q 732 406 732 268 l 732 583 m 543 -229 l 395 -229 l 395 -93 l 543 -93 l 543 -229 z "},"ợ":{"ha":797,"x_min":66,"x_max":852,"o":"m 66 374 q 154 642 66 536 q 393 747 242 747 q 520 724 463 747 q 621 657 578 700 q 704 714 677 669 q 731 829 731 759 l 852 829 q 807 669 852 732 q 674 583 762 605 q 710 485 698 538 q 723 374 723 432 l 723 359 q 635 90 723 195 q 395 -14 547 -14 q 154 91 243 -14 q 66 359 66 195 l 66 374 m 199 359 q 249 166 199 242 q 395 90 298 90 q 540 166 490 90 q 589 359 589 242 l 589 374 q 539 566 589 489 q 393 642 490 642 q 249 566 298 642 q 199 374 199 489 l 199 359 m 469 -234 l 321 -234 l 321 -98 l 469 -98 l 469 -234 z "},"Ụ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 546 -233 l 398 -233 l 398 -98 l 546 -98 l 546 -233 z "},"ụ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 422 -229 l 275 -229 l 275 -93 l 422 -93 l 422 -229 z "},"Ủ":{"ha":940,"x_min":100,"x_max":844,"o":"m 844 987 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 428 1072 l 427 1176 q 501 1190 479 1179 q 523 1225 523 1200 q 493 1265 523 1252 q 414 1278 464 1278 l 419 1351 q 572 1316 519 1351 q 626 1223 626 1281 q 597 1151 626 1175 q 524 1120 568 1126 l 523 1072 l 428 1072 z "},"ủ":{"ha":789,"x_min":94,"x_max":692,"o":"m 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 0 l 572 0 l 563 109 m 349 840 l 348 945 q 421 958 399 947 q 444 994 444 969 q 414 1034 444 1021 q 335 1046 385 1046 l 340 1120 q 493 1085 439 1120 q 547 991 547 1050 q 517 919 547 944 q 444 888 488 895 l 444 840 l 349 840 z "},"Ứ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 536 1236 l 692 1236 l 694 1232 l 511 1055 l 411 1055 l 536 1236 z "},"ứ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 458 1003 l 614 1003 l 615 999 l 432 823 l 332 823 l 458 1003 z "},"Ừ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 528 1058 l 421 1058 l 252 1234 l 254 1238 l 410 1238 l 528 1058 z "},"ừ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 450 825 l 342 825 l 173 1002 l 175 1006 l 331 1006 l 450 825 z "},"Ử":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 427 1072 l 427 1176 q 500 1190 478 1179 q 522 1225 522 1200 q 493 1265 522 1252 q 414 1278 463 1278 l 418 1351 q 572 1316 518 1351 q 625 1223 625 1281 q 596 1151 625 1175 q 523 1120 567 1126 l 522 1072 l 427 1072 z "},"ử":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 349 840 l 348 945 q 421 958 399 947 q 444 994 444 969 q 414 1034 444 1021 q 335 1046 385 1046 l 340 1120 q 493 1085 439 1120 q 547 991 547 1050 q 517 919 547 944 q 444 888 488 895 l 444 840 l 349 840 z "},"Ữ":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 707 1251 q 667 1143 707 1187 q 566 1099 627 1099 q 465 1130 517 1099 q 378 1162 413 1162 q 330 1140 349 1162 q 310 1086 310 1118 l 237 1104 q 277 1214 237 1167 q 378 1260 317 1260 q 473 1228 416 1260 q 566 1196 530 1196 q 614 1218 593 1196 q 634 1272 634 1240 l 707 1251 z "},"ữ":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 629 1018 q 588 910 629 954 q 487 866 548 866 q 387 898 439 866 q 300 929 334 929 q 251 907 271 929 q 231 854 231 885 l 158 871 q 198 981 158 935 q 300 1027 238 1027 q 395 996 338 1027 q 487 964 452 964 q 535 986 515 964 q 555 1040 555 1008 l 629 1018 z "},"Ự":{"ha":970,"x_min":100,"x_max":1101,"o":"m 844 987 l 844 867 l 848 865 q 937 939 906 884 q 968 1079 968 994 l 1097 1079 l 1099 1076 q 1034 862 1101 945 q 844 757 968 780 l 844 318 q 739 71 844 157 q 465 -14 634 -14 q 201 72 302 -14 q 100 318 100 157 l 100 987 l 233 987 l 233 318 q 297 148 233 206 q 465 90 361 90 q 643 148 575 90 q 710 318 710 206 l 710 987 l 844 987 m 546 -233 l 398 -233 l 398 -98 l 546 -98 l 546 -233 z "},"ự":{"ha":817,"x_min":94,"x_max":940,"o":"m 936 832 l 938 828 q 880 643 940 708 q 692 568 820 578 l 692 0 l 572 0 l 563 109 q 474 18 528 50 q 349 -14 420 -14 q 161 62 229 -14 q 94 301 94 139 l 94 734 l 228 734 l 228 300 q 262 138 228 182 q 370 94 297 94 q 488 123 441 94 q 558 205 534 152 l 558 734 l 692 734 l 692 656 l 696 655 q 792 703 764 660 q 821 832 821 747 l 936 832 m 422 -229 l 275 -229 l 275 -93 l 422 -93 l 422 -229 z "},"Ỳ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 480 1057 l 373 1057 l 203 1234 l 205 1238 l 361 1238 l 480 1057 z "},"ỳ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 405 825 l 298 825 l 128 1002 l 130 1006 l 286 1006 l 405 825 z "},"Ỵ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 498 -222 l 350 -222 l 350 -86 l 498 -86 l 498 -222 z "},"ỵ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 560 -334 l 412 -334 l 412 -199 l 560 -199 l 560 -334 z "},"Ỷ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 379 1071 l 378 1175 q 452 1189 430 1178 q 474 1225 474 1200 q 445 1264 474 1251 q 366 1277 415 1277 l 370 1350 q 524 1315 470 1350 q 577 1222 577 1280 q 548 1150 577 1175 q 475 1119 519 1125 l 474 1071 l 379 1071 z "},"ỷ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 304 840 l 303 945 q 377 958 355 947 q 399 994 399 969 q 369 1034 399 1021 q 290 1046 340 1046 l 295 1120 q 448 1085 395 1120 q 502 991 502 1050 q 473 919 502 944 q 399 888 444 895 l 399 840 l 304 840 z "},"Ỹ":{"ha":848,"x_min":14,"x_max":834,"o":"m 424 486 l 682 987 l 834 987 l 488 347 l 488 0 l 355 0 l 355 356 l 14 987 l 166 987 l 424 486 m 659 1250 q 619 1142 659 1186 q 517 1098 578 1098 q 417 1129 469 1098 q 330 1161 365 1161 q 281 1139 301 1161 q 262 1086 262 1117 l 189 1103 q 229 1213 189 1166 q 330 1259 269 1259 q 425 1227 368 1259 q 517 1196 482 1196 q 566 1218 545 1196 q 586 1272 586 1240 l 659 1250 z "},"ỹ":{"ha":699,"x_min":18,"x_max":678,"o":"m 321 272 l 345 180 l 349 180 l 530 734 l 678 734 l 370 -113 q 290 -241 342 -186 q 150 -296 238 -296 q 109 -293 134 -296 q 70 -286 83 -289 l 83 -180 q 107 -182 79 -180 q 143 -184 136 -184 q 213 -146 186 -184 q 258 -62 240 -108 l 290 15 l 18 734 l 167 734 l 321 272 m 584 1018 q 544 910 584 954 q 442 866 503 866 q 342 898 394 866 q 255 929 290 929 q 206 907 226 929 q 186 854 186 885 l 113 871 q 153 981 113 935 q 255 1027 193 1027 q 350 996 293 1027 q 442 964 407 964 q 490 986 470 964 q 511 1040 511 1008 l 584 1018 z "},"Ὅ":{"ha":947,"x_min":-149,"x_max":869,"o":"m 869 406 q 757 105 869 224 q 466 -14 645 -14 q 185 105 293 -14 q 77 406 77 224 l 77 581 q 185 882 77 762 q 466 1002 293 1002 q 757 882 645 1002 q 869 581 869 762 l 869 406 m 736 583 q 663 805 736 718 q 466 892 589 892 q 280 805 349 892 q 210 583 210 718 l 210 406 q 280 182 210 269 q 466 95 349 95 q 663 181 590 95 q 736 406 736 268 l 736 583 m 55 929 l 124 1141 l 266 1141 l 266 1127 l 111 911 l 55 911 l 55 929 m -149 1006 l -39 1141 l 34 1141 l -22 1002 l -22 909 l -149 909 l -149 1006 z "}," ":{"ha":708,"x_min":0,"x_max":0,"o":""}," ":{"ha":1417,"x_min":0,"x_max":0,"o":""}," ":{"ha":708,"x_min":0,"x_max":0,"o":""}," ":{"ha":1417,"x_min":0,"x_max":0,"o":""}," ":{"ha":473,"x_min":0,"x_max":0,"o":""}," ":{"ha":354,"x_min":0,"x_max":0,"o":""}," ":{"ha":236,"x_min":0,"x_max":0,"o":""}," ":{"ha":781,"x_min":0,"x_max":0,"o":""}," ":{"ha":380,"x_min":0,"x_max":0,"o":""}," ":{"ha":283,"x_min":0,"x_max":0,"o":""}," ":{"ha":142,"x_min":0,"x_max":0,"o":""},"​":{"ha":0,"x_min":0,"x_max":0,"o":""},"–":{"ha":960,"x_min":122,"x_max":859,"o":"m 858 439 l 122 439 l 122 545 l 858 545 l 858 439 z "},"—":{"ha":1126,"x_min":128,"x_max":1034,"o":"m 1034 439 l 128 439 l 128 545 l 1034 545 l 1034 439 z "},"―":{"ha":1126,"x_min":128,"x_max":1034,"o":"m 1034 439 l 128 439 l 128 545 l 1034 545 l 1034 439 z "},"‗":{"ha":634,"x_min":3,"x_max":629,"o":"m 629 -277 l 3 -277 l 3 -172 l 629 -172 l 629 -277 m 629 -104 l 3 -104 l 3 0 l 629 0 l 629 -104 z "},"‘":{"ha":283,"x_min":54,"x_max":229,"o":"m 54 817 l 163 1058 l 229 1058 l 188 812 l 188 692 l 54 692 l 54 817 z "},"’":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 z "},"‚":{"ha":283,"x_min":54,"x_max":229,"o":"m 229 18 l 120 -173 l 54 -173 l 95 5 l 95 169 l 229 169 l 229 18 z "},"‛":{"ha":283,"x_min":29,"x_max":203,"o":"m 29 927 l 138 692 l 203 692 l 163 925 l 163 1058 l 29 1058 l 29 927 z "},"“":{"ha":505,"x_min":54,"x_max":452,"o":"m 54 817 l 163 1058 l 229 1058 l 188 812 l 188 692 l 54 692 l 54 817 m 277 817 l 386 1058 l 452 1058 l 411 812 l 411 692 l 277 692 l 277 817 z "},"”":{"ha":510,"x_min":54,"x_max":457,"o":"m 229 927 l 120 692 l 54 692 l 95 925 l 95 1058 l 229 1058 l 229 927 m 457 927 l 349 692 l 283 692 l 323 925 l 323 1058 l 457 1058 l 457 927 z "},"„":{"ha":492,"x_min":54,"x_max":437,"o":"m 229 65 l 120 -162 l 54 -162 l 95 58 l 95 190 l 229 190 l 229 65 m 437 65 l 329 -162 l 263 -162 l 304 64 l 304 190 l 437 190 l 437 65 z "},"†":{"ha":766,"x_min":47,"x_max":719,"o":"m 719 628 l 448 628 l 448 0 l 315 0 l 315 628 l 47 628 l 47 734 l 315 734 l 315 987 l 448 987 l 448 734 l 719 734 l 719 628 z "},"‡":{"ha":793,"x_min":59,"x_max":730,"o":"m 730 0 l 459 0 l 459 -282 l 326 -282 l 326 0 l 59 0 l 59 104 l 326 104 l 326 628 l 59 628 l 59 734 l 326 734 l 326 987 l 459 987 l 459 734 l 730 734 l 730 628 l 459 628 l 459 104 l 730 104 l 730 0 z "},"•":{"ha":471,"x_min":93,"x_max":374,"o":"m 93 538 q 131 635 93 597 q 233 673 169 673 q 335 635 297 673 q 374 538 374 597 l 374 497 q 336 400 374 437 q 233 363 298 363 q 131 400 169 363 q 93 497 93 437 l 93 538 z "},"‥":{"ha":658,"x_min":109,"x_max":543,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 543 0 l 410 0 l 410 137 l 543 137 l 543 0 z "},"…":{"ha":936,"x_min":109,"x_max":828,"o":"m 243 0 l 109 0 l 109 137 l 243 137 l 243 0 m 543 0 l 410 0 l 410 137 l 543 137 l 543 0 m 828 0 l 694 0 l 694 137 l 828 137 l 828 0 z "},"‰":{"ha":1326,"x_min":43,"x_max":1273,"o":"m 555 242 q 611 387 555 328 q 759 446 667 446 q 849 424 809 446 q 913 366 888 403 q 978 424 939 403 q 1068 446 1018 446 q 1217 387 1161 446 q 1273 242 1273 328 l 1273 189 q 1217 44 1273 102 q 1069 -14 1162 -14 q 979 6 1019 -14 q 913 64 939 27 q 849 6 888 27 q 760 -14 810 -14 q 611 44 667 -14 q 555 189 555 102 l 555 242 m 43 798 q 99 943 43 884 q 247 1002 155 1002 q 396 943 340 1002 q 452 798 452 884 l 452 745 q 396 601 452 659 q 248 543 340 543 q 99 601 155 543 q 43 745 43 659 l 43 798 m 654 189 q 682 103 654 138 q 760 68 709 68 q 838 103 810 68 q 865 189 865 138 l 865 242 q 837 328 865 292 q 759 363 809 363 q 682 328 709 363 q 654 242 654 292 l 654 189 m 964 189 q 991 103 964 138 q 1069 68 1019 68 q 1146 103 1119 68 q 1173 189 1173 138 l 1173 242 q 1146 328 1173 292 q 1068 363 1118 363 q 991 328 1018 363 q 964 242 964 292 l 964 189 m 142 745 q 170 660 142 695 q 248 625 197 625 q 325 660 298 625 q 353 745 353 694 l 353 798 q 325 883 353 848 q 247 919 297 919 q 170 883 197 919 q 142 798 142 848 l 142 745 m 311 75 l 237 120 l 719 892 l 793 846 l 311 75 z "},"′":{"ha":243,"x_min":54,"x_max":189,"o":"m 189 907 l 120 715 l 54 715 l 55 895 l 55 1058 l 189 1058 l 189 907 z "},"″":{"ha":453,"x_min":54,"x_max":398,"o":"m 189 875 l 120 705 l 54 705 l 55 868 l 55 1058 l 189 1058 l 189 875 m 398 875 l 330 705 l 264 705 l 264 873 l 264 1058 l 398 1058 l 398 875 z "},"‹":{"ha":417,"x_min":73,"x_max":374,"o":"m 201 373 l 374 103 l 273 103 l 73 367 l 73 380 l 273 644 l 374 644 l 201 373 z "},"›":{"ha":417,"x_min":60,"x_max":360,"o":"m 160 644 l 360 380 l 360 367 l 160 103 l 60 103 l 233 373 l 60 644 l 160 644 z "},"‼":{"ha":731,"x_min":116,"x_max":616,"o":"m 250 324 l 116 324 l 116 987 l 250 987 l 250 324 m 250 0 l 116 0 l 116 138 l 250 138 l 250 0 m 615 324 l 481 324 l 481 987 l 615 987 l 615 324 m 616 0 l 481 0 l 481 138 l 616 138 l 616 0 z "},"⁄":{"ha":633,"x_min":40,"x_max":596,"o":"m 114 75 l 40 120 l 522 892 l 596 846 l 114 75 z "},"⁴":{"ha":638,"x_min":48,"x_max":577,"o":"m 476 592 l 577 592 l 577 505 l 476 505 l 476 380 l 359 380 l 359 505 l 51 505 l 48 570 l 356 1002 l 476 1002 l 476 592 m 177 592 l 359 592 l 359 848 l 355 848 l 347 831 l 177 592 z "},"ⁿ":{"ha":586,"x_min":83,"x_max":517,"o":"m 175 984 l 196 903 q 258 970 220 946 q 342 994 295 994 q 471 943 425 994 q 517 786 517 892 l 517 441 l 395 441 l 395 764 q 371 864 395 833 q 302 895 347 895 q 242 876 267 895 q 205 826 218 858 l 205 441 l 83 441 l 83 984 l 175 984 z "},"₣":{"ha":809,"x_min":19,"x_max":775,"o":"m 706 437 l 256 437 l 256 0 l 122 0 l 122 987 l 775 987 l 775 882 l 256 882 l 256 542 l 706 542 l 706 437 m 454 179 l 19 179 l 19 284 l 454 284 l 454 179 z "},"₤":{"ha":810,"x_min":47,"x_max":753,"o":"m 295 299 q 283 192 294 243 q 255 104 273 141 l 753 104 l 753 0 l 91 0 l 91 104 l 98 104 q 144 171 128 113 q 163 299 160 230 l 47 299 l 47 404 l 159 404 l 155 501 l 48 501 l 48 606 l 151 606 l 148 705 q 224 923 148 844 q 427 1002 300 1002 q 637 931 563 1002 q 709 743 712 860 l 708 739 l 579 739 q 536 858 579 819 q 427 897 494 897 q 321 846 360 897 q 281 705 281 795 l 285 606 l 571 606 l 571 501 l 289 501 l 292 404 l 571 404 l 571 299 l 295 299 z "},"₧":{"ha":1141,"x_min":111,"x_max":1053,"o":"m 907 911 l 907 734 l 1046 734 l 1046 635 l 907 635 l 907 189 q 928 117 907 138 q 985 96 949 96 q 1010 98 996 96 q 1035 105 1024 101 l 1053 14 q 1009 -6 1038 1 q 951 -14 980 -14 q 821 35 869 -14 q 773 189 773 84 l 773 635 l 682 635 q 597 466 668 534 q 351 382 509 382 l 245 382 l 245 0 l 111 0 l 111 987 l 351 987 q 597 903 509 987 q 682 734 668 834 l 773 734 l 773 911 l 907 911 m 245 488 l 351 488 q 501 544 451 488 q 551 684 551 601 q 501 825 551 767 q 351 882 451 882 l 245 882 l 245 488 z "},"₫":{"ha":810,"x_min":66,"x_max":821,"o":"m 738 -187 l 112 -187 l 112 -83 l 738 -83 l 738 -187 m 821 835 l 687 835 l 687 0 l 578 0 l 562 90 q 474 12 526 39 q 353 -14 422 -14 q 142 83 218 -14 q 66 339 66 180 l 66 353 q 142 639 66 530 q 354 747 218 747 q 468 724 418 747 q 553 654 518 700 l 553 835 l 386 835 l 386 940 l 553 940 l 553 1058 l 687 1058 l 687 940 l 821 940 l 821 835 m 200 339 q 245 161 200 228 q 387 94 291 94 q 487 121 446 94 q 553 197 528 149 l 553 540 q 487 612 528 585 q 388 639 446 639 q 246 559 292 639 q 200 353 200 480 l 200 339 z "},"€":{"ha":738,"x_min":54,"x_max":665,"o":"m 620 326 l 291 326 l 290 323 q 345 160 287 229 q 503 90 403 90 q 579 96 541 90 q 652 113 617 102 l 665 7 q 586 -9 627 -3 q 503 -14 544 -14 q 249 84 346 -14 q 153 326 153 182 l 54 326 l 54 431 l 153 431 l 153 524 l 54 524 l 54 629 l 153 629 l 153 639 q 249 903 153 804 q 501 1002 345 1002 q 581 996 541 1002 q 665 981 621 991 l 652 873 q 577 890 616 884 q 501 897 539 897 q 344 827 402 897 q 286 640 286 757 l 286 629 l 620 629 l 620 524 l 286 524 l 286 431 l 620 431 l 620 326 z "},"℅":{"ha":1026,"x_min":84,"x_max":957,"o":"m 459 716 l 460 712 q 412 592 463 641 q 276 543 361 543 q 136 601 189 543 q 84 745 84 659 l 84 798 q 136 943 84 884 q 275 1002 188 1002 q 412 951 360 1002 q 460 831 463 901 l 459 827 l 366 827 q 342 892 366 866 q 275 919 318 919 q 207 883 231 919 q 183 798 183 848 l 183 745 q 207 660 183 695 q 276 625 231 625 q 342 652 319 625 q 366 716 366 680 l 459 716 m 549 242 q 604 387 549 328 q 753 446 660 446 q 901 387 845 446 q 957 242 957 328 l 957 189 q 901 44 957 102 q 754 -14 846 -14 q 605 44 661 -14 q 549 189 549 102 l 549 242 m 648 189 q 675 103 648 138 q 754 68 703 68 q 831 103 804 68 q 858 189 858 138 l 858 242 q 830 328 858 292 q 753 363 802 363 q 675 328 703 363 q 648 242 648 292 l 648 189 m 303 75 l 229 120 l 711 892 l 785 846 l 303 75 z "},"ℓ":{"ha":663,"x_min":72,"x_max":599,"o":"m 487 -9 l 483 -10 q 278 68 348 -13 q 209 292 209 149 l 209 300 q 141 288 176 292 q 72 284 107 284 l 72 406 q 143 411 109 406 q 209 424 178 415 l 209 745 q 263 933 209 865 q 411 1002 317 1002 q 546 943 494 1002 q 599 787 599 885 l 599 759 q 530 537 599 652 q 342 355 461 422 l 342 292 q 375 144 342 194 q 487 95 408 95 l 487 -9 m 465 758 l 465 787 q 450 865 465 839 q 411 892 435 892 q 359 855 376 892 q 342 745 342 818 l 342 503 l 347 502 q 435 614 404 545 q 465 758 465 684 z "},"№":{"ha":1527,"x_min":116,"x_max":1442,"o":"m 863 0 l 729 0 l 254 764 l 250 762 l 250 0 l 116 0 l 116 987 l 250 987 l 725 225 l 729 227 l 729 987 l 863 987 l 863 0 m 973 772 q 1037 936 973 873 q 1207 1000 1101 1000 q 1378 936 1314 1000 q 1442 772 1442 873 l 1442 693 q 1378 529 1442 592 q 1208 467 1315 467 q 1037 529 1101 467 q 973 693 973 592 l 973 772 m 1090 693 q 1120 598 1090 633 q 1208 562 1150 562 q 1295 598 1265 562 q 1325 693 1325 634 l 1325 772 q 1295 866 1325 829 q 1207 902 1264 902 q 1120 866 1150 902 q 1090 772 1090 829 l 1090 693 m 1364 206 l 1031 206 l 1031 311 l 1364 311 l 1364 206 z "},"™":{"ha":871,"x_min":70,"x_max":760,"o":"m 696 856 l 692 857 l 591 623 l 556 623 l 450 867 l 446 865 l 446 623 l 383 623 l 383 987 l 462 987 l 571 723 l 575 723 l 685 987 l 760 987 l 760 623 l 696 623 l 696 856 m 330 932 l 232 932 l 232 623 l 168 623 l 168 932 l 70 932 l 70 987 l 330 987 l 330 932 z "},"℮":{"ha":884,"x_min":103,"x_max":794,"o":"m 709 64 q 587 6 650 26 q 458 -14 523 -14 q 207 98 311 -14 q 103 367 103 210 q 213 634 103 520 q 458 747 322 747 q 696 642 599 747 q 794 380 794 537 l 794 349 l 273 349 l 273 126 q 358 71 311 90 q 458 52 406 52 q 586 72 523 52 q 708 134 649 92 l 709 64 m 458 684 q 360 662 408 684 q 273 601 313 640 l 273 413 l 640 413 l 640 607 q 556 663 603 642 q 458 684 509 684 z "},"⅛":{"ha":1171,"x_min":73,"x_max":1097,"o":"m 277 438 l 159 438 l 159 882 l 73 882 l 73 973 l 277 989 l 277 438 m 292 75 l 218 120 l 700 892 l 774 846 l 292 75 m 1081 401 q 1056 331 1081 361 q 989 281 1031 300 q 1068 229 1039 262 q 1097 153 1097 195 q 1035 35 1097 77 q 877 -7 972 -7 q 713 35 778 -7 q 648 153 648 77 q 678 229 648 195 q 760 282 708 263 q 690 331 715 300 q 665 401 665 361 q 725 512 665 473 q 876 551 785 551 q 1023 512 964 551 q 1081 401 1081 473 m 982 157 q 951 215 982 192 q 876 237 920 237 q 796 215 829 237 q 764 157 764 193 q 796 101 764 122 q 877 81 828 81 q 951 101 921 81 q 982 157 982 122 m 964 395 q 939 444 964 426 q 876 462 913 462 q 808 444 835 462 q 781 395 781 427 q 809 345 781 364 q 877 326 836 326 q 939 345 913 326 q 964 395 964 364 z "},"⅜":{"ha":1274,"x_min":75,"x_max":1200,"o":"m 290 767 q 358 786 336 767 q 380 841 380 806 q 355 890 380 870 q 283 909 330 909 q 222 893 245 909 q 199 850 199 877 l 89 850 l 87 854 q 141 958 83 918 q 283 998 198 998 q 439 958 382 998 q 496 843 496 918 q 472 775 496 806 q 406 726 448 744 q 479 678 453 711 q 505 600 505 646 q 444 482 505 524 q 283 440 382 440 q 136 479 197 440 q 79 594 75 519 l 79 598 l 190 598 q 215 548 190 567 q 283 528 241 528 q 361 548 332 528 q 389 601 389 568 q 365 662 389 643 q 290 682 340 682 l 201 682 l 201 767 l 290 767 m 417 75 l 343 120 l 825 892 l 899 846 l 417 75 m 1183 401 q 1159 331 1183 361 q 1092 281 1134 300 q 1171 229 1141 262 q 1200 153 1200 195 q 1137 35 1200 77 q 979 -7 1074 -7 q 815 35 880 -7 q 750 153 750 77 q 780 229 750 195 q 862 282 810 263 q 792 331 817 300 q 767 401 767 361 q 827 512 767 473 q 979 551 887 551 q 1125 512 1067 551 q 1183 401 1183 473 m 1084 157 q 1053 215 1084 192 q 979 237 1022 237 q 899 215 931 237 q 866 157 866 193 q 898 101 866 122 q 979 81 930 81 q 1054 101 1023 81 q 1084 157 1084 122 m 1067 395 q 1041 444 1067 426 q 979 462 1016 462 q 910 444 937 462 q 884 395 884 427 q 911 345 884 364 q 979 326 939 326 q 1041 345 1015 326 q 1067 395 1067 364 z "},"⅝":{"ha":1306,"x_min":76,"x_max":1232,"o":"m 98 678 l 133 987 l 483 987 l 483 897 l 237 897 l 219 771 q 266 791 239 783 q 321 800 293 800 q 462 752 411 802 q 513 616 513 703 q 459 485 513 534 q 292 436 405 436 q 138 473 199 436 q 80 583 76 511 l 81 587 l 191 593 q 218 543 191 561 q 292 524 246 524 q 371 548 345 524 q 397 616 397 571 q 371 688 397 661 q 299 715 345 715 q 232 704 255 715 q 199 672 210 692 l 98 678 m 455 75 l 381 120 l 863 892 l 937 846 l 455 75 m 1216 401 q 1191 331 1216 361 q 1124 281 1166 300 q 1203 229 1174 262 q 1232 153 1232 195 q 1170 35 1232 77 q 1012 -7 1107 -7 q 848 35 913 -7 q 783 153 783 77 q 813 229 783 195 q 895 282 843 263 q 825 331 850 300 q 800 401 800 361 q 860 512 800 473 q 1011 551 920 551 q 1158 512 1099 551 q 1216 401 1216 473 m 1117 157 q 1086 215 1117 192 q 1011 237 1055 237 q 931 215 964 237 q 899 157 899 193 q 931 101 899 122 q 1012 81 963 81 q 1086 101 1056 81 q 1117 157 1117 122 m 1099 395 q 1074 444 1099 426 q 1011 462 1048 462 q 943 444 970 462 q 916 395 916 427 q 944 345 916 364 q 1012 326 971 326 q 1074 345 1048 326 q 1099 395 1099 364 z "},"⅞":{"ha":1193,"x_min":73,"x_max":1119,"o":"m 472 898 q 343 722 381 791 q 306 530 306 653 l 306 444 l 189 444 l 189 530 q 242 747 189 652 q 355 898 294 842 l 73 898 l 73 987 l 472 987 l 472 898 m 324 75 l 250 120 l 732 892 l 806 846 l 324 75 m 1103 401 q 1078 331 1103 361 q 1011 281 1053 300 q 1090 229 1061 262 q 1119 153 1119 195 q 1056 35 1119 77 q 899 -7 994 -7 q 734 35 800 -7 q 669 153 669 77 q 700 229 669 195 q 781 282 730 263 q 711 331 736 300 q 686 401 686 361 q 746 512 686 473 q 898 551 806 551 q 1044 512 986 551 q 1103 401 1103 473 m 1004 157 q 972 215 1004 192 q 898 237 941 237 q 818 215 850 237 q 785 157 785 193 q 818 101 785 122 q 899 81 850 81 q 973 101 943 81 q 1004 157 1004 122 m 986 395 q 961 444 986 426 q 898 462 935 462 q 830 444 857 462 q 803 395 803 427 q 830 345 803 364 q 899 326 858 326 q 960 345 935 326 q 986 395 986 364 z "},"∂":{"ha":804,"x_min":49,"x_max":727,"o":"m 330 1029 q 618 849 509 986 q 727 512 727 712 l 727 363 q 629 91 727 197 q 386 -14 532 -14 q 143 81 238 -14 q 49 316 49 176 q 134 569 49 474 q 366 663 218 663 q 488 643 431 663 q 581 588 545 623 l 583 591 q 489 802 568 722 q 290 918 409 882 l 330 1029 m 389 90 q 536 168 479 90 q 593 363 593 245 l 593 450 q 511 527 569 496 q 366 558 452 558 q 228 490 274 558 q 182 316 182 422 q 238 158 182 227 q 389 90 294 90 z "},"∏":{"ha":974,"x_min":114,"x_max":860,"o":"m 860 -143 l 726 -143 l 726 882 l 248 882 l 248 -143 l 114 -143 l 114 987 l 860 987 l 860 -143 z "},"∑":{"ha":815,"x_min":47,"x_max":812,"o":"m 589 372 l 211 -74 l 212 -77 l 812 -77 l 812 -182 l 47 -182 l 47 -83 l 461 400 l 47 888 l 47 987 l 759 987 l 759 882 l 211 882 l 210 879 l 589 428 l 589 372 z "},"−":{"ha":793,"x_min":114,"x_max":680,"o":"m 680 439 l 114 439 l 114 545 l 680 545 l 680 439 z "},"√":{"ha":840,"x_min":43,"x_max":812,"o":"m 376 231 l 388 174 l 392 174 l 405 231 l 675 987 l 812 987 l 439 0 l 338 0 l 170 426 l 43 426 l 43 532 l 265 532 l 376 231 z "},"∞":{"ha":1421,"x_min":71,"x_max":1342,"o":"m 1342 344 q 1257 89 1342 191 q 1034 -14 1173 -14 q 841 59 922 -14 q 706 237 760 132 q 571 59 652 132 q 379 -14 490 -14 q 155 89 239 -14 q 71 344 71 191 l 71 389 q 155 644 71 541 q 378 747 239 747 q 570 674 489 747 q 707 496 652 601 q 842 674 760 600 q 1035 747 923 747 q 1257 644 1173 747 q 1342 389 1342 540 l 1342 344 m 204 344 q 249 159 204 229 q 379 90 293 90 q 544 185 472 90 q 635 353 616 279 l 635 381 q 544 548 616 453 q 378 642 471 642 q 248 572 292 642 q 204 389 204 502 l 204 344 m 1208 389 q 1164 572 1208 502 q 1035 642 1119 642 q 869 548 941 642 q 777 381 798 454 l 777 353 q 869 184 797 278 q 1034 90 941 90 q 1164 160 1119 90 q 1208 344 1208 229 l 1208 389 z "},"∫":{"ha":356,"x_min":-46,"x_max":447,"o":"m 250 -60 q 191 -235 250 -174 q 29 -296 133 -296 q -9 -293 8 -296 q -46 -284 -26 -290 l -37 -182 q -5 -189 -27 -186 q 29 -191 17 -191 q 93 -156 69 -191 q 116 -60 116 -121 l 116 827 q 178 1008 116 944 q 349 1072 239 1072 q 395 1068 372 1072 q 447 1058 418 1065 l 431 960 q 402 965 417 963 q 370 967 387 967 q 280 930 311 967 q 250 827 250 893 l 250 -60 z "},"≈":{"ha":783,"x_min":68,"x_max":708,"o":"m 75 593 q 149 665 107 639 q 235 692 191 692 q 304 684 282 692 q 394 642 326 676 q 475 604 452 612 q 542 595 497 595 q 627 622 585 595 q 702 694 669 648 l 708 576 q 633 504 675 530 q 548 477 591 477 q 481 486 503 477 q 400 524 458 494 q 310 566 332 558 q 241 574 288 574 q 155 547 197 574 q 81 475 113 521 l 75 593 m 68 303 q 142 376 100 349 q 229 402 184 402 q 297 395 275 403 q 387 353 319 387 q 469 313 448 321 q 535 305 491 305 q 621 331 578 305 q 695 405 663 357 l 701 286 q 627 213 669 239 q 541 187 585 187 q 474 196 496 187 q 393 234 452 204 q 301 277 322 269 q 235 284 281 285 q 149 258 191 284 q 75 184 106 231 l 68 303 z "},"≠":{"ha":727,"x_min":103,"x_max":669,"o":"m 538 669 l 669 669 l 669 558 l 476 558 l 381 387 l 669 387 l 669 276 l 319 276 l 229 113 l 170 154 l 238 276 l 103 276 l 103 387 l 300 387 l 395 558 l 103 558 l 103 669 l 456 669 l 556 848 l 615 808 l 538 669 z "},"≤":{"ha":732,"x_min":107,"x_max":677,"o":"m 677 5 l 111 5 l 111 110 l 677 110 l 677 5 m 281 469 l 224 458 l 224 454 l 281 442 l 661 303 l 661 181 l 107 412 l 107 503 l 661 734 l 661 611 l 281 469 z "},"≥":{"ha":738,"x_min":104,"x_max":685,"o":"m 677 3 l 111 3 l 111 109 l 677 109 l 677 3 m 104 615 l 104 734 l 685 503 l 685 412 l 104 181 l 104 300 l 510 445 l 568 456 l 568 460 l 510 472 l 104 615 z "},"◊":{"ha":700,"x_min":28,"x_max":672,"o":"m 298 987 l 399 987 l 672 493 l 401 0 l 300 0 l 28 493 l 298 987 m 536 493 l 361 840 l 350 874 l 346 874 l 334 840 l 164 493 l 338 146 l 350 113 l 354 113 l 366 146 l 536 493 z "},"":{"ha":353,"x_min":68,"x_max":217,"o":"m 217 70 l 134 -91 l 68 -91 l 110 76 l 110 165 l 217 165 l 217 70 z "},"fi":{"ha":773,"x_min":19,"x_max":665,"o":"m 134 0 l 134 635 l 19 635 l 19 734 l 134 734 l 134 813 q 207 1004 134 937 q 407 1072 279 1072 q 496 1062 452 1072 q 600 1031 541 1051 l 577 922 q 500 947 541 937 q 417 957 460 957 q 303 922 338 957 q 268 813 268 886 l 268 734 l 414 734 l 414 635 l 268 635 l 268 0 l 134 0 m 665 0 l 531 0 l 531 734 l 665 734 l 665 0 z "},"fl":{"ha":829,"x_min":38,"x_max":721,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 721 0 l 587 0 l 587 1058 l 721 1058 l 721 0 z "},"ffi":{"ha":1253,"x_min":38,"x_max":1145,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 614 0 l 614 635 l 498 635 l 498 734 l 614 734 l 614 813 q 686 1004 614 937 q 886 1072 758 1072 q 976 1062 932 1072 q 1080 1031 1020 1051 l 1057 922 q 980 947 1021 937 q 897 957 939 957 q 782 922 817 957 q 747 813 747 886 l 747 734 l 893 734 l 893 635 l 747 635 l 747 0 l 614 0 m 1145 0 l 1010 0 l 1010 734 l 1145 734 l 1145 0 z "},"ffl":{"ha":1309,"x_min":38,"x_max":1200,"o":"m 153 0 l 153 635 l 38 635 l 38 734 l 153 734 l 153 827 q 214 1008 153 944 q 385 1072 275 1072 q 432 1068 408 1072 q 484 1058 455 1065 l 467 956 q 438 961 455 959 q 401 963 420 963 q 315 928 343 963 q 286 827 286 893 l 286 734 l 439 734 l 439 635 l 286 635 l 286 0 l 153 0 m 632 0 l 632 635 l 517 635 l 517 734 l 632 734 l 632 827 q 693 1008 632 944 q 865 1072 755 1072 q 911 1068 888 1072 q 963 1058 935 1065 l 947 956 q 917 961 935 959 q 881 963 900 963 q 794 928 823 963 q 766 827 766 893 l 766 734 l 918 734 l 918 635 l 766 635 l 766 0 l 632 0 m 1200 0 l 1067 0 l 1067 1058 l 1200 1058 l 1200 0 z "},"":{"ha":0,"x_min":0,"x_max":0,"o":""},"":{"ha":1424,"x_min":62,"x_max":1377,"o":"m 559 317 q 516 210 559 251 q 404 169 473 169 q 290 210 334 169 q 247 317 247 251 l 247 393 q 290 500 247 458 q 403 542 334 542 q 516 500 472 542 q 559 393 559 458 l 559 317 m 605 171 l 605 543 l 732 543 q 839 518 801 543 q 876 441 876 492 q 861 394 876 415 q 819 363 846 373 q 870 330 852 354 q 888 276 888 307 q 853 198 888 224 q 755 171 817 171 l 605 171 m 497 393 q 472 466 497 439 q 403 493 446 493 q 334 466 359 493 q 309 393 309 439 l 309 317 q 334 244 309 271 q 404 218 359 218 q 472 244 447 218 q 497 317 497 271 l 497 393 m 1144 545 l 1206 545 l 1206 288 q 1171 203 1206 235 q 1080 172 1135 172 q 982 200 1018 172 q 951 279 947 227 l 952 283 l 1009 283 q 1027 237 1009 252 q 1080 222 1046 222 q 1125 240 1107 222 q 1144 288 1144 258 l 1144 545 m 62 -270 l 62 -56 l 138 -56 l 138 -193 l 271 -193 l 271 -270 l 62 -270 m 1166 -270 l 1166 -193 l 1301 -193 l 1301 -56 l 1377 -56 l 1377 -270 l 1166 -270 m 62 793 l 62 986 l 271 986 l 271 907 l 138 907 l 138 793 l 62 793 m 1166 907 l 1166 986 l 1377 986 l 1377 793 l 1301 793 l 1301 907 l 1166 907 m 669 336 l 669 222 l 755 222 q 807 236 789 222 q 825 278 825 251 q 807 319 825 304 q 758 336 790 335 l 755 336 l 669 336 m 896 907 l 896 986 l 1084 986 l 1084 907 l 896 907 m 625 907 l 625 986 l 813 986 l 813 907 l 625 907 m 355 907 l 355 986 l 542 986 l 542 907 l 355 907 m 896 -270 l 896 -193 l 1084 -193 l 1084 -270 l 896 -270 m 625 -270 l 625 -193 l 813 -193 l 813 -270 l 625 -270 m 355 -270 l 355 -193 l 542 -193 l 542 -270 l 355 -270 m 669 384 l 732 384 q 792 397 772 384 q 812 438 812 411 q 793 475 812 465 q 732 486 774 486 l 669 486 l 669 384 m 138 282 l 62 282 l 62 453 l 138 453 l 138 282 m 138 538 l 62 538 l 62 708 l 138 708 l 138 538 m 138 28 l 62 28 l 62 197 l 138 197 l 138 28 m 1377 282 l 1301 282 l 1301 453 l 1377 453 l 1377 282 m 1377 538 l 1301 538 l 1301 708 l 1377 708 l 1377 538 m 1377 28 l 1301 28 l 1301 197 l 1377 197 l 1377 28 z "},"�":{"ha":1425,"x_min":62,"x_max":1361,"o":"m 711 1097 l 1361 436 l 711 -225 l 62 436 l 711 1097 m 767 273 q 776 329 767 309 q 812 368 785 350 q 892 438 861 394 q 924 536 924 483 q 867 674 924 623 q 710 725 810 725 q 561 681 618 725 q 507 549 505 637 l 509 545 l 640 545 q 661 597 641 579 q 710 614 681 614 q 767 593 747 614 q 787 536 787 572 q 769 476 787 502 q 724 431 752 450 q 650 363 670 393 q 630 273 630 332 l 767 273 m 767 211 l 630 211 l 630 96 l 767 96 l 767 211 m 716 -374 l 719 -374 l 719 -376 l 716 -376 l 716 -374 m 715 1455 l 718 1455 l 718 1453 l 715 1453 l 715 1455 z "}},"familyName":"Roboto","ascender":1455,"descender":-376,"underlinePosition":-150,"underlineThickness":100,"boundingBox":{"yMin":-555,"xMin":-980,"yMax":2167,"xMax":2396},"resolution":1000,"original_font_information":{"format":0,"copyright":"Font data copyright Google 2012","fontFamily":"Roboto","fontSubfamily":"Regular","uniqueID":"Google:Roboto Regular:2013","fullName":"Roboto Regular","version":"Version 1.100141; 2013","postScriptName":"Roboto-Regular","trademark":"Roboto is a trademark of Google.","designer":"Google","manufacturerURL":"Google.com","designerURL":"Christian Robertson","licence":"Licensed under the Apache License, Version 2.0","licenceURL":"http://www.apache.org/licenses/LICENSE-2.0"},"cssFontWeight":"normal","cssFontStyle":"normal"} \ No newline at end of file From d3120199be1dbe7471d23b18a321c6eafdcb83ff Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Wed, 26 Jun 2024 12:38:52 +0530 Subject: [PATCH 05/60] update --- activities/3DVolume.activity/css/activity.css | 22 ++- activities/3DVolume.activity/icons/deca.svg | 97 +++++++++- .../icons/default_volume.svg | 68 ++++++- .../3DVolume.activity/icons/number_volume.svg | 177 +++++++++++++++++- .../icons/transparent_volume.svg | 64 ++++++- activities/3DVolume.activity/js/activity.js | 62 ++---- .../js/palettes/volumepalette.html | 1 - 7 files changed, 432 insertions(+), 59 deletions(-) diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index 72f1d5c36..a1e732afc 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -53,13 +53,17 @@ body { #main-toolbar #bg-button { background-image: url(../icons/insert-picture.svg); outline: none; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ } #volume-button { - background-image: url(../icons/cube_solid.svg); + background-image: url(../icons/default_volume.svg); outline: none; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } #main-toolbar #clear-button { background-image: url(../icons/minus.svg); + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ outline: none; } #clear-button.active { @@ -90,6 +94,8 @@ body { background-repeat: no-repeat; background-image: url(../icons/glass.svg); outline: none; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } #throw-button { height: 45px; @@ -97,6 +103,7 @@ body { background-size: cover; background-repeat: no-repeat; background-image: url(../icons/throw.svg); + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ outline: none; } #transparent-button { @@ -132,31 +139,40 @@ body { background-image: url(../icons/cube.svg); outline: none; background-repeat: no-repeat; - background-size: cover; + background-position: center; + background-size: calc(100% - 5px) calc(100% - 5px); /* Adjust the values as needed */ } #tetra-button { background-image: url(../icons/tetra.svg); outline: none; background-repeat: no-repeat; background-size: cover; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } #octa-button { background-image: url(../icons/octa.svg); outline: none; background-repeat: no-repeat; background-size: cover; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } #dodeca-button { background-image: url(../icons/dodeca.svg); outline: none; background-repeat: no-repeat; background-size: cover; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } #deca-button { background-image: url(../icons/deca.svg); outline: none; background-repeat: no-repeat; background-size: cover; + background-size: calc(100% - 5px) calc(100% - 5px); /* Adjust the values as needed */ + } #icosa-button { background-image: url(../icons/icosa.svg); @@ -164,6 +180,8 @@ body { margin-left: 10px; background-repeat: no-repeat; background-size: cover; + background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ + } .active { background-color: #656565 !important; diff --git a/activities/3DVolume.activity/icons/deca.svg b/activities/3DVolume.activity/icons/deca.svg index 6f0ef39be..78d80c4d5 100644 --- a/activities/3DVolume.activity/icons/deca.svg +++ b/activities/3DVolume.activity/icons/deca.svg @@ -1,2 +1,95 @@ - - \ No newline at end of file + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/activities/3DVolume.activity/icons/default_volume.svg b/activities/3DVolume.activity/icons/default_volume.svg index a87abf095..d2b004bfa 100644 --- a/activities/3DVolume.activity/icons/default_volume.svg +++ b/activities/3DVolume.activity/icons/default_volume.svg @@ -1,2 +1,66 @@ - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + diff --git a/activities/3DVolume.activity/icons/number_volume.svg b/activities/3DVolume.activity/icons/number_volume.svg index 9dfe31253..311a9111c 100644 --- a/activities/3DVolume.activity/icons/number_volume.svg +++ b/activities/3DVolume.activity/icons/number_volume.svg @@ -1,2 +1,175 @@ - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + 1 + + + diff --git a/activities/3DVolume.activity/icons/transparent_volume.svg b/activities/3DVolume.activity/icons/transparent_volume.svg index c3d3e6146..8c612e1a3 100644 --- a/activities/3DVolume.activity/icons/transparent_volume.svg +++ b/activities/3DVolume.activity/icons/transparent_volume.svg @@ -1,4 +1,62 @@ - - - + + + + + + image/svg+xml + + + + + + + + \ No newline at end of file diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 68a0fdffc..b5ae73c83 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -713,43 +713,6 @@ define([ // } // }) - // document - // .querySelector('.tetra .plus-button') - // .addEventListener('click', () => { - // updateDice('tetra', 1) - // createTetrahedron() - // if (presence) { - // presence.sendMessage(presence.getSharedInfo().id, { - // user: presence.getUserInfo(), - // content: { - // shape: 'tetra', - // color: currentenv.user.colorvalue.fill, - // ifTransparent: toggleTransparent, - // ifNumbers: showNumbers, - // }, - // }) - // } - // }) - - // document - // .querySelector('.octa .plus-button') - // .addEventListener('click', () => { - // updateDice('octa', 1) - // createOctahedron() - // if (presence) { - // presence.sendMessage(presence.getSharedInfo().id, { - // user: presence.getUserInfo(), - // content: { - // shape: 'octa', - // color: currentenv.user.colorvalue.fill, - // ifTransparent: toggleTransparent, - // ifNumbers: showNumbers, - // }, - // }) - // } - // }) - - // const totalScoreElement = document.getElementById('score') const lastRollElement = document.getElementById("roll"); // Function to update the elements @@ -932,6 +895,8 @@ define([ } if (num == 0) { lastRollElement.textContent = ""; + lastRoll = "" + presentScore = 0 } } } @@ -1027,7 +992,7 @@ define([ const groundMesh = new THREE.Mesh(groundGeo, groundMat); groundMesh.receiveShadow = true; - groundMesh.material.color.setHex(0xd3d3d3); + groundMesh.material.color.setHex(0x656565); scene.add(groundMesh); const groundPhysMat = new CANNON.Material(); @@ -1792,7 +1757,7 @@ define([ let decaGeometry = new THREE.PolyhedronGeometry(...args); if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); + let tileDimension = new THREE.Vector2(3, 4); let tileSize = 512; let g = decaGeometry; @@ -1804,10 +1769,10 @@ define([ ctx.fillRect(0, 0, c.width, c.height); let baseUVs = [ - [0.1, 0.25], // bt - [0.933, 0.25], //br - [0.5, 1], // tl - [0.5, 0.15], + [0.67, 1], //br + [0, 0.5], // bt + [1, 0.5], // tl + [0.67, 0], ].map((p) => { return new THREE.Vector2(...p); }); @@ -2212,6 +2177,9 @@ define([ friction: -1, restitution: 5, }); + dodecahedronBody.sleepSpeedLimit = 0.5 + dodecahedronBody.sleepTimeLimit = 3 + console.log(dodecahedronBody) if (tempShowNumbers) { dodecahedronBody.addEventListener("sleep", () => { sleepCounter++; @@ -2650,8 +2618,8 @@ define([ { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 4 }, { vector: new THREE.Vector3(-phi, 0, -1).normalize(), face: 12 }, - { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, - { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 1 }, + { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 1 }, + { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 2 }, { vector: new THREE.Vector3(1, -phi, phi).normalize(), face: 15 }, { vector: new THREE.Vector3(-1, -phi, phi).normalize(), face: 16 }, { vector: new THREE.Vector3(1, phi, -phi).normalize(), face: 10 }, @@ -2662,6 +2630,7 @@ define([ let closestFaces = []; let minDifference = Infinity; + console.log(body.rotation) for (const faceVector of faceVectors) { faceVector.vector.applyEuler(body.rotation); @@ -2788,8 +2757,7 @@ define([ break; case "default": groundMesh.material.needsUpdate = true; - groundMesh.material.color.setHex(0xd3d3d3); - console.log(groundMesh.material.color); + groundMesh.material.color.setHex(0x656565); groundMesh.material.wireframe = false; groundMesh.material.map = null; groundBody.material.friction = 1; diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html index 7ebe78215..007534d68 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.html +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -2,5 +2,4 @@ -
\ No newline at end of file From e1dc9516dd704b076ac4271f7b1b8da5ada288e6 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Wed, 26 Jun 2024 21:18:14 +0530 Subject: [PATCH 06/60] added presence base --- activities/3DVolume.activity/js/activity.js | 187 ++++++++++++-------- 1 file changed, 117 insertions(+), 70 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index b5ae73c83..1c9a43310 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -33,7 +33,9 @@ define([ ); paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper); function changeBoardBackgroundHelper(selectedBoard) { + console.log("changing background from base func") if (presence) { + console.log("presence is true sending message now") presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "changeBg", @@ -115,6 +117,7 @@ define([ throwDice(); } if (msg.action == "changeBg") { + console.log("changeBG is the action now"); changeBoardBackground(msg.content); } if (msg.action == "resetScore") { @@ -123,6 +126,10 @@ define([ lastRoll = ""; lastRollElement.textContent = ""; } + if (msg.action == "remove") { + console.log("removing action received") + remove(msg.content); + } switch (msg.content.shape) { case "cube": createCube( @@ -132,7 +139,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; case "octa": @@ -143,7 +153,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; case "tetra": @@ -155,7 +168,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; case "dodeca": @@ -166,7 +182,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; case "deca": @@ -177,7 +196,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; @@ -189,7 +211,10 @@ define([ msg.content.xCoordinateShared, msg.content.zCoordinateShared, msg.content.ifImage, - msg.content.sharedImageData + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor ); break; } @@ -759,13 +784,16 @@ define([ user: presence.getUserInfo(), content: { shape: "cube", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -776,13 +804,16 @@ define([ user: presence.getUserInfo(), content: { shape: "tetra", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -793,13 +824,16 @@ define([ user: presence.getUserInfo(), content: { shape: "deca", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -810,13 +844,16 @@ define([ user: presence.getUserInfo(), content: { shape: "dodeca", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -827,13 +864,16 @@ define([ user: presence.getUserInfo(), content: { shape: "icosa", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -845,13 +885,16 @@ define([ user: presence.getUserInfo(), content: { shape: "octa", - color: currentenv.user.colorvalue.fill, + color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, }, }); } @@ -862,27 +905,58 @@ define([ } function onRemoveClick(event) { if (removeVolume) { - // Calculate mouse position in normalized device coordinates - var rect = renderer.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + if (!presence) { + // Calculate mouse position in normalized device coordinates + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera); + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera); - // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children); + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children); - var intersectedObject = intersects[0]?.object; - if (intersectedObject.geometry.type == "PlaneGeometry") { - return; + var intersectedObject = intersects[0]?.object; + if (intersectedObject?.geometry.type == "PlaneGeometry") { + return; + } + remove(intersectedObject) + } else { + // Calculate mouse position in normalized device coordinates + console.log("presence remove") + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera); + + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children); + + var intersectedObject = intersects[0]?.object; + if (intersectedObject.geometry.type == "PlaneGeometry") { + return; + } + remove(intersectedObject) + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "remove", + content: intersectedObject, + }); } - let num = 0; + } + } + function remove(intersectedObject) { + let num = 0; for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][3]) { num++; } } + console.log(intersectedObject) + console.log(diceArray[0][0]) for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][0] == intersectedObject) { if (diceArray[i][3]) { @@ -895,10 +969,9 @@ define([ } if (num == 0) { lastRollElement.textContent = ""; - lastRoll = "" - presentScore = 0 + lastRoll = ""; + presentScore = 0; } - } } var presence = null; @@ -915,7 +988,7 @@ define([ return; } network.createSharedActivity( - "org.sugarlabs.Volume", + "org.sugarlabs.3DVolume", function (groupId) { console.log("Activity shared"); } @@ -1062,9 +1135,9 @@ define([ const rollingForceMagnitude = 2; // Adjust for desired intensity const randomDirection = new CANNON.Vec3( - Math.random() - 0.5, // Random x-axis value between -0.5 and 0.5 - Math.random() * 0.2 - 0.1, // Random y-axis value between -0.1 and 0.1 (slightly tilted) - Math.random() - 0.5 // Random z-axis value between -0.5 and 0.5 + 0.3, // Random x-axis value between -0.5 and 0.5 + 0.05, // Random y-axis value between -0.1 and 0.1 (slightly tilted) + 0.3 // Random z-axis value between -0.5 and 0.5 ); randomDirection.normalize(); // Normalize to unit vector @@ -1330,11 +1403,7 @@ define([ }); } world.addBody(tetrahedronBody); - tetrahedronBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); tetrahedronBody.applyImpulse(offset, rollingForce); tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh tetrahedron.quaternion.copy(tetrahedronBody.quaternion); @@ -1508,11 +1577,7 @@ define([ } world.addBody(octahedronBody); - octahedronBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); octahedronBody.applyImpulse(offset, rollingForce); octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh octahedron.quaternion.copy(octahedronBody.quaternion); @@ -1663,11 +1728,7 @@ define([ }); } - boxBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + boxBody.angularVelocity.set(0.5, 0.5, 0.5); boxBody.applyImpulse(offset, rollingForce); // what will happen when the two bodies touch @@ -1899,11 +1960,7 @@ define([ }); } world.addBody(decahedronBody); - decahedronBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); decahedronBody.applyImpulse(offset, rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); @@ -2177,9 +2234,9 @@ define([ friction: -1, restitution: 5, }); - dodecahedronBody.sleepSpeedLimit = 0.5 - dodecahedronBody.sleepTimeLimit = 3 - console.log(dodecahedronBody) + dodecahedronBody.sleepSpeedLimit = 0.5; + dodecahedronBody.sleepTimeLimit = 3; + console.log(dodecahedronBody); if (tempShowNumbers) { dodecahedronBody.addEventListener("sleep", () => { sleepCounter++; @@ -2187,11 +2244,7 @@ define([ }); } world.addBody(dodecahedronBody); - dodecahedronBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); dodecahedronBody.applyImpulse(offset, rollingForce); dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh dodecahedron.quaternion.copy(dodecahedronBody.quaternion); @@ -2397,11 +2450,7 @@ define([ } icosahedronBody.sleepSpeedLimit = 0.2; // ugly hack to bypass the vibration issue in the ConvextPolyhedron class of cannon-es, port phyiscs engine to rapier before removing this :) world.addBody(icosahedronBody); - icosahedronBody.angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); icosahedronBody.applyImpulse(offset, rollingForce); icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh icosahedron.quaternion.copy(icosahedronBody.quaternion); @@ -2433,11 +2482,7 @@ define([ lastRoll = ""; presentScore = 0; for (let i = 0; i < diceArray.length; i++) { - diceArray[i][1].angularVelocity.set( - Math.random() - 0.5, - Math.random(), - Math.random() - 0.5 - ); + diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); diceArray[i][1].applyImpulse(offset, rollingForce); diceArray[i][1].position.set(0, 10, 0); } @@ -2630,7 +2675,7 @@ define([ let closestFaces = []; let minDifference = Infinity; - console.log(body.rotation) + console.log(body.rotation); for (const faceVector of faceVectors) { faceVector.vector.applyEuler(body.rotation); @@ -2731,6 +2776,8 @@ define([ // } function changeBoardBackground(selectedBoard) { + console.log("changing bg now") + console.log(selectedBoard) let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { case "green-board": From eb1b624cd0b7c5c03fb32869daaab47df763be99 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Thu, 27 Jun 2024 13:02:02 +0530 Subject: [PATCH 07/60] presence + journal --- activities/3DVolume.activity/js/activity.js | 173 ++++++++++++++++---- 1 file changed, 145 insertions(+), 28 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 1c9a43310..709bbe8fc 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -33,9 +33,8 @@ define([ ); paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper); function changeBoardBackgroundHelper(selectedBoard) { - console.log("changing background from base func") + console.log("changing background from base func"); if (presence) { - console.log("presence is true sending message now") presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "changeBg", @@ -113,11 +112,108 @@ define([ if (presence.getUserInfo().networkId === msg.user.networkId) { return; } + if (msg.action == "init") { + data = msg.content + console.log(data); + for (let i = 0; i < data.length; i++) { + let fillColorStored = data[i][3]; + let textColorStored = data[i][4]; + switch (data[i][0]) { + case "cube": + createCube( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "octa": + createOctahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "tetra": + createTetrahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "deca": + createDecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "dodeca": + createDodecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "icosa": + createIcosahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + default: + // Default case (optional): Handle unexpected values + console.log(`Unexpected shape: ${data[i][0]}`); + break; + } + } + } if (msg.action == "throw") { throwDice(); } if (msg.action == "changeBg") { - console.log("changeBG is the action now"); changeBoardBackground(msg.content); } if (msg.action == "resetScore") { @@ -127,7 +223,7 @@ define([ lastRollElement.textContent = ""; } if (msg.action == "remove") { - console.log("removing action received") + console.log("removing action received"); remove(msg.content); } switch (msg.content.shape) { @@ -921,10 +1017,10 @@ define([ if (intersectedObject?.geometry.type == "PlaneGeometry") { return; } - remove(intersectedObject) + remove(intersectedObject); } else { // Calculate mouse position in normalized device coordinates - console.log("presence remove") + console.log("presence remove"); var rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; @@ -939,7 +1035,7 @@ define([ if (intersectedObject.geometry.type == "PlaneGeometry") { return; } - remove(intersectedObject) + remove(intersectedObject); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "remove", @@ -950,28 +1046,28 @@ define([ } function remove(intersectedObject) { let num = 0; - for (let i = 0; i < diceArray.length; i++) { - if (diceArray[i][3]) { - num++; - } + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][3]) { + num++; } - console.log(intersectedObject) - console.log(diceArray[0][0]) - for (let i = 0; i < diceArray.length; i++) { - if (diceArray[i][0] == intersectedObject) { - if (diceArray[i][3]) { - num--; - } - world.removeBody(diceArray[i][1]); - scene.remove(diceArray[i][0]); - diceArray.splice(i, 1); + } + console.log(intersectedObject); + console.log(diceArray[0][0]); + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][0] == intersectedObject) { + if (diceArray[i][3]) { + num--; } + world.removeBody(diceArray[i][1]); + scene.remove(diceArray[i][0]); + diceArray.splice(i, 1); } - if (num == 0) { - lastRollElement.textContent = ""; - lastRoll = ""; - presentScore = 0; - } + } + if (num == 0) { + lastRollElement.textContent = ""; + lastRoll = ""; + presentScore = 0; + } } var presence = null; @@ -994,9 +1090,30 @@ define([ } ); network.onDataReceived(onNetworkDataReceived); + network.onSharedActivityUserChanged(onNetworkUserChanged); }); }); + var onNetworkUserChanged = function (msg) { + let presenceDiceArray = []; + for (let i = 0; i < diceArray.length; i++) { + presenceDiceArray.push([ + diceArray[i][2], + diceArray[i][1].position, + diceArray[i][1].quaternion, + diceArray[i][5], + diceArray[i][6], + diceArray[i][3], + diceArray[i][4], + ]); + } + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "init", + content: presenceDiceArray, + }); + }; + // document // .querySelector('#reset-button') // .addEventListener('click', function () { @@ -2776,8 +2893,8 @@ define([ // } function changeBoardBackground(selectedBoard) { - console.log("changing bg now") - console.log(selectedBoard) + console.log("changing bg now"); + console.log(selectedBoard); let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { case "green-board": From 7afd69fd9cf6da9fc72ebc563f505bbcaab34239 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 28 Jun 2024 13:27:40 +0530 Subject: [PATCH 08/60] remove + presence --- activities/3DVolume.activity/js/activity.js | 36 +++++++-------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 709bbe8fc..89f6a491f 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -223,8 +223,15 @@ define([ lastRollElement.textContent = ""; } if (msg.action == "remove") { - console.log("removing action received"); - remove(msg.content); + raycaster.setFromCamera(msg.content, camera) + var intersects = raycaster.intersectObjects(scene.children); + + var intersectedObject = intersects[0]?.object; + if (intersectedObject?.geometry.type == "PlaneGeometry") { + return; + } + remove(intersectedObject); + } switch (msg.content.shape) { case "cube": @@ -1001,7 +1008,6 @@ define([ } function onRemoveClick(event) { if (removeVolume) { - if (!presence) { // Calculate mouse position in normalized device coordinates var rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; @@ -1017,31 +1023,13 @@ define([ if (intersectedObject?.geometry.type == "PlaneGeometry") { return; } - remove(intersectedObject); - } else { - // Calculate mouse position in normalized device coordinates - console.log("presence remove"); - var rect = renderer.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - - // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera); - - // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children); - - var intersectedObject = intersects[0]?.object; - if (intersectedObject.geometry.type == "PlaneGeometry") { - return; - } - remove(intersectedObject); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "remove", - content: intersectedObject, + content: mouse, }); - } + + remove(intersectedObject); } } function remove(intersectedObject) { From 363392de0567b420669520ee8f24a5ed961bb22a Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 30 Jun 2024 21:29:56 +0530 Subject: [PATCH 09/60] changes --- activities/3DVolume.activity/css/activity.css | 5 - activities/3DVolume.activity/js/activity.js | 680 ++++++++---------- 2 files changed, 282 insertions(+), 403 deletions(-) diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index a1e732afc..feff77be9 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -53,17 +53,14 @@ body { #main-toolbar #bg-button { background-image: url(../icons/insert-picture.svg); outline: none; - background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ } #volume-button { background-image: url(../icons/default_volume.svg); outline: none; - background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ } #main-toolbar #clear-button { background-image: url(../icons/minus.svg); - background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ outline: none; } #clear-button.active { @@ -94,7 +91,6 @@ body { background-repeat: no-repeat; background-image: url(../icons/glass.svg); outline: none; - background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ } #throw-button { @@ -103,7 +99,6 @@ body { background-size: cover; background-repeat: no-repeat; background-image: url(../icons/throw.svg); - background-size: calc(100% - 10px) calc(100% - 10px); /* Adjust the values as needed */ outline: none; } #transparent-button { diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 89f6a491f..ef23966a5 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -113,7 +113,7 @@ define([ return; } if (msg.action == "init") { - data = msg.content + data = msg.content; console.log(data); for (let i = 0; i < data.length; i++) { let fillColorStored = data[i][3]; @@ -223,7 +223,7 @@ define([ lastRollElement.textContent = ""; } if (msg.action == "remove") { - raycaster.setFromCamera(msg.content, camera) + raycaster.setFromCamera(msg.content, camera); var intersects = raycaster.intersectObjects(scene.children); var intersectedObject = intersects[0]?.object; @@ -231,96 +231,47 @@ define([ return; } remove(intersectedObject); - } + let createFunction = null; + switch (msg.content.shape) { case "cube": - createCube( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createCube; break; case "octa": - createOctahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createOctahedron; break; case "tetra": - console.log(msg.content.ifImage); - createTetrahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createTetrahedron; break; case "dodeca": - createDodecahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createDodecahedron; break; case "deca": - createDecahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createDecahedron; break; - case "icosa": - createIcosahedron( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); + createFunction = createIcosahedron; + break; + default: + console.error("Unknown shape: " + msg.content.shape); break; } + + if (createFunction) { + createFunction( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor + ); + } }; document @@ -367,94 +318,42 @@ define([ let textColorStored = data[i][4]; switch (data[i][0]) { case "cube": - createCube( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createCube; break; case "octa": - createOctahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createOctahedron; break; case "tetra": - createTetrahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createTetrahedron; break; case "deca": - createDecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createDecahedron; break; case "dodeca": - createDodecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createDodecahedron; break; case "icosa": - createIcosahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createIcosahedron; break; default: - // Default case (optional): Handle unexpected values console.log(`Unexpected shape: ${data[i][0]}`); break; } + + if (createFunction) { + createFunction( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + } } } }); @@ -670,31 +569,33 @@ define([ toggleTransparent = !toggleTransparent; }); - document.querySelector("#default-button").addEventListener("click", () => { - var defaultButton = document.getElementById("default-button"); - // Toggle the 'active' class on the clear button - defaultButton.classList.toggle("active"); - document.getElementById("volume-button").style.backgroundImage = - "url(icons/cube_solid.svg)"; + document + .querySelector("#default-button") + .addEventListener("click", (event) => { + var defaultButton = document.getElementById("default-button"); + // Toggle the 'active' class on the clear button + defaultButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/default_volume.svg)"; - if (toggleTransparent) { - var transparentButton = document.getElementById("transparent-button"); - transparentButton.classList.toggle("active"); - toggleTransparent = !toggleTransparent; - } + if (toggleTransparent) { + var transparentButton = document.getElementById("transparent-button"); + transparentButton.classList.toggle("active"); + toggleTransparent = !toggleTransparent; + } - if (showNumbers) { - var numberButton = document.getElementById("number-button"); - numberButton.classList.toggle("active"); - showNumbers = !showNumbers; - } - if (showImage) { - var imageButton1 = document.getElementById("image-button"); - imageButton1.classList.toggle("active"); - showImage = !showImage; - } - defaultVolume = !defaultVolume; - }); + if (showNumbers) { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + showNumbers = !showNumbers; + } + if (showImage) { + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; + } + defaultVolume = !defaultVolume; + }); let addShape = { cube: true, @@ -880,114 +781,37 @@ define([ xCoordinate = intersects[i].point.x; zCoordinate = intersects[i].point.z; + let createFunction = null; + let shapeType = null; + if (addShape["cube"]) { - createCube(); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: "cube", - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } + createFunction = createCube; + shapeType = "cube"; } else if (addShape["tetra"]) { - createTetrahedron(); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: "tetra", - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } + createFunction = createTetrahedron; + shapeType = "tetra"; } else if (addShape["deca"]) { - createDecahedron(); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: "deca", - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } + createFunction = createDecahedron; + shapeType = "deca"; } else if (addShape["dodeca"]) { - createDodecahedron(); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: "dodeca", - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } + createFunction = createDodecahedron; + shapeType = "dodeca"; } else if (addShape["icosa"]) { - createIcosahedron(); + createFunction = createIcosahedron; + shapeType = "icosa"; + } else if (addShape["octa"]) { + createFunction = createOctahedron; + shapeType = "octa"; + } + + if (createFunction) { + createFunction(); + if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), content: { - shape: "icosa", - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } - } else { - createOctahedron(); - if (addShape["octa"]) { - console.log(showImage); - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: "octa", + shape: shapeType, color: presentColor, ifTransparent: toggleTransparent, ifNumbers: showNumbers, @@ -1008,28 +832,30 @@ define([ } function onRemoveClick(event) { if (removeVolume) { - // Calculate mouse position in normalized device coordinates - var rect = renderer.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + // Calculate mouse position in normalized device coordinates + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera); + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera); - // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children); + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children); - var intersectedObject = intersects[0]?.object; - if (intersectedObject?.geometry.type == "PlaneGeometry") { - return; - } + var intersectedObject = intersects[0]?.object; + if (intersectedObject?.geometry.type == "PlaneGeometry") { + return; + } + if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "remove", content: mouse, }); - - remove(intersectedObject); + } + + remove(intersectedObject); } } function remove(intersectedObject) { @@ -1039,11 +865,51 @@ define([ num++; } } - console.log(intersectedObject); - console.log(diceArray[0][0]); for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][0] == intersectedObject) { if (diceArray[i][3]) { + let score; + switch (diceArray[i][2]) { + case "cube": + score = getCubeScore(diceArray[i][0], true); + break; + case "icosa": + score = getIcosaScore(diceArray[i][0], true); + break; + case "deca": + score = getDecaScore(diceArray[i][0], true); + break; + case "dodeca": + score = getDodecaScore(diceArray[i][0], true); + break; + case "octa": + score = getOctaScore(diceArray[i][0], true); + break; + case "tetra": + score = getTetraScore(diceArray[i][0], true); + break; + default: + console.log(`Unknown type: ${diceArray[i][3]}`); + continue; + } + presentScore = presentScore - score; + console.log(presentScore); + + let scoresArray = lastRoll.split(" + "); + + // Find the index of the first occurrence of the score to remove + let indexToRemove = scoresArray.indexOf(score.toString()); + + // If the score is found, remove it + if (indexToRemove !== -1) { + scoresArray.splice(indexToRemove, 1); + } + + // Join the remaining scores back into a string + lastRoll = scoresArray.join(" + "); + updateElements(); + console.log(lastRoll); + console.log(presentScore); num--; } world.removeBody(diceArray[i][1]); @@ -1360,7 +1226,7 @@ define([ if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 5); let tileSize = 512; - let g = new THREE.TetrahedronGeometry(2); + let g = new THREE.TetrahedronGeometry(1.7); let c = document.createElement("canvas"); let div = document.createElement("div"); @@ -1433,7 +1299,9 @@ define([ tetrahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry(2); // Size of the tetrahedron + const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry( + 1.7 + ); // Size of the tetrahedron const wireframe = new THREE.WireframeGeometry( tetrahedronTransparentGeometry ); @@ -1446,7 +1314,7 @@ define([ const line = new THREE.LineSegments(wireframe, lineMaterial); tetrahedron = line; } else if (tempImage) { - const boxGeo = new THREE.TetrahedronGeometry(2); + const boxGeo = new THREE.TetrahedronGeometry(1.7); const texture = new THREE.TextureLoader().load( sharedImageData != null ? sharedImageData : imageData @@ -1458,7 +1326,7 @@ define([ // Create cube mesh with the material tetrahedron = new THREE.Mesh(boxGeo, material); } else { - const tetrahedronGeometry = new THREE.TetrahedronGeometry(2); // Size of the tetrahedron + const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron const tetraMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, @@ -1550,7 +1418,7 @@ define([ if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 5); let tileSize = 512; - let g = new THREE.OctahedronGeometry(2); + let g = new THREE.OctahedronGeometry(1.6); let c = document.createElement("canvas"); c.width = tileSize * tileDimension.x; @@ -1602,7 +1470,7 @@ define([ octahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const octahedronTransparentGeometry = new THREE.OctahedronGeometry(2); // Size of the octahedron + const octahedronTransparentGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron const wireframe = new THREE.WireframeGeometry( octahedronTransparentGeometry ); @@ -1627,7 +1495,7 @@ define([ // Create cube mesh with the material octahedron = new THREE.Mesh(octahedronGeometry, material); } else { - const octahedronGeometry = new THREE.OctahedronGeometry(2); // Size of the octahedron + const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron const octaMaterial = new THREE.MeshPhongMaterial({ color: sharedColor != null ? sharedColor : presentColor, @@ -1638,31 +1506,34 @@ define([ octahedron.castShadow = true; scene.add(octahedron); + const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) + const verticesOcta = [ - new CANNON.Vec3(2, 0, 0), // Vertex 1 (right) - new CANNON.Vec3(-2, 0, 0), // Vertex 2 (top) - new CANNON.Vec3(0, 2, 0), // Vertex 3 (left) - new CANNON.Vec3(0, -2, 0), // Vertex 4 (front) - new CANNON.Vec3(0, 0, 2), // Vertex 4 (front) - new CANNON.Vec3(0, 0, -2), // Vertex 4 (front) + new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) + new CANNON.Vec3(-2 * scaleFactor, 0, 0), // Vertex 2 (left) + new CANNON.Vec3(0, 2 * scaleFactor, 0), // Vertex 3 (top) + new CANNON.Vec3(0, -2 * scaleFactor, 0), // Vertex 4 (bottom) + new CANNON.Vec3(0, 0, 2 * scaleFactor), // Vertex 5 (front) + new CANNON.Vec3(0, 0, -2 * scaleFactor), // Vertex 6 (back) ]; - // Define the faces of the tetrahedron (counter-clockwise order) + // Define the faces of the octahedron (counter-clockwise order) const facesOcta = [ - [0, 2, 4], // Triangle 1 (right, top, left) - [0, 4, 3], // Triangle 2 (right, front, top) - [0, 3, 5], // Triangle 3 (top, front, left) - [0, 5, 2], // Triangle 4 (left, right, front) - [1, 2, 5], // Triangle 1 (right, top, left) - [1, 5, 3], // Triangle 1 (right, top, left) - [1, 3, 4], // Triangle 1 (right, top, left) - [1, 4, 2], // Triangle 1 (right, top, left) + [0, 2, 4], // Triangle 1 (right, top, front) + [0, 4, 3], // Triangle 2 (right, front, bottom) + [0, 3, 5], // Triangle 3 (right, bottom, back) + [0, 5, 2], // Triangle 4 (right, back, top) + [1, 2, 5], // Triangle 5 (left, top, back) + [1, 5, 3], // Triangle 6 (left, back, bottom) + [1, 3, 4], // Triangle 7 (left, bottom, front) + [1, 4, 2], // Triangle 8 (left, front, top) ]; const octahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesOcta, faces: facesOcta, }); + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; @@ -1885,7 +1756,7 @@ define([ let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; const sides = 10; - const radius = 1; + const radius = 1.3; const verticesGeo = [ [0, 0, 1], [0, 0, -1], @@ -1895,7 +1766,6 @@ define([ const b = (i * Math.PI * 2) / sides; verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); } - console.log(verticesGeo); const facesGeo = [ [0, 2, 3], @@ -1923,10 +1793,11 @@ define([ let decaGeometry = new THREE.PolyhedronGeometry(...args); if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(3, 4); - let tileSize = 512; let g = decaGeometry; + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let c = document.createElement("canvas"); c.width = tileSize * tileDimension.x; c.height = tileSize * tileDimension.y; @@ -1934,19 +1805,15 @@ define([ ctx.fillStyle = tempFillColor; ctx.fillRect(0, 0, c.width, c.height); - let baseUVs = [ - [0.67, 1], //br - [0, 0.5], // bt - [1, 0.5], // tl - [0.67, 0], - ].map((p) => { - return new THREE.Vector2(...p); - }); - - // Initialize UVs array let uvs = []; - // Loop to generate UVs and draw numbers on the canvas + let baseUVs = [ + new THREE.Vector2(0.67, 1), // br + new THREE.Vector2(0, 0.5), // bt + new THREE.Vector2(1, 0.5), // tl + new THREE.Vector2(0.67, 0), // bl + ]; + for (let i = 0; i < 10; i++) { let u = i % tileDimension.x; let v = Math.floor(i / tileDimension.x); @@ -1963,14 +1830,19 @@ define([ ctx.textAlign = "center"; ctx.textBaseline = "middle"; - ctx.font = `bold 200px Arial`; + ctx.font = `bold 175px Arial`; ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i === 5) { + text += "."; + } ctx.fillText( - i + 1, + text, (u + 0.5) * tileSize, c.height - (v + 0.5) * tileSize ); } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); let tex = new THREE.CanvasTexture(c); @@ -1979,7 +1851,6 @@ define([ let m = new THREE.MeshPhongMaterial({ map: tex, }); - decahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { const decahedronTransaprentGeometry = decaGeometry; @@ -2020,18 +1891,18 @@ define([ decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y decahedron.castShadow = true; scene.add(decahedron); - console.log(decahedron); const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; + const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) const verticesCannon = []; for (let i = 0; i < verticesGeo.length; i += 3) { verticesCannon.push( new CANNON.Vec3( - verticesGeo[i], - verticesGeo[i + 1], - verticesGeo[i + 2] + verticesGeo[i] * scaleFactor, + verticesGeo[i + 1] * scaleFactor, + verticesGeo[i + 2] * scaleFactor ) ); } @@ -2041,7 +1912,7 @@ define([ facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); } - // Create a ConvexPolyhedron shape from the vertices and faces + // Create a ConvexPolyhedron shape from the scaled vertices and faces const decahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesCannon, faces: facesCannon, @@ -2391,7 +2262,7 @@ define([ if (tempShowNumbers) { let tileDimension = new THREE.Vector2(4, 5); let tileSize = 512; - let g = new THREE.IcosahedronGeometry(1.2); + let g = new THREE.IcosahedronGeometry(1.5); let c = document.createElement("canvas"); c.width = tileSize * tileDimension.x; @@ -2447,7 +2318,7 @@ define([ icosahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry( - 1.15 + 1.5 ); // Size of the Icosahedron const wireframe = new THREE.WireframeGeometry( icosahedronTransparentGeometry @@ -2473,7 +2344,7 @@ define([ // Create cube mesh with the material icosahedron = new THREE.Mesh(boxGeo, material); } else { - const icosahedronGeometry = new THREE.IcosahedronGeometry(1.2); // Size of the icosahedron + const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron const icosaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : presentColor, @@ -2546,6 +2417,9 @@ define([ friction: -1, restitution: 5, }); + icosahedronBody.sleepSpeedLimit = 0.5; + icosahedronBody.sleepTimeLimit = 3; + if (tempShowNumbers) { icosahedronBody.addEventListener("sleep", () => { console.log("icosa going to sleeep"); @@ -2553,7 +2427,6 @@ define([ getIcosaScore(icosahedron); }); } - icosahedronBody.sleepSpeedLimit = 0.2; // ugly hack to bypass the vibration issue in the ConvextPolyhedron class of cannon-es, port phyiscs engine to rapier before removing this :) world.addBody(icosahedronBody); icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); icosahedronBody.applyImpulse(offset, rollingForce); @@ -2620,7 +2493,7 @@ define([ // } } } - function getOctaScore(body) { + function getOctaScore(body, ifRemove) { const faceVectors = [ { vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis @@ -2673,11 +2546,14 @@ define([ // break // } } - lastRoll += faceVectors[minInd].face + " + "; - presentScore += faceVectors[minInd].face; - updateElements(); + if (!ifRemove) { + lastRoll += faceVectors[minInd].face + " + "; + presentScore += faceVectors[minInd].face; + updateElements(); + } + return faceVectors[minInd].face; } - function getCubeScore(body) { + function getCubeScore(body, ifRemove) { const faceVectors = [ { vector: new THREE.Vector3(1, 0, 0), @@ -2708,14 +2584,16 @@ define([ faceVector.vector.applyEuler(body.rotation); if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + } + return faceVector.face; } } } - function getTetraScore(body) { + function getTetraScore(body, ifRemove) { const faceVectors = [ { vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) @@ -2738,77 +2616,80 @@ define([ for (const faceVector of faceVectors) { faceVector.vector.applyEuler(body.rotation); if (Math.round(faceVector.vector.y) == 1) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + return faceVector.face; } } } - function getDecaScore(body) { + function getDecaScore(body, ifRemove) { console.log("getting deca"); } - function getIcosaScore(body) { + function getIcosaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; // Icosahedron face vectors const faceVectors = [ - { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 14 }, - { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 13 }, - { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 18 }, - { vector: new THREE.Vector3(0, -1, -phi).normalize(), face: 19 }, + { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, + { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, + { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, + { vector: new THREE.Vector3(0, -1, -phi).normalize(), face: 20 }, { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, - { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 7 }, + { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, { vector: new THREE.Vector3(-1, -phi, 0).normalize(), face: 17 }, - { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 9 }, + { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, - { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 4 }, - { vector: new THREE.Vector3(-phi, 0, -1).normalize(), face: 12 }, - { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 1 }, - { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 2 }, - { vector: new THREE.Vector3(1, -phi, phi).normalize(), face: 15 }, - { vector: new THREE.Vector3(-1, -phi, phi).normalize(), face: 16 }, + { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, + { vector: new THREE.Vector3(-phi, 0, -1).normalize(), face: 13 }, + { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, + { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 1 }, + { vector: new THREE.Vector3(1, -phi, phi).normalize(), face: 11 }, + { vector: new THREE.Vector3(-1, -phi, phi).normalize(), face: 12 }, { vector: new THREE.Vector3(1, phi, -phi).normalize(), face: 10 }, { vector: new THREE.Vector3(-1, phi, -phi).normalize(), face: 3 }, - { vector: new THREE.Vector3(1, -phi, -phi).normalize(), face: 11 }, - { vector: new THREE.Vector3(-1, -phi, -phi).normalize(), face: 20 }, + { vector: new THREE.Vector3(1, -phi, -phi).normalize(), face: 14 }, + { vector: new THREE.Vector3(-1, -phi, -phi).normalize(), face: 18 }, ]; - let closestFaces = []; - let minDifference = Infinity; - console.log(body.rotation); + let closestFace = null; + let closestDot = -1; // Initialize with the smallest possible dot product + + // Reference vector pointing up + let upVector = new THREE.Vector3(0, 1, 0); for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation); - console.log(faceVector.vector.y); - const difference = Math.abs(Math.abs(faceVector.vector.y) - 1); - console.log(difference + ", " + faceVector.face); - - if (difference < minDifference) { - closestFaces = [faceVector]; - minDifference = difference; - } else if (difference === minDifference) { - closestFaces.push(faceVector); + // Apply the body's quaternion to the face vector + let worldVector = faceVector.vector + .clone() + .applyQuaternion(body.quaternion); + + // Calculate the dot product with the up vector + let dot = worldVector.dot(upVector); + + // Check if this is the closest to pointing up + if (dot > closestDot) { + closestDot = dot; + closestFace = faceVector; } } - if (closestFaces.length === 1) { - const closestFace = closestFaces[0]; - console.log(`Closest face: ${closestFace.face}`); - lastRoll += closestFace.face + " + "; - presentScore += closestFace.face; - } else { - console.log("Multiple faces are equally close to being on top:"); - closestFaces.forEach((faceVector) => - console.log(`Face: ${faceVector.face}, y: ${faceVector.vector.y}`) - ); + if (closestFace) { + let faceNumber = closestFace.face; + if (!ifRemove) { + lastRoll += faceNumber + " + "; + presentScore += faceNumber; + updateElements(); + } + return faceNumber; } - - updateElements(); } // Function to calculate a face vector based on spherical coordinates @@ -2820,7 +2701,7 @@ define([ ); } - function getDodecaScore(body) { + function getDodecaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; @@ -2844,10 +2725,13 @@ define([ faceVector.vector.normalize().applyEuler(body.rotation); if (Math.round(faceVector.vector.y) === 1) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + return faceVector.face; } } } @@ -2920,7 +2804,7 @@ define([ function animate() { world.step(timeStep); - // cannonDebugger.update(); + cannonDebugger.update(); groundMesh.position.copy(groundBody.position); groundMesh.quaternion.copy(groundBody.quaternion); From e7b4e5c1eee1766d4fd7618b9fb8a312657e9e42 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 30 Jun 2024 21:36:15 +0530 Subject: [PATCH 10/60] changes --- .../js/palettes/volumepalette.html | 2 +- .../js/palettes/volumepalette.js | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html index 007534d68..836c81186 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.html +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -1,4 +1,4 @@ -
+
diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.js b/activities/3DVolume.activity/js/palettes/volumepalette.js index 4dcf35854..6eeae0b70 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.js +++ b/activities/3DVolume.activity/js/palettes/volumepalette.js @@ -1,23 +1,25 @@ define([ - 'sugar-web/graphics/palette', - 'text!activity/palettes/volumepalette.html', + "sugar-web/graphics/palette", + "text!activity/palettes/volumepalette.html", ], function (palette, template) { - var volumepalette = {} + var volumepalette = {}; volumepalette.VolumePalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText) - this.getPalette().id = 'volume-palette' + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "volume-palette"; - var containerElem = document.createElement('div') - containerElem.innerHTML = template + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; - this.setContent([containerElem]) + this.setContent([containerElem]); - - } + document.getElementById("volume-palette").addEventListener("click", () => { + this.popDown(); + }); + }; var addEventListener = function (type, listener, useCapture) { - return this.getPalette().addEventListener(type, listener, useCapture) - } + return this.getPalette().addEventListener(type, listener, useCapture); + }; volumepalette.VolumePalette.prototype = Object.create( palette.Palette.prototype, @@ -28,8 +30,8 @@ define([ configurable: true, writable: true, }, - }, - ) + } + ); - return volumepalette -}) + return volumepalette; +}); From 8c1f8db838a1a71c6e858e0a1947b356b1ba7410 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 1 Jul 2024 01:25:14 +0530 Subject: [PATCH 11/60] indentation --- activities/3DVolume.activity/js/activity.js | 5749 ++++++++++--------- 1 file changed, 2924 insertions(+), 2825 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index ef23966a5..cfdc84b40 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1,2829 +1,2928 @@ define([ - "sugar-web/activity/activity", - "sugar-web/env", - "./palettes/bgpalette", - "./palettes/volumepalette", - "./palettes/colorpalettefill", - "./palettes/colorpalettetext", - "./palettes/zoompalette", - "sugar-web/graphics/presencepalette", - "tutorial", - "sugar-web/graphics/journalchooser", - "sugar-web/datastore", + "sugar-web/activity/activity", + "sugar-web/env", + "./palettes/bgpalette", + "./palettes/volumepalette", + "./palettes/colorpalettefill", + "./palettes/colorpalettetext", + "./palettes/zoompalette", + "sugar-web/graphics/presencepalette", + "tutorial", + "sugar-web/graphics/journalchooser", + "sugar-web/datastore", ], function ( - activity, - env, - bgpalette, - volumepalette, - colorpaletteFill, - colorpaletteText, - zoompalette, - presencepalette, - tutorial, - journalchooser, - datastore + activity, + env, + bgpalette, + volumepalette, + colorpaletteFill, + colorpaletteText, + zoompalette, + presencepalette, + tutorial, + journalchooser, + datastore ) { - // Function to change the background color based on the provided string - requirejs(["domReady!"], function (doc) { - activity.setup(); - // Link presence palette - var paletteBg = new bgpalette.BgPalette( - document.getElementById("bg-button"), - undefined - ); - paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper); - function changeBoardBackgroundHelper(selectedBoard) { - console.log("changing background from base func"); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "changeBg", - content: selectedBoard, - }); - } - changeBoardBackground(selectedBoard); - } - var paletteVolume = new volumepalette.VolumePalette( - document.getElementById("volume-button"), - undefined - ); - var paletteZoom = new zoompalette.ZoomPalette( - document.getElementById("zoom-button"), - undefined - ); - var paletteColorFill = new colorpaletteFill.ColorPalette( - document.getElementById("color-button-fill"), - undefined - ); - - var paletteColorText = new colorpaletteText.ColorPalette( - document.getElementById("color-button-text"), - undefined - ); - - let presentScore = 0; - let lastRoll = ""; - let diceArray = []; - let journalDiceArray = []; - let showNumbers = false; - let showImage = false; - let imageData; - let presentColor; - let textColor = "#ffffff"; - var currentenv; - let removeVolume = false; - let transparent = false; - let toggleTransparent = false; - let defaultVolume = true; - - var defaultButton = document.getElementById("default-button"); - defaultButton.classList.toggle("active"); - - env.getEnvironment(function (err, environment) { - currentenv = environment; - - presentColor = - currentenv.user.colorvalue.fill != null - ? currentenv.user.colorvalue.fill - : presentColor; - - scene.background = new THREE.Color("#A9A9A9"); - console.log(presentColor); - - textColor = - currentenv.user.colorvalue.stroke != null - ? currentenv.user.colorvalue.stroke - : textColor; - - document.getElementById("color-button-fill").style.backgroundColor = - presentColor; - document.getElementById("color-button-text").style.backgroundColor = - textColor; - - if (environment.sharedId) { - console.log("Shared instance"); - presence = activity.getPresenceObject(function (error, network) { - network.onDataReceived(onNetworkDataReceived); - }); - } - }); - - var onNetworkDataReceived = function (msg) { - if (presence.getUserInfo().networkId === msg.user.networkId) { - return; - } - if (msg.action == "init") { - data = msg.content; - console.log(data); - for (let i = 0; i < data.length; i++) { - let fillColorStored = data[i][3]; - let textColorStored = data[i][4]; - switch (data[i][0]) { - case "cube": - createCube( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - case "octa": - createOctahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - case "tetra": - createTetrahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - case "deca": - createDecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - case "dodeca": - createDodecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - case "icosa": - createIcosahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - break; - default: - // Default case (optional): Handle unexpected values - console.log(`Unexpected shape: ${data[i][0]}`); - break; - } - } - } - if (msg.action == "throw") { - throwDice(); - } - if (msg.action == "changeBg") { - changeBoardBackground(msg.content); - } - if (msg.action == "resetScore") { - presentScore = 0; - totalScoreElement.textContent = 0; - lastRoll = ""; - lastRollElement.textContent = ""; - } - if (msg.action == "remove") { - raycaster.setFromCamera(msg.content, camera); - var intersects = raycaster.intersectObjects(scene.children); - - var intersectedObject = intersects[0]?.object; - if (intersectedObject?.geometry.type == "PlaneGeometry") { - return; - } - remove(intersectedObject); - } - let createFunction = null; - - switch (msg.content.shape) { - case "cube": - createFunction = createCube; - break; - case "octa": - createFunction = createOctahedron; - break; - case "tetra": - createFunction = createTetrahedron; - break; - case "dodeca": - createFunction = createDodecahedron; - break; - case "deca": - createFunction = createDecahedron; - break; - case "icosa": - createFunction = createIcosahedron; - break; - default: - console.error("Unknown shape: " + msg.content.shape); - break; - } - - if (createFunction) { - createFunction( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor - ); - } - }; - - document - .getElementById("stop-button") - .addEventListener("click", function (event) { - for (let i = 0; i < diceArray.length; i++) { - journalDiceArray.push([ - diceArray[i][2], - diceArray[i][1].position, - diceArray[i][1].quaternion, - diceArray[i][5], - diceArray[i][6], - diceArray[i][3], - diceArray[i][4], - ]); - } - console.log("writing..."); - var jsonData = JSON.stringify(journalDiceArray); - console.log(jsonData); - activity.getDatastoreObject().setDataAsText(jsonData); - activity.getDatastoreObject().save(function (error) { - if (error === null) { - console.log("write done."); - } else { - console.log("write failed."); - } - }); - }); - - env.getEnvironment(function (err, environment) { - currentenv = environment; - - // Load from datastore - if (!environment.objectId) { - console.log("New instance"); - } else { - activity - .getDatastoreObject() - .loadAsText(function (error, metadata, data) { - if (error == null && data != null) { - data = JSON.parse(data); - for (let i = 0; i < data.length; i++) { - let fillColorStored = data[i][3]; - let textColorStored = data[i][4]; - switch (data[i][0]) { - case "cube": - createFunction = createCube; - break; - case "octa": - createFunction = createOctahedron; - break; - case "tetra": - createFunction = createTetrahedron; - break; - case "deca": - createFunction = createDecahedron; - break; - case "dodeca": - createFunction = createDodecahedron; - break; - case "icosa": - createFunction = createIcosahedron; - break; - default: - console.log(`Unexpected shape: ${data[i][0]}`); - break; - } - - if (createFunction) { - createFunction( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); - } - } - } - }); - } - }); - - // Launch tutorial - document - .getElementById("help-button") - .addEventListener("click", function (e) { - tutorial.start(); - }); - - const redSliderFill = document.getElementById("red-slider-fill"); - const greenSliderFill = document.getElementById("green-slider-fill"); - const blueSliderFill = document.getElementById("blue-slider-fill"); - - let sliderColorFill = { r: 0, g: 0, b: 0 }; - - function rgbToHex(r, g, b) { - return ( - "#" + - ((1 << 24) + (r << 16) + (g << 8) + b) - .toString(16) - .slice(1) - .toUpperCase() - ); - } - - function hexToRgb(hex) { - let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null; - } - - function updateColorDisplayFill() { - const hexColor = rgbToHex( - sliderColorFill.r, - sliderColorFill.g, - sliderColorFill.b - ); - presentColor = hexColor; - document.getElementById("color-button-fill").style.backgroundColor = - presentColor; - } - - function updateSlidersFill(color) { - const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); - redSliderFill.value = rgb[0]; - greenSliderFill.value = rgb[1]; - blueSliderFill.value = rgb[2]; - } - - function handleSliderChangeFill() { - sliderColorFill = { - r: parseInt(redSliderFill.value), - g: parseInt(greenSliderFill.value), - b: parseInt(blueSliderFill.value), - }; - updateColorDisplayFill(); - } - - redSliderFill.addEventListener("input", handleSliderChangeFill); - greenSliderFill.addEventListener("input", handleSliderChangeFill); - blueSliderFill.addEventListener("input", handleSliderChangeFill); - - document.addEventListener("color-selected-fill", function (event) { - const selectedColorFill = event.detail.color; - presentColor = selectedColorFill; - document.getElementById("color-button-fill").style.backgroundColor = - presentColor; - updateSlidersFill(selectedColorFill); - }); - - const redSliderText = document.getElementById("red-slider-text"); - const greenSliderText = document.getElementById("green-slider-text"); - const blueSliderText = document.getElementById("blue-slider-text"); - - let sliderColorText = { r: 0, g: 0, b: 0 }; - - function updateColorDisplayText() { - const hexColor = rgbToHex( - sliderColorText.r, - sliderColorText.g, - sliderColorText.b - ); - textColor = hexColor; - document.getElementById("color-button-text").style.backgroundColor = - textColor; - } - - function updateSlidersText(color) { - const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); - redSliderText.value = rgb[0]; - greenSliderText.value = rgb[1]; - blueSliderText.value = rgb[2]; - } - - function handleSliderChangeText() { - sliderColorText = { - r: parseInt(redSliderText.value), - g: parseInt(greenSliderText.value), - b: parseInt(blueSliderText.value), - }; - updateColorDisplayText(); - } - - redSliderText.addEventListener("input", handleSliderChangeText); - greenSliderText.addEventListener("input", handleSliderChangeText); - blueSliderText.addEventListener("input", handleSliderChangeText); - - document.addEventListener("color-selected-text", function (event) { - const selectedColorText = event.detail.color; - textColor = selectedColorText; - document.getElementById("color-button-text").style.backgroundColor = - textColor; - updateSlidersText(selectedColorText); - }); - - // document.addEventListener('color-selected-fill', function (event) { - // const selectedColor = event.detail.color; - // presentColor = selectedColor; - // document.getElementById('color-button-fill').style.backgroundColor = presentColor; - // updateSlidersFill(selectedColor); - // }); - - function updateDice(type, value) { - dices[type] += value; - document.getElementById(type).innerHTML = "
" + dices[type]; - } - - document.querySelector("#throw-button").addEventListener("click", () => { - throwDice(); - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "throw", - }); - } - }); - - // Toggles the dice's transparency - // document.querySelector('#solid-button').addEventListener('click', () => { - // if (!transparent) { - // document.querySelector('#solid-button').style.backgroundImage = - // 'url(icons/cube.svg)' - // } else { - // document.querySelector('#solid-button').style.backgroundImage = - // 'url(icons/cube_solid.svg)' - // } - // transparent = !transparent - // toggleTransparency() - // }) - - // Toggles showing numbers on dice - - document.querySelector("#number-button").addEventListener("click", () => { - var numberButton = document.getElementById("number-button"); - numberButton.classList.toggle("active"); - document.getElementById("volume-button").style.backgroundImage = - "url(icons/number_volume.svg)"; - if (defaultVolume) { - var defaultButton = document.getElementById("default-button"); - defaultButton.classList.toggle("active"); - defaultVolume = !defaultVolume; - } - if (toggleTransparent) { - var transparentButton = document.getElementById("transparent-button"); - transparentButton.classList.toggle("active"); - toggleTransparent = !toggleTransparent; - } - if (showImage) { - var imageButton1 = document.getElementById("image-button"); - imageButton1.classList.toggle("active"); - showImage = !showImage; - } - showNumbers = !showNumbers; - // toggleNumbers(); - }); - - document - .querySelector("#transparent-button") - .addEventListener("click", () => { - var transparentButton = document.getElementById("transparent-button"); - // Toggle the 'active' class on the clear button - transparentButton.classList.toggle("active"); - document.getElementById("volume-button").style.backgroundImage = - "url(icons/transparent_volume.svg)"; - console.log(defaultVolume); - - if (defaultVolume) { - console.log("it is true"); - var defaultButton = document.getElementById("default-button"); - defaultButton.classList.toggle("active"); - defaultVolume = !defaultVolume; - } - - if (showNumbers) { - var numberButton = document.getElementById("number-button"); - numberButton.classList.toggle("active"); - showNumbers = !showNumbers; - } - if (showImage) { - var imageButton1 = document.getElementById("image-button"); - imageButton1.classList.toggle("active"); - showImage = !showImage; - } - toggleTransparent = !toggleTransparent; - }); - - document - .querySelector("#default-button") - .addEventListener("click", (event) => { - var defaultButton = document.getElementById("default-button"); - // Toggle the 'active' class on the clear button - defaultButton.classList.toggle("active"); - document.getElementById("volume-button").style.backgroundImage = - "url(icons/default_volume.svg)"; - - if (toggleTransparent) { - var transparentButton = document.getElementById("transparent-button"); - transparentButton.classList.toggle("active"); - toggleTransparent = !toggleTransparent; - } - - if (showNumbers) { - var numberButton = document.getElementById("number-button"); - numberButton.classList.toggle("active"); - showNumbers = !showNumbers; - } - if (showImage) { - var imageButton1 = document.getElementById("image-button"); - imageButton1.classList.toggle("active"); - showImage = !showImage; - } - defaultVolume = !defaultVolume; - }); - - let addShape = { - cube: true, - tetra: false, - octa: false, - dodeca: false, - deca: false, - icosa: false, - }; - document.getElementById("cube-button").classList.toggle("active"); - - const buttons = ["cube", "tetra", "octa", "dodeca", "deca", "icosa"]; - const clearButton = document.getElementById("clear-button"); - const removeButton = document.querySelector("#clear-button"); - const solidButton = document.querySelector("#solid-button"); - - const toggleShape = (shape) => { - buttons.forEach((btn) => { - addShape[btn] = btn === shape; - document - .getElementById(`${btn}-button`) - .classList.toggle("active", btn === shape); - }); - removeVolume = false; - removeButton.classList.remove("active"); - - if (transparent) { - transparent = false; - solidButton.style.backgroundImage = "url(icons/cube_solid.svg)"; - toggleTransparency(); - } - }; - - clearButton.addEventListener("click", () => { - clearButton.classList.toggle("active"); - removeVolume = !removeVolume; - buttons.forEach((btn) => { - addShape[btn] = false; - document.getElementById(`${btn}-button`).classList.remove("active"); - }); - }); - - removeButton.addEventListener("click", () => { - if (!removeButton.classList.contains("active")) { - buttons.forEach((btn) => { - addShape[btn] = false; - document.getElementById(`${btn}-button`).classList.remove("active"); - }); - removeVolume = true; - removeButton.classList.add("active"); - - if (transparent) { - transparent = false; - solidButton.style.backgroundImage = "url(icons/cube_solid.svg)"; - toggleTransparency(); - } - } - }); - - buttons.forEach((shape) => { - document - .getElementById(`${shape}-button`) - .addEventListener("click", () => { - if ( - !document - .getElementById(`${shape}-button`) - .classList.contains("active") - ) { - toggleShape(shape); - } - }); - }); - - // const imageButton = document.getElementById('image-button') - // document - // .getElementById('image-button') - // .addEventListener('click', function (e) { - // if (showImage) { - // showImage = !showImage - // imageButton.classList.toggle('active') - // console.log('doing stuff onw') - // return - // } - // journalchooser.show( - // function (entry) { - // // No selection - // if (!entry) { - // return - // } - // // Get object content - // imageButton.classList.add('active') - // showImage = !showImage - - // if (toggleTransparent) { - // var transparentButton = - // document.getElementById('transparent-button') - // transparentButton.classList.toggle('active') - // toggleTransparent = !toggleTransparent - // } - - // if (showNumbers) { - // var numberButton = document.getElementById('number-button') - // numberButton.classList.toggle('active') - // showNumbers = !showNumbers - // } - - // var dataentry = new datastore.DatastoreObject(entry.objectId) - // dataentry.loadAsText(function (err, metadata, data) { - // imageData = data - // console.log(data); - // // if (addCube) { - // // console.log(data) - // // imageData = data; - // // console.log(imageData) - // // createCube() - // // } - // // if (addTetra) { - // // } - // // if (addOcta) { - // // } - // }) - // }, - // { mimetype: 'image/png' }, - // { mimetype: 'image/jpeg' }, - // ) - // }) - - // Event listeners - // document - // .querySelector('.cube .plus-button') - // .addEventListener('click', () => { - // updateDice('cube', 1) - // createCube() - // if (presence) { - // presence.sendMessage(presence.getSharedInfo().id, { - // user: presence.getUserInfo(), - // content: { - // shape: 'cube', - // color: currentenv.user.colorvalue.fill, - // ifTransparent: toggleTransparent, - // ifNumbers: showNumbers, - // }, - // }) - // } - // }) - - const lastRollElement = document.getElementById("roll"); - - // Function to update the elements - function updateElements() { - // totalScoreElement.textContent = presentScore - lastRollElement.textContent = - lastRoll.substring(0, lastRoll.length - 2) + "= " + presentScore; - } - - const renderer = new THREE.WebGLRenderer({ - antialias: true, - alpha: true, - }); - renderer.shadowMap.enabled = true; - - let xCoordinate, zCoordinate, yCoordinate; - const raycaster = new THREE.Raycaster(); - const mouse = new THREE.Vector2(); - document.querySelector("body").addEventListener("click", onRemoveClick); - document.querySelector("body").addEventListener("click", onAddClick); - - function onAddClick(event) { - if (!removeVolume) { - var rect = renderer.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - - // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera); - - // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children); - - for (let i = 0; i < intersects.length; i++) { - var intersectedObject = intersects[i]?.object; - if (intersectedObject.geometry.type == "PlaneGeometry") { - xCoordinate = intersects[i].point.x; - zCoordinate = intersects[i].point.z; - - let createFunction = null; - let shapeType = null; - - if (addShape["cube"]) { - createFunction = createCube; - shapeType = "cube"; - } else if (addShape["tetra"]) { - createFunction = createTetrahedron; - shapeType = "tetra"; - } else if (addShape["deca"]) { - createFunction = createDecahedron; - shapeType = "deca"; - } else if (addShape["dodeca"]) { - createFunction = createDodecahedron; - shapeType = "dodeca"; - } else if (addShape["icosa"]) { - createFunction = createIcosahedron; - shapeType = "icosa"; - } else if (addShape["octa"]) { - createFunction = createOctahedron; - shapeType = "octa"; - } - - if (createFunction) { - createFunction(); - - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - content: { - shape: shapeType, - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, - xCoordinateShared: xCoordinate, - zCoordinateShared: zCoordinate, - ifImage: showImage, - sharedImageData: imageData, - yCoordinateShared: null, - quaternionShared: null, - sharedTextColor: textColor, - }, - }); - } - } - } - } - } - } - function onRemoveClick(event) { - if (removeVolume) { - // Calculate mouse position in normalized device coordinates - var rect = renderer.domElement.getBoundingClientRect(); - mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; - - // Update the picking ray with the camera and mouse position - raycaster.setFromCamera(mouse, camera); - - // Calculate objects intersecting the picking ray - var intersects = raycaster.intersectObjects(scene.children); - - var intersectedObject = intersects[0]?.object; - if (intersectedObject?.geometry.type == "PlaneGeometry") { - return; - } - if (presence) { - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "remove", - content: mouse, - }); - } - - remove(intersectedObject); - } - } - function remove(intersectedObject) { - let num = 0; - for (let i = 0; i < diceArray.length; i++) { - if (diceArray[i][3]) { - num++; - } - } - for (let i = 0; i < diceArray.length; i++) { - if (diceArray[i][0] == intersectedObject) { - if (diceArray[i][3]) { - let score; - switch (diceArray[i][2]) { - case "cube": - score = getCubeScore(diceArray[i][0], true); - break; - case "icosa": - score = getIcosaScore(diceArray[i][0], true); - break; - case "deca": - score = getDecaScore(diceArray[i][0], true); - break; - case "dodeca": - score = getDodecaScore(diceArray[i][0], true); - break; - case "octa": - score = getOctaScore(diceArray[i][0], true); - break; - case "tetra": - score = getTetraScore(diceArray[i][0], true); - break; - default: - console.log(`Unknown type: ${diceArray[i][3]}`); - continue; - } - presentScore = presentScore - score; - console.log(presentScore); - - let scoresArray = lastRoll.split(" + "); - - // Find the index of the first occurrence of the score to remove - let indexToRemove = scoresArray.indexOf(score.toString()); - - // If the score is found, remove it - if (indexToRemove !== -1) { - scoresArray.splice(indexToRemove, 1); - } - - // Join the remaining scores back into a string - lastRoll = scoresArray.join(" + "); - updateElements(); - console.log(lastRoll); - console.log(presentScore); - num--; - } - world.removeBody(diceArray[i][1]); - scene.remove(diceArray[i][0]); - diceArray.splice(i, 1); - } - } - if (num == 0) { - lastRollElement.textContent = ""; - lastRoll = ""; - presentScore = 0; - } - } - - var presence = null; - var palette = new presencepalette.PresencePalette( - document.getElementById("network-button"), - undefined - ); - palette.addEventListener("shared", function () { - palette.popDown(); - console.log("Want to share"); - presence = activity.getPresenceObject(function (error, network) { - if (error) { - console.log("Sharing error"); - return; - } - network.createSharedActivity( - "org.sugarlabs.3DVolume", - function (groupId) { - console.log("Activity shared"); - } - ); - network.onDataReceived(onNetworkDataReceived); - network.onSharedActivityUserChanged(onNetworkUserChanged); - }); - }); - - var onNetworkUserChanged = function (msg) { - let presenceDiceArray = []; - for (let i = 0; i < diceArray.length; i++) { - presenceDiceArray.push([ - diceArray[i][2], - diceArray[i][1].position, - diceArray[i][1].quaternion, - diceArray[i][5], - diceArray[i][6], - diceArray[i][3], - diceArray[i][4], - ]); - } - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "init", - content: presenceDiceArray, - }); - }; - - // document - // .querySelector('#reset-button') - // .addEventListener('click', function () { - // presentScore = 0 - // totalScoreElement.textContent = 0 - // lastRoll = '' - // lastRollElement.textContent = '' - // if (presence) { - // presence.sendMessage(presence.getSharedInfo().id, { - // user: presence.getUserInfo(), - // action: 'resetScore', - // }) - // } - // }) - let sleepCounter = 0; - renderer.setSize(window.innerWidth, window.innerHeight); - const canvas = document.getElementById("game-container"); - document.getElementById("game-container").appendChild(renderer.domElement); - - const scene = new THREE.Scene(); - scene.background = new THREE.Color(presentColor); - const light = new THREE.DirectionalLight(0xffffff, 0.4); - light.castShadow = true; - const leftLight = new THREE.DirectionalLight(0xffffff, 0.25); - leftLight.castShadow = true; - const rightLight = new THREE.DirectionalLight(0xffffff, 0.1); - rightLight.castShadow = true; - const backLight = new THREE.DirectionalLight(0xffffff, 0.1); - const bottomLight = new THREE.DirectionalLight(0xffffff, 0.1); - const topLight = new THREE.DirectionalLight(0xffffff, 0.2); - topLight.castShadow = true; - leftLight.position.set(-30, 20, -30); - rightLight.position.set(30, 20, -30); - backLight.position.set(0, 20, 30); - light.position.set(0, 20, -30); - bottomLight.position.set(0, -20, -30); - topLight.position.set(0, 10, 0); - scene.add(backLight); - scene.add(rightLight); - scene.add(leftLight); - scene.add(light); - scene.add(bottomLight); - scene.add(topLight); - - const ambientLight = new THREE.AmbientLight(0x222222); // Soft ambient lighting - scene.add(ambientLight); - - const camera = new THREE.PerspectiveCamera( - 45, - window.innerWidth / window.innerHeight, - 0.1, - 1000 - ); - - const world = new CANNON.World({ - gravity: new CANNON.Vec3(0, -9.81, 0), - }); - world.allowSleep = true; - - const groundGeo = new THREE.PlaneGeometry(30, 30); - const groundMat = new THREE.MeshPhongMaterial({ - side: THREE.DoubleSide, - wireframe: false, - }); - groundMat.needsUpdate = true; - const groundMesh = new THREE.Mesh(groundGeo, groundMat); - groundMesh.receiveShadow = true; - - groundMesh.material.color.setHex(0x656565); - - scene.add(groundMesh); - const groundPhysMat = new CANNON.Material(); - const groundWidth = 0; // Desired width of the ground - const groundDepth = 0; // Desired depth of the ground - const groundThickness = 0; - const boxWidth = groundWidth / 2; - const boxDepth = groundDepth / 2; - const boxHeight = 10; // Adjust this for desired box height - - const boxShape = new CANNON.Box( - new CANNON.Vec3(boxWidth, boxHeight / 2, boxDepth) - ); - - const groundBody = new CANNON.Body({ - shape: new CANNON.Box(new CANNON.Vec3(15, 15, 0.1)), - type: CANNON.Body.STATIC, - material: groundPhysMat, - }); - groundBody.material.friction = 1; - groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); - world.addBody(groundBody); - - const leftWallBody = new CANNON.Body({ - shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), - type: CANNON.Body.STATIC, - material: groundPhysMat, - friction: -10, - restitution: 10, - }); - world.addBody(leftWallBody); - leftWallBody.position.set(15, 0, 0); - leftWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); - - const rightWallBody = new CANNON.Body({ - shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), - type: CANNON.Body.STATIC, - material: groundPhysMat, - friction: -10, - restitution: 10, - }); - world.addBody(rightWallBody); - rightWallBody.position.set(-15, 0, 0); - rightWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); - - const backWallBody = new CANNON.Body({ - shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), - type: CANNON.Body.STATIC, - material: groundPhysMat, - friction: -10, - restitution: 10, - }); - world.addBody(backWallBody); - backWallBody.position.set(0, 0, 15); - backWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); - - const frontWallBody = new CANNON.Body({ - shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), - type: CANNON.Body.STATIC, - material: groundPhysMat, - friction: -10, - restitution: 10, - }); - world.addBody(frontWallBody); - frontWallBody.position.set(0, 0, -15); - frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); - - const rollingForceMagnitude = 2; // Adjust for desired intensity - const randomDirection = new CANNON.Vec3( - 0.3, // Random x-axis value between -0.5 and 0.5 - 0.05, // Random y-axis value between -0.1 and 0.1 (slightly tilted) - 0.3 // Random z-axis value between -0.5 and 0.5 - ); - randomDirection.normalize(); // Normalize to unit vector - - const rollingForce = randomDirection.scale(rollingForceMagnitude); - - const offset = new CANNON.Vec3(0, 0.1, 0); - - const orbit = new OrbitControls.OrbitControls(camera, renderer.domElement); - camera.position.set(0, 25, -30); - orbit.update(); - orbit.listenToKeyEvents(document.querySelector("body")); - - const goRightButton = document.querySelector("#right-button"); - const goLeftButton = document.querySelector("#left-button"); - const goUpButton = document.querySelector("#up-button"); - const goDownButton = document.querySelector("#down-button"); - - // Add click event listener to the button - goRightButton.addEventListener("click", function (event) { - orbit.rotateRight(); - event.stopPropagation(); - }); - - goLeftButton.addEventListener("click", function (event) { - orbit.rotateLeft(); - event.stopPropagation(); - }); - goUpButton.addEventListener("click", function (event) { - orbit.rotateUp(); - event.stopPropagation(); - }); - goDownButton.addEventListener("click", function (event) { - orbit.rotateDown(); - event.stopPropagation(); - }); - // Zoom code - const evt = new Event("wheel", { bubbles: true, cancelable: true }); - - const zoomInButton = document.getElementById("zoom-in-button"); - const zoomOutButton = document.getElementById("zoom-out-button"); - const zoomEqualButton = document.getElementById("zoom-equal-button"); - const zoomToButton = document.getElementById("zoom-to-button"); - - const zoomInFunction = (e) => { - const fov = getFov(); - camera.fov = clickZoom(fov, "zoomIn"); - camera.updateProjectionMatrix(); - }; - - const zoomOutFunction = (e) => { - const fov = getFov(); - camera.fov = clickZoom(fov, "zoomOut"); - camera.updateProjectionMatrix(); - }; - - const zoomEqualFunction = (e) => { - const fov = getFov(); - camera.fov = 29; - camera.updateProjectionMatrix(); - }; - - const zoomToFunction = (e) => { - const fov = getFov(); - camera.fov = 35; - camera.updateProjectionMatrix(); - }; - - const clickZoom = (value, zoomType) => { - if (value >= 20 && zoomType === "zoomIn") { - return value - 5; - } else if (value <= 75 && zoomType === "zoomOut") { - return value + 5; - } else { - return value; - } - }; - - const getFov = () => { - return Math.floor( - (2 * - Math.atan(camera.getFilmHeight() / 2 / camera.getFocalLength()) * - 180) / - Math.PI - ); - }; - - const fov = getFov(); - camera.fov = 29; - camera.updateProjectionMatrix(); - - zoomInButton.addEventListener("click", zoomInFunction); - zoomOutButton.addEventListener("click", zoomOutFunction); - zoomEqualButton.addEventListener("click", zoomEqualFunction); - zoomToButton.addEventListener("click", zoomToFunction); - - function createTetrahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let tetrahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.TetrahedronGeometry(1.7); - - let c = document.createElement("canvas"); - let div = document.createElement("div"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - let arrOfNums = [ - [2, 1, 3], - [1, 2, 4], - [3, 1, 4], - [2, 3, 4], - ]; - for (let i = 0; i < 4; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 150px Arial`; - ctx.fillStyle = tempTextColor; - // ctx.fillText( - // i + 1, - // (u + 0.5) * tileSize, - // c.height - (v + 0.5) * tileSize - // ); - let aStep = (Math.PI * 2) / 3; - let yAlign = Math.PI * 0.5; - let tileQuarter = tileSize * 0.25; - for (let j = 0; j < 3; j++) { - ctx.save(); - ctx.translate( - (u + 0.5) * tileSize + Math.cos(j * aStep - yAlign) * tileQuarter, - c.height - - (v + 0.5) * tileSize + - Math.sin(j * aStep - yAlign) * tileQuarter - ); - ctx.rotate((j * Math.PI * 2) / 3); - ctx.fillText(arrOfNums[i][j], 0, 0); - ctx.restore(); - } - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - tetrahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry( - 1.7 - ); // Size of the tetrahedron - const wireframe = new THREE.WireframeGeometry( - tetrahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - tetrahedron = line; - } else if (tempImage) { - const boxGeo = new THREE.TetrahedronGeometry(1.7); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - tetrahedron = new THREE.Mesh(boxGeo, material); - } else { - const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron - - const tetraMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial); - } - - tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - tetrahedron.castShadow = true; - scene.add(tetrahedron); - - const verticesTetra = [ - new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) - new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) - new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) - new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) - ]; - const facesTetra = [ - [2, 1, 0], // Triangle 1 (right, top, left) - [0, 3, 2], // Triangle 2 (right, front, top) - [1, 3, 0], // Triangle 3 (top, front, left) - [2, 3, 1], // Triangle 4 (left, right, front) - ]; - // Create a ConvexPolyhedron shape from the vertices and faces - const tetrahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesTetra, - faces: facesTetra, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const tetrahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: tetrahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - tetrahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getTetraScore(tetrahedron); - }); - } - world.addBody(tetrahedronBody); - tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - tetrahedronBody.applyImpulse(offset, rollingForce); - tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh - tetrahedron.quaternion.copy(tetrahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - tetrahedron.quaternion.copy(quaternionShared); - tetrahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - tetrahedron, - tetrahedronBody, - "tetra", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function createOctahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let octahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.OctahedronGeometry(1.6); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - - for (let i = 0; i < 9; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 200px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1 + (i == 5 || i == 8 ? "" : ""), - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - octahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const octahedronTransparentGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron - const wireframe = new THREE.WireframeGeometry( - octahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - octahedron = line; - } else if (tempImage) { - const octahedronGeometry = new THREE.OctahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - octahedron = new THREE.Mesh(octahedronGeometry, material); - } else { - const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron - - const octaMaterial = new THREE.MeshPhongMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial); - } - octahedron.castShadow = true; - scene.add(octahedron); - - const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) - - const verticesOcta = [ - new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) - new CANNON.Vec3(-2 * scaleFactor, 0, 0), // Vertex 2 (left) - new CANNON.Vec3(0, 2 * scaleFactor, 0), // Vertex 3 (top) - new CANNON.Vec3(0, -2 * scaleFactor, 0), // Vertex 4 (bottom) - new CANNON.Vec3(0, 0, 2 * scaleFactor), // Vertex 5 (front) - new CANNON.Vec3(0, 0, -2 * scaleFactor), // Vertex 6 (back) - ]; - - // Define the faces of the octahedron (counter-clockwise order) - const facesOcta = [ - [0, 2, 4], // Triangle 1 (right, top, front) - [0, 4, 3], // Triangle 2 (right, front, bottom) - [0, 3, 5], // Triangle 3 (right, bottom, back) - [0, 5, 2], // Triangle 4 (right, back, top) - [1, 2, 5], // Triangle 5 (left, top, back) - [1, 5, 3], // Triangle 6 (left, back, bottom) - [1, 3, 4], // Triangle 7 (left, bottom, front) - [1, 4, 2], // Triangle 8 (left, front, top) - ]; - - const octahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesOcta, - faces: facesOcta, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const octahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: octahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - octahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getOctaScore(octahedron); - }); - } - world.addBody(octahedronBody); - - octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - octahedronBody.applyImpulse(offset, rollingForce); - octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh - octahedron.quaternion.copy(octahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - octahedron.quaternion.copy(quaternionShared); - octahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - octahedron, - octahedronBody, - "octa", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function createCube( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let boxMesh; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 2); - let tileSize = 512; - let g = new THREE.BoxGeometry(2, 2, 2); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let baseUVs = [ - [0, 1], - [1, 1], - [0, 0], - [1, 0], - ].map((p) => { - return new THREE.Vector2(...p); - }); - let uvs = []; - let vTemp = new THREE.Vector2(); - let vCenter = new THREE.Vector2(0.5, 0.5); - for (let i = 0; i < 6; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - baseUVs.forEach((buv) => { - uvs.push( - (buv.x + u) / tileDimension.x, - (buv.y + v) / tileDimension.y - ); - }); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 300px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - // metalness: 0.75, - // roughness: 0.25, - }); - - boxMesh = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); - const wireframe = new THREE.WireframeGeometry(boxTransparentGeometry); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - boxMesh = line; - } else if (tempImage) { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - boxMesh = new THREE.Mesh(boxGeo, material); - } else { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - const boxMat = new THREE.MeshPhongMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - boxMesh = new THREE.Mesh(boxGeo, boxMat); - } - boxMesh.castShadow = true; - scene.add(boxMesh); - - const boxPhysmat = new CANNON.Material(); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const boxBody = new CANNON.Body({ - mass: 1, - shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), - position: new CANNON.Vec3(x, y, z), - material: boxPhysmat, - friction: 0.1, - restitution: 5, - }); - - world.addBody(boxBody); - - if (tempShowNumbers) { - boxBody.addEventListener("sleep", () => { - sleepCounter++; - getCubeScore(boxMesh); - }); - } - - boxBody.angularVelocity.set(0.5, 0.5, 0.5); - boxBody.applyImpulse(offset, rollingForce); - - // what will happen when the two bodies touch - - const groundBoxContactMat = new CANNON.ContactMaterial( - groundPhysMat, - boxPhysmat, - { friction: 0.5 } - ); - - world.addContactMaterial(groundBoxContactMat); - if (quaternionShared != null && quaternionShared != undefined) { - boxMesh.quaternion.copy(quaternionShared); - boxBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - boxMesh, - boxBody, - "cube", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - const cannonDebugger = new CannonDebugger(scene, world, { - color: 0xadd8e6, - }); - - function createDecahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let decahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - - const sides = 10; - const radius = 1.3; - const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], - ].flat(); - - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); - } - - const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], - ].flat(); - const args = [verticesGeo, facesGeo, radius, 0]; - let decaGeometry = new THREE.PolyhedronGeometry(...args); - - if (tempShowNumbers) { - let g = decaGeometry; - - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - new THREE.Vector2(0.67, 1), // br - new THREE.Vector2(0, 0.5), // bt - new THREE.Vector2(1, 0.5), // tl - new THREE.Vector2(0.67, 0), // bl - ]; - - for (let i = 0; i < 10; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 175px Arial`; - ctx.fillStyle = tempTextColor; - let text = i + 1; - if (i === 5) { - text += "."; - } - ctx.fillText( - text, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - decahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry; - const wireframe = new THREE.WireframeGeometry( - decahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - decahedron = line; - } else if (tempImage) { - const decaGeo = decaGeometry; - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - decahedron = new THREE.Mesh(decaGeo, material); - } else { - const decahedronGeometry = decaGeometry; - - const decaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); - } - - decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - decahedron.castShadow = true; - scene.add(decahedron); - - const t = (1 + Math.sqrt(5)) / 2; - const r = 1 / t; - const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) - - const verticesCannon = []; - for (let i = 0; i < verticesGeo.length; i += 3) { - verticesCannon.push( - new CANNON.Vec3( - verticesGeo[i] * scaleFactor, - verticesGeo[i + 1] * scaleFactor, - verticesGeo[i + 2] * scaleFactor - ) - ); - } - - const facesCannon = []; - for (let i = 0; i < facesGeo.length; i += 3) { - facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); - } - - // Create a ConvexPolyhedron shape from the scaled vertices and faces - const decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesCannon, - faces: facesCannon, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const decahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: decahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - decahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getDecaScore(decahedron); - }); - } - world.addBody(decahedronBody); - decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - decahedronBody.applyImpulse(offset, rollingForce); - decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh - decahedron.quaternion.copy(decahedronBody.quaternion); - - if (quaternionShared != null && quaternionShared != undefined) { - decahedron.quaternion.copy(quaternionShared); - decahedronBody.quaternion.copy(quaternionShared); - } - - diceArray.push([ - decahedron, - decahedronBody, - "deca", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function makeNumbers() { - let c = document.createElement("canvas"); - c.width = 1024; - c.height = 1024; - let ctx = c.getContext("2d"); - ctx.fillStyle = "#f0f"; - ctx.fillRect(0, 0, c.width, c.height); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillStyle = "#0f8"; - ctx.font = "bold 60px Arial"; - let step = 1024 / 10; - let start = step * 0.5; - - for (let i = 0; i < 10; i++) { - ctx.fillText(i + 1, start + step * i, 512); - } - - return new THREE.CanvasTexture(c); - } - - function createDodecahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let dodecahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid - let tileSize = 512; - let g = new THREE.DodecahedronGeometry(1.25); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - const base = new THREE.Vector2(0, 0.5); - const center = new THREE.Vector2(); - const angle = THREE.MathUtils.degToRad(72); - let baseUVs = [ - base - .clone() - .rotateAround(center, angle * 1) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 2) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 3) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 4) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 0) - .addScalar(0.5), - ]; - - for (let i = 0; i < 12; i++) { - // 12 faces for a dodecahedron - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[4].x + u) / tileDimension.x, - (baseUVs[4].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold ${tileSize / 3}px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - dodecahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const dodedodecahedronTransaprentGeometry = - new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron - const wireframe = new THREE.WireframeGeometry( - dodedodecahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - dodecahedron = line; - } else if (tempImage) { - const dodecaGeo = new THREE.DodecahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - dodecahedron = new THREE.Mesh(dodecaGeo, material); - } else { - const dodecahedronGeometry = new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron - - const dodecaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial); - } - - dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - dodecahedron.castShadow = true; - scene.add(dodecahedron); - - const t = 1.618; - const r = 0.618; - const scaleFactor = 0.75; - - const vertices = [ - new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(0, -r, -t).scale(scaleFactor), - new CANNON.Vec3(0, -r, t).scale(scaleFactor), - new CANNON.Vec3(0, r, -t).scale(scaleFactor), - new CANNON.Vec3(0, r, t).scale(scaleFactor), - new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(-r, t, 0).scale(scaleFactor), - new CANNON.Vec3(r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(r, t, 0).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(-t, 0, r).scale(scaleFactor), - new CANNON.Vec3(t, 0, r).scale(scaleFactor), - ]; - - const indices = [ - [3, 11, 7], - [3, 7, 15], - [3, 15, 13], - [7, 19, 17], - [7, 17, 6], - [7, 6, 15], - [17, 4, 8], - [17, 8, 10], - [17, 10, 6], - [8, 0, 16], - [8, 16, 2], - [8, 2, 10], - [0, 12, 1], - [0, 1, 18], - [0, 18, 16], - [6, 10, 2], - [6, 2, 13], - [6, 13, 15], - [2, 16, 18], - [2, 18, 3], - [2, 3, 13], - [18, 1, 9], - [18, 9, 11], - [18, 11, 3], - [4, 14, 12], - [4, 12, 0], - [4, 0, 8], - [11, 9, 5], - [11, 5, 19], - [11, 19, 7], - [19, 5, 14], - [19, 14, 4], - [19, 4, 17], - [1, 12, 14], - [1, 14, 5], - [1, 5, 9], - ]; - - // Create a ConvexPolyhedron shape from the vertices and faces - const dodecahedronShape = new CANNON.ConvexPolyhedron({ - vertices: vertices, - faces: indices, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const dodecahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: dodecahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - dodecahedronBody.sleepSpeedLimit = 0.5; - dodecahedronBody.sleepTimeLimit = 3; - console.log(dodecahedronBody); - if (tempShowNumbers) { - dodecahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getDodecaScore(dodecahedron); - }); - } - world.addBody(dodecahedronBody); - dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - dodecahedronBody.applyImpulse(offset, rollingForce); - dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh - dodecahedron.quaternion.copy(dodecahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - dodecahedron.quaternion.copy(quaternionShared); - dodecahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - dodecahedron, - dodecahedronBody, - "dodeca", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function createIcosahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let icosahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = sharedColor != null ? sharedColor : presentColor; - let tempTextColor = sharedTextColor != null ? sharedTextColor : textColor; - - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.IcosahedronGeometry(1.5); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - for (let i = 0; i < 20; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 175px Arial`; - ctx.fillStyle = tempTextColor; - let text = i + 1; - if (i == 5) { - text + "."; - } - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - icosahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry( - 1.5 - ); // Size of the Icosahedron - const wireframe = new THREE.WireframeGeometry( - icosahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - icosahedron = line; - } else if (tempImage) { - const boxGeo = new THREE.IcosahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - icosahedron = new THREE.Mesh(boxGeo, material); - } else { - const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron - - const icosaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - icosahedron = new THREE.Mesh(icosahedronGeometry, icosaMaterial); - } - icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - icosahedron.castShadow = true; - scene.add(icosahedron); - - // Vertices - // Vertices - const t = (1 + Math.sqrt(5)) / 2; - const scaleFactor = 0.6; - const verticesIcosa = [ - new CANNON.Vec3(-1, t, 0).scale(scaleFactor), - new CANNON.Vec3(1, t, 0).scale(scaleFactor), - new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), - new CANNON.Vec3(1, -t, 0).scale(scaleFactor), - new CANNON.Vec3(0, -1, t).scale(scaleFactor), - new CANNON.Vec3(0, 1, t).scale(scaleFactor), - new CANNON.Vec3(0, -1, -t).scale(scaleFactor), - new CANNON.Vec3(0, 1, -t).scale(scaleFactor), - new CANNON.Vec3(t, 0, -1).scale(scaleFactor), - new CANNON.Vec3(t, 0, 1).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), - new CANNON.Vec3(-t, 0, 1).scale(scaleFactor), - ]; - - // Faces - const facesIcosa = [ - [0, 11, 5], - [0, 5, 1], - [0, 1, 7], - [0, 7, 10], - [0, 10, 11], - [1, 5, 9], - [5, 11, 4], - [11, 10, 2], - [10, 7, 6], - [7, 1, 8], - [3, 9, 4], - [3, 4, 2], - [3, 2, 6], - [3, 6, 8], - [3, 8, 9], - [4, 9, 5], - [2, 4, 11], - [6, 2, 10], - [8, 6, 7], - [9, 8, 1], - ]; - - // Create a ConvexPolyhedron shape from the vertices and faces - const icosahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesIcosa, - faces: facesIcosa, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const icosahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: icosahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - icosahedronBody.sleepSpeedLimit = 0.5; - icosahedronBody.sleepTimeLimit = 3; - - if (tempShowNumbers) { - icosahedronBody.addEventListener("sleep", () => { - console.log("icosa going to sleeep"); - sleepCounter++; - getIcosaScore(icosahedron); - }); - } - world.addBody(icosahedronBody); - icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - icosahedronBody.applyImpulse(offset, rollingForce); - icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh - icosahedron.quaternion.copy(icosahedronBody.quaternion); - - if (quaternionShared != null && quaternionShared != undefined) { - icosahedron.quaternion.copy(quaternionShared); - icosahedronBody.quaternion.copy(quaternionShared); - } - - diceArray.push([ - icosahedron, - icosahedronBody, - "icosa", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - const timeStep = 1 / 20; - - function throwDice() { - for (let i = 0; i < diceArray.length; i++) { - scene.remove(diceArray[i][0]); - world.removeBody(diceArray[i][1]); - } - if (diceArray.length > 0) { - lastRoll = ""; - presentScore = 0; - for (let i = 0; i < diceArray.length; i++) { - diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); - diceArray[i][1].applyImpulse(offset, rollingForce); - diceArray[i][1].position.set(0, 10, 0); - } - for (let i = 0; i < diceArray.length; i++) { - scene.add(diceArray[i][0]); - world.addBody(diceArray[i][1]); - } - } else { - for (let i = 0; i < dices.cube; i++) { - createCube(); - } - for (let i = 0; i < dices.tetra; i++) { - createTetrahedron(); - } - for (let i = 0; i < dices.octa; i++) { - createOctahedron(); - } - for (let i = 0; i < dices.dodeca; i++) { - createDodecahedron(); - } - for (let i = 0; i < dices.deca; i++) { - createDecahedron(); - } - for (let i = 0; i < dices.icosa; i++) { - createIcosahedron(); - } - lastRoll = ""; - // if (showNumbers) { - // getScore(); - // } - } - } - function getOctaScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis - face: 4, - }, - { - vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis - face: 5, - }, - { - vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis - face: 6, - }, - { - vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis - face: 3, - }, - { - vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) - face: 1, - }, - { - vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) - face: 8, - }, - { - vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) - face: 2, - }, - { - vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) - face: 7, - }, - ]; - - let minValue = 1000000; - let minInd; - for (let i = 0; i < faceVectors.length; i++) { - let faceVector = faceVectors[i]; - faceVector.vector.applyEuler(body.rotation); - console.log(Math.abs(faceVector.vector.y)); - if (minValue > Math.abs(1 - faceVector.vector.y)) { - minValue = Math.abs(1 - faceVector.vector.y); - minInd = i; - } - // if (Math.abs(faceVector.vector.y).toString().substring(0, 1) == '1') { - // lastRoll += faceVectors[i].face + ' +' - // presentScore += faceVectors[i].face - // updateElements() - // break - // } - } - if (!ifRemove) { - lastRoll += faceVectors[minInd].face + " + "; - presentScore += faceVectors[minInd].face; - updateElements(); - } - return faceVectors[minInd].face; - } - function getCubeScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 0, 0), - face: 1, - }, - { - vector: new THREE.Vector3(-1, 0, 0), - face: 2, - }, - { - vector: new THREE.Vector3(0, 1, 0), - face: 3, - }, - { - vector: new THREE.Vector3(0, -1, 0), - face: 4, - }, - { - vector: new THREE.Vector3(0, 0, 1), - face: 5, - }, - { - vector: new THREE.Vector3(0, 0, -1), - face: 6, - }, - ]; - for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation); - - if (Math.round(faceVector.vector.y) == 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - } - return faceVector.face; - } - } - } - function getTetraScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) - face: 1, - }, - { - vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) - face: 3, - }, - { - vector: new THREE.Vector3(-1, 1, -1).normalize(), // Towards a corner (negative x, positive y, negative z) - face: 2, - }, - { - vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) - face: 4, - }, - ]; - - for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation); - if (Math.round(faceVector.vector.y) == 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - return faceVector.face; - } - } - } - - function getDecaScore(body, ifRemove) { - console.log("getting deca"); - } - - function getIcosaScore(body, ifRemove) { - // Define the golden ratio - const phi = (1 + Math.sqrt(5)) / 2; - - // Icosahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, - { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, - { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, - { vector: new THREE.Vector3(0, -1, -phi).normalize(), face: 20 }, - { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, - { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, - { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, - { vector: new THREE.Vector3(-1, -phi, 0).normalize(), face: 17 }, - { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, - { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, - { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, - { vector: new THREE.Vector3(-phi, 0, -1).normalize(), face: 13 }, - { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, - { vector: new THREE.Vector3(-1, phi, phi).normalize(), face: 1 }, - { vector: new THREE.Vector3(1, -phi, phi).normalize(), face: 11 }, - { vector: new THREE.Vector3(-1, -phi, phi).normalize(), face: 12 }, - { vector: new THREE.Vector3(1, phi, -phi).normalize(), face: 10 }, - { vector: new THREE.Vector3(-1, phi, -phi).normalize(), face: 3 }, - { vector: new THREE.Vector3(1, -phi, -phi).normalize(), face: 14 }, - { vector: new THREE.Vector3(-1, -phi, -phi).normalize(), face: 18 }, - ]; - - let closestFace = null; - let closestDot = -1; // Initialize with the smallest possible dot product - - // Reference vector pointing up - let upVector = new THREE.Vector3(0, 1, 0); - - for (const faceVector of faceVectors) { - // Apply the body's quaternion to the face vector - let worldVector = faceVector.vector - .clone() - .applyQuaternion(body.quaternion); - - // Calculate the dot product with the up vector - let dot = worldVector.dot(upVector); - - // Check if this is the closest to pointing up - if (dot > closestDot) { - closestDot = dot; - closestFace = faceVector; - } - } - - if (closestFace) { - let faceNumber = closestFace.face; - if (!ifRemove) { - lastRoll += faceNumber + " + "; - presentScore += faceNumber; - updateElements(); - } - return faceNumber; - } - } - - // Function to calculate a face vector based on spherical coordinates - function calculateFaceVector(theta, phi) { - return new THREE.Vector3( - Math.cos(theta) * Math.sin(phi), - Math.sin(theta) * Math.sin(phi), - Math.cos(phi) - ); - } - - function getDodecaScore(body, ifRemove) { - // Define the golden ratio - const phi = (1 + Math.sqrt(5)) / 2; - - // Dodecahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(1, 1, 1), face: 1 }, - { vector: new THREE.Vector3(1, 1, -1), face: 6 }, - { vector: new THREE.Vector3(1, -1, 1), face: 11 }, - { vector: new THREE.Vector3(1, -1, -1), face: 4 }, - { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, - { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, - { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, - { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, - { vector: new THREE.Vector3(0, phi, 1 / phi), face: 9 }, - { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, - { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, - { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, - ]; - - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); - - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - return faceVector.face; - } - } - } - - function toggleTransparency() { - for (let i = 0; i < diceArray.length; i++) { - if (transparent) { - leftLight.intensity = 5; - rightLight.intensity = 5; - backLight.intensity = 5; - bottomLight.intensity = 5; - } else { - leftLight.intensity = 0.1; - rightLight.intensity = 0.1; - backLight.intensity = 0.5; - bottomLight.intensity = 0.1; - } - diceArray[i][0].material.wireframe = - !diceArray[i][0].material.wireframe; - diceArray[i][0].material.transparent = - !diceArray[i][0].material.transparent; - diceArray[i][0].material.needsUpdate = true; - } - groundMesh.material.wireframe = !groundMesh.material.wireframe; - } - // function changeColors() { - // for (let i = 0; i < diceArray.length; i++) { - // diceArray[i][0].material.color?.set(presentColor); - // diceArray[i][0].material.needsUpdate = true; - // } - // } - - function changeBoardBackground(selectedBoard) { - console.log("changing bg now"); - console.log(selectedBoard); - let textureLoader = new THREE.TextureLoader(); - switch (selectedBoard) { - case "green-board": - console.log("now changing bg to green"); - textureLoader.load( - "images/grass_background.png", - function (groundTexture) { - groundMesh.material.wireframe = false; - groundMesh.material.map = groundTexture; - groundMesh.material.needsUpdate = true; - groundBody.material.friction = 5; - } - ); - break; - case "wood": - console.log("wood changing"); - textureLoader.load("images/wood.png", function (groundTexture) { - groundMesh.material.wireframe = false; - groundMesh.material.color.setHex(0xf0c592); - groundMesh.material.map = groundTexture; - groundMesh.material.needsUpdate = true; - groundBody.material.friction = 3; - }); - break; - case "default": - groundMesh.material.needsUpdate = true; - groundMesh.material.color.setHex(0x656565); - groundMesh.material.wireframe = false; - groundMesh.material.map = null; - groundBody.material.friction = 1; - break; - } - } - animate(); - - function animate() { - world.step(timeStep); - cannonDebugger.update(); - - groundMesh.position.copy(groundBody.position); - groundMesh.quaternion.copy(groundBody.quaternion); - - // Loop to merge the cannon bodies to the threejs meshes - for (let i = 0; i < diceArray.length; i++) { - diceArray[i][0]?.position?.copy(diceArray[i][1].position); - diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); - } - - renderer.render(scene, camera); - } - - renderer.setAnimationLoop(animate); - - window.addEventListener("resize", function () { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); - }); - }); + // Function to change the background color based on the provided string + requirejs(["domReady!"], function (doc) { + activity.setup(); + // Link presence palette + var paletteBg = new bgpalette.BgPalette( + document.getElementById("bg-button"), + undefined + ); + paletteBg.setBackgroundChangeCallback(changeBoardBackgroundHelper); + function changeBoardBackgroundHelper(selectedBoard) { + console.log("changing background from base func"); + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "changeBg", + content: selectedBoard, + }); + } + changeBoardBackground(selectedBoard); + } + var paletteVolume = new volumepalette.VolumePalette( + document.getElementById("volume-button"), + undefined + ); + var paletteZoom = new zoompalette.ZoomPalette( + document.getElementById("zoom-button"), + undefined + ); + var paletteColorFill = new colorpaletteFill.ColorPalette( + document.getElementById("color-button-fill"), + undefined + ); + + var paletteColorText = new colorpaletteText.ColorPalette( + document.getElementById("color-button-text"), + undefined + ); + + let presentScore = 0; + let lastRoll = ""; + let diceArray = []; + let journalDiceArray = []; + let showNumbers = false; + let showImage = false; + let imageData; + let presentColor; + let textColor = "#ffffff"; + var currentenv; + let removeVolume = false; + let transparent = false; + let toggleTransparent = false; + let defaultVolume = true; + + var defaultButton = document.getElementById("default-button"); + defaultButton.classList.toggle("active"); + + env.getEnvironment(function (err, environment) { + currentenv = environment; + + presentColor = + currentenv.user.colorvalue.fill != null + ? currentenv.user.colorvalue.fill + : presentColor; + + scene.background = new THREE.Color("#A9A9A9"); + console.log(presentColor); + + textColor = + currentenv.user.colorvalue.stroke != null + ? currentenv.user.colorvalue.stroke + : textColor; + + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; + document.getElementById("color-button-text").style.backgroundColor = + textColor; + + if (environment.sharedId) { + console.log("Shared instance"); + presence = activity.getPresenceObject(function ( + error, + network + ) { + network.onDataReceived(onNetworkDataReceived); + }); + } + }); + + var onNetworkDataReceived = function (msg) { + if (presence.getUserInfo().networkId === msg.user.networkId) { + return; + } + if (msg.action == "init") { + data = msg.content; + console.log(data); + for (let i = 0; i < data.length; i++) { + let fillColorStored = data[i][3]; + let textColorStored = data[i][4]; + switch (data[i][0]) { + case "cube": + createCube( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "octa": + createOctahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "tetra": + createTetrahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "deca": + createDecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "dodeca": + createDodecahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + case "icosa": + createIcosahedron( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + break; + default: + // Default case (optional): Handle unexpected values + console.log(`Unexpected shape: ${data[i][0]}`); + break; + } + } + } + if (msg.action == "throw") { + throwDice(); + } + if (msg.action == "changeBg") { + changeBoardBackground(msg.content); + } + if (msg.action == "resetScore") { + presentScore = 0; + totalScoreElement.textContent = 0; + lastRoll = ""; + lastRollElement.textContent = ""; + } + if (msg.action == "remove") { + raycaster.setFromCamera(msg.content, camera); + var intersects = raycaster.intersectObjects(scene.children); + + var intersectedObject = intersects[0]?.object; + if (intersectedObject?.geometry.type == "PlaneGeometry") { + return; + } + remove(intersectedObject); + } + let createFunction = null; + + switch (msg.content.shape) { + case "cube": + createFunction = createCube; + break; + case "octa": + createFunction = createOctahedron; + break; + case "tetra": + createFunction = createTetrahedron; + break; + case "dodeca": + createFunction = createDodecahedron; + break; + case "deca": + createFunction = createDecahedron; + break; + case "icosa": + createFunction = createIcosahedron; + break; + default: + console.error("Unknown shape: " + msg.content.shape); + break; + } + + if (createFunction) { + createFunction( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor + ); + } + }; + + document + .getElementById("stop-button") + .addEventListener("click", function (event) { + for (let i = 0; i < diceArray.length; i++) { + journalDiceArray.push([ + diceArray[i][2], + diceArray[i][1].position, + diceArray[i][1].quaternion, + diceArray[i][5], + diceArray[i][6], + diceArray[i][3], + diceArray[i][4], + ]); + } + console.log("writing..."); + var jsonData = JSON.stringify(journalDiceArray); + console.log(jsonData); + activity.getDatastoreObject().setDataAsText(jsonData); + activity.getDatastoreObject().save(function (error) { + if (error === null) { + console.log("write done."); + } else { + console.log("write failed."); + } + }); + }); + + env.getEnvironment(function (err, environment) { + currentenv = environment; + + // Load from datastore + if (!environment.objectId) { + console.log("New instance"); + } else { + activity + .getDatastoreObject() + .loadAsText(function (error, metadata, data) { + if (error == null && data != null) { + data = JSON.parse(data); + for (let i = 0; i < data.length; i++) { + let fillColorStored = data[i][3]; + let textColorStored = data[i][4]; + switch (data[i][0]) { + case "cube": + createFunction = createCube; + break; + case "octa": + createFunction = createOctahedron; + break; + case "tetra": + createFunction = createTetrahedron; + break; + case "deca": + createFunction = createDecahedron; + break; + case "dodeca": + createFunction = createDodecahedron; + break; + case "icosa": + createFunction = createIcosahedron; + break; + default: + console.log( + `Unexpected shape: ${data[i][0]}` + ); + break; + } + + if (createFunction) { + createFunction( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored + ); + } + } + } + }); + } + }); + + // Launch tutorial + document + .getElementById("help-button") + .addEventListener("click", function (e) { + tutorial.start(); + }); + + const redSliderFill = document.getElementById("red-slider-fill"); + const greenSliderFill = document.getElementById("green-slider-fill"); + const blueSliderFill = document.getElementById("blue-slider-fill"); + + let sliderColorFill = { r: 0, g: 0, b: 0 }; + + function rgbToHex(r, g, b) { + return ( + "#" + + ((1 << 24) + (r << 16) + (g << 8) + b) + .toString(16) + .slice(1) + .toUpperCase() + ); + } + + function hexToRgb(hex) { + let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; + } + + function updateColorDisplayFill() { + const hexColor = rgbToHex( + sliderColorFill.r, + sliderColorFill.g, + sliderColorFill.b + ); + presentColor = hexColor; + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; + } + + function updateSlidersFill(color) { + const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); + redSliderFill.value = rgb[0]; + greenSliderFill.value = rgb[1]; + blueSliderFill.value = rgb[2]; + } + + function handleSliderChangeFill() { + sliderColorFill = { + r: parseInt(redSliderFill.value), + g: parseInt(greenSliderFill.value), + b: parseInt(blueSliderFill.value), + }; + updateColorDisplayFill(); + } + + redSliderFill.addEventListener("input", handleSliderChangeFill); + greenSliderFill.addEventListener("input", handleSliderChangeFill); + blueSliderFill.addEventListener("input", handleSliderChangeFill); + + document.addEventListener("color-selected-fill", function (event) { + const selectedColorFill = event.detail.color; + presentColor = selectedColorFill; + document.getElementById("color-button-fill").style.backgroundColor = + presentColor; + updateSlidersFill(selectedColorFill); + }); + + const redSliderText = document.getElementById("red-slider-text"); + const greenSliderText = document.getElementById("green-slider-text"); + const blueSliderText = document.getElementById("blue-slider-text"); + + let sliderColorText = { r: 0, g: 0, b: 0 }; + + function updateColorDisplayText() { + const hexColor = rgbToHex( + sliderColorText.r, + sliderColorText.g, + sliderColorText.b + ); + textColor = hexColor; + document.getElementById("color-button-text").style.backgroundColor = + textColor; + } + + function updateSlidersText(color) { + const rgb = color.match(/\d+/g).map((num) => parseInt(num, 10)); + redSliderText.value = rgb[0]; + greenSliderText.value = rgb[1]; + blueSliderText.value = rgb[2]; + } + + function handleSliderChangeText() { + sliderColorText = { + r: parseInt(redSliderText.value), + g: parseInt(greenSliderText.value), + b: parseInt(blueSliderText.value), + }; + updateColorDisplayText(); + } + + redSliderText.addEventListener("input", handleSliderChangeText); + greenSliderText.addEventListener("input", handleSliderChangeText); + blueSliderText.addEventListener("input", handleSliderChangeText); + + document.addEventListener("color-selected-text", function (event) { + const selectedColorText = event.detail.color; + textColor = selectedColorText; + document.getElementById("color-button-text").style.backgroundColor = + textColor; + updateSlidersText(selectedColorText); + }); + + // document.addEventListener('color-selected-fill', function (event) { + // const selectedColor = event.detail.color; + // presentColor = selectedColor; + // document.getElementById('color-button-fill').style.backgroundColor = presentColor; + // updateSlidersFill(selectedColor); + // }); + + function updateDice(type, value) { + dices[type] += value; + document.getElementById(type).innerHTML = "
" + dices[type]; + } + + document + .querySelector("#throw-button") + .addEventListener("click", () => { + throwDice(); + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "throw", + }); + } + }); + + // Toggles the dice's transparency + // document.querySelector('#solid-button').addEventListener('click', () => { + // if (!transparent) { + // document.querySelector('#solid-button').style.backgroundImage = + // 'url(icons/cube.svg)' + // } else { + // document.querySelector('#solid-button').style.backgroundImage = + // 'url(icons/cube_solid.svg)' + // } + // transparent = !transparent + // toggleTransparency() + // }) + + // Toggles showing numbers on dice + + document + .querySelector("#number-button") + .addEventListener("click", () => { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/number_volume.svg)"; + if (defaultVolume) { + var defaultButton = + document.getElementById("default-button"); + defaultButton.classList.toggle("active"); + defaultVolume = !defaultVolume; + } + if (toggleTransparent) { + var transparentButton = + document.getElementById("transparent-button"); + transparentButton.classList.toggle("active"); + toggleTransparent = !toggleTransparent; + } + if (showImage) { + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; + } + showNumbers = !showNumbers; + // toggleNumbers(); + }); + + document + .querySelector("#transparent-button") + .addEventListener("click", () => { + var transparentButton = + document.getElementById("transparent-button"); + // Toggle the 'active' class on the clear button + transparentButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/transparent_volume.svg)"; + console.log(defaultVolume); + + if (defaultVolume) { + console.log("it is true"); + var defaultButton = + document.getElementById("default-button"); + defaultButton.classList.toggle("active"); + defaultVolume = !defaultVolume; + } + + if (showNumbers) { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + showNumbers = !showNumbers; + } + if (showImage) { + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; + } + toggleTransparent = !toggleTransparent; + }); + + document + .querySelector("#default-button") + .addEventListener("click", (event) => { + var defaultButton = document.getElementById("default-button"); + // Toggle the 'active' class on the clear button + defaultButton.classList.toggle("active"); + document.getElementById("volume-button").style.backgroundImage = + "url(icons/default_volume.svg)"; + + if (toggleTransparent) { + var transparentButton = + document.getElementById("transparent-button"); + transparentButton.classList.toggle("active"); + toggleTransparent = !toggleTransparent; + } + + if (showNumbers) { + var numberButton = document.getElementById("number-button"); + numberButton.classList.toggle("active"); + showNumbers = !showNumbers; + } + if (showImage) { + var imageButton1 = document.getElementById("image-button"); + imageButton1.classList.toggle("active"); + showImage = !showImage; + } + defaultVolume = !defaultVolume; + }); + + let addShape = { + cube: true, + tetra: false, + octa: false, + dodeca: false, + deca: false, + icosa: false, + }; + document.getElementById("cube-button").classList.toggle("active"); + + const buttons = ["cube", "tetra", "octa", "dodeca", "deca", "icosa"]; + const clearButton = document.getElementById("clear-button"); + const removeButton = document.querySelector("#clear-button"); + const solidButton = document.querySelector("#solid-button"); + + const toggleShape = (shape) => { + buttons.forEach((btn) => { + addShape[btn] = btn === shape; + document + .getElementById(`${btn}-button`) + .classList.toggle("active", btn === shape); + }); + removeVolume = false; + removeButton.classList.remove("active"); + + if (transparent) { + transparent = false; + solidButton.style.backgroundImage = "url(icons/cube_solid.svg)"; + toggleTransparency(); + } + }; + + clearButton.addEventListener("click", () => { + clearButton.classList.toggle("active"); + removeVolume = !removeVolume; + buttons.forEach((btn) => { + addShape[btn] = false; + document + .getElementById(`${btn}-button`) + .classList.remove("active"); + }); + }); + + removeButton.addEventListener("click", () => { + if (!removeButton.classList.contains("active")) { + buttons.forEach((btn) => { + addShape[btn] = false; + document + .getElementById(`${btn}-button`) + .classList.remove("active"); + }); + removeVolume = true; + removeButton.classList.add("active"); + + if (transparent) { + transparent = false; + solidButton.style.backgroundImage = + "url(icons/cube_solid.svg)"; + toggleTransparency(); + } + } + }); + + buttons.forEach((shape) => { + document + .getElementById(`${shape}-button`) + .addEventListener("click", () => { + if ( + !document + .getElementById(`${shape}-button`) + .classList.contains("active") + ) { + toggleShape(shape); + } + }); + }); + + // const imageButton = document.getElementById('image-button') + // document + // .getElementById('image-button') + // .addEventListener('click', function (e) { + // if (showImage) { + // showImage = !showImage + // imageButton.classList.toggle('active') + // console.log('doing stuff onw') + // return + // } + // journalchooser.show( + // function (entry) { + // // No selection + // if (!entry) { + // return + // } + // // Get object content + // imageButton.classList.add('active') + // showImage = !showImage + + // if (toggleTransparent) { + // var transparentButton = + // document.getElementById('transparent-button') + // transparentButton.classList.toggle('active') + // toggleTransparent = !toggleTransparent + // } + + // if (showNumbers) { + // var numberButton = document.getElementById('number-button') + // numberButton.classList.toggle('active') + // showNumbers = !showNumbers + // } + + // var dataentry = new datastore.DatastoreObject(entry.objectId) + // dataentry.loadAsText(function (err, metadata, data) { + // imageData = data + // console.log(data); + // // if (addCube) { + // // console.log(data) + // // imageData = data; + // // console.log(imageData) + // // createCube() + // // } + // // if (addTetra) { + // // } + // // if (addOcta) { + // // } + // }) + // }, + // { mimetype: 'image/png' }, + // { mimetype: 'image/jpeg' }, + // ) + // }) + + // Event listeners + // document + // .querySelector('.cube .plus-button') + // .addEventListener('click', () => { + // updateDice('cube', 1) + // createCube() + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // content: { + // shape: 'cube', + // color: currentenv.user.colorvalue.fill, + // ifTransparent: toggleTransparent, + // ifNumbers: showNumbers, + // }, + // }) + // } + // }) + + const lastRollElement = document.getElementById("roll"); + + // Function to update the elements + function updateElements() { + // totalScoreElement.textContent = presentScore + lastRollElement.textContent = + lastRoll.substring(0, lastRoll.length - 2) + + "= " + + presentScore; + } + + const renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true, + }); + renderer.shadowMap.enabled = true; + + let xCoordinate, zCoordinate, yCoordinate; + const raycaster = new THREE.Raycaster(); + const mouse = new THREE.Vector2(); + document.querySelector("body").addEventListener("click", onRemoveClick); + document.querySelector("body").addEventListener("click", onAddClick); + + function onAddClick(event) { + if (!removeVolume) { + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera); + + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children); + + for (let i = 0; i < intersects.length; i++) { + var intersectedObject = intersects[i]?.object; + if (intersectedObject.geometry.type == "PlaneGeometry") { + xCoordinate = intersects[i].point.x; + zCoordinate = intersects[i].point.z; + + let createFunction = null; + let shapeType = null; + + if (addShape["cube"]) { + createFunction = createCube; + shapeType = "cube"; + } else if (addShape["tetra"]) { + createFunction = createTetrahedron; + shapeType = "tetra"; + } else if (addShape["deca"]) { + createFunction = createDecahedron; + shapeType = "deca"; + } else if (addShape["dodeca"]) { + createFunction = createDodecahedron; + shapeType = "dodeca"; + } else if (addShape["icosa"]) { + createFunction = createIcosahedron; + shapeType = "icosa"; + } else if (addShape["octa"]) { + createFunction = createOctahedron; + shapeType = "octa"; + } + + if (createFunction) { + createFunction(); + + if (presence) { + presence.sendMessage( + presence.getSharedInfo().id, + { + user: presence.getUserInfo(), + content: { + shape: shapeType, + color: presentColor, + ifTransparent: toggleTransparent, + ifNumbers: showNumbers, + xCoordinateShared: xCoordinate, + zCoordinateShared: zCoordinate, + ifImage: showImage, + sharedImageData: imageData, + yCoordinateShared: null, + quaternionShared: null, + sharedTextColor: textColor, + }, + } + ); + } + } + } + } + } + } + function onRemoveClick(event) { + if (removeVolume) { + // Calculate mouse position in normalized device coordinates + var rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + // Update the picking ray with the camera and mouse position + raycaster.setFromCamera(mouse, camera); + + // Calculate objects intersecting the picking ray + var intersects = raycaster.intersectObjects(scene.children); + + var intersectedObject = intersects[0]?.object; + if (intersectedObject?.geometry.type == "PlaneGeometry") { + return; + } + if (presence) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "remove", + content: mouse, + }); + } + + remove(intersectedObject); + } + } + function remove(intersectedObject) { + let num = 0; + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][3]) { + num++; + } + } + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][0] == intersectedObject) { + if (diceArray[i][3]) { + let score; + switch (diceArray[i][2]) { + case "cube": + score = getCubeScore(diceArray[i][0], true); + break; + case "icosa": + score = getIcosaScore(diceArray[i][0], true); + break; + case "deca": + score = getDecaScore(diceArray[i][0], true); + break; + case "dodeca": + score = getDodecaScore(diceArray[i][0], true); + break; + case "octa": + score = getOctaScore(diceArray[i][0], true); + break; + case "tetra": + score = getTetraScore(diceArray[i][0], true); + break; + default: + console.log(`Unknown type: ${diceArray[i][3]}`); + continue; + } + presentScore = presentScore - score; + console.log(presentScore); + + let scoresArray = lastRoll.split(" + "); + + // Find the index of the first occurrence of the score to remove + let indexToRemove = scoresArray.indexOf( + score.toString() + ); + + // If the score is found, remove it + if (indexToRemove !== -1) { + scoresArray.splice(indexToRemove, 1); + } + + // Join the remaining scores back into a string + lastRoll = scoresArray.join(" + "); + updateElements(); + console.log(lastRoll); + console.log(presentScore); + num--; + } + world.removeBody(diceArray[i][1]); + scene.remove(diceArray[i][0]); + diceArray.splice(i, 1); + } + } + if (num == 0) { + lastRollElement.textContent = ""; + lastRoll = ""; + presentScore = 0; + } + } + + var presence = null; + var palette = new presencepalette.PresencePalette( + document.getElementById("network-button"), + undefined + ); + palette.addEventListener("shared", function () { + palette.popDown(); + console.log("Want to share"); + presence = activity.getPresenceObject(function (error, network) { + if (error) { + console.log("Sharing error"); + return; + } + network.createSharedActivity( + "org.sugarlabs.3DVolume", + function (groupId) { + console.log("Activity shared"); + } + ); + network.onDataReceived(onNetworkDataReceived); + network.onSharedActivityUserChanged(onNetworkUserChanged); + }); + }); + + var onNetworkUserChanged = function (msg) { + let presenceDiceArray = []; + for (let i = 0; i < diceArray.length; i++) { + presenceDiceArray.push([ + diceArray[i][2], + diceArray[i][1].position, + diceArray[i][1].quaternion, + diceArray[i][5], + diceArray[i][6], + diceArray[i][3], + diceArray[i][4], + ]); + } + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "init", + content: presenceDiceArray, + }); + }; + + // document + // .querySelector('#reset-button') + // .addEventListener('click', function () { + // presentScore = 0 + // totalScoreElement.textContent = 0 + // lastRoll = '' + // lastRollElement.textContent = '' + // if (presence) { + // presence.sendMessage(presence.getSharedInfo().id, { + // user: presence.getUserInfo(), + // action: 'resetScore', + // }) + // } + // }) + let sleepCounter = 0; + renderer.setSize(window.innerWidth, window.innerHeight); + const canvas = document.getElementById("game-container"); + document + .getElementById("game-container") + .appendChild(renderer.domElement); + + const scene = new THREE.Scene(); + scene.background = new THREE.Color(presentColor); + const light = new THREE.DirectionalLight(0xffffff, 0.4); + light.castShadow = true; + const leftLight = new THREE.DirectionalLight(0xffffff, 0.25); + leftLight.castShadow = true; + const rightLight = new THREE.DirectionalLight(0xffffff, 0.1); + rightLight.castShadow = true; + const backLight = new THREE.DirectionalLight(0xffffff, 0.1); + const bottomLight = new THREE.DirectionalLight(0xffffff, 0.1); + const topLight = new THREE.DirectionalLight(0xffffff, 0.2); + topLight.castShadow = true; + leftLight.position.set(-30, 20, -30); + rightLight.position.set(30, 20, -30); + backLight.position.set(0, 20, 30); + light.position.set(0, 20, -30); + bottomLight.position.set(0, -20, -30); + topLight.position.set(0, 10, 0); + scene.add(backLight); + scene.add(rightLight); + scene.add(leftLight); + scene.add(light); + scene.add(bottomLight); + scene.add(topLight); + + const ambientLight = new THREE.AmbientLight(0x222222); // Soft ambient lighting + scene.add(ambientLight); + + const camera = new THREE.PerspectiveCamera( + 45, + window.innerWidth / window.innerHeight, + 0.1, + 1000 + ); + + const world = new CANNON.World({ + gravity: new CANNON.Vec3(0, -9.81, 0), + }); + world.allowSleep = true; + + const groundGeo = new THREE.PlaneGeometry(30, 30); + const groundMat = new THREE.MeshPhongMaterial({ + side: THREE.DoubleSide, + wireframe: false, + }); + groundMat.needsUpdate = true; + const groundMesh = new THREE.Mesh(groundGeo, groundMat); + groundMesh.receiveShadow = true; + + groundMesh.material.color.setHex(0x656565); + + scene.add(groundMesh); + const groundPhysMat = new CANNON.Material(); + const groundWidth = 0; // Desired width of the ground + const groundDepth = 0; // Desired depth of the ground + const groundThickness = 0; + const boxWidth = groundWidth / 2; + const boxDepth = groundDepth / 2; + const boxHeight = 10; // Adjust this for desired box height + + const boxShape = new CANNON.Box( + new CANNON.Vec3(boxWidth, boxHeight / 2, boxDepth) + ); + + const groundBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + }); + groundBody.material.friction = 1; + groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); + world.addBody(groundBody); + + const leftWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }); + world.addBody(leftWallBody); + leftWallBody.position.set(15, 0, 0); + leftWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); + + const rightWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }); + world.addBody(rightWallBody); + rightWallBody.position.set(-15, 0, 0); + rightWallBody.quaternion.setFromEuler(0, -Math.PI / 2, 0); + + const backWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }); + world.addBody(backWallBody); + backWallBody.position.set(0, 0, 15); + backWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); + + const frontWallBody = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(100, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + friction: -10, + restitution: 10, + }); + world.addBody(frontWallBody); + frontWallBody.position.set(0, 0, -15); + frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); + + const rollingForceMagnitude = 2; // Adjust for desired intensity + const randomDirection = new CANNON.Vec3( + 0.3, // Random x-axis value between -0.5 and 0.5 + 0.05, // Random y-axis value between -0.1 and 0.1 (slightly tilted) + 0.3 // Random z-axis value between -0.5 and 0.5 + ); + randomDirection.normalize(); // Normalize to unit vector + + const rollingForce = randomDirection.scale(rollingForceMagnitude); + + const offset = new CANNON.Vec3(0, 0.1, 0); + + const orbit = new OrbitControls.OrbitControls( + camera, + renderer.domElement + ); + camera.position.set(0, 25, -30); + orbit.update(); + orbit.listenToKeyEvents(document.querySelector("body")); + + const goRightButton = document.querySelector("#right-button"); + const goLeftButton = document.querySelector("#left-button"); + const goUpButton = document.querySelector("#up-button"); + const goDownButton = document.querySelector("#down-button"); + + // Add click event listener to the button + goRightButton.addEventListener("click", function (event) { + orbit.rotateRight(); + event.stopPropagation(); + }); + + goLeftButton.addEventListener("click", function (event) { + orbit.rotateLeft(); + event.stopPropagation(); + }); + goUpButton.addEventListener("click", function (event) { + orbit.rotateUp(); + event.stopPropagation(); + }); + goDownButton.addEventListener("click", function (event) { + orbit.rotateDown(); + event.stopPropagation(); + }); + // Zoom code + const evt = new Event("wheel", { bubbles: true, cancelable: true }); + + const zoomInButton = document.getElementById("zoom-in-button"); + const zoomOutButton = document.getElementById("zoom-out-button"); + const zoomEqualButton = document.getElementById("zoom-equal-button"); + const zoomToButton = document.getElementById("zoom-to-button"); + + const zoomInFunction = (e) => { + const fov = getFov(); + camera.fov = clickZoom(fov, "zoomIn"); + camera.updateProjectionMatrix(); + }; + + const zoomOutFunction = (e) => { + const fov = getFov(); + camera.fov = clickZoom(fov, "zoomOut"); + camera.updateProjectionMatrix(); + }; + + const zoomEqualFunction = (e) => { + const fov = getFov(); + camera.fov = 29; + camera.updateProjectionMatrix(); + }; + + const zoomToFunction = (e) => { + const fov = getFov(); + camera.fov = 35; + camera.updateProjectionMatrix(); + }; + + const clickZoom = (value, zoomType) => { + if (value >= 20 && zoomType === "zoomIn") { + return value - 5; + } else if (value <= 75 && zoomType === "zoomOut") { + return value + 5; + } else { + return value; + } + }; + + const getFov = () => { + return Math.floor( + (2 * + Math.atan( + camera.getFilmHeight() / 2 / camera.getFocalLength() + ) * + 180) / + Math.PI + ); + }; + + const fov = getFov(); + camera.fov = 29; + camera.updateProjectionMatrix(); + + zoomInButton.addEventListener("click", zoomInFunction); + zoomOutButton.addEventListener("click", zoomOutFunction); + zoomEqualButton.addEventListener("click", zoomEqualFunction); + zoomToButton.addEventListener("click", zoomToFunction); + + function createTetrahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let tetrahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.TetrahedronGeometry(1.7); + + let c = document.createElement("canvas"); + let div = document.createElement("div"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + let arrOfNums = [ + [2, 1, 3], + [1, 2, 4], + [3, 1, 4], + [2, 3, 4], + ]; + for (let i = 0; i < 4; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 150px Arial`; + ctx.fillStyle = tempTextColor; + // ctx.fillText( + // i + 1, + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + let aStep = (Math.PI * 2) / 3; + let yAlign = Math.PI * 0.5; + let tileQuarter = tileSize * 0.25; + for (let j = 0; j < 3; j++) { + ctx.save(); + ctx.translate( + (u + 0.5) * tileSize + + Math.cos(j * aStep - yAlign) * tileQuarter, + c.height - + (v + 0.5) * tileSize + + Math.sin(j * aStep - yAlign) * tileQuarter + ); + ctx.rotate((j * Math.PI * 2) / 3); + ctx.fillText(arrOfNums[i][j], 0, 0); + ctx.restore(); + } + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + tetrahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const tetrahedronTransparentGeometry = + new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron + const wireframe = new THREE.WireframeGeometry( + tetrahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + tetrahedron = line; + } else if (tempImage) { + const boxGeo = new THREE.TetrahedronGeometry(1.7); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + tetrahedron = new THREE.Mesh(boxGeo, material); + } else { + const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron + + const tetraMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + + tetrahedron = new THREE.Mesh( + tetrahedronGeometry, + tetraMaterial + ); + } + + tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + tetrahedron.castShadow = true; + scene.add(tetrahedron); + + const verticesTetra = [ + new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) + new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) + new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) + new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) + ]; + const facesTetra = [ + [2, 1, 0], // Triangle 1 (right, top, left) + [0, 3, 2], // Triangle 2 (right, front, top) + [1, 3, 0], // Triangle 3 (top, front, left) + [2, 3, 1], // Triangle 4 (left, right, front) + ]; + // Create a ConvexPolyhedron shape from the vertices and faces + const tetrahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesTetra, + faces: facesTetra, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const tetrahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: tetrahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + if (tempShowNumbers) { + tetrahedronBody.addEventListener("sleep", () => { + sleepCounter++; + getTetraScore(tetrahedron); + }); + } + world.addBody(tetrahedronBody); + tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + tetrahedronBody.applyImpulse(offset, rollingForce); + tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh + tetrahedron.quaternion.copy(tetrahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + tetrahedron.quaternion.copy(quaternionShared); + tetrahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + tetrahedron, + tetrahedronBody, + "tetra", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + function createOctahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let octahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.OctahedronGeometry(1.6); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + + for (let i = 0; i < 9; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 200px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1 + (i == 5 || i == 8 ? "" : ""), + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + octahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const octahedronTransparentGeometry = + new THREE.OctahedronGeometry(1.6); // Size of the octahedron + const wireframe = new THREE.WireframeGeometry( + octahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + octahedron = line; + } else if (tempImage) { + const octahedronGeometry = new THREE.OctahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + octahedron = new THREE.Mesh(octahedronGeometry, material); + } else { + const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron + + const octaMaterial = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial); + } + octahedron.castShadow = true; + scene.add(octahedron); + + const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) + + const verticesOcta = [ + new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) + new CANNON.Vec3(-2 * scaleFactor, 0, 0), // Vertex 2 (left) + new CANNON.Vec3(0, 2 * scaleFactor, 0), // Vertex 3 (top) + new CANNON.Vec3(0, -2 * scaleFactor, 0), // Vertex 4 (bottom) + new CANNON.Vec3(0, 0, 2 * scaleFactor), // Vertex 5 (front) + new CANNON.Vec3(0, 0, -2 * scaleFactor), // Vertex 6 (back) + ]; + + // Define the faces of the octahedron (counter-clockwise order) + const facesOcta = [ + [0, 2, 4], // Triangle 1 (right, top, front) + [0, 4, 3], // Triangle 2 (right, front, bottom) + [0, 3, 5], // Triangle 3 (right, bottom, back) + [0, 5, 2], // Triangle 4 (right, back, top) + [1, 2, 5], // Triangle 5 (left, top, back) + [1, 5, 3], // Triangle 6 (left, back, bottom) + [1, 3, 4], // Triangle 7 (left, bottom, front) + [1, 4, 2], // Triangle 8 (left, front, top) + ]; + + const octahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesOcta, + faces: facesOcta, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const octahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: octahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + if (tempShowNumbers) { + octahedronBody.addEventListener("sleep", () => { + sleepCounter++; + getOctaScore(octahedron); + }); + } + world.addBody(octahedronBody); + + octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + octahedronBody.applyImpulse(offset, rollingForce); + octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh + octahedron.quaternion.copy(octahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + octahedron.quaternion.copy(quaternionShared); + octahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + octahedron, + octahedronBody, + "octa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + function createCube( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let boxMesh; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 2); + let tileSize = 512; + let g = new THREE.BoxGeometry(2, 2, 2); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let baseUVs = [ + [0, 1], + [1, 1], + [0, 0], + [1, 0], + ].map((p) => { + return new THREE.Vector2(...p); + }); + let uvs = []; + let vTemp = new THREE.Vector2(); + let vCenter = new THREE.Vector2(0.5, 0.5); + for (let i = 0; i < 6; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + baseUVs.forEach((buv) => { + uvs.push( + (buv.x + u) / tileDimension.x, + (buv.y + v) / tileDimension.y + ); + }); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 300px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + // metalness: 0.75, + // roughness: 0.25, + }); + + boxMesh = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); + const wireframe = new THREE.WireframeGeometry( + boxTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + boxMesh = line; + } else if (tempImage) { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + boxMesh = new THREE.Mesh(boxGeo, material); + } else { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + const boxMat = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + boxMesh = new THREE.Mesh(boxGeo, boxMat); + } + boxMesh.castShadow = true; + scene.add(boxMesh); + + const boxPhysmat = new CANNON.Material(); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const boxBody = new CANNON.Body({ + mass: 1, + shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), + position: new CANNON.Vec3(x, y, z), + material: boxPhysmat, + friction: 0.1, + restitution: 5, + }); + + world.addBody(boxBody); + + if (tempShowNumbers) { + boxBody.addEventListener("sleep", () => { + sleepCounter++; + getCubeScore(boxMesh); + }); + } + + boxBody.angularVelocity.set(0.5, 0.5, 0.5); + boxBody.applyImpulse(offset, rollingForce); + + // what will happen when the two bodies touch + + const groundBoxContactMat = new CANNON.ContactMaterial( + groundPhysMat, + boxPhysmat, + { friction: 0.5 } + ); + + world.addContactMaterial(groundBoxContactMat); + if (quaternionShared != null && quaternionShared != undefined) { + boxMesh.quaternion.copy(quaternionShared); + boxBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + boxMesh, + boxBody, + "cube", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + const cannonDebugger = new CannonDebugger(scene, world, { + color: 0xadd8e6, + }); + + function createDecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let decahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + + const sides = 10; + const radius = 1.3; + const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], + ].flat(); + + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push( + -Math.cos(b), + -Math.sin(b), + 0.105 * (i % 2 ? 1 : -1) + ); + } + + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ].flat(); + const args = [verticesGeo, facesGeo, radius, 0]; + let decaGeometry = new THREE.PolyhedronGeometry(...args); + + if (tempShowNumbers) { + let g = decaGeometry; + + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + new THREE.Vector2(0.67, 1), // br + new THREE.Vector2(0, 0.5), // bt + new THREE.Vector2(1, 0.5), // tl + new THREE.Vector2(0.67, 0), // bl + ]; + + for (let i = 0; i < 10; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 175px Arial`; + ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i === 5) { + text += "."; + } + ctx.fillText( + text, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + decahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const decahedronTransaprentGeometry = decaGeometry; + const wireframe = new THREE.WireframeGeometry( + decahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + decahedron = line; + } else if (tempImage) { + const decaGeo = decaGeometry; + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + decahedron = new THREE.Mesh(decaGeo, material); + } else { + const decahedronGeometry = decaGeometry; + + const decaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); + } + + decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true; + scene.add(decahedron); + + const t = (1 + Math.sqrt(5)) / 2; + const r = 1 / t; + const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) + + const verticesCannon = []; + for (let i = 0; i < verticesGeo.length; i += 3) { + verticesCannon.push( + new CANNON.Vec3( + verticesGeo[i] * scaleFactor, + verticesGeo[i + 1] * scaleFactor, + verticesGeo[i + 2] * scaleFactor + ) + ); + } + + const facesCannon = []; + for (let i = 0; i < facesGeo.length; i += 3) { + facesCannon.push([ + facesGeo[i], + facesGeo[i + 1], + facesGeo[i + 2], + ]); + } + + // Create a ConvexPolyhedron shape from the scaled vertices and faces + const decahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesCannon, + faces: facesCannon, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const decahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: decahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + if (tempShowNumbers) { + decahedronBody.addEventListener("sleep", () => { + sleepCounter++; + getDecaScore(decahedron); + }); + } + world.addBody(decahedronBody); + decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + decahedronBody.applyImpulse(offset, rollingForce); + decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh + decahedron.quaternion.copy(decahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + decahedron.quaternion.copy(quaternionShared); + decahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + decahedron, + decahedronBody, + "deca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + function makeNumbers() { + let c = document.createElement("canvas"); + c.width = 1024; + c.height = 1024; + let ctx = c.getContext("2d"); + ctx.fillStyle = "#f0f"; + ctx.fillRect(0, 0, c.width, c.height); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = "#0f8"; + ctx.font = "bold 60px Arial"; + let step = 1024 / 10; + let start = step * 0.5; + + for (let i = 0; i < 10; i++) { + ctx.fillText(i + 1, start + step * i, 512); + } + + return new THREE.CanvasTexture(c); + } + + function createDodecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let dodecahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid + let tileSize = 512; + let g = new THREE.DodecahedronGeometry(1.25); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + const base = new THREE.Vector2(0, 0.5); + const center = new THREE.Vector2(); + const angle = THREE.MathUtils.degToRad(72); + let baseUVs = [ + base + .clone() + .rotateAround(center, angle * 1) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 2) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 3) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 4) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 0) + .addScalar(0.5), + ]; + + for (let i = 0; i < 12; i++) { + // 12 faces for a dodecahedron + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[4].x + u) / tileDimension.x, + (baseUVs[4].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold ${tileSize / 3}px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + dodecahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const dodedodecahedronTransaprentGeometry = + new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron + const wireframe = new THREE.WireframeGeometry( + dodedodecahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + dodecahedron = line; + } else if (tempImage) { + const dodecaGeo = new THREE.DodecahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + dodecahedron = new THREE.Mesh(dodecaGeo, material); + } else { + const dodecahedronGeometry = new THREE.DodecahedronGeometry( + 1.25 + ); // Size of the tetrahedron + + const dodecaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + + dodecahedron = new THREE.Mesh( + dodecahedronGeometry, + dodecaMaterial + ); + } + + dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + dodecahedron.castShadow = true; + scene.add(dodecahedron); + + const t = 1.618; + const r = 0.618; + const scaleFactor = 0.75; + + const vertices = [ + new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(0, -r, -t).scale(scaleFactor), + new CANNON.Vec3(0, -r, t).scale(scaleFactor), + new CANNON.Vec3(0, r, -t).scale(scaleFactor), + new CANNON.Vec3(0, r, t).scale(scaleFactor), + new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(-r, t, 0).scale(scaleFactor), + new CANNON.Vec3(r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(r, t, 0).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(-t, 0, r).scale(scaleFactor), + new CANNON.Vec3(t, 0, r).scale(scaleFactor), + ]; + + const indices = [ + [3, 11, 7], + [3, 7, 15], + [3, 15, 13], + [7, 19, 17], + [7, 17, 6], + [7, 6, 15], + [17, 4, 8], + [17, 8, 10], + [17, 10, 6], + [8, 0, 16], + [8, 16, 2], + [8, 2, 10], + [0, 12, 1], + [0, 1, 18], + [0, 18, 16], + [6, 10, 2], + [6, 2, 13], + [6, 13, 15], + [2, 16, 18], + [2, 18, 3], + [2, 3, 13], + [18, 1, 9], + [18, 9, 11], + [18, 11, 3], + [4, 14, 12], + [4, 12, 0], + [4, 0, 8], + [11, 9, 5], + [11, 5, 19], + [11, 19, 7], + [19, 5, 14], + [19, 14, 4], + [19, 4, 17], + [1, 12, 14], + [1, 14, 5], + [1, 5, 9], + ]; + + // Create a ConvexPolyhedron shape from the vertices and faces + const dodecahedronShape = new CANNON.ConvexPolyhedron({ + vertices: vertices, + faces: indices, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const dodecahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: dodecahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + dodecahedronBody.sleepSpeedLimit = 0.5; + dodecahedronBody.sleepTimeLimit = 3; + console.log(dodecahedronBody); + if (tempShowNumbers) { + dodecahedronBody.addEventListener("sleep", () => { + sleepCounter++; + getDodecaScore(dodecahedron); + }); + } + world.addBody(dodecahedronBody); + dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + dodecahedronBody.applyImpulse(offset, rollingForce); + dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh + dodecahedron.quaternion.copy(dodecahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + dodecahedron.quaternion.copy(quaternionShared); + dodecahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + dodecahedron, + dodecahedronBody, + "dodeca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + function createIcosahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor + ) { + let icosahedron; + let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? toggleTransparent : ifTransparent; + let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : textColor; + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.IcosahedronGeometry(1.5); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + for (let i = 0; i < 20; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 175px Arial`; + ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i == 5) { + text + "."; + } + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + icosahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const icosahedronTransparentGeometry = + new THREE.IcosahedronGeometry(1.5); // Size of the Icosahedron + const wireframe = new THREE.WireframeGeometry( + icosahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + icosahedron = line; + } else if (tempImage) { + const boxGeo = new THREE.IcosahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + icosahedron = new THREE.Mesh(boxGeo, material); + } else { + const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron + + const icosaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : presentColor, + wireframe: false, + }); + + icosahedron = new THREE.Mesh( + icosahedronGeometry, + icosaMaterial + ); + } + icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + icosahedron.castShadow = true; + scene.add(icosahedron); + + // Vertices + // Vertices + const t = (1 + Math.sqrt(5)) / 2; + const scaleFactor = 0.6; + const verticesIcosa = [ + new CANNON.Vec3(-1, t, 0).scale(scaleFactor), + new CANNON.Vec3(1, t, 0).scale(scaleFactor), + new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(0, -1, t).scale(scaleFactor), + new CANNON.Vec3(0, 1, t).scale(scaleFactor), + new CANNON.Vec3(0, -1, -t).scale(scaleFactor), + new CANNON.Vec3(0, 1, -t).scale(scaleFactor), + new CANNON.Vec3(t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(t, 0, 1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, 1).scale(scaleFactor), + ]; + + // Faces + const facesIcosa = [ + [0, 11, 5], + [0, 5, 1], + [0, 1, 7], + [0, 7, 10], + [0, 10, 11], + [1, 5, 9], + [5, 11, 4], + [11, 10, 2], + [10, 7, 6], + [7, 1, 8], + [3, 9, 4], + [3, 4, 2], + [3, 2, 6], + [3, 6, 8], + [3, 8, 9], + [4, 9, 5], + [2, 4, 11], + [6, 2, 10], + [8, 6, 7], + [9, 8, 1], + ]; + + // Create a ConvexPolyhedron shape from the vertices and faces + const icosahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesIcosa, + faces: facesIcosa, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const icosahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: icosahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + icosahedronBody.sleepSpeedLimit = 0.5; + icosahedronBody.sleepTimeLimit = 3; + + if (tempShowNumbers) { + icosahedronBody.addEventListener("sleep", () => { + console.log("icosa going to sleeep"); + sleepCounter++; + getIcosaScore(icosahedron); + }); + } + world.addBody(icosahedronBody); + icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + icosahedronBody.applyImpulse(offset, rollingForce); + icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh + icosahedron.quaternion.copy(icosahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + icosahedron.quaternion.copy(quaternionShared); + icosahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + icosahedron, + icosahedronBody, + "icosa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); + } + + const timeStep = 1 / 20; + + function throwDice() { + for (let i = 0; i < diceArray.length; i++) { + scene.remove(diceArray[i][0]); + world.removeBody(diceArray[i][1]); + } + if (diceArray.length > 0) { + lastRoll = ""; + presentScore = 0; + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); + diceArray[i][1].applyImpulse(offset, rollingForce); + diceArray[i][1].position.set(0, 10, 0); + } + for (let i = 0; i < diceArray.length; i++) { + scene.add(diceArray[i][0]); + world.addBody(diceArray[i][1]); + } + } else { + for (let i = 0; i < dices.cube; i++) { + createCube(); + } + for (let i = 0; i < dices.tetra; i++) { + createTetrahedron(); + } + for (let i = 0; i < dices.octa; i++) { + createOctahedron(); + } + for (let i = 0; i < dices.dodeca; i++) { + createDodecahedron(); + } + for (let i = 0; i < dices.deca; i++) { + createDecahedron(); + } + for (let i = 0; i < dices.icosa; i++) { + createIcosahedron(); + } + lastRoll = ""; + // if (showNumbers) { + // getScore(); + // } + } + } + function getOctaScore(body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis + face: 4, + }, + { + vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis + face: 6, + }, + { + vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis + face: 5, + }, + { + vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis + face: 3, + }, + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) + face: 8, + }, + { + vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) + face: 2, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 7, + }, + ]; + + let minValue = 1000000; + let minInd; + for (let i = 0; i < faceVectors.length; i++) { + let faceVector = faceVectors[i]; + faceVector.vector.applyEuler(body.rotation); + console.log(Math.abs(faceVector.vector.y)); + if (minValue > Math.abs(1 - faceVector.vector.y)) { + minValue = Math.abs(1 - faceVector.vector.y); + minInd = i; + } + // if (Math.abs(faceVector.vector.y).toString().substring(0, 1) == '1') { + // lastRoll += faceVectors[i].face + ' +' + // presentScore += faceVectors[i].face + // updateElements() + // break + // } + } + if (!ifRemove) { + lastRoll += faceVectors[minInd].face + " + "; + presentScore += faceVectors[minInd].face; + updateElements(); + } + return faceVectors[minInd].face; + } + function getCubeScore(body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), + face: 1, + }, + { + vector: new THREE.Vector3(-1, 0, 0), + face: 2, + }, + { + vector: new THREE.Vector3(0, 1, 0), + face: 3, + }, + { + vector: new THREE.Vector3(0, -1, 0), + face: 4, + }, + { + vector: new THREE.Vector3(0, 0, 1), + face: 5, + }, + { + vector: new THREE.Vector3(0, 0, -1), + face: 6, + }, + ]; + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) == 1) { + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + } + return faceVector.face; + } + } + } + function getTetraScore(body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 3, + }, + { + vector: new THREE.Vector3(-1, 1, -1).normalize(), // Towards a corner (negative x, positive y, negative z) + face: 2, + }, + { + vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) + face: 4, + }, + ]; + + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation); + if (Math.round(faceVector.vector.y) == 1) { + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + return faceVector.face; + } + } + } + + function getDecaScore(body, ifRemove) { + console.log("getting deca"); + } + + function getIcosaScore(body, ifRemove) { + // Define the golden ratio + const phi = (1 + Math.sqrt(5)) / 2; + + // Icosahedron face vectors + const faceVectors = [ + { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, + { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, + { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, + { + vector: new THREE.Vector3(0, -1, -phi).normalize(), + face: 20, + }, + { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, + { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, + { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, + { + vector: new THREE.Vector3(-1, -phi, 0).normalize(), + face: 17, + }, + { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, + { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, + { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, + { + vector: new THREE.Vector3(-phi, 0, -1).normalize(), + face: 13, + }, + { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, + { + vector: new THREE.Vector3(-1, phi, phi).normalize(), + face: 1, + }, + { + vector: new THREE.Vector3(1, -phi, phi).normalize(), + face: 11, + }, + { + vector: new THREE.Vector3(-1, -phi, phi).normalize(), + face: 12, + }, + { + vector: new THREE.Vector3(1, phi, -phi).normalize(), + face: 10, + }, + { + vector: new THREE.Vector3(-1, phi, -phi).normalize(), + face: 3, + }, + { + vector: new THREE.Vector3(1, -phi, -phi).normalize(), + face: 14, + }, + { + vector: new THREE.Vector3(-1, -phi, -phi).normalize(), + face: 18, + }, + ]; + + let closestFace = null; + let closestDot = -1; // Initialize with the smallest possible dot product + + // Reference vector pointing up + let upVector = new THREE.Vector3(0, 1, 0); + + for (const faceVector of faceVectors) { + // Apply the body's quaternion to the face vector + let worldVector = faceVector.vector + .clone() + .applyQuaternion(body.quaternion); + + // Calculate the dot product with the up vector + let dot = worldVector.dot(upVector); + + // Check if this is the closest to pointing up + if (dot > closestDot) { + closestDot = dot; + closestFace = faceVector; + } + } + + if (closestFace) { + let faceNumber = closestFace.face; + if (!ifRemove) { + lastRoll += faceNumber + " + "; + presentScore += faceNumber; + updateElements(); + } + return faceNumber; + } + } + + // Function to calculate a face vector based on spherical coordinates + function calculateFaceVector(theta, phi) { + return new THREE.Vector3( + Math.cos(theta) * Math.sin(phi), + Math.sin(theta) * Math.sin(phi), + Math.cos(phi) + ); + } + + function getDodecaScore(body, ifRemove) { + // Define the golden ratio + const phi = (1 + Math.sqrt(5)) / 2; + + // Dodecahedron face vectors + const faceVectors = [ + { vector: new THREE.Vector3(1, 1, 1), face: 1 }, + { vector: new THREE.Vector3(1, 1, -1), face: 6 }, + { vector: new THREE.Vector3(1, -1, 1), face: 11 }, + { vector: new THREE.Vector3(1, -1, -1), face: 4 }, + { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, + { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, + { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, + { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, + { vector: new THREE.Vector3(0, phi, 1 / phi), face: 9 }, + { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, + { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, + { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, + ]; + + for (const faceVector of faceVectors) { + faceVector.vector.normalize().applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) === 1) { + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + return faceVector.face; + } + } + } + + function toggleTransparency() { + for (let i = 0; i < diceArray.length; i++) { + if (transparent) { + leftLight.intensity = 5; + rightLight.intensity = 5; + backLight.intensity = 5; + bottomLight.intensity = 5; + } else { + leftLight.intensity = 0.1; + rightLight.intensity = 0.1; + backLight.intensity = 0.5; + bottomLight.intensity = 0.1; + } + diceArray[i][0].material.wireframe = + !diceArray[i][0].material.wireframe; + diceArray[i][0].material.transparent = + !diceArray[i][0].material.transparent; + diceArray[i][0].material.needsUpdate = true; + } + groundMesh.material.wireframe = !groundMesh.material.wireframe; + } + // function changeColors() { + // for (let i = 0; i < diceArray.length; i++) { + // diceArray[i][0].material.color?.set(presentColor); + // diceArray[i][0].material.needsUpdate = true; + // } + // } + + function changeBoardBackground(selectedBoard) { + console.log("changing bg now"); + console.log(selectedBoard); + let textureLoader = new THREE.TextureLoader(); + switch (selectedBoard) { + case "green-board": + console.log("now changing bg to green"); + textureLoader.load( + "images/grass_background.png", + function (groundTexture) { + groundMesh.material.wireframe = false; + groundMesh.material.map = groundTexture; + groundMesh.material.needsUpdate = true; + groundBody.material.friction = 5; + } + ); + break; + case "wood": + console.log("wood changing"); + textureLoader.load( + "images/wood.png", + function (groundTexture) { + groundMesh.material.wireframe = false; + groundMesh.material.color.setHex(0xf0c592); + groundMesh.material.map = groundTexture; + groundMesh.material.needsUpdate = true; + groundBody.material.friction = 3; + } + ); + break; + case "default": + groundMesh.material.needsUpdate = true; + groundMesh.material.color.setHex(0x656565); + groundMesh.material.wireframe = false; + groundMesh.material.map = null; + groundBody.material.friction = 1; + break; + } + } + animate(); + + function animate() { + world.step(timeStep); + // cannonDebugger.update(); + + groundMesh.position.copy(groundBody.position); + groundMesh.quaternion.copy(groundBody.quaternion); + + // Loop to merge the cannon bodies to the threejs meshes + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][0]?.position?.copy(diceArray[i][1].position); + diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); + } + + renderer.render(scene, camera); + } + + renderer.setAnimationLoop(animate); + + window.addEventListener("resize", function () { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }); + }); }); From 168a48e8e3a79296aaee0775e593e90fb88cda46 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 1 Jul 2024 01:30:59 +0530 Subject: [PATCH 12/60] indentation --- activities/3DVolume.activity/index.html | 332 +++++++++--------- .../js/palettes/bgpalette.html | 34 +- .../js/palettes/bgpalette.js | 114 +++--- .../js/palettes/colorpalettefill.html | 53 ++- .../js/palettes/colorpalettefill.js | 95 ++--- .../js/palettes/colorpalettetext.html | 52 ++- .../js/palettes/colorpalettetext.js | 96 ++--- .../js/palettes/volumepalette.html | 20 +- .../js/palettes/volumepalette.js | 58 +-- .../js/palettes/zoompalette.html | 27 +- .../js/palettes/zoompalette.js | 67 ++-- 11 files changed, 498 insertions(+), 450 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index c808c8351..0a2b0598e 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -1,164 +1,174 @@ - - - 3D Volume Activity - - - - - - - - - - - - - - - - - - -
-
- - - -
- - - - - - - - -
- - - -
- - - - - - - - - - - -
- - - - -
- - - - - - - -
- - -
-
-
- -
- -
- -
- - -
- -
-
-

-
- + + + 3D Volume Activity + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + + + + +
+ + + +
+ + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+ + +
+
+
+ +
+ +
+ +
+ + +
+ +
+
+

+
+ diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.html b/activities/3DVolume.activity/js/palettes/bgpalette.html index 3fc9c8f41..1bf9281ae 100644 --- a/activities/3DVolume.activity/js/palettes/bgpalette.html +++ b/activities/3DVolume.activity/js/palettes/bgpalette.html @@ -1,18 +1,18 @@
-

Select Background

-
-
- - - -
-
\ No newline at end of file +

Select Background

+
+
+ + + +
+
diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.js b/activities/3DVolume.activity/js/palettes/bgpalette.js index fc119c509..963af7651 100644 --- a/activities/3DVolume.activity/js/palettes/bgpalette.js +++ b/activities/3DVolume.activity/js/palettes/bgpalette.js @@ -1,66 +1,78 @@ define([ - "sugar-web/graphics/palette", - "text!activity/palettes/bgpalette.html", - "sugar-web/graphics/presencepalette", + "sugar-web/graphics/palette", + "text!activity/palettes/bgpalette.html", + "sugar-web/graphics/presencepalette", ], function (palette, template, presencepalette) { - var bgpalette = {}; - bgpalette.BgPalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText); - this.getPalette().id = "bg-palette"; + var bgpalette = {}; + bgpalette.BgPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "bg-palette"; - var containerElem = document.createElement("div"); - containerElem.innerHTML = template; + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; - this.setContent([containerElem]); + this.setContent([containerElem]); - this.bgSelectedEvent = document.createEvent("CustomEvent"); - this.bgSelectedEvent.initCustomEvent("bg-selected", true, true, { bg: "" }); + this.bgSelectedEvent = document.createEvent("CustomEvent"); + this.bgSelectedEvent.initCustomEvent("bg-selected", true, true, { + bg: "", + }); - let that = this; - let backgroundChangeCallback = null; + let that = this; + let backgroundChangeCallback = null; - this.setBackgroundChangeCallback = function (callback) { - backgroundChangeCallback = callback; - }; + this.setBackgroundChangeCallback = function (callback) { + backgroundChangeCallback = callback; + }; - document.getElementById("green-board").addEventListener("click", onClick); - document.getElementById("default").addEventListener("click", onClick); - document.getElementById("wood").addEventListener("click", onClick); + document + .getElementById("green-board") + .addEventListener("click", onClick); + document.getElementById("default").addEventListener("click", onClick); + document.getElementById("wood").addEventListener("click", onClick); - function onClick(event) { - that.bgSelectedEvent.bg = event.currentTarget.id; - if (document.querySelector("#green-board").classList.contains("active")) { - document.querySelector("#green-board").classList.remove("active"); - } - if (document.querySelector("#wood").classList.contains("active")) { - document.querySelector("#wood").classList.remove("active"); - } - if (document.querySelector("#default").classList.contains("active")) { - document.querySelector("#default").classList.remove("active"); - } - event.currentTarget.classList.add("active"); + function onClick(event) { + that.bgSelectedEvent.bg = event.currentTarget.id; + if ( + document + .querySelector("#green-board") + .classList.contains("active") + ) { + document + .querySelector("#green-board") + .classList.remove("active"); + } + if (document.querySelector("#wood").classList.contains("active")) { + document.querySelector("#wood").classList.remove("active"); + } + if ( + document.querySelector("#default").classList.contains("active") + ) { + document.querySelector("#default").classList.remove("active"); + } + event.currentTarget.classList.add("active"); - if (backgroundChangeCallback) { - backgroundChangeCallback(that.bgSelectedEvent.bg); - } + if (backgroundChangeCallback) { + backgroundChangeCallback(that.bgSelectedEvent.bg); + } - that.popDown(); - } - }; + that.popDown(); + } + }; - var addEventListener = function (type, listener, useCapture) { - console.log("adding event listener"); - return this.getPalette().addEventListener(type, listener, useCapture); - }; + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; - bgpalette.BgPalette.prototype = Object.create(palette.Palette.prototype, { - addEventListener: { - value: addEventListener, - enumerable: true, - configurable: true, - writable: true, - }, - }); + bgpalette.BgPalette.prototype = Object.create(palette.Palette.prototype, { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + }); - return bgpalette; + return bgpalette; }); diff --git a/activities/3DVolume.activity/js/palettes/colorpalettefill.html b/activities/3DVolume.activity/js/palettes/colorpalettefill.html index a3506c8f0..c5649e277 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettefill.html +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.html @@ -1,28 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
- - -
-
- - -
-
-
- \ No newline at end of file +
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
diff --git a/activities/3DVolume.activity/js/palettes/colorpalettefill.js b/activities/3DVolume.activity/js/palettes/colorpalettefill.js index ee6f4f194..380700c57 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettefill.js +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.js @@ -1,49 +1,52 @@ define([ - "sugar-web/graphics/palette", - "text!activity/palettes/colorpalettefill.html", + "sugar-web/graphics/palette", + "text!activity/palettes/colorpalettefill.html", ], function (palette, template) { - var colorpalette = {}; - colorpalette.ColorPalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText); - this.getPalette().id = "color-palette"; - - var containerElem = document.createElement("div"); - - containerElem.innerHTML = template; - - this.setContent([containerElem]); - - let that = this; - - const colors = document.querySelectorAll(".color-fill"); - colors.forEach((color) => { - color.addEventListener("click", function () { - const selectedColor = this.style.backgroundColor; - const colorChangeEvent = new CustomEvent("color-selected-fill", { - detail: { color: selectedColor }, - }); - document.dispatchEvent(colorChangeEvent); - that.popDown(); - }); - }); - }; - - var addEventListener = function (type, listener, useCapture) { - console.log("adding event listener"); - return this.getPalette().addEventListener(type, listener, useCapture); - }; - - colorpalette.ColorPalette.prototype = Object.create( - palette.Palette.prototype, - { - addEventListener: { - value: addEventListener, - enumerable: true, - configurable: true, - writable: true, - }, - } - ); - - return colorpalette; + var colorpalette = {}; + colorpalette.ColorPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "color-palette"; + + var containerElem = document.createElement("div"); + + containerElem.innerHTML = template; + + this.setContent([containerElem]); + + let that = this; + + const colors = document.querySelectorAll(".color-fill"); + colors.forEach((color) => { + color.addEventListener("click", function () { + const selectedColor = this.style.backgroundColor; + const colorChangeEvent = new CustomEvent( + "color-selected-fill", + { + detail: { color: selectedColor }, + } + ); + document.dispatchEvent(colorChangeEvent); + that.popDown(); + }); + }); + }; + + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + colorpalette.ColorPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); + + return colorpalette; }); diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.html b/activities/3DVolume.activity/js/palettes/colorpalettetext.html index 0599bcb21..a9a12e8de 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettetext.html +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.html @@ -1,30 +1,28 @@ -
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
- - -
-
- - -
-
- - -
+
+
+ + +
+
+ + +
+
+ + +
+
-
- diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.js b/activities/3DVolume.activity/js/palettes/colorpalettetext.js index 40fb4d57e..8d2e1f0ef 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettetext.js +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.js @@ -1,51 +1,51 @@ define([ - "sugar-web/graphics/palette", - "text!activity/palettes/colorpalettetext.html", - ], function (palette, template) { - var colorpalette = {}; - colorpalette.ColorPalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText); - this.getPalette().id = "color-palette"; - - var containerElem = document.createElement("div"); - containerElem.innerHTML = template; - - this.setContent([containerElem]); - - let that = this; - - const colors = document.querySelectorAll(".color-text"); - colors.forEach((color) => { - color.addEventListener("click", function () { - const selectedColor = this.style.backgroundColor; - const colorChangeEvent = new CustomEvent("color-selected-text", { - detail: { color: selectedColor }, - }); - document.dispatchEvent(colorChangeEvent); - that.popDown(); - }); - }); - }; - - var addEventListener = function (type, listener, useCapture) { - console.log("adding event listener"); - return this.getPalette().addEventListener(type, listener, useCapture); - }; - - colorpalette.ColorPalette.prototype = Object.create( - palette.Palette.prototype, - { - addEventListener: { - value: addEventListener, - enumerable: true, - configurable: true, - writable: true, - }, - } - ); - - return colorpalette; - }); + "sugar-web/graphics/palette", + "text!activity/palettes/colorpalettetext.html", +], function (palette, template) { + var colorpalette = {}; + colorpalette.ColorPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "color-palette"; + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; - \ No newline at end of file + this.setContent([containerElem]); + + let that = this; + + const colors = document.querySelectorAll(".color-text"); + colors.forEach((color) => { + color.addEventListener("click", function () { + const selectedColor = this.style.backgroundColor; + const colorChangeEvent = new CustomEvent( + "color-selected-text", + { + detail: { color: selectedColor }, + } + ); + document.dispatchEvent(colorChangeEvent); + that.popDown(); + }); + }); + }; + + var addEventListener = function (type, listener, useCapture) { + console.log("adding event listener"); + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + colorpalette.ColorPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); + + return colorpalette; +}); diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html index 836c81186..05e149823 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.html +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -1,5 +1,17 @@
- - - -
\ No newline at end of file + + + +
diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.js b/activities/3DVolume.activity/js/palettes/volumepalette.js index 6eeae0b70..d2a936693 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.js +++ b/activities/3DVolume.activity/js/palettes/volumepalette.js @@ -1,37 +1,39 @@ define([ - "sugar-web/graphics/palette", - "text!activity/palettes/volumepalette.html", + "sugar-web/graphics/palette", + "text!activity/palettes/volumepalette.html", ], function (palette, template) { - var volumepalette = {}; - volumepalette.VolumePalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText); - this.getPalette().id = "volume-palette"; + var volumepalette = {}; + volumepalette.VolumePalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "volume-palette"; - var containerElem = document.createElement("div"); - containerElem.innerHTML = template; + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; - this.setContent([containerElem]); + this.setContent([containerElem]); - document.getElementById("volume-palette").addEventListener("click", () => { - this.popDown(); - }); - }; + document + .getElementById("volume-palette") + .addEventListener("click", () => { + this.popDown(); + }); + }; - var addEventListener = function (type, listener, useCapture) { - return this.getPalette().addEventListener(type, listener, useCapture); - }; + var addEventListener = function (type, listener, useCapture) { + return this.getPalette().addEventListener(type, listener, useCapture); + }; - volumepalette.VolumePalette.prototype = Object.create( - palette.Palette.prototype, - { - addEventListener: { - value: addEventListener, - enumerable: true, - configurable: true, - writable: true, - }, - } - ); + volumepalette.VolumePalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); - return volumepalette; + return volumepalette; }); diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.html b/activities/3DVolume.activity/js/palettes/zoompalette.html index 14cb9cd30..39c52e0f0 100644 --- a/activities/3DVolume.activity/js/palettes/zoompalette.html +++ b/activities/3DVolume.activity/js/palettes/zoompalette.html @@ -1,7 +1,22 @@
- - - - - -
\ No newline at end of file + + + + + diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.js b/activities/3DVolume.activity/js/palettes/zoompalette.js index 58a6dd907..6191e2968 100644 --- a/activities/3DVolume.activity/js/palettes/zoompalette.js +++ b/activities/3DVolume.activity/js/palettes/zoompalette.js @@ -1,36 +1,33 @@ define([ - 'sugar-web/graphics/palette', - 'text!activity/palettes/zoompalette.html', - ], function (palette, template) { - var zoompalette = {} - zoompalette.ZoomPalette = function (invoker, primaryText) { - palette.Palette.call(this, invoker, primaryText) - this.getPalette().id = 'zoom-palette' - - var containerElem = document.createElement('div') - containerElem.innerHTML = template - - this.setContent([containerElem]) - - - } - - var addEventListener = function (type, listener, useCapture) { - return this.getPalette().addEventListener(type, listener, useCapture) - } - - zoompalette.ZoomPalette.prototype = Object.create( - palette.Palette.prototype, - { - addEventListener: { - value: addEventListener, - enumerable: true, - configurable: true, - writable: true, - }, - }, - ) - - return zoompalette - }) - \ No newline at end of file + "sugar-web/graphics/palette", + "text!activity/palettes/zoompalette.html", +], function (palette, template) { + var zoompalette = {}; + zoompalette.ZoomPalette = function (invoker, primaryText) { + palette.Palette.call(this, invoker, primaryText); + this.getPalette().id = "zoom-palette"; + + var containerElem = document.createElement("div"); + containerElem.innerHTML = template; + + this.setContent([containerElem]); + }; + + var addEventListener = function (type, listener, useCapture) { + return this.getPalette().addEventListener(type, listener, useCapture); + }; + + zoompalette.ZoomPalette.prototype = Object.create( + palette.Palette.prototype, + { + addEventListener: { + value: addEventListener, + enumerable: true, + configurable: true, + writable: true, + }, + } + ); + + return zoompalette; +}); From 7acb6f234fa4eed2968f3105f2fa627e4da1c75d Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 5 Jul 2024 16:56:42 +0530 Subject: [PATCH 13/60] refactoring --- activities/3DVolume.activity/index.html | 7 + activities/3DVolume.activity/js/activity.js | 1596 +++-------------- activities/3DVolume.activity/js/creatOcta.js | 202 +++ activities/3DVolume.activity/js/createCube.js | 183 ++ activities/3DVolume.activity/js/createDeca.js | 227 +++ .../3DVolume.activity/js/createDodeca.js | 265 +++ .../3DVolume.activity/js/createIcosa.js | 215 +++ .../3DVolume.activity/js/createTetra.js | 197 ++ 8 files changed, 1504 insertions(+), 1388 deletions(-) create mode 100644 activities/3DVolume.activity/js/creatOcta.js create mode 100644 activities/3DVolume.activity/js/createCube.js create mode 100644 activities/3DVolume.activity/js/createDeca.js create mode 100644 activities/3DVolume.activity/js/createDodeca.js create mode 100644 activities/3DVolume.activity/js/createIcosa.js create mode 100644 activities/3DVolume.activity/js/createTetra.js diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 0a2b0598e..7beab0b22 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -29,6 +29,13 @@ + + + + + + + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index cfdc84b40..068df0221 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -61,44 +61,58 @@ define([ undefined ); + const randomDirection = new CANNON.Vec3( + 0.3, // Random x-axis value between -0.5 and 0.5 + 0.05, // Random y-axis value between -0.1 and 0.1 (slightly tilted) + 0.3 // Random z-axis value between -0.5 and 0.5 + ); + randomDirection.normalize(); // Normalize to unit vector + + let ctx = { + showNumbers: false, + presentColor: null, + textColor: "#ffffff", + toggleTransparent: false, + offset: new CANNON.Vec3(0, 0.1, 0), + rollingForce: randomDirection.scale(2), + }; + let presentScore = 0; let lastRoll = ""; let diceArray = []; let journalDiceArray = []; - let showNumbers = false; let showImage = false; let imageData; - let presentColor; - let textColor = "#ffffff"; var currentenv; let removeVolume = false; let transparent = false; - let toggleTransparent = false; let defaultVolume = true; + let xCoordinate, zCoordinate; + var defaultButton = document.getElementById("default-button"); defaultButton.classList.toggle("active"); env.getEnvironment(function (err, environment) { currentenv = environment; - presentColor = + ctx.presentColor = currentenv.user.colorvalue.fill != null ? currentenv.user.colorvalue.fill - : presentColor; + : ctx.presentColor; scene.background = new THREE.Color("#A9A9A9"); - console.log(presentColor); + console.log(ctx.presentColor); - textColor = + ctx.textColor = currentenv.user.colorvalue.stroke != null ? currentenv.user.colorvalue.stroke - : textColor; + : ctx.textColor; document.getElementById("color-button-fill").style.backgroundColor = - presentColor; + ctx.presentColor; document.getElementById("color-button-text").style.backgroundColor = - textColor; + ctx.textColor; if (environment.sharedId) { console.log("Shared instance"); @@ -119,97 +133,51 @@ define([ data = msg.content; console.log(data); for (let i = 0; i < data.length; i++) { - let fillColorStored = data[i][3]; - let textColorStored = data[i][4]; + let createFunction = null; switch (data[i][0]) { case "cube": - createCube( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createCube; break; case "octa": - createOctahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createOctahedron; break; case "tetra": - createTetrahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createTetrahedron; break; case "deca": - createDecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createDecahedron; break; case "dodeca": - createDodecahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createDodecahedron; break; case "icosa": - createIcosahedron( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored - ); + createFunction = createIcosahedron; break; default: - // Default case (optional): Handle unexpected values console.log(`Unexpected shape: ${data[i][0]}`); - break; + continue; // Skip the rest of the loop for unexpected shapes + } + + if (createFunction) { + const fillColorStored = data[i][3]; + const textColorStored = data[i][4]; + createFunction( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored, + ctx, + diceArray, + world, + scene, + groundPhysMat + ); } } } @@ -272,7 +240,12 @@ define([ msg.content.sharedImageData, msg.content.yCoordinateShared, msg.content.quaternionShared, - msg.content.sharedTextColor + msg.content.sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat ); } }; @@ -356,7 +329,12 @@ define([ null, data[i][1].y, data[i][2], - textColorStored + textColorStored, + ctx, + diceArray, + world, + scene, + groundPhysMat ); } } @@ -405,9 +383,9 @@ define([ sliderColorFill.g, sliderColorFill.b ); - presentColor = hexColor; + ctx.presentColor = hexColor; document.getElementById("color-button-fill").style.backgroundColor = - presentColor; + ctx.presentColor; } function updateSlidersFill(color) { @@ -432,9 +410,9 @@ define([ document.addEventListener("color-selected-fill", function (event) { const selectedColorFill = event.detail.color; - presentColor = selectedColorFill; + ctx.presentColor = selectedColorFill; document.getElementById("color-button-fill").style.backgroundColor = - presentColor; + ctx.presentColor; updateSlidersFill(selectedColorFill); }); @@ -450,9 +428,9 @@ define([ sliderColorText.g, sliderColorText.b ); - textColor = hexColor; + ctx.textColor = hexColor; document.getElementById("color-button-text").style.backgroundColor = - textColor; + ctx.textColor; } function updateSlidersText(color) { @@ -477,16 +455,16 @@ define([ document.addEventListener("color-selected-text", function (event) { const selectedColorText = event.detail.color; - textColor = selectedColorText; + ctx.textColor = selectedColorText; document.getElementById("color-button-text").style.backgroundColor = - textColor; + ctx.textColor; updateSlidersText(selectedColorText); }); // document.addEventListener('color-selected-fill', function (event) { // const selectedColor = event.detail.color; - // presentColor = selectedColor; - // document.getElementById('color-button-fill').style.backgroundColor = presentColor; + // ctx.presentColor = selectedColor; + // document.getElementById('color-button-fill').style.backgroundColor = ctx.presentColor; // updateSlidersFill(selectedColor); // }); @@ -535,18 +513,18 @@ define([ defaultButton.classList.toggle("active"); defaultVolume = !defaultVolume; } - if (toggleTransparent) { + if (ctx.toggleTransparent) { var transparentButton = document.getElementById("transparent-button"); transparentButton.classList.toggle("active"); - toggleTransparent = !toggleTransparent; + ctx.toggleTransparent = !ctx.toggleTransparent; } if (showImage) { var imageButton1 = document.getElementById("image-button"); imageButton1.classList.toggle("active"); showImage = !showImage; } - showNumbers = !showNumbers; + ctx.showNumbers = !ctx.showNumbers; // toggleNumbers(); }); @@ -569,17 +547,17 @@ define([ defaultVolume = !defaultVolume; } - if (showNumbers) { + if (ctx.showNumbers) { var numberButton = document.getElementById("number-button"); numberButton.classList.toggle("active"); - showNumbers = !showNumbers; + ctx.showNumbers = !ctx.showNumbers; } if (showImage) { var imageButton1 = document.getElementById("image-button"); imageButton1.classList.toggle("active"); showImage = !showImage; } - toggleTransparent = !toggleTransparent; + ctx.toggleTransparent = !ctx.toggleTransparent; }); document @@ -591,17 +569,17 @@ define([ document.getElementById("volume-button").style.backgroundImage = "url(icons/default_volume.svg)"; - if (toggleTransparent) { + if (ctx.toggleTransparent) { var transparentButton = document.getElementById("transparent-button"); transparentButton.classList.toggle("active"); - toggleTransparent = !toggleTransparent; + ctx.toggleTransparent = !ctx.toggleTransparent; } - if (showNumbers) { + if (ctx.showNumbers) { var numberButton = document.getElementById("number-button"); numberButton.classList.toggle("active"); - showNumbers = !showNumbers; + ctx.showNumbers = !ctx.showNumbers; } if (showImage) { var imageButton1 = document.getElementById("image-button"); @@ -708,17 +686,17 @@ define([ // imageButton.classList.add('active') // showImage = !showImage - // if (toggleTransparent) { + // if (ctx.toggleTransparent) { // var transparentButton = // document.getElementById('transparent-button') // transparentButton.classList.toggle('active') - // toggleTransparent = !toggleTransparent + // ctx.toggleTransparent = !ctx.toggleTransparent // } - // if (showNumbers) { + // if (ctx.showNumbers) { // var numberButton = document.getElementById('number-button') // numberButton.classList.toggle('active') - // showNumbers = !showNumbers + // ctx.showNumbers = !ctx.showNumbers // } // var dataentry = new datastore.DatastoreObject(entry.objectId) @@ -754,8 +732,8 @@ define([ // content: { // shape: 'cube', // color: currentenv.user.colorvalue.fill, - // ifTransparent: toggleTransparent, - // ifNumbers: showNumbers, + // ifTransparent: ctx.toggleTransparent, + // ifNumbers: ctx.showNumbers, // }, // }) // } @@ -778,7 +756,6 @@ define([ }); renderer.shadowMap.enabled = true; - let xCoordinate, zCoordinate, yCoordinate; const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); document.querySelector("body").addEventListener("click", onRemoveClick); @@ -826,7 +803,23 @@ define([ } if (createFunction) { - createFunction(); + createFunction( + null, + null, + null, + xCoordinate, + zCoordinate, + null, + null, + null, + null, + null, + ctx, + diceArray, + world, + scene, + groundPhysMat + ); if (presence) { presence.sendMessage( @@ -835,16 +828,17 @@ define([ user: presence.getUserInfo(), content: { shape: shapeType, - color: presentColor, - ifTransparent: toggleTransparent, - ifNumbers: showNumbers, + color: ctx.presentColor, + ifTransparent: + ctx.toggleTransparent, + ifNumbers: ctx.showNumbers, xCoordinateShared: xCoordinate, zCoordinateShared: zCoordinate, ifImage: showImage, sharedImageData: imageData, yCoordinateShared: null, quaternionShared: null, - sharedTextColor: textColor, + sharedTextColor: ctx.textColor, }, } ); @@ -1016,7 +1010,7 @@ define([ .appendChild(renderer.domElement); const scene = new THREE.Scene(); - scene.background = new THREE.Color(presentColor); + scene.background = new THREE.Color(ctx.presentColor); const light = new THREE.DirectionalLight(0xffffff, 0.4); light.castShadow = true; const leftLight = new THREE.DirectionalLight(0xffffff, 0.25); @@ -1132,17 +1126,7 @@ define([ frontWallBody.position.set(0, 0, -15); frontWallBody.quaternion.setFromEuler(0, 0, -Math.PI / 2); - const rollingForceMagnitude = 2; // Adjust for desired intensity - const randomDirection = new CANNON.Vec3( - 0.3, // Random x-axis value between -0.5 and 0.5 - 0.05, // Random y-axis value between -0.1 and 0.1 (slightly tilted) - 0.3 // Random z-axis value between -0.5 and 0.5 - ); - randomDirection.normalize(); // Normalize to unit vector - - const rollingForce = randomDirection.scale(rollingForceMagnitude); - - const offset = new CANNON.Vec3(0, 0.1, 0); + // const rollingForceMagnitude = 2; // Adjust for desired intensity const orbit = new OrbitControls.OrbitControls( camera, @@ -1237,781 +1221,31 @@ define([ zoomEqualButton.addEventListener("click", zoomEqualFunction); zoomToButton.addEventListener("click", zoomToFunction); - function createTetrahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let tetrahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.TetrahedronGeometry(1.7); - - let c = document.createElement("canvas"); - let div = document.createElement("div"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - let arrOfNums = [ - [2, 1, 3], - [1, 2, 4], - [3, 1, 4], - [2, 3, 4], - ]; - for (let i = 0; i < 4; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 150px Arial`; - ctx.fillStyle = tempTextColor; - // ctx.fillText( - // i + 1, - // (u + 0.5) * tileSize, - // c.height - (v + 0.5) * tileSize - // ); - let aStep = (Math.PI * 2) / 3; - let yAlign = Math.PI * 0.5; - let tileQuarter = tileSize * 0.25; - for (let j = 0; j < 3; j++) { - ctx.save(); - ctx.translate( - (u + 0.5) * tileSize + - Math.cos(j * aStep - yAlign) * tileQuarter, - c.height - - (v + 0.5) * tileSize + - Math.sin(j * aStep - yAlign) * tileQuarter - ); - ctx.rotate((j * Math.PI * 2) / 3); - ctx.fillText(arrOfNums[i][j], 0, 0); - ctx.restore(); - } - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - tetrahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const tetrahedronTransparentGeometry = - new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron - const wireframe = new THREE.WireframeGeometry( - tetrahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - tetrahedron = line; - } else if (tempImage) { - const boxGeo = new THREE.TetrahedronGeometry(1.7); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - tetrahedron = new THREE.Mesh(boxGeo, material); - } else { - const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron - - const tetraMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - tetrahedron = new THREE.Mesh( - tetrahedronGeometry, - tetraMaterial - ); - } - - tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - tetrahedron.castShadow = true; - scene.add(tetrahedron); - - const verticesTetra = [ - new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) - new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) - new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) - new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) - ]; - const facesTetra = [ - [2, 1, 0], // Triangle 1 (right, top, left) - [0, 3, 2], // Triangle 2 (right, front, top) - [1, 3, 0], // Triangle 3 (top, front, left) - [2, 3, 1], // Triangle 4 (left, right, front) - ]; - // Create a ConvexPolyhedron shape from the vertices and faces - const tetrahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesTetra, - faces: facesTetra, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const tetrahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: tetrahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - tetrahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getTetraScore(tetrahedron); - }); - } - world.addBody(tetrahedronBody); - tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - tetrahedronBody.applyImpulse(offset, rollingForce); - tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh - tetrahedron.quaternion.copy(tetrahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - tetrahedron.quaternion.copy(quaternionShared); - tetrahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - tetrahedron, - tetrahedronBody, - "tetra", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function createOctahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let octahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.OctahedronGeometry(1.6); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - - for (let i = 0; i < 9; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 200px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1 + (i == 5 || i == 8 ? "" : ""), - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - octahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const octahedronTransparentGeometry = - new THREE.OctahedronGeometry(1.6); // Size of the octahedron - const wireframe = new THREE.WireframeGeometry( - octahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - octahedron = line; - } else if (tempImage) { - const octahedronGeometry = new THREE.OctahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - octahedron = new THREE.Mesh(octahedronGeometry, material); - } else { - const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron - - const octaMaterial = new THREE.MeshPhongMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial); - } - octahedron.castShadow = true; - scene.add(octahedron); - - const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) - - const verticesOcta = [ - new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) - new CANNON.Vec3(-2 * scaleFactor, 0, 0), // Vertex 2 (left) - new CANNON.Vec3(0, 2 * scaleFactor, 0), // Vertex 3 (top) - new CANNON.Vec3(0, -2 * scaleFactor, 0), // Vertex 4 (bottom) - new CANNON.Vec3(0, 0, 2 * scaleFactor), // Vertex 5 (front) - new CANNON.Vec3(0, 0, -2 * scaleFactor), // Vertex 6 (back) - ]; - - // Define the faces of the octahedron (counter-clockwise order) - const facesOcta = [ - [0, 2, 4], // Triangle 1 (right, top, front) - [0, 4, 3], // Triangle 2 (right, front, bottom) - [0, 3, 5], // Triangle 3 (right, bottom, back) - [0, 5, 2], // Triangle 4 (right, back, top) - [1, 2, 5], // Triangle 5 (left, top, back) - [1, 5, 3], // Triangle 6 (left, back, bottom) - [1, 3, 4], // Triangle 7 (left, bottom, front) - [1, 4, 2], // Triangle 8 (left, front, top) - ]; - - const octahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesOcta, - faces: facesOcta, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + function onSleepStateChangeToOne(body) { + let score; + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][0] == body) { + score = diceArray[i][7]; + let scoresArray = lastRoll.split(" + "); - const octahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: octahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - octahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getOctaScore(octahedron); - }); - } - world.addBody(octahedronBody); - - octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - octahedronBody.applyImpulse(offset, rollingForce); - octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh - octahedron.quaternion.copy(octahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - octahedron.quaternion.copy(quaternionShared); - octahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - octahedron, - octahedronBody, - "octa", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } + // Find the index of the first occurrence of the score to remove + let indexToRemove = scoresArray.indexOf(score.toString()); - function createCube( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let boxMesh; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 2); - let tileSize = 512; - let g = new THREE.BoxGeometry(2, 2, 2); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let baseUVs = [ - [0, 1], - [1, 1], - [0, 0], - [1, 0], - ].map((p) => { - return new THREE.Vector2(...p); - }); - let uvs = []; - let vTemp = new THREE.Vector2(); - let vCenter = new THREE.Vector2(0.5, 0.5); - for (let i = 0; i < 6; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - baseUVs.forEach((buv) => { - uvs.push( - (buv.x + u) / tileDimension.x, - (buv.y + v) / tileDimension.y - ); - }); + // If the score is found, remove it + if (indexToRemove !== -1) { + scoresArray.splice(indexToRemove, 1); + } - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 300px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); + // Join the remaining scores back into a string + lastRoll = scoresArray.join(" + "); } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - // metalness: 0.75, - // roughness: 0.25, - }); - - boxMesh = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); - const wireframe = new THREE.WireframeGeometry( - boxTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - boxMesh = line; - } else if (tempImage) { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - boxMesh = new THREE.Mesh(boxGeo, material); - } else { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - const boxMat = new THREE.MeshPhongMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - boxMesh = new THREE.Mesh(boxGeo, boxMat); - } - boxMesh.castShadow = true; - scene.add(boxMesh); - - const boxPhysmat = new CANNON.Material(); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const boxBody = new CANNON.Body({ - mass: 1, - shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), - position: new CANNON.Vec3(x, y, z), - material: boxPhysmat, - friction: 0.1, - restitution: 5, - }); - - world.addBody(boxBody); - - if (tempShowNumbers) { - boxBody.addEventListener("sleep", () => { - sleepCounter++; - getCubeScore(boxMesh); - }); - } - - boxBody.angularVelocity.set(0.5, 0.5, 0.5); - boxBody.applyImpulse(offset, rollingForce); - - // what will happen when the two bodies touch - - const groundBoxContactMat = new CANNON.ContactMaterial( - groundPhysMat, - boxPhysmat, - { friction: 0.5 } - ); - - world.addContactMaterial(groundBoxContactMat); - if (quaternionShared != null && quaternionShared != undefined) { - boxMesh.quaternion.copy(quaternionShared); - boxBody.quaternion.copy(quaternionShared); } - diceArray.push([ - boxMesh, - boxBody, - "cube", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); } const cannonDebugger = new CannonDebugger(scene, world, { color: 0xadd8e6, }); - function createDecahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let decahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - - const sides = 10; - const radius = 1.3; - const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], - ].flat(); - - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push( - -Math.cos(b), - -Math.sin(b), - 0.105 * (i % 2 ? 1 : -1) - ); - } - - const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], - ].flat(); - const args = [verticesGeo, facesGeo, radius, 0]; - let decaGeometry = new THREE.PolyhedronGeometry(...args); - - if (tempShowNumbers) { - let g = decaGeometry; - - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - new THREE.Vector2(0.67, 1), // br - new THREE.Vector2(0, 0.5), // bt - new THREE.Vector2(1, 0.5), // tl - new THREE.Vector2(0.67, 0), // bl - ]; - - for (let i = 0; i < 10; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 175px Arial`; - ctx.fillStyle = tempTextColor; - let text = i + 1; - if (i === 5) { - text += "."; - } - ctx.fillText( - text, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - decahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry; - const wireframe = new THREE.WireframeGeometry( - decahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - decahedron = line; - } else if (tempImage) { - const decaGeo = decaGeometry; - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - decahedron = new THREE.Mesh(decaGeo, material); - } else { - const decahedronGeometry = decaGeometry; - - const decaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); - } - - decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - decahedron.castShadow = true; - scene.add(decahedron); - - const t = (1 + Math.sqrt(5)) / 2; - const r = 1 / t; - const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) - - const verticesCannon = []; - for (let i = 0; i < verticesGeo.length; i += 3) { - verticesCannon.push( - new CANNON.Vec3( - verticesGeo[i] * scaleFactor, - verticesGeo[i + 1] * scaleFactor, - verticesGeo[i + 2] * scaleFactor - ) - ); - } - - const facesCannon = []; - for (let i = 0; i < facesGeo.length; i += 3) { - facesCannon.push([ - facesGeo[i], - facesGeo[i + 1], - facesGeo[i + 2], - ]); - } - - // Create a ConvexPolyhedron shape from the scaled vertices and faces - const decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesCannon, - faces: facesCannon, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const decahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: decahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - if (tempShowNumbers) { - decahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getDecaScore(decahedron); - }); - } - world.addBody(decahedronBody); - decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - decahedronBody.applyImpulse(offset, rollingForce); - decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh - decahedron.quaternion.copy(decahedronBody.quaternion); - - if (quaternionShared != null && quaternionShared != undefined) { - decahedron.quaternion.copy(quaternionShared); - decahedronBody.quaternion.copy(quaternionShared); - } - - diceArray.push([ - decahedron, - decahedronBody, - "deca", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - function makeNumbers() { let c = document.createElement("canvas"); c.width = 1024; @@ -2034,487 +1268,6 @@ define([ return new THREE.CanvasTexture(c); } - function createDodecahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let dodecahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid - let tileSize = 512; - let g = new THREE.DodecahedronGeometry(1.25); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - const base = new THREE.Vector2(0, 0.5); - const center = new THREE.Vector2(); - const angle = THREE.MathUtils.degToRad(72); - let baseUVs = [ - base - .clone() - .rotateAround(center, angle * 1) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 2) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 3) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 4) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 0) - .addScalar(0.5), - ]; - - for (let i = 0; i < 12; i++) { - // 12 faces for a dodecahedron - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[4].x + u) / tileDimension.x, - (baseUVs[4].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold ${tileSize / 3}px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - dodecahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const dodedodecahedronTransaprentGeometry = - new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron - const wireframe = new THREE.WireframeGeometry( - dodedodecahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - dodecahedron = line; - } else if (tempImage) { - const dodecaGeo = new THREE.DodecahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - dodecahedron = new THREE.Mesh(dodecaGeo, material); - } else { - const dodecahedronGeometry = new THREE.DodecahedronGeometry( - 1.25 - ); // Size of the tetrahedron - - const dodecaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - dodecahedron = new THREE.Mesh( - dodecahedronGeometry, - dodecaMaterial - ); - } - - dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - dodecahedron.castShadow = true; - scene.add(dodecahedron); - - const t = 1.618; - const r = 0.618; - const scaleFactor = 0.75; - - const vertices = [ - new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(0, -r, -t).scale(scaleFactor), - new CANNON.Vec3(0, -r, t).scale(scaleFactor), - new CANNON.Vec3(0, r, -t).scale(scaleFactor), - new CANNON.Vec3(0, r, t).scale(scaleFactor), - new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(-r, t, 0).scale(scaleFactor), - new CANNON.Vec3(r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(r, t, 0).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(-t, 0, r).scale(scaleFactor), - new CANNON.Vec3(t, 0, r).scale(scaleFactor), - ]; - - const indices = [ - [3, 11, 7], - [3, 7, 15], - [3, 15, 13], - [7, 19, 17], - [7, 17, 6], - [7, 6, 15], - [17, 4, 8], - [17, 8, 10], - [17, 10, 6], - [8, 0, 16], - [8, 16, 2], - [8, 2, 10], - [0, 12, 1], - [0, 1, 18], - [0, 18, 16], - [6, 10, 2], - [6, 2, 13], - [6, 13, 15], - [2, 16, 18], - [2, 18, 3], - [2, 3, 13], - [18, 1, 9], - [18, 9, 11], - [18, 11, 3], - [4, 14, 12], - [4, 12, 0], - [4, 0, 8], - [11, 9, 5], - [11, 5, 19], - [11, 19, 7], - [19, 5, 14], - [19, 14, 4], - [19, 4, 17], - [1, 12, 14], - [1, 14, 5], - [1, 5, 9], - ]; - - // Create a ConvexPolyhedron shape from the vertices and faces - const dodecahedronShape = new CANNON.ConvexPolyhedron({ - vertices: vertices, - faces: indices, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const dodecahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: dodecahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - dodecahedronBody.sleepSpeedLimit = 0.5; - dodecahedronBody.sleepTimeLimit = 3; - console.log(dodecahedronBody); - if (tempShowNumbers) { - dodecahedronBody.addEventListener("sleep", () => { - sleepCounter++; - getDodecaScore(dodecahedron); - }); - } - world.addBody(dodecahedronBody); - dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - dodecahedronBody.applyImpulse(offset, rollingForce); - dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh - dodecahedron.quaternion.copy(dodecahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { - dodecahedron.quaternion.copy(quaternionShared); - dodecahedronBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - dodecahedron, - dodecahedronBody, - "dodeca", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - - function createIcosahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor - ) { - let icosahedron; - let tempShowNumbers = ifNumbers == null ? showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? toggleTransparent : ifTransparent; - let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : textColor; - - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - let g = new THREE.IcosahedronGeometry(1.5); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - [0.067, 0.25], - [0.933, 0.25], - [0.5, 1], - ].map((p) => { - return new THREE.Vector2(...p); - }); - for (let i = 0; i < 20; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 175px Arial`; - ctx.fillStyle = tempTextColor; - let text = i + 1; - if (i == 5) { - text + "."; - } - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - icosahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const icosahedronTransparentGeometry = - new THREE.IcosahedronGeometry(1.5); // Size of the Icosahedron - const wireframe = new THREE.WireframeGeometry( - icosahedronTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - icosahedron = line; - } else if (tempImage) { - const boxGeo = new THREE.IcosahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - icosahedron = new THREE.Mesh(boxGeo, material); - } else { - const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron - - const icosaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : presentColor, - wireframe: false, - }); - - icosahedron = new THREE.Mesh( - icosahedronGeometry, - icosaMaterial - ); - } - icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - icosahedron.castShadow = true; - scene.add(icosahedron); - - // Vertices - // Vertices - const t = (1 + Math.sqrt(5)) / 2; - const scaleFactor = 0.6; - const verticesIcosa = [ - new CANNON.Vec3(-1, t, 0).scale(scaleFactor), - new CANNON.Vec3(1, t, 0).scale(scaleFactor), - new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), - new CANNON.Vec3(1, -t, 0).scale(scaleFactor), - new CANNON.Vec3(0, -1, t).scale(scaleFactor), - new CANNON.Vec3(0, 1, t).scale(scaleFactor), - new CANNON.Vec3(0, -1, -t).scale(scaleFactor), - new CANNON.Vec3(0, 1, -t).scale(scaleFactor), - new CANNON.Vec3(t, 0, -1).scale(scaleFactor), - new CANNON.Vec3(t, 0, 1).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), - new CANNON.Vec3(-t, 0, 1).scale(scaleFactor), - ]; - - // Faces - const facesIcosa = [ - [0, 11, 5], - [0, 5, 1], - [0, 1, 7], - [0, 7, 10], - [0, 10, 11], - [1, 5, 9], - [5, 11, 4], - [11, 10, 2], - [10, 7, 6], - [7, 1, 8], - [3, 9, 4], - [3, 4, 2], - [3, 2, 6], - [3, 6, 8], - [3, 8, 9], - [4, 9, 5], - [2, 4, 11], - [6, 2, 10], - [8, 6, 7], - [9, 8, 1], - ]; - - // Create a ConvexPolyhedron shape from the vertices and faces - const icosahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesIcosa, - faces: facesIcosa, - }); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const icosahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: icosahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - icosahedronBody.sleepSpeedLimit = 0.5; - icosahedronBody.sleepTimeLimit = 3; - - if (tempShowNumbers) { - icosahedronBody.addEventListener("sleep", () => { - console.log("icosa going to sleeep"); - sleepCounter++; - getIcosaScore(icosahedron); - }); - } - world.addBody(icosahedronBody); - icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); - icosahedronBody.applyImpulse(offset, rollingForce); - icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh - icosahedron.quaternion.copy(icosahedronBody.quaternion); - - if (quaternionShared != null && quaternionShared != undefined) { - icosahedron.quaternion.copy(quaternionShared); - icosahedronBody.quaternion.copy(quaternionShared); - } - - diceArray.push([ - icosahedron, - icosahedronBody, - "icosa", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - ]); - } - const timeStep = 1 / 20; function throwDice() { @@ -2527,7 +1280,7 @@ define([ presentScore = 0; for (let i = 0; i < diceArray.length; i++) { diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); - diceArray[i][1].applyImpulse(offset, rollingForce); + diceArray[i][1].applyImpulse(ctx.offset, ctx.rollingForce); diceArray[i][1].position.set(0, 10, 0); } for (let i = 0; i < diceArray.length; i++) { @@ -2554,7 +1307,7 @@ define([ createIcosahedron(); } lastRoll = ""; - // if (showNumbers) { + // if (ctx.showNumbers) { // getScore(); // } } @@ -2600,23 +1353,21 @@ define([ for (let i = 0; i < faceVectors.length; i++) { let faceVector = faceVectors[i]; faceVector.vector.applyEuler(body.rotation); - console.log(Math.abs(faceVector.vector.y)); if (minValue > Math.abs(1 - faceVector.vector.y)) { minValue = Math.abs(1 - faceVector.vector.y); minInd = i; } - // if (Math.abs(faceVector.vector.y).toString().substring(0, 1) == '1') { - // lastRoll += faceVectors[i].face + ' +' - // presentScore += faceVectors[i].face - // updateElements() - // break - // } } if (!ifRemove) { lastRoll += faceVectors[minInd].face + " + "; presentScore += faceVectors[minInd].face; updateElements(); } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVectors[minInd].face; + } + } return faceVectors[minInd].face; } function getCubeScore(body, ifRemove) { @@ -2655,6 +1406,11 @@ define([ presentScore += faceVector.face; updateElements(); } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVector.face; + } + } return faceVector.face; } } @@ -2688,6 +1444,11 @@ define([ updateElements(); break; } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVector.face; + } + } return faceVector.face; } } @@ -2784,6 +1545,16 @@ define([ presentScore += faceNumber; updateElements(); } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceNumber; + } + } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceNumber; + } + } return faceNumber; } } @@ -2827,6 +1598,11 @@ define([ updateElements(); break; } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVector.face; + } + } return faceVector.face; } } @@ -2855,7 +1631,7 @@ define([ } // function changeColors() { // for (let i = 0; i < diceArray.length; i++) { - // diceArray[i][0].material.color?.set(presentColor); + // diceArray[i][0].material.color?.set(ctx.presentColor); // diceArray[i][0].material.needsUpdate = true; // } // } @@ -2899,6 +1675,42 @@ define([ break; } } + + function getScores() { + presentScore = 0; + lastRoll = ""; + lastRollElement.textContent = ""; + + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][3]) { + switch (diceArray[i][2]) { + case "cube": + score = getCubeScore(diceArray[i][0]); + break; + case "icosa": + score = getIcosaScore(diceArray[i][0]); + break; + case "deca": + score = getDecaScore(diceArray[i][0]); + break; + case "dodeca": + score = getDodecaScore(diceArray[i][0]); + break; + case "octa": + score = getOctaScore(diceArray[i][0]); + break; + case "tetra": + score = getTetraScore(diceArray[i][0]); + break; + default: + console.log(`Unknown type: ${diceArray[i][3]}`); + continue; + } + } + } + } + + let awake = false; animate(); function animate() { @@ -2913,6 +1725,14 @@ define([ diceArray[i][0]?.position?.copy(diceArray[i][1].position); diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); } + if (world.hasActiveBodies == false && awake == true) { + awake = false; + console.log("the world is going to sleep now bye bye"); + getScores(); + } + if (world.hasActiveBodies == true) { + awake = true; + } renderer.render(scene, camera); } diff --git a/activities/3DVolume.activity/js/creatOcta.js b/activities/3DVolume.activity/js/creatOcta.js new file mode 100644 index 000000000..fef2d8ae6 --- /dev/null +++ b/activities/3DVolume.activity/js/creatOcta.js @@ -0,0 +1,202 @@ +function createOctahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let octahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.OctahedronGeometry(1.6); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + + for (let i = 0; i < 9; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 200px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1 + (i == 5 || i == 8 ? "" : ""), + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + octahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const octahedronTransparentGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron + const wireframe = new THREE.WireframeGeometry( + octahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + octahedron = line; + } else if (false) { + const octahedronGeometry = new THREE.OctahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + octahedron = new THREE.Mesh(octahedronGeometry, material); + } else { + const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron + + const octaMaterial = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + octahedron = new THREE.Mesh(octahedronGeometry, octaMaterial); + } + octahedron.castShadow = true; + scene.add(octahedron); + + const scaleFactor = 0.8; // Change this value to scale the shape (e.g., 2 for doubling the size) + + const verticesOcta = [ + new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) + new CANNON.Vec3(-2 * scaleFactor, 0, 0), // Vertex 2 (left) + new CANNON.Vec3(0, 2 * scaleFactor, 0), // Vertex 3 (top) + new CANNON.Vec3(0, -2 * scaleFactor, 0), // Vertex 4 (bottom) + new CANNON.Vec3(0, 0, 2 * scaleFactor), // Vertex 5 (front) + new CANNON.Vec3(0, 0, -2 * scaleFactor), // Vertex 6 (back) + ]; + + // Define the faces of the octahedron (counter-clockwise order) + const facesOcta = [ + [0, 2, 4], // Triangle 1 (right, top, front) + [0, 4, 3], // Triangle 2 (right, front, bottom) + [0, 3, 5], // Triangle 3 (right, bottom, back) + [0, 5, 2], // Triangle 4 (right, back, top) + [1, 2, 5], // Triangle 5 (left, top, back) + [1, 5, 3], // Triangle 6 (left, back, bottom) + [1, 3, 4], // Triangle 7 (left, bottom, front) + [1, 4, 2], // Triangle 8 (left, front, top) + ]; + + const octahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesOcta, + faces: facesOcta, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const octahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: octahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + + let previousSleepState = 1; + + // const octahedronBody = new Proxy(octahedronBody, { + // set(target, property, value) { + // if ( + // property === "sleepState" && + // previousSleepState === 2 && + // value === 1 + // ) { + // onSleepStateChangeToOne(octahedron); + // } + // target[property] = value; + // return true; + // }, + // }); + + world.addBody(octahedronBody); + + // if (tempShowNumbers) { + // octahedronBody.addEventListener("sleep", () => { + // previousSleepState = 2; + // console.log(octahedronBody.sleepState); + // sleepCounter++; + // getOctaScore(octahedron); + // }); + // } + + octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + octahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh + octahedron.quaternion.copy(octahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + octahedron.quaternion.copy(quaternionShared); + octahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + octahedron, + octahedronBody, + "octa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); +} diff --git a/activities/3DVolume.activity/js/createCube.js b/activities/3DVolume.activity/js/createCube.js new file mode 100644 index 000000000..baf41baf5 --- /dev/null +++ b/activities/3DVolume.activity/js/createCube.js @@ -0,0 +1,183 @@ +function createCube( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let boxMesh; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = + sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 2); + let tileSize = 512; + let g = new THREE.BoxGeometry(2, 2, 2); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let baseUVs = [ + [0, 1], + [1, 1], + [0, 0], + [1, 0], + ].map((p) => { + return new THREE.Vector2(...p); + }); + let uvs = []; + let vTemp = new THREE.Vector2(); + let vCenter = new THREE.Vector2(0.5, 0.5); + for (let i = 0; i < 6; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + baseUVs.forEach((buv) => { + uvs.push( + (buv.x + u) / tileDimension.x, + (buv.y + v) / tileDimension.y + ); + }); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 300px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + boxMesh = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); + const wireframe = new THREE.WireframeGeometry( + boxTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + boxMesh = line; + } else if (false) { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + const material = new THREE.MeshPhongMaterial({ map: texture }); + + boxMesh = new THREE.Mesh(boxGeo, material); + } else { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + const boxMat = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + boxMesh = new THREE.Mesh(boxGeo, boxMat); + } + boxMesh.castShadow = true; + scene.add(boxMesh); + + const boxPhysmat = new CANNON.Material(); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const boxBody = new CANNON.Body({ + mass: 1, + shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), + position: new CANNON.Vec3(x, y, z), + material: boxPhysmat, + friction: 0.1, + restitution: 5, + }); + + let previousSleepState = 1; + + // const boxBody = new Proxy(boxBody, { + // set(target, property, value) { + // if ( + // property === "sleepState" && + // previousSleepState === 2 && + // value === 1 + // ) { + // onSleepStateChangeToOne(boxMesh); + // } + // // previousSleepState = target[property]; // update previous state + // target[property] = value; + // return true; + // }, + // }); + + world.addBody(boxBody); + + // if (tempShowNumbers) { + // boxBody.addEventListener("sleep", () => { + // previousSleepState = 2; + // console.log(boxBody.sleepState); + // sleepCounter++; + // getCubeScore(boxMesh); + // }); + // } + + boxBody.angularVelocity.set(0.5, 0.5, 0.5); + boxBody.applyImpulse(ctx.offset, ctx.rollingForce); + + const groundBoxContactMat = new CANNON.ContactMaterial( + groundPhysMat, + boxPhysmat, + { friction: 0.5 } + ); + + world.addContactMaterial(groundBoxContactMat); + if (quaternionShared != null && quaternionShared != undefined) { + boxMesh.quaternion.copy(quaternionShared); + boxBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + boxMesh, + boxBody, + "cube", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + "0", + ]); +} \ No newline at end of file diff --git a/activities/3DVolume.activity/js/createDeca.js b/activities/3DVolume.activity/js/createDeca.js new file mode 100644 index 000000000..140b13660 --- /dev/null +++ b/activities/3DVolume.activity/js/createDeca.js @@ -0,0 +1,227 @@ +function createDecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let decahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + const sides = 10; + const radius = 1.3; + const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], + ].flat(); + + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); + } + + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ].flat(); + const args = [verticesGeo, facesGeo, radius, 0]; + let decaGeometry = new THREE.PolyhedronGeometry(...args); + + if (tempShowNumbers) { + let g = decaGeometry; + + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + new THREE.Vector2(0.67, 1), // br + new THREE.Vector2(0, 0.5), // bt + new THREE.Vector2(1, 0.5), // tl + new THREE.Vector2(0.67, 0), // bl + ]; + + for (let i = 0; i < 10; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 175px Arial`; + ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i === 5) { + text += "."; + } + ctx.fillText( + text, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + decahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const decahedronTransaprentGeometry = decaGeometry; + const wireframe = new THREE.WireframeGeometry( + decahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + decahedron = line; + } else if (false) { + const decaGeo = decaGeometry; + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + decahedron = new THREE.Mesh(decaGeo, material); + } else { + const decahedronGeometry = decaGeometry; + + const decaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); + } + + decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true; + scene.add(decahedron); + + const t = (1 + Math.sqrt(5)) / 2; + const r = 1 / t; + const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) + + const verticesCannon = []; + for (let i = 0; i < verticesGeo.length; i += 3) { + verticesCannon.push( + new CANNON.Vec3( + verticesGeo[i] * scaleFactor, + verticesGeo[i + 1] * scaleFactor, + verticesGeo[i + 2] * scaleFactor + ) + ); + } + + const facesCannon = []; + for (let i = 0; i < facesGeo.length; i += 3) { + facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); + } + + // Create a ConvexPolyhedron shape from the scaled vertices and faces + const decahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesCannon, + faces: facesCannon, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const decahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: decahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + // if (tempShowNumbers) { + // decahedronBody.addEventListener("sleep", () => { + // sleepCounter++; + // getDecaScore(decahedron); + // }); + // } + world.addBody(decahedronBody); + decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh + decahedron.quaternion.copy(decahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + decahedron.quaternion.copy(quaternionShared); + decahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + decahedron, + decahedronBody, + "deca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); +} diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/createDodeca.js new file mode 100644 index 000000000..3d67d6b5f --- /dev/null +++ b/activities/3DVolume.activity/js/createDodeca.js @@ -0,0 +1,265 @@ +function createDodecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let dodecahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid + let tileSize = 512; + let g = new THREE.DodecahedronGeometry(1.25); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + const base = new THREE.Vector2(0, 0.5); + const center = new THREE.Vector2(); + const angle = THREE.MathUtils.degToRad(72); + let baseUVs = [ + base + .clone() + .rotateAround(center, angle * 1) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 2) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 3) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 4) + .addScalar(0.5), + base + .clone() + .rotateAround(center, angle * 0) + .addScalar(0.5), + ]; + + for (let i = 0; i < 12; i++) { + // 12 faces for a dodecahedron + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y, + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + + (baseUVs[3].x + u) / tileDimension.x, + (baseUVs[3].y + v) / tileDimension.y, + (baseUVs[4].x + u) / tileDimension.x, + (baseUVs[4].y + v) / tileDimension.y, + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold ${tileSize / 3}px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + tex.anisotropy = 16; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + dodecahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const dodedodecahedronTransaprentGeometry = + new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron + const wireframe = new THREE.WireframeGeometry( + dodedodecahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + dodecahedron = line; + } else if (false) { + const dodecaGeo = new THREE.DodecahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + dodecahedron = new THREE.Mesh(dodecaGeo, material); + } else { + const dodecahedronGeometry = new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron + + const dodecaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial); + } + + dodecahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + dodecahedron.castShadow = true; + scene.add(dodecahedron); + + const t = 1.618; + const r = 0.618; + const scaleFactor = 0.75; + + const vertices = [ + new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(1, -1, -1).scale(scaleFactor), + new CANNON.Vec3(1, -1, 1).scale(scaleFactor), + new CANNON.Vec3(1, 1, -1).scale(scaleFactor), + new CANNON.Vec3(1, 1, 1).scale(scaleFactor), + new CANNON.Vec3(0, -r, -t).scale(scaleFactor), + new CANNON.Vec3(0, -r, t).scale(scaleFactor), + new CANNON.Vec3(0, r, -t).scale(scaleFactor), + new CANNON.Vec3(0, r, t).scale(scaleFactor), + new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(-r, t, 0).scale(scaleFactor), + new CANNON.Vec3(r, -t, 0).scale(scaleFactor), + new CANNON.Vec3(r, t, 0).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(t, 0, -r).scale(scaleFactor), + new CANNON.Vec3(-t, 0, r).scale(scaleFactor), + new CANNON.Vec3(t, 0, r).scale(scaleFactor), + ]; + + const indices = [ + [3, 11, 7], + [3, 7, 15], + [3, 15, 13], + [7, 19, 17], + [7, 17, 6], + [7, 6, 15], + [17, 4, 8], + [17, 8, 10], + [17, 10, 6], + [8, 0, 16], + [8, 16, 2], + [8, 2, 10], + [0, 12, 1], + [0, 1, 18], + [0, 18, 16], + [6, 10, 2], + [6, 2, 13], + [6, 13, 15], + [2, 16, 18], + [2, 18, 3], + [2, 3, 13], + [18, 1, 9], + [18, 9, 11], + [18, 11, 3], + [4, 14, 12], + [4, 12, 0], + [4, 0, 8], + [11, 9, 5], + [11, 5, 19], + [11, 19, 7], + [19, 5, 14], + [19, 14, 4], + [19, 4, 17], + [1, 12, 14], + [1, 14, 5], + [1, 5, 9], + ]; + + // Create a ConvexPolyhedron shape from the vertices and faces + const dodecahedronShape = new CANNON.ConvexPolyhedron({ + vertices: vertices, + faces: indices, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const dodecahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: dodecahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + dodecahedronBody.sleepSpeedLimit = 0.2; + dodecahedronBody.sleepTimeLimit = 3; + console.log(dodecahedronBody); + // if (tempShowNumbers) { + // dodecahedronBody.addEventListener("sleep", () => { + // sleepCounter++; + // getDodecaScore(dodecahedron); + // }); + // } + world.addBody(dodecahedronBody); + dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + dodecahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh + dodecahedron.quaternion.copy(dodecahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + dodecahedron.quaternion.copy(quaternionShared); + dodecahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + dodecahedron, + dodecahedronBody, + "dodeca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); +} diff --git a/activities/3DVolume.activity/js/createIcosa.js b/activities/3DVolume.activity/js/createIcosa.js new file mode 100644 index 000000000..f3a33ef21 --- /dev/null +++ b/activities/3DVolume.activity/js/createIcosa.js @@ -0,0 +1,215 @@ +function createIcosahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let icosahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.IcosahedronGeometry(1.5); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + for (let i = 0; i < 20; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 175px Arial`; + ctx.fillStyle = tempTextColor; + let text = i + 1; + if (i == 5) { + text + "."; + } + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + icosahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const icosahedronTransparentGeometry = new THREE.IcosahedronGeometry( + 1.5 + ); // Size of the Icosahedron + const wireframe = new THREE.WireframeGeometry( + icosahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + icosahedron = line; + } else if (false) { + const boxGeo = new THREE.IcosahedronGeometry(2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + icosahedron = new THREE.Mesh(boxGeo, material); + } else { + const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron + + const icosaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + icosahedron = new THREE.Mesh(icosahedronGeometry, icosaMaterial); + } + icosahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + icosahedron.castShadow = true; + scene.add(icosahedron); + + // Vertices + // Vertices + const t = (1 + Math.sqrt(5)) / 2; + const scaleFactor = 0.8; + const verticesIcosa = [ + new CANNON.Vec3(-1, t, 0).scale(scaleFactor), + new CANNON.Vec3(1, t, 0).scale(scaleFactor), + new CANNON.Vec3(-1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(1, -t, 0).scale(scaleFactor), + new CANNON.Vec3(0, -1, t).scale(scaleFactor), + new CANNON.Vec3(0, 1, t).scale(scaleFactor), + new CANNON.Vec3(0, -1, -t).scale(scaleFactor), + new CANNON.Vec3(0, 1, -t).scale(scaleFactor), + new CANNON.Vec3(t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(t, 0, 1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, -1).scale(scaleFactor), + new CANNON.Vec3(-t, 0, 1).scale(scaleFactor), + ]; + + // Faces + const facesIcosa = [ + [0, 11, 5], + [0, 5, 1], + [0, 1, 7], + [0, 7, 10], + [0, 10, 11], + [1, 5, 9], + [5, 11, 4], + [11, 10, 2], + [10, 7, 6], + [7, 1, 8], + [3, 9, 4], + [3, 4, 2], + [3, 2, 6], + [3, 6, 8], + [3, 8, 9], + [4, 9, 5], + [2, 4, 11], + [6, 2, 10], + [8, 6, 7], + [9, 8, 1], + ]; + + // Create a ConvexPolyhedron shape from the vertices and faces + const icosahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesIcosa, + faces: facesIcosa, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const icosahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: icosahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + icosahedronBody.sleepSpeedLimit = 0.5; + icosahedronBody.sleepTimeLimit = 3; + + // if (tempShowNumbers) { + // icosahedronBody.addEventListener("sleep", () => { + // console.log("icosa going to sleeep"); + // sleepCounter++; + // getIcosaScore(icosahedron); + // }); + // } + world.addBody(icosahedronBody); + icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + icosahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh + icosahedron.quaternion.copy(icosahedronBody.quaternion); + + if (quaternionShared != null && quaternionShared != undefined) { + icosahedron.quaternion.copy(quaternionShared); + icosahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + icosahedron, + icosahedronBody, + "icosa", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); +} diff --git a/activities/3DVolume.activity/js/createTetra.js b/activities/3DVolume.activity/js/createTetra.js new file mode 100644 index 000000000..1418457f5 --- /dev/null +++ b/activities/3DVolume.activity/js/createTetra.js @@ -0,0 +1,197 @@ +function createTetrahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat +) { + let tetrahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 5); + let tileSize = 512; + let g = new THREE.TetrahedronGeometry(1.7); + + let c = document.createElement("canvas"); + let div = document.createElement("div"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let uvs = []; + + let baseUVs = [ + [0.067, 0.25], + [0.933, 0.25], + [0.5, 1], + ].map((p) => { + return new THREE.Vector2(...p); + }); + let arrOfNums = [ + [2, 1, 3], + [1, 2, 4], + [3, 1, 4], + [2, 3, 4], + ]; + for (let i = 0; i < 4; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + uvs.push( + (baseUVs[0].x + u) / tileDimension.x, + (baseUVs[0].y + v) / tileDimension.y, + (baseUVs[1].x + u) / tileDimension.x, + (baseUVs[1].y + v) / tileDimension.y, + (baseUVs[2].x + u) / tileDimension.x, + (baseUVs[2].y + v) / tileDimension.y + ); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 150px Arial`; + ctx.fillStyle = tempTextColor; + // ctx.fillText( + // i + 1, + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + let aStep = (Math.PI * 2) / 3; + let yAlign = Math.PI * 0.5; + let tileQuarter = tileSize * 0.25; + for (let j = 0; j < 3; j++) { + ctx.save(); + ctx.translate( + (u + 0.5) * tileSize + + Math.cos(j * aStep - yAlign) * tileQuarter, + c.height - + (v + 0.5) * tileSize + + Math.sin(j * aStep - yAlign) * tileQuarter + ); + ctx.rotate((j * Math.PI * 2) / 3); + ctx.fillText(arrOfNums[i][j], 0, 0); + ctx.restore(); + } + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + tetrahedron = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const tetrahedronTransparentGeometry = new THREE.TetrahedronGeometry( + 1.7 + ); // Size of the tetrahedron + const wireframe = new THREE.WireframeGeometry( + tetrahedronTransparentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + tetrahedron = line; + } else if (false) { + const boxGeo = new THREE.TetrahedronGeometry(1.7); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + tetrahedron = new THREE.Mesh(boxGeo, material); + } else { + const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron + + const tetraMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial); + } + + tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + tetrahedron.castShadow = true; + scene.add(tetrahedron); + + const verticesTetra = [ + new CANNON.Vec3(1, 1, 1), // Vertex 1 (right) + new CANNON.Vec3(-1, -1, 1), // Vertex 2 (top) + new CANNON.Vec3(-1, 1, -1), // Vertex 3 (left) + new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) + ]; + const facesTetra = [ + [2, 1, 0], // Triangle 1 (right, top, left) + [0, 3, 2], // Triangle 2 (right, front, top) + [1, 3, 0], // Triangle 3 (top, front, left) + [2, 3, 1], // Triangle 4 (left, right, front) + ]; + // Create a ConvexPolyhedron shape from the vertices and faces + const tetrahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesTetra, + faces: facesTetra, + }); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const tetrahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: tetrahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + // if (tempShowNumbers) { + // tetrahedronBody.addEventListener("sleep", () => { + // sleepCounter++; + // getTetraScore(tetrahedron); + // }); + // } + world.addBody(tetrahedronBody); + tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + tetrahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh + tetrahedron.quaternion.copy(tetrahedronBody.quaternion); + if (quaternionShared != null && quaternionShared != undefined) { + tetrahedron.quaternion.copy(quaternionShared); + tetrahedronBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + tetrahedron, + tetrahedronBody, + "tetra", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + ]); +} From bb21b241d80400ee4c8326167406b96ccd5ef3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Lask=C3=A9?= Date: Sun, 7 Jul 2024 09:11:10 +0200 Subject: [PATCH 14/60] Fix volume palette behavior --- activities/3DVolume.activity/js/palettes/volumepalette.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.js b/activities/3DVolume.activity/js/palettes/volumepalette.js index d2a936693..d545528ee 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.js +++ b/activities/3DVolume.activity/js/palettes/volumepalette.js @@ -12,9 +12,11 @@ define([ this.setContent([containerElem]); + var that = this; document .getElementById("volume-palette") .addEventListener("click", () => { + that.getPalette().childNodes[0].style.backgroundImage = invoker.style.backgroundImage; this.popDown(); }); }; From da9319a5d4e4028cfa8f3fc08d2b98ae5dedf266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Lask=C3=A9?= Date: Sun, 7 Jul 2024 09:34:04 +0200 Subject: [PATCH 15/60] Fix library loading issue --- activities/3DVolume.activity/index.html | 4 ++-- activities/3DVolume.activity/js/activity.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 7beab0b22..a20907603 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -18,6 +18,7 @@ href="lib/sugar-web/graphics/css/sugar-200dpi.css" /> + @@ -25,11 +26,10 @@ + - - diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 068df0221..c7c80fee4 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1,11 +1,11 @@ define([ "sugar-web/activity/activity", "sugar-web/env", - "./palettes/bgpalette", - "./palettes/volumepalette", - "./palettes/colorpalettefill", - "./palettes/colorpalettetext", - "./palettes/zoompalette", + "activity/palettes/bgpalette", + "activity/palettes/volumepalette", + "activity/palettes/colorpalettefill", + "activity/palettes/colorpalettetext", + "activity/palettes/zoompalette", "sugar-web/graphics/presencepalette", "tutorial", "sugar-web/graphics/journalchooser", From a99599caa90de2ca716183b8da318cbe159b2e52 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 8 Jul 2024 00:30:13 +0530 Subject: [PATCH 16/60] changes --- activities/3DVolume.activity/css/activity.css | 60 ++-- activities/3DVolume.activity/icons/sensor.svg | 70 +++++ .../icons/view-fullscreen.svg | 6 + .../3DVolume.activity/icons/view-return.svg | 74 +++++ activities/3DVolume.activity/index.html | 10 +- activities/3DVolume.activity/js/activity.js | 285 ++++++++++++++++-- activities/3DVolume.activity/js/creatOcta.js | 11 +- activities/3DVolume.activity/js/createCube.js | 12 +- activities/3DVolume.activity/js/createDeca.js | 218 ++++++++++---- .../3DVolume.activity/js/createDodeca.js | 13 +- .../3DVolume.activity/js/createIcosa.js | 11 +- .../3DVolume.activity/js/createTetra.js | 11 +- .../js/palettes/colorpalettefill.js | 14 +- .../js/palettes/colorpalettetext.js | 11 +- .../js/palettes/volumepalette.js | 4 +- activities/3DVolume.activity/lib/orbit.js | 10 +- activities/3DVolume.activity/lib/tutorial.js | 73 +++-- 17 files changed, 724 insertions(+), 169 deletions(-) create mode 100644 activities/3DVolume.activity/icons/sensor.svg create mode 100644 activities/3DVolume.activity/icons/view-fullscreen.svg create mode 100644 activities/3DVolume.activity/icons/view-return.svg diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index feff77be9..50d342638 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -83,6 +83,22 @@ body { background-image: url(../icons/number_volume.svg); border-radius: 5px; } +#fullscreen-button { + background-image: url(../icons/view-fullscreen.svg); +} + +#unfullscreen-button { + background-image: url(../icons/view-return.svg); + border: 0; + border-radius: 0px; + margin: 1px; + width: 45px; + height: 45px; + top: 10px; + right: 15px; + position: absolute; + visibility: hidden; +} #zoom-button { height: 45px; @@ -101,6 +117,14 @@ body { background-image: url(../icons/throw.svg); outline: none; } +#sensor-button { + height: 45px; + width: 45xpx; + background-size: cover; + background-repeat: no-repeat; + background-image: url(../icons/sensor.svg); + outline: none; +} #transparent-button { margin: 0; padding: 0; @@ -185,6 +209,9 @@ body { #main-toolbar #help-button { background-image: url(../icons/help.svg); } +/* #main-toolbar { + visibility: hidden; +} */ .vertical-line { width: 5px; height: 55px; @@ -241,11 +268,10 @@ body { margin: 0; overflow: hidden; height: 100vh; - background-color: #f0f0f0; } #game-container { - position: relative; + position: absolute; } /* #throw-button { @@ -420,26 +446,26 @@ body { .introjs-overlay { background-color: #000 !important; - opacity: 0.8 !important; + opacity: .8 !important; } .introjs-tooltip { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif!important; border-radius: 6px !important; padding: 2px 1px !important; font-size: 14px !important; } -.introjs-helperLayer { +.introjs-helperLayer{ background: inherit !important; opacity: 0.6 !important; } .customTooltip .introjs-tooltip-header { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #ffffff; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color : #ffffff; text-shadow: none; - /* background-color: #808080; */ + background-color: #808080; margin: 0; padding: 0px 10px; border-bottom: 1px solid #ebebeb; @@ -491,7 +517,7 @@ body { .customTooltip .introjs-tooltipbuttons { display: flex; - flex-wrap: wrap; + flex-wrap:wrap; justify-content: center; align-items: center; cursor: pointer; @@ -506,7 +532,7 @@ body { border-radius: 22px; margin: 5px 8px 8px 8px; width: fit-content; - /* background-color: #808080 !important; */ + background-color: #808080 !important; display: flex !important; align-items: center !important; color: white !important; @@ -515,20 +541,20 @@ body { } .customTooltip .introjs-button:focus { - -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); - box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); border: 0px; - /* background-color: #808080 !important; */ + background-color: #808080 !important; } .customTooltip .introjs-disabled { color: black !important; border: 0px; - opacity: 0.65; + opacity: .65; } .customTooltip .introjs-disabled:focus { - -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); - box-shadow: 0 0 0 0rem rgba(158, 158, 158, 0.5); + -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); + box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); border: 0px; -} +} \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/sensor.svg b/activities/3DVolume.activity/icons/sensor.svg new file mode 100644 index 000000000..4f71f8533 --- /dev/null +++ b/activities/3DVolume.activity/icons/sensor.svg @@ -0,0 +1,70 @@ + +image/svg+xml \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/view-fullscreen.svg b/activities/3DVolume.activity/icons/view-fullscreen.svg new file mode 100644 index 000000000..f71058c69 --- /dev/null +++ b/activities/3DVolume.activity/icons/view-fullscreen.svg @@ -0,0 +1,6 @@ + + +]> + + \ No newline at end of file diff --git a/activities/3DVolume.activity/icons/view-return.svg b/activities/3DVolume.activity/icons/view-return.svg new file mode 100644 index 000000000..dade2d208 --- /dev/null +++ b/activities/3DVolume.activity/icons/view-return.svg @@ -0,0 +1,74 @@ + +image/svg+xml \ No newline at end of file diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index a20907603..63ef395c2 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -30,6 +30,8 @@ + + @@ -53,7 +55,11 @@ >
- + + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index c7c80fee4..bc3cebd74 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -51,15 +51,29 @@ define([ document.getElementById("zoom-button"), undefined ); - var paletteColorFill = new colorpaletteFill.ColorPalette( - document.getElementById("color-button-fill"), - undefined - ); - var paletteColorText = new colorpaletteText.ColorPalette( - document.getElementById("color-button-text"), - undefined - ); + // Full screen + document + .getElementById("fullscreen-button") + .addEventListener("click", function () { + document.getElementById("main-toolbar").style.visibility = + "hidden"; + document.getElementById("game-container").style.top = "0px"; + document.getElementById( + "unfullscreen-button" + ).style.visibility = "visible"; + }); + document + .getElementById("unfullscreen-button") + .addEventListener("click", function () { + document.getElementById("main-toolbar").style.visibility = + "visible"; + document.getElementById("game-container").style.top = "55px"; + document.getElementById( + "unfullscreen-button" + ).style.visibility = "hidden"; + + }); const randomDirection = new CANNON.Vec3( 0.3, // Random x-axis value between -0.5 and 0.5 @@ -76,7 +90,7 @@ define([ offset: new CANNON.Vec3(0, 0.1, 0), rollingForce: randomDirection.scale(2), }; - + let presentBackground = null; let presentScore = 0; let lastRoll = ""; let diceArray = []; @@ -125,12 +139,25 @@ define([ } }); + var paletteColorFill = new colorpaletteFill.ColorPalette( + document.getElementById("color-button-fill"), + undefined, + ctx + ); + + var paletteColorText = new colorpaletteText.ColorPalette( + document.getElementById("color-button-text"), + undefined, + ctx + ); + var onNetworkDataReceived = function (msg) { if (presence.getUserInfo().networkId === msg.user.networkId) { return; } if (msg.action == "init") { - data = msg.content; + changeBoardBackground(msg.content[1]); + data = msg.content[0]; console.log(data); for (let i = 0; i < data.length; i++) { let createFunction = null; @@ -176,7 +203,9 @@ define([ diceArray, world, scene, - groundPhysMat + groundPhysMat, + data[i][7], + data[i][8] ); } } @@ -245,7 +274,9 @@ define([ diceArray, world, scene, - groundPhysMat + groundPhysMat, + msg.content.sharedAngVel1, + msg.content.sharedAngVel2 ); } }; @@ -262,6 +293,8 @@ define([ diceArray[i][6], diceArray[i][3], diceArray[i][4], + diceArray[i][7], + diceArray[i][8], ]); } console.log("writing..."); @@ -334,7 +367,9 @@ define([ diceArray, world, scene, - groundPhysMat + groundPhysMat, + data[i][7], + data[i][8] ); } } @@ -803,6 +838,8 @@ define([ } if (createFunction) { + let angVel1 = Math.random() * (1 - 0.1) + 0.1; + let angVel2 = Math.random() * (1 - 0.1) + 0.1; createFunction( null, null, @@ -818,7 +855,9 @@ define([ diceArray, world, scene, - groundPhysMat + groundPhysMat, + angVel1, + angVel2 ); if (presence) { @@ -839,6 +878,8 @@ define([ yCoordinateShared: null, quaternionShared: null, sharedTextColor: ctx.textColor, + sharedAngVel1: angVel1, + sharedAngVel2: angVel2, }, } ); @@ -979,12 +1020,14 @@ define([ diceArray[i][6], diceArray[i][3], diceArray[i][4], + diceArray[i][7], + diceArray[i][8], ]); } presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "init", - content: presenceDiceArray, + content: [presenceDiceArray, presentBackground], }); }; @@ -1049,6 +1092,148 @@ define([ }); world.allowSleep = true; + function adjustGravity(gamma) { + console.log("adjusting gravity"); + var gravityStrength = 9.82; // Earth's gravity in m/s^2 + var maxTilt = 90; // Maximum tilt value + + // Map the gamma value to the gravity direction + var gravityX = (gamma / maxTilt) * gravityStrength; + + // Set the new gravity vector + world.gravity.set(gravityX, -gravityStrength, 0); + } + + var useragent = navigator.userAgent.toLowerCase(); + var sensorButton = document.getElementById("sensor-button"); + var sensorMode = false; + var readyToWatch = false; + console.log(useragent.indexOf("android")); + + // if (useragent.indexOf('android') != -1 || useragent.indexOf('iphone') != -1 || useragent.indexOf('ipad') != -1 || useragent.indexOf('ipod') != -1 || useragent.indexOf('mozilla/5.0 (mobile') != -1) { + // document.addEventListener('deviceready', function() { + // readyToWatch = true; + // }, false); + // sensorButton.disabled = false; + // } else { + // sensorButton.disabled = true; + // } + + sensorButton.addEventListener("click", function () { + sensorMode = !sensorMode; + if (sensorMode) { + sensorButton.classList.add("active"); + // window.addEventListener( + // "deviceorientation", + // function (event) { + // if (sensorMode) { + // handleDeviceOrientation(event); + // } + // }, + // true + // ); + watchId = navigator.accelerometer.watchAcceleration(accelerationChanged, null, { frequency: 500 }); + + console.log(screen.orientation.type); + } else { + sensorButton.classList.remove("active"); + world.gravity.set(0, -9.81, 0); + } + }); + + + function setGravity(gravityDirection) { + let gravityX = 0; + let gravityY = 0; + let gravityZ = 0; + + switch (gravityDirection) { + case 0: + gravityX = 1; // Right + break; + case 1: + gravityX = -1; // Right-bottom (diagonal down-right) + gravityY = -1; + break; + case 2: + gravityY = -1; // Bottom (straight down) + break; + case 3: + gravityX = -1; // Left-bottom (diagonal down-left) + gravityY = 1; + break; + case 4: + gravityX = -1; // Left (straight left) + break; + case 5: + gravityX = -1; // Left-top (diagonal up-left) + gravityY = -1; + break; + case 6: + gravityY = 1; // Top (straight up) + break; + case 7: + gravityX = 1; // Right-top (diagonal up-right) + gravityY = 1; + break; + default: + break; + } + + // Assuming you have a Cannon.js world instance called 'world' + world.gravity.set(gravityX * 9.82, gravityY * 9.82, gravityZ * 9.82); // Scale gravity with 9.82 m/s² (approximate Earth gravity) + } + + + function accelerationChanged(acceleration) { + if (!sensorMode) return; + if (acceleration.x < -4.5) { + if (acceleration.y > 4.75) + setGravity(3); + else if (acceleration.y < -4.75) + setGravity(5); + else + setGravity(4); + } else if (acceleration.x <= 4.5 && acceleration.x >= -4.5) { + if (acceleration.y > 4.75) + setGravity(2); + else if (acceleration.y < -4.75) + setGravity(6); + } else if (acceleration.x > 4.5) { + if (acceleration.y > 4.75) + setGravity(1); + else if (acceleration.y < -4.75) + setGravity(7); + else + setGravity(0); + } + } + + function handleDeviceOrientation(event) { + var gamma = event.gamma; // Tilt left or right (range from -90 to 90) + + // Adjust gravity based on tilt + adjustGravity(gamma); + } + + + // if (readyToWatch) { + // sensorButton.disabled = false; + // sensorButton.addEventListener("click", function () { + // sensorMode = !sensorMode; + // if (sensorMode) { + // sensorButton.classList.add("active"); + // } else { + // sensorButton.classList.remove("active"); + // } + // }); + // window.addEventListener('deviceorientation', function(event) { + // if (sensorMode) { + // handleDeviceOrientation(event); + // } + // }, true); + // } + const groundGeo = new THREE.PlaneGeometry(30, 30); const groundMat = new THREE.MeshPhongMaterial({ side: THREE.DoubleSide, @@ -1288,6 +1473,7 @@ define([ world.addBody(diceArray[i][1]); } } else { + console.log("what is never supposed to happen is happening :0"); for (let i = 0; i < dices.cube; i++) { createCube(); } @@ -1455,9 +1641,42 @@ define([ } function getDecaScore(body, ifRemove) { - console.log("getting deca"); + // Define face vectors based on vertices + const faceVectors = [ + { vector: new THREE.Vector3(0, 0, 1), face: 1 }, + { vector: new THREE.Vector3(0, 0, -1), face: 2 }, + ]; + + const sides = 10; + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + faceVectors.push({ + vector: new THREE.Vector3(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)), + face: i + 3, + }); + } + + for (const faceVector of faceVectors) { + faceVector.vector.normalize().applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) === 1) { + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVector.face; + } + } + return faceVector.face; + } + } } + function getIcosaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; @@ -1568,29 +1787,27 @@ define([ ); } - function getDodecaScore(body, ifRemove) { + function getDecaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; - - // Dodecahedron face vectors + + // Decahedron face vectors const faceVectors = [ - { vector: new THREE.Vector3(1, 1, 1), face: 1 }, - { vector: new THREE.Vector3(1, 1, -1), face: 6 }, - { vector: new THREE.Vector3(1, -1, 1), face: 11 }, - { vector: new THREE.Vector3(1, -1, -1), face: 4 }, - { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, - { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, - { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, - { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, - { vector: new THREE.Vector3(0, phi, 1 / phi), face: 9 }, - { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, - { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, - { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, + { vector: new THREE.Vector3(0, 1, phi), face: 1 }, + { vector: new THREE.Vector3(0, 1, -phi), face: 2 }, + { vector: new THREE.Vector3(0, -1, phi), face: 3 }, + { vector: new THREE.Vector3(0, -1, -phi), face: 8 }, + { vector: new THREE.Vector3(1, phi, 0), face: 5 }, + { vector: new THREE.Vector3(1, -phi, 0), face: 6 }, + { vector: new THREE.Vector3(-1, phi, 0), face: 7 }, + { vector: new THREE.Vector3(-1, -phi, 0), face: 4 }, + { vector: new THREE.Vector3(phi, 0, 1), face: 9 }, + { vector: new THREE.Vector3(phi, 0, -1), face: 10 }, ]; - + for (const faceVector of faceVectors) { faceVector.vector.normalize().applyEuler(body.rotation); - + if (Math.round(faceVector.vector.y) === 1) { if (!ifRemove) { lastRoll += faceVector.face + " + "; @@ -1607,6 +1824,7 @@ define([ } } } + function toggleTransparency() { for (let i = 0; i < diceArray.length; i++) { @@ -1639,6 +1857,7 @@ define([ function changeBoardBackground(selectedBoard) { console.log("changing bg now"); console.log(selectedBoard); + presentBackground = selectedBoard; let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { case "green-board": diff --git a/activities/3DVolume.activity/js/creatOcta.js b/activities/3DVolume.activity/js/creatOcta.js index fef2d8ae6..ecbf00e51 100644 --- a/activities/3DVolume.activity/js/creatOcta.js +++ b/activities/3DVolume.activity/js/creatOcta.js @@ -13,7 +13,9 @@ function createOctahedron( diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let octahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -182,7 +184,10 @@ function createOctahedron( // }); // } - octahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + octahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); octahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh octahedron.quaternion.copy(octahedronBody.quaternion); @@ -198,5 +203,7 @@ function createOctahedron( tempTransparent, tempFillColor, tempTextColor, + angVel1, + angVel2 ]); } diff --git a/activities/3DVolume.activity/js/createCube.js b/activities/3DVolume.activity/js/createCube.js index baf41baf5..14292d8e6 100644 --- a/activities/3DVolume.activity/js/createCube.js +++ b/activities/3DVolume.activity/js/createCube.js @@ -13,7 +13,9 @@ function createCube( diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let boxMesh; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -155,8 +157,9 @@ function createCube( // getCubeScore(boxMesh); // }); // } - - boxBody.angularVelocity.set(0.5, 0.5, 0.5); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + boxBody.angularVelocity.set(angVel1, angVel2, 0.5); boxBody.applyImpulse(ctx.offset, ctx.rollingForce); const groundBoxContactMat = new CANNON.ContactMaterial( @@ -178,6 +181,7 @@ function createCube( tempTransparent, tempFillColor, tempTextColor, - "0", + angVel1, + angVel2 ]); } \ No newline at end of file diff --git a/activities/3DVolume.activity/js/createDeca.js b/activities/3DVolume.activity/js/createDeca.js index 140b13660..0e9b1c17c 100644 --- a/activities/3DVolume.activity/js/createDeca.js +++ b/activities/3DVolume.activity/js/createDeca.js @@ -1,3 +1,37 @@ +const sides = 10; +const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], +].flat(); + +for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); +} + +const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], +].flat(); + function createDecahedron( sharedColor, ifNumbers, @@ -9,28 +43,29 @@ function createDecahedron( yCoordinateShared, quaternionShared, sharedTextColor, - ctx, + ctx, diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let decahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; let tempTransparent = ifTransparent == null ? ctx.toggleTransparent : ifTransparent; - // let tempImage = ifImage == null ? showImage : ifImage; let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; let tempTextColor = sharedTextColor != null ? sharedTextColor : ctx.textColor; - const sides = 10; const radius = 1.3; const verticesGeo = [ [0, 0, 1], [0, 0, -1], ].flat(); + const sides = 10; for (let i = 0; i < sides; ++i) { const b = (i * Math.PI * 2) / sides; verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); @@ -58,68 +93,61 @@ function createDecahedron( [1, 11, 10], [1, 2, 11], ].flat(); - const args = [verticesGeo, facesGeo, radius, 0]; - let decaGeometry = new THREE.PolyhedronGeometry(...args); + const decaGeometry = new THREE.PolyhedronGeometry(verticesGeo, facesGeo, 1.3, 0); + if (tempShowNumbers) { - let g = decaGeometry; - - let tileDimension = new THREE.Vector2(4, 5); - let tileSize = 512; - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - - let baseUVs = [ - new THREE.Vector2(0.67, 1), // br - new THREE.Vector2(0, 0.5), // bt - new THREE.Vector2(1, 0.5), // tl - new THREE.Vector2(0.67, 0), // bl - ]; - - for (let i = 0; i < 10; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 175px Arial`; - ctx.fillStyle = tempTextColor; - let text = i + 1; - if (i === 5) { - text += "."; - } - ctx.fillText( - text, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } + let vertStep = THREE.MathUtils.degToRad(36); + let vertices = [ + [0, 0, 1], + [Math.cos(vertStep * 0), Math.sin(vertStep * 0), 0.105], + [Math.cos(vertStep * 1), Math.sin(vertStep * 1), -0.105], + [Math.cos(vertStep * 2), Math.sin(vertStep * 2), 0.105], + ].map((p) => { + return new THREE.Vector3(...p); + }); + let h = vertices[0].distanceTo(vertices[2]); + let w = vertices[1].distanceTo(vertices[3]); + let u = (w / h) * 0.5; + let v01 = new THREE.Vector3().subVectors(vertices[1], vertices[0]); + let v02 = new THREE.Vector3().subVectors(vertices[2], vertices[0]); + let dot = v02.clone().normalize().dot(v01); + let v = 1 - dot / h; + + let gSide = new THREE.BufferGeometry() + .setFromPoints(vertices) + .rotateZ(-vertStep); + gSide.setIndex([0, 1, 2, 0, 2, 3]); + gSide.setAttribute( + "uv", + new THREE.Float32BufferAttribute( + [0.5, 1, 0.5 - u, v, 0.5, 0, 0.5 + u, v], + 2 + ) + ); + gSide.computeVertexNormals(); + gSide = gSide.toNonIndexed(); - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + // all sides + let gs = []; - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; + for (let i = 0; i < 5; i++) { + let a = vertStep * 2 * i; + let g1 = gSide.clone().rotateZ(-a); + recomputeUVs(g1, i * 2 + 0); + let g2 = gSide + .clone() + .rotateX(Math.PI) + .rotateZ(vertStep + a); + recomputeUVs(g2, i * 2 + 1); + gs.push(g1, g2); + } + let g = BufferGeometryUtils.mergeBufferGeometries(gs); - let m = new THREE.MeshPhongMaterial({ - map: tex, + let m = new THREE.MeshLambertMaterial({ + map: getNumbers(), }); + decahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { const decahedronTransaprentGeometry = decaGeometry; @@ -205,7 +233,13 @@ function createDecahedron( // }); // } world.addBody(decahedronBody); - decahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); @@ -223,5 +257,67 @@ function createDecahedron( tempTransparent, tempFillColor, tempTextColor, + angVel1, + angVel2, ]); } + +function recomputeUVs(g, idx) { + let tiles = { + x: 4, + y: 4, + }; + let x = idx % tiles.x; + let y = Math.floor(idx / tiles.x); + + let uvs = g.attributes.uv; + for (let i = 0; i < uvs.count; i++) { + let u = (uvs.getX(i) + x) / tiles.x; + let v = (uvs.getY(i) + y) / tiles.y; + uvs.setXY(i, u, v); + } +} + +function getNumbers() { + let tileSize = 256; + let tiles = { + x: 4, + y: 4, + }; + + let c = document.createElement("canvas"); + let ctx = c.getContext("2d"); + c.width = tileSize * 4; + c.height = tileSize * 4; + let u = (val) => tileSize * 0.01 * val; + + ctx.fillStyle = "rgba(0, 127, 255, 1)"; + ctx.fillRect(0, 0, c.width, c.height); + + ctx.font = `bold ${u(40)}px Arial`; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = "#f80"; + + for (let i = 0; i < sides; i++) { + let y = Math.floor(i / tiles.x); + let x = i % tiles.x; + let text = i + 1; + + ctx.save(); + ctx.translate(x * tileSize, c.height - y * tileSize); + + ctx.fillText(text, u(50), -u(40)); + if (text == 6 || text == 9) { + ctx.fillText("_", u(50), -u(40)); + } + ctx.restore(); + } + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = "srgb"; + // tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); + tex.needsUpdate = true; + + return tex; +} diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/createDodeca.js index 3d67d6b5f..ccb05136a 100644 --- a/activities/3DVolume.activity/js/createDodeca.js +++ b/activities/3DVolume.activity/js/createDodeca.js @@ -13,7 +13,9 @@ function createDodecahedron( diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let dodecahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -237,7 +239,6 @@ function createDodecahedron( }); dodecahedronBody.sleepSpeedLimit = 0.2; dodecahedronBody.sleepTimeLimit = 3; - console.log(dodecahedronBody); // if (tempShowNumbers) { // dodecahedronBody.addEventListener("sleep", () => { // sleepCounter++; @@ -245,7 +246,11 @@ function createDodecahedron( // }); // } world.addBody(dodecahedronBody); - dodecahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + dodecahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); dodecahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh dodecahedron.quaternion.copy(dodecahedronBody.quaternion); @@ -261,5 +266,7 @@ function createDodecahedron( tempTransparent, tempFillColor, tempTextColor, + angVel1, + angVel2 ]); } diff --git a/activities/3DVolume.activity/js/createIcosa.js b/activities/3DVolume.activity/js/createIcosa.js index f3a33ef21..5e0962bba 100644 --- a/activities/3DVolume.activity/js/createIcosa.js +++ b/activities/3DVolume.activity/js/createIcosa.js @@ -13,7 +13,9 @@ function createIcosahedron( diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let icosahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -193,7 +195,10 @@ function createIcosahedron( // }); // } world.addBody(icosahedronBody); - icosahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + icosahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); icosahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh icosahedron.quaternion.copy(icosahedronBody.quaternion); @@ -211,5 +216,7 @@ function createIcosahedron( tempTransparent, tempFillColor, tempTextColor, + angVel1, + angVel2 ]); } diff --git a/activities/3DVolume.activity/js/createTetra.js b/activities/3DVolume.activity/js/createTetra.js index 1418457f5..530e15af0 100644 --- a/activities/3DVolume.activity/js/createTetra.js +++ b/activities/3DVolume.activity/js/createTetra.js @@ -13,7 +13,9 @@ function createTetrahedron( diceArray, world, scene, - groundPhysMat + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { let tetrahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -177,7 +179,10 @@ function createTetrahedron( // }); // } world.addBody(tetrahedronBody); - tetrahedronBody.angularVelocity.set(0.5, 0.5, 0.5); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + tetrahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); tetrahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh tetrahedron.quaternion.copy(tetrahedronBody.quaternion); @@ -193,5 +198,7 @@ function createTetrahedron( tempTransparent, tempFillColor, tempTextColor, + angVel1, + angVel2 ]); } diff --git a/activities/3DVolume.activity/js/palettes/colorpalettefill.js b/activities/3DVolume.activity/js/palettes/colorpalettefill.js index 380700c57..52a2a5114 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettefill.js +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.js @@ -1,7 +1,8 @@ define([ "sugar-web/graphics/palette", "text!activity/palettes/colorpalettefill.html", -], function (palette, template) { + "sugar-web/env", +], function (palette, template, env) { var colorpalette = {}; colorpalette.ColorPalette = function (invoker, primaryText) { palette.Palette.call(this, invoker, primaryText); @@ -14,11 +15,18 @@ define([ this.setContent([containerElem]); let that = this; + // that.getPalette().childNodes[0].style.backgroundColor = ctx.presentColor + env.getEnvironment(function (err, environment) { + currentenv = environment; + that.getPalette().childNodes[0].style.backgroundColor = currentenv.user.colorvalue.fill + }); + const colors = document.querySelectorAll(".color-fill"); colors.forEach((color) => { color.addEventListener("click", function () { const selectedColor = this.style.backgroundColor; + that.getPalette().childNodes[0].style.backgroundColor = this.style.backgroundColor; const colorChangeEvent = new CustomEvent( "color-selected-fill", { @@ -31,6 +39,10 @@ define([ }); }; + function myFunc123() { + console.log("HI FROM PAINT") + } + var addEventListener = function (type, listener, useCapture) { console.log("adding event listener"); return this.getPalette().addEventListener(type, listener, useCapture); diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.js b/activities/3DVolume.activity/js/palettes/colorpalettetext.js index 8d2e1f0ef..08bca2c32 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettetext.js +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.js @@ -1,7 +1,8 @@ define([ "sugar-web/graphics/palette", "text!activity/palettes/colorpalettetext.html", -], function (palette, template) { + "sugar-web/env", +], function (palette, template,env) { var colorpalette = {}; colorpalette.ColorPalette = function (invoker, primaryText) { palette.Palette.call(this, invoker, primaryText); @@ -13,11 +14,19 @@ define([ this.setContent([containerElem]); let that = this; + // that.getPalette().childNodes[0].style.backgroundColor = ctx.textColor + env.getEnvironment(function (err, environment) { + currentenv = environment; + that.getPalette().childNodes[0].style.backgroundColor = currentenv.user.colorvalue.stroke + }); + + const colors = document.querySelectorAll(".color-text"); colors.forEach((color) => { color.addEventListener("click", function () { const selectedColor = this.style.backgroundColor; + that.getPalette().childNodes[0].style.backgroundColor = this.style.backgroundColor; const colorChangeEvent = new CustomEvent( "color-selected-text", { diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.js b/activities/3DVolume.activity/js/palettes/volumepalette.js index d545528ee..754cc528a 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.js +++ b/activities/3DVolume.activity/js/palettes/volumepalette.js @@ -13,10 +13,12 @@ define([ this.setContent([containerElem]); var that = this; + document .getElementById("volume-palette") .addEventListener("click", () => { - that.getPalette().childNodes[0].style.backgroundImage = invoker.style.backgroundImage; + that.getPalette().childNodes[0].style.backgroundImage = + invoker.style.backgroundImage; this.popDown(); }); }; diff --git a/activities/3DVolume.activity/lib/orbit.js b/activities/3DVolume.activity/lib/orbit.js index eec2346b2..5aa559659 100644 --- a/activities/3DVolume.activity/lib/orbit.js +++ b/activities/3DVolume.activity/lib/orbit.js @@ -540,6 +540,7 @@ function rotateLeft( angle ) { sphericalDelta.theta -= angle; + } @@ -724,10 +725,10 @@ rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - rotateStart.copy( rotateEnd ); + event.stopPropagation(); + rotateStart.copy( rotateEnd ); scope.update(); - } function handleMouseMoveDolly( event ) { @@ -746,6 +747,7 @@ } + dollyStart.copy( dollyEnd ); scope.update(); @@ -952,12 +954,16 @@ const element = scope.domElement; + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); rotateStart.copy( rotateEnd ); + event.stopPropagation(); + + } function handleTouchMovePan( event ) { diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index 9e881de5d..39ace9e81 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -9,70 +9,65 @@ define([], function () { 'Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.', }, { - element: '#bg-button', - position: 'bottom', - title: 'Change board background', - intro: 'Choose any one of the three boards backgrounds according to their different frictions.', - }, - { - element: '#color-button', - title: 'Change Volume Color', - intro: 'Choose any one of the colors for your volumes. The default is your buddy color.', + element: '#tetra-button', + title: 'Add Tetrahedrons', + intro: 'Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.', }, { element: '#cube-button', title: 'Add Cubes', intro: 'Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.', }, - { - element: '#tetra-button', - title: 'Add Tetrahedrons', - intro: 'Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.', - }, { element: '#octa-button', title: 'Add Octahedrons', intro: 'Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.', }, { - element: '#clear-button', - title: 'Remove volumes', - intro: 'Select this and click on any volume on the board to remove it.', + element: '#deca-button', + title: 'Add Decahedrons', + intro: 'Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.', }, { - element: '#reset-button', - title: 'Reset Scores', - intro: 'Click on this button to reset the scores calculated through the numbered dice.', + element: '#dodeca-button', + title: 'Add Dodecahedron', + intro: 'Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.', }, { - element: '#number-button', - title: 'Add volumes with numbers', - intro: 'Select this to add volumes with numbers on their sides. These will be used to calculate the score. This works with the earlier volume buttons, keep this and any one of the volume buttons selected and click on the board to add numbered volumes.', + element: '#icosa-button', + title: 'Add Icosahedron', + intro: 'Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.', }, { - element: '#transparent-button', - title: 'Add transparent volumes to the board', - intro: 'Select this to add transparent volumes. This works with the earlier volume buttons, keep this and any one of the volume buttons selected and click on the board to add transparent volumes to the board.', + element: '#clear-button', + title: 'Remove volumes', + intro: 'Select this and click on any volume on the board to remove it.', }, { - element: '#image-button', - title: 'Add images to the sides of the volume', - intro: 'Select this to add volumes with images on their sides. You can select an image from the journal to apply it to the volumes. This works with the earlier volume buttons, keep this and any one of the volume buttons selected and click on the board to add image volumes to the board.', + element: '#volume-button', + title: 'Select Volume Type', + intro: 'Select the type of volume you want to add to the board.', }, { - element: '#solid-button', - title: 'Make all volumes transparent', - intro: 'Toggle this button to make all the volumes added on the board transparent.', + element: '#color-button-fill', + title: 'Change Volume Color', + intro: 'Choose any one of the colors for your volumes. The default is your buddy color.', + }, + { + element: '#color-button-text', + title: 'Change Volume Text Color', + intro: 'Choose any one of the colors for your the text on your volumes. The default is your buddy stroke color.', }, { - element: '#zoom-in-button', - title: 'Zoom In Button', - intro: 'Click this button to zoom in on the scene.', + element: '#bg-button', + position: 'bottom', + title: 'Change board background', + intro: 'Choose any one of the three boards backgrounds according to their different frictions.', }, { - element: '#zoom-out-button', - title: 'Zoom Out Button', - intro: 'Click this button to zoom out of the scene.', + element: '#zoom-button', + title: 'Zoom In/Out', + intro: 'Use the zoom pallette to zoom in or out..', }, { element: '#throw-button', @@ -80,7 +75,7 @@ define([], function () { intro: 'Use this button to shake the volumes on the board.', }, { - element: '.arrow-container', + element: '.right-container', title: 'Rotate the board', intro: 'Select the arrow pointing in the direction you wish the board to rotate.', }, From f885933cdbdc1c51df5aca5c5a9dfeb79a3add3f Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 8 Jul 2024 00:33:51 +0530 Subject: [PATCH 17/60] changes --- activities/3DVolume.activity/js/creatOcta.js | 8 +- activities/3DVolume.activity/js/createCube.js | 368 +++++++++--------- activities/3DVolume.activity/js/createDeca.js | 8 +- .../3DVolume.activity/js/createDodeca.js | 8 +- .../3DVolume.activity/js/createIcosa.js | 10 +- .../3DVolume.activity/js/createTetra.js | 8 +- 6 files changed, 210 insertions(+), 200 deletions(-) diff --git a/activities/3DVolume.activity/js/creatOcta.js b/activities/3DVolume.activity/js/creatOcta.js index ecbf00e51..44988b133 100644 --- a/activities/3DVolume.activity/js/creatOcta.js +++ b/activities/3DVolume.activity/js/creatOcta.js @@ -184,8 +184,10 @@ function createOctahedron( // }); // } - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; octahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); octahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -204,6 +206,6 @@ function createOctahedron( tempFillColor, tempTextColor, angVel1, - angVel2 + angVel2, ]); } diff --git a/activities/3DVolume.activity/js/createCube.js b/activities/3DVolume.activity/js/createCube.js index 14292d8e6..c6d6c344a 100644 --- a/activities/3DVolume.activity/js/createCube.js +++ b/activities/3DVolume.activity/js/createCube.js @@ -1,187 +1,185 @@ function createCube( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor, - ctx, - diceArray, - world, - scene, - groundPhysMat, - sharedAngVel1, - sharedAngVel2 + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat, + sharedAngVel1, + sharedAngVel2 ) { - let boxMesh; - let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? ctx.toggleTransparent : ifTransparent; - // let tempImage = ifImage == null ? showImage : ifImage; - let tempFillColor = - sharedColor != null ? sharedColor : ctx.presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : ctx.textColor; - - - if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 2); - let tileSize = 512; - let g = new THREE.BoxGeometry(2, 2, 2); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let baseUVs = [ - [0, 1], - [1, 1], - [0, 0], - [1, 0], - ].map((p) => { - return new THREE.Vector2(...p); - }); - let uvs = []; - let vTemp = new THREE.Vector2(); - let vCenter = new THREE.Vector2(0.5, 0.5); - for (let i = 0; i < 6; i++) { - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - baseUVs.forEach((buv) => { - uvs.push( - (buv.x + u) / tileDimension.x, - (buv.y + v) / tileDimension.y - ); - }); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold 300px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1, - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - boxMesh = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); - const wireframe = new THREE.WireframeGeometry( - boxTransparentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - boxMesh = line; - } else if (false) { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - const material = new THREE.MeshPhongMaterial({ map: texture }); - - boxMesh = new THREE.Mesh(boxGeo, material); - } else { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - const boxMat = new THREE.MeshPhongMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - wireframe: false, - }); - boxMesh = new THREE.Mesh(boxGeo, boxMat); - } - boxMesh.castShadow = true; - scene.add(boxMesh); - - const boxPhysmat = new CANNON.Material(); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - const boxBody = new CANNON.Body({ - mass: 1, - shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), - position: new CANNON.Vec3(x, y, z), - material: boxPhysmat, - friction: 0.1, - restitution: 5, - }); - - let previousSleepState = 1; - - // const boxBody = new Proxy(boxBody, { - // set(target, property, value) { - // if ( - // property === "sleepState" && - // previousSleepState === 2 && - // value === 1 - // ) { - // onSleepStateChangeToOne(boxMesh); - // } - // // previousSleepState = target[property]; // update previous state - // target[property] = value; - // return true; - // }, - // }); - - world.addBody(boxBody); - - // if (tempShowNumbers) { - // boxBody.addEventListener("sleep", () => { - // previousSleepState = 2; - // console.log(boxBody.sleepState); - // sleepCounter++; - // getCubeScore(boxMesh); - // }); - // } - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - boxBody.angularVelocity.set(angVel1, angVel2, 0.5); - boxBody.applyImpulse(ctx.offset, ctx.rollingForce); - - const groundBoxContactMat = new CANNON.ContactMaterial( - groundPhysMat, - boxPhysmat, - { friction: 0.5 } - ); - - world.addContactMaterial(groundBoxContactMat); - if (quaternionShared != null && quaternionShared != undefined) { - boxMesh.quaternion.copy(quaternionShared); - boxBody.quaternion.copy(quaternionShared); - } - diceArray.push([ - boxMesh, - boxBody, - "cube", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - angVel1, - angVel2 - ]); -} \ No newline at end of file + let boxMesh; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + // let tempImage = ifImage == null ? showImage : ifImage; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + if (tempShowNumbers) { + let tileDimension = new THREE.Vector2(4, 2); + let tileSize = 512; + let g = new THREE.BoxGeometry(2, 2, 2); + + let c = document.createElement("canvas"); + c.width = tileSize * tileDimension.x; + c.height = tileSize * tileDimension.y; + let ctx = c.getContext("2d"); + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + let baseUVs = [ + [0, 1], + [1, 1], + [0, 0], + [1, 0], + ].map((p) => { + return new THREE.Vector2(...p); + }); + let uvs = []; + let vTemp = new THREE.Vector2(); + let vCenter = new THREE.Vector2(0.5, 0.5); + for (let i = 0; i < 6; i++) { + let u = i % tileDimension.x; + let v = Math.floor(i / tileDimension.x); + baseUVs.forEach((buv) => { + uvs.push( + (buv.x + u) / tileDimension.x, + (buv.y + v) / tileDimension.y + ); + }); + + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = `bold 300px Arial`; + ctx.fillStyle = tempTextColor; + ctx.fillText( + i + 1, + (u + 0.5) * tileSize, + c.height - (v + 0.5) * tileSize + ); + } + g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = THREE.SRGBColorSpace; + + let m = new THREE.MeshPhongMaterial({ + map: tex, + }); + + boxMesh = new THREE.Mesh(g, m); + } else if (tempTransparent) { + const boxTransparentGeometry = new THREE.BoxGeometry(2, 2, 2); + const wireframe = new THREE.WireframeGeometry(boxTransparentGeometry); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + boxMesh = line; + } else if (false) { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + const material = new THREE.MeshPhongMaterial({ map: texture }); + + boxMesh = new THREE.Mesh(boxGeo, material); + } else { + const boxGeo = new THREE.BoxGeometry(2, 2, 2); + const boxMat = new THREE.MeshPhongMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + boxMesh = new THREE.Mesh(boxGeo, boxMat); + } + boxMesh.castShadow = true; + scene.add(boxMesh); + + const boxPhysmat = new CANNON.Material(); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + const boxBody = new CANNON.Body({ + mass: 1, + shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), + position: new CANNON.Vec3(x, y, z), + material: boxPhysmat, + friction: 0.1, + restitution: 5, + }); + + let previousSleepState = 1; + + // const boxBody = new Proxy(boxBody, { + // set(target, property, value) { + // if ( + // property === "sleepState" && + // previousSleepState === 2 && + // value === 1 + // ) { + // onSleepStateChangeToOne(boxMesh); + // } + // // previousSleepState = target[property]; // update previous state + // target[property] = value; + // return true; + // }, + // }); + + world.addBody(boxBody); + + // if (tempShowNumbers) { + // boxBody.addEventListener("sleep", () => { + // previousSleepState = 2; + // console.log(boxBody.sleepState); + // sleepCounter++; + // getCubeScore(boxMesh); + // }); + // } + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + boxBody.angularVelocity.set(angVel1, angVel2, 0.5); + boxBody.applyImpulse(ctx.offset, ctx.rollingForce); + + const groundBoxContactMat = new CANNON.ContactMaterial( + groundPhysMat, + boxPhysmat, + { friction: 0.5 } + ); + + world.addContactMaterial(groundBoxContactMat); + if (quaternionShared != null && quaternionShared != undefined) { + boxMesh.quaternion.copy(quaternionShared); + boxBody.quaternion.copy(quaternionShared); + } + diceArray.push([ + boxMesh, + boxBody, + "cube", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + angVel1, + angVel2, + ]); +} diff --git a/activities/3DVolume.activity/js/createDeca.js b/activities/3DVolume.activity/js/createDeca.js index 0e9b1c17c..a2f60b904 100644 --- a/activities/3DVolume.activity/js/createDeca.js +++ b/activities/3DVolume.activity/js/createDeca.js @@ -93,8 +93,12 @@ function createDecahedron( [1, 11, 10], [1, 2, 11], ].flat(); - const decaGeometry = new THREE.PolyhedronGeometry(verticesGeo, facesGeo, 1.3, 0); - + const decaGeometry = new THREE.PolyhedronGeometry( + verticesGeo, + facesGeo, + 1.3, + 0 + ); if (tempShowNumbers) { let vertStep = THREE.MathUtils.degToRad(36); diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/createDodeca.js index ccb05136a..1d7fd7218 100644 --- a/activities/3DVolume.activity/js/createDodeca.js +++ b/activities/3DVolume.activity/js/createDodeca.js @@ -247,8 +247,10 @@ function createDodecahedron( // } world.addBody(dodecahedronBody); - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; dodecahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); dodecahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -267,6 +269,6 @@ function createDodecahedron( tempFillColor, tempTextColor, angVel1, - angVel2 + angVel2, ]); } diff --git a/activities/3DVolume.activity/js/createIcosa.js b/activities/3DVolume.activity/js/createIcosa.js index 5e0962bba..6263e5c9a 100644 --- a/activities/3DVolume.activity/js/createIcosa.js +++ b/activities/3DVolume.activity/js/createIcosa.js @@ -9,7 +9,7 @@ function createIcosahedron( yCoordinateShared, quaternionShared, sharedTextColor, - ctx, + ctx, diceArray, world, scene, @@ -195,8 +195,10 @@ function createIcosahedron( // }); // } world.addBody(icosahedronBody); - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; icosahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); icosahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -217,6 +219,6 @@ function createIcosahedron( tempFillColor, tempTextColor, angVel1, - angVel2 + angVel2, ]); } diff --git a/activities/3DVolume.activity/js/createTetra.js b/activities/3DVolume.activity/js/createTetra.js index 530e15af0..bc4126380 100644 --- a/activities/3DVolume.activity/js/createTetra.js +++ b/activities/3DVolume.activity/js/createTetra.js @@ -179,8 +179,10 @@ function createTetrahedron( // }); // } world.addBody(tetrahedronBody); - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; tetrahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); tetrahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -199,6 +201,6 @@ function createTetrahedron( tempFillColor, tempTextColor, angVel1, - angVel2 + angVel2, ]); } From cabc1dad52a5b11291409c0d4d5e61639b986fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Lask=C3=A9?= Date: Mon, 8 Jul 2024 21:34:52 +0200 Subject: [PATCH 18/60] Remove unwished url --- activities/3DVolume.activity/index.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 63ef395c2..e4afd7386 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -30,8 +30,6 @@ - - From 1f5a86ef88423d008f724f0912631c894edcc38e Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Wed, 10 Jul 2024 12:18:26 +0530 Subject: [PATCH 19/60] changes --- activities/3DVolume.activity/js/activity.js | 134 ++++++++++---------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index bc3cebd74..41714b496 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -72,7 +72,6 @@ define([ document.getElementById( "unfullscreen-button" ).style.visibility = "hidden"; - }); const randomDirection = new CANNON.Vec3( @@ -115,7 +114,7 @@ define([ ? currentenv.user.colorvalue.fill : ctx.presentColor; - scene.background = new THREE.Color("#A9A9A9"); + scene.background = new THREE.Color("#ffffff"); console.log(ctx.presentColor); ctx.textColor = @@ -1053,7 +1052,7 @@ define([ .appendChild(renderer.domElement); const scene = new THREE.Scene(); - scene.background = new THREE.Color(ctx.presentColor); + scene.background = new THREE.Color("#ffffff"); const light = new THREE.DirectionalLight(0xffffff, 0.4); light.castShadow = true; const leftLight = new THREE.DirectionalLight(0xffffff, 0.25); @@ -1132,21 +1131,24 @@ define([ // }, // true // ); - watchId = navigator.accelerometer.watchAcceleration(accelerationChanged, null, { frequency: 500 }); - + watchId = navigator.accelerometer.watchAcceleration( + accelerationChanged, + null, + { frequency: 500 } + ); + console.log(screen.orientation.type); } else { sensorButton.classList.remove("active"); world.gravity.set(0, -9.81, 0); } }); - function setGravity(gravityDirection) { let gravityX = 0; let gravityY = 0; let gravityZ = 0; - + switch (gravityDirection) { case 0: gravityX = 1; // Right @@ -1179,33 +1181,28 @@ define([ default: break; } - + // Assuming you have a Cannon.js world instance called 'world' - world.gravity.set(gravityX * 9.82, gravityY * 9.82, gravityZ * 9.82); // Scale gravity with 9.82 m/s² (approximate Earth gravity) + world.gravity.set( + gravityX * 9.82, + gravityY * 9.82, + gravityZ * 9.82 + ); // Scale gravity with 9.82 m/s² (approximate Earth gravity) } - - + function accelerationChanged(acceleration) { if (!sensorMode) return; if (acceleration.x < -4.5) { - if (acceleration.y > 4.75) - setGravity(3); - else if (acceleration.y < -4.75) - setGravity(5); - else - setGravity(4); + if (acceleration.y > 4.75) setGravity(3); + else if (acceleration.y < -4.75) setGravity(5); + else setGravity(4); } else if (acceleration.x <= 4.5 && acceleration.x >= -4.5) { - if (acceleration.y > 4.75) - setGravity(2); - else if (acceleration.y < -4.75) - setGravity(6); + if (acceleration.y > 4.75) setGravity(2); + else if (acceleration.y < -4.75) setGravity(6); } else if (acceleration.x > 4.5) { - if (acceleration.y > 4.75) - setGravity(1); - else if (acceleration.y < -4.75) - setGravity(7); - else - setGravity(0); + if (acceleration.y > 4.75) setGravity(1); + else if (acceleration.y < -4.75) setGravity(7); + else setGravity(0); } } @@ -1216,7 +1213,6 @@ define([ adjustGravity(gamma); } - // if (readyToWatch) { // sensorButton.disabled = false; // sensorButton.addEventListener("click", function () { @@ -1243,7 +1239,7 @@ define([ const groundMesh = new THREE.Mesh(groundGeo, groundMat); groundMesh.receiveShadow = true; - groundMesh.material.color.setHex(0x656565); + groundMesh.material.color.setHex(0xc9c9c9); scene.add(groundMesh); const groundPhysMat = new CANNON.Material(); @@ -1641,41 +1637,44 @@ define([ } function getDecaScore(body, ifRemove) { - // Define face vectors based on vertices - const faceVectors = [ - { vector: new THREE.Vector3(0, 0, 1), face: 1 }, - { vector: new THREE.Vector3(0, 0, -1), face: 2 }, - ]; - - const sides = 10; - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - faceVectors.push({ - vector: new THREE.Vector3(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)), - face: i + 3, - }); - } - - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); - - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; - } - } - } + // Define face vectors based on vertices + const faceVectors = [ + { vector: new THREE.Vector3(0, 0, 1), face: 1 }, + { vector: new THREE.Vector3(0, 0, -1), face: 2 }, + ]; + const sides = 10; + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + faceVectors.push({ + vector: new THREE.Vector3( + -Math.cos(b), + -Math.sin(b), + 0.105 * (i % 2 ? 1 : -1) + ), + face: i + 3, + }); + } + + for (const faceVector of faceVectors) { + faceVector.vector.normalize().applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) === 1) { + if (!ifRemove) { + lastRoll += faceVector.face + " + "; + presentScore += faceVector.face; + updateElements(); + break; + } + for (let i = 0; i < diceArray.length; i++) { + if (body == diceArray[i][0]) { + diceArray[i][7] = faceVector.face; + } + } + return faceVector.face; + } + } + } function getIcosaScore(body, ifRemove) { // Define the golden ratio @@ -1790,7 +1789,7 @@ define([ function getDecaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; - + // Decahedron face vectors const faceVectors = [ { vector: new THREE.Vector3(0, 1, phi), face: 1 }, @@ -1804,10 +1803,10 @@ define([ { vector: new THREE.Vector3(phi, 0, 1), face: 9 }, { vector: new THREE.Vector3(phi, 0, -1), face: 10 }, ]; - + for (const faceVector of faceVectors) { faceVector.vector.normalize().applyEuler(body.rotation); - + if (Math.round(faceVector.vector.y) === 1) { if (!ifRemove) { lastRoll += faceVector.face + " + "; @@ -1824,7 +1823,6 @@ define([ } } } - function toggleTransparency() { for (let i = 0; i < diceArray.length; i++) { @@ -1887,7 +1885,7 @@ define([ break; case "default": groundMesh.material.needsUpdate = true; - groundMesh.material.color.setHex(0x656565); + groundMesh.material.color.setHex(0xc9c9c9); groundMesh.material.wireframe = false; groundMesh.material.map = null; groundBody.material.friction = 1; From 4e8fc7a3255f688cd44744c0139140c06d8b00ef Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 12 Jul 2024 23:55:20 +0530 Subject: [PATCH 20/60] changes --- activities/3DVolume.activity/css/activity.css | 12 + activities/3DVolume.activity/index.html | 6 + activities/3DVolume.activity/js/activity.js | 109 +++-- activities/3DVolume.activity/js/createDeca.js | 118 +++-- .../3DVolume.activity/js/createDeca2.js | 444 ++++++++++++++++++ activities/3DVolume.activity/lib/tutorial.js | 2 +- 6 files changed, 592 insertions(+), 99 deletions(-) create mode 100644 activities/3DVolume.activity/js/createDeca2.js diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index 50d342638..4957f1c8a 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -313,6 +313,14 @@ body { right: 100px; font-size: 1.5rem; } +#roll { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently supported by most browsers */ + +} .color-palette { display: flex; @@ -397,6 +405,10 @@ body { #left-button { background-image: url(../icons/go-left.svg); } +#try-button { + background-image: url(../icons/go-left.svg); + transform: rotate(-90deg); +} .arrow-container { width: 50%; diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index e4afd7386..2ee043eac 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -30,6 +30,8 @@ + + @@ -139,6 +141,9 @@ id="throw-button" title="Throw the dice" > + + + @@ -183,5 +188,6 @@

+ diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 41714b496..31470fc49 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1136,8 +1136,6 @@ define([ null, { frequency: 500 } ); - - console.log(screen.orientation.type); } else { sensorButton.classList.remove("active"); world.gravity.set(0, -9.81, 0); @@ -1151,31 +1149,31 @@ define([ switch (gravityDirection) { case 0: - gravityX = 1; // Right + gravityY = 1; // Right break; case 1: - gravityX = -1; // Right-bottom (diagonal down-right) - gravityY = -1; + gravityX = 1; // Right-bottom (diagonal down-right) + gravityY = 1; break; case 2: - gravityY = -1; // Bottom (straight down) + gravityY = 1; // Bottom (straight down) break; case 3: - gravityX = -1; // Left-bottom (diagonal down-left) - gravityY = 1; + gravityX = 1; // Left-bottom (diagonal down-left) + gravityY = -1; break; case 4: - gravityX = -1; // Left (straight left) + gravityY = -1; // Left (straight left) break; case 5: gravityX = -1; // Left-top (diagonal up-left) gravityY = -1; break; case 6: - gravityY = 1; // Top (straight up) + gravityX = -1; // Top (straight up) break; case 7: - gravityX = 1; // Right-top (diagonal up-right) + gravityX = -1; // Right-top (diagonal up-right) gravityY = 1; break; default: @@ -1192,20 +1190,47 @@ define([ function accelerationChanged(acceleration) { if (!sensorMode) return; - if (acceleration.x < -4.5) { - if (acceleration.y > 4.75) setGravity(3); - else if (acceleration.y < -4.75) setGravity(5); - else setGravity(4); - } else if (acceleration.x <= 4.5 && acceleration.x >= -4.5) { - if (acceleration.y > 4.75) setGravity(2); - else if (acceleration.y < -4.75) setGravity(6); - } else if (acceleration.x > 4.5) { - if (acceleration.y > 4.75) setGravity(1); - else if (acceleration.y < -4.75) setGravity(7); - else setGravity(0); + if (acceleration.y > 0) { + // right + world.gravity.set(-9.81, 0, 0); // Gravity towards the right + wakeAll(); + } else { + if (acceleration.z > 0) { + if (acceleration.y > -1 && acceleration.y < 1) { + if (acceleration.z > 6) { + // back + world.gravity.set(0, 0, 9.81); // Gravity towards the back + wakeAll(); + } else { + // straight + world.gravity.set(0, -9.81, 0); + wakeAll(); + } + } else { + // left + world.gravity.set(9.81, 0, 0); // Gravity towards the left + wakeAll(); + } + } else { + // front + world.gravity.set(0, 0, -9.81); // Gravity towards the front + wakeAll(); + } + } + + } + + function wakeAll() { + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][1].wakeUp(); } } + function myFunction() { + console.log("hello"); + } + // var intervalId = setInterval(accelerationChanged, 5000); // 2000 milliseconds (2 seconds) + function handleDeviceOrientation(event) { var gamma = event.gamma; // Tilt left or right (range from -90 to 90) @@ -1259,10 +1284,21 @@ define([ type: CANNON.Body.STATIC, material: groundPhysMat, }); + console.log(groundBody) groundBody.material.friction = 1; groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); world.addBody(groundBody); + const topWall = new CANNON.Body({ + shape: new CANNON.Box(new CANNON.Vec3(15, 15, 0.1)), + type: CANNON.Body.STATIC, + material: groundPhysMat, + }); + topWall.quaternion.setFromEuler(-Math.PI / 2, 0, 0); + topWall.position.set(0, 12, 0); + world.addBody(topWall) + + const leftWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), type: CANNON.Body.STATIC, @@ -1786,22 +1822,24 @@ define([ ); } - function getDecaScore(body, ifRemove) { + function getDodecaScore(body, ifRemove) { // Define the golden ratio const phi = (1 + Math.sqrt(5)) / 2; // Decahedron face vectors const faceVectors = [ - { vector: new THREE.Vector3(0, 1, phi), face: 1 }, - { vector: new THREE.Vector3(0, 1, -phi), face: 2 }, - { vector: new THREE.Vector3(0, -1, phi), face: 3 }, - { vector: new THREE.Vector3(0, -1, -phi), face: 8 }, - { vector: new THREE.Vector3(1, phi, 0), face: 5 }, - { vector: new THREE.Vector3(1, -phi, 0), face: 6 }, - { vector: new THREE.Vector3(-1, phi, 0), face: 7 }, - { vector: new THREE.Vector3(-1, -phi, 0), face: 4 }, - { vector: new THREE.Vector3(phi, 0, 1), face: 9 }, - { vector: new THREE.Vector3(phi, 0, -1), face: 10 }, + { vector: new THREE.Vector3(1, 1, 1), face: 1 }, + { vector: new THREE.Vector3(1, 1, -1), face: 6 }, + { vector: new THREE.Vector3(1, -1, 1), face: 11 }, + { vector: new THREE.Vector3(1, -1, -1), face: 9 }, + { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, + { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, + { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, + { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, + { vector: new THREE.Vector3(0, phi, 1 / phi), face: 4 }, + { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, + { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, + { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, ]; for (const faceVector of faceVectors) { @@ -1926,6 +1964,10 @@ define([ } } } + let try_button = document.getElementById("try-button"); + try_button.addEventListener("click", ()=>{ + diceArray[0][0].rotateY(THREE.MathUtils.degToRad(20)); + }) let awake = false; animate(); @@ -1950,6 +1992,7 @@ define([ if (world.hasActiveBodies == true) { awake = true; } + // accelerationChanged(); renderer.render(scene, camera); } diff --git a/activities/3DVolume.activity/js/createDeca.js b/activities/3DVolume.activity/js/createDeca.js index a2f60b904..11f1b903b 100644 --- a/activities/3DVolume.activity/js/createDeca.js +++ b/activities/3DVolume.activity/js/createDeca.js @@ -9,6 +9,8 @@ for (let i = 0; i < sides; ++i) { verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); } +let myDecahedron; + const facesGeo = [ [0, 2, 3], [0, 3, 4], @@ -93,68 +95,20 @@ function createDecahedron( [1, 11, 10], [1, 2, 11], ].flat(); - const decaGeometry = new THREE.PolyhedronGeometry( + const decaGeometry2 = new THREE.PolyhedronGeometry( verticesGeo, facesGeo, - 1.3, + 1, 0 ); - if (tempShowNumbers) { - let vertStep = THREE.MathUtils.degToRad(36); - let vertices = [ - [0, 0, 1], - [Math.cos(vertStep * 0), Math.sin(vertStep * 0), 0.105], - [Math.cos(vertStep * 1), Math.sin(vertStep * 1), -0.105], - [Math.cos(vertStep * 2), Math.sin(vertStep * 2), 0.105], - ].map((p) => { - return new THREE.Vector3(...p); - }); - let h = vertices[0].distanceTo(vertices[2]); - let w = vertices[1].distanceTo(vertices[3]); - let u = (w / h) * 0.5; - let v01 = new THREE.Vector3().subVectors(vertices[1], vertices[0]); - let v02 = new THREE.Vector3().subVectors(vertices[2], vertices[0]); - let dot = v02.clone().normalize().dot(v01); - let v = 1 - dot / h; - - let gSide = new THREE.BufferGeometry() - .setFromPoints(vertices) - .rotateZ(-vertStep); - gSide.setIndex([0, 1, 2, 0, 2, 3]); - gSide.setAttribute( - "uv", - new THREE.Float32BufferAttribute( - [0.5, 1, 0.5 - u, v, 0.5, 0, 0.5 + u, v], - 2 - ) - ); - gSide.computeVertexNormals(); - gSide = gSide.toNonIndexed(); - - // all sides - let gs = []; - - for (let i = 0; i < 5; i++) { - let a = vertStep * 2 * i; - let g1 = gSide.clone().rotateZ(-a); - recomputeUVs(g1, i * 2 + 0); - let g2 = gSide - .clone() - .rotateX(Math.PI) - .rotateZ(vertStep + a); - recomputeUVs(g2, i * 2 + 1); - gs.push(g1, g2); - } - let g = BufferGeometryUtils.mergeBufferGeometries(gs); - + let g = decaGeometry2; let m = new THREE.MeshLambertMaterial({ - map: getNumbers(), + map: getNumbers(tempFillColor, tempTextColor), }); - decahedron = new THREE.Mesh(g, m); } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry; + const decahedronTransaprentGeometry = decaGeometry2; const wireframe = new THREE.WireframeGeometry( decahedronTransaprentGeometry ); @@ -179,7 +133,7 @@ function createDecahedron( // Create cube mesh with the material decahedron = new THREE.Mesh(decaGeo, material); } else { - const decahedronGeometry = decaGeometry; + const decahedronGeometry = decaGeometry2; const decaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : ctx.presentColor, @@ -189,13 +143,13 @@ function createDecahedron( decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); } - decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y decahedron.castShadow = true; scene.add(decahedron); const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; - const scaleFactor = 1.2; // Change this value to scale the shape (e.g., 2 for doubling the size) + const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) const verticesCannon = []; for (let i = 0; i < verticesGeo.length; i += 3) { @@ -207,29 +161,35 @@ function createDecahedron( ) ); } - const facesCannon = []; for (let i = 0; i < facesGeo.length; i += 3) { facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); } + let cannonVertices = myVertices.map( + (v) => new CANNON.Vec3(v[0], v[1], v[2]) + ); + // Create a ConvexPolyhedron shape from the scaled vertices and faces const decahedronShape = new CANNON.ConvexPolyhedron({ vertices: verticesCannon, faces: facesCannon, }); + // let myShape = getPolyhedronShape(decahedron); + // console.log(myShape); let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; - const decahedronBody = new CANNON.Body({ + let decahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: decahedronShape, position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, }); + // decahedronBody = polyhedronShape // if (tempShowNumbers) { // decahedronBody.addEventListener("sleep", () => { // sleepCounter++; @@ -238,15 +198,17 @@ function createDecahedron( // } world.addBody(decahedronBody); - let angVel1 = - sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = - sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = 0.2 + // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = 0.2 + // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); + console.log(decahedronBody); + console.log(decahedronBody.rotation); if (quaternionShared != null && quaternionShared != undefined) { decahedron.quaternion.copy(quaternionShared); @@ -282,7 +244,7 @@ function recomputeUVs(g, idx) { } } -function getNumbers() { +function getNumbers(tempFillColor, tempTextColor) { let tileSize = 256; let tiles = { x: 4, @@ -295,13 +257,13 @@ function getNumbers() { c.height = tileSize * 4; let u = (val) => tileSize * 0.01 * val; - ctx.fillStyle = "rgba(0, 127, 255, 1)"; + ctx.fillStyle = tempFillColor; ctx.fillRect(0, 0, c.width, c.height); ctx.font = `bold ${u(40)}px Arial`; ctx.textAlign = "center"; ctx.textBaseline = "middle"; - ctx.fillStyle = "#f80"; + ctx.fillStyle = tempTextColor; for (let i = 0; i < sides; i++) { let y = Math.floor(i / tiles.x); @@ -325,3 +287,29 @@ function getNumbers() { return tex; } +function getPolyhedronShape(mesh) { + let geometry = new THREE.BufferGeometry(); + geometry.setAttribute("position", mesh.geometry.getAttribute("position")); + + geometry = BufferGeometryUtils.mergeVertices(geometry); + + let position = geometry.attributes.position.array; + let index = geometry.getIndex(); + + const points = []; + for (let i = 0; i < position.length; i += 3) { + points.push( + new CANNON.Vec3(position[i], position[i + 1], position[i + 2]) + ); + } + const myfaces = []; + for (let i = 0; i < index.length; i += 3) { + myfaces.push([index[i], index[i + 1], index[i + 2]]); + } + + let myShape = new CANNON.ConvexPolyhedron({ + vertices: points, + faces: myfaces, + }); + return myShape; +} diff --git a/activities/3DVolume.activity/js/createDeca2.js b/activities/3DVolume.activity/js/createDeca2.js new file mode 100644 index 000000000..22edf37a9 --- /dev/null +++ b/activities/3DVolume.activity/js/createDeca2.js @@ -0,0 +1,444 @@ +const sides = 10; +const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], +].flat(); + +for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); +} + +let myDecahedron; + +const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], +].flat(); + +function createDecahedron( + sharedColor, + ifNumbers, + ifTransparent, + xCoordinateShared, + zCoordinateShared, + ifImage, + sharedImageData, + yCoordinateShared, + quaternionShared, + sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat, + sharedAngVel1, + sharedAngVel2 +) { + let decahedron; + let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; + let tempTransparent = + ifTransparent == null ? ctx.toggleTransparent : ifTransparent; + let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; + let tempTextColor = + sharedTextColor != null ? sharedTextColor : ctx.textColor; + + const radius = 1.3; + const verticesGeo = [ + [0, 0, 1], + [0, 0, -1], + ].flat(); + + const sides = 10; + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); + } + + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ].flat(); + const decaGeometry2 = new THREE.PolyhedronGeometry( + verticesGeo, + facesGeo, + 1, + 0 + ); + let myVertices = []; + let myFaces = []; + + let vertStep = THREE.MathUtils.degToRad(36); + let vertices = [ + [0, 0, 1], + [Math.cos(vertStep * 0), Math.sin(vertStep * 0), 0.105], + [Math.cos(vertStep * 1), Math.sin(vertStep * 1), -0.105], + [Math.cos(vertStep * 2), Math.sin(vertStep * 2), 0.105], + ].map((p) => { + return new THREE.Vector3(...p); + }); + let h = vertices[0].distanceTo(vertices[2]); + let w = vertices[1].distanceTo(vertices[3]); + let u = (w / h) * 0.5; + let v01 = new THREE.Vector3().subVectors(vertices[1], vertices[0]); + let v02 = new THREE.Vector3().subVectors(vertices[2], vertices[0]); + let dot = v02.clone().normalize().dot(v01); + let v = 1 - dot / h; + + let gSide = new THREE.BufferGeometry() + .setFromPoints(vertices) + .rotateZ(-vertStep); + gSide.setIndex([0, 1, 2, 0, 2, 3]); + gSide.setAttribute( + "uv", + new THREE.Float32BufferAttribute( + [0.5, 1, 0.5 - u, v, 0.5, 0, 0.5 + u, v], + 2 + ) + ); + gSide.computeVertexNormals(); + gSide = gSide.toNonIndexed(); + + // all sides + let gs = []; + + for (let i = 0; i < 5; i++) { + let a = vertStep * 2 * i; + let g1 = gSide.clone().rotateZ(-a); + recomputeUVs(g1, i * 2 + 0); + let g2 = gSide + .clone() + .rotateX(Math.PI) + .rotateZ(vertStep + a); + recomputeUVs(g2, i * 2 + 1); + gs.push(g1, g2); + } + + let decaGeometry = BufferGeometryUtils.mergeBufferGeometries(gs); + let g = decaGeometry; + let positionAttribute = g.getAttribute("position"); + + for (let i = 0; i < positionAttribute.count; i++) { + let vertex = new THREE.Vector3().fromBufferAttribute( + positionAttribute, + i + ); + myVertices.push([vertex.x, vertex.y, vertex.z]); + } + + // Extract faces + for (let i = 0; i < myVertices.length; i += 3) { + myFaces.push([i, i + 1, i + 2]); + } + + if (tempShowNumbers) { + let m = new THREE.MeshLambertMaterial({ + map: getNumbers(tempFillColor, tempTextColor), + }); + + // let tileDimension = new THREE.Vector2(4, 4); + // let tileSize = 256; + // // let g = new THREE.OctahedronGeometry(1.6); + + // let c = document.createElement("canvas"); + // c.width = tileSize * tileDimension.x; + // c.height = tileSize * tileDimension.y; + // let ctx = c.getContext("2d"); + // ctx.fillStyle = tempFillColor; + // ctx.fillRect(0, 0, c.width, c.height); + + // let uvs = []; + + // let baseUVs = [ + // [0, 0], + // [Math.cos(vertStep * 0), Math.sin(vertStep * 0)], + // [Math.cos(vertStep * 1), Math.sin(vertStep * 1)], + // [Math.cos(vertStep * 2), Math.sin(vertStep * 2)], + // ].map((p) => { + // return new THREE.Vector2(...p); + // }); + + // for (let i = 0; i < 10; i++) { + // let u = i % tileDimension.x; + // let v = Math.floor(i / tileDimension.x); + // uvs.push( + // (baseUVs[0].x + u) / tileDimension.x, + // (baseUVs[0].y + v) / tileDimension.y, + // (baseUVs[1].x + u) / tileDimension.x, + // (baseUVs[1].y + v) / tileDimension.y, + // (baseUVs[2].x + u) / tileDimension.x, + // (baseUVs[2].y + v) / tileDimension.y, + // (baseUVs[3].x + u) / tileDimension.x, + // (baseUVs[3].y + v) / tileDimension.y + // ); + + // ctx.textAlign = "center"; + // ctx.textBaseline = "middle"; + // ctx.font = `bold 200px Arial`; + // ctx.fillStyle = tempTextColor; + // ctx.fillText( + // i + 1 + (i == 5 || i == 8 ? "" : ""), + // (u + 0.5) * tileSize, + // c.height - (v + 0.5) * tileSize + // ); + // } + // g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + + // let tex = new THREE.CanvasTexture(c); + // tex.colorSpace = THREE.SRGBColorSpace; + + // let m2 = new THREE.MeshPhongMaterial({ + // map: tex, + // }); + + decahedron = new THREE.Mesh(g, m); + decahedron.rotation.z = Math.PI / 2; + + // myDecahedron = decahedron; + // decahedron.rotateY(THREE.MathUtils.degToRad(20)); + // const position = g.attributes.position.array; + // const index = g.index.array; + // console.log(position); + // console.log(index); + } else if (tempTransparent) { + const decahedronTransaprentGeometry = decaGeometry2; + const wireframe = new THREE.WireframeGeometry( + decahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + decahedron = line; + } else if (false) { + const decaGeo = decaGeometry; + + const texture = new THREE.TextureLoader().load( + sharedImageData != null ? sharedImageData : imageData + ); + + // Create material using the texture + const material = new THREE.MeshPhongMaterial({ map: texture }); + + // Create cube mesh with the material + decahedron = new THREE.Mesh(decaGeo, material); + } else { + const decahedronGeometry = decaGeometry2; + + const decaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); + } + + // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true; + scene.add(decahedron); + + const t = (1 + Math.sqrt(5)) / 2; + const r = 1 / t; + const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) + + const verticesCannon = []; + for (let i = 0; i < verticesGeo.length; i += 3) { + verticesCannon.push( + new CANNON.Vec3( + verticesGeo[i] * scaleFactor, + verticesGeo[i + 1] * scaleFactor, + verticesGeo[i + 2] * scaleFactor + ) + ); + } + const facesCannon = []; + for (let i = 0; i < facesGeo.length; i += 3) { + facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); + } + + let cannonVertices = myVertices.map( + (v) => new CANNON.Vec3(v[0], v[1], v[2]) + ); + + // Create a ConvexPolyhedron shape from the scaled vertices and faces + const decahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesCannon, + faces: facesCannon, + }); + // let myShape = getPolyhedronShape(decahedron); + // console.log(myShape); + + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; + let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; + let y = yCoordinateShared == null ? 10 : yCoordinateShared; + + let decahedronBody = new CANNON.Body({ + mass: 2, // Set mass + shape: decahedronShape, + position: new CANNON.Vec3(x, y, z), + friction: -1, + restitution: 5, + }); + // decahedronBody = polyhedronShape + // if (tempShowNumbers) { + // decahedronBody.addEventListener("sleep", () => { + // sleepCounter++; + // getDecaScore(decahedron); + // }); + // } + world.addBody(decahedronBody); + + let angVel1 = 0.2 + // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = 0.2 + // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + + decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); + decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh + decahedron.quaternion.copy(decahedronBody.quaternion); + console.log(decahedronBody); + console.log(decahedronBody.rotation); + + if (quaternionShared != null && quaternionShared != undefined) { + decahedron.quaternion.copy(quaternionShared); + decahedronBody.quaternion.copy(quaternionShared); + } + + diceArray.push([ + decahedron, + decahedronBody, + "deca", + tempShowNumbers, + tempTransparent, + tempFillColor, + tempTextColor, + angVel1, + angVel2, + ]); +} + +function recomputeUVs(g, idx) { + let tiles = { + x: 4, + y: 4, + }; + let x = idx % tiles.x; + let y = Math.floor(idx / tiles.x); + + let uvs = g.attributes.uv; + for (let i = 0; i < uvs.count; i++) { + let u = (uvs.getX(i) + x) / tiles.x; + let v = (uvs.getY(i) + y) / tiles.y; + uvs.setXY(i, u, v); + } +} + +function getNumbers(tempFillColor, tempTextColor) { + let tileSize = 256; + let tiles = { + x: 4, + y: 4, + }; + + let c = document.createElement("canvas"); + let ctx = c.getContext("2d"); + c.width = tileSize * 4; + c.height = tileSize * 4; + let u = (val) => tileSize * 0.01 * val; + + ctx.fillStyle = tempFillColor; + ctx.fillRect(0, 0, c.width, c.height); + + ctx.font = `bold ${u(40)}px Arial`; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = tempTextColor; + + for (let i = 0; i < sides; i++) { + let y = Math.floor(i / tiles.x); + let x = i % tiles.x; + let text = i + 1; + + ctx.save(); + ctx.translate(x * tileSize, c.height - y * tileSize); + + ctx.fillText(text, u(50), -u(40)); + if (text == 6 || text == 9) { + ctx.fillText("_", u(50), -u(40)); + } + ctx.restore(); + } + + let tex = new THREE.CanvasTexture(c); + tex.colorSpace = "srgb"; + // tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); + tex.needsUpdate = true; + + return tex; +} +function getPolyhedronShape(mesh) { + let geometry = new THREE.BufferGeometry(); + geometry.setAttribute("position", mesh.geometry.getAttribute("position")); + + geometry = BufferGeometryUtils.mergeVertices(geometry); + + let position = geometry.attributes.position.array; + let index = geometry.getIndex(); + + const points = []; + for (let i = 0; i < position.length; i += 3) { + points.push( + new CANNON.Vec3(position[i], position[i + 1], position[i + 2]) + ); + } + const myfaces = []; + for (let i = 0; i < index.length; i += 3) { + myfaces.push([index[i], index[i + 1], index[i + 2]]); + } + + let myShape = new CANNON.ConvexPolyhedron({ + vertices: points, + faces: myfaces, + }); + return myShape; +} diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index 39ace9e81..5c845452d 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -75,7 +75,7 @@ define([], function () { intro: 'Use this button to shake the volumes on the board.', }, { - element: '.right-container', + element: '.arrow-container', title: 'Rotate the board', intro: 'Select the arrow pointing in the direction you wish the board to rotate.', }, From 344192a09259bc29519e3eb2d6cde9ec965c5d08 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 14 Jul 2024 16:31:48 +0530 Subject: [PATCH 21/60] changes --- activities/3DVolume.activity/css/activity.css | 17 +++- activities/3DVolume.activity/index.html | 15 ++-- activities/3DVolume.activity/js/activity.js | 82 ++++--------------- activities/3DVolume.activity/lib/orbit.js | 12 ++- 4 files changed, 50 insertions(+), 76 deletions(-) diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index 4957f1c8a..2c3a7bfed 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -569,4 +569,19 @@ body { -webkit-box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); box-shadow: 0 0 0 0rem rgba(158, 158, 158, .5); border: 0px; -} \ No newline at end of file +} + +@media (max-width: 1120px) { + #color-button-text, + #bg-button, + #zoom-button { + display: none; + } + + /* Adjust remaining buttons to fit within the toolbar */ + .toolbutton { + margin: 2px; + width: 40px; + height: 40px; + } +} diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 2ee043eac..a3b5fd395 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -31,8 +31,7 @@ - - + @@ -55,11 +54,6 @@ >
- - - + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 31470fc49..ed0d4f0dc 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -796,6 +796,10 @@ define([ document.querySelector("body").addEventListener("click", onAddClick); function onAddClick(event) { + if (window.isRotating) { + window.isRotating = false; + return; + } if (!removeVolume) { var rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; @@ -1107,18 +1111,21 @@ define([ var sensorButton = document.getElementById("sensor-button"); var sensorMode = false; var readyToWatch = false; - console.log(useragent.indexOf("android")); - // if (useragent.indexOf('android') != -1 || useragent.indexOf('iphone') != -1 || useragent.indexOf('ipad') != -1 || useragent.indexOf('ipod') != -1 || useragent.indexOf('mozilla/5.0 (mobile') != -1) { - // document.addEventListener('deviceready', function() { - // readyToWatch = true; - // }, false); - // sensorButton.disabled = false; - // } else { - // sensorButton.disabled = true; - // } + if (navigator.accelerometer != null) { + sensorMode = true; + sensorButton.classList.add("active"); + watchId = navigator.accelerometer.watchAcceleration( + accelerationChanged, + null, + { frequency: 500 } + ); + } sensorButton.addEventListener("click", function () { + if (navigator.accelerometer == null) { + return; + } sensorMode = !sensorMode; if (sensorMode) { sensorButton.classList.add("active"); @@ -1142,52 +1149,6 @@ define([ } }); - function setGravity(gravityDirection) { - let gravityX = 0; - let gravityY = 0; - let gravityZ = 0; - - switch (gravityDirection) { - case 0: - gravityY = 1; // Right - break; - case 1: - gravityX = 1; // Right-bottom (diagonal down-right) - gravityY = 1; - break; - case 2: - gravityY = 1; // Bottom (straight down) - break; - case 3: - gravityX = 1; // Left-bottom (diagonal down-left) - gravityY = -1; - break; - case 4: - gravityY = -1; // Left (straight left) - break; - case 5: - gravityX = -1; // Left-top (diagonal up-left) - gravityY = -1; - break; - case 6: - gravityX = -1; // Top (straight up) - break; - case 7: - gravityX = -1; // Right-top (diagonal up-right) - gravityY = 1; - break; - default: - break; - } - - // Assuming you have a Cannon.js world instance called 'world' - world.gravity.set( - gravityX * 9.82, - gravityY * 9.82, - gravityZ * 9.82 - ); // Scale gravity with 9.82 m/s² (approximate Earth gravity) - } - function accelerationChanged(acceleration) { if (!sensorMode) return; if (acceleration.y > 0) { @@ -1217,7 +1178,6 @@ define([ wakeAll(); } } - } function wakeAll() { @@ -1284,7 +1244,7 @@ define([ type: CANNON.Body.STATIC, material: groundPhysMat, }); - console.log(groundBody) + console.log(groundBody); groundBody.material.friction = 1; groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); world.addBody(groundBody); @@ -1296,8 +1256,7 @@ define([ }); topWall.quaternion.setFromEuler(-Math.PI / 2, 0, 0); topWall.position.set(0, 12, 0); - world.addBody(topWall) - + world.addBody(topWall); const leftWallBody = new CANNON.Body({ shape: new CANNON.Box(new CANNON.Vec3(15, 100, 0.1)), @@ -1964,11 +1923,6 @@ define([ } } } - let try_button = document.getElementById("try-button"); - try_button.addEventListener("click", ()=>{ - diceArray[0][0].rotateY(THREE.MathUtils.degToRad(20)); - }) - let awake = false; animate(); diff --git a/activities/3DVolume.activity/lib/orbit.js b/activities/3DVolume.activity/lib/orbit.js index 5aa559659..326fc8169 100644 --- a/activities/3DVolume.activity/lib/orbit.js +++ b/activities/3DVolume.activity/lib/orbit.js @@ -545,13 +545,14 @@ } function rotateUp( angle ) { - sphericalDelta.phi -= angle; + } const panLeft = function () { + const v = new THREE.Vector3(); return function panLeft( distance, objectMatrix ) { @@ -567,6 +568,7 @@ const panUp = function () { + const v = new THREE.Vector3(); return function panUp( distance, objectMatrix ) { @@ -712,8 +714,10 @@ panStart.set( event.clientX, event.clientY ); } + window.isRotating = false; function handleMouseMoveRotate( event ) { + window.isRotating = true; rotateEnd.set( event.clientX, event.clientY ); @@ -724,11 +728,15 @@ rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - event.stopPropagation(); + + rotateStart.copy( rotateEnd ); + scope.update(); + + } function handleMouseMoveDolly( event ) { From ad5bba32f04bd7ebc93582eb6e8b93a8fbdfdc9a Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 14 Jul 2024 23:23:15 +0530 Subject: [PATCH 22/60] changes --- activities/3DVolume.activity/css/activity.css | 12 + activities/3DVolume.activity/index.html | 4 +- activities/3DVolume.activity/js/activity.js | 467 ++++-------------- activities/3DVolume.activity/js/creatOcta.js | 10 +- activities/3DVolume.activity/js/createCube.js | 8 +- activities/3DVolume.activity/js/createDeca.js | 16 +- .../3DVolume.activity/js/createDodeca.js | 8 +- .../3DVolume.activity/js/createIcosa.js | 8 +- .../3DVolume.activity/js/createTetra.js | 12 +- activities/3DVolume.activity/lib/tutorial.js | 209 ++++---- 10 files changed, 265 insertions(+), 489 deletions(-) diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index 2c3a7bfed..297474471 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -585,3 +585,15 @@ body { height: 40px; } } + +@media (max-width: 800px) { + #volume-button, #color-button-fill, #remove-first { + display: none; + } +} + +@media (max-width: 690px) { + #sensor-button, #throw-button { + display: none; + } +} diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index a3b5fd395..7bb098c69 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -94,7 +94,7 @@ title="Clear Button" > -
+
-
+
+ + @@ -181,6 +187,7 @@ +

Time Step is 1/20

diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index d669dfc03..9687078af 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -209,7 +209,7 @@ define([ } } if (msg.action == "throw") { - throwDice(); + throwDice(msg.content[0], msg.content[1]); } if (msg.action == "changeBg") { changeBoardBackground(msg.content); @@ -514,6 +514,7 @@ define([ presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "throw", + content: [ctx.offset, ctx.rollingForce], }); } }); @@ -524,6 +525,9 @@ define([ .querySelector("#number-button") .addEventListener("click", () => { var numberButton = document.getElementById("number-button"); + if (ctx.showNumbers){ + return; + } numberButton.classList.toggle("active"); document.getElementById("volume-button").style.backgroundImage = "url(icons/number_volume.svg)"; @@ -547,13 +551,15 @@ define([ document .querySelector("#transparent-button") .addEventListener("click", () => { + if (ctx.toggleTransparent) { + return; + } var transparentButton = document.getElementById("transparent-button"); // Toggle the 'active' class on the clear button transparentButton.classList.toggle("active"); document.getElementById("volume-button").style.backgroundImage = "url(icons/transparent_volume.svg)"; - console.log(defaultVolume); if (defaultVolume) { var defaultButton = @@ -574,6 +580,9 @@ define([ document .querySelector("#default-button") .addEventListener("click", (event) => { + if (defaultVolume) { + return; + } var defaultButton = document.getElementById("default-button"); // Toggle the 'active' class on the clear button defaultButton.classList.toggle("active"); @@ -1239,8 +1248,7 @@ define([ // This function handles the tossing of the volumes. - function throwDice() { - // Remove all the volumes from the board. + function throwDice(sharedOffset, sharedRolling) { for (let i = 0; i < diceArray.length; i++) { scene.remove(diceArray[i][0]); world.removeBody(diceArray[i][1]); @@ -1248,11 +1256,51 @@ define([ if (diceArray.length > 0) { lastRoll = ""; presentScore = 0; + for (let i = 0; i < diceArray.length; i++) { + let rollingForce; + if (sharedRolling != null) { + rollingForce = new THREE.Vector3( + sharedRolling.x, + sharedRolling.y, + sharedRolling.z + ); + } else { + rollingForce = ctx.rollingForce; + } + diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); + diceArray[i][1].applyImpulse( + sharedOffset != null ? sharedOffset : ctx.offset, + rollingForce + ); + diceArray[i][1].position.set(0, 10, 0); + } for (let i = 0; i < diceArray.length; i++) { scene.add(diceArray[i][0]); world.addBody(diceArray[i][1]); - diceArray[i][1].position.set(0, 10, 0); } + } else { + for (let i = 0; i < dices.cube; i++) { + createCube(); + } + for (let i = 0; i < dices.tetra; i++) { + createTetrahedron(); + } + for (let i = 0; i < dices.octa; i++) { + createOctahedron(); + } + for (let i = 0; i < dices.dodeca; i++) { + createDodecahedron(); + } + for (let i = 0; i < dices.deca; i++) { + createDecahedron(); + } + for (let i = 0; i < dices.icosa; i++) { + createIcosahedron(); + } + lastRoll = ""; + // if (ctx.showNumbers) { + // getScore(); + // } } } @@ -1442,104 +1490,134 @@ define([ } function getIcosaScore(body, ifRemove) { - // Define the golden ratio - const phi = (1 + Math.sqrt(5)) / 2; - - // Icosahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, - { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, - { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, - { - vector: new THREE.Vector3(0, -1, -phi).normalize(), - face: 20, - }, - { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, - { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, - { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, - { - vector: new THREE.Vector3(-1, -phi, 0).normalize(), - face: 17, - }, - { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, - { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, - { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, - { - vector: new THREE.Vector3(-phi, 0, -1).normalize(), - face: 13, - }, - { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, - { - vector: new THREE.Vector3(-1, phi, phi).normalize(), - face: 1, - }, - { - vector: new THREE.Vector3(1, -phi, phi).normalize(), - face: 11, - }, - { - vector: new THREE.Vector3(-1, -phi, phi).normalize(), - face: 12, - }, - { - vector: new THREE.Vector3(1, phi, -phi).normalize(), - face: 10, - }, - { - vector: new THREE.Vector3(-1, phi, -phi).normalize(), - face: 3, - }, - { - vector: new THREE.Vector3(1, -phi, -phi).normalize(), - face: 14, - }, - { - vector: new THREE.Vector3(-1, -phi, -phi).normalize(), - face: 18, - }, - ]; - - let closestFace = null; - let closestDot = -1; // Initialize with the smallest possible dot product - - // Reference vector pointing up - let upVector = new THREE.Vector3(0, 1, 0); - - for (const faceVector of faceVectors) { - // Apply the body's quaternion to the face vector - let worldVector = faceVector.vector + // // Define the golden ratio + // const phi = (1 + Math.sqrt(5)) / 2; + + // // Icosahedron face vectors + // const faceVectors = [ + // { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, + // { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, + // { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, + // { + // vector: new THREE.Vector3(0, -1, -phi).normalize(), + // face: 20, + // }, + // { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, + // { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, + // { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, + // { + // vector: new THREE.Vector3(-1, -phi, 0).normalize(), + // face: 17, + // }, + // { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, + // { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, + // { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, + // { + // vector: new THREE.Vector3(-phi, 0, -1).normalize(), + // face: 13, + // }, + // { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, + // { + // vector: new THREE.Vector3(-1, phi, phi).normalize(), + // face: 1, + // }, + // { + // vector: new THREE.Vector3(1, -phi, phi).normalize(), + // face: 11, + // }, + // { + // vector: new THREE.Vector3(-1, -phi, phi).normalize(), + // face: 12, + // }, + // { + // vector: new THREE.Vector3(1, phi, -phi).normalize(), + // face: 10, + // }, + // { + // vector: new THREE.Vector3(-1, phi, -phi).normalize(), + // face: 3, + // }, + // { + // vector: new THREE.Vector3(1, -phi, -phi).normalize(), + // face: 14, + // }, + // { + // vector: new THREE.Vector3(-1, -phi, -phi).normalize(), + // face: 18, + // }, + // ]; + + // let closestFace = null; + // let closestDot = -1; // Initialize with the smallest possible dot product + + // // Reference vector pointing up + // let upVector = new THREE.Vector3(0, 1, 0); + + // for (const faceVector of faceVectors) { + // // Apply the body's quaternion to the face vector + // let worldVector = faceVector.vector + // .clone() + // .applyQuaternion(body.quaternion); + + // // Calculate the dot product with the up vector + // let dot = worldVector.dot(upVector); + + // // Check if this is the closest to pointing up + // if (dot > closestDot) { + // closestDot = dot; + // closestFace = faceVector; + // } + // } + + // if (closestFace) { + // let faceNumber = closestFace.face; + // if (!ifRemove) { + // lastRoll += faceNumber + " + "; + // presentScore += faceNumber; + // updateElements(); + // } + // for (let i = 0; i < diceArray.length; i++) { + // if (body == diceArray[i][0]) { + // diceArray[i][7] = faceNumber; + // } + // } + // for (let i = 0; i < diceArray.length; i++) { + // if (body == diceArray[i][0]) { + // diceArray[i][7] = faceNumber; + // } + // } + // return faceNumber; + // } + + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < 20; ++i) { + let face = i + 1; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal .clone() - .applyQuaternion(body.quaternion); - - // Calculate the dot product with the up vector - let dot = worldVector.dot(upVector); - - // Check if this is the closest to pointing up - if (dot > closestDot) { - closestDot = dot; - closestFace = faceVector; + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; } } + lastRoll += closest_face + " + "; + presentScore += closest_face; + updateElements(); - if (closestFace) { - let faceNumber = closestFace.face; - if (!ifRemove) { - lastRoll += faceNumber + " + "; - presentScore += faceNumber; - updateElements(); - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceNumber; - } - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceNumber; - } - } - return faceNumber; - } + return closest_face; } function getDodecaScore(body, ifRemove) { @@ -1580,6 +1658,39 @@ define([ return faceVector.face; } } + + // let vector = new THREE.Vector3(0, -1); + // let closest_face; + // let closest_angle = Math.PI * 2; + + // let normals = body.geometry.getAttribute("normal").array; + // console.log(normals) + // for (let i = 0; i < 12; ++i) { + // let face = i + 1; + + // //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + // let startVertex = i * 9; + // let normal = new THREE.Vector3( + // normals[startVertex], + // normals[startVertex + 1], + // normals[startVertex + 2] + // ); + // let angle = normal + // .clone() + // .applyQuaternion(body.quaternion) + // .angleTo(vector); + // if (angle < closest_angle) { + // closest_angle = angle; + // closest_face = face; + // } + // } + + // // switch(closest_face) { + // // } + // lastRoll += closest_face + " + "; + // presentScore += closest_face; + // updateElements(); + // return closest_face; } function getDecaScore(body, ifRemove) { @@ -1698,7 +1809,18 @@ define([ }); let awake = false; - const timeStep = 1 / 20; + let time = 20; + let timeStep = 1 / time; + + document.querySelector("#increase-button").addEventListener('click', () => { + if (time == 5) { + alert("cant go lower") + return; + } + time -= 5; + timeStep = 1 / time; + document.getElementById("time").innerHTML = time; + }) animate(); diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/createDodeca.js index 10cacfddc..6cf5caa00 100644 --- a/activities/3DVolume.activity/js/createDodeca.js +++ b/activities/3DVolume.activity/js/createDodeca.js @@ -97,7 +97,7 @@ function createDodecahedron( ctx.font = `bold ${tileSize / 3}px Arial`; ctx.fillStyle = tempTextColor; ctx.fillText( - i + 1, + i + 1 + (i == 5 ? "." : ""), (u + 0.5) * tileSize, c.height - (v + 0.5) * tileSize ); From 91dc1ed7755b46cbc692fbeae8e19021b6c7aebc Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 19 Jul 2024 00:11:49 +0530 Subject: [PATCH 27/60] changes --- activities/3DVolume.activity/js/activity.js | 271 +++++++++++--------- 1 file changed, 145 insertions(+), 126 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 9687078af..34a2fad1f 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -736,9 +736,9 @@ define([ } if (createFunction) { - let angVel1 = Math.random() * (2 - 0.1) + 0.1; - let angVel2 = Math.random() * (2 - 0.1) + 0.1; - let angVel3 = Math.random() * (2 - 0.1) + 0.1; + let angVel1 = Math.random() * (4 - 0.1) + 0.1; + let angVel2 = Math.random() * (4 - 0.1) + 0.1; + let angVel3 = Math.random() * (4 - 0.1) + 0.1; createFunction( null, null, @@ -1307,64 +1307,95 @@ define([ // Functions to get the scores of the dice. function getOctaScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis - face: 4, - }, - { - vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis - face: 6, - }, - { - vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis - face: 5, - }, - { - vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis - face: 3, - }, - { - vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) - face: 1, - }, - { - vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) - face: 8, - }, - { - vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) - face: 2, - }, - { - vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) - face: 7, - }, - ]; + // const faceVectors = [ + // { + // vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis + // face: 4, + // }, + // { + // vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis + // face: 6, + // }, + // { + // vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis + // face: 5, + // }, + // { + // vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis + // face: 3, + // }, + // { + // vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + // face: 1, + // }, + // { + // vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) + // face: 8, + // }, + // { + // vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) + // face: 2, + // }, + // { + // vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + // face: 7, + // }, + // ]; - let minValue = 1000000; - let minInd; - for (let i = 0; i < faceVectors.length; i++) { - let faceVector = faceVectors[i]; - faceVector.vector.applyEuler(body.rotation); - if (minValue > Math.abs(1 - faceVector.vector.y)) { - minValue = Math.abs(1 - faceVector.vector.y); - minInd = i; - } - } - if (!ifRemove) { - lastRoll += faceVectors[minInd].face + " + "; - presentScore += faceVectors[minInd].face; - updateElements(); - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVectors[minInd].face; + // let minValue = 1000000; + // let minInd; + // for (let i = 0; i < faceVectors.length; i++) { + // let faceVector = faceVectors[i]; + // faceVector.vector.applyEuler(body.rotation); + // if (minValue > Math.abs(1 - faceVector.vector.y)) { + // minValue = Math.abs(1 - faceVector.vector.y); + // minInd = i; + // } + // } + // if (!ifRemove) { + // lastRoll += faceVectors[minInd].face + " + "; + // presentScore += faceVectors[minInd].face; + // updateElements(); + // } + // for (let i = 0; i < diceArray.length; i++) { + // if (body == diceArray[i][0]) { + // diceArray[i][7] = faceVectors[minInd].face; + // } + // } + // return faceVectors[minInd].face; + + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < 8; ++i) { + let face = i + 1; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal + .clone() + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; } } - return faceVectors[minInd].face; + lastRoll += closest_face + " + "; + presentScore += closest_face; + updateElements(); + + return closest_face; } + function getCubeScore(body, ifRemove) { const faceVectors = [ { @@ -1394,7 +1425,6 @@ define([ ]; for (const faceVector of faceVectors) { faceVector.vector.applyEuler(body.rotation); - if (Math.round(faceVector.vector.y) == 1) { if (!ifRemove) { lastRoll += faceVector.face + " + "; @@ -1448,47 +1478,6 @@ define([ } } } - - function getDecaScore(body, ifRemove) { - // Define face vectors based on vertices - const faceVectors = [ - { vector: new THREE.Vector3(0, 0, 1), face: 1 }, - { vector: new THREE.Vector3(0, 0, -1), face: 2 }, - ]; - - const sides = 10; - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - faceVectors.push({ - vector: new THREE.Vector3( - -Math.cos(b), - -Math.sin(b), - 0.105 * (i % 2 ? 1 : -1) - ), - face: i + 3, - }); - } - - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); - - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; - } - } - } - function getIcosaScore(body, ifRemove) { // // Define the golden ratio // const phi = (1 + Math.sqrt(5)) / 2; @@ -1695,37 +1684,67 @@ define([ function getDecaScore(body, ifRemove) { // Decahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(0, 1, 0.5), face: 7 }, - { vector: new THREE.Vector3(0, 1, -0.5), face: 2 }, - { vector: new THREE.Vector3(0, -1, 0.5), face: 3 }, - { vector: new THREE.Vector3(0, -1, -0.5), face: 10 }, - { vector: new THREE.Vector3(0.5, 0, 1), face: 5 }, - { vector: new THREE.Vector3(-0.5, 0, 1), face: 8 }, - { vector: new THREE.Vector3(0.5, 0, -1), face: 9 }, - { vector: new THREE.Vector3(-0.5, 0, -1), face: 6 }, - { vector: new THREE.Vector3(1, 0.5, 0), face: 1 }, - { vector: new THREE.Vector3(-1, 0.5, 0), face: 4 }, - ]; + // const faceVectors = [ + // { vector: new THREE.Vector3(0, 1, 0.5), face: 7 }, + // { vector: new THREE.Vector3(0, 1, -0.5), face: 2 }, + // { vector: new THREE.Vector3(0, -1, 0.5), face: 3 }, + // { vector: new THREE.Vector3(0, -1, -0.5), face: 10 }, + // { vector: new THREE.Vector3(0.5, 0, 1), face: 5 }, + // { vector: new THREE.Vector3(-0.5, 0, 1), face: 8 }, + // { vector: new THREE.Vector3(0.5, 0, -1), face: 9 }, + // { vector: new THREE.Vector3(-0.5, 0, -1), face: 6 }, + // { vector: new THREE.Vector3(1, 0.5, 0), face: 1 }, + // { vector: new THREE.Vector3(-1, 0.5, 0), face: 4 }, + // ]; - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); + // for (const faceVector of faceVectors) { + // faceVector.vector.normalize().applyEuler(body.rotation); + + // if (Math.round(faceVector.vector.y) === 1) { + // if (!ifRemove) { + // lastRoll += faceVector.face + " + "; + // presentScore += faceVector.face; + // updateElements(); + // break; + // } + // for (let i = 0; i < diceArray.length; i++) { + // if (body == diceArray[i][0]) { + // diceArray[i][7] = faceVector.face; + // } + // } + // return faceVector.face; + // } + // } - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < 10; ++i) { + let face = i + 1; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal + .clone() + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; } } + lastRoll += closest_face + " + "; + presentScore += closest_face; + updateElements(); + + return closest_face; } function changeBoardBackground(selectedBoard) { @@ -1817,7 +1836,7 @@ define([ alert("cant go lower") return; } - time -= 5; + time -= 1; timeStep = 1 / time; document.getElementById("time").innerHTML = time; }) From 6be7575e4ad7c75af3a9a7f74591c227f38ae10c Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 19 Jul 2024 18:10:15 +0530 Subject: [PATCH 28/60] changes --- activities/3DVolume.activity/index.html | 2 +- activities/3DVolume.activity/js/activity.js | 43 +- .../3DVolume.activity/js/createDeca2.js | 671 +++++++++--------- 3 files changed, 348 insertions(+), 368 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 2b19fe94b..efae98d83 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -31,7 +31,7 @@ - + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 34a2fad1f..5743dd5e4 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -525,7 +525,7 @@ define([ .querySelector("#number-button") .addEventListener("click", () => { var numberButton = document.getElementById("number-button"); - if (ctx.showNumbers){ + if (ctx.showNumbers) { return; } numberButton.classList.toggle("active"); @@ -1395,7 +1395,6 @@ define([ return closest_face; } - function getCubeScore(body, ifRemove) { const faceVectors = [ { @@ -1831,20 +1830,22 @@ define([ let time = 20; let timeStep = 1 / time; - document.querySelector("#increase-button").addEventListener('click', () => { - if (time == 5) { - alert("cant go lower") - return; - } - time -= 1; - timeStep = 1 / time; - document.getElementById("time").innerHTML = time; - }) + document + .querySelector("#increase-button") + .addEventListener("click", () => { + if (time == 5) { + alert("cant go lower"); + return; + } + time -= 1; + timeStep = 1 / time; + document.getElementById("time").innerHTML = time; + }); animate(); function animate(time) { - world.step(timeStep); + // world.step(timeStep); // Uncomment the next line to view how the physical world actually looks like. // cannonDebugger.update(); @@ -1859,14 +1860,7 @@ define([ if (world.hasActiveBodies == false && awake == true) { awake = false; console.log("the world is going to sleep now bye bye"); - for (let i = 0; i < diceArray.length; i++) { - console.log(diceArray[i][1].interpolatedQuaternion.x); - console.log(diceArray[i][1].interpolatedQuaternion.y); - console.log(diceArray[i][1].interpolatedQuaternion.z); - console.log(diceArray[i][1].initQuaternion.x); - console.log(diceArray[i][1].initQuaternion.y); - console.log(diceArray[i][1].initQuaternion.z); - } + for (let i = 0; i < diceArray.length; i++) {} getScores(); } if (world.hasActiveBodies == true) { @@ -1878,6 +1872,15 @@ define([ renderer.setAnimationLoop(animate); + const fixedTimeStep = 1 / 40; + const maxSubSteps = 3; + + function updatePhysics() { + world.step(fixedTimeStep, fixedTimeStep * maxSubSteps); + } + + setInterval(updatePhysics, 1000 * fixedTimeStep); + window.addEventListener("resize", function () { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); diff --git a/activities/3DVolume.activity/js/createDeca2.js b/activities/3DVolume.activity/js/createDeca2.js index 22edf37a9..d99666faf 100644 --- a/activities/3DVolume.activity/js/createDeca2.js +++ b/activities/3DVolume.activity/js/createDeca2.js @@ -1,39 +1,64 @@ -const sides = 10; -const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], -].flat(); - -for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); +let vertices = []; +let faces = [ + [5, 7, 11, 0], + [4, 2, 10, 1], + [1, 3, 11, 2], + [0, 8, 10, 3], + [7, 9, 11, 4], + [8, 6, 10, 5], + [9, 1, 11, 6], + [2, 0, 10, 7], + [3, 5, 11, 8], + [6, 4, 10, 9], + [1, 0, 2, -1], + [1, 2, 3, -1], + [3, 2, 4, -1], + [3, 4, 5, -1], + [5, 4, 6, -1], + [5, 6, 7, -1], + [7, 6, 8, -1], + [7, 8, 9, -1], + [9, 8, 0, -1], + [9, 0, 1, -1], +]; + +for (let i = 0, b = 0; i < 10; ++i, b += (Math.PI * 2) / 10) { + vertices.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]); } - -let myDecahedron; - -const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], -].flat(); - +vertices.push([0, 0, -1]); +vertices.push([0, 0, 1]); +let scaleFactor = 0.9; +value = 10; +let faceTexts = [ + " ", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", +]; +let textMargin = 1.0; +let chamfer = 0.945; +let af = (Math.PI * 6) / 5; +let tab = 0; +let backColor; +let color; function createDecahedron( sharedColor, ifNumbers, @@ -60,252 +85,67 @@ function createDecahedron( let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; let tempTextColor = sharedTextColor != null ? sharedTextColor : ctx.textColor; + backColor = tempFillColor; + color = tempTextColor; - const radius = 1.3; - const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], - ].flat(); - - const sides = 10; - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); - } - - const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], - ].flat(); - const decaGeometry2 = new THREE.PolyhedronGeometry( - verticesGeo, - facesGeo, - 1, - 0 - ); - let myVertices = []; - let myFaces = []; - - let vertStep = THREE.MathUtils.degToRad(36); - let vertices = [ - [0, 0, 1], - [Math.cos(vertStep * 0), Math.sin(vertStep * 0), 0.105], - [Math.cos(vertStep * 1), Math.sin(vertStep * 1), -0.105], - [Math.cos(vertStep * 2), Math.sin(vertStep * 2), 0.105], - ].map((p) => { - return new THREE.Vector3(...p); - }); - let h = vertices[0].distanceTo(vertices[2]); - let w = vertices[1].distanceTo(vertices[3]); - let u = (w / h) * 0.5; - let v01 = new THREE.Vector3().subVectors(vertices[1], vertices[0]); - let v02 = new THREE.Vector3().subVectors(vertices[2], vertices[0]); - let dot = v02.clone().normalize().dot(v01); - let v = 1 - dot / h; - - let gSide = new THREE.BufferGeometry() - .setFromPoints(vertices) - .rotateZ(-vertStep); - gSide.setIndex([0, 1, 2, 0, 2, 3]); - gSide.setAttribute( - "uv", - new THREE.Float32BufferAttribute( - [0.5, 1, 0.5 - u, v, 0.5, 0, 0.5 + u, v], - 2 - ) - ); - gSide.computeVertexNormals(); - gSide = gSide.toNonIndexed(); - - // all sides - let gs = []; - - for (let i = 0; i < 5; i++) { - let a = vertStep * 2 * i; - let g1 = gSide.clone().rotateZ(-a); - recomputeUVs(g1, i * 2 + 0); - let g2 = gSide - .clone() - .rotateX(Math.PI) - .rotateZ(vertStep + a); - recomputeUVs(g2, i * 2 + 1); - gs.push(g1, g2); - } - - let decaGeometry = BufferGeometryUtils.mergeBufferGeometries(gs); - let g = decaGeometry; - let positionAttribute = g.getAttribute("position"); - - for (let i = 0; i < positionAttribute.count; i++) { - let vertex = new THREE.Vector3().fromBufferAttribute( - positionAttribute, - i - ); - myVertices.push([vertex.x, vertex.y, vertex.z]); - } - - // Extract faces - for (let i = 0; i < myVertices.length; i += 3) { - myFaces.push([i, i + 1, i + 2]); - } - - if (tempShowNumbers) { - let m = new THREE.MeshLambertMaterial({ - map: getNumbers(tempFillColor, tempTextColor), - }); - - // let tileDimension = new THREE.Vector2(4, 4); - // let tileSize = 256; - // // let g = new THREE.OctahedronGeometry(1.6); - - // let c = document.createElement("canvas"); - // c.width = tileSize * tileDimension.x; - // c.height = tileSize * tileDimension.y; - // let ctx = c.getContext("2d"); - // ctx.fillStyle = tempFillColor; - // ctx.fillRect(0, 0, c.width, c.height); - - // let uvs = []; - - // let baseUVs = [ - // [0, 0], - // [Math.cos(vertStep * 0), Math.sin(vertStep * 0)], - // [Math.cos(vertStep * 1), Math.sin(vertStep * 1)], - // [Math.cos(vertStep * 2), Math.sin(vertStep * 2)], - // ].map((p) => { - // return new THREE.Vector2(...p); - // }); - - // for (let i = 0; i < 10; i++) { - // let u = i % tileDimension.x; - // let v = Math.floor(i / tileDimension.x); - // uvs.push( - // (baseUVs[0].x + u) / tileDimension.x, - // (baseUVs[0].y + v) / tileDimension.y, - // (baseUVs[1].x + u) / tileDimension.x, - // (baseUVs[1].y + v) / tileDimension.y, - // (baseUVs[2].x + u) / tileDimension.x, - // (baseUVs[2].y + v) / tileDimension.y, - // (baseUVs[3].x + u) / tileDimension.x, - // (baseUVs[3].y + v) / tileDimension.y - // ); + let diceMesh = new THREE.Mesh(getGeometry(), getMaterials()); - // ctx.textAlign = "center"; - // ctx.textBaseline = "middle"; - // ctx.font = `bold 200px Arial`; - // ctx.fillStyle = tempTextColor; - // ctx.fillText( - // i + 1 + (i == 5 || i == 8 ? "" : ""), - // (u + 0.5) * tileSize, - // c.height - (v + 0.5) * tileSize - // ); - // } - // g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - // let tex = new THREE.CanvasTexture(c); - // tex.colorSpace = THREE.SRGBColorSpace; - - // let m2 = new THREE.MeshPhongMaterial({ - // map: tex, - // }); - - decahedron = new THREE.Mesh(g, m); - decahedron.rotation.z = Math.PI / 2; - - // myDecahedron = decahedron; - // decahedron.rotateY(THREE.MathUtils.degToRad(20)); - // const position = g.attributes.position.array; - // const index = g.index.array; - // console.log(position); - // console.log(index); - } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry2; - const wireframe = new THREE.WireframeGeometry( - decahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - decahedron = line; - } else if (false) { - const decaGeo = decaGeometry; - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - decahedron = new THREE.Mesh(decaGeo, material); - } else { - const decahedronGeometry = decaGeometry2; + // if (tempShowNumbers) { + // let m = new THREE.MeshLambertMaterial({ + // map: getNumbers(tempFillColor, tempTextColor), + // }); - const decaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - wireframe: false, - }); + // decahedron = new THREE.Mesh(g, m); + // decahedron.rotation.z = Math.PI / 2; + // } else if (tempTransparent) { + // const decahedronTransaprentGeometry = decaGeometry2; + // const wireframe = new THREE.WireframeGeometry( + // decahedronTransaprentGeometry + // ); + // const lineMaterial = new THREE.LineBasicMaterial({ + // color: sharedColor != null ? sharedColor : ctx.presentColor, + // depthTest: true, + // opacity: 1, + // transparent: false, + // }); + // const line = new THREE.LineSegments(wireframe, lineMaterial); + // decahedron = line; + // } else if (false) { + // const decaGeo = decaGeometry; + + // const texture = new THREE.TextureLoader().load( + // sharedImageData != null ? sharedImageData : imageData + // ); + + // // Create material using the texture + // const material = new THREE.MeshPhongMaterial({ map: texture }); + + // // Create cube mesh with the material + // decahedron = new THREE.Mesh(decaGeo, material); + // } else { + // const decahedronGeometry = decaGeometry2; + + // const decaMaterial = new THREE.MeshStandardMaterial({ + // color: sharedColor != null ? sharedColor : ctx.presentColor, + // wireframe: false, + // }); - decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); - } + // decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); + // } // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - decahedron.castShadow = true; + // decahedron.castShadow = true; + decahedron = diceMesh scene.add(decahedron); - const t = (1 + Math.sqrt(5)) / 2; - const r = 1 / t; - const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) - - const verticesCannon = []; - for (let i = 0; i < verticesGeo.length; i += 3) { - verticesCannon.push( - new CANNON.Vec3( - verticesGeo[i] * scaleFactor, - verticesGeo[i + 1] * scaleFactor, - verticesGeo[i + 2] * scaleFactor - ) - ); - } - const facesCannon = []; - for (let i = 0; i < facesGeo.length; i += 3) { - facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); - } - - let cannonVertices = myVertices.map( - (v) => new CANNON.Vec3(v[0], v[1], v[2]) - ); - // Create a ConvexPolyhedron shape from the scaled vertices and faces - const decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesCannon, - faces: facesCannon, - }); + // const decahedronShape = new CANNON.ConvexPolyhedron({ + // vertices: verticesCannon, + // faces: facesCannon, + // }); // let myShape = getPolyhedronShape(decahedron); // console.log(myShape); + console.log(diceMesh.geometry.cannon_shape) let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; @@ -313,7 +153,7 @@ function createDecahedron( let decahedronBody = new CANNON.Body({ mass: 2, // Set mass - shape: decahedronShape, + shape: diceMesh.geometry.cannon_shape, position: new CANNON.Vec3(x, y, z), friction: -1, restitution: 5, @@ -327,17 +167,17 @@ function createDecahedron( // } world.addBody(decahedronBody); - let angVel1 = 0.2 - // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = 0.2 - // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel1 = 0.2; + // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = 0.2; + // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); - console.log(decahedronBody); - console.log(decahedronBody.rotation); + // console.log(decahedronBody); + // console.log(decahedronBody.rotation); if (quaternionShared != null && quaternionShared != undefined) { decahedron.quaternion.copy(quaternionShared); @@ -357,88 +197,225 @@ function createDecahedron( ]); } -function recomputeUVs(g, idx) { - let tiles = { - x: 4, - y: 4, - }; - let x = idx % tiles.x; - let y = Math.floor(idx / tiles.x); - - let uvs = g.attributes.uv; - for (let i = 0; i < uvs.count; i++) { - let u = (uvs.getX(i) + x) / tiles.x; - let v = (uvs.getY(i) + y) / tiles.y; - uvs.setXY(i, u, v); +function getGeometry() { + let radius = 1 * scaleFactor; + + let vectors = new Array(vertices.length); + for (let i = 0; i < vertices.length; ++i) { + vectors[i] = new THREE.Vector3().fromArray(vertices[i]).normalize(); } + + let chamferGeometry = getChamferGeometry(vectors); + let geometry = makeGeometry( + chamferGeometry.vectors, + chamferGeometry.faces, + radius, + tab, + af + ); + geometry.cannon_shape = createShape(vectors, faces, radius); + + return geometry; } -function getNumbers(tempFillColor, tempTextColor) { - let tileSize = 256; - let tiles = { - x: 4, - y: 4, - }; - - let c = document.createElement("canvas"); - let ctx = c.getContext("2d"); - c.width = tileSize * 4; - c.height = tileSize * 4; - let u = (val) => tileSize * 0.01 * val; - - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - ctx.font = `bold ${u(40)}px Arial`; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillStyle = tempTextColor; - - for (let i = 0; i < sides; i++) { - let y = Math.floor(i / tiles.x); - let x = i % tiles.x; - let text = i + 1; - - ctx.save(); - ctx.translate(x * tileSize, c.height - y * tileSize); - - ctx.fillText(text, u(50), -u(40)); - if (text == 6 || text == 9) { - ctx.fillText("_", u(50), -u(40)); +function getChamferGeometry(vectors) { + let chamfer_vectors = [], + chamfer_faces = [], + corner_faces = new Array(vectors.length); + for (let i = 0; i < vectors.length; ++i) corner_faces[i] = []; + for (let i = 0; i < faces.length; ++i) { + let ii = faces[i], + fl = ii.length - 1; + let center_point = new THREE.Vector3(); + let face = new Array(fl); + for (let j = 0; j < fl; ++j) { + let vv = vectors[ii[j]].clone(); + center_point.add(vv); + corner_faces[ii[j]].push((face[j] = chamfer_vectors.push(vv) - 1)); + } + center_point.divideScalar(fl); + for (let j = 0; j < fl; ++j) { + let vv = chamfer_vectors[face[j]]; + vv.subVectors(vv, center_point) + .multiplyScalar(chamfer) + .addVectors(vv, center_point); } - ctx.restore(); + face.push(ii[fl]); + chamfer_faces.push(face); + } + for (let i = 0; i < faces.length - 1; ++i) { + for (let j = i + 1; j < faces.length; ++j) { + let pairs = [], + lastm = -1; + for (let m = 0; m < faces[i].length - 1; ++m) { + let n = faces[j].indexOf(faces[i][m]); + if (n >= 0 && n < faces[j].length - 1) { + if (lastm >= 0 && m !== lastm + 1) + pairs.unshift([i, m], [j, n]); + else pairs.push([i, m], [j, n]); + lastm = m; + } + } + if (pairs.length !== 4) continue; + chamfer_faces.push([ + chamfer_faces[pairs[0][0]][pairs[0][1]], + chamfer_faces[pairs[1][0]][pairs[1][1]], + chamfer_faces[pairs[3][0]][pairs[3][1]], + chamfer_faces[pairs[2][0]][pairs[2][1]], + -1, + ]); + } + } + for (let i = 0; i < corner_faces.length; ++i) { + let cf = corner_faces[i], + face = [cf[0]], + count = cf.length - 1; + while (count) { + for (let m = faces.length; m < chamfer_faces.length; ++m) { + let index = chamfer_faces[m].indexOf(face[face.length - 1]); + if (index >= 0 && index < 4) { + if (--index === -1) index = 3; + let next_vertex = chamfer_faces[m][index]; + if (cf.indexOf(next_vertex) >= 0) { + face.push(next_vertex); + break; + } + } + } + --count; + } + face.push(-1); + chamfer_faces.push(face); + } + return { vectors: chamfer_vectors, faces: chamfer_faces }; +} + +function makeGeometry(vertices, faces, radius) { + let geom = new THREE.BufferGeometry(); + + for (let i = 0; i < vertices.length; ++i) { + vertices[i] = vertices[i].multiplyScalar(radius); } - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = "srgb"; - // tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); - tex.needsUpdate = true; + let positions = []; + const normals = []; + const uvs = []; + + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + let materialIndex; + let faceFirstVertexIndex = 0; + + for (let i = 0; i < faces.length; ++i) { + let ii = faces[i], + fl = ii.length - 1; + let aa = (Math.PI * 2) / fl; + materialIndex = ii[fl] + 1; + for (let j = 0; j < fl - 2; ++j) { + //Vertices + positions.push(...vertices[ii[0]].toArray()); + positions.push(...vertices[ii[j + 1]].toArray()); + positions.push(...vertices[ii[j + 2]].toArray()); + + // Flat face normals + cb.subVectors(vertices[ii[j + 2]], vertices[ii[j + 1]]); + ab.subVectors(vertices[ii[0]], vertices[ii[j + 1]]); + cb.cross(ab); + cb.normalize(); + + // Vertex Normals + normals.push(...cb.toArray()); + normals.push(...cb.toArray()); + normals.push(...cb.toArray()); + + //UVs + uvs.push( + (Math.cos(af) + 1 + tab) / 2 / (1 + tab), + (Math.sin(af) + 1 + tab) / 2 / (1 + tab) + ); + uvs.push( + (Math.cos(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab), + (Math.sin(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab) + ); + uvs.push( + (Math.cos(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab), + (Math.sin(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab) + ); + } + + //Set Group for face materials. + let numOfVertices = (fl - 2) * 3; + for (let i = 0; i < numOfVertices / 3; i++) { + geom.addGroup(faceFirstVertexIndex, 3, materialIndex); + faceFirstVertexIndex += 3; + } + } - return tex; + geom.setAttribute( + "position", + new THREE.Float32BufferAttribute(positions, 3) + ); + geom.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3)); + geom.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius); + return geom; } -function getPolyhedronShape(mesh) { - let geometry = new THREE.BufferGeometry(); - geometry.setAttribute("position", mesh.geometry.getAttribute("position")); - geometry = BufferGeometryUtils.mergeVertices(geometry); +function createShape(vertices, faces, radius) { + let cv = new Array(vertices.length), + cf = new Array(faces.length); + for (let i = 0; i < vertices.length; ++i) { + let v = vertices[i]; + cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius); + } + for (let i = 0; i < faces.length; ++i) { + cf[i] = faces[i].slice(0, faces[i].length - 1); + } + return new CANNON.ConvexPolyhedron(cv, cf); +} - let position = geometry.attributes.position.array; - let index = geometry.getIndex(); +function getMaterials() { + let materials = []; + for (let i = 0; i < faceTexts.length; ++i) { + let texture = null; + // if (customTextTextureFunction) { + // texture = customTextTextureFunction( + // faceTexts[i], + // labelColor, + // diceColor + // ); + // } else { + texture = createTextTexture(faceTexts[i]); - const points = []; - for (let i = 0; i < position.length; i += 3) { - points.push( - new CANNON.Vec3(position[i], position[i + 1], position[i + 2]) + materials.push( + new THREE.MeshPhongMaterial( + Object.assign({}, { map: texture }) + ) ); } - const myfaces = []; - for (let i = 0; i < index.length; i += 3) { - myfaces.push([index[i], index[i + 1], index[i + 2]]); - } + return materials; +} - let myShape = new CANNON.ConvexPolyhedron({ - vertices: points, - faces: myfaces, - }); - return myShape; +function createTextTexture(text) { + let canvas = document.createElement("canvas"); + let context = canvas.getContext("2d"); + let ts = calculateTextureSize(1 / 2 + 1 * textMargin) * 2; + canvas.width = canvas.height = ts; + context.font = ts / (1 + 2 * textMargin) + "pt Arial"; + context.fillStyle = backColor; + context.fillRect(0, 0, canvas.width, canvas.height); + context.textAlign = "center"; + context.textBaseline = "middle"; + context.fillStyle = color; + context.fillText(text, canvas.width / 2, canvas.height / 2); + let texture = new THREE.Texture(canvas); + texture.needsUpdate = true; + return texture; +} + +function calculateTextureSize(approx) { + return Math.max( + 128, + Math.pow(2, Math.floor(Math.log(approx) / Math.log(2))) + ); } From 30e81fc3f301d86e478c7bffea0aec55343fd45c Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Fri, 19 Jul 2024 19:26:22 +0530 Subject: [PATCH 29/60] d10 working --- activities/3DVolume.activity/js/activity.js | 59 ++++---- activities/3DVolume.activity/js/createCube.js | 1 - .../3DVolume.activity/js/createDeca2.js | 127 +++++++++++++++--- .../3DVolume.activity/js/createDodeca.js | 1 + 4 files changed, 143 insertions(+), 45 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 5743dd5e4..d04e7cba6 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1716,34 +1716,35 @@ define([ // } let vector = new THREE.Vector3(0, 1); - let closest_face; - let closest_angle = Math.PI * 2; - - let normals = body.geometry.getAttribute("normal").array; - for (let i = 0; i < 10; ++i) { - let face = i + 1; - - //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - let startVertex = i * 9; - let normal = new THREE.Vector3( - normals[startVertex], - normals[startVertex + 1], - normals[startVertex + 2] - ); - let angle = normal - .clone() - .applyQuaternion(body.quaternion) - .angleTo(vector); - if (angle < closest_angle) { - closest_angle = angle; - closest_face = face; - } - } - lastRoll += closest_face + " + "; - presentScore += closest_face; - updateElements(); - - return closest_face; + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute('normal').array; + for (let i = 0; i < body.geometry.groups.length; ++i) { + let face = body.geometry.groups[i]; + if (face.materialIndex === 0) continue; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3(normals[startVertex], normals[startVertex + 1], normals[startVertex + 2]); + let angle = normal.clone().applyQuaternion(body.quaternion).angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; + } + } + if (closest_face.materialIndex - 1 == 0) { + lastRoll += 10 + " + "; + presentScore += 10; + updateElements(); + return 10; + } else { + lastRoll += closest_face.materialIndex - 1 + " + "; + presentScore += closest_face.materialIndex - 1; + updateElements(); + return closest_face.materialIndex - 1; + } + } function changeBoardBackground(selectedBoard) { @@ -1847,7 +1848,7 @@ define([ function animate(time) { // world.step(timeStep); // Uncomment the next line to view how the physical world actually looks like. - // cannonDebugger.update(); + cannonDebugger.update(); groundMesh.position.copy(groundBody.position); groundMesh.quaternion.copy(groundBody.quaternion); diff --git a/activities/3DVolume.activity/js/createCube.js b/activities/3DVolume.activity/js/createCube.js index 96bc8943b..71c7caf45 100644 --- a/activities/3DVolume.activity/js/createCube.js +++ b/activities/3DVolume.activity/js/createCube.js @@ -117,7 +117,6 @@ function createCube( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; - const boxBody = new CANNON.Body({ mass: 1, shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), diff --git a/activities/3DVolume.activity/js/createDeca2.js b/activities/3DVolume.activity/js/createDeca2.js index d99666faf..5fa72a4aa 100644 --- a/activities/3DVolume.activity/js/createDeca2.js +++ b/activities/3DVolume.activity/js/createDeca2.js @@ -27,11 +27,11 @@ for (let i = 0, b = 0; i < 10; ++i, b += (Math.PI * 2) / 10) { } vertices.push([0, 0, -1]); vertices.push([0, 0, 1]); -let scaleFactor = 0.9; -value = 10; +let scaleFactor = 1; +let values = 10; let faceTexts = [ " ", - "0", + "10", "1", "2", "3", @@ -53,7 +53,7 @@ let faceTexts = [ "19", "20", ]; -let textMargin = 1.0; +let textMargin = 0.8; let chamfer = 0.945; let af = (Math.PI * 6) / 5; let tab = 0; @@ -89,6 +89,9 @@ function createDecahedron( color = tempTextColor; let diceMesh = new THREE.Mesh(getGeometry(), getMaterials()); + diceMesh.reveiceShadow = true; + diceMesh.castShadow = true; + diceMesh.diceObject = this; // if (tempShowNumbers) { // let m = new THREE.MeshLambertMaterial({ @@ -135,7 +138,7 @@ function createDecahedron( // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y // decahedron.castShadow = true; - decahedron = diceMesh + decahedron = diceMesh; scene.add(decahedron); // Create a ConvexPolyhedron shape from the scaled vertices and faces @@ -145,18 +148,110 @@ function createDecahedron( // }); // let myShape = getPolyhedronShape(decahedron); // console.log(myShape); - console.log(diceMesh.geometry.cannon_shape) + let verticesI = []; + let facesI = [ + [5, 7, 11, 0], + [4, 2, 10, 1], + [1, 3, 11, 2], + [0, 8, 10, 3], + [7, 9, 11, 4], + [8, 6, 10, 5], + [9, 1, 11, 6], + [2, 0, 10, 7], + [3, 5, 11, 8], + [6, 4, 10, 9], + [1, 0, 2, -1], + [1, 2, 3, -1], + [3, 2, 4, -1], + [3, 4, 5, -1], + [5, 4, 6, -1], + [5, 6, 7, -1], + [7, 6, 8, -1], + [7, 8, 9, -1], + [9, 8, 0, -1], + [9, 0, 1, -1], + ]; + + // Create vertices for the decahedron + for (let i = 0, b = 0; i < 10; ++i, b += (Math.PI * 2) / 10) { + verticesI.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]); + } + verticesI.push([0, 0, -1]); // bottom vertex + verticesI.push([0, 0, 1]); // top vertex + + // Convert vertices to the format required by CANNON.js + let verticesCannonI = verticesI.map( + (v) => new CANNON.Vec3(v[0], v[1], v[2]) + ); + + // Convert faces to the format required by CANNON.js, removing the last -1 entry + let facesCannonI = facesI.map((face) => face.slice(0, -1)); + + const decahedronShape = new CANNON.ConvexPolyhedron({ + vertices: verticesCannonI, + faces: facesCannonI, + }); let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; + const sides = 10; + const verticesGeo = [ + [0, 0, 1], // Top vertex + [0, 0, -1], // Bottom vertex + ]; + + for (let i = 0; i < sides; ++i) { + const b = (i * Math.PI * 2) / sides; + verticesGeo.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]); + } + + // Convert vertices to CANNON.Vec3 format + let verticesCannon2 = verticesGeo.map( + (v) => new CANNON.Vec3(v[0], v[1], v[2]) + ); + + // Define faces + const facesGeo = [ + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 7], + [0, 7, 8], + [0, 8, 9], + [0, 9, 10], + [0, 10, 11], + [0, 11, 2], + [1, 3, 2], + [1, 4, 3], + [1, 5, 4], + [1, 6, 5], + [1, 7, 6], + [1, 8, 7], + [1, 9, 8], + [1, 10, 9], + [1, 11, 10], + [1, 2, 11], + ]; + + // Corrected face definition + let correctedFacesGeo = facesGeo.map((face) => { + return [face[0], face[1], face[2]]; + }); + + // Create the ConvexPolyhedron shape + const decahedronShape2 = new CANNON.ConvexPolyhedron({ + vertices: verticesCannon2, + faces: correctedFacesGeo, + }); let decahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: diceMesh.geometry.cannon_shape, + mass: 3, // Set mass + shape: decahedronShape2, position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, + friction: 0.5, + restitution: 0.5, }); // decahedronBody = polyhedronShape // if (tempShowNumbers) { @@ -166,14 +261,17 @@ function createDecahedron( // }); // } world.addBody(decahedronBody); + decahedronBody.sleepSpeedLimit = 4; + decahedronBody.sleepTimeLimit = 10; + let angVel1 = 0.2; // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = 0.2; // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); - decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + // decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); + // decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); // console.log(decahedronBody); @@ -371,6 +469,7 @@ function createShape(vertices, faces, radius) { for (let i = 0; i < faces.length; ++i) { cf[i] = faces[i].slice(0, faces[i].length - 1); } + console.log(faces); return new CANNON.ConvexPolyhedron(cv, cf); } @@ -388,9 +487,7 @@ function getMaterials() { texture = createTextTexture(faceTexts[i]); materials.push( - new THREE.MeshPhongMaterial( - Object.assign({}, { map: texture }) - ) + new THREE.MeshPhongMaterial(Object.assign({}, { map: texture })) ); } return materials; diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/createDodeca.js index 6cf5caa00..76e01c0fa 100644 --- a/activities/3DVolume.activity/js/createDodeca.js +++ b/activities/3DVolume.activity/js/createDodeca.js @@ -226,6 +226,7 @@ function createDodecahedron( vertices: vertices, faces: indices, }); + console.log(dodecahedronShape) let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; From 0ee0659a340147be1b096f0271caf5d46bf45108 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sat, 20 Jul 2024 23:42:25 +0530 Subject: [PATCH 30/60] changes --- .../3DVolume.activity/images/default.png | Bin 134909 -> 10936 bytes activities/3DVolume.activity/index.html | 19 +- .../js/{createCube.js => Cube.js} | 80 +-- .../js/{createDeca2.js => Deca.js} | 187 ++++--- .../js/{createDodeca.js => Dodeca.js} | 92 ++- .../js/{createIcosa.js => Icosa.js} | 48 +- .../js/{creatOcta.js => Octa.js} | 128 +++-- .../js/{createTetra.js => Tetra.js} | 63 ++- activities/3DVolume.activity/js/activity.js | 523 ++---------------- activities/3DVolume.activity/js/createDeca.js | 392 ------------- activities/3DVolume.activity/lib/tutorial.js | 23 +- 11 files changed, 451 insertions(+), 1104 deletions(-) rename activities/3DVolume.activity/js/{createCube.js => Cube.js} (80%) rename activities/3DVolume.activity/js/{createDeca2.js => Deca.js} (74%) rename activities/3DVolume.activity/js/{createDodeca.js => Dodeca.js} (77%) rename activities/3DVolume.activity/js/{createIcosa.js => Icosa.js} (88%) rename activities/3DVolume.activity/js/{creatOcta.js => Octa.js} (69%) rename activities/3DVolume.activity/js/{createTetra.js => Tetra.js} (83%) delete mode 100644 activities/3DVolume.activity/js/createDeca.js diff --git a/activities/3DVolume.activity/images/default.png b/activities/3DVolume.activity/images/default.png index a0471c6070ba0f6949af8b4d73d12f4b7812945b..27e4b2f77d3ed5c994e2a367dae6c7ccb7d71cb1 100644 GIT binary patch literal 10936 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 PoMyXX&GIZO`fvXS5WfOY literal 134909 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 z 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 - - - - - - + + + + + + @@ -140,12 +140,6 @@ id="sensor-button" title="Sensor" > - - @@ -187,7 +181,6 @@ -

Time Step is 1/20

diff --git a/activities/3DVolume.activity/js/createCube.js b/activities/3DVolume.activity/js/Cube.js similarity index 80% rename from activities/3DVolume.activity/js/createCube.js rename to activities/3DVolume.activity/js/Cube.js index 71c7caf45..bcb7cf95f 100644 --- a/activities/3DVolume.activity/js/createCube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -91,16 +91,6 @@ function createCube( }); const line = new THREE.LineSegments(wireframe, lineMaterial); boxMesh = line; - } else if (false) { - const boxGeo = new THREE.BoxGeometry(2, 2, 2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - const material = new THREE.MeshPhongMaterial({ map: texture }); - - boxMesh = new THREE.Mesh(boxGeo, material); } else { const boxGeo = new THREE.BoxGeometry(2, 2, 2); const boxMat = new THREE.MeshPhongMaterial({ @@ -122,37 +112,10 @@ function createCube( shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), position: new CANNON.Vec3(x, y, z), material: boxPhysmat, - friction: 0.1, restitution: 5, }); - let previousSleepState = 1; - - // const boxBody = new Proxy(boxBody, { - // set(target, property, value) { - // if ( - // property === "sleepState" && - // previousSleepState === 2 && - // value === 1 - // ) { - // onSleepStateChangeToOne(boxMesh); - // } - // // previousSleepState = target[property]; // update previous state - // target[property] = value; - // return true; - // }, - // }); - world.addBody(boxBody); - - // if (tempShowNumbers) { - // boxBody.addEventListener("sleep", () => { - // previousSleepState = 2; - // console.log(boxBody.sleepState); - // sleepCounter++; - // getCubeScore(boxMesh); - // }); - // } let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = @@ -165,7 +128,7 @@ function createCube( const groundBoxContactMat = new CANNON.ContactMaterial( groundPhysMat, boxPhysmat, - { friction: 0.5 } + { friction: 0 } ); world.addContactMaterial(groundBoxContactMat); @@ -183,6 +146,45 @@ function createCube( tempTextColor, angVel1, angVel2, - angVel3 + angVel3, ]); } + +function getCubeScore(scoresObject, body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), + face: 1, + }, + { + vector: new THREE.Vector3(-1, 0, 0), + face: 2, + }, + { + vector: new THREE.Vector3(0, 1, 0), + face: 3, + }, + { + vector: new THREE.Vector3(0, -1, 0), + face: 4, + }, + { + vector: new THREE.Vector3(0, 0, 1), + face: 5, + }, + { + vector: new THREE.Vector3(0, 0, -1), + face: 6, + }, + ]; + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation); + if (Math.round(faceVector.vector.y) == 1) { + if (!ifRemove) { + scoresObject.lastRoll += faceVector.face + " + "; + scoresObject.presentScore += faceVector.face; + } + return faceVector.face; + } + } +} diff --git a/activities/3DVolume.activity/js/createDeca2.js b/activities/3DVolume.activity/js/Deca.js similarity index 74% rename from activities/3DVolume.activity/js/createDeca2.js rename to activities/3DVolume.activity/js/Deca.js index 5fa72a4aa..5e1f848a7 100644 --- a/activities/3DVolume.activity/js/createDeca2.js +++ b/activities/3DVolume.activity/js/Deca.js @@ -88,57 +88,40 @@ function createDecahedron( backColor = tempFillColor; color = tempTextColor; - let diceMesh = new THREE.Mesh(getGeometry(), getMaterials()); - diceMesh.reveiceShadow = true; - diceMesh.castShadow = true; - diceMesh.diceObject = this; - - // if (tempShowNumbers) { - // let m = new THREE.MeshLambertMaterial({ - // map: getNumbers(tempFillColor, tempTextColor), - // }); - - // decahedron = new THREE.Mesh(g, m); - // decahedron.rotation.z = Math.PI / 2; - // } else if (tempTransparent) { - // const decahedronTransaprentGeometry = decaGeometry2; - // const wireframe = new THREE.WireframeGeometry( - // decahedronTransaprentGeometry - // ); - // const lineMaterial = new THREE.LineBasicMaterial({ - // color: sharedColor != null ? sharedColor : ctx.presentColor, - // depthTest: true, - // opacity: 1, - // transparent: false, - // }); - // const line = new THREE.LineSegments(wireframe, lineMaterial); - // decahedron = line; - // } else if (false) { - // const decaGeo = decaGeometry; - - // const texture = new THREE.TextureLoader().load( - // sharedImageData != null ? sharedImageData : imageData - // ); - - // // Create material using the texture - // const material = new THREE.MeshPhongMaterial({ map: texture }); - - // // Create cube mesh with the material - // decahedron = new THREE.Mesh(decaGeo, material); - // } else { - // const decahedronGeometry = decaGeometry2; - - // const decaMaterial = new THREE.MeshStandardMaterial({ - // color: sharedColor != null ? sharedColor : ctx.presentColor, - // wireframe: false, - // }); - - // decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); - // } + // let diceMesh = new THREE.Mesh(getGeometry(), getMaterials()); + // diceMesh.reveiceShadow = true; + // diceMesh.castShadow = true; + // diceMesh.diceObject = this; + + if (tempShowNumbers) { + decahedron = new THREE.Mesh(getGeometry(), getMaterials()); + } else if (tempTransparent) { + const decahedronTransaprentGeometry = getGeometry(); + const wireframe = new THREE.WireframeGeometry( + decahedronTransaprentGeometry + ); + const lineMaterial = new THREE.LineBasicMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + depthTest: true, + opacity: 1, + transparent: false, + }); + const line = new THREE.LineSegments(wireframe, lineMaterial); + decahedron = line; + } else { + const decahedronGeometry = getGeometry(); + + const decaMaterial = new THREE.MeshStandardMaterial({ + color: sharedColor != null ? sharedColor : ctx.presentColor, + wireframe: false, + }); + + decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); + } - // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - // decahedron.castShadow = true; - decahedron = diceMesh; + decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + decahedron.castShadow = true; + // decahedron = diceMesh; scene.add(decahedron); // Create a ConvexPolyhedron shape from the scaled vertices and faces @@ -250,32 +233,22 @@ function createDecahedron( mass: 3, // Set mass shape: decahedronShape2, position: new CANNON.Vec3(x, y, z), - friction: 0.5, restitution: 0.5, }); - // decahedronBody = polyhedronShape - // if (tempShowNumbers) { - // decahedronBody.addEventListener("sleep", () => { - // sleepCounter++; - // getDecaScore(decahedron); - // }); - // } world.addBody(decahedronBody); decahedronBody.sleepSpeedLimit = 4; decahedronBody.sleepTimeLimit = 10; + let angVel1 = + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + let angVel2 = + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - let angVel1 = 0.2; - // sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = 0.2; - // sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - - // decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); - // decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); + decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); + decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); - // console.log(decahedronBody); - // console.log(decahedronBody.rotation); + if (quaternionShared != null && quaternionShared != undefined) { decahedron.quaternion.copy(quaternionShared); @@ -477,13 +450,6 @@ function getMaterials() { let materials = []; for (let i = 0; i < faceTexts.length; ++i) { let texture = null; - // if (customTextTextureFunction) { - // texture = customTextTextureFunction( - // faceTexts[i], - // labelColor, - // diceColor - // ); - // } else { texture = createTextTexture(faceTexts[i]); materials.push( @@ -516,3 +482,76 @@ function calculateTextureSize(approx) { Math.pow(2, Math.floor(Math.log(approx) / Math.log(2))) ); } + +function getDecaScore(scoresObject, body, ifRemove) { + // Decahedron face vectors + // const faceVectors = [ + // { vector: new THREE.Vector3(0, 1, 0.5), face: 7 }, + // { vector: new THREE.Vector3(0, 1, -0.5), face: 2 }, + // { vector: new THREE.Vector3(0, -1, 0.5), face: 3 }, + // { vector: new THREE.Vector3(0, -1, -0.5), face: 10 }, + // { vector: new THREE.Vector3(0.5, 0, 1), face: 5 }, + // { vector: new THREE.Vector3(-0.5, 0, 1), face: 8 }, + // { vector: new THREE.Vector3(0.5, 0, -1), face: 9 }, + // { vector: new THREE.Vector3(-0.5, 0, -1), face: 6 }, + // { vector: new THREE.Vector3(1, 0.5, 0), face: 1 }, + // { vector: new THREE.Vector3(-1, 0.5, 0), face: 4 }, + // ]; + + // for (const faceVector of faceVectors) { + // faceVector.vector.normalize().applyEuler(body.rotation); + + // if (Math.round(faceVector.vector.y) === 1) { + // if (!ifRemove) { + // scoresObject.lastRoll += faceVector.face + " + "; + // scoresObject.presentScore += faceVector.face; + // updateElements(); + // break; + // } + // for (let i = 0; i < diceArray.length; i++) { + // if (body == diceArray[i][0]) { + // diceArray[i][7] = faceVector.face; + // } + // } + // return faceVector.face; + // } + // } + + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < body.geometry.groups.length; ++i) { + let face = body.geometry.groups[i]; + if (face.materialIndex === 0) continue; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal + .clone() + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; + } + } + let scoreToShow; + if (closest_face.materialIndex - 1 == 0) { + scoreToShow = 10; + } else { + scoreToShow = closest_face.materialIndex - 1; + } + if (!ifRemove) { + scoresObject.lastRoll += scoreToShow + " + "; + scoresObject.presentScore += scoreToShow; + } else { + return scoreToShow; + } +} \ No newline at end of file diff --git a/activities/3DVolume.activity/js/createDodeca.js b/activities/3DVolume.activity/js/Dodeca.js similarity index 77% rename from activities/3DVolume.activity/js/createDodeca.js rename to activities/3DVolume.activity/js/Dodeca.js index 76e01c0fa..c4dc217a4 100644 --- a/activities/3DVolume.activity/js/createDodeca.js +++ b/activities/3DVolume.activity/js/Dodeca.js @@ -128,18 +128,6 @@ function createDodecahedron( }); const line = new THREE.LineSegments(wireframe, lineMaterial); dodecahedron = line; - } else if (false) { - const dodecaGeo = new THREE.DodecahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - dodecahedron = new THREE.Mesh(dodecaGeo, material); } else { const dodecahedronGeometry = new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron @@ -226,7 +214,7 @@ function createDodecahedron( vertices: vertices, faces: indices, }); - console.log(dodecahedronShape) + console.log(dodecahedronShape); let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; @@ -241,19 +229,14 @@ function createDodecahedron( }); dodecahedronBody.sleepSpeedLimit = 0.2; dodecahedronBody.sleepTimeLimit = 3; - // if (tempShowNumbers) { - // dodecahedronBody.addEventListener("sleep", () => { - // sleepCounter++; - // getDodecaScore(dodecahedron); - // }); - // } + world.addBody(dodecahedronBody); let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - let angVel3 = + let angVel3 = sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; dodecahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); @@ -274,6 +257,73 @@ function createDodecahedron( tempTextColor, angVel1, angVel2, - angVel3 + angVel3, ]); } + +function getDodecaScore(scoresObject, body, ifRemove) { + // Define the golden ratio + const phi = (1 + Math.sqrt(5)) / 2; + + // Decahedron face vectors + const faceVectors = [ + { vector: new THREE.Vector3(1, 1, 1), face: 1 }, + { vector: new THREE.Vector3(1, 1, -1), face: 6 }, + { vector: new THREE.Vector3(1, -1, 1), face: 11 }, + { vector: new THREE.Vector3(1, -1, -1), face: 9 }, + { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, + { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, + { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, + { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, + { vector: new THREE.Vector3(0, phi, 1 / phi), face: 4 }, + { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, + { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, + { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, + ]; + + for (const faceVector of faceVectors) { + faceVector.vector.normalize().applyEuler(body.rotation); + + if (Math.round(faceVector.vector.y) === 1) { + if (!ifRemove) { + scoresObject.lastRoll += faceVector.face + " + "; + scoresObject.presentScore += faceVector.face; + break; + } + return faceVector.face; + } + } + + // let vector = new THREE.Vector3(0, -1); + // let closest_face; + // let closest_angle = Math.PI * 2; + + // let normals = body.geometry.getAttribute("normal").array; + // console.log(normals) + // for (let i = 0; i < 12; ++i) { + // let face = i + 1; + + // //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + // let startVertex = i * 9; + // let normal = new THREE.Vector3( + // normals[startVertex], + // normals[startVertex + 1], + // normals[startVertex + 2] + // ); + // let angle = normal + // .clone() + // .applyQuaternion(body.quaternion) + // .angleTo(vector); + // if (angle < closest_angle) { + // closest_angle = angle; + // closest_face = face; + // } + // } + + // // switch(closest_face) { + // // } + // scoresObject.lastRoll += closest_face + " + "; + // scoresObject.presentScore += closest_face; + // updateElements(); + // return closest_face; +} diff --git a/activities/3DVolume.activity/js/createIcosa.js b/activities/3DVolume.activity/js/Icosa.js similarity index 88% rename from activities/3DVolume.activity/js/createIcosa.js rename to activities/3DVolume.activity/js/Icosa.js index 6c76db0b7..12477fd90 100644 --- a/activities/3DVolume.activity/js/createIcosa.js +++ b/activities/3DVolume.activity/js/Icosa.js @@ -99,18 +99,6 @@ function createIcosahedron( }); const line = new THREE.LineSegments(wireframe, lineMaterial); icosahedron = line; - } else if (false) { - const boxGeo = new THREE.IcosahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - icosahedron = new THREE.Mesh(boxGeo, material); } else { const icosahedronGeometry = new THREE.IcosahedronGeometry(1.5); // Size of the icosahedron @@ -223,6 +211,40 @@ function createIcosahedron( tempTextColor, angVel1, angVel2, - angVel3 + angVel3, ]); } + + +function getIcosaScore(scoresObject, body, ifRemove) { + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < 20; ++i) { + let face = i + 1; + + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal + .clone() + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; + } + } + if (!ifRemove) { + scoresObject.lastRoll += closest_face + " + "; + scoresObject.presentScore += closest_face; + } + + + return closest_face; +} diff --git a/activities/3DVolume.activity/js/creatOcta.js b/activities/3DVolume.activity/js/Octa.js similarity index 69% rename from activities/3DVolume.activity/js/creatOcta.js rename to activities/3DVolume.activity/js/Octa.js index d8ead704a..b95108f43 100644 --- a/activities/3DVolume.activity/js/creatOcta.js +++ b/activities/3DVolume.activity/js/Octa.js @@ -94,18 +94,6 @@ function createOctahedron( }); const line = new THREE.LineSegments(wireframe, lineMaterial); octahedron = line; - } else if (false) { - const octahedronGeometry = new THREE.OctahedronGeometry(2); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - octahedron = new THREE.Mesh(octahedronGeometry, material); } else { const octahedronGeometry = new THREE.OctahedronGeometry(1.6); // Size of the octahedron @@ -118,7 +106,7 @@ function createOctahedron( octahedron.castShadow = true; scene.add(octahedron); - const scaleFactor = 0.8; // Change this value to scale the shape (e.g., 2 for doubling the size) + const scaleFactor = 0.8; // Change this value to scale the shape const verticesOcta = [ new CANNON.Vec3(2 * scaleFactor, 0, 0), // Vertex 1 (right) @@ -158,42 +146,15 @@ function createOctahedron( restitution: 5, }); - let previousSleepState = 1; - - // const octahedronBody = new Proxy(octahedronBody, { - // set(target, property, value) { - // if ( - // property === "sleepState" && - // previousSleepState === 2 && - // value === 1 - // ) { - // onSleepStateChangeToOne(octahedron); - // } - // target[property] = value; - // return true; - // }, - // }); - world.addBody(octahedronBody); - // if (tempShowNumbers) { - // octahedronBody.addEventListener("sleep", () => { - // previousSleepState = 2; - // console.log(octahedronBody.sleepState); - // sleepCounter++; - // getOctaScore(octahedron); - // }); - // } - let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - let angVel3 = + let angVel3 = sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; - - octahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); octahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh @@ -212,6 +173,89 @@ function createOctahedron( tempTextColor, angVel1, angVel2, - angVel3 + angVel3, ]); } + +function getOctaScore(scoresObject, body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis + face: 4, + }, + { + vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis + face: 6, + }, + { + vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis + face: 5, + }, + { + vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis + face: 3, + }, + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) + face: 8, + }, + { + vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) + face: 2, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 7, + }, + ]; + + let minValue = 1000000; + let minInd; + for (let i = 0; i < faceVectors.length; i++) { + let faceVector = faceVectors[i]; + faceVector.vector.applyEuler(body.rotation); + if (minValue > Math.abs(1 - faceVector.vector.y)) { + minValue = Math.abs(1 - faceVector.vector.y); + minInd = i; + } + } + if (!ifRemove) { + scoresObject.lastRoll += faceVectors[minInd].face + " + "; + scoresObject.presentScore += faceVectors[minInd].face; + } + return faceVectors[minInd].face; + + // let vector = new THREE.Vector3(0, 1); + // let closest_face; + // let closest_angle = Math.PI * 2; + + // let normals = body.geometry.getAttribute("normal").array; + // for (let i = 0; i < 8; ++i) { + // let face = i + 1; + + // //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + // let startVertex = i * 9; + // let normal = new THREE.Vector3( + // normals[startVertex], + // normals[startVertex + 1], + // normals[startVertex + 2] + // ); + // let angle = normal + // .clone() + // .applyQuaternion(body.quaternion) + // .angleTo(vector); + // if (angle < closest_angle) { + // closest_angle = angle; + // closest_face = face; + // } + // } + // scoresObject.lastRoll += closest_face + " + "; + // scoresObject.presentScore += closest_face; + // updateElements(); + + // return closest_face; +} diff --git a/activities/3DVolume.activity/js/createTetra.js b/activities/3DVolume.activity/js/Tetra.js similarity index 83% rename from activities/3DVolume.activity/js/createTetra.js rename to activities/3DVolume.activity/js/Tetra.js index 137ef63a6..98463d4c9 100644 --- a/activities/3DVolume.activity/js/createTetra.js +++ b/activities/3DVolume.activity/js/Tetra.js @@ -117,18 +117,6 @@ function createTetrahedron( }); const line = new THREE.LineSegments(wireframe, lineMaterial); tetrahedron = line; - } else if (false) { - const boxGeo = new THREE.TetrahedronGeometry(1.7); - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - tetrahedron = new THREE.Mesh(boxGeo, material); } else { const tetrahedronGeometry = new THREE.TetrahedronGeometry(1.7); // Size of the tetrahedron @@ -140,7 +128,7 @@ function createTetrahedron( tetrahedron = new THREE.Mesh(tetrahedronGeometry, tetraMaterial); } - tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y + tetrahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); tetrahedron.castShadow = true; scene.add(tetrahedron); @@ -151,10 +139,10 @@ function createTetrahedron( new CANNON.Vec3(1, -1, -1), // Vertex 4 (front) ]; const facesTetra = [ - [2, 1, 0], // Triangle 1 (right, top, left) - [0, 3, 2], // Triangle 2 (right, front, top) - [1, 3, 0], // Triangle 3 (top, front, left) - [2, 3, 1], // Triangle 4 (left, right, front) + [2, 1, 0], + [0, 3, 2], + [1, 3, 0], + [2, 3, 1], ]; // Create a ConvexPolyhedron shape from the vertices and faces const tetrahedronShape = new CANNON.ConvexPolyhedron({ @@ -173,12 +161,6 @@ function createTetrahedron( friction: -1, restitution: 5, }); - // if (tempShowNumbers) { - // tetrahedronBody.addEventListener("sleep", () => { - // sleepCounter++; - // getTetraScore(tetrahedron); - // }); - // } world.addBody(tetrahedronBody); let angVel1 = sharedAngVel1 == null ? Math.random() * (3 - 0.1) + 0.1 : sharedAngVel1; @@ -205,6 +187,39 @@ function createTetrahedron( tempTextColor, angVel1, angVel2, - angVel3 + angVel3, ]); } + +function getTetraScore(scoresObject, body, ifRemove) { + const faceVectors = [ + { + vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) + face: 1, + }, + { + vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) + face: 3, + }, + { + vector: new THREE.Vector3(-1, 1, -1).normalize(), // Towards a corner (negative x, positive y, negative z) + face: 2, + }, + { + vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) + face: 4, + }, + ]; + + for (const faceVector of faceVectors) { + faceVector.vector.applyEuler(body.rotation); + if (Math.round(faceVector.vector.y) == 1) { + if (!ifRemove) { + scoresObject.lastRoll += faceVector.face + " + "; + scoresObject.presentScore += faceVector.face; + break; + } + return faceVector.face; + } + } +} diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index d04e7cba6..cb00e9aac 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -86,8 +86,10 @@ define([ rollingForce: randomDirection.scale(2), }; let presentBackground = null; - let presentScore = 0; - let lastRoll = ""; + let scoresObject = { + lastRoll : "", + presentScore : 0 + } let diceArray = []; let journalDiceArray = []; let showImage = false; @@ -661,9 +663,9 @@ define([ // Adds all the numbers on top for the numbered volumes. function updateElements() { lastRollElement.textContent = - lastRoll.substring(0, lastRoll.length - 2) + + scoresObject.lastRoll.substring(0, scoresObject.lastRoll.length - 2) + "= " + - presentScore; + scoresObject.presentScore; } const renderer = new THREE.WebGLRenderer({ @@ -837,32 +839,32 @@ define([ let score; switch (diceArray[i][2]) { case "cube": - score = getCubeScore(diceArray[i][0], true); + score = getCubeScore(scoresObject, diceArray[i][0], true); break; case "icosa": - score = getIcosaScore(diceArray[i][0], true); + score = getIcosaScore(scoresObject, diceArray[i][0], true); break; case "deca": - score = getDecaScore(diceArray[i][0], true); + score = getDecaScore(scoresObject, diceArray[i][0], true); break; case "dodeca": - score = getDodecaScore(diceArray[i][0], true); + score = getDodecaScore(scoresObject, diceArray[i][0], true); break; case "octa": - score = getOctaScore(diceArray[i][0], true); + score = getOctaScore(scoresObject, diceArray[i][0], true); break; case "tetra": - score = getTetraScore(diceArray[i][0], true); + score = getTetraScore(scoresObject, diceArray[i][0], true); break; default: console.log(`Unknown type: ${diceArray[i][3]}`); continue; } - presentScore = presentScore - score; - console.log(presentScore); + scoresObject.presentScore = scoresObject.presentScore - score; + console.log(scoresObject.presentScore); - let scoresArray = lastRoll.split(" + "); + let scoresArray = scoresObject.lastRoll.split(" + "); // Find the index of the first occurrence of the score to remove let indexToRemove = scoresArray.indexOf( @@ -875,10 +877,10 @@ define([ } // Join the remaining scores back into a string - lastRoll = scoresArray.join(" + "); + scoresObject.lastRoll = scoresArray.join(" + "); updateElements(); - console.log(lastRoll); - console.log(presentScore); + console.log(scoresObject.lastRoll); + console.log(scoresObject.presentScore); num--; } world.removeBody(diceArray[i][1]); @@ -888,8 +890,8 @@ define([ } if (num == 0) { lastRollElement.textContent = ""; - lastRoll = ""; - presentScore = 0; + scoresObject.lastRoll = ""; + scoresObject.presentScore = 0; } } @@ -1048,6 +1050,7 @@ define([ wakeAll(); } } + lastRollElement.textContent = "x- " + acceleration.x + ", " + "y- " + acceleration.y + ", " + "z- " + acceleration.x + " "; } // Wakes all the volumes so that they move towards the gravity. @@ -1087,7 +1090,9 @@ define([ type: CANNON.Body.STATIC, material: groundPhysMat, }); - groundBody.material.friction = 1; + groundBody.material.friction = 0; + groundBody.material.contactEquationStiffness = 1e8; + groundBody.material.contactEquationRelaxation = 3; groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); world.addBody(groundBody); @@ -1254,8 +1259,8 @@ define([ world.removeBody(diceArray[i][1]); } if (diceArray.length > 0) { - lastRoll = ""; - presentScore = 0; + scoresObject.lastRoll = ""; + scoresObject.presentScore = 0; for (let i = 0; i < diceArray.length; i++) { let rollingForce; if (sharedRolling != null) { @@ -1297,7 +1302,7 @@ define([ for (let i = 0; i < dices.icosa; i++) { createIcosahedron(); } - lastRoll = ""; + scoresObject.lastRoll = ""; // if (ctx.showNumbers) { // getScore(); // } @@ -1306,446 +1311,12 @@ define([ // Functions to get the scores of the dice. - function getOctaScore(body, ifRemove) { - // const faceVectors = [ - // { - // vector: new THREE.Vector3(1, 0, 0), // Along the positive x-axis - // face: 4, - // }, - // { - // vector: new THREE.Vector3(-1, 0, 0), // Along the negative x-axis - // face: 6, - // }, - // { - // vector: new THREE.Vector3(0, 1, 0), // Along the positive y-axis - // face: 5, - // }, - // { - // vector: new THREE.Vector3(0, -1, 0), // Along the negative y-axis - // face: 3, - // }, - // { - // vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) - // face: 1, - // }, - // { - // vector: new THREE.Vector3(-1, 1, 1).normalize(), // Towards a corner (negative x, positive y, z) - // face: 8, - // }, - // { - // vector: new THREE.Vector3(1, -1, 1).normalize(), // Towards a corner (positive x, negative y, z) - // face: 2, - // }, - // { - // vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) - // face: 7, - // }, - // ]; - - // let minValue = 1000000; - // let minInd; - // for (let i = 0; i < faceVectors.length; i++) { - // let faceVector = faceVectors[i]; - // faceVector.vector.applyEuler(body.rotation); - // if (minValue > Math.abs(1 - faceVector.vector.y)) { - // minValue = Math.abs(1 - faceVector.vector.y); - // minInd = i; - // } - // } - // if (!ifRemove) { - // lastRoll += faceVectors[minInd].face + " + "; - // presentScore += faceVectors[minInd].face; - // updateElements(); - // } - // for (let i = 0; i < diceArray.length; i++) { - // if (body == diceArray[i][0]) { - // diceArray[i][7] = faceVectors[minInd].face; - // } - // } - // return faceVectors[minInd].face; - - let vector = new THREE.Vector3(0, 1); - let closest_face; - let closest_angle = Math.PI * 2; - - let normals = body.geometry.getAttribute("normal").array; - for (let i = 0; i < 8; ++i) { - let face = i + 1; - - //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - let startVertex = i * 9; - let normal = new THREE.Vector3( - normals[startVertex], - normals[startVertex + 1], - normals[startVertex + 2] - ); - let angle = normal - .clone() - .applyQuaternion(body.quaternion) - .angleTo(vector); - if (angle < closest_angle) { - closest_angle = angle; - closest_face = face; - } - } - lastRoll += closest_face + " + "; - presentScore += closest_face; - updateElements(); - - return closest_face; - } - - function getCubeScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 0, 0), - face: 1, - }, - { - vector: new THREE.Vector3(-1, 0, 0), - face: 2, - }, - { - vector: new THREE.Vector3(0, 1, 0), - face: 3, - }, - { - vector: new THREE.Vector3(0, -1, 0), - face: 4, - }, - { - vector: new THREE.Vector3(0, 0, 1), - face: 5, - }, - { - vector: new THREE.Vector3(0, 0, -1), - face: 6, - }, - ]; - for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation); - if (Math.round(faceVector.vector.y) == 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; - } - } - } - function getTetraScore(body, ifRemove) { - const faceVectors = [ - { - vector: new THREE.Vector3(1, 1, 1).normalize(), // Towards a corner (positive x, y, z) - face: 1, - }, - { - vector: new THREE.Vector3(-1, -1, 1).normalize(), // Towards a corner (negative x, negative y, z) - face: 3, - }, - { - vector: new THREE.Vector3(-1, 1, -1).normalize(), // Towards a corner (negative x, positive y, negative z) - face: 2, - }, - { - vector: new THREE.Vector3(1, -1, -1).normalize(), // Towards a corner (positive x, negative y, negative z) - face: 4, - }, - ]; - - for (const faceVector of faceVectors) { - faceVector.vector.applyEuler(body.rotation); - if (Math.round(faceVector.vector.y) == 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; - } - } - } - function getIcosaScore(body, ifRemove) { - // // Define the golden ratio - // const phi = (1 + Math.sqrt(5)) / 2; - - // // Icosahedron face vectors - // const faceVectors = [ - // { vector: new THREE.Vector3(0, 1, phi).normalize(), face: 7 }, - // { vector: new THREE.Vector3(0, -1, phi).normalize(), face: 16 }, - // { vector: new THREE.Vector3(0, 1, -phi).normalize(), face: 4 }, - // { - // vector: new THREE.Vector3(0, -1, -phi).normalize(), - // face: 20, - // }, - // { vector: new THREE.Vector3(1, phi, 0).normalize(), face: 6 }, - // { vector: new THREE.Vector3(-1, phi, 0).normalize(), face: 5 }, - // { vector: new THREE.Vector3(1, -phi, 0).normalize(), face: 9 }, - // { - // vector: new THREE.Vector3(-1, -phi, 0).normalize(), - // face: 17, - // }, - // { vector: new THREE.Vector3(phi, 0, 1).normalize(), face: 15 }, - // { vector: new THREE.Vector3(-phi, 0, 1).normalize(), face: 8 }, - // { vector: new THREE.Vector3(phi, 0, -1).normalize(), face: 19 }, - // { - // vector: new THREE.Vector3(-phi, 0, -1).normalize(), - // face: 13, - // }, - // { vector: new THREE.Vector3(1, phi, phi).normalize(), face: 2 }, - // { - // vector: new THREE.Vector3(-1, phi, phi).normalize(), - // face: 1, - // }, - // { - // vector: new THREE.Vector3(1, -phi, phi).normalize(), - // face: 11, - // }, - // { - // vector: new THREE.Vector3(-1, -phi, phi).normalize(), - // face: 12, - // }, - // { - // vector: new THREE.Vector3(1, phi, -phi).normalize(), - // face: 10, - // }, - // { - // vector: new THREE.Vector3(-1, phi, -phi).normalize(), - // face: 3, - // }, - // { - // vector: new THREE.Vector3(1, -phi, -phi).normalize(), - // face: 14, - // }, - // { - // vector: new THREE.Vector3(-1, -phi, -phi).normalize(), - // face: 18, - // }, - // ]; - - // let closestFace = null; - // let closestDot = -1; // Initialize with the smallest possible dot product - - // // Reference vector pointing up - // let upVector = new THREE.Vector3(0, 1, 0); - - // for (const faceVector of faceVectors) { - // // Apply the body's quaternion to the face vector - // let worldVector = faceVector.vector - // .clone() - // .applyQuaternion(body.quaternion); - - // // Calculate the dot product with the up vector - // let dot = worldVector.dot(upVector); - - // // Check if this is the closest to pointing up - // if (dot > closestDot) { - // closestDot = dot; - // closestFace = faceVector; - // } - // } - - // if (closestFace) { - // let faceNumber = closestFace.face; - // if (!ifRemove) { - // lastRoll += faceNumber + " + "; - // presentScore += faceNumber; - // updateElements(); - // } - // for (let i = 0; i < diceArray.length; i++) { - // if (body == diceArray[i][0]) { - // diceArray[i][7] = faceNumber; - // } - // } - // for (let i = 0; i < diceArray.length; i++) { - // if (body == diceArray[i][0]) { - // diceArray[i][7] = faceNumber; - // } - // } - // return faceNumber; - // } - - let vector = new THREE.Vector3(0, 1); - let closest_face; - let closest_angle = Math.PI * 2; - - let normals = body.geometry.getAttribute("normal").array; - for (let i = 0; i < 20; ++i) { - let face = i + 1; - - //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - let startVertex = i * 9; - let normal = new THREE.Vector3( - normals[startVertex], - normals[startVertex + 1], - normals[startVertex + 2] - ); - let angle = normal - .clone() - .applyQuaternion(body.quaternion) - .angleTo(vector); - if (angle < closest_angle) { - closest_angle = angle; - closest_face = face; - } - } - lastRoll += closest_face + " + "; - presentScore += closest_face; - updateElements(); + - return closest_face; - } - function getDodecaScore(body, ifRemove) { - // Define the golden ratio - const phi = (1 + Math.sqrt(5)) / 2; - - // Decahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(1, 1, 1), face: 1 }, - { vector: new THREE.Vector3(1, 1, -1), face: 6 }, - { vector: new THREE.Vector3(1, -1, 1), face: 11 }, - { vector: new THREE.Vector3(1, -1, -1), face: 9 }, - { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, - { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, - { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, - { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, - { vector: new THREE.Vector3(0, phi, 1 / phi), face: 4 }, - { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, - { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, - { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, - ]; - - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); - - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - lastRoll += faceVector.face + " + "; - presentScore += faceVector.face; - updateElements(); - break; - } - for (let i = 0; i < diceArray.length; i++) { - if (body == diceArray[i][0]) { - diceArray[i][7] = faceVector.face; - } - } - return faceVector.face; - } - } - // let vector = new THREE.Vector3(0, -1); - // let closest_face; - // let closest_angle = Math.PI * 2; - - // let normals = body.geometry.getAttribute("normal").array; - // console.log(normals) - // for (let i = 0; i < 12; ++i) { - // let face = i + 1; - - // //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - // let startVertex = i * 9; - // let normal = new THREE.Vector3( - // normals[startVertex], - // normals[startVertex + 1], - // normals[startVertex + 2] - // ); - // let angle = normal - // .clone() - // .applyQuaternion(body.quaternion) - // .angleTo(vector); - // if (angle < closest_angle) { - // closest_angle = angle; - // closest_face = face; - // } - // } - - // // switch(closest_face) { - // // } - // lastRoll += closest_face + " + "; - // presentScore += closest_face; - // updateElements(); - // return closest_face; - } - function getDecaScore(body, ifRemove) { - // Decahedron face vectors - // const faceVectors = [ - // { vector: new THREE.Vector3(0, 1, 0.5), face: 7 }, - // { vector: new THREE.Vector3(0, 1, -0.5), face: 2 }, - // { vector: new THREE.Vector3(0, -1, 0.5), face: 3 }, - // { vector: new THREE.Vector3(0, -1, -0.5), face: 10 }, - // { vector: new THREE.Vector3(0.5, 0, 1), face: 5 }, - // { vector: new THREE.Vector3(-0.5, 0, 1), face: 8 }, - // { vector: new THREE.Vector3(0.5, 0, -1), face: 9 }, - // { vector: new THREE.Vector3(-0.5, 0, -1), face: 6 }, - // { vector: new THREE.Vector3(1, 0.5, 0), face: 1 }, - // { vector: new THREE.Vector3(-1, 0.5, 0), face: 4 }, - // ]; - - // for (const faceVector of faceVectors) { - // faceVector.vector.normalize().applyEuler(body.rotation); - - // if (Math.round(faceVector.vector.y) === 1) { - // if (!ifRemove) { - // lastRoll += faceVector.face + " + "; - // presentScore += faceVector.face; - // updateElements(); - // break; - // } - // for (let i = 0; i < diceArray.length; i++) { - // if (body == diceArray[i][0]) { - // diceArray[i][7] = faceVector.face; - // } - // } - // return faceVector.face; - // } - // } - - let vector = new THREE.Vector3(0, 1); - let closest_face; - let closest_angle = Math.PI * 2; - - let normals = body.geometry.getAttribute('normal').array; - for (let i = 0; i < body.geometry.groups.length; ++i) { - let face = body.geometry.groups[i]; - if (face.materialIndex === 0) continue; - - //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - let startVertex = i * 9; - let normal = new THREE.Vector3(normals[startVertex], normals[startVertex + 1], normals[startVertex + 2]); - let angle = normal.clone().applyQuaternion(body.quaternion).angleTo(vector); - if (angle < closest_angle) { - closest_angle = angle; - closest_face = face; - } - } - if (closest_face.materialIndex - 1 == 0) { - lastRoll += 10 + " + "; - presentScore += 10; - updateElements(); - return 10; - } else { - lastRoll += closest_face.materialIndex - 1 + " + "; - presentScore += closest_face.materialIndex - 1; - updateElements(); - return closest_face.materialIndex - 1; - } - } function changeBoardBackground(selectedBoard) { console.log(selectedBoard); @@ -1753,19 +1324,17 @@ define([ let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { case "green-board": - console.log("now changing bg to green"); textureLoader.load( "images/grass_background.png", function (groundTexture) { groundMesh.material.wireframe = false; groundMesh.material.map = groundTexture; groundMesh.material.needsUpdate = true; - groundBody.material.friction = 5; + groundBody.material.friction = 100; } ); break; case "wood": - console.log("wood changing"); textureLoader.load( "images/wood.png", function (groundTexture) { @@ -1773,7 +1342,7 @@ define([ groundMesh.material.color.setHex(0xf0c592); groundMesh.material.map = groundTexture; groundMesh.material.needsUpdate = true; - groundBody.material.friction = 3; + groundBody.material.friction = 60; } ); break; @@ -1788,35 +1357,36 @@ define([ } // This function calls the getScore functions for all the volumes and displays them. function getScores() { - presentScore = 0; - lastRoll = ""; + scoresObject.presentScore = 0; + scoresObject.lastRoll = ""; lastRollElement.textContent = ""; for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][3]) { switch (diceArray[i][2]) { case "cube": - score = getCubeScore(diceArray[i][0]); + score = getCubeScore(scoresObject, diceArray[i][0]); break; case "icosa": - score = getIcosaScore(diceArray[i][0]); + score = getIcosaScore(scoresObject, diceArray[i][0]); break; case "deca": - score = getDecaScore(diceArray[i][0]); + score = getDecaScore(scoresObject, diceArray[i][0]); break; case "dodeca": - score = getDodecaScore(diceArray[i][0]); + score = getDodecaScore(scoresObject, diceArray[i][0]); break; case "octa": - score = getOctaScore(diceArray[i][0]); + score = getOctaScore(scoresObject, diceArray[i][0]); break; case "tetra": - score = getTetraScore(diceArray[i][0]); + score = getTetraScore(scoresObject, diceArray[i][0]); break; default: console.log(`Unknown type: ${diceArray[i][3]}`); continue; } + updateElements() } } } @@ -1831,24 +1401,13 @@ define([ let time = 20; let timeStep = 1 / time; - document - .querySelector("#increase-button") - .addEventListener("click", () => { - if (time == 5) { - alert("cant go lower"); - return; - } - time -= 1; - timeStep = 1 / time; - document.getElementById("time").innerHTML = time; - }); animate(); function animate(time) { // world.step(timeStep); // Uncomment the next line to view how the physical world actually looks like. - cannonDebugger.update(); + // cannonDebugger.update(); groundMesh.position.copy(groundBody.position); groundMesh.quaternion.copy(groundBody.quaternion); @@ -1873,7 +1432,7 @@ define([ renderer.setAnimationLoop(animate); - const fixedTimeStep = 1 / 40; + const fixedTimeStep = 1 / 40; const maxSubSteps = 3; function updatePhysics() { diff --git a/activities/3DVolume.activity/js/createDeca.js b/activities/3DVolume.activity/js/createDeca.js deleted file mode 100644 index 3caaff3cf..000000000 --- a/activities/3DVolume.activity/js/createDeca.js +++ /dev/null @@ -1,392 +0,0 @@ -const sides = 10; -const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], -].flat(); - -for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); -} - -let myDecahedron; - -const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], -].flat(); - -function createDecahedron( - sharedColor, - ifNumbers, - ifTransparent, - xCoordinateShared, - zCoordinateShared, - ifImage, - sharedImageData, - yCoordinateShared, - quaternionShared, - sharedTextColor, - ctx, - diceArray, - world, - scene, - groundPhysMat, - sharedAngVel1, - sharedAngVel2, - sharedAngVel3 -) { - let decahedron; - let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; - let tempTransparent = - ifTransparent == null ? ctx.toggleTransparent : ifTransparent; - let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; - let tempTextColor = - sharedTextColor != null ? sharedTextColor : ctx.textColor; - - const radius = 1.3; - const verticesGeo = [ - [0, 0, 1], - [0, 0, -1], - ].flat(); - - const sides = 10; - for (let i = 0; i < sides; ++i) { - const b = (i * Math.PI * 2) / sides; - verticesGeo.push(-Math.cos(b), -Math.sin(b), 0.105 * (i % 2 ? 1 : -1)); - } - - const facesGeo = [ - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 6], - [0, 6, 7], - [0, 7, 8], - [0, 8, 9], - [0, 9, 10], - [0, 10, 11], - [0, 11, 2], - [1, 3, 2], - [1, 4, 3], - [1, 5, 4], - [1, 6, 5], - [1, 7, 6], - [1, 8, 7], - [1, 9, 8], - [1, 10, 9], - [1, 11, 10], - [1, 2, 11], - ].flat(); - const decaGeometry2 = new THREE.PolyhedronGeometry( - verticesGeo, - facesGeo, - 1, - 0 - ); - let geo; - if (tempShowNumbers) { - let vertStep = THREE.MathUtils.degToRad(36); - let vertices = [ - [0, 0, 1], - [Math.cos(vertStep * 0), Math.sin(vertStep * 0), 0.105], - [Math.cos(vertStep * 1), Math.sin(vertStep * 1), -0.105], - [Math.cos(vertStep * 2), Math.sin(vertStep * 2), 0.105], - ].map((p) => { - return new THREE.Vector3(...p); - }); - let h = vertices[0].distanceTo(vertices[2]); - let w = vertices[1].distanceTo(vertices[3]); - let u = (w / h) * 0.5; - let v01 = new THREE.Vector3().subVectors(vertices[1], vertices[0]); - let v02 = new THREE.Vector3().subVectors(vertices[2], vertices[0]); - let dot = v02.clone().normalize().dot(v01); - let v = 1 - dot / h; - - let gSide = new THREE.BufferGeometry() - .setFromPoints(vertices) - .rotateZ(-vertStep); - gSide.setIndex([0, 1, 2, 0, 2, 3]); - gSide.setAttribute( - "uv", - new THREE.Float32BufferAttribute( - [0.5, 1, 0.5 - u, v, 0.5, 0, 0.5 + u, v], - 2 - ) - ); - gSide.computeVertexNormals(); - gSide = gSide.toNonIndexed(); - - // all sides - let gs = []; - - for (let i = 0; i < 5; i++) { - let a = vertStep * 2 * i; - let g1 = gSide.clone().rotateZ(-a); - recomputeUVs(g1, i * 2 + 0); - let g2 = gSide - .clone() - .rotateX(Math.PI) - .rotateZ(vertStep + a); - recomputeUVs(g2, i * 2 + 1); - gs.push(g1, g2); - } - let g = BufferGeometryUtils.mergeBufferGeometries(gs); - geo = g; - - let m = new THREE.MeshLambertMaterial({ - map: getNumbers(tempFillColor, tempTextColor), - }); - - decahedron = new THREE.Mesh(g, m); - } else if (tempTransparent) { - const decahedronTransaprentGeometry = decaGeometry2; - const wireframe = new THREE.WireframeGeometry( - decahedronTransaprentGeometry - ); - const lineMaterial = new THREE.LineBasicMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - depthTest: true, - opacity: 1, - transparent: false, - }); - const line = new THREE.LineSegments(wireframe, lineMaterial); - decahedron = line; - } else if (false) { - const decaGeo = decaGeometry; - - const texture = new THREE.TextureLoader().load( - sharedImageData != null ? sharedImageData : imageData - ); - - // Create material using the texture - const material = new THREE.MeshPhongMaterial({ map: texture }); - - // Create cube mesh with the material - decahedron = new THREE.Mesh(decaGeo, material); - } else { - const decahedronGeometry = decaGeometry2; - - const decaMaterial = new THREE.MeshStandardMaterial({ - color: sharedColor != null ? sharedColor : ctx.presentColor, - wireframe: false, - }); - - decahedron = new THREE.Mesh(decahedronGeometry, decaMaterial); - } - - // decahedron.rotation.set(Math.PI / 4, Math.PI / 4, 0); // Rotates 90 degrees on X, 45 degrees on Y - decahedron.castShadow = true; - scene.add(decahedron); - - const t = (1 + Math.sqrt(5)) / 2; - const r = 1 / t; - const scaleFactor = 1; // Change this value to scale the shape (e.g., 2 for doubling the size) - - let decahedronShape - if (geo != undefined) { - let positionAttribute = geo.getAttribute("position"); - let vertices = []; - for (let i = 0; i < positionAttribute.count; i++) { - let vertex = new THREE.Vector3().fromBufferAttribute( - positionAttribute, - i - ); - vertices.push([vertex.x, vertex.y, vertex.z]); - } - - // Extract faces - let faces = []; - for (let i = 0; i < vertices.length; i += 3) { - faces.push([i, i + 1, i + 2]); - } - - let cannonVertices = vertices.map((v) => new CANNON.Vec3(v[0], v[1], v[2])); - - // Create a ConvexPolyhedron shape from the scaled vertices and faces - decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: cannonVertices, - faces: faces, - }); - - - } else { - const verticesCannon = []; - for (let i = 0; i < verticesGeo.length; i += 3) { - verticesCannon.push( - new CANNON.Vec3( - verticesGeo[i] * scaleFactor, - verticesGeo[i + 1] * scaleFactor, - verticesGeo[i + 2] * scaleFactor - ) - ); - } - const facesCannon = []; - for (let i = 0; i < facesGeo.length; i += 3) { - facesCannon.push([facesGeo[i], facesGeo[i + 1], facesGeo[i + 2]]); - } - decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesCannon, - faces: facesCannon, - }); - } - - // let myShape = getPolyhedronShape(decahedron); - // console.log(myShape); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; - let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; - - let decahedronBody = new CANNON.Body({ - mass: 2, // Set mass - shape: decahedronShape, - position: new CANNON.Vec3(x, y, z), - friction: -1, - restitution: 5, - }); - // decahedronBody = polyhedronShape - // if (tempShowNumbers) { - // decahedronBody.addEventListener("sleep", () => { - // sleepCounter++; - // getDecaScore(decahedron); - // }); - // } - world.addBody(decahedronBody); - - let angVel1 = - sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; - let angVel2 = - sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; - let angVel3 = - sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; - - decahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); - decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); - decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh - decahedron.quaternion.copy(decahedronBody.quaternion); - console.log(decahedronBody); - console.log(decahedronBody.rotation); - - if (quaternionShared != null && quaternionShared != undefined) { - decahedron.quaternion.copy(quaternionShared); - decahedronBody.quaternion.copy(quaternionShared); - } - - diceArray.push([ - decahedron, - decahedronBody, - "deca", - tempShowNumbers, - tempTransparent, - tempFillColor, - tempTextColor, - angVel1, - angVel2, - angVel3, - ]); -} - -function recomputeUVs(g, idx) { - let tiles = { - x: 4, - y: 4, - }; - let x = idx % tiles.x; - let y = Math.floor(idx / tiles.x); - - let uvs = g.attributes.uv; - for (let i = 0; i < uvs.count; i++) { - let u = (uvs.getX(i) + x) / tiles.x; - let v = (uvs.getY(i) + y) / tiles.y; - uvs.setXY(i, u, v); - } -} - -function getNumbers(tempFillColor, tempTextColor) { - let tileSize = 256; - let tiles = { - x: 4, - y: 4, - }; - - let c = document.createElement("canvas"); - let ctx = c.getContext("2d"); - c.width = tileSize * 4; - c.height = tileSize * 4; - let u = (val) => tileSize * 0.01 * val; - - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - ctx.font = `bold ${u(40)}px Arial`; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillStyle = tempTextColor; - - for (let i = 0; i < sides; i++) { - let y = Math.floor(i / tiles.x); - let x = i % tiles.x; - let text = i + 1; - - ctx.save(); - ctx.translate(x * tileSize, c.height - y * tileSize); - - ctx.fillText(text, u(50), -u(40)); - if (text == 6 || text == 9) { - ctx.fillText("_", u(50), -u(40)); - } - ctx.restore(); - } - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = "srgb"; - // tex.anisotropy = renderer.capabilities.getMaxAnisotropy(); - tex.needsUpdate = true; - - return tex; -} -function getPolyhedronShape(mesh) { - let geometry = new THREE.BufferGeometry(); - geometry.setAttribute("position", mesh.geometry.getAttribute("position")); - - geometry = BufferGeometryUtils.mergeVertices(geometry); - - let position = geometry.attributes.position.array; - let index = geometry.getIndex(); - - const points = []; - for (let i = 0; i < position.length; i += 3) { - points.push( - new CANNON.Vec3(position[i], position[i + 1], position[i + 2]) - ); - } - const myfaces = []; - for (let i = 0; i < index.length; i += 3) { - myfaces.push([index[i], index[i + 1], index[i + 2]]); - } - - let myShape = new CANNON.ConvexPolyhedron({ - vertices: points, - faces: myfaces, - }); - return myShape; -} diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index 763237347..eed1837ae 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -55,7 +55,7 @@ define([], function () { { element: "#color-button-text", title: "Change Volume Text Color", - intro: "Choose any one of the colors for your the text on your volumes. The default is your buddy stroke color.", + intro: "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", }, { element: "#bg-button", @@ -80,12 +80,27 @@ define([], function () { }, ]; + // Filter out hidden buttons based on screen width + var hiddenButtonSelectors = []; + var screenWidth = window.innerWidth; + + if (screenWidth <= 1120) { + hiddenButtonSelectors.push("#color-button-text", "#bg-button", "#zoom-button"); + } + if (screenWidth <= 800) { + hiddenButtonSelectors.push("#volume-button", "#color-button-fill", "#remove-first"); + } + if (screenWidth <= 690) { + hiddenButtonSelectors.push("#sensor-button", "#throw-button"); + } + steps = steps.filter(function (obj) { return ( !("element" in obj) || (obj.element.length && document.querySelector(obj.element) && - document.querySelector(obj.element).style.display != "none") + document.querySelector(obj.element).style.display != "none" && + !hiddenButtonSelectors.includes(obj.element)) ); }); @@ -101,8 +116,8 @@ define([], function () { }) .start() .onexit(function() { - ifAdding.adding = true; - }); + ifAdding.adding = true; + }); }; return tutorial; From 7a63a2511fb4166c1c1dd1c7275474bdf93539a4 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 28 Jul 2024 23:21:04 +0530 Subject: [PATCH 31/60] changes --- activities/3DVolume.activity/index.html | 2 +- activities/3DVolume.activity/js/Cube.js | 15 +- activities/3DVolume.activity/js/Deca.js | 89 +--- activities/3DVolume.activity/js/Dodeca.js | 539 +++++++++++++------- activities/3DVolume.activity/js/Icosa.js | 15 +- activities/3DVolume.activity/js/Octa.js | 10 +- activities/3DVolume.activity/js/Tetra.js | 13 +- activities/3DVolume.activity/js/activity.js | 140 +++-- 8 files changed, 520 insertions(+), 303 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index c2e9d4b28..d445ee45a 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -22,7 +22,7 @@ - + diff --git a/activities/3DVolume.activity/js/Cube.js b/activities/3DVolume.activity/js/Cube.js index bcb7cf95f..8bfdc6dc2 100644 --- a/activities/3DVolume.activity/js/Cube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -102,16 +102,17 @@ function createCube( boxMesh.castShadow = true; scene.add(boxMesh); - const boxPhysmat = new CANNON.Material(); + let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; + const boxBody = new CANNON.Body({ mass: 1, shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), position: new CANNON.Vec3(x, y, z), - material: boxPhysmat, + material: new CANNON.Material(), restitution: 5, }); @@ -125,17 +126,18 @@ function createCube( boxBody.angularVelocity.set(angVel1, angVel2, angVel3); boxBody.applyImpulse(ctx.offset, ctx.rollingForce); - const groundBoxContactMat = new CANNON.ContactMaterial( + const contactMat = new CANNON.ContactMaterial( groundPhysMat, - boxPhysmat, - { friction: 0 } + boxBody.material, + { friction: ctx.friction } ); - world.addContactMaterial(groundBoxContactMat); + world.addContactMaterial(contactMat); if (quaternionShared != null && quaternionShared != undefined) { boxMesh.quaternion.copy(quaternionShared); boxBody.quaternion.copy(quaternionShared); } + console.log(boxBody.material) diceArray.push([ boxMesh, boxBody, @@ -147,6 +149,7 @@ function createCube( angVel1, angVel2, angVel3, + contactMat ]); } diff --git a/activities/3DVolume.activity/js/Deca.js b/activities/3DVolume.activity/js/Deca.js index 5e1f848a7..15d34b58e 100644 --- a/activities/3DVolume.activity/js/Deca.js +++ b/activities/3DVolume.activity/js/Deca.js @@ -124,56 +124,9 @@ function createDecahedron( // decahedron = diceMesh; scene.add(decahedron); - // Create a ConvexPolyhedron shape from the scaled vertices and faces - // const decahedronShape = new CANNON.ConvexPolyhedron({ - // vertices: verticesCannon, - // faces: facesCannon, - // }); - // let myShape = getPolyhedronShape(decahedron); - // console.log(myShape); - let verticesI = []; - let facesI = [ - [5, 7, 11, 0], - [4, 2, 10, 1], - [1, 3, 11, 2], - [0, 8, 10, 3], - [7, 9, 11, 4], - [8, 6, 10, 5], - [9, 1, 11, 6], - [2, 0, 10, 7], - [3, 5, 11, 8], - [6, 4, 10, 9], - [1, 0, 2, -1], - [1, 2, 3, -1], - [3, 2, 4, -1], - [3, 4, 5, -1], - [5, 4, 6, -1], - [5, 6, 7, -1], - [7, 6, 8, -1], - [7, 8, 9, -1], - [9, 8, 0, -1], - [9, 0, 1, -1], - ]; // Create vertices for the decahedron - for (let i = 0, b = 0; i < 10; ++i, b += (Math.PI * 2) / 10) { - verticesI.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]); - } - verticesI.push([0, 0, -1]); // bottom vertex - verticesI.push([0, 0, 1]); // top vertex - - // Convert vertices to the format required by CANNON.js - let verticesCannonI = verticesI.map( - (v) => new CANNON.Vec3(v[0], v[1], v[2]) - ); - // Convert faces to the format required by CANNON.js, removing the last -1 entry - let facesCannonI = facesI.map((face) => face.slice(0, -1)); - - const decahedronShape = new CANNON.ConvexPolyhedron({ - vertices: verticesCannonI, - faces: facesCannonI, - }); let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; @@ -233,8 +186,17 @@ function createDecahedron( mass: 3, // Set mass shape: decahedronShape2, position: new CANNON.Vec3(x, y, z), + material: new CANNON.Material(), restitution: 0.5, }); + const contactMat = new CANNON.ContactMaterial( + groundPhysMat, + decahedronBody.material, + { friction: ctx.friction } + ); + + world.addContactMaterial(contactMat); + world.addBody(decahedronBody); decahedronBody.sleepSpeedLimit = 4; decahedronBody.sleepTimeLimit = 10; @@ -265,6 +227,7 @@ function createDecahedron( tempTextColor, angVel1, angVel2, + contactMat ]); } @@ -484,38 +447,6 @@ function calculateTextureSize(approx) { } function getDecaScore(scoresObject, body, ifRemove) { - // Decahedron face vectors - // const faceVectors = [ - // { vector: new THREE.Vector3(0, 1, 0.5), face: 7 }, - // { vector: new THREE.Vector3(0, 1, -0.5), face: 2 }, - // { vector: new THREE.Vector3(0, -1, 0.5), face: 3 }, - // { vector: new THREE.Vector3(0, -1, -0.5), face: 10 }, - // { vector: new THREE.Vector3(0.5, 0, 1), face: 5 }, - // { vector: new THREE.Vector3(-0.5, 0, 1), face: 8 }, - // { vector: new THREE.Vector3(0.5, 0, -1), face: 9 }, - // { vector: new THREE.Vector3(-0.5, 0, -1), face: 6 }, - // { vector: new THREE.Vector3(1, 0.5, 0), face: 1 }, - // { vector: new THREE.Vector3(-1, 0.5, 0), face: 4 }, - // ]; - - // for (const faceVector of faceVectors) { - // faceVector.vector.normalize().applyEuler(body.rotation); - - // if (Math.round(faceVector.vector.y) === 1) { - // if (!ifRemove) { - // scoresObject.lastRoll += faceVector.face + " + "; - // scoresObject.presentScore += faceVector.face; - // updateElements(); - // break; - // } - // for (let i = 0; i < diceArray.length; i++) { - // if (body == diceArray[i][0]) { - // diceArray[i][7] = faceVector.face; - // } - // } - // return faceVector.face; - // } - // } let vector = new THREE.Vector3(0, 1); let closest_face; diff --git a/activities/3DVolume.activity/js/Dodeca.js b/activities/3DVolume.activity/js/Dodeca.js index c4dc217a4..668bf2318 100644 --- a/activities/3DVolume.activity/js/Dodeca.js +++ b/activities/3DVolume.activity/js/Dodeca.js @@ -1,3 +1,74 @@ +let p = (1 + Math.sqrt(5)) / 2; +let q = 1 / p; +let vertices2 = [ + [0, q, p], + [0, q, -p], + [0, -q, p], + [0, -q, -p], + [p, 0, q], + [p, 0, -q], + [-p, 0, q], + [-p, 0, -q], + [q, p, 0], + [q, -p, 0], + [-q, p, 0], + [-q, -p, 0], + [1, 1, 1], + [1, 1, -1], + [1, -1, 1], + [1, -1, -1], + [-1, 1, 1], + [-1, 1, -1], + [-1, -1, 1], + [-1, -1, -1], +]; +let faces2 = [ + [2, 14, 4, 12, 0, 1], + [15, 9, 11, 19, 3, 2], + [16, 10, 17, 7, 6, 3], + [6, 7, 19, 11, 18, 4], + [6, 18, 2, 0, 16, 5], + [18, 11, 9, 14, 2, 6], + [1, 17, 10, 8, 13, 7], + [1, 13, 5, 15, 3, 8], + [13, 8, 12, 4, 5, 9], + [5, 4, 14, 9, 15, 10], + [0, 12, 8, 10, 16, 11], + [3, 19, 7, 17, 1, 12], +]; + +let scaleFactor2 = 1.5; +let values2 = 12; +let faceTexts2 = [ + " ", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", +]; +let textMargin2 = 1.0; +let chamfer2 = 0.968; +let af2 = -Math.PI / 4 / 2; +let tab2 = 0.2; +let backColor2; +let color2; function createDodecahedron( sharedColor, ifNumbers, @@ -26,99 +97,14 @@ function createDodecahedron( let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; let tempTextColor = sharedTextColor != null ? sharedTextColor : ctx.textColor; + backColor2 = tempFillColor; + color2 = tempTextColor; if (tempShowNumbers) { - let tileDimension = new THREE.Vector2(4, 3); // 12 faces, arranged in a 4x3 grid - let tileSize = 512; - let g = new THREE.DodecahedronGeometry(1.25); - - let c = document.createElement("canvas"); - c.width = tileSize * tileDimension.x; - c.height = tileSize * tileDimension.y; - let ctx = c.getContext("2d"); - ctx.fillStyle = tempFillColor; - ctx.fillRect(0, 0, c.width, c.height); - - let uvs = []; - const base = new THREE.Vector2(0, 0.5); - const center = new THREE.Vector2(); - const angle = THREE.MathUtils.degToRad(72); - let baseUVs = [ - base - .clone() - .rotateAround(center, angle * 1) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 2) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 3) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 4) - .addScalar(0.5), - base - .clone() - .rotateAround(center, angle * 0) - .addScalar(0.5), - ]; - - for (let i = 0; i < 12; i++) { - // 12 faces for a dodecahedron - let u = i % tileDimension.x; - let v = Math.floor(i / tileDimension.x); - uvs.push( - (baseUVs[1].x + u) / tileDimension.x, - (baseUVs[1].y + v) / tileDimension.y, - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[2].x + u) / tileDimension.x, - (baseUVs[2].y + v) / tileDimension.y, - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y, - - (baseUVs[3].x + u) / tileDimension.x, - (baseUVs[3].y + v) / tileDimension.y, - (baseUVs[4].x + u) / tileDimension.x, - (baseUVs[4].y + v) / tileDimension.y, - (baseUVs[0].x + u) / tileDimension.x, - (baseUVs[0].y + v) / tileDimension.y - ); - - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.font = `bold ${tileSize / 3}px Arial`; - ctx.fillStyle = tempTextColor; - ctx.fillText( - i + 1 + (i == 5 ? "." : ""), - (u + 0.5) * tileSize, - c.height - (v + 0.5) * tileSize - ); - } - - g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); - - let tex = new THREE.CanvasTexture(c); - tex.colorSpace = THREE.SRGBColorSpace; - tex.anisotropy = 16; - - let m = new THREE.MeshPhongMaterial({ - map: tex, - }); - - dodecahedron = new THREE.Mesh(g, m); + dodecahedron = new THREE.Mesh(getGeometry2(), getMaterials2()); } else if (tempTransparent) { - const dodedodecahedronTransaprentGeometry = - new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron + const dodecahedronTransaprentGeometry = getGeometry2(); const wireframe = new THREE.WireframeGeometry( - dodedodecahedronTransaprentGeometry + dodecahedronTransaprentGeometry ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : ctx.presentColor, @@ -129,7 +115,7 @@ function createDodecahedron( const line = new THREE.LineSegments(wireframe, lineMaterial); dodecahedron = line; } else { - const dodecahedronGeometry = new THREE.DodecahedronGeometry(1.25); // Size of the tetrahedron + const dodecahedronGeometry = getGeometry2(); const dodecaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : ctx.presentColor, @@ -145,29 +131,29 @@ function createDodecahedron( const t = 1.618; const r = 0.618; - const scaleFactor = 0.75; - - const vertices = [ - new CANNON.Vec3(-1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(-1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(1, -1, -1).scale(scaleFactor), - new CANNON.Vec3(1, -1, 1).scale(scaleFactor), - new CANNON.Vec3(1, 1, -1).scale(scaleFactor), - new CANNON.Vec3(1, 1, 1).scale(scaleFactor), - new CANNON.Vec3(0, -r, -t).scale(scaleFactor), - new CANNON.Vec3(0, -r, t).scale(scaleFactor), - new CANNON.Vec3(0, r, -t).scale(scaleFactor), - new CANNON.Vec3(0, r, t).scale(scaleFactor), - new CANNON.Vec3(-r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(-r, t, 0).scale(scaleFactor), - new CANNON.Vec3(r, -t, 0).scale(scaleFactor), - new CANNON.Vec3(r, t, 0).scale(scaleFactor), - new CANNON.Vec3(-t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(t, 0, -r).scale(scaleFactor), - new CANNON.Vec3(-t, 0, r).scale(scaleFactor), - new CANNON.Vec3(t, 0, r).scale(scaleFactor), + let scaleFactorCannon = 0.83; + + const vertices3 = [ + new CANNON.Vec3(-1, -1, -1).scale(scaleFactorCannon), + new CANNON.Vec3(-1, -1, 1).scale(scaleFactorCannon), + new CANNON.Vec3(-1, 1, -1).scale(scaleFactorCannon), + new CANNON.Vec3(-1, 1, 1).scale(scaleFactorCannon), + new CANNON.Vec3(1, -1, -1).scale(scaleFactorCannon), + new CANNON.Vec3(1, -1, 1).scale(scaleFactorCannon), + new CANNON.Vec3(1, 1, -1).scale(scaleFactorCannon), + new CANNON.Vec3(1, 1, 1).scale(scaleFactorCannon), + new CANNON.Vec3(0, -r, -t).scale(scaleFactorCannon), + new CANNON.Vec3(0, -r, t).scale(scaleFactorCannon), + new CANNON.Vec3(0, r, -t).scale(scaleFactorCannon), + new CANNON.Vec3(0, r, t).scale(scaleFactorCannon), + new CANNON.Vec3(-r, -t, 0).scale(scaleFactorCannon), + new CANNON.Vec3(-r, t, 0).scale(scaleFactorCannon), + new CANNON.Vec3(r, -t, 0).scale(scaleFactorCannon), + new CANNON.Vec3(r, t, 0).scale(scaleFactorCannon), + new CANNON.Vec3(-t, 0, -r).scale(scaleFactorCannon), + new CANNON.Vec3(t, 0, -r).scale(scaleFactorCannon), + new CANNON.Vec3(-t, 0, r).scale(scaleFactorCannon), + new CANNON.Vec3(t, 0, r).scale(scaleFactorCannon), ]; const indices = [ @@ -209,28 +195,36 @@ function createDodecahedron( [1, 5, 9], ]; - // Create a ConvexPolyhedron shape from the vertices and faces + // Create a ConvexPolyhedron shape from the vertices2 and faces2 const dodecahedronShape = new CANNON.ConvexPolyhedron({ - vertices: vertices, + vertices: vertices3, faces: indices, }); - console.log(dodecahedronShape); let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let y = yCoordinateShared == null ? 4 : yCoordinateShared; + + let newGeometry = getGeometry2(); const dodecahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: dodecahedronShape, position: new CANNON.Vec3(x, y, z), - friction: -1, + material: new CANNON.Material(), restitution: 5, }); dodecahedronBody.sleepSpeedLimit = 0.2; dodecahedronBody.sleepTimeLimit = 3; world.addBody(dodecahedronBody); + const contactMat = new CANNON.ContactMaterial( + groundPhysMat, + dodecahedronBody.material, + { friction: ctx.friction } + ); + + world.addContactMaterial(contactMat); let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; @@ -258,72 +252,261 @@ function createDodecahedron( angVel1, angVel2, angVel3, + contactMat ]); } +function getGeometry2() { + let radius = 1 * scaleFactor2; -function getDodecaScore(scoresObject, body, ifRemove) { - // Define the golden ratio - const phi = (1 + Math.sqrt(5)) / 2; - - // Decahedron face vectors - const faceVectors = [ - { vector: new THREE.Vector3(1, 1, 1), face: 1 }, - { vector: new THREE.Vector3(1, 1, -1), face: 6 }, - { vector: new THREE.Vector3(1, -1, 1), face: 11 }, - { vector: new THREE.Vector3(1, -1, -1), face: 9 }, - { vector: new THREE.Vector3(-1, 1, 1), face: 7 }, - { vector: new THREE.Vector3(-1, 1, -1), face: 2 }, - { vector: new THREE.Vector3(-1, -1, 1), face: 5 }, - { vector: new THREE.Vector3(-1, -1, -1), face: 8 }, - { vector: new THREE.Vector3(0, phi, 1 / phi), face: 4 }, - { vector: new THREE.Vector3(0, phi, -1 / phi), face: 10 }, - { vector: new THREE.Vector3(0, -phi, 1 / phi), face: 3 }, - { vector: new THREE.Vector3(0, -phi, -1 / phi), face: 12 }, - ]; + let vectors = new Array(vertices2.length); + for (let i = 0; i < vertices2.length; ++i) { + vectors[i] = new THREE.Vector3().fromArray(vertices2[i]).normalize(); + } + + let chamferGeometry = getChamferGeometry2(vectors); + let geometry = makeGeometry2( + chamferGeometry.vectors, + chamferGeometry.faces2, + radius, + tab2, + af2 + ); + geometry.cannon_shape = createShape(vectors, faces2, radius); - for (const faceVector of faceVectors) { - faceVector.vector.normalize().applyEuler(body.rotation); + return geometry; +} - if (Math.round(faceVector.vector.y) === 1) { - if (!ifRemove) { - scoresObject.lastRoll += faceVector.face + " + "; - scoresObject.presentScore += faceVector.face; - break; +function getChamferGeometry2(vectors) { + let chamfer_vectors = [], + chamfer_faces = [], + corner_faces = new Array(vectors.length); + for (let i = 0; i < vectors.length; ++i) corner_faces[i] = []; + for (let i = 0; i < faces2.length; ++i) { + let ii = faces2[i], + fl = ii.length - 1; + let center_point = new THREE.Vector3(); + let face = new Array(fl); + for (let j = 0; j < fl; ++j) { + let vv = vectors[ii[j]].clone(); + center_point.add(vv); + corner_faces[ii[j]].push((face[j] = chamfer_vectors.push(vv) - 1)); + } + center_point.divideScalar(fl); + for (let j = 0; j < fl; ++j) { + let vv = chamfer_vectors[face[j]]; + vv.subVectors(vv, center_point) + .multiplyScalar(chamfer2) + .addVectors(vv, center_point); + } + face.push(ii[fl]); + chamfer_faces.push(face); + } + for (let i = 0; i < faces2.length - 1; ++i) { + for (let j = i + 1; j < faces2.length; ++j) { + let pairs = [], + lastm = -1; + for (let m = 0; m < faces2[i].length - 1; ++m) { + let n = faces2[j].indexOf(faces2[i][m]); + if (n >= 0 && n < faces2[j].length - 1) { + if (lastm >= 0 && m !== lastm + 1) + pairs.unshift([i, m], [j, n]); + else pairs.push([i, m], [j, n]); + lastm = m; + } } - return faceVector.face; + if (pairs.length !== 4) continue; + chamfer_faces.push([ + chamfer_faces[pairs[0][0]][pairs[0][1]], + chamfer_faces[pairs[1][0]][pairs[1][1]], + chamfer_faces[pairs[3][0]][pairs[3][1]], + chamfer_faces[pairs[2][0]][pairs[2][1]], + -1, + ]); } } + for (let i = 0; i < corner_faces.length; ++i) { + let cf = corner_faces[i], + face = [cf[0]], + count = cf.length - 1; + while (count) { + for (let m = faces2.length; m < chamfer_faces.length; ++m) { + let index = chamfer_faces[m].indexOf(face[face.length - 1]); + if (index >= 0 && index < 4) { + if (--index === -1) index = 3; + let next_vertex = chamfer_faces[m][index]; + if (cf.indexOf(next_vertex) >= 0) { + face.push(next_vertex); + break; + } + } + } + --count; + } + face.push(-1); + chamfer_faces.push(face); + } + return { vectors: chamfer_vectors, faces2: chamfer_faces }; +} + +function makeGeometry2(vertices2, faces2, radius) { + let geom = new THREE.BufferGeometry(); + + for (let i = 0; i < vertices2.length; ++i) { + vertices2[i] = vertices2[i].multiplyScalar(radius); + } + + let positions = []; + const normals = []; + const uvs = []; + + const cb = new THREE.Vector3(); + const ab = new THREE.Vector3(); + let materialIndex; + let faceFirstVertexIndex = 0; + + for (let i = 0; i < faces2.length; ++i) { + let ii = faces2[i], + fl = ii.length - 1; + let aa = (Math.PI * 2) / fl; + materialIndex = ii[fl] + 1; + for (let j = 0; j < fl - 2; ++j) { + //Vertices + positions.push(...vertices2[ii[0]].toArray()); + positions.push(...vertices2[ii[j + 1]].toArray()); + positions.push(...vertices2[ii[j + 2]].toArray()); - // let vector = new THREE.Vector3(0, -1); - // let closest_face; - // let closest_angle = Math.PI * 2; - - // let normals = body.geometry.getAttribute("normal").array; - // console.log(normals) - // for (let i = 0; i < 12; ++i) { - // let face = i + 1; - - // //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 - // let startVertex = i * 9; - // let normal = new THREE.Vector3( - // normals[startVertex], - // normals[startVertex + 1], - // normals[startVertex + 2] - // ); - // let angle = normal - // .clone() - // .applyQuaternion(body.quaternion) - // .angleTo(vector); - // if (angle < closest_angle) { - // closest_angle = angle; - // closest_face = face; - // } - // } - - // // switch(closest_face) { - // // } - // scoresObject.lastRoll += closest_face + " + "; - // scoresObject.presentScore += closest_face; - // updateElements(); - // return closest_face; + // Flat face normals + cb.subVectors(vertices2[ii[j + 2]], vertices2[ii[j + 1]]); + ab.subVectors(vertices2[ii[0]], vertices2[ii[j + 1]]); + cb.cross(ab); + cb.normalize(); + + // Vertex Normals + normals.push(...cb.toArray()); + normals.push(...cb.toArray()); + normals.push(...cb.toArray()); + + //UVs + uvs.push( + (Math.cos(af2) + 1 + tab2) / 2 / (1 + tab2), + (Math.sin(af2) + 1 + tab2) / 2 / (1 + tab2) + ); + uvs.push( + (Math.cos(aa * (j + 1) + af2) + 1 + tab2) / 2 / (1 + tab2), + (Math.sin(aa * (j + 1) + af2) + 1 + tab2) / 2 / (1 + tab2) + ); + uvs.push( + (Math.cos(aa * (j + 2) + af2) + 1 + tab2) / 2 / (1 + tab2), + (Math.sin(aa * (j + 2) + af2) + 1 + tab2) / 2 / (1 + tab2) + ); + } + + //Set Group for face materials. + let numOfVertices = (fl - 2) * 3; + for (let i = 0; i < numOfVertices / 3; i++) { + geom.addGroup(faceFirstVertexIndex, 3, materialIndex); + faceFirstVertexIndex += 3; + } + } + + geom.setAttribute( + "position", + new THREE.Float32BufferAttribute(positions, 3) + ); + geom.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3)); + geom.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); + geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius); + return geom; +} + +function createShape(vertices2, faces2, radius) { + let cv = new Array(vertices2.length), + cf = new Array(faces2.length); + for (let i = 0; i < vertices2.length; ++i) { + let v = vertices2[i]; + cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius); + } + for (let i = 0; i < faces2.length; ++i) { + cf[i] = faces2[i].slice(0, faces2[i].length - 1); + } + console.log(faces2); + return new CANNON.ConvexPolyhedron(cv, cf); +} + +function getMaterials2() { + let materials = []; + for (let i = 0; i < faceTexts2.length; ++i) { + let texture = null; + texture = createTextTexture2(faceTexts2[i]); + + materials.push( + new THREE.MeshPhongMaterial(Object.assign({}, { map: texture })) + ); + } + return materials; } + +function createTextTexture2(text) { + let canvas = document.createElement("canvas"); + let context = canvas.getContext("2d"); + let ts = calculateTextureSize(1 / 2 + 1 * textMargin2) * 2; + canvas.width = canvas.height = ts; + context.font = ts / (1 + 2 * textMargin2) + "pt Arial"; + context.fillStyle = backColor2; + context.fillRect(0, 0, canvas.width, canvas.height); + context.textAlign = "center"; + context.textBaseline = "middle"; + context.fillStyle = color2; + context.fillText(text, canvas.width / 2, canvas.height / 2); + let texture = new THREE.Texture(canvas); + texture.needsUpdate = true; + return texture; +} + +function calculateTextureSize(approx) { + return Math.max( + 128, + Math.pow(2, Math.floor(Math.log(approx) / Math.log(2))) + ); +} +function getDodecaScore(scoresObject, body, ifRemove) { + let vector = new THREE.Vector3(0, 1); + let closest_face; + let closest_angle = Math.PI * 2; + + let normals = body.geometry.getAttribute("normal").array; + for (let i = 0; i < body.geometry.groups.length; ++i) { + let face = body.geometry.groups[i]; + if (face.materialIndex === 0) continue; + + //Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9 + let startVertex = i * 9; + let normal = new THREE.Vector3( + normals[startVertex], + normals[startVertex + 1], + normals[startVertex + 2] + ); + let angle = normal + .clone() + .applyQuaternion(body.quaternion) + .angleTo(vector); + if (angle < closest_angle) { + closest_angle = angle; + closest_face = face; + } + } + let scoreToShow; + if (closest_face.materialIndex - 1 == 0) { + scoreToShow = 10; + } else { + scoreToShow = closest_face.materialIndex - 1; + } + if (!ifRemove) { + scoresObject.lastRoll += scoreToShow + " + "; + scoresObject.presentScore += scoreToShow; + } else { + return scoreToShow; + } + +} + diff --git a/activities/3DVolume.activity/js/Icosa.js b/activities/3DVolume.activity/js/Icosa.js index 12477fd90..4fbcc139e 100644 --- a/activities/3DVolume.activity/js/Icosa.js +++ b/activities/3DVolume.activity/js/Icosa.js @@ -164,18 +164,26 @@ function createIcosahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let y = yCoordinateShared == null ? 4 : yCoordinateShared; const icosahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: icosahedronShape, position: new CANNON.Vec3(x, y, z), - friction: -1, + material: new CANNON.Material(), restitution: 5, }); icosahedronBody.sleepSpeedLimit = 0.5; icosahedronBody.sleepTimeLimit = 3; + const contactMat = new CANNON.ContactMaterial( + groundPhysMat, + icosahedronBody.material, + { friction: ctx.friction } + ); + + world.addContactMaterial(contactMat); + // if (tempShowNumbers) { // icosahedronBody.addEventListener("sleep", () => { // console.log("icosa going to sleeep"); @@ -212,10 +220,10 @@ function createIcosahedron( angVel1, angVel2, angVel3, + contactMat, ]); } - function getIcosaScore(scoresObject, body, ifRemove) { let vector = new THREE.Vector3(0, 1); let closest_face; @@ -245,6 +253,5 @@ function getIcosaScore(scoresObject, body, ifRemove) { scoresObject.presentScore += closest_face; } - return closest_face; } diff --git a/activities/3DVolume.activity/js/Octa.js b/activities/3DVolume.activity/js/Octa.js index b95108f43..d4477948b 100644 --- a/activities/3DVolume.activity/js/Octa.js +++ b/activities/3DVolume.activity/js/Octa.js @@ -142,10 +142,17 @@ function createOctahedron( mass: 2, // Set mass shape: octahedronShape, position: new CANNON.Vec3(x, y, z), - friction: -1, + material: new CANNON.Material(), restitution: 5, }); + const contactMat = new CANNON.ContactMaterial( + groundPhysMat, + octahedronBody.material, + { friction: ctx.friction } + ); + + world.addContactMaterial(contactMat); world.addBody(octahedronBody); let angVel1 = @@ -174,6 +181,7 @@ function createOctahedron( angVel1, angVel2, angVel3, + contactMat ]); } diff --git a/activities/3DVolume.activity/js/Tetra.js b/activities/3DVolume.activity/js/Tetra.js index 98463d4c9..7b364150d 100644 --- a/activities/3DVolume.activity/js/Tetra.js +++ b/activities/3DVolume.activity/js/Tetra.js @@ -152,13 +152,13 @@ function createTetrahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let y = yCoordinateShared == null ? 4 : yCoordinateShared; const tetrahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: tetrahedronShape, position: new CANNON.Vec3(x, y, z), - friction: -1, + material: new CANNON.Material(), restitution: 5, }); world.addBody(tetrahedronBody); @@ -169,6 +169,14 @@ function createTetrahedron( let angVel3 = sharedAngVel3 == null ? Math.random() * (3 - 0.1) + 0.1 : sharedAngVel3; + const contactMat = new CANNON.ContactMaterial( + groundPhysMat, + tetrahedronBody.material, + { friction: ctx.friction } + ); + + world.addContactMaterial(contactMat); + tetrahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); tetrahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh @@ -188,6 +196,7 @@ function createTetrahedron( angVel1, angVel2, angVel3, + contactMat ]); } diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index cb00e9aac..9b7dcafa7 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -84,12 +84,25 @@ define([ toggleTransparent: false, offset: new CANNON.Vec3(0, 0.1, 0), rollingForce: randomDirection.scale(2), + cubeMaterial: new CANNON.Material(), + tetraMaterial: new CANNON.Material(), + octaMaterial: new CANNON.Material(), + decaMaterial: new CANNON.Material(), + dodecaMaterial: new CANNON.Material(), + icosaMaterial: new CANNON.Material(), + cubeContact: null, + tetraContact: null, + octaContact: null, + decaContact: null, + dodecaContact: null, + icosaContact: null, + friction: 1, }; let presentBackground = null; let scoresObject = { - lastRoll : "", - presentScore : 0 - } + lastRoll: "", + presentScore: 0, + }; let diceArray = []; let journalDiceArray = []; let showImage = false; @@ -663,7 +676,10 @@ define([ // Adds all the numbers on top for the numbered volumes. function updateElements() { lastRollElement.textContent = - scoresObject.lastRoll.substring(0, scoresObject.lastRoll.length - 2) + + scoresObject.lastRoll.substring( + 0, + scoresObject.lastRoll.length - 2 + ) + "= " + scoresObject.presentScore; } @@ -839,29 +855,54 @@ define([ let score; switch (diceArray[i][2]) { case "cube": - score = getCubeScore(scoresObject, diceArray[i][0], true); + score = getCubeScore( + scoresObject, + diceArray[i][0], + true + ); break; case "icosa": - score = getIcosaScore(scoresObject, diceArray[i][0], true); + score = getIcosaScore( + scoresObject, + diceArray[i][0], + true + ); break; case "deca": - score = getDecaScore(scoresObject, diceArray[i][0], true); + score = getDecaScore( + scoresObject, + diceArray[i][0], + true + ); break; case "dodeca": - score = getDodecaScore(scoresObject, diceArray[i][0], true); + score = getDodecaScore( + scoresObject, + diceArray[i][0], + true + ); break; case "octa": - score = getOctaScore(scoresObject, diceArray[i][0], true); + score = getOctaScore( + scoresObject, + diceArray[i][0], + true + ); break; case "tetra": - score = getTetraScore(scoresObject, diceArray[i][0], true); + score = getTetraScore( + scoresObject, + diceArray[i][0], + true + ); break; default: console.log(`Unknown type: ${diceArray[i][3]}`); continue; } - scoresObject.presentScore = scoresObject.presentScore - score; + scoresObject.presentScore = + scoresObject.presentScore - score; console.log(scoresObject.presentScore); let scoresArray = scoresObject.lastRoll.split(" + "); @@ -1017,22 +1058,28 @@ define([ ); } else { sensorButton.classList.remove("active"); - world.gravity.set(0, -9.81, 0); + world.gravity.set(-9.81, -9.81, -9.81); } }); + world.gravity.set(-3, -9.81, 0); // Gravity towards the right function accelerationChanged(acceleration) { if (!sensorMode) return; if (acceleration.y > 0) { + if (acceleration.y < -2.7) { + world.gravity.set(-3, -9.81, 0); // Gravity towards the right + wakeAll(); + } else { + world.gravity.set(-9.81, -9.81, 0); // Gravity towards the right + wakeAll(); + } // right - world.gravity.set(-9.81, 0, 0); // Gravity towards the right - wakeAll(); } else { if (acceleration.z > 0) { if (acceleration.y > -1 && acceleration.y < 1) { if (acceleration.z > 6) { // back - world.gravity.set(0, 0, 9.81); // Gravity towards the back + world.gravity.set(0, -9.81, 9.81); // Gravity towards the back wakeAll(); } else { // straight @@ -1040,17 +1087,32 @@ define([ wakeAll(); } } else { + if (acceleration.y < -2.8) { + world.gravity.set(3, -9.81, 0); // Gravity towards the left + wakeAll(); + } else { + world.gravity.set(9.81, -9.81, 0); // Gravity towards the left + wakeAll(); + } // left - world.gravity.set(9.81, 0, 0); // Gravity towards the left - wakeAll(); } } else { // front - world.gravity.set(0, 0, -9.81); // Gravity towards the front + if (acceleration.y > ) + world.gravity.set(0, -9.81, -9.81); // Gravity towards the front wakeAll(); } } - lastRollElement.textContent = "x- " + acceleration.x + ", " + "y- " + acceleration.y + ", " + "z- " + acceleration.x + " "; + lastRollElement.textContent = + "x- " + + acceleration.x + + ", " + + "y- " + + acceleration.y + + ", " + + "z- " + + acceleration.x + + " "; } // Wakes all the volumes so that they move towards the gravity. @@ -1311,13 +1373,6 @@ define([ // Functions to get the scores of the dice. - - - - - - - function changeBoardBackground(selectedBoard) { console.log(selectedBoard); presentBackground = selectedBoard; @@ -1333,6 +1388,7 @@ define([ groundBody.material.friction = 100; } ); + ctx.friction = 100; break; case "wood": textureLoader.load( @@ -1342,9 +1398,10 @@ define([ groundMesh.material.color.setHex(0xf0c592); groundMesh.material.map = groundTexture; groundMesh.material.needsUpdate = true; - groundBody.material.friction = 60; + // groundBody.material.friction = 60; } ); + ctx.friction = 0; break; case "default": groundMesh.material.needsUpdate = true; @@ -1352,8 +1409,19 @@ define([ groundMesh.material.wireframe = false; groundMesh.material.map = null; groundBody.material.friction = 1; + ctx.friction = 1; + break; } + for (let i = 0; i < diceArray.length; i++) { + world.removeContactMaterial(diceArray[i][10]); + let newContactMaterial = new CANNON.ContactMaterial( + groundPhysMat, + diceArray[i][1].material, + { friction: ctx.friction } + ); + world.addContactMaterial(newContactMaterial); + } } // This function calls the getScore functions for all the volumes and displays them. function getScores() { @@ -1368,25 +1436,34 @@ define([ score = getCubeScore(scoresObject, diceArray[i][0]); break; case "icosa": - score = getIcosaScore(scoresObject, diceArray[i][0]); + score = getIcosaScore( + scoresObject, + diceArray[i][0] + ); break; case "deca": score = getDecaScore(scoresObject, diceArray[i][0]); break; case "dodeca": - score = getDodecaScore(scoresObject, diceArray[i][0]); + score = getDodecaScore( + scoresObject, + diceArray[i][0] + ); break; case "octa": score = getOctaScore(scoresObject, diceArray[i][0]); break; case "tetra": - score = getTetraScore(scoresObject, diceArray[i][0]); + score = getTetraScore( + scoresObject, + diceArray[i][0] + ); break; default: console.log(`Unknown type: ${diceArray[i][3]}`); continue; } - updateElements() + updateElements(); } } } @@ -1401,7 +1478,6 @@ define([ let time = 20; let timeStep = 1 / time; - animate(); function animate(time) { From 4bf0deab400866d6b3e2c97c409f38a536fad7da Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 28 Jul 2024 23:29:52 +0530 Subject: [PATCH 32/60] changes --- activities/3DVolume.activity/js/activity.js | 1 - 1 file changed, 1 deletion(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 9b7dcafa7..c90c84c72 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1098,7 +1098,6 @@ define([ } } else { // front - if (acceleration.y > ) world.gravity.set(0, -9.81, -9.81); // Gravity towards the front wakeAll(); } From 6eaf0d819a2dc7e1a006b60101b786132d4c989c Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 29 Jul 2024 00:21:55 +0530 Subject: [PATCH 33/60] localization --- activities/3DVolume.activity/js/activity.js | 15 ++++- activities/3DVolume.activity/lib/axios.min.js | 3 + .../3DVolume.activity/lib/i18next.min.js | 1 + activities/3DVolume.activity/lib/l10n.js | 55 +++++++++++++++++++ activities/3DVolume.activity/lib/tutorial.js | 4 +- activities/3DVolume.activity/locales/en.json | 0 activities/3DVolume.activity/locales/fr.json | 3 + 7 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 activities/3DVolume.activity/lib/axios.min.js create mode 100644 activities/3DVolume.activity/lib/i18next.min.js create mode 100644 activities/3DVolume.activity/lib/l10n.js create mode 100644 activities/3DVolume.activity/locales/en.json create mode 100644 activities/3DVolume.activity/locales/fr.json diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index c90c84c72..ee1c04d4b 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -10,6 +10,7 @@ define([ "tutorial", "sugar-web/graphics/journalchooser", "sugar-web/datastore", + "l10n", ], function ( activity, env, @@ -21,7 +22,8 @@ define([ presencepalette, tutorial, journalchooser, - datastore + datastore, + l10n ) { // Function to change the background color based on the provided string requirejs(["domReady!"], function (doc) { @@ -121,6 +123,16 @@ define([ env.getEnvironment(function (err, environment) { currentenv = environment; + // Set current language to Sugarizer + var defaultLanguage = + typeof chrome != "undefined" && chrome.app && chrome.app.runtime + ? chrome.i18n.getUILanguage() + : navigator.language; + var language = environment.user + ? environment.user.language + : defaultLanguage; + l10n.init(language); + ctx.presentColor = currentenv.user.colorvalue.fill != null ? currentenv.user.colorvalue.fill @@ -1061,7 +1073,6 @@ define([ world.gravity.set(-9.81, -9.81, -9.81); } }); - world.gravity.set(-3, -9.81, 0); // Gravity towards the right function accelerationChanged(acceleration) { if (!sensorMode) return; diff --git a/activities/3DVolume.activity/lib/axios.min.js b/activities/3DVolume.activity/lib/axios.min.js new file mode 100644 index 000000000..fc6c8b618 --- /dev/null +++ b/activities/3DVolume.activity/lib/axios.min.js @@ -0,0 +1,3 @@ +/* axios v0.21.1 | (c) 2020 by Matt Zabriskie */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(13),s=n(16),i=n(5),a=n(17),u=n(20),c=n(21),f=n(14);e.exports=function(e){return new Promise(function(t,n){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";d.Authorization="Basic "+btoa(h+":"+m)}var y=a(e.baseURL,e.url);if(l.open(e.method.toUpperCase(),i(y,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l.onreadystatechange=function(){if(l&&4===l.readyState&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var r="getAllResponseHeaders"in l?u(l.getAllResponseHeaders()):null,s=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:s,status:l.status,statusText:l.statusText,headers:r,config:e,request:l};o(t,n,i),l=null}},l.onabort=function(){l&&(n(f("Request aborted",e,"ECONNABORTED",l)),l=null)},l.onerror=function(){n(f("Network Error",e,null,l)),l=null},l.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(f(t,e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=(e.withCredentials||c(y))&&e.xsrfCookieName?s.read(e.xsrfCookieName):void 0;g&&(d[e.xsrfHeaderName]=g)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),r.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),n(e),l=null)}),p||(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";var r=n(18),o=n(19);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){function n(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function o(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(s[o]=n(void 0,e[o])):s[o]=n(e[o],t[o])}t=t||{};var s={},i=["url","method","data"],a=["headers","auth","proxy","params"],u=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],c=["validateStatus"];r.forEach(i,function(e){r.isUndefined(t[e])||(s[e]=n(void 0,t[e]))}),r.forEach(a,o),r.forEach(u,function(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(s[o]=n(void 0,e[o])):s[o]=n(void 0,t[o])}),r.forEach(c,function(r){r in t?s[r]=n(e[r],t[r]):r in e&&(s[r]=n(void 0,e[r]))});var f=i.concat(a).concat(u).concat(c),p=Object.keys(e).concat(Object.keys(t)).filter(function(e){return f.indexOf(e)===-1});return r.forEach(p,o),s}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t){"use strict";e.exports=function(e){return"object"==typeof e&&e.isAxiosError===!0}}])}); +//# sourceMappingURL=axios.min.map \ No newline at end of file diff --git a/activities/3DVolume.activity/lib/i18next.min.js b/activities/3DVolume.activity/lib/i18next.min.js new file mode 100644 index 000000000..08f5469cf --- /dev/null +++ b/activities/3DVolume.activity/lib/i18next.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).i18next=t()}(this,function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function n(e){for(var n=1;n1&&void 0!==arguments[1]?arguments[1]:{};i(this,e),this.init(t,n)}return r(e,[{key:"init",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=t.prefix||"i18next:",this.logger=e||p,this.options=t,this.debug=t.debug}},{key:"setDebug",value:function(e){this.debug=e}},{key:"log",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n1?t-1:0),i=1;i-1?e.replace(/###/g,"."):e}function o(){return!e||"string"==typeof e}for(var r="string"!=typeof t?[].concat(t):t.split(".");r.length>1;){if(o())return{};var a=i(r.shift());!e[a]&&n&&(e[a]=new n),e=Object.prototype.hasOwnProperty.call(e,a)?e[a]:{}}return o()?{}:{obj:e,k:i(r.shift())}}function y(e,t,n){var i=v(e,t,Object);i.obj[i.k]=n}function m(e,t){var n=v(e,t),i=n.obj,o=n.k;if(i)return i[o]}function b(e,t,n){var i=m(e,n);return void 0!==i?i:m(t,n)}function k(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var x={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};function S(e){return"string"==typeof e?e.replace(/[&<>"'\/]/g,function(e){return x[e]}):e}var w="undefined"!=typeof window&&window.navigator&&window.navigator.userAgent&&window.navigator.userAgent.indexOf("MSIE")>-1;var L=function(e){function t(e){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{ns:["translation"],defaultNS:"translation"};return i(this,t),n=s(this,u(t).call(this)),w&&f.call(a(n)),n.data=e||{},n.options=o,void 0===n.options.keySeparator&&(n.options.keySeparator="."),void 0===n.options.ignoreJSONStructure&&(n.options.ignoreJSONStructure=!0),n}return c(t,f),r(t,[{key:"addNamespaces",value:function(e){this.options.ns.indexOf(e)<0&&this.options.ns.push(e)}},{key:"removeNamespaces",value:function(e){var t=this.options.ns.indexOf(e);t>-1&&this.options.ns.splice(t,1)}},{key:"getResource",value:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=void 0!==i.keySeparator?i.keySeparator:this.options.keySeparator,r=void 0!==i.ignoreJSONStructure?i.ignoreJSONStructure:this.options.ignoreJSONStructure,a=[e,t];n&&"string"!=typeof n&&(a=a.concat(n)),n&&"string"==typeof n&&(a=a.concat(o?n.split(o):n)),e.indexOf(".")>-1&&(a=e.split("."));var s=m(this.data,a);return s||!r||"string"!=typeof n?s:function e(t,n){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:".";if(t){if(t[n])return t[n];for(var o=n.split(i),r=t,a=0;aa+s;)s++,l=r[u=o.slice(a,a+s).join(i)];if(void 0===l)return;if("string"==typeof l)return l;if(u&&"string"==typeof l[u])return l[u];var c=o.slice(a+s).join(i);return c?e(l,c,i):void 0}r=r[o[a]]}return r}}(this.data&&this.data[e]&&this.data[e][t],n,o)}},{key:"addResource",value:function(e,t,n,i){var o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1},r=this.options.keySeparator;void 0===r&&(r=".");var a=[e,t];n&&(a=a.concat(r?n.split(r):n)),e.indexOf(".")>-1&&(i=t,t=(a=e.split("."))[1]),this.addNamespaces(t),y(this.data,a,i),o.silent||this.emit("added",e,t,n,i)}},{key:"addResources",value:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(var o in n)"string"!=typeof n[o]&&"[object Array]"!==Object.prototype.toString.apply(n[o])||this.addResource(e,t,o,n[o],{silent:!0});i.silent||this.emit("added",e,t,n)}},{key:"addResourceBundle",value:function(e,t,i,o,r){var a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1},s=[e,t];e.indexOf(".")>-1&&(o=i,i=t,t=(s=e.split("."))[1]),this.addNamespaces(t);var u=m(this.data,s)||{};o?function e(t,n,i){for(var o in n)"__proto__"!==o&&"constructor"!==o&&(o in t?"string"==typeof t[o]||t[o]instanceof String||"string"==typeof n[o]||n[o]instanceof String?i&&(t[o]=n[o]):e(t[o],n[o],i):t[o]=n[o]);return t}(u,i,r):u=n({},u,i),y(this.data,s,u),a.silent||this.emit("added",e,t,i)}},{key:"removeResourceBundle",value:function(e,t){this.hasResourceBundle(e,t)&&delete this.data[e][t],this.removeNamespaces(t),this.emit("removed",e,t)}},{key:"hasResourceBundle",value:function(e,t){return void 0!==this.getResource(e,t)}},{key:"getResourceBundle",value:function(e,t){return t||(t=this.options.defaultNS),"v1"===this.options.compatibilityAPI?n({},{},this.getResource(e,t)):this.getResource(e,t)}},{key:"getDataByLanguage",value:function(e){return this.data[e]}},{key:"toJSON",value:function(){return this.data}}]),t}(),O={processors:{},addPostProcessor:function(e){this.processors[e.name]=e},handle:function(e,t,n,i,o){var r=this;return e.forEach(function(e){r.processors[e]&&(t=r.processors[e].process(t,n,i,o))}),t}},N={},R=function(t){function o(e){var t,n,r,l,c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return i(this,o),t=s(this,u(o).call(this)),w&&f.call(a(t)),n=["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],r=e,l=a(t),n.forEach(function(e){r[e]&&(l[e]=r[e])}),t.options=c,void 0===t.options.keySeparator&&(t.options.keySeparator="."),t.logger=g.create("translator"),t}return c(o,f),r(o,[{key:"changeLanguage",value:function(e){e&&(this.language=e)}},{key:"exists",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}},n=this.resolve(e,t);return n&&void 0!==n.res}},{key:"extractFromKey",value:function(e,t){var n=void 0!==t.nsSeparator?t.nsSeparator:this.options.nsSeparator;void 0===n&&(n=":");var i=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator,o=t.ns||this.options.defaultNS;if(n&&e.indexOf(n)>-1){var r=e.match(this.interpolator.nestingRegexp);if(r&&r.length>0)return{key:e,namespaces:o};var a=e.split(n);(n!==i||n===i&&this.options.ns.indexOf(a[0])>-1)&&(o=a.shift()),e=a.join(i)}return"string"==typeof o&&(o=[o]),{key:e,namespaces:o}}},{key:"translate",value:function(t,i,r){var a=this;if("object"!==e(i)&&this.options.overloadTranslationOptionHandler&&(i=this.options.overloadTranslationOptionHandler(arguments)),i||(i={}),null==t)return"";Array.isArray(t)||(t=[String(t)]);var s=void 0!==i.keySeparator?i.keySeparator:this.options.keySeparator,u=this.extractFromKey(t[t.length-1],i),l=u.key,c=u.namespaces,p=c[c.length-1],g=i.lng||this.language,f=i.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(g&&"cimode"===g.toLowerCase()){if(f){var h=i.nsSeparator||this.options.nsSeparator;return p+h+l}return l}var d=this.resolve(t,i),v=d&&d.res,y=d&&d.usedKey||l,m=d&&d.exactUsedKey||l,b=Object.prototype.toString.apply(v),k=void 0!==i.joinArrays?i.joinArrays:this.options.joinArrays,x=!this.i18nFormat||this.i18nFormat.handleAsObject;if(x&&v&&("string"!=typeof v&&"boolean"!=typeof v&&"number"!=typeof v)&&["[object Number]","[object Function]","[object RegExp]"].indexOf(b)<0&&("string"!=typeof k||"[object Array]"!==b)){if(!i.returnObjects&&!this.options.returnObjects)return this.logger.warn("accessing an object - but returnObjects options is not enabled!"),this.options.returnedObjectHandler?this.options.returnedObjectHandler(y,v,i):"key '".concat(l," (").concat(this.language,")' returned an object instead of string.");if(s){var S="[object Array]"===b,w=S?[]:{},L=S?m:y;for(var O in v)if(Object.prototype.hasOwnProperty.call(v,O)){var N="".concat(L).concat(s).concat(O);w[O]=this.translate(N,n({},i,{joinArrays:!1,ns:c})),w[O]===N&&(w[O]=v[O])}v=w}}else if(x&&"string"==typeof k&&"[object Array]"===b)(v=v.join(k))&&(v=this.extendTranslation(v,t,i,r));else{var R=!1,C=!1,j=void 0!==i.count&&"string"!=typeof i.count,E=o.hasDefaultValue(i),P=j?this.pluralResolver.getSuffix(g,i.count):"",F=i["defaultValue".concat(P)]||i.defaultValue;!this.isValidLookup(v)&&E&&(R=!0,v=F),this.isValidLookup(v)||(C=!0,v=l);var V=E&&F!==v&&this.options.updateMissing;if(C||R||V){if(this.logger.log(V?"updateKey":"missingKey",g,p,l,V?F:v),s){var T=this.resolve(l,n({},i,{keySeparator:!1}));T&&T.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}var A=[],U=this.languageUtils.getFallbackCodes(this.options.fallbackLng,i.lng||this.language);if("fallback"===this.options.saveMissingTo&&U&&U[0])for(var I=0;I1&&void 0!==arguments[1]?arguments[1]:{};return"string"==typeof e&&(e=[e]),e.forEach(function(e){if(!a.isValidLookup(t)){var u=a.extractFromKey(e,s),l=u.key;n=l;var c=u.namespaces;a.options.fallbackNS&&(c=c.concat(a.options.fallbackNS));var p=void 0!==s.count&&"string"!=typeof s.count,g=void 0!==s.context&&"string"==typeof s.context&&""!==s.context,f=s.lngs?s.lngs:a.languageUtils.toResolveHierarchy(s.lng||a.language,s.fallbackLng);c.forEach(function(e){a.isValidLookup(t)||(r=e,!N["".concat(f[0],"-").concat(e)]&&a.utils&&a.utils.hasLoadedNamespace&&!a.utils.hasLoadedNamespace(r)&&(N["".concat(f[0],"-").concat(e)]=!0,a.logger.warn('key "'.concat(n,'" for languages "').concat(f.join(", "),'" won\'t get resolved as namespace "').concat(r,'" was not yet loaded'),"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),f.forEach(function(n){if(!a.isValidLookup(t)){o=n;var r,u,c=l,f=[c];if(a.i18nFormat&&a.i18nFormat.addLookupKeys)a.i18nFormat.addLookupKeys(f,l,n,e,s);else p&&(r=a.pluralResolver.getSuffix(n,s.count)),p&&g&&f.push(c+r),g&&f.push(c+="".concat(a.options.contextSeparator).concat(s.context)),p&&f.push(c+=r);for(;u=f.pop();)a.isValidLookup(t)||(i=u,t=a.getResource(n,e,u,s))}}))})}}),{res:t,usedKey:n,exactUsedKey:i,usedLng:o,usedNS:r}}},{key:"isValidLookup",value:function(e){return!(void 0===e||!this.options.returnNull&&null===e||!this.options.returnEmptyString&&""===e)}},{key:"getResource",value:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return this.i18nFormat&&this.i18nFormat.getResource?this.i18nFormat.getResource(e,t,n,i):this.resourceStore.getResource(e,t,n,i)}}],[{key:"hasDefaultValue",value:function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t)&&"defaultValue"===t.substring(0,"defaultValue".length)&&void 0!==e[t])return!0;return!1}}]),o}();function C(e){return e.charAt(0).toUpperCase()+e.slice(1)}var j=function(){function e(t){i(this,e),this.options=t,this.whitelist=this.options.supportedLngs||!1,this.supportedLngs=this.options.supportedLngs||!1,this.logger=g.create("languageUtils")}return r(e,[{key:"getScriptPartFromCode",value:function(e){if(!e||e.indexOf("-")<0)return null;var t=e.split("-");return 2===t.length?null:(t.pop(),"x"===t[t.length-1].toLowerCase()?null:this.formatLanguageCode(t.join("-")))}},{key:"getLanguagePartFromCode",value:function(e){if(!e||e.indexOf("-")<0)return e;var t=e.split("-");return this.formatLanguageCode(t[0])}},{key:"formatLanguageCode",value:function(e){if("string"==typeof e&&e.indexOf("-")>-1){var t=["hans","hant","latn","cyrl","cans","mong","arab"],n=e.split("-");return this.options.lowerCaseLng?n=n.map(function(e){return e.toLowerCase()}):2===n.length?(n[0]=n[0].toLowerCase(),n[1]=n[1].toUpperCase(),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=C(n[1].toLowerCase()))):3===n.length&&(n[0]=n[0].toLowerCase(),2===n[1].length&&(n[1]=n[1].toUpperCase()),"sgn"!==n[0]&&2===n[2].length&&(n[2]=n[2].toUpperCase()),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=C(n[1].toLowerCase())),t.indexOf(n[2].toLowerCase())>-1&&(n[2]=C(n[2].toLowerCase()))),n.join("-")}return this.options.cleanCode||this.options.lowerCaseLng?e.toLowerCase():e}},{key:"isWhitelisted",value:function(e){return this.logger.deprecate("languageUtils.isWhitelisted",'function "isWhitelisted" will be renamed to "isSupportedCode" in the next major - please make sure to rename it\'s usage asap.'),this.isSupportedCode(e)}},{key:"isSupportedCode",value:function(e){return("languageOnly"===this.options.load||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}},{key:"getBestMatchFromCodes",value:function(e){var t,n=this;return e?(e.forEach(function(e){if(!t){var i=n.formatLanguageCode(e);n.options.supportedLngs&&!n.isSupportedCode(i)||(t=i)}}),!t&&this.options.supportedLngs&&e.forEach(function(e){if(!t){var i=n.getLanguagePartFromCode(e);if(n.isSupportedCode(i))return t=i;t=n.options.supportedLngs.find(function(e){if(0===e.indexOf(i))return e})}}),t||(t=this.getFallbackCodes(this.options.fallbackLng)[0]),t):null}},{key:"getFallbackCodes",value:function(e,t){if(!e)return[];if("function"==typeof e&&(e=e(t)),"string"==typeof e&&(e=[e]),"[object Array]"===Object.prototype.toString.apply(e))return e;if(!t)return e.default||[];var n=e[t];return n||(n=e[this.getScriptPartFromCode(t)]),n||(n=e[this.formatLanguageCode(t)]),n||(n=e[this.getLanguagePartFromCode(t)]),n||(n=e.default),n||[]}},{key:"toResolveHierarchy",value:function(e,t){var n=this,i=this.getFallbackCodes(t||this.options.fallbackLng||[],e),o=[],r=function(e){e&&(n.isSupportedCode(e)?o.push(e):n.logger.warn("rejecting language code not found in supportedLngs: ".concat(e)))};return"string"==typeof e&&e.indexOf("-")>-1?("languageOnly"!==this.options.load&&r(this.formatLanguageCode(e)),"languageOnly"!==this.options.load&&"currentOnly"!==this.options.load&&r(this.getScriptPartFromCode(e)),"currentOnly"!==this.options.load&&r(this.getLanguagePartFromCode(e))):"string"==typeof e&&r(this.formatLanguageCode(e)),i.forEach(function(e){o.indexOf(e)<0&&r(n.formatLanguageCode(e))}),o}}]),e}(),E=[{lngs:["ach","ak","am","arn","br","fil","gun","ln","mfe","mg","mi","oc","pt","pt-BR","tg","tl","ti","tr","uz","wa"],nr:[1,2],fc:1},{lngs:["af","an","ast","az","bg","bn","ca","da","de","dev","el","en","eo","es","et","eu","fi","fo","fur","fy","gl","gu","ha","hi","hu","hy","ia","it","kk","kn","ku","lb","mai","ml","mn","mr","nah","nap","nb","ne","nl","nn","no","nso","pa","pap","pms","ps","pt-PT","rm","sco","se","si","so","son","sq","sv","sw","ta","te","tk","ur","yo"],nr:[1,2],fc:2},{lngs:["ay","bo","cgg","fa","ht","id","ja","jbo","ka","km","ko","ky","lo","ms","sah","su","th","tt","ug","vi","wo","zh"],nr:[1],fc:3},{lngs:["be","bs","cnr","dz","hr","ru","sr","uk"],nr:[1,2,5],fc:4},{lngs:["ar"],nr:[0,1,2,3,11,100],fc:5},{lngs:["cs","sk"],nr:[1,2,5],fc:6},{lngs:["csb","pl"],nr:[1,2,5],fc:7},{lngs:["cy"],nr:[1,2,3,8],fc:8},{lngs:["fr"],nr:[1,2],fc:9},{lngs:["ga"],nr:[1,2,3,7,11],fc:10},{lngs:["gd"],nr:[1,2,3,20],fc:11},{lngs:["is"],nr:[1,2],fc:12},{lngs:["jv"],nr:[0,1],fc:13},{lngs:["kw"],nr:[1,2,3,4],fc:14},{lngs:["lt"],nr:[1,2,10],fc:15},{lngs:["lv"],nr:[1,2,0],fc:16},{lngs:["mk"],nr:[1,2],fc:17},{lngs:["mnk"],nr:[0,1,2],fc:18},{lngs:["mt"],nr:[1,2,11,20],fc:19},{lngs:["or"],nr:[2,1],fc:2},{lngs:["ro"],nr:[1,2,20],fc:20},{lngs:["sl"],nr:[5,1,2,3],fc:21},{lngs:["he","iw"],nr:[1,2,20,21],fc:22}],P={1:function(e){return Number(e>1)},2:function(e){return Number(1!=e)},3:function(e){return 0},4:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},5:function(e){return Number(0==e?0:1==e?1:2==e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5)},6:function(e){return Number(1==e?0:e>=2&&e<=4?1:2)},7:function(e){return Number(1==e?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},8:function(e){return Number(1==e?0:2==e?1:8!=e&&11!=e?2:3)},9:function(e){return Number(e>=2)},10:function(e){return Number(1==e?0:2==e?1:e<7?2:e<11?3:4)},11:function(e){return Number(1==e||11==e?0:2==e||12==e?1:e>2&&e<20?2:3)},12:function(e){return Number(e%10!=1||e%100==11)},13:function(e){return Number(0!==e)},14:function(e){return Number(1==e?0:2==e?1:3==e?2:3)},15:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&(e%100<10||e%100>=20)?1:2)},16:function(e){return Number(e%10==1&&e%100!=11?0:0!==e?1:2)},17:function(e){return Number(1==e||e%10==1&&e%100!=11?0:1)},18:function(e){return Number(0==e?0:1==e?1:2)},19:function(e){return Number(1==e?0:0==e||e%100>1&&e%100<11?1:e%100>10&&e%100<20?2:3)},20:function(e){return Number(1==e?0:0==e||e%100>0&&e%100<20?1:2)},21:function(e){return Number(e%100==1?1:e%100==2?2:e%100==3||e%100==4?3:0)},22:function(e){return Number(1==e?0:2==e?1:(e<0||e>10)&&e%10==0?2:3)}};var F=function(){function e(t){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};i(this,e),this.languageUtils=t,this.options=o,this.logger=g.create("pluralResolver"),this.rules=(n={},E.forEach(function(e){e.lngs.forEach(function(t){n[t]={numbers:e.nr,plurals:P[e.fc]}})}),n)}return r(e,[{key:"addRule",value:function(e,t){this.rules[e]=t}},{key:"getRule",value:function(e){return this.rules[e]||this.rules[this.languageUtils.getLanguagePartFromCode(e)]}},{key:"needsPlural",value:function(e){var t=this.getRule(e);return t&&t.numbers.length>1}},{key:"getPluralFormsOfKey",value:function(e,t){return this.getSuffixes(e).map(function(e){return t+e})}},{key:"getSuffixes",value:function(e){var t=this,n=this.getRule(e);return n?n.numbers.map(function(n){return t.getSuffix(e,n)}):[]}},{key:"getSuffix",value:function(e,t){var n=this,i=this.getRule(e);if(i){var o=i.noAbs?i.plurals(t):i.plurals(Math.abs(t)),r=i.numbers[o];this.options.simplifyPluralSuffix&&2===i.numbers.length&&1===i.numbers[0]&&(2===r?r="plural":1===r&&(r=""));var a=function(){return n.options.prepend&&r.toString()?n.options.prepend+r.toString():r.toString()};return"v1"===this.options.compatibilityJSON?1===r?"":"number"==typeof r?"_plural_".concat(r.toString()):a():"v2"===this.options.compatibilityJSON?a():this.options.simplifyPluralSuffix&&2===i.numbers.length&&1===i.numbers[0]?a():this.options.prepend&&o.toString()?this.options.prepend+o.toString():o.toString()}return this.logger.warn("no plural rule found for: ".concat(e)),""}}]),e}(),V=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};i(this,e),this.logger=g.create("interpolator"),this.options=t,this.format=t.interpolation&&t.interpolation.format||function(e){return e},this.init(t)}return r(e,[{key:"init",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e.interpolation||(e.interpolation={escapeValue:!0});var t=e.interpolation;this.escape=void 0!==t.escape?t.escape:S,this.escapeValue=void 0===t.escapeValue||t.escapeValue,this.useRawValueToEscape=void 0!==t.useRawValueToEscape&&t.useRawValueToEscape,this.prefix=t.prefix?k(t.prefix):t.prefixEscaped||"{{",this.suffix=t.suffix?k(t.suffix):t.suffixEscaped||"}}",this.formatSeparator=t.formatSeparator?t.formatSeparator:t.formatSeparator||",",this.unescapePrefix=t.unescapeSuffix?"":t.unescapePrefix||"-",this.unescapeSuffix=this.unescapePrefix?"":t.unescapeSuffix||"",this.nestingPrefix=t.nestingPrefix?k(t.nestingPrefix):t.nestingPrefixEscaped||k("$t("),this.nestingSuffix=t.nestingSuffix?k(t.nestingSuffix):t.nestingSuffixEscaped||k(")"),this.nestingOptionsSeparator=t.nestingOptionsSeparator?t.nestingOptionsSeparator:t.nestingOptionsSeparator||",",this.maxReplaces=t.maxReplaces?t.maxReplaces:1e3,this.alwaysFormat=void 0!==t.alwaysFormat&&t.alwaysFormat,this.resetRegExp()}},{key:"reset",value:function(){this.options&&this.init(this.options)}},{key:"resetRegExp",value:function(){var e="".concat(this.prefix,"(.+?)").concat(this.suffix);this.regexp=new RegExp(e,"g");var t="".concat(this.prefix).concat(this.unescapePrefix,"(.+?)").concat(this.unescapeSuffix).concat(this.suffix);this.regexpUnescape=new RegExp(t,"g");var n="".concat(this.nestingPrefix,"(.+?)").concat(this.nestingSuffix);this.nestingRegexp=new RegExp(n,"g")}},{key:"interpolate",value:function(e,t,i,o){var r,a,s,u=this,l=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{};function c(e){return e.replace(/\$/g,"$$$$")}var p=function(e){if(e.indexOf(u.formatSeparator)<0){var r=b(t,l,e);return u.alwaysFormat?u.format(r,void 0,i,n({},o,t,{interpolationkey:e})):r}var a=e.split(u.formatSeparator),s=a.shift().trim(),c=a.join(u.formatSeparator).trim();return u.format(b(t,l,s),c,i,n({},o,t,{interpolationkey:s}))};this.resetRegExp();var g=o&&o.missingInterpolationHandler||this.options.missingInterpolationHandler,f=o&&o.interpolation&&o.interpolation.skipOnVariables||this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:function(e){return c(e)}},{regex:this.regexp,safeValue:function(e){return u.escapeValue?c(u.escape(e)):c(e)}}].forEach(function(t){for(s=0;r=t.regex.exec(e);){if(void 0===(a=p(r[1].trim())))if("function"==typeof g){var n=g(e,r,o);a="string"==typeof n?n:""}else{if(f){a=r[0];continue}u.logger.warn("missed to pass in variable ".concat(r[1]," for interpolating ").concat(e)),a=""}else"string"==typeof a||u.useRawValueToEscape||(a=d(a));var i=t.safeValue(a);if(e=e.replace(r[0],i),f?(t.regex.lastIndex+=i.length,t.regex.lastIndex-=r[0].length):t.regex.lastIndex=0,++s>=u.maxReplaces)break}}),e}},{key:"nest",value:function(e,t){var i,o,r=this,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},s=n({},a);function u(e,t){var i=this.nestingOptionsSeparator;if(e.indexOf(i)<0)return e;var o=e.split(new RegExp("".concat(i,"[ ]*{"))),r="{".concat(o[1]);e=o[0],r=(r=this.interpolate(r,s)).replace(/'/g,'"');try{s=JSON.parse(r),t&&(s=n({},t,s))}catch(t){return this.logger.warn("failed parsing options string in nesting for key ".concat(e),t),"".concat(e).concat(i).concat(r)}return delete s.defaultValue,e}for(s.applyPostProcessor=!1,delete s.defaultValue;i=this.nestingRegexp.exec(e);){var l=[],c=!1;if(-1!==i[0].indexOf(this.formatSeparator)&&!/{.*}/.test(i[1])){var p=i[1].split(this.formatSeparator).map(function(e){return e.trim()});i[1]=p.shift(),l=p,c=!0}if((o=t(u.call(this,i[1].trim(),s),s))&&i[0]===e&&"string"!=typeof o)return o;"string"!=typeof o&&(o=d(o)),o||(this.logger.warn("missed to resolve ".concat(i[1]," for nesting ").concat(e)),o=""),c&&(o=l.reduce(function(e,t){return r.format(e,t,a.lng,n({},a,{interpolationkey:i[1].trim()}))},o.trim())),e=e.replace(i[0],o),this.regexp.lastIndex=0}return e}}]),e}();var T=function(e){function t(e,n,o){var r,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return i(this,t),r=s(this,u(t).call(this)),w&&f.call(a(r)),r.backend=e,r.store=n,r.services=o,r.languageUtils=o.languageUtils,r.options=l,r.logger=g.create("backendConnector"),r.state={},r.queue=[],r.backend&&r.backend.init&&r.backend.init(o,l.backend,l),r}return c(t,f),r(t,[{key:"queueLoad",value:function(e,t,n,i){var o=this,r=[],a=[],s=[],u=[];return e.forEach(function(e){var i=!0;t.forEach(function(t){var s="".concat(e,"|").concat(t);!n.reload&&o.store.hasResourceBundle(e,t)?o.state[s]=2:o.state[s]<0||(1===o.state[s]?a.indexOf(s)<0&&a.push(s):(o.state[s]=1,i=!1,a.indexOf(s)<0&&a.push(s),r.indexOf(s)<0&&r.push(s),u.indexOf(t)<0&&u.push(t)))}),i||s.push(e)}),(r.length||a.length)&&this.queue.push({pending:a,loaded:{},errors:[],callback:i}),{toLoad:r,pending:a,toLoadLanguages:s,toLoadNamespaces:u}}},{key:"loaded",value:function(e,t,n){var i=e.split("|"),o=i[0],r=i[1];t&&this.emit("failedLoading",o,r,t),n&&this.store.addResourceBundle(o,r,n),this.state[e]=t?-1:2;var a={};this.queue.forEach(function(n){var i,s,u,l,c,p;i=n.loaded,s=r,l=v(i,[o],Object),c=l.obj,p=l.k,c[p]=c[p]||[],u&&(c[p]=c[p].concat(s)),u||c[p].push(s),function(e,t){for(var n=e.indexOf(t);-1!==n;)e.splice(n,1),n=e.indexOf(t)}(n.pending,e),t&&n.errors.push(t),0!==n.pending.length||n.done||(Object.keys(n.loaded).forEach(function(e){a[e]||(a[e]=[]),n.loaded[e].length&&n.loaded[e].forEach(function(t){a[e].indexOf(t)<0&&a[e].push(t)})}),n.done=!0,n.errors.length?n.callback(n.errors):n.callback())}),this.emit("loaded",a),this.queue=this.queue.filter(function(e){return!e.done})}},{key:"read",value:function(e,t,n){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:350,a=arguments.length>5?arguments[5]:void 0;return e.length?this.backend[n](e,t,function(s,u){s&&u&&o<5?setTimeout(function(){i.read.call(i,e,t,n,o+1,2*r,a)},r):a(s,u)}):a(null,{})}},{key:"prepareLoading",value:function(e,t){var n=this,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),o&&o();"string"==typeof e&&(e=this.languageUtils.toResolveHierarchy(e)),"string"==typeof t&&(t=[t]);var r=this.queueLoad(e,t,i,o);if(!r.toLoad.length)return r.pending.length||o(),null;r.toLoad.forEach(function(e){n.loadOne(e)})}},{key:"load",value:function(e,t,n){this.prepareLoading(e,t,{},n)}},{key:"reload",value:function(e,t,n){this.prepareLoading(e,t,{reload:!0},n)}},{key:"loadOne",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=e.split("|"),o=i[0],r=i[1];this.read(o,r,"read",void 0,void 0,function(i,a){i&&t.logger.warn("".concat(n,"loading namespace ").concat(r," for language ").concat(o," failed"),i),!i&&a&&t.logger.log("".concat(n,"loaded namespace ").concat(r," for language ").concat(o),a),t.loaded(e,i,a)})}},{key:"saveMissing",value:function(e,t,i,o,r){var a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{};this.services.utils&&this.services.utils.hasLoadedNamespace&&!this.services.utils.hasLoadedNamespace(t)?this.logger.warn('did not save key "'.concat(i,'" as the namespace "').concat(t,'" was not yet loaded'),"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!"):null!=i&&""!==i&&(this.backend&&this.backend.create&&this.backend.create(e,t,i,o,null,n({},a,{isUpdate:r})),e&&e[0]&&this.store.addResource(e[0],t,i,o))}}]),t}();function A(e){return"string"==typeof e.ns&&(e.ns=[e.ns]),"string"==typeof e.fallbackLng&&(e.fallbackLng=[e.fallbackLng]),"string"==typeof e.fallbackNS&&(e.fallbackNS=[e.fallbackNS]),e.whitelist&&(e.whitelist&&e.whitelist.indexOf("cimode")<0&&(e.whitelist=e.whitelist.concat(["cimode"])),e.supportedLngs=e.whitelist),e.nonExplicitWhitelist&&(e.nonExplicitSupportedLngs=e.nonExplicitWhitelist),e.supportedLngs&&e.supportedLngs.indexOf("cimode")<0&&(e.supportedLngs=e.supportedLngs.concat(["cimode"])),e}function U(){}return new(function(t){function o(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1?arguments[1]:void 0;if(i(this,o),e=s(this,u(o).call(this)),w&&f.call(a(e)),e.options=A(t),e.services={},e.logger=g,e.modules={external:[]},n&&!e.isInitialized&&!t.isClone){if(!e.options.initImmediate)return e.init(t,n),s(e,a(e));setTimeout(function(){e.init(t,n)},0)}return e}return c(o,f),r(o,[{key:"init",value:function(){var t=this,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=arguments.length>1?arguments[1]:void 0;function r(e){return e?"function"==typeof e?new e:e:null}if("function"==typeof i&&(o=i,i={}),i.whitelist&&!i.supportedLngs&&this.logger.deprecate("whitelist",'option "whitelist" will be renamed to "supportedLngs" in the next major - please make sure to rename this option asap.'),i.nonExplicitWhitelist&&!i.nonExplicitSupportedLngs&&this.logger.deprecate("whitelist",'options "nonExplicitWhitelist" will be renamed to "nonExplicitSupportedLngs" in the next major - please make sure to rename this option asap.'),this.options=n({},{debug:!1,initImmediate:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,whitelist:!1,nonExplicitWhitelist:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!0,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:function(t){var n={};if("object"===e(t[1])&&(n=t[1]),"string"==typeof t[1]&&(n.defaultValue=t[1]),"string"==typeof t[2]&&(n.tDescription=t[2]),"object"===e(t[2])||"object"===e(t[3])){var i=t[3]||t[2];Object.keys(i).forEach(function(e){n[e]=i[e]})}return n},interpolation:{escapeValue:!0,format:function(e,t,n,i){return e},prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!1}},this.options,A(i)),this.format=this.options.interpolation.format,o||(o=U),!this.options.isClone){this.modules.logger?g.init(r(this.modules.logger),this.options):g.init(null,this.options);var a=new j(this.options);this.store=new L(this.options.resources,this.options);var s=this.services;s.logger=g,s.resourceStore=this.store,s.languageUtils=a,s.pluralResolver=new F(a,{prepend:this.options.pluralSeparator,compatibilityJSON:this.options.compatibilityJSON,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),s.interpolator=new V(this.options),s.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},s.backendConnector=new T(r(this.modules.backend),s.resourceStore,s,this.options),s.backendConnector.on("*",function(e){for(var n=arguments.length,i=new Array(n>1?n-1:0),o=1;o1?n-1:0),o=1;o0&&"dev"!==u[0]&&(this.options.lng=u[0])}this.services.languageDetector||this.options.lng||this.logger.warn("init: no languageDetector is used and no lng is defined");["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(function(e){t[e]=function(){var n;return(n=t.store)[e].apply(n,arguments)}});["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(function(e){t[e]=function(){var n;return(n=t.store)[e].apply(n,arguments),t}});var l=h(),c=function(){var e=function(e,n){t.isInitialized&&t.logger.warn("init: i18next is already initialized. You should call init just once!"),t.isInitialized=!0,t.options.isClone||t.logger.log("initialized",t.options),t.emit("initialized",t.options),l.resolve(n),o(e,n)};if(t.languages&&"v1"!==t.options.compatibilityAPI&&!t.isInitialized)return e(null,t.t.bind(t));t.changeLanguage(t.options.lng,e)};return this.options.resources||!this.options.initImmediate?c():setTimeout(c,0),l}},{key:"loadResources",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:U,i="string"==typeof e?e:this.language;if("function"==typeof e&&(n=e),!this.options.resources||this.options.partialBundledLanguages){if(i&&"cimode"===i.toLowerCase())return n();var o=[],r=function(e){e&&t.services.languageUtils.toResolveHierarchy(e).forEach(function(e){o.indexOf(e)<0&&o.push(e)})};if(i)r(i);else this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(function(e){return r(e)});this.options.preload&&this.options.preload.forEach(function(e){return r(e)}),this.services.backendConnector.load(o,this.options.ns,n)}else n(null)}},{key:"reloadResources",value:function(e,t,n){var i=h();return e||(e=this.languages),t||(t=this.options.ns),n||(n=U),this.services.backendConnector.reload(e,t,function(e){i.resolve(),n(e)}),i}},{key:"use",value:function(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return"backend"===e.type&&(this.modules.backend=e),("logger"===e.type||e.log&&e.warn&&e.error)&&(this.modules.logger=e),"languageDetector"===e.type&&(this.modules.languageDetector=e),"i18nFormat"===e.type&&(this.modules.i18nFormat=e),"postProcessor"===e.type&&O.addPostProcessor(e),"3rdParty"===e.type&&this.modules.external.push(e),this}},{key:"changeLanguage",value:function(e,t){var n=this;this.isLanguageChangingTo=e;var i=h();this.emit("languageChanging",e);var o=function(e){var o="string"==typeof e?e:n.services.languageUtils.getBestMatchFromCodes(e);o&&(n.language||(n.language=o,n.languages=n.services.languageUtils.toResolveHierarchy(o)),n.translator.language||n.translator.changeLanguage(o),n.services.languageDetector&&n.services.languageDetector.cacheUserLanguage(o)),n.loadResources(o,function(e){!function(e,o){o?(n.language=o,n.languages=n.services.languageUtils.toResolveHierarchy(o),n.translator.changeLanguage(o),n.isLanguageChangingTo=void 0,n.emit("languageChanged",o),n.logger.log("languageChanged",o)):n.isLanguageChangingTo=void 0,i.resolve(function(){return n.t.apply(n,arguments)}),t&&t(e,function(){return n.t.apply(n,arguments)})}(e,o)})};return e||!this.services.languageDetector||this.services.languageDetector.async?!e&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect(o):o(e):o(this.services.languageDetector.detect()),i}},{key:"getFixedT",value:function(t,i){var o=this,r=function t(i,r){var a;if("object"!==e(r)){for(var s=arguments.length,u=new Array(s>2?s-2:0),l=2;l1&&void 0!==arguments[1]?arguments[1]:{};if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;var i=this.languages[0],o=!!this.options&&this.options.fallbackLng,r=this.languages[this.languages.length-1];if("cimode"===i.toLowerCase())return!0;var a=function(e,n){var i=t.services.backendConnector.state["".concat(e,"|").concat(n)];return-1===i||2===i};if(n.precheck){var s=n.precheck(this,a);if(void 0!==s)return s}return!!this.hasResourceBundle(i,e)||(!this.services.backendConnector.backend||!(!a(i,e)||o&&!a(r,e)))}},{key:"loadNamespaces",value:function(e,t){var n=this,i=h();return this.options.ns?("string"==typeof e&&(e=[e]),e.forEach(function(e){n.options.ns.indexOf(e)<0&&n.options.ns.push(e)}),this.loadResources(function(e){i.resolve(),t&&t(e)}),i):(t&&t(),Promise.resolve())}},{key:"loadLanguages",value:function(e,t){var n=h();"string"==typeof e&&(e=[e]);var i=this.options.preload||[],o=e.filter(function(e){return i.indexOf(e)<0});return o.length?(this.options.preload=i.concat(o),this.loadResources(function(e){n.resolve(),t&&t(e)}),n):(t&&t(),Promise.resolve())}},{key:"dir",value:function(e){if(e||(e=this.languages&&this.languages.length>0?this.languages[0]:this.language),!e)return"rtl";return["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam"].indexOf(this.services.languageUtils.getLanguagePartFromCode(e))>=0?"rtl":"ltr"}},{key:"createInstance",value:function(){return new o(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},arguments.length>1?arguments[1]:void 0)}},{key:"cloneInstance",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:U,r=n({},this.options,t,{isClone:!0}),a=new o(r);return["store","services","language"].forEach(function(t){a[t]=e[t]}),a.services=n({},this.services),a.services.utils={hasLoadedNamespace:a.hasLoadedNamespace.bind(a)},a.translator=new R(a.services,a.options),a.translator.on("*",function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),i=1;i { + await i18next.init({ + lng: lang, + debug: false, + fallbackLng: "en", + resources: {} + }).then(() => { + l10n.switchTo(lang); + }); + }; + + l10n.get = (key) => { + return i18next.t(key); + }; + + l10n.loadLanguageResource = (lang) => { + return new Promise((resolve, reject) => { + axios.get("./locales/" + lang + ".json").then((response) => { + resolve(response.data); + }).catch((error) => { + console.log("Failed to load " + lang + " language: " + error); + resolve(null); // Resolve with null to indicate failure + }); + }); + }; + + l10n.switchTo = (lang) => { + if (!i18next.hasResourceBundle(lang, "translation")) { + console.log("Loading " + lang + " language"); + l10n.loadLanguageResource(lang).then((locales) => { + if (locales !== null) { + i18next.addResourceBundle(lang, "translation", locales); + i18next.changeLanguage(lang); + triggerLocalizedEvent(); + } else { + l10n.init("en"); + } + }); + } else { + i18next.changeLanguage(lang); + triggerLocalizedEvent(); + } + }; + + + function triggerLocalizedEvent() { + const event = new Event("localized"); + window.dispatchEvent(event); + }; + + return l10n; +}); \ No newline at end of file diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index eed1837ae..235be3d65 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -1,10 +1,10 @@ -define([], function () { +define(["l10n"], function (l10n) { var tutorial = {}; tutorial.start = function (ifAdding) { var steps = [ { - title: "Volume Activity", + title: l10n.get("VolumeActivity"), intro: "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", }, { diff --git a/activities/3DVolume.activity/locales/en.json b/activities/3DVolume.activity/locales/en.json new file mode 100644 index 000000000..e69de29bb diff --git a/activities/3DVolume.activity/locales/fr.json b/activities/3DVolume.activity/locales/fr.json new file mode 100644 index 000000000..fb1c4d264 --- /dev/null +++ b/activities/3DVolume.activity/locales/fr.json @@ -0,0 +1,3 @@ +{ + "VolumeActivity" : "vol act" +} \ No newline at end of file From 67fe91bdf5f3690046d718eb99ebe2baf116ec7d Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 29 Jul 2024 00:32:52 +0530 Subject: [PATCH 34/60] localization --- activities/3DVolume.activity/lib/tutorial.js | 232 +++++++++---------- activities/3DVolume.activity/locales/en.json | 32 +++ 2 files changed, 148 insertions(+), 116 deletions(-) diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index 235be3d65..71ecb4a8d 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -1,124 +1,124 @@ define(["l10n"], function (l10n) { - var tutorial = {}; + var tutorial = {}; - tutorial.start = function (ifAdding) { - var steps = [ - { - title: l10n.get("VolumeActivity"), - intro: "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", - }, - { - element: "#tetra-button", - title: "Add Tetrahedrons", - intro: "Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.", - }, - { - element: "#cube-button", - title: "Add Cubes", - intro: "Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.", - }, - { - element: "#octa-button", - title: "Add Octahedrons", - intro: "Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.", - }, - { - element: "#deca-button", - title: "Add Decahedrons", - intro: "Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.", - }, - { - element: "#dodeca-button", - title: "Add Dodecahedron", - intro: "Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.", - }, - { - element: "#icosa-button", - title: "Add Icosahedron", - intro: "Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.", - }, - { - element: "#clear-button", - title: "Remove volumes", - intro: "Select this and click on any volume on the board to remove it.", - }, - { - element: "#volume-button", - title: "Select Volume Type", - intro: "Select the type of volume you want to add to the board.", - }, - { - element: "#color-button-fill", - title: "Change Volume Color", - intro: "Choose any one of the colors for your volumes. The default is your buddy color.", - }, - { - element: "#color-button-text", - title: "Change Volume Text Color", - intro: "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", - }, - { - element: "#bg-button", - position: "bottom", - title: "Change board background", - intro: "Choose any one of the three boards backgrounds according to their different frictions.", - }, - { - element: "#zoom-button", - title: "Zoom In/Out", - intro: "Use the zoom pallette to zoom in or out..", - }, - { - element: "#throw-button", - title: "Throw Button", - intro: "Use this button to shake the volumes on the board.", - }, - { - element: ".arrow-container", - title: "Rotate the board", - intro: "Select the arrow pointing in the direction you wish the board to rotate.", - }, - ]; + tutorial.start = function (ifAdding) { + var steps = [ + { + title: l10n.get("VolumeTitle"), + intro: l10n.get("VolumeIntro"), + }, + { + element: "#tetra-button", + title: l10n.get("AddTetrahedronsTitle"), + intro: l10n.get("AddTetrahedronsIntro"), + }, + { + element: "#cube-button", + title: l10n.get("AddCubesTitle"), + intro: l10n.get("AddCubesIntro"), + }, + { + element: "#octa-button", + title: l10n.get("AddOctahedronsTitle"), + intro: l10n.get("AddOctahedronsIntro"), + }, + { + element: "#deca-button", + title: l10n.get("AddDecahedronsTitle"), + intro: l10n.get("AddDecahedronsIntro"), + }, + { + element: "#dodeca-button", + title: l10n.get("AddDodecahedronTitle"), + intro: l10n.get("AddDodecahedronIntro"), + }, + { + element: "#icosa-button", + title: l10n.get("AddIcosahedronTitle"), + intro: l10n.get("AddIcosahedronIntro"), + }, + { + element: "#clear-button", + title: l10n.get("RemoveVolumesTitle"), + intro: l10n.get("RemoveVolumesIntro"), + }, + { + element: "#volume-button", + title: l10n.get("SelectVolumeTypeTitle"), + intro: l10n.get("SelectVolumeTypeIntro"), + }, + { + element: "#color-button-fill", + title: l10n.get("ChangeVolumeColorTitle"), + intro: l10n.get("ChangeVolumeColorIntro"), + }, + { + element: "#color-button-text", + title: l10n.get("ChangeVolumeTextColorTitle"), + intro: l10n.get("ChangeVolumeTextColorIntro"), + }, + { + element: "#bg-button", + position: "bottom", + title: l10n.get("ChangeBoardBackgroundTitle"), + intro: l10n.get("ChangeBoardBackgroundIntro"), + }, + { + element: "#zoom-button", + title: l10n.get("ZoomInOutTitle"), + intro: l10n.get("ZoomInOutIntro"), + }, + { + element: "#throw-button", + title: l10n.get("ThrowButtonTitle"), + intro: l10n.get("ThrowButtonIntro"), + }, + { + element: ".arrow-container", + title: l10n.get("RotateBoardTitle"), + intro: l10n.get("RotateBoardIntro"), + }, + ]; - // Filter out hidden buttons based on screen width - var hiddenButtonSelectors = []; - var screenWidth = window.innerWidth; + // Filter out hidden buttons based on screen width + var hiddenButtonSelectors = []; + var screenWidth = window.innerWidth; - if (screenWidth <= 1120) { - hiddenButtonSelectors.push("#color-button-text", "#bg-button", "#zoom-button"); - } - if (screenWidth <= 800) { - hiddenButtonSelectors.push("#volume-button", "#color-button-fill", "#remove-first"); - } - if (screenWidth <= 690) { - hiddenButtonSelectors.push("#sensor-button", "#throw-button"); - } + if (screenWidth <= 1120) { + hiddenButtonSelectors.push("#color-button-text", "#bg-button", "#zoom-button"); + } + if (screenWidth <= 800) { + hiddenButtonSelectors.push("#volume-button", "#color-button-fill", "#remove-first"); + } + if (screenWidth <= 690) { + hiddenButtonSelectors.push("#sensor-button", "#throw-button"); + } - steps = steps.filter(function (obj) { - return ( - !("element" in obj) || - (obj.element.length && - document.querySelector(obj.element) && - document.querySelector(obj.element).style.display != "none" && - !hiddenButtonSelectors.includes(obj.element)) - ); - }); + steps = steps.filter(function (obj) { + return ( + !("element" in obj) || + (obj.element.length && + document.querySelector(obj.element) && + document.querySelector(obj.element).style.display != "none" && + !hiddenButtonSelectors.includes(obj.element)) + ); + }); - introJs() - .setOptions({ - tooltipClass: "customTooltip", - steps: steps, - prevLabel: "Prev", - nextLabel: "Next", - exitOnOverlayClick: false, - nextToDone: false, - showBullets: false, - }) - .start() - .onexit(function() { - ifAdding.adding = true; - }); - }; + introJs() + .setOptions({ + tooltipClass: "customTooltip", + steps: steps, + prevLabel: "Prev", + nextLabel: "Next", + exitOnOverlayClick: false, + nextToDone: false, + showBullets: false, + }) + .start() + .onexit(function() { + ifAdding.adding = true; + }); + }; - return tutorial; + return tutorial; }); diff --git a/activities/3DVolume.activity/locales/en.json b/activities/3DVolume.activity/locales/en.json index e69de29bb..9d0429206 100644 --- a/activities/3DVolume.activity/locales/en.json +++ b/activities/3DVolume.activity/locales/en.json @@ -0,0 +1,32 @@ +{ + "VolumeTitle": "Volume Activity", + "VolumeIntro": "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", + "AddTetrahedronsTitle": "Add Tetrahedrons", + "AddTetrahedronsIntro": "Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.", + "AddCubesTitle": "Add Cubes", + "AddCubesIntro": "Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.", + "AddOctahedronsTitle": "Add Octahedrons", + "AddOctahedronsIntro": "Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.", + "AddDecahedronsTitle": "Add Decahedrons", + "AddDecahedronsIntro": "Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.", + "AddDodecahedronTitle": "Add Dodecahedron", + "AddDodecahedronIntro": "Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.", + "AddIcosahedronTitle": "Add Icosahedron", + "AddIcosahedronIntro": "Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.", + "RemoveVolumesTitle": "Remove Volumes", + "RemoveVolumesIntro": "Select this and click on any volume on the board to remove it.", + "SelectVolumeTypeTitle": "Select Volume Type", + "SelectVolumeTypeIntro": "Select the type of volume you want to add to the board.", + "ChangeVolumeColorTitle": "Change Volume Color", + "ChangeVolumeColorIntro": "Choose any one of the colors for your volumes. The default is your buddy color.", + "ChangeVolumeTextColorTitle": "Change Volume Text Color", + "ChangeVolumeTextColorIntro": "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", + "ChangeBoardBackgroundTitle": "Change Board Background", + "ChangeBoardBackgroundIntro": "Choose any one of the three boards backgrounds according to their different frictions.", + "ZoomInOutTitle": "Zoom In/Out", + "ZoomInOutIntro": "Use the zoom pallette to zoom in or out.", + "ThrowButtonTitle": "Throw Button", + "ThrowButtonIntro": "Use this button to shake the volumes on the board.", + "RotateBoardTitle": "Rotate the Board", + "RotateBoardIntro": "Select the arrow pointing in the direction you wish the board to rotate." +} From b90022239806e0a12727a6f9da9643eaccbe76df Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Thu, 1 Aug 2024 00:02:56 +0530 Subject: [PATCH 35/60] changes --- activities/3DVolume.activity/css/activity.css | 11 +++++ activities/3DVolume.activity/js/Cube.js | 4 +- activities/3DVolume.activity/js/Deca.js | 33 ++++++++------ activities/3DVolume.activity/js/Dodeca.js | 44 ++++++++++--------- activities/3DVolume.activity/js/Icosa.js | 2 + activities/3DVolume.activity/js/Octa.js | 2 + activities/3DVolume.activity/js/Tetra.js | 2 + activities/3DVolume.activity/js/activity.js | 12 ----- .../js/palettes/bgpalette.html | 7 +-- 9 files changed, 67 insertions(+), 50 deletions(-) diff --git a/activities/3DVolume.activity/css/activity.css b/activities/3DVolume.activity/css/activity.css index a49eb57ec..c00ae1ab7 100644 --- a/activities/3DVolume.activity/css/activity.css +++ b/activities/3DVolume.activity/css/activity.css @@ -270,6 +270,17 @@ body { height: 100vh; } +.gray-rectangle { + width: 50px; /* Adjust the width as needed */ + height: 25px; /* Adjust the height as needed */ + background-color: #9C9C9C; + margin-bottom: 5px; /* Adjust the spacing between the rectangle and the text as needed */ +} + +#wood img { + margin-right: 25px; +} + #game-container { position: absolute; } diff --git a/activities/3DVolume.activity/js/Cube.js b/activities/3DVolume.activity/js/Cube.js index 8bfdc6dc2..ad5f8f79d 100644 --- a/activities/3DVolume.activity/js/Cube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -124,12 +124,14 @@ function createCube( let angVel3 = sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; boxBody.angularVelocity.set(angVel1, angVel2, angVel3); + boxBody.angularDamping = 0.1; // This will help in reducing rotation over time boxBody.applyImpulse(ctx.offset, ctx.rollingForce); const contactMat = new CANNON.ContactMaterial( groundPhysMat, boxBody.material, - { friction: ctx.friction } + { friction: ctx.friction}, + ); world.addContactMaterial(contactMat); diff --git a/activities/3DVolume.activity/js/Deca.js b/activities/3DVolume.activity/js/Deca.js index 15d34b58e..bf5ac70f8 100644 --- a/activities/3DVolume.activity/js/Deca.js +++ b/activities/3DVolume.activity/js/Deca.js @@ -27,7 +27,7 @@ for (let i = 0, b = 0; i < 10; ++i, b += (Math.PI * 2) / 10) { } vertices.push([0, 0, -1]); vertices.push([0, 0, 1]); -let scaleFactor = 1; +let scaleFactor = 1.5; let values = 10; let faceTexts = [ " ", @@ -53,7 +53,7 @@ let faceTexts = [ "19", "20", ]; -let textMargin = 0.8; +let textMargin = 1; let chamfer = 0.945; let af = (Math.PI * 6) / 5; let tab = 0; @@ -76,7 +76,8 @@ function createDecahedron( scene, groundPhysMat, sharedAngVel1, - sharedAngVel2 + sharedAngVel2, + sharedAngVel3 ) { let decahedron; let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; @@ -124,23 +125,27 @@ function createDecahedron( // decahedron = diceMesh; scene.add(decahedron); - // Create vertices for the decahedron - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; const sides = 10; + const scalingFactor = 1.5; + const verticesGeo = [ - [0, 0, 1], // Top vertex - [0, 0, -1], // Bottom vertex + [0, 0, scalingFactor * 1], // Top vertex + [0, 0, scalingFactor * -1], // Bottom vertex ]; for (let i = 0; i < sides; ++i) { const b = (i * Math.PI * 2) / sides; - verticesGeo.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]); + verticesGeo.push([ + scalingFactor * Math.cos(b), + scalingFactor * Math.sin(b), + scalingFactor * 0.105 * (i % 2 ? 1 : -1), + ]); } // Convert vertices to CANNON.Vec3 format @@ -205,13 +210,15 @@ function createDecahedron( sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + let angVel3 = + sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; - decahedronBody.angularVelocity.set(angVel1, angVel2, 0.5); + decahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); + decahedronBody.angularDamping = 0.1; // This will help in reducing rotation over time decahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); decahedron.position.copy(decahedronBody.position); // this merges the physics body to threejs mesh decahedron.quaternion.copy(decahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { decahedron.quaternion.copy(quaternionShared); decahedronBody.quaternion.copy(quaternionShared); @@ -227,7 +234,8 @@ function createDecahedron( tempTextColor, angVel1, angVel2, - contactMat + angVel3, + contactMat, ]); } @@ -447,7 +455,6 @@ function calculateTextureSize(approx) { } function getDecaScore(scoresObject, body, ifRemove) { - let vector = new THREE.Vector3(0, 1); let closest_face; let closest_angle = Math.PI * 2; @@ -485,4 +492,4 @@ function getDecaScore(scoresObject, body, ifRemove) { } else { return scoreToShow; } -} \ No newline at end of file +} diff --git a/activities/3DVolume.activity/js/Dodeca.js b/activities/3DVolume.activity/js/Dodeca.js index 668bf2318..40c85cc61 100644 --- a/activities/3DVolume.activity/js/Dodeca.js +++ b/activities/3DVolume.activity/js/Dodeca.js @@ -93,18 +93,18 @@ function createDodecahedron( let tempShowNumbers = ifNumbers == null ? ctx.showNumbers : ifNumbers; let tempTransparent = ifTransparent == null ? ctx.toggleTransparent : ifTransparent; - // let tempImage = ifImage == null ? showImage : ifImage; let tempFillColor = sharedColor != null ? sharedColor : ctx.presentColor; let tempTextColor = sharedTextColor != null ? sharedTextColor : ctx.textColor; backColor2 = tempFillColor; color2 = tempTextColor; + if (tempShowNumbers) { dodecahedron = new THREE.Mesh(getGeometry2(), getMaterials2()); } else if (tempTransparent) { - const dodecahedronTransaprentGeometry = getGeometry2(); + const dodecahedronTransparentGeometry = getGeometry2(); const wireframe = new THREE.WireframeGeometry( - dodecahedronTransaprentGeometry + dodecahedronTransparentGeometry ); const lineMaterial = new THREE.LineBasicMaterial({ color: sharedColor != null ? sharedColor : ctx.presentColor, @@ -116,12 +116,10 @@ function createDodecahedron( dodecahedron = line; } else { const dodecahedronGeometry = getGeometry2(); - const dodecaMaterial = new THREE.MeshStandardMaterial({ color: sharedColor != null ? sharedColor : ctx.presentColor, wireframe: false, }); - dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecaMaterial); } @@ -195,7 +193,6 @@ function createDodecahedron( [1, 5, 9], ]; - // Create a ConvexPolyhedron shape from the vertices2 and faces2 const dodecahedronShape = new CANNON.ConvexPolyhedron({ vertices: vertices3, faces: indices, @@ -203,27 +200,27 @@ function createDodecahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 4 : yCoordinateShared; - - let newGeometry = getGeometry2(); - + let y = yCoordinateShared == null ? 4 : yCoordinateShared + 1; // Ensure initial y is above the plane + console.log(myShape) const dodecahedronBody = new CANNON.Body({ mass: 2, // Set mass - shape: dodecahedronShape, + shape: myShape, position: new CANNON.Vec3(x, y, z), material: new CANNON.Material(), - restitution: 5, + restitution: 0.3, // Lower the restitution value }); dodecahedronBody.sleepSpeedLimit = 0.2; dodecahedronBody.sleepTimeLimit = 3; + dodecahedronBody.linearDamping = 0.1; // Apply linear damping + dodecahedronBody.angularDamping = 0.1; // Apply angular damping world.addBody(dodecahedronBody); + const contactMat = new CANNON.ContactMaterial( groundPhysMat, dodecahedronBody.material, { friction: ctx.friction } ); - world.addContactMaterial(contactMat); let angVel1 = @@ -234,13 +231,15 @@ function createDodecahedron( sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; dodecahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); + dodecahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); - dodecahedron.position.copy(dodecahedronBody.position); // this merges the physics body to threejs mesh - dodecahedron.quaternion.copy(dodecahedronBody.quaternion); - if (quaternionShared != null && quaternionShared != undefined) { + dodecahedron.position.copy(dodecahedronBody.position); // Sync positions + dodecahedron.quaternion.copy(dodecahedronBody.quaternion); // Sync orientations + if (quaternionShared != null) { dodecahedron.quaternion.copy(quaternionShared); dodecahedronBody.quaternion.copy(quaternionShared); } + diceArray.push([ dodecahedron, dodecahedronBody, @@ -252,9 +251,12 @@ function createDodecahedron( angVel1, angVel2, angVel3, - contactMat + contactMat, ]); + + console.log("Dodecahedron initial position:", dodecahedronBody.position); // Debugging log } + function getGeometry2() { let radius = 1 * scaleFactor2; @@ -271,7 +273,7 @@ function getGeometry2() { tab2, af2 ); - geometry.cannon_shape = createShape(vectors, faces2, radius); + myShape = createShape(vectors, faces2, radius); return geometry; } @@ -430,8 +432,10 @@ function createShape(vertices2, faces2, radius) { cf[i] = faces2[i].slice(0, faces2[i].length - 1); } console.log(faces2); - return new CANNON.ConvexPolyhedron(cv, cf); + + return new CANNON.ConvexPolyhedron({ vertices: cv, faces: cf }); } +let myShape = null; function getMaterials2() { let materials = []; @@ -507,6 +511,4 @@ function getDodecaScore(scoresObject, body, ifRemove) { } else { return scoreToShow; } - } - diff --git a/activities/3DVolume.activity/js/Icosa.js b/activities/3DVolume.activity/js/Icosa.js index 4fbcc139e..be43703bb 100644 --- a/activities/3DVolume.activity/js/Icosa.js +++ b/activities/3DVolume.activity/js/Icosa.js @@ -200,6 +200,8 @@ function createIcosahedron( sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; icosahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); + icosahedronBody.angularDamping = 0.1; // This will help in reducing rotation over time + icosahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); icosahedron.position.copy(icosahedronBody.position); // this merges the physics body to threejs mesh icosahedron.quaternion.copy(icosahedronBody.quaternion); diff --git a/activities/3DVolume.activity/js/Octa.js b/activities/3DVolume.activity/js/Octa.js index d4477948b..79b99ce82 100644 --- a/activities/3DVolume.activity/js/Octa.js +++ b/activities/3DVolume.activity/js/Octa.js @@ -163,6 +163,8 @@ function createOctahedron( sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; octahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); + octahedronBody.angularDamping = 0.1; // This will help in reducing rotation over time + octahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); octahedron.position.copy(octahedronBody.position); // this merges the physics body to threejs mesh octahedron.quaternion.copy(octahedronBody.quaternion); diff --git a/activities/3DVolume.activity/js/Tetra.js b/activities/3DVolume.activity/js/Tetra.js index 7b364150d..28d035938 100644 --- a/activities/3DVolume.activity/js/Tetra.js +++ b/activities/3DVolume.activity/js/Tetra.js @@ -178,6 +178,8 @@ function createTetrahedron( world.addContactMaterial(contactMat); tetrahedronBody.angularVelocity.set(angVel1, angVel2, angVel3); + tetrahedronBody.angularDamping = 0.1; // This will help in reducing rotation over time + tetrahedronBody.applyImpulse(ctx.offset, ctx.rollingForce); tetrahedron.position.copy(tetrahedronBody.position); // this merges the physics body to threejs mesh tetrahedron.quaternion.copy(tetrahedronBody.quaternion); diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index ee1c04d4b..d106bb6bb 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -86,18 +86,6 @@ define([ toggleTransparent: false, offset: new CANNON.Vec3(0, 0.1, 0), rollingForce: randomDirection.scale(2), - cubeMaterial: new CANNON.Material(), - tetraMaterial: new CANNON.Material(), - octaMaterial: new CANNON.Material(), - decaMaterial: new CANNON.Material(), - dodecaMaterial: new CANNON.Material(), - icosaMaterial: new CANNON.Material(), - cubeContact: null, - tetraContact: null, - octaContact: null, - decaContact: null, - dodecaContact: null, - icosaContact: null, friction: 1, }; let presentBackground = null; diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.html b/activities/3DVolume.activity/js/palettes/bgpalette.html index 1bf9281ae..52c263198 100644 --- a/activities/3DVolume.activity/js/palettes/bgpalette.html +++ b/activities/3DVolume.activity/js/palettes/bgpalette.html @@ -7,12 +7,13 @@

Select Background

Green Board (High Friction)
+ From b0eb23d84c0420edf099ffa4fc229efb999ed1fc Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Thu, 1 Aug 2024 23:35:08 +0530 Subject: [PATCH 36/60] reverse? --- activities/3DVolume.activity/index.html | 28 ++- activities/3DVolume.activity/js/activity.js | 118 ++++++--- activities/3DVolume.activity/lib/tutorial.js | 241 ++++++++++--------- activities/3DVolume.activity/locales/en.json | 62 ++--- 4 files changed, 258 insertions(+), 191 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index d445ee45a..8f9486a6a 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -18,19 +18,26 @@ href="lib/sugar-web/graphics/css/sugar-200dpi.css" /> - + - + - + - + @@ -154,7 +161,11 @@ id="stop-button" title="Stop" > - + - + diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index d106bb6bb..6891295eb 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1064,53 +1064,95 @@ define([ function accelerationChanged(acceleration) { if (!sensorMode) return; - if (acceleration.y > 0) { - if (acceleration.y < -2.7) { - world.gravity.set(-3, -9.81, 0); // Gravity towards the right - wakeAll(); + if (window.orientation == -90) { + // If its in reverse. + if (acceleration.y > 0) { + if (acceleration.y < -2.8) { + world.gravity.set(3, 9.81, 0); // Gravity towards the left + wakeAll(); + } else { + world.gravity.set(9.81, 9.81, 0); // Gravity towards the left + wakeAll(); + } + // left } else { - world.gravity.set(-9.81, -9.81, 0); // Gravity towards the right - wakeAll(); - } - // right - } else { - if (acceleration.z > 0) { - if (acceleration.y > -1 && acceleration.y < 1) { - if (acceleration.z > 6) { - // back - world.gravity.set(0, -9.81, 9.81); // Gravity towards the back - wakeAll(); + if (acceleration.z > 0) { + if (acceleration.y > -1 && acceleration.y < 1) { + if (acceleration.z > 6) { + // front + world.gravity.set(0, 9.81, -9.81); // Gravity towards the front + wakeAll(); + } else { + // straight + world.gravity.set(0, 9.81, 0); + wakeAll(); + } } else { - // straight - world.gravity.set(0, -9.81, 0); - wakeAll(); + if (acceleration.y < -2.7) { + world.gravity.set(-3, 9.81, 0); // Gravity towards the right + wakeAll(); + } else { + world.gravity.set(-9.81, 9.81, 0); // Gravity towards the right + wakeAll(); + } + // right } } else { - if (acceleration.y < -2.8) { - world.gravity.set(3, -9.81, 0); // Gravity towards the left - wakeAll(); + // back + world.gravity.set(0, 9.81, 9.81); // Gravity towards the back + wakeAll(); + } + } + } else { + if (acceleration.y > 0) { + if (acceleration.y < -2.7) { + world.gravity.set(-3, -9.81, 0); // Gravity towards the right + wakeAll(); + } else { + world.gravity.set(-9.81, -9.81, 0); // Gravity towards the right + wakeAll(); + } + // right + } else { + if (acceleration.z > 0) { + if (acceleration.y > -1 && acceleration.y < 1) { + if (acceleration.z > 6) { + // back + world.gravity.set(0, -9.81, 9.81); // Gravity towards the back + wakeAll(); + } else { + // straight + world.gravity.set(0, -9.81, 0); + wakeAll(); + } } else { - world.gravity.set(9.81, -9.81, 0); // Gravity towards the left - wakeAll(); + if (acceleration.y < -2.8) { + world.gravity.set(3, -9.81, 0); // Gravity towards the left + wakeAll(); + } else { + world.gravity.set(9.81, -9.81, 0); // Gravity towards the left + wakeAll(); + } + // left } - // left + } else { + // front + world.gravity.set(0, -9.81, -9.81); // Gravity towards the front + wakeAll(); } - } else { - // front - world.gravity.set(0, -9.81, -9.81); // Gravity towards the front - wakeAll(); } } - lastRollElement.textContent = - "x- " + - acceleration.x + - ", " + - "y- " + - acceleration.y + - ", " + - "z- " + - acceleration.x + - " "; + + // lastRollElement.textContent = + // "x- " + + // acceleration.x + + // ", " + + // "y- " + + // acceleration.y + + // ", " + + // "z- " + + // acceleration.x + + // " "; } // Wakes all the volumes so that they move towards the gravity. diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index 71ecb4a8d..c40ace853 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -1,124 +1,133 @@ define(["l10n"], function (l10n) { - var tutorial = {}; + var tutorial = {}; - tutorial.start = function (ifAdding) { - var steps = [ - { - title: l10n.get("VolumeTitle"), - intro: l10n.get("VolumeIntro"), - }, - { - element: "#tetra-button", - title: l10n.get("AddTetrahedronsTitle"), - intro: l10n.get("AddTetrahedronsIntro"), - }, - { - element: "#cube-button", - title: l10n.get("AddCubesTitle"), - intro: l10n.get("AddCubesIntro"), - }, - { - element: "#octa-button", - title: l10n.get("AddOctahedronsTitle"), - intro: l10n.get("AddOctahedronsIntro"), - }, - { - element: "#deca-button", - title: l10n.get("AddDecahedronsTitle"), - intro: l10n.get("AddDecahedronsIntro"), - }, - { - element: "#dodeca-button", - title: l10n.get("AddDodecahedronTitle"), - intro: l10n.get("AddDodecahedronIntro"), - }, - { - element: "#icosa-button", - title: l10n.get("AddIcosahedronTitle"), - intro: l10n.get("AddIcosahedronIntro"), - }, - { - element: "#clear-button", - title: l10n.get("RemoveVolumesTitle"), - intro: l10n.get("RemoveVolumesIntro"), - }, - { - element: "#volume-button", - title: l10n.get("SelectVolumeTypeTitle"), - intro: l10n.get("SelectVolumeTypeIntro"), - }, - { - element: "#color-button-fill", - title: l10n.get("ChangeVolumeColorTitle"), - intro: l10n.get("ChangeVolumeColorIntro"), - }, - { - element: "#color-button-text", - title: l10n.get("ChangeVolumeTextColorTitle"), - intro: l10n.get("ChangeVolumeTextColorIntro"), - }, - { - element: "#bg-button", - position: "bottom", - title: l10n.get("ChangeBoardBackgroundTitle"), - intro: l10n.get("ChangeBoardBackgroundIntro"), - }, - { - element: "#zoom-button", - title: l10n.get("ZoomInOutTitle"), - intro: l10n.get("ZoomInOutIntro"), - }, - { - element: "#throw-button", - title: l10n.get("ThrowButtonTitle"), - intro: l10n.get("ThrowButtonIntro"), - }, - { - element: ".arrow-container", - title: l10n.get("RotateBoardTitle"), - intro: l10n.get("RotateBoardIntro"), - }, - ]; + tutorial.start = function (ifAdding) { + var steps = [ + { + title: l10n.get("VolumeTitle"), + intro: l10n.get("VolumeIntro"), + }, + { + element: "#tetra-button", + title: l10n.get("AddTetrahedronsTitle"), + intro: l10n.get("AddTetrahedronsIntro"), + }, + { + element: "#cube-button", + title: l10n.get("AddCubesTitle"), + intro: l10n.get("AddCubesIntro"), + }, + { + element: "#octa-button", + title: l10n.get("AddOctahedronsTitle"), + intro: l10n.get("AddOctahedronsIntro"), + }, + { + element: "#deca-button", + title: l10n.get("AddDecahedronsTitle"), + intro: l10n.get("AddDecahedronsIntro"), + }, + { + element: "#dodeca-button", + title: l10n.get("AddDodecahedronTitle"), + intro: l10n.get("AddDodecahedronIntro"), + }, + { + element: "#icosa-button", + title: l10n.get("AddIcosahedronTitle"), + intro: l10n.get("AddIcosahedronIntro"), + }, + { + element: "#clear-button", + title: l10n.get("RemoveVolumesTitle"), + intro: l10n.get("RemoveVolumesIntro"), + }, + { + element: "#volume-button", + title: l10n.get("SelectVolumeTypeTitle"), + intro: l10n.get("SelectVolumeTypeIntro"), + }, + { + element: "#color-button-fill", + title: l10n.get("ChangeVolumeColorTitle"), + intro: l10n.get("ChangeVolumeColorIntro"), + }, + { + element: "#color-button-text", + title: l10n.get("ChangeVolumeTextColorTitle"), + intro: l10n.get("ChangeVolumeTextColorIntro"), + }, + { + element: "#bg-button", + position: "bottom", + title: l10n.get("ChangeBoardBackgroundTitle"), + intro: l10n.get("ChangeBoardBackgroundIntro"), + }, + { + element: "#zoom-button", + title: l10n.get("ZoomInOutTitle"), + intro: l10n.get("ZoomInOutIntro"), + }, + { + element: "#throw-button", + title: l10n.get("ThrowButtonTitle"), + intro: l10n.get("ThrowButtonIntro"), + }, + { + element: ".arrow-container", + title: l10n.get("RotateBoardTitle"), + intro: l10n.get("RotateBoardIntro"), + }, + ]; - // Filter out hidden buttons based on screen width - var hiddenButtonSelectors = []; - var screenWidth = window.innerWidth; + // Filter out hidden buttons based on screen width + var hiddenButtonSelectors = []; + var screenWidth = window.innerWidth; - if (screenWidth <= 1120) { - hiddenButtonSelectors.push("#color-button-text", "#bg-button", "#zoom-button"); - } - if (screenWidth <= 800) { - hiddenButtonSelectors.push("#volume-button", "#color-button-fill", "#remove-first"); - } - if (screenWidth <= 690) { - hiddenButtonSelectors.push("#sensor-button", "#throw-button"); - } + if (screenWidth <= 1120) { + hiddenButtonSelectors.push( + "#color-button-text", + "#bg-button", + "#zoom-button" + ); + } + if (screenWidth <= 800) { + hiddenButtonSelectors.push( + "#volume-button", + "#color-button-fill", + "#remove-first" + ); + } + if (screenWidth <= 690) { + hiddenButtonSelectors.push("#sensor-button", "#throw-button"); + } - steps = steps.filter(function (obj) { - return ( - !("element" in obj) || - (obj.element.length && - document.querySelector(obj.element) && - document.querySelector(obj.element).style.display != "none" && - !hiddenButtonSelectors.includes(obj.element)) - ); - }); + steps = steps.filter(function (obj) { + return ( + !("element" in obj) || + (obj.element.length && + document.querySelector(obj.element) && + document.querySelector(obj.element).style.display != + "none" && + !hiddenButtonSelectors.includes(obj.element)) + ); + }); - introJs() - .setOptions({ - tooltipClass: "customTooltip", - steps: steps, - prevLabel: "Prev", - nextLabel: "Next", - exitOnOverlayClick: false, - nextToDone: false, - showBullets: false, - }) - .start() - .onexit(function() { - ifAdding.adding = true; - }); - }; + introJs() + .setOptions({ + tooltipClass: "customTooltip", + steps: steps, + prevLabel: l10n.get("TutoPrev"), + nextLabel: l10n.get("TutoNext"), + exitOnOverlayClick: false, + nextToDone: false, + showBullets: false, + }) + .start() + .onexit(function () { + ifAdding.adding = true; + }); + }; - return tutorial; + return tutorial; }); diff --git a/activities/3DVolume.activity/locales/en.json b/activities/3DVolume.activity/locales/en.json index 9d0429206..7cea7e0ec 100644 --- a/activities/3DVolume.activity/locales/en.json +++ b/activities/3DVolume.activity/locales/en.json @@ -1,32 +1,34 @@ { - "VolumeTitle": "Volume Activity", - "VolumeIntro": "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", - "AddTetrahedronsTitle": "Add Tetrahedrons", - "AddTetrahedronsIntro": "Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.", - "AddCubesTitle": "Add Cubes", - "AddCubesIntro": "Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.", - "AddOctahedronsTitle": "Add Octahedrons", - "AddOctahedronsIntro": "Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.", - "AddDecahedronsTitle": "Add Decahedrons", - "AddDecahedronsIntro": "Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.", - "AddDodecahedronTitle": "Add Dodecahedron", - "AddDodecahedronIntro": "Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.", - "AddIcosahedronTitle": "Add Icosahedron", - "AddIcosahedronIntro": "Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.", - "RemoveVolumesTitle": "Remove Volumes", - "RemoveVolumesIntro": "Select this and click on any volume on the board to remove it.", - "SelectVolumeTypeTitle": "Select Volume Type", - "SelectVolumeTypeIntro": "Select the type of volume you want to add to the board.", - "ChangeVolumeColorTitle": "Change Volume Color", - "ChangeVolumeColorIntro": "Choose any one of the colors for your volumes. The default is your buddy color.", - "ChangeVolumeTextColorTitle": "Change Volume Text Color", - "ChangeVolumeTextColorIntro": "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", - "ChangeBoardBackgroundTitle": "Change Board Background", - "ChangeBoardBackgroundIntro": "Choose any one of the three boards backgrounds according to their different frictions.", - "ZoomInOutTitle": "Zoom In/Out", - "ZoomInOutIntro": "Use the zoom pallette to zoom in or out.", - "ThrowButtonTitle": "Throw Button", - "ThrowButtonIntro": "Use this button to shake the volumes on the board.", - "RotateBoardTitle": "Rotate the Board", - "RotateBoardIntro": "Select the arrow pointing in the direction you wish the board to rotate." + "VolumeTitle": "Volume Activity", + "VolumeIntro": "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", + "AddTetrahedronsTitle": "Add Tetrahedrons", + "AddTetrahedronsIntro": "Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.", + "AddCubesTitle": "Add Cubes", + "AddCubesIntro": "Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.", + "AddOctahedronsTitle": "Add Octahedrons", + "AddOctahedronsIntro": "Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.", + "AddDecahedronsTitle": "Add Decahedrons", + "AddDecahedronsIntro": "Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.", + "AddDodecahedronTitle": "Add Dodecahedron", + "AddDodecahedronIntro": "Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.", + "AddIcosahedronTitle": "Add Icosahedron", + "AddIcosahedronIntro": "Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.", + "RemoveVolumesTitle": "Remove Volumes", + "RemoveVolumesIntro": "Select this and click on any volume on the board to remove it.", + "SelectVolumeTypeTitle": "Select Volume Type", + "SelectVolumeTypeIntro": "Select the type of volume you want to add to the board.", + "ChangeVolumeColorTitle": "Change Volume Color", + "ChangeVolumeColorIntro": "Choose any one of the colors for your volumes. The default is your buddy color.", + "ChangeVolumeTextColorTitle": "Change Volume Text Color", + "ChangeVolumeTextColorIntro": "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", + "ChangeBoardBackgroundTitle": "Change Board Background", + "ChangeBoardBackgroundIntro": "Choose any one of the three boards backgrounds according to their different frictions.", + "ZoomInOutTitle": "Zoom In/Out", + "ZoomInOutIntro": "Use the zoom pallette to zoom in or out.", + "ThrowButtonTitle": "Throw Button", + "ThrowButtonIntro": "Use this button to shake the volumes on the board.", + "RotateBoardTitle": "Rotate the Board", + "RotateBoardIntro": "Select the arrow pointing in the direction you wish the board to rotate.", + "TutoNext": "Next", + "TutoPrev": "Prev" } From d3f0f27e7f66fc78d8e33e01725cade34207ddca Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sat, 3 Aug 2024 23:30:38 +0530 Subject: [PATCH 37/60] changes --- activities/3DVolume.activity/index.html | 40 ++++- activities/3DVolume.activity/js/Cube.js | 13 +- activities/3DVolume.activity/js/activity.js | 162 ++++++++---------- .../js/palettes/bgpalette.html | 10 +- .../js/palettes/colorpalettefill.html | 6 +- .../js/palettes/colorpalettetext.html | 6 +- .../js/palettes/volumepalette.html | 7 +- .../js/palettes/zoompalette.html | 7 + activities/3DVolume.activity/locales/en.json | 38 +++- activities/3DVolume.activity/locales/fr.json | 66 ++++++- 10 files changed, 237 insertions(+), 118 deletions(-) diff --git a/activities/3DVolume.activity/index.html b/activities/3DVolume.activity/index.html index 8f9486a6a..2084f3ccd 100644 --- a/activities/3DVolume.activity/index.html +++ b/activities/3DVolume.activity/index.html @@ -63,31 +63,37 @@ @@ -97,6 +103,7 @@ class="toolbutton" class="active" id="clear-button" + data-i18n="Clear" title="Clear Button" > @@ -105,7 +112,8 @@ @@ -113,18 +121,21 @@ @@ -133,18 +144,21 @@ @@ -159,16 +173,19 @@ @@ -199,5 +216,24 @@ id="unfullscreen-button" title="UnFullscreen" > + diff --git a/activities/3DVolume.activity/js/Cube.js b/activities/3DVolume.activity/js/Cube.js index ad5f8f79d..8456a4a65 100644 --- a/activities/3DVolume.activity/js/Cube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -102,8 +102,6 @@ function createCube( boxMesh.castShadow = true; scene.add(boxMesh); - - let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; @@ -123,6 +121,10 @@ function createCube( sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; let angVel3 = sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; + console.log(angVel1); + console.log(angVel2); + + console.log(angVel3); boxBody.angularVelocity.set(angVel1, angVel2, angVel3); boxBody.angularDamping = 0.1; // This will help in reducing rotation over time boxBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -130,8 +132,7 @@ function createCube( const contactMat = new CANNON.ContactMaterial( groundPhysMat, boxBody.material, - { friction: ctx.friction}, - + { friction: ctx.friction } ); world.addContactMaterial(contactMat); @@ -139,7 +140,7 @@ function createCube( boxMesh.quaternion.copy(quaternionShared); boxBody.quaternion.copy(quaternionShared); } - console.log(boxBody.material) + console.log(boxBody.material); diceArray.push([ boxMesh, boxBody, @@ -151,7 +152,7 @@ function createCube( angVel1, angVel2, angVel3, - contactMat + contactMat, ]); } diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 6891295eb..f6d4a9980 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -84,9 +84,9 @@ define([ presentColor: null, textColor: "#ffffff", toggleTransparent: false, - offset: new CANNON.Vec3(0, 0.1, 0), - rollingForce: randomDirection.scale(2), - friction: 1, + offset: new CANNON.Vec3(0, 0, 0), + rollingForce: randomDirection, + friction: 0.1, }; let presentBackground = null; let scoresObject = { @@ -170,7 +170,6 @@ define([ if (msg.action == "init") { changeBoardBackground(msg.content[1]); data = msg.content[0]; - console.log(data); for (let i = 0; i < data.length; i++) { let createFunction = null; switch (data[i][0]) { @@ -222,6 +221,7 @@ define([ ); } } + console.log(diceArray); } if (msg.action == "throw") { throwDice(msg.content[0], msg.content[1]); @@ -299,6 +299,7 @@ define([ .getElementById("stop-button") .addEventListener("click", function (event) { // We must add to the journal all the features of the added volume along with its exact position and quaternion + journalDiceArray.push(presentBackground); for (let i = 0; i < diceArray.length; i++) { journalDiceArray.push([ diceArray[i][2], @@ -340,60 +341,63 @@ define([ .loadAsText(function (error, metadata, data) { if (error == null && data != null) { data = JSON.parse(data); - for (let i = 0; i < data.length; i++) { - let fillColorStored = data[i][3]; - let textColorStored = data[i][4]; - switch (data[i][0]) { - case "cube": - createFunction = createCube; - break; - case "octa": - createFunction = createOctahedron; - break; - case "tetra": - createFunction = createTetrahedron; - break; - case "deca": - createFunction = createDecahedron; - break; - case "dodeca": - createFunction = createDodecahedron; - break; - case "icosa": - createFunction = createIcosahedron; - break; - default: - console.log( - `Unexpected shape: ${data[i][0]}` - ); - break; - } + if (data.length > 1) { + for (let i = 1; i < data.length; i++) { + let fillColorStored = data[i][3]; + let textColorStored = data[i][4]; + switch (data[i][0]) { + case "cube": + createFunction = createCube; + break; + case "octa": + createFunction = createOctahedron; + break; + case "tetra": + createFunction = createTetrahedron; + break; + case "deca": + createFunction = createDecahedron; + break; + case "dodeca": + createFunction = createDodecahedron; + break; + case "icosa": + createFunction = createIcosahedron; + break; + default: + console.log( + `Unexpected shape: ${data[i][0]}` + ); + break; + } - // Create the volumes - - if (createFunction) { - createFunction( - fillColorStored, - data[i][5], - data[i][6], - data[i][1].x, - data[i][1].z, - false, - null, - data[i][1].y, - data[i][2], - textColorStored, - ctx, - diceArray, - world, - scene, - groundPhysMat, - data[i][7], - data[i][8], - data[i][9] - ); + // Create the volumes + + if (createFunction) { + createFunction( + fillColorStored, + data[i][5], + data[i][6], + data[i][1].x, + data[i][1].z, + false, + null, + data[i][1].y, + data[i][2], + textColorStored, + ctx, + diceArray, + world, + scene, + groundPhysMat, + data[i][7], + data[i][8], + data[i][9] + ); + } } } + changeBoardBackground(data[0]); } }); } @@ -974,8 +978,10 @@ define([ diceArray[i][4], diceArray[i][7], diceArray[i][8], + diceArray[i][9], ]); } + console.log(diceArray); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "init", @@ -1364,57 +1370,25 @@ define([ scoresObject.lastRoll = ""; scoresObject.presentScore = 0; for (let i = 0; i < diceArray.length; i++) { - let rollingForce; - if (sharedRolling != null) { - rollingForce = new THREE.Vector3( - sharedRolling.x, - sharedRolling.y, - sharedRolling.z - ); - } else { - rollingForce = ctx.rollingForce; - } - diceArray[i][1].angularVelocity.set(0.5, 0.5, 0.5); - diceArray[i][1].applyImpulse( - sharedOffset != null ? sharedOffset : ctx.offset, - rollingForce + diceArray[i][1].angularVelocity.set( + diceArray[i][7], + diceArray[i][8], + diceArray[i][9] ); + diceArray[i][1].applyImpulse(ctx.offset, ctx.rollingForce); diceArray[i][1].position.set(0, 10, 0); } for (let i = 0; i < diceArray.length; i++) { scene.add(diceArray[i][0]); world.addBody(diceArray[i][1]); } - } else { - for (let i = 0; i < dices.cube; i++) { - createCube(); - } - for (let i = 0; i < dices.tetra; i++) { - createTetrahedron(); - } - for (let i = 0; i < dices.octa; i++) { - createOctahedron(); - } - for (let i = 0; i < dices.dodeca; i++) { - createDodecahedron(); - } - for (let i = 0; i < dices.deca; i++) { - createDecahedron(); - } - for (let i = 0; i < dices.icosa; i++) { - createIcosahedron(); - } - scoresObject.lastRoll = ""; - // if (ctx.showNumbers) { - // getScore(); - // } } + console.log(diceArray); } // Functions to get the scores of the dice. function changeBoardBackground(selectedBoard) { - console.log(selectedBoard); presentBackground = selectedBoard; let textureLoader = new THREE.TextureLoader(); switch (selectedBoard) { @@ -1548,7 +1522,7 @@ define([ renderer.setAnimationLoop(animate); - const fixedTimeStep = 1 / 40; + const fixedTimeStep = 1 / 60; const maxSubSteps = 3; function updatePhysics() { diff --git a/activities/3DVolume.activity/js/palettes/bgpalette.html b/activities/3DVolume.activity/js/palettes/bgpalette.html index 52c263198..05a203f5c 100644 --- a/activities/3DVolume.activity/js/palettes/bgpalette.html +++ b/activities/3DVolume.activity/js/palettes/bgpalette.html @@ -1,19 +1,21 @@
-

Select Background

+

Select Background


diff --git a/activities/3DVolume.activity/js/palettes/colorpalettefill.html b/activities/3DVolume.activity/js/palettes/colorpalettefill.html index c5649e277..476ca3520 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettefill.html +++ b/activities/3DVolume.activity/js/palettes/colorpalettefill.html @@ -12,15 +12,15 @@
- +
- +
- +
diff --git a/activities/3DVolume.activity/js/palettes/colorpalettetext.html b/activities/3DVolume.activity/js/palettes/colorpalettetext.html index a9a12e8de..2e10a6060 100644 --- a/activities/3DVolume.activity/js/palettes/colorpalettetext.html +++ b/activities/3DVolume.activity/js/palettes/colorpalettetext.html @@ -13,15 +13,15 @@
- +
- +
- +
diff --git a/activities/3DVolume.activity/js/palettes/volumepalette.html b/activities/3DVolume.activity/js/palettes/volumepalette.html index 05e149823..134bed195 100644 --- a/activities/3DVolume.activity/js/palettes/volumepalette.html +++ b/activities/3DVolume.activity/js/palettes/volumepalette.html @@ -2,16 +2,19 @@ diff --git a/activities/3DVolume.activity/js/palettes/zoompalette.html b/activities/3DVolume.activity/js/palettes/zoompalette.html index 39c52e0f0..f277e9710 100644 --- a/activities/3DVolume.activity/js/palettes/zoompalette.html +++ b/activities/3DVolume.activity/js/palettes/zoompalette.html @@ -3,20 +3,27 @@ class="toolbutton" id="zoom-in-button" title="Zoom in button" + data-i18n="ZoomIn" > diff --git a/activities/3DVolume.activity/locales/en.json b/activities/3DVolume.activity/locales/en.json index 7cea7e0ec..0090e2c7a 100644 --- a/activities/3DVolume.activity/locales/en.json +++ b/activities/3DVolume.activity/locales/en.json @@ -24,11 +24,45 @@ "ChangeBoardBackgroundTitle": "Change Board Background", "ChangeBoardBackgroundIntro": "Choose any one of the three boards backgrounds according to their different frictions.", "ZoomInOutTitle": "Zoom In/Out", - "ZoomInOutIntro": "Use the zoom pallette to zoom in or out.", + "ZoomInOutIntro": "Use the zoom palette to zoom in or out.", "ThrowButtonTitle": "Throw Button", "ThrowButtonIntro": "Use this button to shake the volumes on the board.", "RotateBoardTitle": "Rotate the Board", "RotateBoardIntro": "Select the arrow pointing in the direction you wish the board to rotate.", "TutoNext": "Next", - "TutoPrev": "Prev" + "TutoPrev": "Prev", + "AddTetra.title": "Add Tetrahedron", + "AddCube.title": "Add Cube", + "AddOcta.title": "Add Octahedron", + "AddDeca.title": "Add Decahedron", + "AddDodeca.title": "Add Dodecahedron", + "AddIcosa.title": "Add Icosahedron", + "Clear.title": "Clear Button", + "VolumeType.title": "Select a Volume Type", + "FillColor.title": "Fill Color", + "TextColor.title": "Text Color", + "Background.title": "Change Playmat", + "Zoom.title": "Zoom In/Out", + "ThrowDice.title": "Throw the dice", + "Sensor.title": "Sensor", + "StopButton.title": "Stop", + "Fullscreen.title": "Fullscreen", + "Help.title": "Tutorial", + "SelectBackground":"Select Background", + "GreenHighFriction":"Green Board (High Friction)", + "DefaultMediumFriction":"Default (Medium Friction)", + "WoodLowFriction":"Wood (Low Friction)", + "Red":"Red", + "Green":"Green", + "Blue":"Blue", + "Transparency.title":"Transparent Volume", + "Number.title":"Numered Volume", + "Default.title":"Default Volume", + "ZoomIn.title":"Zoom in button", + "ZoomOut.title":"Zoom out button", + "ZoomEqual.title":"Zoom equal button", + "ZoomTo.title":"Zoom to button" + + + } diff --git a/activities/3DVolume.activity/locales/fr.json b/activities/3DVolume.activity/locales/fr.json index fb1c4d264..1f497785f 100644 --- a/activities/3DVolume.activity/locales/fr.json +++ b/activities/3DVolume.activity/locales/fr.json @@ -1,3 +1,65 @@ { - "VolumeActivity" : "vol act" -} \ No newline at end of file + "VolumeTitle": "Activité de Volume", + "VolumeIntro": "Bienvenue dans l'activité Volume. N'hésitez pas à zoomer et à faire pivoter le plateau selon vos préférences en utilisant des gestes tactiles ou simplement en cliquant et en faisant défiler.", + "AddTetrahedronsTitle": "Ajouter des Tétraèdres", + "AddTetrahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des tétraèdres à cet endroit. Le défaut est un tétraèdre solide sans chiffres.", + "AddCubesTitle": "Ajouter des Cubes", + "AddCubesIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des cubes à cet endroit. Le défaut est un cube solide sans chiffres.", + "AddOctahedronsTitle": "Ajouter des Octaèdres", + "AddOctahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des octaèdres à cet endroit. Le défaut est un octaèdre solide sans chiffres.", + "AddDecahedronsTitle": "Ajouter des Decaèdres", + "AddDecahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des decaèdres à cet endroit. Le défaut est un decaèdre solide sans chiffres.", + "AddDodecahedronTitle": "Ajouter des Dodécaèdres", + "AddDodecahedronIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des dodécaèdres à cet endroit. Le défaut est un dodécaèdre solide sans chiffres.", + "AddIcosahedronTitle": "Ajouter des Icosaèdres", + "AddIcosahedronIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des icosaèdres à cet endroit. Le défaut est un icosaèdre solide sans chiffres.", + "RemoveVolumesTitle": "Retirer des Volumes", + "RemoveVolumesIntro": "Sélectionnez ceci et cliquez sur n'importe quel volume sur le plateau pour le retirer.", + "SelectVolumeTypeTitle": "Sélectionner le Type de Volume", + "SelectVolumeTypeIntro": "Sélectionnez le type de volume que vous souhaitez ajouter au plateau.", + "ChangeVolumeColorTitle": "Changer la Couleur du Volume", + "ChangeVolumeColorIntro": "Choisissez l'une des couleurs pour vos volumes. La couleur par défaut est celle de votre compagnon.", + "ChangeVolumeTextColorTitle": "Changer la Couleur du Texte du Volume", + "ChangeVolumeTextColorIntro": "Choisissez l'une des couleurs pour le texte sur vos volumes. La couleur par défaut est celle du contour de votre compagnon.", + "ChangeBoardBackgroundTitle": "Changer le Fond du Plateau", + "ChangeBoardBackgroundIntro": "Choisissez l'un des trois fonds de plateau selon leurs frictions différentes.", + "ZoomInOutTitle": "Zoom Avant/Arrière", + "ZoomInOutIntro": "Utilisez la palette de zoom pour zoomer en avant ou en arrière.", + "ThrowButtonTitle": "Bouton Lancer", + "ThrowButtonIntro": "Utilisez ce bouton pour secouer les volumes sur le plateau.", + "RotateBoardTitle": "Faire Pivoter le Plateau", + "RotateBoardIntro": "Sélectionnez la flèche pointant dans la direction où vous souhaitez faire pivoter le plateau.", + "TutoNext": "Suivant", + "TutoPrev": "Précédent", + "AddTetra.title": "Ajouter Tétraèdre", + "AddCube.title": "Ajouter Cube", + "AddOcta.title": "Ajouter Octaèdre", + "AddDeca.title": "Ajouter Decaèdre", + "AddDodeca.title": "Ajouter Dodécaèdre", + "AddIcosa.title": "Ajouter Icosaèdre", + "Clear.title": "Bouton Effacer", + "VolumeType.title": "Sélectionner un Type de Volume", + "FillColor.title": "Couleur de Remplissage", + "TextColor.title": "Couleur du Texte", + "Background.title": "Changer le Tapis de Jeu", + "Zoom.title": "Zoom Avant/Arrière", + "ThrowDice.title": "Lancer les dés", + "Sensor.title": "Capteur", + "StopButton.title": "Arrêter", + "Fullscreen.title": "Plein écran", + "Help.title": "Tutoriel", + "SelectBackground": "Sélectionner le Fond", + "GreenHighFriction": "Plateau Vert (Haute Friction)", + "DefaultMediumFriction": "Défaut (Friction Moyenne)", + "WoodLowFriction": "Bois (Basse Friction)", + "Red": "Rouge", + "Green": "Vert", + "Blue": "Bleu", + "Transparency.title": "Volume Transparent", + "Number.title": "Volume Numéroté", + "Default.title": "Volume Par Défaut", + "ZoomIn.title": "Bouton Zoom Avant", + "ZoomOut.title": "Bouton Zoom Arrière", + "ZoomEqual.title": "Bouton Zoom Égal", + "ZoomTo.title": "Bouton Zoomer sur" +} From df92e9e0c27c8d718e12ea928487ea84ef486787 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 4 Aug 2024 00:22:24 +0530 Subject: [PATCH 38/60] changes --- activities/3DVolume.activity/js/activity.js | 20 +-- activities/3DVolume.activity/lib/l10n.js | 143 ++++++++++++-------- 2 files changed, 99 insertions(+), 64 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index f6d4a9980..a7336ab75 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -758,9 +758,9 @@ define([ } if (createFunction) { - let angVel1 = Math.random() * (4 - 0.1) + 0.1; - let angVel2 = Math.random() * (4 - 0.1) + 0.1; - let angVel3 = Math.random() * (4 - 0.1) + 0.1; + let angVel1 = Math.random() * (1 - 0.1) + 0.1; + let angVel2 = Math.random() * (1 - 0.1) + 0.1; + let angVel3 = Math.random() * (1 - 0.1) + 0.1; createFunction( null, null, @@ -1074,10 +1074,10 @@ define([ // If its in reverse. if (acceleration.y > 0) { if (acceleration.y < -2.8) { - world.gravity.set(3, 9.81, 0); // Gravity towards the left + world.gravity.set(3, -9.81, 0); // Gravity towards the left wakeAll(); } else { - world.gravity.set(9.81, 9.81, 0); // Gravity towards the left + world.gravity.set(9.81, -9.81, 0); // Gravity towards the left wakeAll(); } // left @@ -1086,26 +1086,26 @@ define([ if (acceleration.y > -1 && acceleration.y < 1) { if (acceleration.z > 6) { // front - world.gravity.set(0, 9.81, -9.81); // Gravity towards the front + world.gravity.set(0, -9.81, -9.81); // Gravity towards the front wakeAll(); } else { // straight - world.gravity.set(0, 9.81, 0); + world.gravity.set(0, -9.81, 0); wakeAll(); } } else { if (acceleration.y < -2.7) { - world.gravity.set(-3, 9.81, 0); // Gravity towards the right + world.gravity.set(-3, -9.81, 0); // Gravity towards the right wakeAll(); } else { - world.gravity.set(-9.81, 9.81, 0); // Gravity towards the right + world.gravity.set(-9.81, -9.81, 0); // Gravity towards the right wakeAll(); } // right } } else { // back - world.gravity.set(0, 9.81, 9.81); // Gravity towards the back + world.gravity.set(0, -9.81, 9.81); // Gravity towards the back wakeAll(); } } diff --git a/activities/3DVolume.activity/lib/l10n.js b/activities/3DVolume.activity/lib/l10n.js index 23f2e1bda..468ce3846 100644 --- a/activities/3DVolume.activity/lib/l10n.js +++ b/activities/3DVolume.activity/lib/l10n.js @@ -1,55 +1,90 @@ -define(['i18next.min', 'axios.min'], function (i18next, axios) { - const l10n = {language: {direction: "ltr"}}; - - l10n.init = async (lang) => { - await i18next.init({ - lng: lang, - debug: false, - fallbackLng: "en", - resources: {} - }).then(() => { - l10n.switchTo(lang); - }); - }; - - l10n.get = (key) => { - return i18next.t(key); - }; - - l10n.loadLanguageResource = (lang) => { - return new Promise((resolve, reject) => { - axios.get("./locales/" + lang + ".json").then((response) => { - resolve(response.data); - }).catch((error) => { - console.log("Failed to load " + lang + " language: " + error); - resolve(null); // Resolve with null to indicate failure - }); - }); - }; - - l10n.switchTo = (lang) => { - if (!i18next.hasResourceBundle(lang, "translation")) { - console.log("Loading " + lang + " language"); - l10n.loadLanguageResource(lang).then((locales) => { - if (locales !== null) { - i18next.addResourceBundle(lang, "translation", locales); - i18next.changeLanguage(lang); - triggerLocalizedEvent(); - } else { - l10n.init("en"); - } - }); - } else { - i18next.changeLanguage(lang); - triggerLocalizedEvent(); - } - }; - - - function triggerLocalizedEvent() { - const event = new Event("localized"); - window.dispatchEvent(event); - }; - - return l10n; +define(['https://sugarizer.org/js/i18next.min.js', 'https://sugarizer.org/js/axios.min.js'], function (i18next, axios) { + const l10n = {language: {direction: "ltr"}}; + + l10n.init = async (lang) => { + await i18next.init({ + lng: lang, + fallbackLng: "en", + resources: {} + }).then(() => { + l10n.language.direction = i18next.dir(); + l10n.switchTo(lang); + }); + }; + + l10n.get = (key, parameter, resource) => { + if (resource !== undefined) { + i18next.setDefaultNamespace(resource); + } else { + i18next.setDefaultNamespace("translation"); + } + return i18next.t(key, parameter); + }; + + l10n.loadLanguageResource = (lang) => { + return new Promise((resolve, reject) => { + let prefix = "https://sugarizer.org/locales/"; + if (location.protocol === "file:") { + prefix = "locales/"; + } + axios.get(prefix + lang + ".json").then((response) => { + resolve(response.data); + }).catch((error) => { + console.log("Failed to load " + lang + " language: " + error); + resolve(null); // Resolve with null to indicate failure + }); + }); + }; + + l10n.switchTo = (lang) => { + if (!i18next.hasResourceBundle(lang, "translation")) { + console.log("Loading " + lang + " language"); + l10n.loadLanguageResource(lang).then((locales) => { + if (locales !== null) { + i18next.addResourceBundle(lang, "translation", locales); + i18next.changeLanguage(lang); + triggerLocalizedEvent(); + } else { + l10n.init("en"); + } + }); + } else { + i18next.changeLanguage(lang); + triggerLocalizedEvent(); + } + }; + + l10n.loadExternalResource = (name, url) => { + return new Promise((resolve, reject) => { + axios.get(url+"/" + i18next.language + ".json").then((response) => { + i18next.addResourceBundle(i18next.language, name, response.data); + resolve(response.data); + }).catch((error) => { + console.log("Failed to load " + url + "/" + i18next.language + ".json " + error); + resolve(null); + }); + }); + }; + + l10n.getLanguage = () => { + return i18next.language; + }; + + l10n.updateDocument = () => { + const elements = document.getElementsByTagName("*"); + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + const key = element.getAttribute("data-i18n"); + if (key !== null) { + element.innerHTML = i18next.t(key); + } + } + }; + + function triggerLocalizedEvent() { + const event = new Event("localized"); + window.dispatchEvent(event); + }; + + return l10n; }); \ No newline at end of file From 6ddb66b59546d00f34436b175ec379289f162ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Lask=C3=A9?= Date: Sun, 4 Aug 2024 10:36:20 +0200 Subject: [PATCH 39/60] Fix localization --- .../3DVolume.activity/activity/activity.info | 2 +- activities/3DVolume.activity/js/activity.js | 3 + activities/3DVolume.activity/lib/l10n.js | 17 ++- activities/3DVolume.activity/lib/tutorial.js | 5 + activities/3DVolume.activity/locales/en.json | 117 +++++++++-------- activities/3DVolume.activity/locales/fr.json | 120 +++++++++--------- 6 files changed, 138 insertions(+), 126 deletions(-) diff --git a/activities/3DVolume.activity/activity/activity.info b/activities/3DVolume.activity/activity/activity.info index 688d98e42..19af2d93b 100644 --- a/activities/3DVolume.activity/activity/activity.info +++ b/activities/3DVolume.activity/activity/activity.info @@ -1,5 +1,5 @@ [Activity] -name = 3DVolume +name = 3D Volume activity_version = 1 bundle_id = org.sugarlabs.3DVolume exec = sugar-activity-web diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index a7336ab75..0f31ca7f5 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -120,6 +120,9 @@ define([ ? environment.user.language : defaultLanguage; l10n.init(language); + window.addEventListener('localized', function() { + l10n.updateDocument(); + }, false); ctx.presentColor = currentenv.user.colorvalue.fill != null diff --git a/activities/3DVolume.activity/lib/l10n.js b/activities/3DVolume.activity/lib/l10n.js index 468ce3846..c42cf2400 100644 --- a/activities/3DVolume.activity/lib/l10n.js +++ b/activities/3DVolume.activity/lib/l10n.js @@ -1,4 +1,4 @@ -define(['https://sugarizer.org/js/i18next.min.js', 'https://sugarizer.org/js/axios.min.js'], function (i18next, axios) { +define(['lib/i18next.min.js', 'lib/axios.min.js'], function (i18next, axios) { const l10n = {language: {direction: "ltr"}}; l10n.init = async (lang) => { @@ -23,7 +23,7 @@ define(['https://sugarizer.org/js/i18next.min.js', 'https://sugarizer.org/js/axi l10n.loadLanguageResource = (lang) => { return new Promise((resolve, reject) => { - let prefix = "https://sugarizer.org/locales/"; + let prefix = "locales/"; if (location.protocol === "file:") { prefix = "locales/"; } @@ -73,11 +73,14 @@ define(['https://sugarizer.org/js/i18next.min.js', 'https://sugarizer.org/js/axi l10n.updateDocument = () => { const elements = document.getElementsByTagName("*"); for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - const key = element.getAttribute("data-i18n"); - if (key !== null) { - element.innerHTML = i18next.t(key); - } + const element = elements[i]; + const key = element.getAttribute("data-i18n"); + if (key !== null && i18next.exists(key)) { + element.innerHTML = i18next.t(key); + } + if (key !== null && i18next.exists(key+".title")) { + element.setAttribute('title', i18next.t(key+".title")); + } } }; diff --git a/activities/3DVolume.activity/lib/tutorial.js b/activities/3DVolume.activity/lib/tutorial.js index c40ace853..a366bb4dc 100644 --- a/activities/3DVolume.activity/lib/tutorial.js +++ b/activities/3DVolume.activity/lib/tutorial.js @@ -73,6 +73,11 @@ define(["l10n"], function (l10n) { title: l10n.get("ThrowButtonTitle"), intro: l10n.get("ThrowButtonIntro"), }, + { + element: "#sensor-button", + title: l10n.get("SensorButtonTitle"), + intro: l10n.get("SensorButtonIntro"), + }, { element: ".arrow-container", title: l10n.get("RotateBoardTitle"), diff --git a/activities/3DVolume.activity/locales/en.json b/activities/3DVolume.activity/locales/en.json index 0090e2c7a..b09469a6c 100644 --- a/activities/3DVolume.activity/locales/en.json +++ b/activities/3DVolume.activity/locales/en.json @@ -1,68 +1,67 @@ { - "VolumeTitle": "Volume Activity", - "VolumeIntro": "Welcome into the Volume activity. Feel free to zoom in and rotate the board to your preference using touch gestures or simply by clicking and scrolling.", - "AddTetrahedronsTitle": "Add Tetrahedrons", - "AddTetrahedronsIntro": "Select this and click anywhere on the board to add tetrahedrons to that position. The default is a solid tetrahedron without numbers.", - "AddCubesTitle": "Add Cubes", - "AddCubesIntro": "Select this and click anywhere on the board to add cubes to that position. The default is a solid cube without numbers.", - "AddOctahedronsTitle": "Add Octahedrons", - "AddOctahedronsIntro": "Select this and click anywhere on the board to add octahedrons to that position. The default is a solid octahedron without numbers.", - "AddDecahedronsTitle": "Add Decahedrons", - "AddDecahedronsIntro": "Select this and click anywhere on the board to add decahedrons to that position. The default is a solid decahedron without numbers.", - "AddDodecahedronTitle": "Add Dodecahedron", - "AddDodecahedronIntro": "Select this and click anywhere on the board to add dodecahedron to that position. The default is a solid dodecahedrons without numbers.", - "AddIcosahedronTitle": "Add Icosahedron", - "AddIcosahedronIntro": "Select this and click anywhere on the board to add icosahedron to that position. The default is a solid icosahedron without numbers.", - "RemoveVolumesTitle": "Remove Volumes", - "RemoveVolumesIntro": "Select this and click on any volume on the board to remove it.", - "SelectVolumeTypeTitle": "Select Volume Type", - "SelectVolumeTypeIntro": "Select the type of volume you want to add to the board.", - "ChangeVolumeColorTitle": "Change Volume Color", - "ChangeVolumeColorIntro": "Choose any one of the colors for your volumes. The default is your buddy color.", - "ChangeVolumeTextColorTitle": "Change Volume Text Color", - "ChangeVolumeTextColorIntro": "Choose any one of the colors for the text on your volumes. The default is your buddy stroke color.", - "ChangeBoardBackgroundTitle": "Change Board Background", - "ChangeBoardBackgroundIntro": "Choose any one of the three boards backgrounds according to their different frictions.", - "ZoomInOutTitle": "Zoom In/Out", - "ZoomInOutIntro": "Use the zoom palette to zoom in or out.", - "ThrowButtonTitle": "Throw Button", - "ThrowButtonIntro": "Use this button to shake the volumes on the board.", - "RotateBoardTitle": "Rotate the Board", - "RotateBoardIntro": "Select the arrow pointing in the direction you wish the board to rotate.", + "VolumeTitle": "3D Volume Activity", + "VolumeIntro": "Welcome to the 3D Volume activity. Explore the world of volumes with this interactive simulator! Add and remove volumes of different shapes and colors, transform them into dice, rotate, zoom, and shake the board. And share the fun and learning in real-time with friends!", + "AddTetrahedronsTitle": "Tetrahedron", + "AddTetrahedronsIntro": "Click here to switch to Tetrahedron mode. The next volume added will be a regular Tetrahedron (4 faces, 6 edges, 4 vertices).", + "AddCubesTitle": "Cube", + "AddCubesIntro": "Click here to switch to Cube mode. The next volume added will be a Cube (6 faces, 12 edges, 8 vertices).", + "AddOctahedronsTitle": "Octahedron", + "AddOctahedronsIntro": "Click here to switch to Octahedron mode. The next volume added will be a regular Octahedron (8 faces, 12 edges, 6 vertices).", + "AddDecahedronsTitle": "Decahedron", + "AddDecahedronsIntro": "Click here to switch to Decahedron mode. The next volume added will be an irregular Decahedron (10 faces, 20 edges, 12 vertices).", + "AddDodecahedronTitle": "Dodecahedron", + "AddDodecahedronIntro": "Click here to switch to Dodecahedron mode. The next volume added will be a regular Dodecahedron (12 faces, 30 edges, 20 vertices).", + "AddIcosahedronTitle": "Icosahedron", + "AddIcosahedronIntro": "Click here to switch to Icosahedron mode. The next volume added will be a regular Icosahedron (20 faces, 30 edges, 12 vertices).", + "RemoveVolumesTitle": "Remove", + "RemoveVolumesIntro": "Click here to switch to removal mode. In this mode, click on any volume to remove it.", + "SelectVolumeTypeTitle": "Volume Type", + "SelectVolumeTypeIntro": "Select the volume type: transparent, solid (default), or dice.", + "ChangeVolumeColorTitle": "Volume Color", + "ChangeVolumeColorIntro": "Choose the color of the next volume.", + "ChangeVolumeTextColorTitle": "Text Color", + "ChangeVolumeTextColorIntro": "If you are in dice mode, choose the color of the numbers.", + "ChangeBoardBackgroundTitle": "Board", + "ChangeBoardBackgroundIntro": "Choose one of the three possible boards based on their different friction resistances.", + "ZoomInOutTitle": "Zoom", + "ZoomInOutIntro": "Use these icons to adjust the zoom level.", + "ThrowButtonTitle": "Shake", + "ThrowButtonIntro": "Use this button to shake the board. Beware of the avalanche of volumes!", + "SensorButtonTitle": "Sensor", + "SensorButtonIntro": "When this icon is active, your device supports gravity, you can simply rotate your device to change the direction of gravity.", + "RotateBoardTitle": "Orientation", + "RotateBoardIntro": "Select the arrow pointing in the direction you want to rotate the board.", "TutoNext": "Next", "TutoPrev": "Prev", - "AddTetra.title": "Add Tetrahedron", - "AddCube.title": "Add Cube", - "AddOcta.title": "Add Octahedron", - "AddDeca.title": "Add Decahedron", - "AddDodeca.title": "Add Dodecahedron", - "AddIcosa.title": "Add Icosahedron", - "Clear.title": "Clear Button", - "VolumeType.title": "Select a Volume Type", + "AddTetra.title": "Tetrahedron", + "AddCube.title": "Cube", + "AddOcta.title": "Octahedron", + "AddDeca.title": "Decahedron", + "AddDodeca.title": "Dodecahedron", + "AddIcosa.title": "Icosahedron", + "Clear.title": "Remove", + "VolumeType.title": "Volume Type", "FillColor.title": "Fill Color", "TextColor.title": "Text Color", - "Background.title": "Change Playmat", - "Zoom.title": "Zoom In/Out", - "ThrowDice.title": "Throw the dice", + "Background.title": "Change Board", + "Zoom.title": "Zoom", + "ThrowDice.title": "Shake", "Sensor.title": "Sensor", "StopButton.title": "Stop", "Fullscreen.title": "Fullscreen", "Help.title": "Tutorial", - "SelectBackground":"Select Background", - "GreenHighFriction":"Green Board (High Friction)", - "DefaultMediumFriction":"Default (Medium Friction)", - "WoodLowFriction":"Wood (Low Friction)", - "Red":"Red", - "Green":"Green", - "Blue":"Blue", - "Transparency.title":"Transparent Volume", - "Number.title":"Numered Volume", - "Default.title":"Default Volume", - "ZoomIn.title":"Zoom in button", - "ZoomOut.title":"Zoom out button", - "ZoomEqual.title":"Zoom equal button", - "ZoomTo.title":"Zoom to button" - - - -} + "SelectBackground": "Select Board", + "GreenHighFriction": "Grass (high friction)", + "DefaultMediumFriction": "Default (medium friction)", + "WoodLowFriction": "Wood (low friction)", + "Red": "Red", + "Green": "Green", + "Blue": "Blue", + "Transparency.title": "Transparent", + "Number.title": "Dice", + "Default.title": "Solid", + "ZoomIn.title": "Zoom In", + "ZoomOut.title": "Zoom Out", + "ZoomEqual.title": "Default Zoom", + "ZoomTo.title": "Fit Zoom" +} \ No newline at end of file diff --git a/activities/3DVolume.activity/locales/fr.json b/activities/3DVolume.activity/locales/fr.json index 1f497785f..f114fc685 100644 --- a/activities/3DVolume.activity/locales/fr.json +++ b/activities/3DVolume.activity/locales/fr.json @@ -1,65 +1,67 @@ { - "VolumeTitle": "Activité de Volume", - "VolumeIntro": "Bienvenue dans l'activité Volume. N'hésitez pas à zoomer et à faire pivoter le plateau selon vos préférences en utilisant des gestes tactiles ou simplement en cliquant et en faisant défiler.", - "AddTetrahedronsTitle": "Ajouter des Tétraèdres", - "AddTetrahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des tétraèdres à cet endroit. Le défaut est un tétraèdre solide sans chiffres.", - "AddCubesTitle": "Ajouter des Cubes", - "AddCubesIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des cubes à cet endroit. Le défaut est un cube solide sans chiffres.", - "AddOctahedronsTitle": "Ajouter des Octaèdres", - "AddOctahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des octaèdres à cet endroit. Le défaut est un octaèdre solide sans chiffres.", - "AddDecahedronsTitle": "Ajouter des Decaèdres", - "AddDecahedronsIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des decaèdres à cet endroit. Le défaut est un decaèdre solide sans chiffres.", - "AddDodecahedronTitle": "Ajouter des Dodécaèdres", - "AddDodecahedronIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des dodécaèdres à cet endroit. Le défaut est un dodécaèdre solide sans chiffres.", - "AddIcosahedronTitle": "Ajouter des Icosaèdres", - "AddIcosahedronIntro": "Sélectionnez ceci et cliquez n'importe où sur le plateau pour ajouter des icosaèdres à cet endroit. Le défaut est un icosaèdre solide sans chiffres.", - "RemoveVolumesTitle": "Retirer des Volumes", - "RemoveVolumesIntro": "Sélectionnez ceci et cliquez sur n'importe quel volume sur le plateau pour le retirer.", - "SelectVolumeTypeTitle": "Sélectionner le Type de Volume", - "SelectVolumeTypeIntro": "Sélectionnez le type de volume que vous souhaitez ajouter au plateau.", - "ChangeVolumeColorTitle": "Changer la Couleur du Volume", - "ChangeVolumeColorIntro": "Choisissez l'une des couleurs pour vos volumes. La couleur par défaut est celle de votre compagnon.", - "ChangeVolumeTextColorTitle": "Changer la Couleur du Texte du Volume", - "ChangeVolumeTextColorIntro": "Choisissez l'une des couleurs pour le texte sur vos volumes. La couleur par défaut est celle du contour de votre compagnon.", - "ChangeBoardBackgroundTitle": "Changer le Fond du Plateau", - "ChangeBoardBackgroundIntro": "Choisissez l'un des trois fonds de plateau selon leurs frictions différentes.", - "ZoomInOutTitle": "Zoom Avant/Arrière", - "ZoomInOutIntro": "Utilisez la palette de zoom pour zoomer en avant ou en arrière.", - "ThrowButtonTitle": "Bouton Lancer", - "ThrowButtonIntro": "Utilisez ce bouton pour secouer les volumes sur le plateau.", - "RotateBoardTitle": "Faire Pivoter le Plateau", - "RotateBoardIntro": "Sélectionnez la flèche pointant dans la direction où vous souhaitez faire pivoter le plateau.", - "TutoNext": "Suivant", - "TutoPrev": "Précédent", - "AddTetra.title": "Ajouter Tétraèdre", - "AddCube.title": "Ajouter Cube", - "AddOcta.title": "Ajouter Octaèdre", - "AddDeca.title": "Ajouter Decaèdre", - "AddDodeca.title": "Ajouter Dodécaèdre", - "AddIcosa.title": "Ajouter Icosaèdre", - "Clear.title": "Bouton Effacer", - "VolumeType.title": "Sélectionner un Type de Volume", - "FillColor.title": "Couleur de Remplissage", - "TextColor.title": "Couleur du Texte", - "Background.title": "Changer le Tapis de Jeu", - "Zoom.title": "Zoom Avant/Arrière", - "ThrowDice.title": "Lancer les dés", + "VolumeTitle": "Activité 3D Volume", + "VolumeIntro": "Bienvenue dans l'activité 3D Volume. Explorez le monde des volumes avec ce simulateur interactif ! Ajoutez et retirez des volumes de différentes formes et couleurs, transformez-les en dés, faites pivoter, zoomez, et secouez le plateau. Et partagez le plaisir et l'apprentissage en temps réel avec des amis !", + "AddTetrahedronsTitle": "Tétraèdre", + "AddTetrahedronsIntro": "Cliquez ici pour passer en mode Tétraèdre. Le prochain volume ajouté sera un Tétraèdre régulier (4 faces, 6 arêtes, 4 sommets).", + "AddCubesTitle": "Cube", + "AddCubesIntro": "Cliquez ici pour passer en mode Cube. Le prochain volume ajouté sera un Cube (6 faces, 12 arêtes, 8 sommets).", + "AddOctahedronsTitle": "Octaèdre", + "AddOctahedronsIntro": "Cliquez ici pour passer en mode Octaèdre. Le prochain volume ajouté sera un Octaèdre régulier (8 faces, 12 arêtes, 6 sommets).", + "AddDecahedronsTitle": "Décaèdre", + "AddDecahedronsIntro": "Cliquez ici pour passer en mode Décaèdre. Le prochain volume ajouté sera un Décaèdre irrégulier (10 faces, 20 arêtes, 12 sommets).", + "AddDodecahedronTitle": "Dodécaèdre", + "AddDodecahedronIntro": "Cliquez ici pour passer en mode Dodécaèdre. Le prochain volume ajouté sera un Dodécaèdre régulier (12 faces, 30 arêtes, 20 sommets).", + "AddIcosahedronTitle": "Icosaèdre", + "AddIcosahedronIntro": "Cliquez ici pour passer en mode Icosaèdre. Le prochain volume ajouté sera un Icosaèdre régulier (20 faces, 30 arêtes, 12 sommets).", + "RemoveVolumesTitle": "Supprimer", + "RemoveVolumesIntro": "Cliquez ici pour passer en mode suppression. Dans ce mode, cliquez sur n'importe quel volume pour le supprimer.", + "SelectVolumeTypeTitle": "Type de volume", + "SelectVolumeTypeIntro": "Sélectionnez le type de volume: transparent, plein (par défaut) ou dé.", + "ChangeVolumeColorTitle": "Couleur volume", + "ChangeVolumeColorIntro": "Choisissez la couleur du prochain volume.", + "ChangeVolumeTextColorTitle": "Couleur texte", + "ChangeVolumeTextColorIntro": "Si vous êtes en mode Dé, choisissez la couleur des numéros.", + "ChangeBoardBackgroundTitle": "Plateau", + "ChangeBoardBackgroundIntro": "Choisissez l'un des trois plateaux possibles en fonction de leurs différentes résistances aux frottements.", + "ZoomInOutTitle": "Zoom", + "ZoomInOutIntro": "Utilisez ces icônes pour adapter le niveau de zoom.", + "ThrowButtonTitle": "Secouer", + "ThrowButtonIntro": "Utilisez ce bouton pour secouer le plateau. Attention à l'avalanche de volumes !", + "SensorButtonTitle": "Capteur", + "SensorButtonIntro": "Quand cet icône est actif, votre machine supporte la gravité, vous pouvez simplement tourner votre machine pour changer la direction de la gravité.", + "RotateBoardTitle": "Orientation", + "RotateBoardIntro": "Sélectionnez la flèche pointant dans la direction dans laquelle vous souhaitez faire tourner le plateau.", + "TutoNext": "Suiv", + "TutoPrev": "Préc", + "AddTetra.title": "Tétraèdre", + "AddCube.title": "Cube", + "AddOcta.title": "Octaèdre", + "AddDeca.title": "Décaèdre", + "AddDodeca.title": "Dodécaèdre", + "AddIcosa.title": "Icosaèdre", + "Clear.title": "Supprimer", + "VolumeType.title": "Type de volume", + "FillColor.title": "Couleur remplissage", + "TextColor.title": "Couleur texte", + "Background.title": "Changer plateau", + "Zoom.title": "Zoom", + "ThrowDice.title": "Secouer", "Sensor.title": "Capteur", "StopButton.title": "Arrêter", "Fullscreen.title": "Plein écran", "Help.title": "Tutoriel", - "SelectBackground": "Sélectionner le Fond", - "GreenHighFriction": "Plateau Vert (Haute Friction)", - "DefaultMediumFriction": "Défaut (Friction Moyenne)", - "WoodLowFriction": "Bois (Basse Friction)", - "Red": "Rouge", - "Green": "Vert", - "Blue": "Bleu", - "Transparency.title": "Volume Transparent", - "Number.title": "Volume Numéroté", - "Default.title": "Volume Par Défaut", - "ZoomIn.title": "Bouton Zoom Avant", - "ZoomOut.title": "Bouton Zoom Arrière", - "ZoomEqual.title": "Bouton Zoom Égal", - "ZoomTo.title": "Bouton Zoomer sur" + "SelectBackground":"Sélectionner le plateau", + "GreenHighFriction":"Herbe (haute friction)", + "DefaultMediumFriction":"Défaut (friction moyenne)", + "WoodLowFriction":"Bois (faible friction)", + "Red":"Rouge", + "Green":"Vert", + "Blue":"Bleu", + "Transparency.title":"Transparent", + "Number.title":"Dé", + "Default.title":"Plein", + "ZoomIn.title":"Zoom avant", + "ZoomOut.title":"Zoom arrière", + "ZoomEqual.title":"Zoom par défaut", + "ZoomTo.title":"Zoom englobant" } From b53a0d877befc2a0c56b6e2b0e9518c4320a39b7 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 18 Aug 2024 00:06:01 +0530 Subject: [PATCH 40/60] positions fixed --- activities/3DVolume.activity/js/Cube.js | 46 ++- activities/3DVolume.activity/js/Tetra.js | 6 +- activities/3DVolume.activity/js/activity.js | 351 +++++++++++++++----- 3 files changed, 305 insertions(+), 98 deletions(-) diff --git a/activities/3DVolume.activity/js/Cube.js b/activities/3DVolume.activity/js/Cube.js index 8456a4a65..72bc6d63e 100644 --- a/activities/3DVolume.activity/js/Cube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -106,9 +106,29 @@ function createCube( let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let vertices = [ + [-1, -1, -1], + [1, -1, -1], + [1, 1, -1], + [-1, 1, -1], + [-1, -1, 1], + [1, -1, 1], + [1, 1, 1], + [-1, 1, 1], + ]; + + let faces = [ + [0, 3, 2, 1, 1], + [1, 2, 6, 5, 2], + [0, 1, 5, 4, 3], + [3, 7, 6, 2, 4], + [0, 4, 7, 3, 5], + [4, 5, 6, 7, 6], + ]; + const boxBody = new CANNON.Body({ mass: 1, - shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1)), + shape: createCubeShape(vertices, faces, 1), position: new CANNON.Vec3(x, y, z), material: new CANNON.Material(), restitution: 5, @@ -116,11 +136,11 @@ function createCube( world.addBody(boxBody); let angVel1 = - sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; + sharedAngVel1 == null ? Math.random() * (4 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = - sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; + sharedAngVel2 == null ? Math.random() * (4 - 0.1) + 0.1 : sharedAngVel2; let angVel3 = - sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; + sharedAngVel3 == null ? Math.random() * (4 - 0.1) + 0.1 : sharedAngVel3; console.log(angVel1); console.log(angVel2); @@ -194,3 +214,21 @@ function getCubeScore(scoresObject, body, ifRemove) { } } } + +function createCubeShape(vertices, faces, radius) { + let cv = new Array(vertices.length), + cf = new Array(faces.length); + + // Convert vertices to CANNON.Vec3 + for (let i = 0; i < vertices.length; ++i) { + let v = vertices[i]; + cv[i] = new CANNON.Vec3(v[0] * radius, v[1] * radius, v[2] * radius); + } + + // Extract vertex indices from faces, ignoring the last number + for (let i = 0; i < faces.length; ++i) { + cf[i] = faces[i].slice(0, faces[i].length - 1); + } + + return new CANNON.ConvexPolyhedron({ vertices: cv, faces: cf }); +} diff --git a/activities/3DVolume.activity/js/Tetra.js b/activities/3DVolume.activity/js/Tetra.js index 28d035938..8ed98908f 100644 --- a/activities/3DVolume.activity/js/Tetra.js +++ b/activities/3DVolume.activity/js/Tetra.js @@ -163,11 +163,11 @@ function createTetrahedron( }); world.addBody(tetrahedronBody); let angVel1 = - sharedAngVel1 == null ? Math.random() * (3 - 0.1) + 0.1 : sharedAngVel1; + sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = - sharedAngVel2 == null ? Math.random() * (3 - 0.1) + 0.1 : sharedAngVel2; + sharedAngVel2 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel2; let angVel3 = - sharedAngVel3 == null ? Math.random() * (3 - 0.1) + 0.1 : sharedAngVel3; + sharedAngVel3 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel3; const contactMat = new CANNON.ContactMaterial( groundPhysMat, diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 0f31ca7f5..ab8b59389 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -120,9 +120,13 @@ define([ ? environment.user.language : defaultLanguage; l10n.init(language); - window.addEventListener('localized', function() { - l10n.updateDocument(); - }, false); + window.addEventListener( + "localized", + function () { + l10n.updateDocument(); + }, + false + ); ctx.presentColor = currentenv.user.colorvalue.fill != null @@ -224,17 +228,21 @@ define([ ); } } - console.log(diceArray); } - if (msg.action == "throw") { - throwDice(msg.content[0], msg.content[1]); + + if (msg.action == "positions") { + copyPositions(msg.content); } + // if (msg.action == "throw") { + // copyPosition(); + // throwDice(msg.content[0], msg.content[1]); + // } if (msg.action == "changeBg") { changeBoardBackground(msg.content); } if (msg.action == "remove") { // This starts a ray from the top of the scene and that ray intersects objects. - raycaster.setFromCamera(msg.content, camera); + raycaster.setFromCamera(msg.content[0], camera); var intersects = raycaster.intersectObjects(scene.children); // The object that is intersected first is the object that needs to be removed. @@ -242,6 +250,9 @@ define([ // If the first object is the board do not let it be removed if (intersectedObject?.geometry.type == "PlaneGeometry") { + if (msg.content[1] != null) { + remove(null, msg.content[1]); + } return; } remove(intersectedObject); @@ -709,6 +720,7 @@ define([ }; function onAddClick(event) { + throwingDice = true; // This will be false only when tutorial is running. if (!ifAdding.adding) { return; @@ -761,9 +773,9 @@ define([ } if (createFunction) { - let angVel1 = Math.random() * (1 - 0.1) + 0.1; - let angVel2 = Math.random() * (1 - 0.1) + 0.1; - let angVel3 = Math.random() * (1 - 0.1) + 0.1; + let angVel1 = Math.random() * (3 - 0.1) + 0.1; + let angVel2 = Math.random() * (3 - 0.1) + 0.1; + let angVel3 = Math.random() * (3 - 0.1) + 0.1; createFunction( null, null, @@ -834,20 +846,27 @@ define([ if (intersectedObject?.geometry.type == "PlaneGeometry") { return; } + let index = null; + for (let i = 0; i < diceArray.length; i++) { + if (intersectedObject == diceArray[i][0]) { + index = i; + break; + } + } // Removing the volume for other users as well. if (presence) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "remove", - content: mouse, // Send the point which the user is clicking on. + content: [mouse, index], // Send the point which the user is clicking on. }); } remove(intersectedObject); } } - function remove(intersectedObject) { + function remove(intersectedObject, index) { let num = 0; for (let i = 0; i < diceArray.length; i++) { if (diceArray[i][3]) { @@ -855,85 +874,168 @@ define([ } } // Find the volume being clicked within the diceArray to remove it. - for (let i = 0; i < diceArray.length; i++) { - if (diceArray[i][0] == intersectedObject) { - if (diceArray[i][3]) { - // If the volume being removed is a numbered volume then get the number on top of the volume and remove it from the score. - let score; - switch (diceArray[i][2]) { - case "cube": - score = getCubeScore( - scoresObject, - diceArray[i][0], - true - ); - break; - case "icosa": - score = getIcosaScore( - scoresObject, - diceArray[i][0], - true - ); - break; - case "deca": - score = getDecaScore( - scoresObject, - diceArray[i][0], - true - ); - break; - case "dodeca": - score = getDodecaScore( - scoresObject, - diceArray[i][0], - true - ); - break; - case "octa": - score = getOctaScore( - scoresObject, - diceArray[i][0], - true - ); - break; - case "tetra": - score = getTetraScore( - scoresObject, - diceArray[i][0], - true - ); - break; - default: - console.log(`Unknown type: ${diceArray[i][3]}`); - continue; - } + if (intersectedObject == null) { + if (index < diceArray.length && diceArray[index][3]) { + // If the volume being removed is a numbered volume then get the number on top of the volume and remove it from the score. + let score; + switch (diceArray[index][2]) { + case "cube": + score = getCubeScore( + scoresObject, + diceArray[index][0], + true + ); + break; + case "icosa": + score = getIcosaScore( + scoresObject, + diceArray[index][0], + true + ); + break; + case "deca": + score = getDecaScore( + scoresObject, + diceArray[index][0], + true + ); + break; + case "dodeca": + score = getDodecaScore( + scoresObject, + diceArray[index][0], + true + ); + break; + case "octa": + score = getOctaScore( + scoresObject, + diceArray[index][0], + true + ); + break; + case "tetra": + score = getTetraScore( + scoresObject, + diceArray[index][0], + true + ); + break; + default: + console.log(`Unknown type: ${diceArray[index][3]}`); + } - scoresObject.presentScore = - scoresObject.presentScore - score; - console.log(scoresObject.presentScore); + scoresObject.presentScore = + scoresObject.presentScore - score; + console.log(scoresObject.presentScore); - let scoresArray = scoresObject.lastRoll.split(" + "); + let scoresArray = scoresObject.lastRoll.split(" + "); - // Find the index of the first occurrence of the score to remove - let indexToRemove = scoresArray.indexOf( - score.toString() - ); + // Find the index of the first occurrence of the score to remove + let indexToRemove = scoresArray.indexOf(score.toString()); - // If the score is found, remove it - if (indexToRemove !== -1) { - scoresArray.splice(indexToRemove, 1); - } + // If the score is found, remove it + if (indexToRemove !== -1) { + scoresArray.splice(indexToRemove, 1); + } + + // Join the remaining scores back into a string + scoresObject.lastRoll = scoresArray.join(" + "); + updateElements(); + console.log(scoresObject.lastRoll); + console.log(scoresObject.presentScore); + num--; + } + console.log(diceArray); + console.log(diceArray[index][1]); + console.log(diceArray[index][0]); + world.removeBody(diceArray[index][1]); + scene.remove(diceArray[index][0]); + diceArray.splice(index, 1); + } else { + for (let i = 0; i < diceArray.length; i++) { + if (diceArray[i][0] == intersectedObject) { + if (diceArray[i][3]) { + // If the volume being removed is a numbered volume then get the number on top of the volume and remove it from the score. + let score; + switch (diceArray[i][2]) { + case "cube": + score = getCubeScore( + scoresObject, + diceArray[i][0], + true + ); + break; + case "icosa": + score = getIcosaScore( + scoresObject, + diceArray[i][0], + true + ); + break; + case "deca": + score = getDecaScore( + scoresObject, + diceArray[i][0], + true + ); + break; + case "dodeca": + score = getDodecaScore( + scoresObject, + diceArray[i][0], + true + ); + break; + case "octa": + score = getOctaScore( + scoresObject, + diceArray[i][0], + true + ); + break; + case "tetra": + score = getTetraScore( + scoresObject, + diceArray[i][0], + true + ); + break; + default: + console.log( + `Unknown type: ${diceArray[i][3]}` + ); + continue; + } + + scoresObject.presentScore = + scoresObject.presentScore - score; + console.log(scoresObject.presentScore); - // Join the remaining scores back into a string - scoresObject.lastRoll = scoresArray.join(" + "); - updateElements(); - console.log(scoresObject.lastRoll); - console.log(scoresObject.presentScore); - num--; + let scoresArray = + scoresObject.lastRoll.split(" + "); + + // Find the index of the first occurrence of the score to remove + let indexToRemove = scoresArray.indexOf( + score.toString() + ); + + // If the score is found, remove it + if (indexToRemove !== -1) { + scoresArray.splice(indexToRemove, 1); + } + + // Join the remaining scores back into a string + scoresObject.lastRoll = scoresArray.join(" + "); + updateElements(); + console.log(scoresObject.lastRoll); + console.log(scoresObject.presentScore); + num--; + } + world.removeBody(diceArray[i][1]); + scene.remove(diceArray[i][0]); + diceArray.splice(i, 1); } - world.removeBody(diceArray[i][1]); - scene.remove(diceArray[i][0]); - diceArray.splice(i, 1); } } if (num == 0) { @@ -984,14 +1086,18 @@ define([ diceArray[i][9], ]); } - console.log(diceArray); presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: "init", content: [presenceDiceArray, presentBackground], // sends the diceArray and the present background of the user to the users which are joining }); + setTimeout(sendPositions, 4500); + + }; + + renderer.setSize(window.innerWidth, window.innerHeight); const canvas = document.getElementById("game-container"); document @@ -1215,7 +1321,7 @@ define([ material: groundPhysMat, }); topWall.quaternion.setFromEuler(-Math.PI / 2, 0, 0); - topWall.position.set(0, 12, 0); + topWall.position.set(0, 15, 0); world.addBody(topWall); @@ -1364,7 +1470,32 @@ define([ // This function handles the tossing of the volumes. + // function throwDice(sharedOffset, sharedRolling) { + // for (let i = 0; i < diceArray.length; i++) { + // scene.remove(diceArray[i][0]); + // world.removeBody(diceArray[i][1]); + // } + // if (diceArray.length > 0) { + // scoresObject.lastRoll = ""; + // scoresObject.presentScore = 0; + // for (let i = 0; i < diceArray.length; i++) { + // diceArray[i][1].angularVelocity.set( + // diceArray[i][7], + // diceArray[i][8], + // diceArray[i][9] + // ); + // diceArray[i][1].applyImpulse(ctx.offset, ctx.rollingForce); + // diceArray[i][1].position.set(0, 10, 0); + // } + // for (let i = 0; i < diceArray.length; i++) { + // scene.add(diceArray[i][0]); + // world.addBody(diceArray[i][1]); + // } + // } + // console.log(diceArray); + // } function throwDice(sharedOffset, sharedRolling) { + throwingDice = true; for (let i = 0; i < diceArray.length; i++) { scene.remove(diceArray[i][0]); world.removeBody(diceArray[i][1]); @@ -1485,6 +1616,40 @@ define([ } } + function sendPositions() { + let dicePositions = []; + for (let i = 0; i < diceArray.length; i++) { + dicePositions.push([ + diceArray[i][1].position, + diceArray[i][1].quaternion, + ]); + } + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "positions", + content: dicePositions, + }); + } + + function copyPositions(positions) { + console.log("copying positions now") + for (let i = 0; i < diceArray.length; i++) { + scene.remove(diceArray[i][0]); + world.removeBody(diceArray[i][1]); + } + for (let i = 0; i < diceArray.length; i++) { + diceArray[i][0].position.copy(positions[i][0]); + diceArray[i][1].position.copy(positions[i][0]); + diceArray[i][1].quaternion.copy(positions[i][1]); + diceArray[i][1].quaternion.copy(positions[i][1]); + } + for (let i = 0; i < diceArray.length; i++) { + scene.add(diceArray[i][0]); + world.addBody(diceArray[i][1]); + } + awake = true; + } + // Leaving this here so that in the future contributors find it easier to debug the cannon-es physical world. Go to the animate function and uncomment the cannonDebugger line to view the physical world. const cannonDebugger = new CannonDebugger(scene, world, { @@ -1492,8 +1657,7 @@ define([ }); let awake = false; - let time = 20; - let timeStep = 1 / time; + let throwingDice = false; animate(); @@ -1513,7 +1677,12 @@ define([ if (world.hasActiveBodies == false && awake == true) { awake = false; console.log("the world is going to sleep now bye bye"); - for (let i = 0; i < diceArray.length; i++) {} + if (throwingDice) { + throwingDice = false; + if (presence) { + sendPositions(); + } + } getScores(); } if (world.hasActiveBodies == true) { @@ -1525,7 +1694,7 @@ define([ renderer.setAnimationLoop(animate); - const fixedTimeStep = 1 / 60; + const fixedTimeStep = 1 / 40; const maxSubSteps = 3; function updatePhysics() { From 3ed742366a57a73c46083d026ecdd62ba04967ce Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 25 Aug 2024 13:37:59 +0530 Subject: [PATCH 41/60] changes --- activities/3DVolume.activity/js/Cube.js | 6 +- activities/3DVolume.activity/js/Dodeca.js | 3 - activities/3DVolume.activity/js/activity.js | 157 +++++++++++--------- 3 files changed, 85 insertions(+), 81 deletions(-) diff --git a/activities/3DVolume.activity/js/Cube.js b/activities/3DVolume.activity/js/Cube.js index 72bc6d63e..9bba8fc63 100644 --- a/activities/3DVolume.activity/js/Cube.js +++ b/activities/3DVolume.activity/js/Cube.js @@ -141,10 +141,7 @@ function createCube( sharedAngVel2 == null ? Math.random() * (4 - 0.1) + 0.1 : sharedAngVel2; let angVel3 = sharedAngVel3 == null ? Math.random() * (4 - 0.1) + 0.1 : sharedAngVel3; - console.log(angVel1); - console.log(angVel2); - - console.log(angVel3); + boxBody.angularVelocity.set(angVel1, angVel2, angVel3); boxBody.angularDamping = 0.1; // This will help in reducing rotation over time boxBody.applyImpulse(ctx.offset, ctx.rollingForce); @@ -160,7 +157,6 @@ function createCube( boxMesh.quaternion.copy(quaternionShared); boxBody.quaternion.copy(quaternionShared); } - console.log(boxBody.material); diceArray.push([ boxMesh, boxBody, diff --git a/activities/3DVolume.activity/js/Dodeca.js b/activities/3DVolume.activity/js/Dodeca.js index 40c85cc61..ba7b4319a 100644 --- a/activities/3DVolume.activity/js/Dodeca.js +++ b/activities/3DVolume.activity/js/Dodeca.js @@ -201,7 +201,6 @@ function createDodecahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; let y = yCoordinateShared == null ? 4 : yCoordinateShared + 1; // Ensure initial y is above the plane - console.log(myShape) const dodecahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: myShape, @@ -254,7 +253,6 @@ function createDodecahedron( contactMat, ]); - console.log("Dodecahedron initial position:", dodecahedronBody.position); // Debugging log } function getGeometry2() { @@ -431,7 +429,6 @@ function createShape(vertices2, faces2, radius) { for (let i = 0; i < faces2.length; ++i) { cf[i] = faces2[i].slice(0, faces2[i].length - 1); } - console.log(faces2); return new CANNON.ConvexPolyhedron({ vertices: cv, faces: cf }); } diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index ab8b59389..3c7caeaae 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -147,6 +147,7 @@ define([ ctx.textColor; if (environment.sharedId) { + console.log(environment.sharedId) console.log("Shared instance"); presence = activity.getPresenceObject(function ( error, @@ -170,6 +171,9 @@ define([ ); var onNetworkDataReceived = function (msg) { + console.log("received data"); + console.log(msg); + console.log(msg.action); if (presence.getUserInfo().networkId === msg.user.networkId) { return; } @@ -231,6 +235,7 @@ define([ } if (msg.action == "positions") { + console.log("received positonns"); copyPositions(msg.content); } // if (msg.action == "throw") { @@ -256,54 +261,55 @@ define([ return; } remove(intersectedObject); - } - let createFunction = null; + } else { + let createFunction = null; - switch (msg.content.shape) { - case "cube": - createFunction = createCube; - break; - case "octa": - createFunction = createOctahedron; - break; - case "tetra": - createFunction = createTetrahedron; - break; - case "dodeca": - createFunction = createDodecahedron; - break; - case "deca": - createFunction = createDecahedron; - break; - case "icosa": - createFunction = createIcosahedron; - break; - default: - console.error("Unknown shape: " + msg.content.shape); - break; - } + switch (msg.content.shape) { + case "cube": + createFunction = createCube; + break; + case "octa": + createFunction = createOctahedron; + break; + case "tetra": + createFunction = createTetrahedron; + break; + case "dodeca": + createFunction = createDodecahedron; + break; + case "deca": + createFunction = createDecahedron; + break; + case "icosa": + createFunction = createIcosahedron; + break; + default: + console.error("Unknown shape: " + msg.content.shape); + break; + } - if (createFunction) { - createFunction( - msg.content.color, - msg.content.ifNumbers, - msg.content.ifTransparent, - msg.content.xCoordinateShared, - msg.content.zCoordinateShared, - msg.content.ifImage, - msg.content.sharedImageData, - msg.content.yCoordinateShared, - msg.content.quaternionShared, - msg.content.sharedTextColor, - ctx, - diceArray, - world, - scene, - groundPhysMat, - msg.content.sharedAngVel1, - msg.content.sharedAngVel2, - msg.content.sharedAngVel3 - ); + if (createFunction) { + createFunction( + msg.content.color, + msg.content.ifNumbers, + msg.content.ifTransparent, + msg.content.xCoordinateShared, + msg.content.zCoordinateShared, + msg.content.ifImage, + msg.content.sharedImageData, + msg.content.yCoordinateShared, + msg.content.quaternionShared, + msg.content.sharedTextColor, + ctx, + diceArray, + world, + scene, + groundPhysMat, + msg.content.sharedAngVel1, + msg.content.sharedAngVel2, + msg.content.sharedAngVel3 + ); + } } }; @@ -1050,6 +1056,7 @@ define([ document.getElementById("network-button"), undefined ); + var isHost = false; palette.addEventListener("shared", function () { palette.popDown(); console.log("Want to share"); @@ -1062,6 +1069,8 @@ define([ "org.sugarlabs.3DVolume", function (groupId) { console.log("Activity shared"); + isHost = true; + console.log(groupId) } ); network.onDataReceived(onNetworkDataReceived); @@ -1070,34 +1079,32 @@ define([ }); var onNetworkUserChanged = function (msg) { - let presenceDiceArray = []; - for (let i = 0; i < diceArray.length; i++) { - // Handles the situation when the user already has some volume on the board before the connected user joins. - presenceDiceArray.push([ - diceArray[i][2], - diceArray[i][1].position, - diceArray[i][1].quaternion, - diceArray[i][5], - diceArray[i][6], - diceArray[i][3], - diceArray[i][4], - diceArray[i][7], - diceArray[i][8], - diceArray[i][9], - ]); + if (isHost) { + let presenceDiceArray = []; + for (let i = 0; i < diceArray.length; i++) { + // Handles the situation when the user already has some volume on the board before the connected user joins. + presenceDiceArray.push([ + diceArray[i][2], + diceArray[i][1].position, + diceArray[i][1].quaternion, + diceArray[i][5], + diceArray[i][6], + diceArray[i][3], + diceArray[i][4], + diceArray[i][7], + diceArray[i][8], + diceArray[i][9], + ]); + } + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "init", + content: [presenceDiceArray, presentBackground], // sends the diceArray and the present background of the user to the users which are joining + }); + setTimeout(sendPositions, 4500); } - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "init", - content: [presenceDiceArray, presentBackground], // sends the diceArray and the present background of the user to the users which are joining - }); - setTimeout(sendPositions, 4500); - - }; - - renderer.setSize(window.innerWidth, window.innerHeight); const canvas = document.getElementById("game-container"); document @@ -1573,6 +1580,7 @@ define([ } // This function calls the getScore functions for all the volumes and displays them. function getScores() { + console.log("getting scores now"); scoresObject.presentScore = 0; scoresObject.lastRoll = ""; lastRollElement.textContent = ""; @@ -1617,6 +1625,7 @@ define([ } function sendPositions() { + console.log("sending positions now"); let dicePositions = []; for (let i = 0; i < diceArray.length; i++) { dicePositions.push([ @@ -1632,7 +1641,8 @@ define([ } function copyPositions(positions) { - console.log("copying positions now") + console.log("copying positions now"); + console.log(diceArray); for (let i = 0; i < diceArray.length; i++) { scene.remove(diceArray[i][0]); world.removeBody(diceArray[i][1]); @@ -1674,16 +1684,17 @@ define([ diceArray[i][0]?.position?.copy(diceArray[i][1].position); diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); } + // console.log(world.hasActiveBodies); if (world.hasActiveBodies == false && awake == true) { awake = false; console.log("the world is going to sleep now bye bye"); + getScores(); if (throwingDice) { throwingDice = false; if (presence) { sendPositions(); } } - getScores(); } if (world.hasActiveBodies == true) { awake = true; From 7cad726a46e3eb03c0abedb57dec34547cb92851 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 25 Aug 2024 14:00:49 +0530 Subject: [PATCH 42/60] changes --- activities/3DVolume.activity/js/activity.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 3c7caeaae..4f9dde519 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1096,11 +1096,13 @@ define([ diceArray[i][9], ]); } - presence.sendMessage(presence.getSharedInfo().id, { - user: presence.getUserInfo(), - action: "init", - content: [presenceDiceArray, presentBackground], // sends the diceArray and the present background of the user to the users which are joining - }); + if (isHost) { + presence.sendMessage(presence.getSharedInfo().id, { + user: presence.getUserInfo(), + action: "init", + content: [presenceDiceArray, presentBackground], // sends the diceArray and the present background of the user to the users which are joining + }); + } setTimeout(sendPositions, 4500); } }; From 993facdfd2c60f2dac7b68ef4747625fb12402d6 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 25 Aug 2024 14:07:35 +0530 Subject: [PATCH 43/60] maybe fix --- activities/3DVolume.activity/js/activity.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 4f9dde519..c861156b6 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -101,6 +101,7 @@ define([ let removeVolume = false; let transparent = false; let defaultVolume = true; + let isInit = false; let xCoordinate, zCoordinate; @@ -179,6 +180,11 @@ define([ } // If user already has some volumes on the board if (msg.action == "init") { + if (isInit) { + return; + } + isInit = true; + changeBoardBackground(msg.content[1]); data = msg.content[0]; for (let i = 0; i < data.length; i++) { From 85038fdd2c25d9ad489369cc8868ac8f8d43d114 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Tue, 27 Aug 2024 11:00:58 +0530 Subject: [PATCH 44/60] changes --- activities/3DVolume.activity/js/activity.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index c861156b6..f41da2ca8 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1188,7 +1188,8 @@ define([ ); } else { sensorButton.classList.remove("active"); - world.gravity.set(-9.81, -9.81, -9.81); + world.gravity.set(0, -9.81, 0); + wakeAll(); } }); @@ -1693,6 +1694,7 @@ define([ diceArray[i][0]?.quaternion?.copy(diceArray[i][1].quaternion); } // console.log(world.hasActiveBodies); + // console.log(world.hasActiveBodies + "," + awake) if (world.hasActiveBodies == false && awake == true) { awake = false; console.log("the world is going to sleep now bye bye"); From 400329d4a3261c7b91d183c22aa3b7781f10915e Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Mon, 2 Sep 2024 11:19:44 +0530 Subject: [PATCH 45/60] two step fix --- activities/3DVolume.activity/js/Deca.js | 12 ++++++++---- activities/3DVolume.activity/js/Dodeca.js | 5 ++++- activities/3DVolume.activity/js/Icosa.js | 8 ++++++-- activities/3DVolume.activity/js/Octa.js | 12 +++++++++--- activities/3DVolume.activity/js/Tetra.js | 1 + 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/activities/3DVolume.activity/js/Deca.js b/activities/3DVolume.activity/js/Deca.js index bf5ac70f8..94687bc57 100644 --- a/activities/3DVolume.activity/js/Deca.js +++ b/activities/3DVolume.activity/js/Deca.js @@ -129,7 +129,7 @@ function createDecahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let y = yCoordinateShared == null ? 10 : 1; const sides = 10; const scalingFactor = 1.5; @@ -188,12 +188,16 @@ function createDecahedron( faces: correctedFacesGeo, }); let decahedronBody = new CANNON.Body({ - mass: 3, // Set mass + mass: 2, // Set mass shape: decahedronShape2, position: new CANNON.Vec3(x, y, z), material: new CANNON.Material(), - restitution: 0.5, + restitution: 0.3, }); + world.addBody(decahedronBody); + if (xCoordinateShared != null) { + decahedronBody.sleep() + } const contactMat = new CANNON.ContactMaterial( groundPhysMat, decahedronBody.material, @@ -202,9 +206,9 @@ function createDecahedron( world.addContactMaterial(contactMat); - world.addBody(decahedronBody); decahedronBody.sleepSpeedLimit = 4; decahedronBody.sleepTimeLimit = 10; + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; diff --git a/activities/3DVolume.activity/js/Dodeca.js b/activities/3DVolume.activity/js/Dodeca.js index ba7b4319a..5d83b277e 100644 --- a/activities/3DVolume.activity/js/Dodeca.js +++ b/activities/3DVolume.activity/js/Dodeca.js @@ -200,7 +200,7 @@ function createDodecahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 4 : yCoordinateShared + 1; // Ensure initial y is above the plane + let y = yCoordinateShared == null ? 4 : 1; // Ensure initial y is above the plane const dodecahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: myShape, @@ -215,6 +215,9 @@ function createDodecahedron( world.addBody(dodecahedronBody); + if (xCoordinateShared != null) { + dodecahedronBody.sleep() + } const contactMat = new CANNON.ContactMaterial( groundPhysMat, dodecahedronBody.material, diff --git a/activities/3DVolume.activity/js/Icosa.js b/activities/3DVolume.activity/js/Icosa.js index be43703bb..512891f3e 100644 --- a/activities/3DVolume.activity/js/Icosa.js +++ b/activities/3DVolume.activity/js/Icosa.js @@ -164,18 +164,22 @@ function createIcosahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 4 : yCoordinateShared; + let y = yCoordinateShared == null ? 4 : 1; const icosahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: icosahedronShape, position: new CANNON.Vec3(x, y, z), material: new CANNON.Material(), - restitution: 5, + restitution: 0.3, }); icosahedronBody.sleepSpeedLimit = 0.5; icosahedronBody.sleepTimeLimit = 3; + if (xCoordinateShared != null) { + icosahedronBody.sleep() + } + const contactMat = new CANNON.ContactMaterial( groundPhysMat, icosahedronBody.material, diff --git a/activities/3DVolume.activity/js/Octa.js b/activities/3DVolume.activity/js/Octa.js index 79b99ce82..f75df80de 100644 --- a/activities/3DVolume.activity/js/Octa.js +++ b/activities/3DVolume.activity/js/Octa.js @@ -136,16 +136,22 @@ function createOctahedron( let x = xCoordinateShared == null ? xCoordinate : xCoordinateShared; let z = zCoordinateShared == null ? zCoordinate : zCoordinateShared; - let y = yCoordinateShared == null ? 10 : yCoordinateShared; + let y = yCoordinateShared == null ? 10 : 1; const octahedronBody = new CANNON.Body({ mass: 2, // Set mass shape: octahedronShape, position: new CANNON.Vec3(x, y, z), material: new CANNON.Material(), - restitution: 5, + restitution: 0.3, }); + world.addBody(octahedronBody); + + if (xCoordinateShared != null) { + octahedronBody.sleep() + } + const contactMat = new CANNON.ContactMaterial( groundPhysMat, octahedronBody.material, @@ -153,7 +159,7 @@ function createOctahedron( ); world.addContactMaterial(contactMat); - world.addBody(octahedronBody); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; diff --git a/activities/3DVolume.activity/js/Tetra.js b/activities/3DVolume.activity/js/Tetra.js index 8ed98908f..64434462f 100644 --- a/activities/3DVolume.activity/js/Tetra.js +++ b/activities/3DVolume.activity/js/Tetra.js @@ -162,6 +162,7 @@ function createTetrahedron( restitution: 5, }); world.addBody(tetrahedronBody); + let angVel1 = sharedAngVel1 == null ? Math.random() * (1 - 0.1) + 0.1 : sharedAngVel1; let angVel2 = From cd46969574aa6d40669ccea7519466cf5fc2bdbc Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 8 Sep 2024 20:42:05 +0530 Subject: [PATCH 46/60] changes --- activities/3DVolume.activity/js/activity.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index f41da2ca8..b25a01ff9 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -289,9 +289,6 @@ define([ case "icosa": createFunction = createIcosahedron; break; - default: - console.error("Unknown shape: " + msg.content.shape); - break; } if (createFunction) { From 971ea377daa485a2dc0ca84906d769fd2e5b2687 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 8 Sep 2024 20:47:44 +0530 Subject: [PATCH 47/60] changes --- activities/3DVolume.activity/js/activity.js | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index b25a01ff9..285212560 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -282,7 +282,27 @@ define([ break; case "dodeca": createFunction = createDodecahedron; + switch (msg.content.shape) { + case "cube": + createFunction = createCube; + break; + case "octa": + createFunction = createOctahedron; + break; + case "tetra": + createFunction = createTetrahedron; + break; + case "dodeca": + createFunction = createDodecahedron; + break; + case "deca": + createFunction = createDecahedron; break; + case "icosa": + createFunction = createIcosahedron; + break; + } + break; case "deca": createFunction = createDecahedron; break; @@ -936,7 +956,6 @@ define([ scoresObject.presentScore = scoresObject.presentScore - score; - console.log(scoresObject.presentScore); let scoresArray = scoresObject.lastRoll.split(" + "); @@ -951,8 +970,6 @@ define([ // Join the remaining scores back into a string scoresObject.lastRoll = scoresArray.join(" + "); updateElements(); - console.log(scoresObject.lastRoll); - console.log(scoresObject.presentScore); num--; } console.log(diceArray); From e87a932b0983632c91cf44a92bc6f35eb37a1f39 Mon Sep 17 00:00:00 2001 From: SamarthBagga Date: Sun, 8 Sep 2024 20:55:02 +0530 Subject: [PATCH 48/60] error correction --- activities/3DVolume.activity/js/activity.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 285212560..2e756e21e 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -972,9 +972,6 @@ define([ updateElements(); num--; } - console.log(diceArray); - console.log(diceArray[index][1]); - console.log(diceArray[index][0]); world.removeBody(diceArray[index][1]); scene.remove(diceArray[index][0]); diceArray.splice(index, 1); From 618197bda5e7cb1041cc2ea5899a5020394f4d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lionel=20Lask=C3=A9?= Date: Sat, 2 Nov 2024 22:24:48 +0100 Subject: [PATCH 49/60] Add compatibility with WebAPI accelerometer --- activities/3DVolume.activity/js/activity.js | 38 +++++++--- config.xml | 75 +++++++++++--------- res/icon/android/icon-192x192.png | Bin 0 -> 6479 bytes 3 files changed, 67 insertions(+), 46 deletions(-) create mode 100644 res/icon/android/icon-192x192.png diff --git a/activities/3DVolume.activity/js/activity.js b/activities/3DVolume.activity/js/activity.js index 2e756e21e..0e76bf973 100644 --- a/activities/3DVolume.activity/js/activity.js +++ b/activities/3DVolume.activity/js/activity.js @@ -1173,9 +1173,16 @@ define([ var sensorButton = document.getElementById("sensor-button"); var sensorMode = false; - // Handles the accelerometer - if (navigator.accelerometer != null) { - // If the accelerometer is available then keep the accelerometer on by default. + // If the accelerometer is available then keep the accelerometer on by default. + if (window.Accelerometer) { + sensorMode = true; + sensorButton.classList.add("active"); + var accelerometer = new Accelerometer({ frequency: 500 }); + if (accelerometer) { + accelerometer.addEventListener('reading', accelerationChanged); + accelerometer.start(); + } + } else if (navigator.accelerometer) { sensorMode = true; sensorButton.classList.add("active"); watchId = navigator.accelerometer.watchAcceleration( @@ -1186,17 +1193,25 @@ define([ } sensorButton.addEventListener("click", function () { - if (navigator.accelerometer == null) { + if (!navigator.accelerometer && !window.Accelerometer) { return; } sensorMode = !sensorMode; if (sensorMode) { sensorButton.classList.add("active"); - watchId = navigator.accelerometer.watchAcceleration( - accelerationChanged, - null, - { frequency: 500 } - ); + if (window.Accelerometer) { + var accelerometer = new Accelerometer({ frequency: 500 }); + if (accelerometer) { + accelerometer.addEventListener('reading', accelerationChanged); + accelerometer.start(); + } + } else if (navigator.accelerometer) { + watchId = navigator.accelerometer.watchAcceleration( + accelerationChanged, + null, + { frequency: 500 } + ); + } } else { sensorButton.classList.remove("active"); world.gravity.set(0, -9.81, 0); @@ -1204,9 +1219,10 @@ define([ } }); - function accelerationChanged(acceleration) { + function accelerationChanged(accelerationEvent) { if (!sensorMode) return; - if (window.orientation == -90) { + var acceleration = window.Accelerometer ? accelerationEvent.target : accelerationEvent; + if (window.orientation && window.orientation == -90) { // If its in reverse. if (acceleration.y > 0) { if (acceleration.y < -2.8) { diff --git a/config.xml b/config.xml index e956d3d60..21ed631c0 100644 --- a/config.xml +++ b/config.xml @@ -23,34 +23,12 @@ - - - - - - - - - - - - - - - - - - - - - - - +

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
_b(tkzQIo0HFfo30Z^ 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