├── .gitignore ├── README.md ├── docs ├── distance-demo.md └── wormhole-demo.md ├── graphics ├── NACIS.png ├── advanced_rectangle.gif ├── bases.png ├── distance-tutorial │ ├── 2-D.jpg │ ├── 2D-normal.png │ ├── 2D-pinch.png │ ├── 3D-normal.png │ ├── 3D-pinch.png │ ├── 3D-view.png │ ├── add-map-grid.png │ ├── change-distance.gif │ ├── grids.png │ ├── inputs.png │ ├── nacis-2016-with-line.png │ ├── nacis-2016.jpg │ ├── nodes-triangles.png │ ├── nodes.png │ ├── pinch-color.png │ └── wireframe-peaks.jpg ├── focus.gif ├── grid.gif ├── nodes-condensed-2.gif ├── nodes.gif ├── reset.gif ├── wireframe.gif └── wormhole │ ├── 11-bind-compilation.png │ ├── 11-wire-view.png │ ├── 2D-11-wire-compilation.png │ ├── 2D-all-maps.png │ ├── 3D-11-wire-compilation.png │ ├── 3D-all-maps.png │ ├── adjust-distance-M2-compilation.png │ ├── adjust-distance-compilation.png │ ├── adjust-edge-distance-view-2.jpg │ ├── adjust-edge-distance-with-text.gif │ ├── bind-maps-3.jpg │ ├── bind-maps-transparency.jpg │ ├── bind-wireframe-compilation.png │ ├── grid--11-compilation.png │ ├── grid-all-maps.png │ ├── grid-complete-11-compilation.png │ ├── grid-complete-all-maps.png │ ├── title.png │ └── two-map-mode.jpg ├── graphs └── test.graphml.js ├── images ├── grid.gif ├── logo.png ├── maps.gif └── texture.jpg ├── index.html ├── js ├── FileSaver.min.js ├── OBJExporter.js ├── OLDp5.dom.js ├── OLDp5.min.js ├── OrbitControls.js ├── delaunay.js ├── gis.js ├── gisUI.js ├── graph-io.js ├── iro.js ├── numeric-1.2.6.min.js ├── p5.dom.js ├── p5.dom.old.js ├── p5.js ├── p5.min.old.js ├── three-vr-viewer.js ├── three.min.js └── vr │ ├── ViveController.js │ └── webvr2.js ├── models └── obj │ └── vive-controller │ ├── onepointfive_spec.png │ ├── onepointfive_texture.png │ └── vr_controller_vive_1_5.obj └── style └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | 4 | .DS_Store 5 | 6 | *.komodoproject 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _enfolding:_ a prototype geographical imagination system (gis) 2 | _enfolding_ was created for people searching for new ways to visualize absolute and relational spaces. We hope this software will assist users as they conduct exploratory visual analysis and tailor new visualizations of space. If you are not an avid mapper within traditional GIS environments or do not consider yourself a spatial theorist, do not worry. This platform and tutorials have been designed for people with varying knowledge, interests, and skills. Bring your creativity and stay tuned for upcoming examples and tutorials. 3 | 4 | # online demo 5 | If you want to jump right in, you can get started with the [online version of enfolding](https://foldingspace.github.io/enfolding/) 6 | 7 | # tutorials 8 | The following tutorials, first written by Lindsey Funke, will help you get up-and-running with _enfolding_ and give you an idea of what's possible: 9 | 1. [Changing measures of distance](docs/distance-demo.md) 10 | 2. [Creating wormholes](docs/wormhole-demo.md) 11 | 12 | ![example](graphics/wormhole/11-bind-compilation.png) 13 | 14 | # Documentation 15 | - [_enfolding_](#enfolding) 16 | - [Using the interface](#using-the-interface) 17 | + [Edit Mode](#edit-mode) 18 | + [Render Mode](#render-mode) 19 | - [Getting Started in Editing Mode](#getting-started-in-editing-mode) 20 | * [Choosing a file](#choosing-a-file) 21 | * [Importing your file](#importing-your-file) 22 | * [Important user notes](#important-user-notes) 23 | + [Focus](#focus) 24 | + [Reset](#reset) 25 | * [Creating Grids and Nodes](#creating-grids-and-nodes) 26 | + [Grids](#Adding-a-basic-grid) 27 | + [Nodes](#Adding-nodes) 28 | * [Advanced Options](#advanced-options) 29 | - [Rendering your visualization](#rendering-your-visualization) 30 | * [Map Controls](#map-controls) 31 | + [3D MODE](#3d-mode) 32 | + [Two map mode](#two-map-mode) 33 | + [Rotate](#rotate) 34 | * [Appearance](#appearance) 35 | + [Transparency](#transparency) 36 | + [Background color](#background-color) 37 | * [Advanced visual options](#advanced-visual-options) 38 | + [WebVR](#webvr) 39 | + [Wire frame](#wire-frame) 40 | * [Exporting your visualization](#exporting-your-visualization) 41 | 42 | # _enfolding_ 43 | ### Why geographical *imagination* systems? 44 | _enfolding_ allows users to fold, bend, collage, reassemble, fragment, interweave, and reimagine maps and other images and render them in 2D and 3D visualizations. At its core is the desire to upset the rigid spatialities of traditional Geographic Information Systems (GIS), opening them to the broader realm of geographical imagination systems (gis). What does this mean for users? _enfolding_ can be used to bind two distant places that share intimate economic, emotional, or extractive connections to create an intertwined visualization of these relations across space. Through the interface you can stretch or shrink surface distances or get inside the map with rotating 3D representations. The possibilities to reshape, drape, and create are numerous. 45 | 46 | ### How it works? 47 | _enfolding_ draws on a number of JavaScript libraries including [p5.js](https://p5js.org/), [three.js](https://threejs.org/), and [the delaunator](https://github.com/mapbox/delaunator). 48 | 49 | ### What is to come? 50 | In what follows we present a short introduction to the interface. More tutorials and guides will be published over time. 51 | 52 | # Using the interface 53 | _enfolding_ has two canvases, the editing canvas and the rendering canvas. Once you add your maps or layers to the editing canvas, you may toggle back and forth between editing and rendering as you work. Let´s take a closer look at each of these canvases before getting started. 54 | 55 | ### Edit Mode 56 | Edit mode is where you will begin your project. Here you will add your maps and begin creating a grid or adding nodes that will shape your map later. We will examine each of these functions in more detail later on in the guide. 57 | 58 | ### Render Mode 59 | In the rendering canvas, you will be able to view your creation in 2D or 3D, bind maps together, and export your project for use outside of Enfolding. 60 | 61 | # Getting Started in Editing Mode 62 | To get started with _enfolding_ you will need at least one image. This may be a map, a satellite image, a layer exported from a mapping software, etc. 63 | 64 | ### Choosing a file 65 | Enfolding accepts most image formats including .PNG, JPG, .GIF, .SVG, among others. Be sure to save your images, when possible, with a transparent background if there is any white space around them. 66 | 67 | To take full advantage of the transparency options in rendering mode, we like to work with SVGs. (Extra tip: you can export your QGIS projects as SVGs. Be sure to select a transparent background. Open them in Inkscape, a free and open source program for vector editing to make any final changes or remove unwanted layers.) 68 | 69 | ### Importing your file 70 | In order to add your image to the canvas, simply drag and drop. You may add up to two images to the editing canvas. The first image you add will be denominated “MAP 1” and the second “MAP 2”. This distinction will be important later on and determine the drawing order of the images. Try out working with a single image first in order to get a handle on the different options within _enfolding_. 71 | 72 | ### Important user notes 73 | You will notice two buttons at the top of the editing menu: “Focus” and “Reset”. As you are creating and adjusting your nodes and grids, these two buttons will be quite important. 74 | 75 | - #### Focus 76 | You must be focused on an image in order to add or remove nodes and grids. If you are working with two maps or layers, swap back and forth between the two by using the focus button. 77 | 78 | ![focus](graphics/focus.gif) 79 | 80 | - #### Reset 81 | Click the reset button when you are ready to clear all the current nodes or grids from your images. If you would like to delete the current image, refresh the page to start with a blank canvas. 82 | 83 | ## Creating Grids and Nodes 84 | In order to fold, bend or crinkle your map, you will need to add a grid or individual nodes to the image. 85 | 86 | ## Adding a basic grid: 87 | You can add a basic grid by simply selecting its dimensions and clicking add grid. This will generate a new grid on the map in focus. You can tweak this grid later on by adjusting edge distance, creating new nodes, etc. 88 | 89 | ![Grids](graphics/grid.gif) 90 | 91 | ##### Things to keep in mind: 92 | In order to bind your maps later, you will need the same number of nodes in each. 93 | For example, if you add a 3x5 grid in Map 1, you could choose a 5x3 grid in Map 2, or manually add 15 nodes. 94 | 95 | 96 | ## Adding Nodes 97 | Manually add nodes by double clicking the map of interest. By default, the node will make connection to the nearest 4 other nodes. If you would like adjust the number of connections made, simply use the drop-down menu in the advanced settings. The new node will automatically connect to number you selected. 98 | 99 | ![Adding Nodes](graphics/nodes-condensed-2.gif) 100 | 101 | #### Adjusting edge distances 102 | You will notice that as you create nodes, text boxes that display node-node distances appear on each edge. You may adjust these distances in order to compress or pinch the map. Simply click the box, type a new number, and hit enter. 103 | 104 | In order to change the distance between two particular nodes, click a single node. It will appear in red. Then select another node with a shared edge. The text box will appear and allow you to adjust the distance. 105 | 106 | - To pinch the map, adjust to 0 and click enter. [See an extended example here.](docs/distance-demo.md) 107 | - In some particular cases, enfolding allows you to increase or exaggerate distances as well. [Check out the wormhole demo for an example of this.](docs/wormhole-demo.md) 108 | 109 | ## Advanced Options 110 | ![advanced](graphics/advanced_rectangle.gif) 111 | - ### Show triangles 112 | Selecting the show triangles option will reveal the triangles created by your grid and node selection. For more information about Delaunay triangulation check out the [delaunator’s guide](https://mapbox.github.io/delaunator/). 113 | - ### Show maps 114 | The show maps option allows you to hide or display your maps. All nodes and edges will remain visible. 115 | - ### Show inputs 116 | Check or uncheck the show inputs box if you would like to see or hide the distance text boxes as you create new nodes. 117 | 118 | 119 | # Rendering your visualization 120 | Once you have added a grid or drawn a few nodes, switch over to the rendering canvas to see how your maps are shaping up. You can always return to make any necessary adjustments. 121 | 122 | 123 | ## Map Controls 124 | - ### 3D MODE 125 | Your map will automatically render in 3D. Uncheck the box next to 3D in order to view your maps in two dimensions. 126 | - ### Two map mode 127 | If you choose to work with 2 images, check the box next to “Two map mode” to activate other features. Here you may bind the two images together, connecting them by 1-6 nodes. Remember, each image must contain the same number of nodes to bind together successfully. 128 | - ### Rotate 129 | Activate the rotation feature below. 130 | 131 | 132 | ## Appearance 133 | - ### Transparency 134 | Once you have added a map or maps, you will be able to adjust their transparency using the associated sliders. We have found that this works best with SVGs. 135 | 136 | - ### Background color 137 | Click any of the colored boxes to adjust the background color of the canvas. 138 | 139 | 140 | ## Advanced visual options 141 | - ### WebVR 142 | If you have a VR headset, get inside your map by checking the box. 143 | - ### Wire frame 144 | Reveal the wire framework that sculpts your final map. 145 | ![wireframe](graphics/wireframe.gif) 146 | 147 | 148 | ## Exporting your visualization 149 | We are still developing a number of export options. For now, we have a few recommendations on how to export your enfolded map. 150 | 151 | * ### Save as image 152 | Use the SAVE IMAGE button in the rendering canvas to export a .jpg of your canvas. 153 | * ### Screen recordings 154 | Some operating systems automatically offer users the chance to capture a video of your screen. If your do not, many free options exist to do so. 155 | * ### Export as a 3D object 156 | If you would like to continue creating within a 3D graphics program like Blender, save your project as an OBJ. 157 | ![Enfolding](graphics/bases.png) 158 | #### enfolding output rendered in blender 159 | 160 | _enfolding_ (c) Luke Bergmann and Nick Lally 161 | -------------------------------------------------------------------------------- /docs/distance-demo.md: -------------------------------------------------------------------------------- 1 | # Distance Demo 2 | ![Distance 0 in Color](../graphics/distance-tutorial/pinch-color.png) 3 | ## Getting Started 4 | _enfolding_ allows you to change the distances between two nodes within the editing canvas. This will create a pinch on the maps surface according to the distance you enter. In the image above, with a new edge distance of 0, the two corners are completely cinched together. Here, we will walk you through how to adjust distances and finish with a geographic example from NACIS 2016. A more technical explanation is to come. 5 | ### Add an image to _enfolding_ + create a grid 6 | Here, we are working with a simple grey grid image. Drag and drop whatever image or map you would like to work with into the editing canvas. Add a grid of your choice. 7 | ![Add map grid](../graphics/distance-tutorial/add-map-grid.png) 8 | ### Create two nodes that connect where you would like to adjust the distance 9 | Create two nodes to get started. You are not limited to just two nodes, but for this example, we are using two. 10 | ![Nodes-triangles](../graphics/distance-tutorial/nodes-triangles.png) 11 | ### Working with the inputs 12 | Be sure to check "show inputs" so that the edge distances appear. All distances are recorded in pixels. Type a new distance in the text box, then hit enter. Swap over to the rendering canvas to see your changes in 2D and 3D. You can always switch back to edit mode and enter new distances in order to adjust your visualization. 13 | ### Overview 14 | Here is a brief overview of the process before we jump into a geographic example. 15 | ![change-distance](../graphics/distance-tutorial/change-distance.gif) 16 | 17 | ## Example: Getting to NACIS 2016 18 | How do different modes of transportation shape relations across space? 19 | ### Add image and grid 20 | ![nacis-2016](../graphics/distance-tutorial/grids.png) 21 | ### Create nodes and connections between airports 22 | Here we compare the driving route to flight paths from Madison, WI to Colorado Springs. The driving path is symbolized in black, while the flight path is created through new nodes within enfolding. This specific path transports passengers from Madison to Dallas and then back north to Colorado Springs. After laying out a basic grid, we add a number of nodes to connect these airports and examine their original distances. 23 | ![nacis-2016](../graphics/distance-tutorial/nodes.png) 24 | ### Adjust distances 25 | Driving time from Madison to Colorado Springs averages around 15 hours, and flight time totals around 4.5 hours in the selected route. We add an extra 2 hours to account for airport waiting and decrease the node distances to reflect the difference between driving and flight times, cinching together the three airport hubs. 26 | ![nacis-2016](../graphics/distance-tutorial/inputs.png) 27 | ### Display in render canvas 28 | **Here’s how the image changes in 2D with the new distances.** 29 | ![nacis-2016](../graphics/distance-tutorial/2-D.jpg) 30 | **Here’s how it changes in 3D as relations get scaled.** 31 | ![nacis-2016](../graphics/distance-tutorial/nacis-2016.jpg) 32 | ### View wireframe 33 | ![wireframe-peaks](../graphics/distance-tutorial/wireframe-peaks.jpg) 34 | #### Add route lines in external program 35 | ![nacis-2016-with-line](../graphics/distance-tutorial/nacis-2016-with-line.png) 36 | 37 | #### Map tiles used for image by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL. 38 | 39 | 40 | ### Additional grey grid renders 41 | Here are a few extra renders from the grey grid example. 42 | #### 2D render with no distance changes 43 | The new nodes cause the corners to slightly fold even in the 2D view. 44 | ![2D-normal](../graphics/distance-tutorial/2D-normal.png) 45 | #### 2D render with distance set to zero 46 | Notice how this changes when distance is drastically decreased, creating intense folds. 47 | ![2D-pinch](../graphics/distance-tutorial/2D-pinch.png) 48 | #### 3D render with no distance changes 49 | ![3D-normal](../graphics/distance-tutorial/3D-normal.png) 50 | #### 3D render with distance set to zero 51 | In the 3D render, these distance changes reflect in folding, curling or pinching of the image's surface. 52 | ![3D-pinch](../graphics/distance-tutorial/3D-pinch.png) 53 | #### Zero distance alternative view 54 | Use the rotate feature or your mouse to move the graphic around and export alternate 3D views. 55 | ![3D-view](../graphics/distance-tutorial/3D-view.png) 56 | -------------------------------------------------------------------------------- /docs/wormhole-demo.md: -------------------------------------------------------------------------------- 1 | # Wormhole Demo 2 | ![Bind Maps 3](../graphics/wormhole/bind-maps-3.jpg) 3 | # How to create a wormhole with enfolding 4 | ![Bind Maps Transparency 1](../graphics/wormhole/title.png) 5 | ## **Editing Canvas** 6 | ### Step 1: Add two maps to enfolding 7 | To get started you will need two images. Drag and drop each into the enfolding editing canvas. 8 | ### Step 2: Create a grid 9 | Before the images can be bound together to form the wormhole, you must create a grid. We suggest creating a grid with an odd number of nodes. For this demo we will show you the results from a 5x5 grid and an 11x11 grid. 10 | **5x5 Grid** 11 | ![Partial-Grid](../graphics/wormhole/grid-all-maps.png) 12 | **11x11 Grid** 13 | ![Partial-Grid](../graphics/wormhole/grid--11-compilation.png) 14 | ### Step 3: Add a node in the center of the grid 15 | To complete the nodes for the wormhole visualization, add a single node in the middle square of the grid. Be sure that this node connects with the four nearest nodes. You can adjust this in the advanced settings. Simply double click to create the new node. 16 | **5x5 Grid** 17 | ![Complete Grid](../graphics/wormhole/grid-complete-all-maps.png) 18 | **11x11 Grid** 19 | ![Complete Grid](../graphics/wormhole/grid-complete-11-compilation.png) 20 | ## **Rendering Canvas** 21 | ### Step 4: Swap to render mode. 22 | **5x5 Grid** 23 | You will notice a 2D version of your images is displayed. The wireframe to the right displays the delaunay triangles created within the grid. 24 | ![2D](../graphics/wormhole/2D-all-maps.png) 25 | **11x11 Grid** 26 | Notice how the 11x11 grid shows smoother edges. This will impact your 3D visualization later on. 27 | ![2D](../graphics/wormhole/2D-11-wire-compilation.png) 28 | ### Step 5: Activate 3D View 29 | Activate the 3D view within the map controls by checking the box. Use your mouse to drag or rotate the image, or active rotation to get a detailed look at the 3D rendering. 30 | 31 | **5x5 Grid** 32 | ![3D](../graphics/wormhole/3D-all-maps.png) 33 | **11x11 Grid** 34 | Once again you will notice that both the 5x5 and 11x11 grids have similar structures and shapes. However, the 11x11 provides a gentler transition between squares. Choose a grid with fewer squares if you are aiming for a more blocky visual. 35 | ![3D](../graphics/wormhole/3D-11-wire-compilation.png) 36 | ### Step 6: Check two map mode 37 | Checking the two map mode box will unlock the controls needed to create the final wormhole. 38 | ![Two Map Mode 3D](../graphics/wormhole/two-map-mode.jpg) 39 | ### Step 7: Bind maps by the last node 40 | Once you activate Two Map Mode, bind the maps together. While the dropdown menu allows you to connect several nodes, for this case select 1 node. 41 | #### And .... You have a wormhole! 42 | **5x5 Grid** 43 | ![Bind Maps Wireframe](../graphics/wormhole/bind-wireframe-compilation.png) 44 | **11x11 Grid** 45 | Now, we see how our initial grid selection can effect the outcome of the bound maps. Feel free to toggle back to editting mode and switch up your grid if you are not satisfied with the initial shape. 46 | ![Bind Maps Transparency 1](../graphics/wormhole/11-wire-view.png) 47 | ### Step 8: Adjust and Export 48 | #### Play with transparency Levels 49 | Adjust the transparency and take advantage of the rotation object to create and export images of your final project. 50 | ![Bind Maps Transparency](../graphics/wormhole/bind-maps-transparency.jpg) 51 | #### Export 52 | The "SAVE IMAGE" button creates and downloads a .JPG of your current view. The "SAVE OBJ" button generates and .OBJ file for use in blender. If you would like to export a rotating version of your visualizations, we encourage you to use a screen capture or recording application. 53 | ## Advanced Options 54 | ### Altering the relation between the two images 55 | In the previous versions, you note that the wormhole height is equal in both MAP 1 and MAP 2. You can adjust this by increasing or decreasing the distances of each edge that connects to the central node of your grid. Let's take a closer look. We imagine this may be useful in representing unequal connections, extractive relationships, etc. 56 | ![Advanced 1](../graphics/wormhole/adjust-edge-distance-view-2.jpg) 57 | ### Step 1: Swap back to the Editing Canvas 58 | ### Step 2: Focus on the map you will like to edge by using the "FOCUS" button. 59 | This will allow you to add nodes and adjust edge distances. 60 | ### Step 3: Adjust edge distances. 61 | - Click on an outer node that connects with the central node. 62 | Here, we will only be adjusting the edge distances for the central block of the grid where we added the central node earlier. You will see a text box appear. 63 | - Type a new value. 64 | If you would like to stretch the wormhole, select a higher value. If you would like to compress it, select a lower valie. Here, we chose to double the edges from around 125 to 250. 65 | - Repeat for all four edges. 66 | ![Advanced Directions GIF](../graphics/wormhole/adjust-edge-distance-with-text.gif) 67 | ### Step 4: Return to the Rendering Canvas to view the results 68 | These two examples were made using the 5x5 grid. Examine how the dynamic shifts when editing MAP 1 and MAP 2. 69 | #### MAP 1 with exagerated edge distances around the central node 70 | ![Map 1 advanced view](../graphics/wormhole/adjust-distance-compilation.png) 71 | #### MAP 2 with exagerated edge distances around the central node 72 | ![Map 2 advanced view](../graphics/wormhole/adjust-distance-M2-compilation.png) 73 | # Why create a wormhole with enfolding? 74 | ![example](../graphics/wormhole/11-bind-compilation.png) 75 | -------------------------------------------------------------------------------- /graphics/NACIS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/NACIS.png -------------------------------------------------------------------------------- /graphics/advanced_rectangle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/advanced_rectangle.gif -------------------------------------------------------------------------------- /graphics/bases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/bases.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/2-D.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/2-D.jpg -------------------------------------------------------------------------------- /graphics/distance-tutorial/2D-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/2D-normal.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/2D-pinch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/2D-pinch.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/3D-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/3D-normal.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/3D-pinch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/3D-pinch.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/3D-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/3D-view.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/add-map-grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/add-map-grid.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/change-distance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/change-distance.gif -------------------------------------------------------------------------------- /graphics/distance-tutorial/grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/grids.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/inputs.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/nacis-2016-with-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/nacis-2016-with-line.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/nacis-2016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/nacis-2016.jpg -------------------------------------------------------------------------------- /graphics/distance-tutorial/nodes-triangles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/nodes-triangles.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/nodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/nodes.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/pinch-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/pinch-color.png -------------------------------------------------------------------------------- /graphics/distance-tutorial/wireframe-peaks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/distance-tutorial/wireframe-peaks.jpg -------------------------------------------------------------------------------- /graphics/focus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/focus.gif -------------------------------------------------------------------------------- /graphics/grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/grid.gif -------------------------------------------------------------------------------- /graphics/nodes-condensed-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/nodes-condensed-2.gif -------------------------------------------------------------------------------- /graphics/nodes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/nodes.gif -------------------------------------------------------------------------------- /graphics/reset.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/reset.gif -------------------------------------------------------------------------------- /graphics/wireframe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wireframe.gif -------------------------------------------------------------------------------- /graphics/wormhole/11-bind-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/11-bind-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/11-wire-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/11-wire-view.png -------------------------------------------------------------------------------- /graphics/wormhole/2D-11-wire-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/2D-11-wire-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/2D-all-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/2D-all-maps.png -------------------------------------------------------------------------------- /graphics/wormhole/3D-11-wire-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/3D-11-wire-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/3D-all-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/3D-all-maps.png -------------------------------------------------------------------------------- /graphics/wormhole/adjust-distance-M2-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/adjust-distance-M2-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/adjust-distance-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/adjust-distance-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/adjust-edge-distance-view-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/adjust-edge-distance-view-2.jpg -------------------------------------------------------------------------------- /graphics/wormhole/adjust-edge-distance-with-text.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/adjust-edge-distance-with-text.gif -------------------------------------------------------------------------------- /graphics/wormhole/bind-maps-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/bind-maps-3.jpg -------------------------------------------------------------------------------- /graphics/wormhole/bind-maps-transparency.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/bind-maps-transparency.jpg -------------------------------------------------------------------------------- /graphics/wormhole/bind-wireframe-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/bind-wireframe-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/grid--11-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/grid--11-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/grid-all-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/grid-all-maps.png -------------------------------------------------------------------------------- /graphics/wormhole/grid-complete-11-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/grid-complete-11-compilation.png -------------------------------------------------------------------------------- /graphics/wormhole/grid-complete-all-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/grid-complete-all-maps.png -------------------------------------------------------------------------------- /graphics/wormhole/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/title.png -------------------------------------------------------------------------------- /graphics/wormhole/two-map-mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/graphics/wormhole/two-map-mode.jpg -------------------------------------------------------------------------------- /images/grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/images/grid.gif -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/images/logo.png -------------------------------------------------------------------------------- /images/maps.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/images/maps.gif -------------------------------------------------------------------------------- /images/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/images/texture.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | geographical imagination system 5 | 6 | --> 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 85 |
86 | 87 | 88 | 140 | 141 |
142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /js/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/constructor/i.test(e.HTMLElement),f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(i){u(i)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,s){if(!s){t=p(t)}var v=this,w=t.type,m=w===d,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&a)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;i(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define([],function(){return saveAs})} -------------------------------------------------------------------------------- /js/OBJExporter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJExporter = function () {}; 6 | 7 | THREE.OBJExporter.prototype = { 8 | 9 | constructor: THREE.OBJExporter, 10 | 11 | parse: function ( object ) { 12 | 13 | var output = ''; 14 | 15 | var indexVertex = 0; 16 | var indexVertexUvs = 0; 17 | var indexNormals = 0; 18 | 19 | var vertex = new THREE.Vector3(); 20 | var normal = new THREE.Vector3(); 21 | var uv = new THREE.Vector2(); 22 | 23 | var i, j, l, m, face = []; 24 | 25 | var parseMesh = function ( mesh ) { 26 | 27 | var nbVertex = 0; 28 | var nbNormals = 0; 29 | var nbVertexUvs = 0; 30 | 31 | var geometry = mesh.geometry; 32 | 33 | var normalMatrixWorld = new THREE.Matrix3(); 34 | 35 | if ( geometry instanceof THREE.Geometry ) { 36 | 37 | geometry = new THREE.BufferGeometry().setFromObject( mesh ); 38 | 39 | } 40 | 41 | if ( geometry instanceof THREE.BufferGeometry ) { 42 | 43 | // shortcuts 44 | var vertices = geometry.getAttribute( 'position' ); 45 | var normals = geometry.getAttribute( 'normal' ); 46 | var uvs = geometry.getAttribute( 'uv' ); 47 | var indices = geometry.getIndex(); 48 | 49 | // name of the mesh object 50 | output += 'o ' + mesh.name + '\n'; 51 | 52 | // vertices 53 | 54 | if( vertices !== undefined ) { 55 | 56 | for ( i = 0, l = vertices.count; i < l; i ++, nbVertex++ ) { 57 | 58 | vertex.x = vertices.getX( i ); 59 | vertex.y = vertices.getY( i ); 60 | vertex.z = vertices.getZ( i ); 61 | 62 | // transfrom the vertex to world space 63 | vertex.applyMatrix4( mesh.matrixWorld ); 64 | 65 | // transform the vertex to export format 66 | output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; 67 | 68 | } 69 | 70 | } 71 | 72 | // uvs 73 | 74 | if( uvs !== undefined ) { 75 | 76 | for ( i = 0, l = uvs.count; i < l; i ++, nbVertexUvs++ ) { 77 | 78 | uv.x = uvs.getX( i ); 79 | uv.y = uvs.getY( i ); 80 | 81 | // transform the uv to export format 82 | output += 'vt ' + uv.x + ' ' + uv.y + '\n'; 83 | 84 | } 85 | 86 | } 87 | 88 | // normals 89 | 90 | if( normals !== undefined ) { 91 | 92 | normalMatrixWorld.getNormalMatrix( mesh.matrixWorld ); 93 | 94 | for ( i = 0, l = normals.count; i < l; i ++, nbNormals++ ) { 95 | 96 | normal.x = normals.getX( i ); 97 | normal.y = normals.getY( i ); 98 | normal.z = normals.getZ( i ); 99 | 100 | // transfrom the normal to world space 101 | normal.applyMatrix3( normalMatrixWorld ); 102 | 103 | // transform the normal to export format 104 | output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; 105 | 106 | } 107 | 108 | } 109 | 110 | // faces 111 | 112 | if( indices !== null ) { 113 | 114 | for ( i = 0, l = indices.count; i < l; i += 3 ) { 115 | 116 | for( m = 0; m < 3; m ++ ){ 117 | 118 | j = indices.getX( i + m ) + 1; 119 | 120 | face[ m ] = ( indexVertex + j ) + '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ); 121 | 122 | } 123 | 124 | // transform the face to export format 125 | output += 'f ' + face.join( ' ' ) + "\n"; 126 | 127 | } 128 | 129 | } else { 130 | 131 | for ( i = 0, l = vertices.count; i < l; i += 3 ) { 132 | 133 | for( m = 0; m < 3; m ++ ){ 134 | 135 | j = i + m + 1; 136 | 137 | face[ m ] = ( indexVertex + j ) + '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ); 138 | 139 | } 140 | 141 | // transform the face to export format 142 | output += 'f ' + face.join( ' ' ) + "\n"; 143 | 144 | } 145 | 146 | } 147 | 148 | } else { 149 | 150 | console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', geometry ); 151 | 152 | } 153 | 154 | // update index 155 | indexVertex += nbVertex; 156 | indexVertexUvs += nbVertexUvs; 157 | indexNormals += nbNormals; 158 | 159 | }; 160 | 161 | var parseLine = function( line ) { 162 | 163 | var nbVertex = 0; 164 | 165 | var geometry = line.geometry; 166 | var type = line.type; 167 | 168 | if ( geometry instanceof THREE.Geometry ) { 169 | 170 | geometry = new THREE.BufferGeometry().setFromObject( line ); 171 | 172 | } 173 | 174 | if ( geometry instanceof THREE.BufferGeometry ) { 175 | 176 | // shortcuts 177 | var vertices = geometry.getAttribute( 'position' ); 178 | var indices = geometry.getIndex(); 179 | 180 | // name of the line object 181 | output += 'o ' + line.name + '\n'; 182 | 183 | if( vertices !== undefined ) { 184 | 185 | for ( i = 0, l = vertices.count; i < l; i ++, nbVertex++ ) { 186 | 187 | vertex.x = vertices.getX( i ); 188 | vertex.y = vertices.getY( i ); 189 | vertex.z = vertices.getZ( i ); 190 | 191 | // transfrom the vertex to world space 192 | vertex.applyMatrix4( line.matrixWorld ); 193 | 194 | // transform the vertex to export format 195 | output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; 196 | 197 | } 198 | 199 | } 200 | 201 | if ( type === 'Line' ) { 202 | 203 | output += 'l '; 204 | 205 | for ( j = 1, l = vertices.count; j <= l; j++ ) { 206 | 207 | output += ( indexVertex + j ) + ' '; 208 | 209 | } 210 | 211 | output += '\n'; 212 | 213 | } 214 | 215 | if ( type === 'LineSegments' ) { 216 | 217 | for ( j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) { 218 | 219 | output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n'; 220 | 221 | } 222 | 223 | } 224 | 225 | } else { 226 | 227 | console.warn('THREE.OBJExporter.parseLine(): geometry type unsupported', geometry ); 228 | 229 | } 230 | 231 | // update index 232 | indexVertex += nbVertex; 233 | 234 | }; 235 | 236 | object.traverse( function ( child ) { 237 | 238 | if ( child instanceof THREE.Mesh ) { 239 | 240 | parseMesh( child ); 241 | 242 | } 243 | 244 | if ( child instanceof THREE.Line ) { 245 | 246 | parseLine( child ); 247 | 248 | } 249 | 250 | } ); 251 | 252 | return output; 253 | 254 | } 255 | 256 | }; 257 | -------------------------------------------------------------------------------- /js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | ( function () { 11 | 12 | function OrbitConstraint ( object ) { 13 | 14 | this.object = object; 15 | 16 | // "target" sets the location of focus, where the object orbits around 17 | // and where it pans with respect to. 18 | this.target = new THREE.Vector3(); 19 | 20 | // Limits to how far you can dolly in and out ( PerspectiveCamera only ) 21 | this.minDistance = 0; 22 | this.maxDistance = Infinity; 23 | 24 | // Limits to how far you can zoom in and out ( OrthographicCamera only ) 25 | this.minZoom = 0; 26 | this.maxZoom = Infinity; 27 | 28 | // How far you can orbit vertically, upper and lower limits. 29 | // Range is 0 to Math.PI radians. 30 | this.minPolarAngle = 0; // radians 31 | this.maxPolarAngle = Math.PI; // radians 32 | 33 | // How far you can orbit horizontally, upper and lower limits. 34 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 35 | this.minAzimuthAngle = - Infinity; // radians 36 | this.maxAzimuthAngle = Infinity; // radians 37 | 38 | // Set to true to enable damping (inertia) 39 | // If damping is enabled, you must call controls.update() in your animation loop 40 | this.enableDamping = false; 41 | this.dampingFactor = 0.25; 42 | 43 | //////////// 44 | // internals 45 | 46 | var scope = this; 47 | 48 | var EPS = 0.000001; 49 | 50 | // Current position in spherical coordinate system. 51 | var theta; 52 | var phi; 53 | 54 | // Pending changes 55 | var phiDelta = 0; 56 | var thetaDelta = 0; 57 | var scale = 1; 58 | var panOffset = new THREE.Vector3(); 59 | var zoomChanged = false; 60 | 61 | // API 62 | 63 | this.getPolarAngle = function () { 64 | 65 | return phi; 66 | 67 | }; 68 | 69 | this.getAzimuthalAngle = function () { 70 | 71 | return theta; 72 | 73 | }; 74 | 75 | this.rotateLeft = function ( angle ) { 76 | 77 | thetaDelta -= angle; 78 | 79 | }; 80 | 81 | this.rotateUp = function ( angle ) { 82 | 83 | phiDelta -= angle; 84 | 85 | }; 86 | 87 | // pass in distance in world space to move left 88 | this.panLeft = function() { 89 | 90 | var v = new THREE.Vector3(); 91 | 92 | return function panLeft ( distance ) { 93 | 94 | var te = this.object.matrix.elements; 95 | 96 | // get X column of matrix 97 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] ); 98 | v.multiplyScalar( - distance ); 99 | 100 | panOffset.add( v ); 101 | 102 | }; 103 | 104 | }(); 105 | 106 | // pass in distance in world space to move up 107 | this.panUp = function() { 108 | 109 | var v = new THREE.Vector3(); 110 | 111 | return function panUp ( distance ) { 112 | 113 | var te = this.object.matrix.elements; 114 | 115 | // get Y column of matrix 116 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] ); 117 | v.multiplyScalar( distance ); 118 | 119 | panOffset.add( v ); 120 | 121 | }; 122 | 123 | }(); 124 | 125 | // pass in x,y of change desired in pixel space, 126 | // right and down are positive 127 | this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) { 128 | 129 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 130 | 131 | // perspective 132 | var position = scope.object.position; 133 | var offset = position.clone().sub( scope.target ); 134 | var targetDistance = offset.length(); 135 | 136 | // half of the fov is center to top of screen 137 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 138 | 139 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 140 | scope.panLeft( 2 * deltaX * targetDistance / screenHeight ); 141 | scope.panUp( 2 * deltaY * targetDistance / screenHeight ); 142 | 143 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 144 | 145 | // orthographic 146 | scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth ); 147 | scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight ); 148 | 149 | } else { 150 | 151 | // camera neither orthographic or perspective 152 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 153 | 154 | } 155 | 156 | }; 157 | 158 | this.dollyIn = function ( dollyScale ) { 159 | 160 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 161 | 162 | scale /= dollyScale; 163 | 164 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 165 | 166 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); 167 | scope.object.updateProjectionMatrix(); 168 | zoomChanged = true; 169 | 170 | } else { 171 | 172 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 173 | 174 | } 175 | 176 | }; 177 | 178 | this.dollyOut = function ( dollyScale ) { 179 | 180 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 181 | 182 | scale *= dollyScale; 183 | 184 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 185 | 186 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); 187 | scope.object.updateProjectionMatrix(); 188 | zoomChanged = true; 189 | 190 | } else { 191 | 192 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 193 | 194 | } 195 | 196 | }; 197 | 198 | this.update = function() { 199 | 200 | var offset = new THREE.Vector3(); 201 | 202 | // so camera.up is the orbit axis 203 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 204 | var quatInverse = quat.clone().inverse(); 205 | 206 | var lastPosition = new THREE.Vector3(); 207 | var lastQuaternion = new THREE.Quaternion(); 208 | 209 | return function () { 210 | 211 | var position = this.object.position; 212 | 213 | offset.copy( position ).sub( this.target ); 214 | 215 | // rotate offset to "y-axis-is-up" space 216 | offset.applyQuaternion( quat ); 217 | 218 | // angle from z-axis around y-axis 219 | 220 | theta = Math.atan2( offset.x, offset.z ); 221 | 222 | // angle from y-axis 223 | 224 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 225 | 226 | theta += thetaDelta; 227 | phi += phiDelta; 228 | 229 | // restrict theta to be between desired limits 230 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); 231 | 232 | // restrict phi to be between desired limits 233 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 234 | 235 | // restrict phi to be betwee EPS and PI-EPS 236 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 237 | 238 | var radius = offset.length() * scale; 239 | 240 | // restrict radius to be between desired limits 241 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 242 | 243 | // move target to panned location 244 | this.target.add( panOffset ); 245 | 246 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 247 | offset.y = radius * Math.cos( phi ); 248 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 249 | 250 | // rotate offset back to "camera-up-vector-is-up" space 251 | offset.applyQuaternion( quatInverse ); 252 | 253 | position.copy( this.target ).add( offset ); 254 | 255 | this.object.lookAt( this.target ); 256 | 257 | if ( this.enableDamping === true ) { 258 | 259 | thetaDelta *= ( 1 - this.dampingFactor ); 260 | phiDelta *= ( 1 - this.dampingFactor ); 261 | 262 | } else { 263 | 264 | thetaDelta = 0; 265 | phiDelta = 0; 266 | 267 | } 268 | 269 | scale = 1; 270 | panOffset.set( 0, 0, 0 ); 271 | 272 | // update condition is: 273 | // min(camera displacement, camera rotation in radians)^2 > EPS 274 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 275 | 276 | if ( zoomChanged || 277 | lastPosition.distanceToSquared( this.object.position ) > EPS || 278 | 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) { 279 | 280 | lastPosition.copy( this.object.position ); 281 | lastQuaternion.copy( this.object.quaternion ); 282 | zoomChanged = false; 283 | 284 | return true; 285 | 286 | } 287 | 288 | return false; 289 | 290 | }; 291 | 292 | }(); 293 | 294 | }; 295 | 296 | 297 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 298 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 299 | // supported. 300 | // 301 | // Orbit - left mouse / touch: one finger move 302 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 303 | // Pan - right mouse, or arrow keys / touch: three finter swipe 304 | 305 | THREE.OrbitControls = function ( object, domElement ) { 306 | 307 | var constraint = new OrbitConstraint( object ); 308 | 309 | this.domElement = ( domElement !== undefined ) ? domElement : document; 310 | 311 | // API 312 | 313 | Object.defineProperty( this, 'constraint', { 314 | 315 | get: function() { 316 | 317 | return constraint; 318 | 319 | } 320 | 321 | } ); 322 | 323 | this.getPolarAngle = function () { 324 | 325 | return constraint.getPolarAngle(); 326 | 327 | }; 328 | 329 | this.getAzimuthalAngle = function () { 330 | 331 | return constraint.getAzimuthalAngle(); 332 | 333 | }; 334 | 335 | // Set to false to disable this control 336 | this.enabled = true; 337 | 338 | // center is old, deprecated; use "target" instead 339 | this.center = this.target; 340 | 341 | // This option actually enables dollying in and out; left as "zoom" for 342 | // backwards compatibility. 343 | // Set to false to disable zooming 344 | this.enableZoom = true; 345 | this.zoomSpeed = 1.0; 346 | 347 | // Set to false to disable rotating 348 | this.enableRotate = true; 349 | this.rotateSpeed = 1.0; 350 | 351 | // Set to false to disable panning 352 | this.enablePan = true; 353 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 354 | 355 | // Set to true to automatically rotate around the target 356 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 357 | this.autoRotate = false; 358 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 359 | 360 | // Set to false to disable use of the keys 361 | this.enableKeys = true; 362 | 363 | // The four arrow keys 364 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 365 | 366 | // Mouse buttons 367 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 368 | 369 | //////////// 370 | // internals 371 | 372 | var scope = this; 373 | 374 | var rotateStart = new THREE.Vector2(); 375 | var rotateEnd = new THREE.Vector2(); 376 | var rotateDelta = new THREE.Vector2(); 377 | 378 | var panStart = new THREE.Vector2(); 379 | var panEnd = new THREE.Vector2(); 380 | var panDelta = new THREE.Vector2(); 381 | 382 | var dollyStart = new THREE.Vector2(); 383 | var dollyEnd = new THREE.Vector2(); 384 | var dollyDelta = new THREE.Vector2(); 385 | 386 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 387 | 388 | var state = STATE.NONE; 389 | 390 | // for reset 391 | 392 | this.target0 = this.target.clone(); 393 | this.position0 = this.object.position.clone(); 394 | this.zoom0 = this.object.zoom; 395 | 396 | // events 397 | 398 | var changeEvent = { type: 'change' }; 399 | var startEvent = { type: 'start' }; 400 | var endEvent = { type: 'end' }; 401 | 402 | // pass in x,y of change desired in pixel space, 403 | // right and down are positive 404 | function pan( deltaX, deltaY ) { 405 | 406 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 407 | 408 | constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight ); 409 | 410 | } 411 | 412 | this.update = function () { 413 | 414 | if ( this.autoRotate && state === STATE.NONE ) { 415 | 416 | constraint.rotateLeft( getAutoRotationAngle() ); 417 | 418 | } 419 | 420 | if ( constraint.update() === true ) { 421 | 422 | this.dispatchEvent( changeEvent ); 423 | 424 | } 425 | 426 | }; 427 | 428 | this.reset = function () { 429 | 430 | state = STATE.NONE; 431 | 432 | this.target.copy( this.target0 ); 433 | this.object.position.copy( this.position0 ); 434 | this.object.zoom = this.zoom0; 435 | 436 | this.object.updateProjectionMatrix(); 437 | this.dispatchEvent( changeEvent ); 438 | 439 | this.update(); 440 | 441 | }; 442 | 443 | function getAutoRotationAngle() { 444 | 445 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 446 | 447 | } 448 | 449 | function getZoomScale() { 450 | 451 | return Math.pow( 0.95, scope.zoomSpeed ); 452 | 453 | } 454 | 455 | function onMouseDown( event ) { 456 | 457 | if ( scope.enabled === false ) return; 458 | 459 | event.preventDefault(); 460 | 461 | if ( event.button === scope.mouseButtons.ORBIT ) { 462 | 463 | if ( scope.enableRotate === false ) return; 464 | 465 | state = STATE.ROTATE; 466 | 467 | rotateStart.set( event.clientX, event.clientY ); 468 | 469 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 470 | 471 | if ( scope.enableZoom === false ) return; 472 | 473 | state = STATE.DOLLY; 474 | 475 | dollyStart.set( event.clientX, event.clientY ); 476 | 477 | } else if ( event.button === scope.mouseButtons.PAN ) { 478 | 479 | if ( scope.enablePan === false ) return; 480 | 481 | state = STATE.PAN; 482 | 483 | panStart.set( event.clientX, event.clientY ); 484 | 485 | } 486 | 487 | if ( state !== STATE.NONE ) { 488 | 489 | document.addEventListener( 'mousemove', onMouseMove, false ); 490 | document.addEventListener( 'mouseup', onMouseUp, false ); 491 | scope.dispatchEvent( startEvent ); 492 | 493 | } 494 | 495 | } 496 | 497 | function onMouseMove( event ) { 498 | 499 | if ( scope.enabled === false ) return; 500 | 501 | event.preventDefault(); 502 | 503 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 504 | 505 | if ( state === STATE.ROTATE ) { 506 | 507 | if ( scope.enableRotate === false ) return; 508 | 509 | rotateEnd.set( event.clientX, event.clientY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | 515 | // rotating up and down along whole screen attempts to go 360, but limited to 180 516 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 517 | 518 | rotateStart.copy( rotateEnd ); 519 | 520 | } else if ( state === STATE.DOLLY ) { 521 | 522 | if ( scope.enableZoom === false ) return; 523 | 524 | dollyEnd.set( event.clientX, event.clientY ); 525 | dollyDelta.subVectors( dollyEnd, dollyStart ); 526 | 527 | if ( dollyDelta.y > 0 ) { 528 | 529 | constraint.dollyIn( getZoomScale() ); 530 | 531 | } else if ( dollyDelta.y < 0 ) { 532 | 533 | constraint.dollyOut( getZoomScale() ); 534 | 535 | } 536 | 537 | dollyStart.copy( dollyEnd ); 538 | 539 | } else if ( state === STATE.PAN ) { 540 | 541 | if ( scope.enablePan === false ) return; 542 | 543 | panEnd.set( event.clientX, event.clientY ); 544 | panDelta.subVectors( panEnd, panStart ); 545 | 546 | pan( panDelta.x, panDelta.y ); 547 | 548 | panStart.copy( panEnd ); 549 | 550 | } 551 | 552 | if ( state !== STATE.NONE ) scope.update(); 553 | 554 | } 555 | 556 | function onMouseUp( /* event */ ) { 557 | 558 | if ( scope.enabled === false ) return; 559 | 560 | document.removeEventListener( 'mousemove', onMouseMove, false ); 561 | document.removeEventListener( 'mouseup', onMouseUp, false ); 562 | scope.dispatchEvent( endEvent ); 563 | state = STATE.NONE; 564 | 565 | } 566 | 567 | function onMouseWheel( event ) { 568 | 569 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; 570 | 571 | event.preventDefault(); 572 | event.stopPropagation(); 573 | 574 | var delta = 0; 575 | 576 | if ( event.wheelDelta !== undefined ) { 577 | 578 | // WebKit / Opera / Explorer 9 579 | 580 | delta = event.wheelDelta; 581 | 582 | } else if ( event.detail !== undefined ) { 583 | 584 | // Firefox 585 | 586 | delta = - event.detail; 587 | 588 | } 589 | 590 | if ( delta > 0 ) { 591 | 592 | constraint.dollyOut( getZoomScale() ); 593 | 594 | } else if ( delta < 0 ) { 595 | 596 | constraint.dollyIn( getZoomScale() ); 597 | 598 | } 599 | 600 | scope.update(); 601 | scope.dispatchEvent( startEvent ); 602 | scope.dispatchEvent( endEvent ); 603 | 604 | } 605 | 606 | function onKeyDown( event ) { 607 | 608 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 609 | 610 | switch ( event.keyCode ) { 611 | 612 | case scope.keys.UP: 613 | pan( 0, scope.keyPanSpeed ); 614 | scope.update(); 615 | break; 616 | 617 | case scope.keys.BOTTOM: 618 | pan( 0, - scope.keyPanSpeed ); 619 | scope.update(); 620 | break; 621 | 622 | case scope.keys.LEFT: 623 | pan( scope.keyPanSpeed, 0 ); 624 | scope.update(); 625 | break; 626 | 627 | case scope.keys.RIGHT: 628 | pan( - scope.keyPanSpeed, 0 ); 629 | scope.update(); 630 | break; 631 | 632 | } 633 | 634 | } 635 | 636 | function touchstart( event ) { 637 | 638 | if ( scope.enabled === false ) return; 639 | 640 | switch ( event.touches.length ) { 641 | 642 | case 1: // one-fingered touch: rotate 643 | 644 | if ( scope.enableRotate === false ) return; 645 | 646 | state = STATE.TOUCH_ROTATE; 647 | 648 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 649 | break; 650 | 651 | case 2: // two-fingered touch: dolly 652 | 653 | if ( scope.enableZoom === false ) return; 654 | 655 | state = STATE.TOUCH_DOLLY; 656 | 657 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 658 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 659 | var distance = Math.sqrt( dx * dx + dy * dy ); 660 | dollyStart.set( 0, distance ); 661 | break; 662 | 663 | case 3: // three-fingered touch: pan 664 | 665 | if ( scope.enablePan === false ) return; 666 | 667 | state = STATE.TOUCH_PAN; 668 | 669 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 670 | break; 671 | 672 | default: 673 | 674 | state = STATE.NONE; 675 | 676 | } 677 | 678 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); 679 | 680 | } 681 | 682 | function touchmove( event ) { 683 | 684 | if ( scope.enabled === false ) return; 685 | 686 | event.preventDefault(); 687 | event.stopPropagation(); 688 | 689 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 690 | 691 | switch ( event.touches.length ) { 692 | 693 | case 1: // one-fingered touch: rotate 694 | 695 | if ( scope.enableRotate === false ) return; 696 | if ( state !== STATE.TOUCH_ROTATE ) return; 697 | 698 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 699 | rotateDelta.subVectors( rotateEnd, rotateStart ); 700 | 701 | // rotating across whole screen goes 360 degrees around 702 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 703 | // rotating up and down along whole screen attempts to go 360, but limited to 180 704 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 705 | 706 | rotateStart.copy( rotateEnd ); 707 | 708 | scope.update(); 709 | break; 710 | 711 | case 2: // two-fingered touch: dolly 712 | 713 | if ( scope.enableZoom === false ) return; 714 | if ( state !== STATE.TOUCH_DOLLY ) return; 715 | 716 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 717 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 718 | var distance = Math.sqrt( dx * dx + dy * dy ); 719 | 720 | dollyEnd.set( 0, distance ); 721 | dollyDelta.subVectors( dollyEnd, dollyStart ); 722 | 723 | if ( dollyDelta.y > 0 ) { 724 | 725 | constraint.dollyOut( getZoomScale() ); 726 | 727 | } else if ( dollyDelta.y < 0 ) { 728 | 729 | constraint.dollyIn( getZoomScale() ); 730 | 731 | } 732 | 733 | dollyStart.copy( dollyEnd ); 734 | 735 | scope.update(); 736 | break; 737 | 738 | case 3: // three-fingered touch: pan 739 | 740 | if ( scope.enablePan === false ) return; 741 | if ( state !== STATE.TOUCH_PAN ) return; 742 | 743 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 744 | panDelta.subVectors( panEnd, panStart ); 745 | 746 | pan( panDelta.x, panDelta.y ); 747 | 748 | panStart.copy( panEnd ); 749 | 750 | scope.update(); 751 | break; 752 | 753 | default: 754 | 755 | state = STATE.NONE; 756 | 757 | } 758 | 759 | } 760 | 761 | function touchend( /* event */ ) { 762 | 763 | if ( scope.enabled === false ) return; 764 | 765 | scope.dispatchEvent( endEvent ); 766 | state = STATE.NONE; 767 | 768 | } 769 | 770 | function contextmenu( event ) { 771 | 772 | event.preventDefault(); 773 | 774 | } 775 | 776 | this.dispose = function() { 777 | 778 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); 779 | this.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 780 | this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false ); 781 | this.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox 782 | 783 | this.domElement.removeEventListener( 'touchstart', touchstart, false ); 784 | this.domElement.removeEventListener( 'touchend', touchend, false ); 785 | this.domElement.removeEventListener( 'touchmove', touchmove, false ); 786 | 787 | document.removeEventListener( 'mousemove', onMouseMove, false ); 788 | document.removeEventListener( 'mouseup', onMouseUp, false ); 789 | 790 | window.removeEventListener( 'keydown', onKeyDown, false ); 791 | 792 | } 793 | 794 | this.domElement.addEventListener( 'contextmenu', contextmenu, false ); 795 | 796 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 797 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 798 | this.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox 799 | 800 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 801 | this.domElement.addEventListener( 'touchend', touchend, false ); 802 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 803 | 804 | window.addEventListener( 'keydown', onKeyDown, false ); 805 | 806 | // force an update at start 807 | this.update(); 808 | 809 | }; 810 | 811 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 812 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 813 | 814 | Object.defineProperties( THREE.OrbitControls.prototype, { 815 | 816 | object: { 817 | 818 | get: function () { 819 | 820 | return this.constraint.object; 821 | 822 | } 823 | 824 | }, 825 | 826 | target: { 827 | 828 | get: function () { 829 | 830 | return this.constraint.target; 831 | 832 | }, 833 | 834 | set: function ( value ) { 835 | 836 | console.warn( 'THREE.OrbitControls: target is now immutable. Use target.set() instead.' ); 837 | this.constraint.target.copy( value ); 838 | 839 | } 840 | 841 | }, 842 | 843 | minDistance : { 844 | 845 | get: function () { 846 | 847 | return this.constraint.minDistance; 848 | 849 | }, 850 | 851 | set: function ( value ) { 852 | 853 | this.constraint.minDistance = value; 854 | 855 | } 856 | 857 | }, 858 | 859 | maxDistance : { 860 | 861 | get: function () { 862 | 863 | return this.constraint.maxDistance; 864 | 865 | }, 866 | 867 | set: function ( value ) { 868 | 869 | this.constraint.maxDistance = value; 870 | 871 | } 872 | 873 | }, 874 | 875 | minZoom : { 876 | 877 | get: function () { 878 | 879 | return this.constraint.minZoom; 880 | 881 | }, 882 | 883 | set: function ( value ) { 884 | 885 | this.constraint.minZoom = value; 886 | 887 | } 888 | 889 | }, 890 | 891 | maxZoom : { 892 | 893 | get: function () { 894 | 895 | return this.constraint.maxZoom; 896 | 897 | }, 898 | 899 | set: function ( value ) { 900 | 901 | this.constraint.maxZoom = value; 902 | 903 | } 904 | 905 | }, 906 | 907 | minPolarAngle : { 908 | 909 | get: function () { 910 | 911 | return this.constraint.minPolarAngle; 912 | 913 | }, 914 | 915 | set: function ( value ) { 916 | 917 | this.constraint.minPolarAngle = value; 918 | 919 | } 920 | 921 | }, 922 | 923 | maxPolarAngle : { 924 | 925 | get: function () { 926 | 927 | return this.constraint.maxPolarAngle; 928 | 929 | }, 930 | 931 | set: function ( value ) { 932 | 933 | this.constraint.maxPolarAngle = value; 934 | 935 | } 936 | 937 | }, 938 | 939 | minAzimuthAngle : { 940 | 941 | get: function () { 942 | 943 | return this.constraint.minAzimuthAngle; 944 | 945 | }, 946 | 947 | set: function ( value ) { 948 | 949 | this.constraint.minAzimuthAngle = value; 950 | 951 | } 952 | 953 | }, 954 | 955 | maxAzimuthAngle : { 956 | 957 | get: function () { 958 | 959 | return this.constraint.maxAzimuthAngle; 960 | 961 | }, 962 | 963 | set: function ( value ) { 964 | 965 | this.constraint.maxAzimuthAngle = value; 966 | 967 | } 968 | 969 | }, 970 | 971 | enableDamping : { 972 | 973 | get: function () { 974 | 975 | return this.constraint.enableDamping; 976 | 977 | }, 978 | 979 | set: function ( value ) { 980 | 981 | this.constraint.enableDamping = value; 982 | 983 | } 984 | 985 | }, 986 | 987 | dampingFactor : { 988 | 989 | get: function () { 990 | 991 | return this.constraint.dampingFactor; 992 | 993 | }, 994 | 995 | set: function ( value ) { 996 | 997 | this.constraint.dampingFactor = value; 998 | 999 | } 1000 | 1001 | }, 1002 | 1003 | // backward compatibility 1004 | 1005 | noZoom: { 1006 | 1007 | get: function () { 1008 | 1009 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 1010 | return ! this.enableZoom; 1011 | 1012 | }, 1013 | 1014 | set: function ( value ) { 1015 | 1016 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 1017 | this.enableZoom = ! value; 1018 | 1019 | } 1020 | 1021 | }, 1022 | 1023 | noRotate: { 1024 | 1025 | get: function () { 1026 | 1027 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 1028 | return ! this.enableRotate; 1029 | 1030 | }, 1031 | 1032 | set: function ( value ) { 1033 | 1034 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 1035 | this.enableRotate = ! value; 1036 | 1037 | } 1038 | 1039 | }, 1040 | 1041 | noPan: { 1042 | 1043 | get: function () { 1044 | 1045 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 1046 | return ! this.enablePan; 1047 | 1048 | }, 1049 | 1050 | set: function ( value ) { 1051 | 1052 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 1053 | this.enablePan = ! value; 1054 | 1055 | } 1056 | 1057 | }, 1058 | 1059 | noKeys: { 1060 | 1061 | get: function () { 1062 | 1063 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 1064 | return ! this.enableKeys; 1065 | 1066 | }, 1067 | 1068 | set: function ( value ) { 1069 | 1070 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 1071 | this.enableKeys = ! value; 1072 | 1073 | } 1074 | 1075 | }, 1076 | 1077 | staticMoving : { 1078 | 1079 | get: function () { 1080 | 1081 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 1082 | return ! this.constraint.enableDamping; 1083 | 1084 | }, 1085 | 1086 | set: function ( value ) { 1087 | 1088 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 1089 | this.constraint.enableDamping = ! value; 1090 | 1091 | } 1092 | 1093 | }, 1094 | 1095 | dynamicDampingFactor : { 1096 | 1097 | get: function () { 1098 | 1099 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1100 | return this.constraint.dampingFactor; 1101 | 1102 | }, 1103 | 1104 | set: function ( value ) { 1105 | 1106 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1107 | this.constraint.dampingFactor = value; 1108 | 1109 | } 1110 | 1111 | } 1112 | 1113 | } ); 1114 | 1115 | }() ); 1116 | -------------------------------------------------------------------------------- /js/delaunay.js: -------------------------------------------------------------------------------- 1 | // from https://github.com/mapbox/delaunator 2 | 3 | module.exports = Delaunator; 4 | 5 | function Delaunator(points, getX, getY) { 6 | 7 | if (!getX) getX = defaultGetX; 8 | if (!getY) getY = defaultGetY; 9 | 10 | var minX = Infinity; 11 | var minY = Infinity; 12 | var maxX = -Infinity; 13 | var maxY = -Infinity; 14 | 15 | var coords = this.coords = []; 16 | var ids = this.ids = new Uint32Array(points.length); 17 | 18 | for (var i = 0; i < points.length; i++) { 19 | var p = points[i]; 20 | var x = getX(p); 21 | var y = getY(p); 22 | ids[i] = i; 23 | coords[2 * i] = x; 24 | coords[2 * i + 1] = y; 25 | if (x < minX) minX = x; 26 | if (y < minY) minY = y; 27 | if (x > maxX) maxX = x; 28 | if (y > maxY) maxY = y; 29 | } 30 | 31 | var cx = (minX + maxX) / 2; 32 | var cy = (minY + maxY) / 2; 33 | 34 | var minDist = Infinity; 35 | var i0, i1, i2; 36 | 37 | // pick a seed point close to the centroid 38 | for (i = 0; i < points.length; i++) { 39 | var d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]); 40 | if (d < minDist) { 41 | i0 = i; 42 | minDist = d; 43 | } 44 | } 45 | 46 | minDist = Infinity; 47 | 48 | // find the point closest to the seed 49 | for (i = 0; i < points.length; i++) { 50 | if (i === i0) continue; 51 | d = dist(coords[2 * i0], coords[2 * i0 + 1], coords[2 * i], coords[2 * i + 1]); 52 | if (d < minDist && d > 0) { 53 | i1 = i; 54 | minDist = d; 55 | } 56 | } 57 | 58 | var minRadius = Infinity; 59 | 60 | // find the third point which forms the smallest circumcircle with the first two 61 | for (i = 0; i < points.length; i++) { 62 | if (i === i0 || i === i1) continue; 63 | 64 | var r = circumradius( 65 | coords[2 * i0], coords[2 * i0 + 1], 66 | coords[2 * i1], coords[2 * i1 + 1], 67 | coords[2 * i], coords[2 * i + 1]); 68 | 69 | if (r < minRadius) { 70 | i2 = i; 71 | minRadius = r; 72 | } 73 | } 74 | 75 | if (minRadius === Infinity) { 76 | throw new Error('No Delaunay triangulation exists for this input.'); 77 | } 78 | 79 | // swap the order of the seed points for counter-clockwise orientation 80 | if (area(coords[2 * i0], coords[2 * i0 + 1], 81 | coords[2 * i1], coords[2 * i1 + 1], 82 | coords[2 * i2], coords[2 * i2 + 1]) < 0) { 83 | 84 | var tmp = i1; 85 | i1 = i2; 86 | i2 = tmp; 87 | } 88 | 89 | var i0x = coords[2 * i0]; 90 | var i0y = coords[2 * i0 + 1]; 91 | var i1x = coords[2 * i1]; 92 | var i1y = coords[2 * i1 + 1]; 93 | var i2x = coords[2 * i2]; 94 | var i2y = coords[2 * i2 + 1]; 95 | 96 | var center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); 97 | 98 | // sort the points by distance from the seed triangle circumcenter 99 | quicksort(ids, coords, 0, ids.length - 1, center.x, center.y); 100 | 101 | // initialize a circular doubly-linked list that will hold an advancing convex hull 102 | this.hull = insertNode(coords, i0); 103 | this.hull.t = 0; 104 | this.hull = insertNode(coords, i1, this.hull); 105 | this.hull.t = 1; 106 | this.hull = insertNode(coords, i2, this.hull); 107 | this.hull.t = 2; 108 | 109 | var maxTriangles = 2 * points.length - 5; 110 | var triangles = this.triangles = new Uint32Array(maxTriangles * 3); 111 | triangles[0] = i0; 112 | triangles[1] = i1; 113 | triangles[2] = i2; 114 | this.trianglesLen = 3; 115 | 116 | var adjacent = this.adjacent = new Int32Array(maxTriangles * 3); 117 | adjacent[0] = -1; 118 | adjacent[1] = -1; 119 | adjacent[2] = -1; 120 | 121 | var xp, yp; 122 | for (var k = 0; k < ids.length; k++) { 123 | i = ids[k]; 124 | x = coords[2 * i]; 125 | y = coords[2 * i + 1]; 126 | 127 | // skip duplicate points 128 | if (x === xp && y === yp) continue; 129 | xp = x; 130 | yp = y; 131 | 132 | // skip seed triangle points 133 | if ((x === i0x && y === i0y) || 134 | (x === i1x && y === i1y) || 135 | (x === i2x && y === i2y)) continue; 136 | 137 | // find a visible edge on the convex hull 138 | var e = this.hull; 139 | while (area(x, y, e.x, e.y, e.next.x, e.next.y) >= 0) { 140 | e = e.next; 141 | if (e === this.hull) { 142 | throw new Error('Something is wrong with the input points.'); 143 | } 144 | } 145 | var walkBack = e === this.hull; 146 | 147 | // add the first triangle from the point 148 | var t = this._addTriangle(i, e); 149 | adjacent[t] = -1; 150 | adjacent[t + 1] = -1; 151 | this._link(t + 2, e.t); 152 | 153 | e.t = t; // keep track of boundary triangles on the hull 154 | e = insertNode(coords, i, e); 155 | 156 | // recursively flip triangles from the point until they satisfy the Delaunay condition 157 | e.t = this._legalize(t + 2); 158 | 159 | // walk forward through the hull, adding more triangles and flipping recursively 160 | var q = e.next; 161 | while (area(x, y, q.x, q.y, q.next.x, q.next.y) < 0) { 162 | 163 | t = this._addTriangle(i, q); 164 | this._link(t, q.prev.t); 165 | adjacent[t + 1] = -1; 166 | this._link(t + 2, q.t); 167 | 168 | q.prev.t = this._legalize(t + 2); 169 | 170 | this.hull = removeNode(q); 171 | q = q.next; 172 | } 173 | 174 | if (!walkBack) continue; 175 | 176 | // walk backward from the other side, adding more triangles and flipping 177 | q = e.prev; 178 | while (area(x, y, q.prev.x, q.prev.y, q.x, q.y) < 0) { 179 | 180 | t = this._addTriangle(i, q.prev); 181 | adjacent[t] = -1; 182 | this._link(t + 1, q.t); 183 | this._link(t + 2, q.prev.t); 184 | 185 | this._legalize(t + 2); 186 | 187 | q.prev.t = t; 188 | this.hull = removeNode(q); 189 | q = q.prev; 190 | } 191 | } 192 | 193 | // trim typed triangle mesh arrays 194 | this.triangles = triangles.subarray(0, this.trianglesLen); 195 | this.adjacent = adjacent.subarray(0, this.trianglesLen); 196 | } 197 | 198 | Delaunator.prototype = { 199 | 200 | _legalize: function (a) { 201 | var triangles = this.triangles; 202 | var coords = this.coords; 203 | var adjacent = this.adjacent; 204 | 205 | var b = adjacent[a]; 206 | 207 | var a0 = a - a % 3; 208 | var b0 = b - b % 3; 209 | 210 | var al = a0 + (a + 1) % 3; 211 | var ar = a0 + (a + 2) % 3; 212 | var br = b0 + (b + 1) % 3; 213 | var bl = b0 + (b + 2) % 3; 214 | 215 | var p0 = triangles[ar]; 216 | var pr = triangles[a]; 217 | var pl = triangles[al]; 218 | var p1 = triangles[bl]; 219 | 220 | var illegal = inCircle( 221 | coords[2 * p0], coords[2 * p0 + 1], 222 | coords[2 * pr], coords[2 * pr + 1], 223 | coords[2 * pl], coords[2 * pl + 1], 224 | coords[2 * p1], coords[2 * p1 + 1]); 225 | 226 | if (illegal) { 227 | triangles[a] = p1; 228 | triangles[b] = p0; 229 | 230 | this._link(a, adjacent[bl]); 231 | this._link(b, adjacent[ar]); 232 | this._link(ar, bl); 233 | 234 | this._legalize(a); 235 | return this._legalize(br); 236 | } 237 | 238 | return ar; 239 | }, 240 | 241 | _link: function (a, b) { 242 | this.adjacent[a] = b; 243 | if (b !== -1) this.adjacent[b] = a; 244 | }, 245 | 246 | _addTriangle(i, e) { 247 | var t = this.trianglesLen; 248 | this.triangles[t] = e.i; 249 | this.triangles[t + 1] = i; 250 | this.triangles[t + 2] = e.next.i; 251 | this.trianglesLen += 3; 252 | return t; 253 | } 254 | }; 255 | 256 | function dist(ax, ay, bx, by) { 257 | var dx = ax - bx; 258 | var dy = ay - by; 259 | return dx * dx + dy * dy; 260 | } 261 | 262 | function area(px, py, qx, qy, rx, ry) { 263 | return (qy - py) * (rx - qx) - (qx - px) * (ry - qy); 264 | } 265 | 266 | function inCircle(ax, ay, bx, by, cx, cy, px, py) { 267 | ax -= px; 268 | ay -= py; 269 | bx -= px; 270 | by -= py; 271 | cx -= px; 272 | cy -= py; 273 | 274 | var ap = ax * ax + ay * ay; 275 | var bp = bx * bx + by * by; 276 | var cp = cx * cx + cy * cy; 277 | 278 | var det = ax * (by * cp - bp * cy) - 279 | ay * (bx * cp - bp * cx) + 280 | ap * (bx * cy - by * cx); 281 | 282 | return det < 0; 283 | } 284 | 285 | function circumradius(ax, ay, bx, by, cx, cy) { 286 | bx -= ax; 287 | by -= ay; 288 | cx -= ax; 289 | cy -= ay; 290 | 291 | var bl = bx * bx + by * by; 292 | var cl = cx * cx + cy * cy; 293 | 294 | if (bl === 0 || cl === 0) return Infinity; 295 | 296 | var d = bx * cy - by * cx; 297 | if (d === 0) return Infinity; 298 | 299 | var x = (cy * bl - by * cl) * 0.5 / d; 300 | var y = (bx * cl - cx * bl) * 0.5 / d; 301 | 302 | return x * x + y * y; 303 | } 304 | 305 | function circumcenter(ax, ay, bx, by, cx, cy) { 306 | bx -= ax; 307 | by -= ay; 308 | cx -= ax; 309 | cy -= ay; 310 | 311 | var bl = bx * bx + by * by; 312 | var cl = cx * cx + cy * cy; 313 | 314 | var d = bx * cy - by * cx; 315 | 316 | var x = (cy * bl - by * cl) * 0.5 / d; 317 | var y = (bx * cl - cx * bl) * 0.5 / d; 318 | 319 | return { 320 | x: ax + x, 321 | y: ay + y 322 | }; 323 | } 324 | 325 | // create a new node in a doubly linked list 326 | function insertNode(coords, i, prev) { 327 | var node = { 328 | i: i, 329 | x: coords[2 * i], 330 | y: coords[2 * i + 1], 331 | t: 0, 332 | prev: null, 333 | next: null 334 | }; 335 | 336 | if (!prev) { 337 | node.prev = node; 338 | node.next = node; 339 | 340 | } else { 341 | node.next = prev.next; 342 | node.prev = prev; 343 | prev.next.prev = node; 344 | prev.next = node; 345 | } 346 | return node; 347 | } 348 | 349 | function removeNode(node) { 350 | node.prev.next = node.next; 351 | node.next.prev = node.prev; 352 | return node.prev; 353 | } 354 | 355 | function quicksort(ids, coords, left, right, cx, cy) { 356 | var i, j, temp; 357 | 358 | if (right - left <= 20) { 359 | for (i = left + 1; i <= right; i++) { 360 | temp = ids[i]; 361 | j = i - 1; 362 | while (j >= left && compare(coords, ids[j], temp, cx, cy) > 0) ids[j + 1] = ids[j--]; 363 | ids[j + 1] = temp; 364 | } 365 | } else { 366 | var median = (left + right) >> 1; 367 | i = left + 1; 368 | j = right; 369 | swap(ids, median, i); 370 | if (compare(coords, ids[left], ids[right], cx, cy) > 0) swap(ids, left, right); 371 | if (compare(coords, ids[i], ids[right], cx, cy) > 0) swap(ids, i, right); 372 | if (compare(coords, ids[left], ids[i], cx, cy) > 0) swap(ids, left, i); 373 | 374 | temp = ids[i]; 375 | while (true) { 376 | do i++; while (compare(coords, ids[i], temp, cx, cy) < 0); 377 | do j--; while (compare(coords, ids[j], temp, cx, cy) > 0); 378 | if (j < i) break; 379 | swap(ids, i, j); 380 | } 381 | ids[left + 1] = ids[j]; 382 | ids[j] = temp; 383 | 384 | if (right - i + 1 >= j - left) { 385 | quicksort(ids, coords, i, right, cx, cy); 386 | quicksort(ids, coords, left, j - 1, cx, cy); 387 | } else { 388 | quicksort(ids, coords, left, j - 1, cx, cy); 389 | quicksort(ids, coords, i, right, cx, cy); 390 | } 391 | } 392 | } 393 | 394 | function compare(coords, i, j, cx, cy) { 395 | var d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy); 396 | var d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy); 397 | return (d1 - d2) || (coords[2 * i] - coords[2 * j]) || (coords[2 * i + 1] - coords[2 * j + 1]); 398 | } 399 | 400 | function swap(arr, i, j) { 401 | var tmp = arr[i]; 402 | arr[i] = arr[j]; 403 | arr[j] = tmp; 404 | } 405 | 406 | function defaultGetX(p) { 407 | return p[0]; 408 | } 409 | function defaultGetY(p) { 410 | return p[1]; 411 | } 412 | -------------------------------------------------------------------------------- /js/gis.js: -------------------------------------------------------------------------------- 1 | var maps = []; //array to store images 2 | var mapFocus = 0; //number in array to focus 3 | var bindTwo = false; 4 | var tSlider1, tSlider2; 5 | var bSlider1, bSlider2; 6 | var nNodeSelect; 7 | var dim = 3; 8 | var mode = 0; //mode 0 = edit nodes, 1 = edit distances 9 | var imageOn = true; 10 | var rt = 0; //rotate variable 11 | var zoom = 1; 12 | var nNodes = 4; 13 | //var trans = 1.0; 14 | var mapImages = []; 15 | var autoRotate = false; 16 | var worm = false; 17 | var delaunayOn = false; 18 | var bindDist = 180; 19 | var connectDist = 20; 20 | var connectLastNodes = 1; //connect last n-nodes in two map mode 21 | var scrollLock = false; 22 | var editMode = true; 23 | 24 | var jitterEdgeMultiplierMagnitude = 0.01; // fractional maximum jitter multiplier per edge 25 | var jitterVertexAbsoluteMagnitude = 1.00; // maximum jitter addition per node x or y coordinate 26 | 27 | //these generated dynamically to fit screen 28 | var canvaswidth = 0; 29 | var canvasheight = 0; 30 | 31 | var dragDiffX = 0; 32 | var dragDiffY = 0; 33 | var dragOffX = 0; 34 | var dragOffY = 0; 35 | var dragging = false; 36 | 37 | var vrModeOn = false; 38 | var renderer; 39 | 40 | new p5(); 41 | 42 | 43 | //BEGIN LEFT CANVAS 44 | //instance mode of p5.js https://github.com/processing/p5.js/wiki/p5.js-overview#instantiation--namespace 45 | 46 | setup = function() { 47 | // create canvas 48 | noLoop(); 49 | var c = createCanvas(document.getElementById("leftCanv").offsetWidth,document.getElementById("leftCanv").offsetHeight); 50 | c.parent("leftCanv"); 51 | //colorMode('HSB',360,100,100,100) 52 | background('#fff'); 53 | fill(0,0,0,100); 54 | noStroke(); 55 | textSize(24); 56 | textAlign('CENTER'); 57 | text('Drag and drop a map or other image here', width/4, height/2); 58 | fill(0,0,10,80); 59 | noStroke(); 60 | textSize(14); 61 | textAlign('CENTER'); 62 | text(' (Enfolding supports .JPG, .JPEG, .GIF, .PNG, .SVG)', width/4, height/1.87);// Add an event for when a file is dropped onto the canvas 63 | c.drop(gotFile); 64 | //createDiv('data: ').id('dataResults'); 65 | }; 66 | 67 | draw = function() {}; 68 | 69 | 70 | 71 | gotFile = function(file) { 72 | //console.log(file); 73 | if (file.type === 'image') { 74 | // Create an image DOM element and add to maps array 75 | // console.log("We're currently seeing: "+file.data) 76 | loadImage(file.data,addMap);//callback to addMap once image loaded 77 | //array mapImages holds map images for three.js access 78 | //images also added to Map objects 79 | //double storage should be consolidated in future versions 80 | //currently unable to access img file from Map class for three.js, possible because 81 | //stored as a p5 filetype (using file.data might fix) 82 | mapImages.push(file.data); 83 | //console.log('map focus: ' + mapFocus); 84 | } else { 85 | console.log('Not an image file!'); 86 | } 87 | }; 88 | 89 | 90 | addMap = function(imgLoaded){ 91 | var offX = 30 92 | if(maps.length > 0){ 93 | offX = maps[mapFocus].img.width + 50; 94 | } 95 | append(maps,new Map('map'+maps.length,1,imgLoaded,offX, maps.length)); 96 | mapFocus = maps.length - 1; //change focus to last uploaded map 97 | maps[mapFocus].makeNew(); 98 | maps[mapFocus].reCalculate(); 99 | 100 | //create new transparency slider for map 101 | var div = document.getElementById('transSliders'); 102 | var newSlider = document.createElement("INPUT"); 103 | newSlider.setAttribute("type", "range"); 104 | newSlider.setAttribute("min", 0); 105 | newSlider.setAttribute("max", 100); 106 | newSlider.setAttribute("value", 100); 107 | newSlider.setAttribute("id",maps.length-1); 108 | newSlider.setAttribute("onchange","tslide(this)"); 109 | newSlider.style.width = "150px"; 110 | var targetDiv = document.getElementById("transSliders"); 111 | targetDiv.innerHTML += "Opacity, map#" + maps.length + "
"; 112 | targetDiv.appendChild(newSlider); 113 | targetDiv.innerHTML += "
"; 114 | } 115 | 116 | deleteIns = function(){ 117 | var allInputs = document.getElementsByClassName(maps[mapFocus].name); 118 | //console.log(allInputs); 119 | for(var i = allInputs.length-1; i >= 0; i--){ 120 | allInputs[i].remove(); 121 | } 122 | } 123 | 124 | addAllInputs = function(){ 125 | maps[mapFocus].addInputs(); 126 | } 127 | 128 | moveIns = function(){ 129 | var allInputs = document.getElementsByClassName("mapIn"); 130 | for(var i = 0; i < allInputs.length; i++){ 131 | var ogY = parseInt(allInputs[i].dataset.ypos); 132 | var ogX = parseInt(allInputs[i].dataset.xpos); 133 | allInputs[i].style.top = ogY + dragOffY; 134 | allInputs[i].style.left = ogX + dragOffX; 135 | } 136 | } 137 | 138 | //input.attribute("data-xpos", posX); 139 | //input.attribute("data-ypos", posY); 140 | 141 | showDefaultIns = function(){ 142 | var allInputs = document.getElementsByClassName("mapIn"); 143 | for(var i = 0; i < allInputs.length; i++){ 144 | allInputs[i].style.visibility = "hidden"; 145 | } 146 | var allInputs = document.getElementsByClassName("defaultIn"); 147 | for(var i = 0; i < allInputs.length; i++){ 148 | allInputs[i].style.visibility = "visible"; 149 | } 150 | } 151 | 152 | gridMode = function(){ 153 | //resetMaps(); 154 | maps[mapFocus].grid(); 155 | gridMode = true; 156 | recalcMaps(); 157 | } 158 | 159 | gridMode2 = function(){ 160 | //resetMaps(); 161 | deleteIns(); 162 | //maps[mapFocus].reset(p); 163 | maps[mapFocus].grid2(); 164 | gridMode = true; 165 | recalcMaps(); 166 | } 167 | 168 | testNodes = function(){ 169 | maps[mapFocus].addTestNodes(); 170 | } 171 | 172 | testNodes2 = function(){ 173 | maps[mapFocus].addTestNodes2(); 174 | } 175 | 176 | mouseReleased = function(){ 177 | if(dragging){ 178 | moveIns(); 179 | //showDefaultIns(); 180 | dragging = false; 181 | } else { 182 | maps[mapFocus].selectNode(mouseX,mouseY); 183 | } 184 | }; 185 | 186 | mousePressed = function(){ 187 | if(editMode){ 188 | dragDiffX = mouseX - dragOffX; 189 | dragDiffY = mouseY - dragOffY; 190 | } 191 | } 192 | 193 | mouseDragged = function(){ 194 | dragging = true; 195 | //hideIns(); 196 | if(editMode){ 197 | dragOffX = mouseX - dragDiffX; 198 | dragOffY = mouseY - dragDiffY; 199 | displayMaps(); 200 | } 201 | } 202 | 203 | //FUNCTION FOR ZOOMING IN ON MAPS IN EDIT MODE 204 | // mouseWheel = function(event){ 205 | // if(!scrollLock){ 206 | // maps[mapFocus].mapZoom(event.delta,p); 207 | // } 208 | // return false; 209 | // } 210 | 211 | trans1 = function(){ 212 | transOne(); 213 | } 214 | 215 | trans2 = function(){ 216 | transTwo(); 217 | } 218 | 219 | bind1 = function(){ 220 | bindDist = bSlider1.value(); 221 | reCalc(); 222 | } 223 | 224 | bind2 = function(){ 225 | connectDist = bSlider2.value(); 226 | reCalc(); 227 | } 228 | 229 | keyPressed = function(){ 230 | 231 | } 232 | 233 | //END LEFT CANVAS 234 | 235 | //BEGIN RIGHT CANVAS, THREE.JS 236 | 237 | window.onload = function() { 238 | canvaswidth = document.getElementById('leftCanv').clientWidth; 239 | canvasheight = document.getElementById('leftCanv').clientHeight; 240 | initThree(); 241 | animateThree(); 242 | }; 243 | 244 | var scene, camera; 245 | var strDownloadMime = "image/octet-stream"; 246 | var wireframeOn = false; 247 | 248 | //var material = new THREE.MeshLambertMaterial( { color: 0x0000FF, transparent: true, opacity: 0.8, side: THREE.DoubleSide, wireframe:wireframeOn } ); 249 | var slices = []; 250 | 251 | 252 | function initThree() { 253 | renderer = new THREE.WebGLRenderer({ 254 | alpha: true, 255 | preserveDrawingBuffer: true 256 | }); 257 | renderer.setSize(window.innerWidth, window.innerHeight); 258 | document.body.appendChild(renderer.domElement); 259 | 260 | scene = new THREE.Scene(); 261 | camera = new THREE.PerspectiveCamera( 262 | 75, // Field of view 263 | canvaswidth / canvasheight, // Aspect ratio 264 | 0.1, // Near plane 265 | 100000 // Far plane 266 | ); 267 | camera.position.set( 0, 0, canvaswidth/2 ); 268 | camera.up = new THREE.Vector3(0,0,1); 269 | camera.lookAt( new THREE.Vector3(0,0,0)); 270 | scene.add(camera); 271 | 272 | renderer.setSize( canvaswidth, canvasheight ); 273 | //renderer.vr.enabled = true; 274 | 275 | var div = document.getElementById('rightCanv'); 276 | //console.log(div); 277 | div.appendChild(renderer.domElement); 278 | 279 | controls = new THREE.OrbitControls(camera, renderer.domElement); 280 | 281 | scene.add( new THREE.AmbientLight( 0xC0C0C0 ) ); 282 | 283 | var dLight = new THREE.DirectionalLight(0xFFFFFF, 0.2); 284 | dLight.position.set(5,5,-5); 285 | scene.add(dLight); 286 | 287 | var dLight2 = new THREE.DirectionalLight(0xFFFFFF, 0.5); 288 | dLight2.position.set(0,5,-5); 289 | //scene.add(dLight2); 290 | 291 | var dLight3 = new THREE.DirectionalLight(0xFFFFFF, 0.5); 292 | dLight3.position.set(0,0,5); 293 | //scene.add(dLight3); 294 | 295 | var dLight4 = new THREE.DirectionalLight(0xFFFFFF, 0.2); 296 | dLight4.position.set(5,-5,5); 297 | scene.add(dLight4); 298 | 299 | var light = new THREE.AmbientLight( 0x404040 ); // soft white light 300 | //scene.add( light ); 301 | 302 | renderer.setClearColor(0xFFFFFF, 1); 303 | //The X axis is red. The Y axis is green. The Z axis is blue. 304 | //var axesHelper = new THREE.AxesHelper( 5 ); 305 | //scene.add( axesHelper ); 306 | 307 | //plotTriangles(mdsArray,triangles); 308 | }; 309 | 310 | function animateThree(){ 311 | var render = function () { 312 | requestAnimationFrame( render ); 313 | 314 | if(autoRotate){ 315 | for ( var i = 0; i < slices.length; i ++ ) { 316 | var slice = slices[ i ]; 317 | 318 | slice.rotation.x += 0.005; 319 | slice.rotation.z += -0.005; 320 | //console.log(slice.rotation.z); 321 | //slice.x += .00001; 322 | } 323 | } 324 | if(vrModeOn){ 325 | 326 | } 327 | controls.update(); 328 | renderer.render(scene, camera); 329 | 330 | 331 | }; 332 | render(); 333 | 334 | }; 335 | 336 | 337 | function saveAsImage() { 338 | var imgData, imgNode; 339 | 340 | try { 341 | var strMime = "image/jpeg"; 342 | imgData = renderer.domElement.toDataURL(strMime); 343 | 344 | saveFile(imgData.replace(strMime, strDownloadMime), "test.jpg"); 345 | 346 | } catch (e) { 347 | console.log(e); 348 | return; 349 | } 350 | 351 | } 352 | 353 | var saveFile = function(strData, filename) { 354 | var link = document.createElement('a'); 355 | if (typeof link.download === 'string') { 356 | document.body.appendChild(link); 357 | link.download = filename; 358 | link.href = strData; 359 | link.click(); 360 | document.body.removeChild(link); 361 | } else { 362 | location.replace(uri); 363 | } 364 | } 365 | 366 | //END RIGHT CANVAS 367 | 368 | //map class, contains main data structure 369 | function Map(name, opac, img, xoff, id){ 370 | this.name = name; 371 | this.opac = opac; 372 | this.img = img; 373 | this.id = id; 374 | this.internalNodes = []; 375 | this.internalEdges = []; 376 | this.offSetX = xoff; 377 | this.offSetY = 50; 378 | this.trias = [0]; 379 | this.mdsMatrix = []; 380 | this.gridMode = false; 381 | this.clickCount = 0; //count clicks for long distance edges in gridMode 382 | this.selectNo = 0; //0 = nothing selected, 1 = first node selected 383 | this.selectedOne; 384 | this.selectedTwo; 385 | //this.delaunayOn = true; 386 | this.trans = 1.0; 387 | this.zoomScroll = 1.0; 388 | 389 | 390 | this.makeNew = function(){ 391 | //start with 4 nodes at corners 392 | append(this.internalNodes, new Node(0,0)); 393 | append(this.internalNodes, new Node(this.img.width,0)); 394 | append(this.internalNodes, new Node(this.img.width, this.img.height)); 395 | append(this.internalNodes, new Node(0,this.img.height)); 396 | 397 | 398 | //and 4 edges that connect them 399 | append(this.internalEdges, new Edge(0,1,nodeDist(this.internalNodes[0],this.internalNodes[1]))); 400 | append(this.internalEdges, new Edge(1,2,nodeDist(this.internalNodes[1],this.internalNodes[2]))); 401 | append(this.internalEdges, new Edge(2,3,nodeDist(this.internalNodes[2],this.internalNodes[3]))); 402 | append(this.internalEdges, new Edge(3,0,nodeDist(this.internalNodes[3],this.internalNodes[0]))); 403 | 404 | for(var j = 0; j < this.internalEdges.length; j++){ 405 | makeInput(this.internalEdges[j], this.internalNodes, j, this.offSetX, this.offSetY, this.name + " defaultIn"); 406 | } 407 | 408 | strokeWeight(3); 409 | stroke(0,0,0,100); 410 | this.display(); 411 | } 412 | 413 | this.display = function(){ 414 | //scale(this.zoomScroll); 415 | push(); 416 | translate(this.offSetX + dragOffX,this.offSetY + dragOffY); 417 | if(imageOn){ 418 | image(this.img,0,0,this.img.width,this.img.height); 419 | } 420 | if(delaunayOn){ 421 | if(this.trias.length % 3 == 0){ 422 | for(var i = 0; i < this.trias.length; i+=3){ 423 | var x1 = this.internalNodes[this.trias[i]].xpos; 424 | var y1 = this.internalNodes[this.trias[i]].ypos; 425 | var x2 = this.internalNodes[this.trias[i+1]].xpos; 426 | var y2 = this.internalNodes[this.trias[i+1]].ypos; 427 | var x3 = this.internalNodes[this.trias[i+2]].xpos; 428 | var y3 = this.internalNodes[this.trias[i+2]].ypos; 429 | stroke(255,0,0,50); 430 | strokeWeight(8); 431 | line(x1,y1,x2,y2); 432 | line(x2,y2,x3,y3); 433 | line(x3,y3,x1,y1); 434 | } 435 | } 436 | } 437 | //display nodes 438 | stroke(0,0,0,150); 439 | strokeWeight(1); 440 | for(var i=0; i < this.internalNodes.length; i++){ 441 | // if(this.internalNodes.length-1 == i){ 442 | // stroke(255); 443 | // strokeWeight(3); 444 | // } 445 | //if node is selected, turn on highlight color 446 | if(this.internalNodes[i].nodeHL == true){ 447 | fill(255,0,0,100); 448 | } else { 449 | fill(0,0,0,100); 450 | } 451 | ellipse(this.internalNodes[i].xpos,this.internalNodes[i].ypos, 10, 10); 452 | //text(i,this.internalNodes[i].xpos,this.internalNodes[i].ypos); 453 | } 454 | //display edges 455 | strokeWeight(3); 456 | stroke(0,0,0, 150); 457 | for(var i=0; i < this.internalEdges.length; i++){ 458 | var x1 = this.internalNodes[this.internalEdges[i].node1].xpos; 459 | var x2 = this.internalNodes[this.internalEdges[i].node2].xpos; 460 | var y1 = this.internalNodes[this.internalEdges[i].node1].ypos; 461 | var y2 = this.internalNodes[this.internalEdges[i].node2].ypos; 462 | line(x1,y1,x2,y2); 463 | } 464 | pop(); 465 | }; 466 | 467 | this.blur = function(){ 468 | noStroke(); 469 | fill(255,150); 470 | rect(0+this.offSetX+dragOffX,0+this.offSetY+dragOffY,this.img.width+this.offSetX,this.img.height+this.offSetY); 471 | }; 472 | 473 | this.returnImg = function(){ 474 | return this.img; 475 | }; 476 | 477 | this.reCalculate = function(){ 478 | //updateData(p); 479 | resetThree(); 480 | var matrices = makeMatrix(this.id); 481 | this.mdsMatrix = matrices[0]; 482 | this.trias = matrices[1]; 483 | plotTriangles(this.mdsMatrix, this.trias, this.id, false); 484 | displayMaps(); 485 | //console.log("recalculate"); 486 | }; 487 | 488 | this.reCalculateW = function(){ 489 | var matrices = makeMatrix(this.id); 490 | this.trias = matrices[1]; 491 | displayMaps(); 492 | }; 493 | 494 | this.mapZoom = function(scrollVal){ 495 | this.zoomScroll += scrollVal/30; 496 | displayMaps(); 497 | } 498 | 499 | this.addInputs = function(){ 500 | deleteIns(); 501 | for(var i = 0; i < this.internalEdges.length; i++){ 502 | makeInput(this.internalEdges[i], this.internalNodes, i, this.offSetX, this.offSetY, this.name); 503 | } 504 | } 505 | 506 | this.selectNode = function(mx,my){ 507 | mx = mx-this.offSetX-dragOffX; 508 | my = my-this.offSetY-dragOffY; 509 | for(var i = 0; i < this.internalNodes.length; i++){ 510 | if(dist(mx,my,this.internalNodes[i].xpos,this.internalNodes[i].ypos) < 10){ 511 | if(this.selectNo == 0){ 512 | this.unHighlightNodes(); 513 | this.selectedOne = i; 514 | this.selectNo = 1; 515 | } else if(this.selectNo == 1){ 516 | this.selectedTwo = i; 517 | this.selectNo = 0; 518 | this.checkForEdge(this.selectedOne,this.selectedTwo); 519 | } 520 | this.internalNodes[i].nodeHL = true; 521 | } 522 | } 523 | this.display(); 524 | } 525 | 526 | this.unHighlightNodes = function(){ 527 | for(var i = 0; i < this.internalNodes.length; i++){ 528 | this.internalNodes[i].nodeHL = false; 529 | } 530 | } 531 | this.checkForEdge = function(one,two){ 532 | var foundEdge = false; 533 | for(var i = 0; i < this.internalEdges.length; i++){ 534 | if(this.internalEdges[i].node1 == one && this.internalEdges[i].node2 == two){ 535 | foundEdge = true; 536 | makeInput(this.internalEdges[i], this.internalNodes, i, this.offSetX, this.offSetY,this.name); 537 | } else if(this.internalEdges[i].node1 == two && this.internalEdges[i].node2 == one){ 538 | makeInput(this.internalEdges[i], this.internalNodes, i, this.offSetX, this.offSetY, this.name); 539 | foundEdge = true; 540 | } 541 | } 542 | if(!foundEdge){ 543 | alert('no edge'); 544 | } 545 | } 546 | 547 | //called when mouseReleased 548 | this.addNode = function(mx,my){ 549 | if(mx >this.offSetX && mx < this.img.width+this.offSetX && my > 0 && my < this.img.height+this.offSetY){ //check if on map 550 | if(this.gridMode){ 551 | if(this.clickCount % 2 == 0){//evens are first clicks 552 | this.autoAddNode(mx-this.offSetX,my-this.offSetY); 553 | } else {//odds complete edge 554 | this.autoAddNode(mx-this.offSetX,my-this.offSetY); 555 | var d = nodeDist(this.internalNodes[this.internalNodes.length-2], this.internalNodes[this.internalNodes.length-1]); 556 | append(this.internalEdges, new Edge(this.internalNodes.length-2,this.internalNodes.length-1,d)); //connect two above nodes 557 | makeInput(this.internalEdges[this.internalEdges.length-1], this.internalNodes, this.internalEdges.length-1, this.offSetX, this.offSetY, this.name + " defaultIn"); 558 | } 559 | this.clickCount++; 560 | } else { 561 | append(this.internalNodes, new Node(mx-this.offSetX,my-this.offSetY)); 562 | 563 | //third argument = n nearest nodes to connect to 564 | var nodeShort = findClosestNNodes(mx-this.offSetX,my-this.offSetY, nNodes, this.internalNodes); 565 | //console.log(nodeShort); 566 | for(var i = 0; i < nodeShort.length; i++){ 567 | //subtract offsets from mx, my because nodes start from (0,0), then translated 568 | append(this.internalEdges, new Edge(nodeShort[i], this.internalNodes.length - 1, nodeDistXY(this.internalNodes[nodeShort[i]], mx-this.offSetX,my-this.offSetY))); 569 | makeInput(this.internalEdges[this.internalEdges.length-1], this.internalNodes, this.internalEdges.length-1, this.offSetX, this.offSetY, this.name + " defaultIn"); 570 | } 571 | //console.log(this.internalEdges[this.internalEdges.length-1]); 572 | displayMaps(); 573 | //updateData(p); 574 | } 575 | }; 576 | }; 577 | 578 | //called for grid building 579 | this.autoAddNode = function(mx,my){ 580 | append(this.internalNodes, new Node(mx,my)); 581 | var nodeShort = findClosestNNodes(mx,my, nNodes, this.internalNodes); 582 | for(var i = 0; i < nodeShort.length; i++){ 583 | append(this.internalEdges, new Edge(nodeShort[i], this.internalNodes.length - 1, nodeDistXY(this.internalNodes[nodeShort[i]], mx,my))); 584 | //makeInput(this.internalEdges[this.internalEdges.length-1], this.internalNodes, this.internalEdges.length-1, this.offSetX, this.offSetY, this.name); 585 | } 586 | displayMaps(); 587 | //updateData(p); 588 | }; 589 | 590 | this.reset = function(){ 591 | dragOffX = 0; 592 | dragOffY = 0; 593 | //resetMatrix(); 594 | this.internalNodes = []; 595 | this.internalEdges = []; 596 | this.trias = [0]; 597 | 598 | //Following routine same as initialization--could be condensed to single function 599 | //start with 4 nodes at corners 600 | append(this.internalNodes, new Node(0,0)); 601 | append(this.internalNodes, new Node(this.img.width,0)); 602 | append(this.internalNodes, new Node(this.img.width, this.img.height)); 603 | append(this.internalNodes, new Node(0,this.img.height)); 604 | 605 | //and 4 edges that connect them 606 | append(this.internalEdges, new Edge(0,1,nodeDist(this.internalNodes[0],this.internalNodes[1]))); 607 | append(this.internalEdges, new Edge(1,2,nodeDist(this.internalNodes[1],this.internalNodes[2]))); 608 | append(this.internalEdges, new Edge(2,3,nodeDist(this.internalNodes[2],this.internalNodes[3]))); 609 | append(this.internalEdges, new Edge(3,0,nodeDist(this.internalNodes[3],this.internalNodes[0]))); 610 | 611 | for(var j = 0; j < this.internalEdges.length; j++){ 612 | makeInput(this.internalEdges[j], this.internalNodes, j, this.offSetX, this.offSetY,this.name + " defaultIn"); 613 | } 614 | this.gridMode = false; 615 | this.clickCount = 0; 616 | this.reCalculate(); 617 | }; 618 | 619 | this.grid = function(){ 620 | this.internalNodes = []; 621 | this.internalEdges = []; 622 | 623 | var nodeCount = 0; 624 | var e = document.getElementById("yval"); 625 | var n = e.options[e.selectedIndex].value; 626 | var f = document.getElementById("xval"); 627 | var m = f.options[f.selectedIndex].value; 628 | for(var i = 0; i <= m; i++){ //height 629 | for(var j = 0; j <=n; j++){ //width 630 | append(this.internalNodes, new Node(j*img.width/n,i*img.height/m)); 631 | //console.log('j' + j + 'i' + i); 632 | if(j > 0 ){ //draw horizontal lines to previous node && i > 0 && i < gridH 633 | append(this.internalEdges,new Edge(nodeCount-1,nodeCount, 634 | nodeDist(this.internalNodes[nodeCount-1],this.internalNodes[nodeCount]))); 635 | } 636 | 637 | if(i > 0 ){ //draw vertical lines on outline && (j == 0 || j == gridW) && j> 0 && j < gridW 638 | // :-/ 639 | if(typeof this.internalNodes[nodeCount-(n+1)] !== "undefined" && typeof this.internalNodes[nodeCount] !== "undefined" ){ 640 | append(this.internalEdges,new Edge(nodeCount-(n+1),nodeCount, 641 | nodeDist(this.internalNodes[nodeCount-(n+1)],this.internalNodes[nodeCount]))); 642 | } 643 | 644 | } 645 | //console.log(this.internalNodes.length + ' ' + nodeCount); 646 | nodeCount++; 647 | } 648 | } 649 | 650 | //add nodes in center of grid squares 651 | for(var i = 0; i < m; i++){ 652 | for(var j = 0; j < n; j++){ 653 | this.autoAddNode((j*img.width/n)+(img.width/n*0.5), (i*img.height/m)+(0.5*img.height/m)); 654 | if(j > 0 ){ 655 | //horizontals 656 | append(this.internalEdges,new Edge(nodeCount-1,nodeCount, 657 | nodeDist(this.internalNodes[nodeCount-1],this.internalNodes[nodeCount]))); 658 | //console.log(nodeCount); 659 | //console.log(nodeDist(this.internalNodes[nodeCount-1],this.internalNodes[nodeCount],p)); 660 | //verticals 661 | } 662 | if(i > 0){ 663 | // :-/ 664 | if(typeof this.internalNodes[nodeCount-(n+1)] !== "undefined" && typeof this.internalNodes[nodeCount] !== "undefined" ){ 665 | append(this.internalEdges,new Edge(nodeCount-(n),nodeCount, 666 | nodeDist(this.internalNodes[nodeCount-(n)],this.internalNodes[nodeCount]))); 667 | } 668 | } 669 | 670 | nodeCount++; 671 | 672 | } 673 | } 674 | /* 675 | //define distances of outside of image 676 | append(this.internalEdges,new Edge(0,gridW,img.width)); 677 | append(this.internalEdges, new Edge(gridW, (gridW+1)*(gridH+1)-1,img.height)); 678 | append(this.internalEdges, new Edge((gridW+1)*(gridH+1)-1, (gridW+1)*(gridH+1)-1-gridW,img.width)); 679 | append(this.internalEdges, new Edge((gridW+1)*(gridH+1)-1-gridW,0,img.height)); 680 | 681 | //define diagonal distances across entire image 682 | append(this.internalEdges, new Edge(0,(gridW+1)*(gridH+1)-1,dist(0,0,this.img.width,this.img.height))); 683 | append(this.internalEdges, new Edge(gridW, (gridW+1)*(gridH+1)-1-gridW, dist(0,0,this.img.width, this.img.height))); 684 | */ 685 | nodeCount = 0; 686 | this.gridMode = true; 687 | //this.reCalculate(); 688 | }; 689 | 690 | this.grid2 = function(){ 691 | 692 | this.internalNodes = []; 693 | this.internalEdges = []; 694 | 695 | var nodeCount = 0; 696 | var e = document.getElementById("yval"); 697 | var n = e.options[e.selectedIndex].value; 698 | var f = document.getElementById("xval"); 699 | var m = f.options[f.selectedIndex].value; 700 | //console.log(n + ' ' + m); 701 | 702 | for(var i = 0; i <= m; i++){ //height 703 | for(var j = 0; j <=n; j++){ //width 704 | append(this.internalNodes, new Node(j*img.width/n,i*img.height/m)); 705 | //console.log('j' + j + 'i' + i); 706 | if(j > 0){ //draw horizontal lines to previous node && i > 0 && i < gridH 707 | append(this.internalEdges,new Edge(nodeCount-1,nodeCount, 708 | nodeDist(this.internalNodes[nodeCount-1],this.internalNodes[nodeCount]))); 709 | } 710 | if(i > 0 ){ 711 | //console.log('n:' + n); 712 | var cc = nodeCount-n-1; 713 | //console.log(cc + " " + nodeCount); 714 | if(typeof this.internalNodes[cc] !== "undefined" && typeof this.internalNodes[nodeCount] !== "undefined" ){ 715 | append(this.internalEdges,new Edge(cc,nodeCount, 716 | nodeDist(this.internalNodes[cc],this.internalNodes[nodeCount]))); 717 | } 718 | 719 | } 720 | nodeCount++; 721 | } 722 | } 723 | nodeCount = 0; 724 | this.gridMode = true; 725 | //this.reCalculate(); 726 | }; 727 | 728 | //add two test nodes, connect with edge and custom distance 729 | this.addTestNodes = function(){ 730 | this.autoAddNode(100,100); 731 | this.autoAddNode(350,100); 732 | append(this.internalEdges, new Edge(this.internalNodes.length-2,this.internalNodes.length-1,250)); //connect two above nodes 733 | makeInput(this.internalEdges[this.internalEdges.length-1], this.internalNodes, this.internalEdges.length-1, this.offSetX, this.offSetY, this.name); 734 | 735 | //console.log(this.internalEdges[this.internalEdges.length-1]); 736 | }; 737 | 738 | //add two test nodes, connect with edge and custom distance 739 | this.addTestNodes2 = function(){ 740 | this.autoAddNode(100,100); 741 | this.autoAddNode(100,350); 742 | append(this.internalEdges, new Edge(this.internalNodes.length-2,this.internalNodes.length-1,250)); //connect two above nodes 743 | makeInput(this.internalEdges[this.internalEdges.length-1], this.internalNodes, this.internalEdges.length-1, this.offSetX, this.offSetY, this.name); 744 | }; 745 | } 746 | 747 | //node class 748 | function Node(xpos, ypos){ 749 | this.xpos = xpos; 750 | this.ypos = ypos; 751 | this.nodeHL = false; 752 | 753 | this.display = function(){ 754 | ellipse(this.xpos, this.ypos); 755 | }; 756 | } 757 | 758 | //edge class, takes node pairs by order in array 759 | function Edge(node1, node2, distance){ 760 | this.node1 = node1; 761 | this.node2 = node2; 762 | this.distance = distance; 763 | this.distanceMod = distance; 764 | } 765 | 766 | //connect to n nearest nodes 767 | function findClosestNNodes(mx, my, n, nodes){ 768 | var closest = []; 769 | var nodeIDs = []; 770 | for(var i = 0; i < nodes.length; i++){ 771 | var distN = dist(nodes[i].xpos, nodes[i].ypos, mx, my); 772 | if(distN != 0){ //to avoid comparing to self 773 | append(closest, {distance:distN, id:i}); 774 | } 775 | } 776 | //sort by distances, lowest to highest 777 | closest.sort(function(a, b) {return parseFloat(a.distance) - parseFloat(b.distance);}); 778 | 779 | //return nodeIDS for the n closest nodes 780 | for(var i = 0; i < n; i++){ 781 | append(nodeIDs, closest[i].id); 782 | } 783 | //console.log(nodeIDs); 784 | return nodeIDs; 785 | } 786 | 787 | //returns distance btw two nodes 788 | function nodeDist(nn1,nn2){ 789 | return dist(nn1.xpos,nn1.ypos,nn2.xpos,nn2.ypos); 790 | } 791 | 792 | //make dist input box 793 | function makeInput(edge, nodes, n, xOff, yOff, nm){ 794 | input = createInput(); 795 | var x1 = nodes[edge.node1].xpos+xOff+dragOffX; 796 | var x2 = nodes[edge.node2].xpos+xOff+dragOffX; 797 | var y1 = nodes[edge.node1].ypos+yOff+dragOffY; 798 | var y2 = nodes[edge.node2].ypos+yOff+dragOffY; 799 | 800 | //console.log(edge.node1); 801 | var posX = x1+(x2-x1)/2; 802 | var posY = y1+(y2-y1)/2; 803 | input.position(posX, posY); 804 | if(int(edge.distance) != int(edge.distanceMod)){ 805 | input.value(int(edge.distanceMod)+'/'+int(edge.distance)); 806 | } else { 807 | input.value(int(edge.distance)); 808 | } 809 | input.id(nm + "_" + n); //adds id that refers to edge 810 | input.class(nm + " mapIn" ); //uses image name for class for deletion later 811 | input.attribute("onkeydown", "keypress(event, " + "'" + nm + "_" + n + "')"); 812 | input.attribute("data-xpos", posX-dragOffX); 813 | input.attribute("data-ypos", posY-dragOffY); 814 | 815 | } 816 | 817 | //returns distance btw node and x,y 818 | function nodeDistXY(nn1,mx,my){ 819 | return dist(nn1.xpos,nn1.ypos,mx,my); 820 | } 821 | 822 | function jitterEdgeMatrix(matrix,jitterEdgeMultiplierMagnitude){ 823 | // this code randomly perturbs edge distances 824 | // such that 'nearby' similarly-good MDS results might be found 825 | // e.g. symmetric mirrors. 826 | var jitterEdgeMatrixMultipliers = numeric.add( 827 | 1, 828 | numeric.add( 829 | -1 * jitterEdgeMultiplierMagnitude, 830 | numeric.mul( 831 | jitterEdgeMultiplierMagnitude, 832 | numeric.random([matrix.length,matrix[0].length]) 833 | ) 834 | ) 835 | ); 836 | for(var y = 0; y < matrix.length; y++) { 837 | for(var x = 0; x < matrix[0].length; x++) { 838 | // do elementwise multiplication -- could be sped up. 839 | matrix[x][y] = matrix[x][y] * jitterEdgeMatrixMultipliers[x][y]; 840 | }; 841 | }; 842 | return matrix; 843 | } 844 | 845 | function floatingPointClose(num1,num2,myTolerance) { 846 | return(abs(num1-num2) < myTolerance); 847 | } 848 | 849 | function jitterVertexPositionArray(vertices,jitterVertexAbsoluteMagnitude) { 850 | // this code randomly perturbs vertex node positions (for the Delaunay) 851 | // such that 'nearby' similarly-good MDS results might be found 852 | // e.g. symmetric mirrors. 853 | 854 | // Find min and max positions for x and y. 855 | // The principle is that no jitters occur along the edges of the maps. 856 | // The reason for this is that they otherwise can create very elongated delaunay triangles, 857 | // which creates associated distortions in the rendering. 858 | var xPosArray = vertices.map(p => p[0]); 859 | var yPosArray = vertices.map(p => p[1]); 860 | var xMax = Math.max(...xPosArray); 861 | var yMax = Math.max(...yPosArray); 862 | var xMin = Math.min(...xPosArray); 863 | var yMin = Math.min(...yPosArray); 864 | 865 | for(var v = 0; v < vertices.length; v++) { 866 | if ( 867 | !floatingPointClose(xMin,vertices[v][0],jitterVertexAbsoluteMagnitude) && 868 | !floatingPointClose(xMax,vertices[v][0],jitterVertexAbsoluteMagnitude) 869 | ) { 870 | vertices[v][0] = vertices[v][0] + 2 * (Math.random()-1) * jitterVertexAbsoluteMagnitude; 871 | }; 872 | if ( 873 | !floatingPointClose(yMin,vertices[v][1],jitterVertexAbsoluteMagnitude) && 874 | !floatingPointClose(yMax,vertices[v][1],jitterVertexAbsoluteMagnitude) 875 | ) { 876 | vertices[v][1] = vertices[v][1] + 2 * (Math.random()-1) * jitterVertexAbsoluteMagnitude; 877 | }; 878 | }; 879 | return vertices; 880 | } 881 | 882 | //build empty matrix, run through Floyd Warshall and MDS 883 | function makeMatrix(focus){ 884 | //console.log(maps[mapFocus].internalNodes); 885 | var nodes = maps[focus].internalNodes; 886 | var edges = maps[focus].internalEdges; 887 | var triangles = maps[focus].trias; 888 | var matrix = []; 889 | var vertices = new Array(nodes.length); 890 | 891 | //from http://stackoverflow.com/questions/6495187/best-way-to-generate-empty-2d-array 892 | var matrix = (function(matrix){ while(matrix.push([]) < nodes.length); return matrix})([]); 893 | 894 | //populate empty matrix from edge info 895 | for(var i = 0; i < edges.length; i++){ 896 | var x = edges[i].node1; 897 | var y = edges[i].node2; 898 | var dis = parseFloat(edges[i].distanceMod); 899 | //console.log(x + ' ' + y + ' ' + dis); 900 | //distances are equal in both directions 901 | //populates both spots n matrix by switching x/y 902 | matrix[x][y] = dis; 903 | matrix[y][x] = dis; 904 | } 905 | 906 | for(var y = 0; y < nodes.length; y++){ 907 | //cycle through all values replacing 'undefined' with 'Infinity' 908 | //KLUDGE 909 | for(var x = 0; x < nodes.length; x++){ 910 | if(matrix[x][y] === undefined){ 911 | matrix[x][y] = 'Infinity'; 912 | } 913 | } 914 | //populate vertices array for Delaunay 915 | vertices[y] = [nodes[y].xpos, nodes[y].ypos]; 916 | } 917 | 918 | matrix = jitterEdgeMatrix(matrix,jitterEdgeMultiplierMagnitude); 919 | vertices = jitterVertexPositionArray(vertices,jitterVertexAbsoluteMagnitude); 920 | 921 | //console.log(matrix); 922 | 923 | //calculate Infinity entries with Floyd Warshall algo 924 | var shortestDists = floydWarshall(matrix); 925 | 926 | //uncomment to print floyd warshall matrix to console 927 | /* 928 | for(var i = 0; i < shortestDists.length; i++){ 929 | var entries = []; 930 | for(var j = 0; j 1){ 1089 | for(var i = 0; i < trias.length; i+=3){ 1090 | 1091 | //pull out width/height of image to normalize to 1 scale of UV 1092 | //for future versions, move outside this function 1093 | var w = maps[focus].img.width; 1094 | var h = maps[focus].img.height; 1095 | 1096 | var x1 = coords[trias[i]][0]; 1097 | var y1 = coords[trias[i]][1]; 1098 | var z1 = coords[trias[i]][2]; 1099 | var x2 = coords[trias[i+1]][0]; 1100 | var y2 = coords[trias[i+1]][1]; 1101 | var z2 = coords[trias[i+1]][2]; 1102 | var x3 = coords[trias[i+2]][0]; 1103 | var y3 = coords[trias[i+2]][1]; 1104 | var z3 = coords[trias[i+2]][2]; 1105 | 1106 | //console.log("[" + i + "]" + "(" + x1 + "," + y1 + "," + z1 + ") " + "(" + x2 + "," + y2 + "," + z3 + ") " + "(" + x3 + "," + y3 + "," + z3 +")"); 1107 | 1108 | var geo = new THREE.Geometry(); 1109 | geo.vertices.push( 1110 | new THREE.Vector3( x1, y1, z1 ), 1111 | new THREE.Vector3( x2, y2, z2 ), 1112 | new THREE.Vector3( x3, y3, z3 ) 1113 | ); 1114 | 1115 | var uvs = []; 1116 | //subtract 1 on y-axis because flipped in p5.js --> three.js 1117 | var uv1x = maps[focus].internalNodes[trias[i]].xpos/w; 1118 | var uv1y = 1-maps[focus].internalNodes[trias[i]].ypos/h; 1119 | var uv2x = maps[focus].internalNodes[trias[i+1]].xpos/w; 1120 | var uv2y = 1-maps[focus].internalNodes[trias[i+1]].ypos/h; 1121 | var uv3x = maps[focus].internalNodes[trias[i+2]].xpos/w; 1122 | var uv3y = 1-maps[focus].internalNodes[trias[i+2]].ypos/h; 1123 | 1124 | var uvs = []; 1125 | uvs.push( 1126 | new THREE.Vector2(uv1x,uv1y), 1127 | new THREE.Vector2(uv2x,uv2y), 1128 | new THREE.Vector2(uv3x,uv3y) 1129 | 1130 | ); 1131 | 1132 | geo.faces.push( new THREE.Face3( 0, 1, 2 ) ); 1133 | geo.faceVertexUvs[0].push([uvs[0],uvs[1],uvs[2]]); 1134 | 1135 | geo.computeFaceNormals(); 1136 | geo.computeVertexNormals(); 1137 | var triangle = new THREE.Mesh(geo, material); 1138 | triangle.rotation.x = 0; 1139 | triangle.rotation.z = 0; 1140 | 1141 | scene.add(triangle); 1142 | slices.push(triangle); 1143 | //console.log(x1 + ' ' + y1 + ' ' + z1); 1144 | 1145 | } 1146 | 1147 | /* 1148 | //move mesh away from origin 1149 | if(vrModeOn){ 1150 | for(var i = 0; i < slices.length; i++){ 1151 | slices[i].position.x += 300; 1152 | slices[i].position.y += 300; 1153 | slices[i].position.z += 300; 1154 | } 1155 | console.log('webVR on'); 1156 | } 1157 | */ 1158 | } 1159 | 1160 | //var material2 = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture('images/texture.jpg'), side: THREE.DoubleSide } ); 1161 | //var cube = new THREE.CubeGeometry(300,300,300); 1162 | //var mesh = new THREE.Mesh(cube,material); 1163 | //scene.add(mesh); 1164 | //maps[focus].trias = trias; 1165 | 1166 | } 1167 | 1168 | 1169 | function plotCoords(coords, es){ 1170 | //background(255,255,255); 1171 | push(); 1172 | translate(0,0,2); //offset 2 pixels from triangulation graph 1173 | for(var i = 0; i < es.length; i++){ 1174 | var x1 = coords[es[i].node1][0]; 1175 | var x2 = coords[es[i].node2][0]; 1176 | var y1 = coords[es[i].node1][1]; 1177 | var y2 = coords[es[i].node2][1]; 1178 | var z1 = coords[es[i].node1][2]; 1179 | var z2 = coords[es[i].node2][2]; 1180 | stroke(150,150,150); 1181 | line(x1,y1,z1,x2,y2,z2); 1182 | //console.log(x + ' ' + y); 1183 | //ellipse(x1,y1,5,5); 1184 | //ellipse(x2,y2,5,5); 1185 | //line(x1,y1,x2,y2); 1186 | } 1187 | pop(); 1188 | } 1189 | 1190 | function displayMaps(){ 1191 | background('#fff'); 1192 | for (var i=0; iIMAGE:
id: ' + i + ', ' + maps[i].name + ', w: ' + maps[i].img.width + ', h: ' + maps[i].img.height + '
'; 1236 | div.innerHTML = div.innerHTML + 'NODES:
'; 1237 | for(var j=0; j < maps[i].internalNodes.length; j++){ 1238 | div.innerHTML = div.innerHTML + 'id: ' + j + ', x: ' + maps[i].internalNodes[j].xpos + ', y: ' + maps[i].internalNodes[j].ypos + '
'; 1239 | } 1240 | div.innerHTML = div.innerHTML + 'EDGES:
'; 1241 | for(var j=0; j < maps[i].internalEdges.length; j++){ 1242 | div.innerHTML = div.innerHTML + 'id: ' + j + ' distance: ' + int(maps[i].internalEdges[j].distance) + ' distanceMod: ' + int(maps[i].internalEdges[j].distanceMod) + ' node1: ' + maps[i].internalEdges[j].node1 + ' node2: ' + maps[i].internalEdges[j].node2 + '
'; 1243 | } 1244 | } 1245 | } 1246 | 1247 | function reCalc(){ 1248 | if(mode == 0){ 1249 | maps[mapFocus].addNode(mouseX-dragOffX, mouseY-dragOffY); 1250 | //console.log(mouseX + " " + mouseY); 1251 | } 1252 | wormCalc(); 1253 | } 1254 | 1255 | function wormCalc(){ 1256 | if(worm){ 1257 | maps[mapFocus].reCalculateW(); 1258 | combineMatrix(0,1); 1259 | resetThree(); 1260 | plotTriangles(maps[0].mdsMatrix, maps[0].trias, 0, false); 1261 | plotTriangles(maps[1].mdsMatrix, maps[1].trias, 1, false); 1262 | } 1263 | } 1264 | 1265 | function mode0(){ 1266 | mode = 0; 1267 | console.log('mode = 0'); 1268 | } 1269 | 1270 | function mode1(){ 1271 | mode = 1; 1272 | console.log('mode = 1'); 1273 | } 1274 | 1275 | function recalcMaps(){ 1276 | for(var i = 0; i < maps.length; i++){ 1277 | maps[i].reCalculate(); 1278 | } 1279 | wormCalc(); 1280 | } 1281 | 1282 | function transOne(pp){ 1283 | maps[0].trans = tSlider1.value()/100; 1284 | reCalc(pp); 1285 | } 1286 | 1287 | function transTwo(pp){ 1288 | maps[1].trans = tSlider2.value()/100; 1289 | reCalc(pp); 1290 | } 1291 | 1292 | function outOBJ(){ 1293 | var exporter = new THREE.OBJExporter(); 1294 | //console.log(exporter.parse(scene)); 1295 | var blob = new Blob([exporter.parse(scene)], {type: "text/plain;charset=utf-8"}); 1296 | saveAs(blob, "mesh.obj"); 1297 | } 1298 | 1299 | //from http://www.benfrederickson.com/multidimensional-scaling/ 1300 | function mdsCoords(distances, dimensions) { 1301 | dimensions = dimensions || 2; 1302 | 1303 | // square distances 1304 | var M = numeric.mul(-.5, numeric.pow(distances, 2)); 1305 | 1306 | // double centre the rows/columns 1307 | function mean(A) { return numeric.div(numeric.add.apply(null, A), A.length); } 1308 | var rowMeans = mean(M), 1309 | colMeans = mean(numeric.transpose(M)), 1310 | totalMean = mean(rowMeans); 1311 | 1312 | for (var i = 0; i < M.length; ++i) { 1313 | for (var j =0; j < M[0].length; ++j) { 1314 | M[i][j] += totalMean - rowMeans[i] - colMeans[j]; 1315 | } 1316 | } 1317 | 1318 | // take the SVD of the double centred matrix, and return the 1319 | // points from it 1320 | var ret = numeric.svd(M), 1321 | eigenValues = numeric.sqrt(ret.S); 1322 | return ret.U.map(function(row) { 1323 | return numeric.mul(row, eigenValues).splice(0, dimensions); 1324 | }); 1325 | }; 1326 | 1327 | //from https://mgechev.github.io/javascript-algorithms/graphs_shortest-path_floyd-warshall.js.html 1328 | (function (exports) { 1329 | 'use strict'; 1330 | var floydWarshall = (function () { 1331 | /** 1332 | * Matrix used for the algorithm. 1333 | */ 1334 | var dist; 1335 | /** 1336 | * Initialize the distance matrix. 1337 | * 1338 | * @private 1339 | * @param {Array} graph Distance matrix of the array. 1340 | * @return {Array} Distance matrix used for the algorithm. 1341 | */ 1342 | 1343 | function init(graph) { 1344 | var dist = []; 1345 | var size = graph.length; 1346 | for (var i = 0; i < size; i += 1) { 1347 | dist[i] = []; 1348 | for (var j = 0; j < size; j += 1) { 1349 | if (i === j) { 1350 | dist[i][j] = 0; 1351 | } else if (!isFinite(graph[i][j])) { 1352 | dist[i][j] = Infinity; 1353 | } else { 1354 | dist[i][j] = graph[i][j]; 1355 | } 1356 | } 1357 | } 1358 | return dist; 1359 | } 1360 | /** 1361 | * Floyd-Warshall algorithm. Finds the shortest path between 1362 | * each two vertices.

1363 | * Complexity: O(|V|^3) where V is the number of vertices. 1364 | * 1365 | * @public 1366 | * @module graphs/shortest-path/floyd-warshall 1367 | * @param {Array} graph A distance matrix of the graph. 1368 | * @return {Array} Array which contains the shortest 1369 | * distance between each two vertices. 1370 | * 1371 | * @example 1372 | * var floydWarshall = 1373 | * require('path-to-algorithms/src/graphs/shortest-path/floyd-warshall').floydWarshall; 1374 | * var distMatrix = 1375 | * [[Infinity, 7, 9, Infinity, Infinity, 16], 1376 | * [7, Infinity, 10, 15, Infinity, Infinity], 1377 | * [9, 10, Infinity, 11, Infinity, 2], 1378 | * [Infinity, 15, 11, Infinity, 6, Infinity], 1379 | * [Infinity, Infinity, Infinity, 6, Infinity, 9], 1380 | * [16, Infinity, 2, Infinity, 9, Infinity]]; 1381 | * 1382 | * // [ [ 0, 7, 9, 20, 20, 11 ], 1383 | * // [ 7, 0, 10, 15, 21, 12 ], 1384 | * // [ 9, 10, 0, 11, 11, 2 ], 1385 | * // [ 20, 15, 11, 0, 6, 13 ], 1386 | * // [ 20, 21, 11, 6, 0, 9 ], 1387 | * // [ 11, 12, 2, 13, 9, 0 ] ] 1388 | * var shortestDists = floydWarshall(distMatrix); 1389 | */ 1390 | return function (graph) { 1391 | dist = init(graph); 1392 | var size = graph.length; 1393 | for (var k = 0; k < size; k += 1) { 1394 | for (var i = 0; i < size; i += 1) { 1395 | for (var j = 0; j < size; j += 1) { 1396 | if (dist[i][j] > dist[i][k] + dist[k][j]) { 1397 | dist[i][j] = dist[i][k] + dist[k][j]; 1398 | } 1399 | } 1400 | } 1401 | } 1402 | return dist; 1403 | }; 1404 | }()); 1405 | exports.floydWarshall = floydWarshall; 1406 | })(typeof window === 'undefined' ? module.exports : window); 1407 | -------------------------------------------------------------------------------- /js/gisUI.js: -------------------------------------------------------------------------------- 1 | function goInterface(){ 2 | document.getElementById("leftCanv").style.display = "block"; 3 | document.getElementById("rightCanv").style.display = "none"; 4 | document.getElementById("menuEdit").style.display = "block"; 5 | document.getElementById("menuRender").style.display = "none"; 6 | document.getElementById("goInterface").style.backgroundColor = "#666"; 7 | document.getElementById("goRender").style.backgroundColor = "#333"; 8 | editMode = true; 9 | insToggle2(); 10 | } 11 | 12 | function goRender(){ 13 | document.getElementById("leftCanv").style.display = "none"; 14 | document.getElementById("rightCanv").style.display = "block"; 15 | document.getElementById("menuEdit").style.display = "none"; 16 | document.getElementById("menuRender").style.display = "block"; 17 | document.getElementById("goInterface").style.backgroundColor = "#333"; 18 | document.getElementById("goRender").style.backgroundColor = "#666"; 19 | editMode = false; 20 | insToggle2(); 21 | } 22 | 23 | function changeFocus(){ 24 | mapFocus++; 25 | if(mapFocus > maps.length-1){ 26 | mapFocus = 0; 27 | } 28 | maps[mapFocus].reCalculate(); 29 | } 30 | 31 | function resetMaps(){ 32 | //find and delete all input DOM elements with class name of map's image 33 | var allInputs = document.getElementsByClassName(maps[mapFocus].name); 34 | //console.log(allInputs); 35 | for(var i = allInputs.length-1; i >= 0; i--){ 36 | var toRemove = allInputs[i]; 37 | toRemove.parentNode.removeChild(toRemove); 38 | } 39 | //run object reset routine 40 | maps[mapFocus].reset(); 41 | } 42 | 43 | function delaunay(obj){ 44 | if(!obj.checked){ 45 | delaunayOn = false; 46 | } else { 47 | delaunayOn = true; 48 | } 49 | maps[mapFocus].reCalculate(); 50 | wormCalc(); 51 | } 52 | 53 | function imgToggle(obj){ 54 | if(!obj.checked){ 55 | imageOn = false; 56 | } else { 57 | imageOn = true; 58 | } 59 | displayMaps(); 60 | } 61 | 62 | insToggle = function(obj){ 63 | if(!obj.checked){ 64 | var allInputs = document.getElementsByClassName("mapIn"); 65 | for(var i = 0; i < allInputs.length; i++){ 66 | allInputs[i].style.visibility = "hidden"; 67 | } 68 | } else { 69 | var allInputs = document.getElementsByClassName("mapIn"); 70 | for(var i = 0; i < allInputs.length; i++){ 71 | allInputs[i].style.visibility = "visible"; 72 | } 73 | } 74 | } 75 | 76 | function insToggle2(){ 77 | if(!editMode){ 78 | var allInputs = document.getElementsByClassName("mapIn"); 79 | for(var i = 0; i < allInputs.length; i++){ 80 | allInputs[i].style.visibility = "hidden"; 81 | } 82 | } else if(editMode){ 83 | var allInputs = document.getElementsByClassName("mapIn"); 84 | for(var i = 0; i < allInputs.length; i++){ 85 | allInputs[i].style.visibility = "visible"; 86 | } 87 | } 88 | 89 | } 90 | //color picker iro.js 91 | //color picker iro.js 92 | var colorPicker = new iro.ColorPicker('#color-picker-container', { 93 | width: 80, 94 | padding:4, 95 | handleRadius: 4, 96 | }); 97 | 98 | //var values = document.getElementById("values"); 99 | //colorPicker.on(["color:init", "color:change"], function(color, values){ 100 | // var hexPicker = colorPicker.color.hexString; 101 | // console.log(hexPicker); 102 | // values.innerHTML = [ 103 | // "hex: " + color.hexString, 104 | // ].join("
"); 105 | //}); 106 | 107 | function onColorChange(color, changes) { 108 | //change render canvas color 109 | renderer.setClearColor(color.hexString, 1); 110 | } 111 | 112 | // listen to a color picker's color:change event 113 | colorPicker.on('color:change', onColorChange); 114 | 115 | 116 | //change canvas color 117 | function bgColorChange(colorID) { 118 | var colorIDthree = concat('0x',colorID); 119 | console.log(colorIDthree); 120 | renderer.setClearColor(colorIDthree, 1); 121 | colorPicker.color.hexString = "#" + colorID; 122 | }; 123 | 124 | //end color change button 125 | function nNodesChange(obj){ 126 | var item = obj.value; 127 | nNodes = item; 128 | } 129 | 130 | function dimensionChange(obj){ 131 | if(!obj.checked){ 132 | dim = 2; 133 | } else { 134 | dim = 3; 135 | } 136 | recalcMaps(); 137 | } 138 | 139 | function wormMode(obj){ 140 | if(!obj.checked){ 141 | worm = false; 142 | document.getElementById("nodeConnect").style.display = "none"; 143 | document.getElementById("renderFocus").style.display = "block"; 144 | } else { 145 | worm = true; 146 | document.getElementById("nodeConnect").style.display = "block"; 147 | document.getElementById("renderFocus").style.display = "none" 148 | } 149 | recalcMaps(); 150 | } 151 | 152 | function bindMaps(obj){ 153 | if(!obj.checked){ 154 | bindTwo = false; 155 | } else { 156 | bindTwo = true; 157 | } 158 | wormCalc(); 159 | } 160 | 161 | function cNodesChange(obj){ 162 | var item = obj.value; 163 | connectLastNodes = item; 164 | recalcMaps(); 165 | } 166 | 167 | function bindslide(obj){ 168 | bindDist = obj.value; 169 | bindMaps(obj); 170 | } 171 | 172 | function rotateMode(obj){ 173 | if(!obj.checked){ 174 | autoRotate = false; 175 | } else { 176 | autoRotate = true; 177 | } 178 | } 179 | 180 | function tslide(obj){ 181 | maps[obj.id].trans = obj.value/100; 182 | recalcMaps(); 183 | } 184 | 185 | function wireFrameMode(obj){ 186 | if(!obj.checked){ 187 | wireframeOn = false; 188 | } else { 189 | wireframeOn = true; 190 | } 191 | recalcMaps(); 192 | } 193 | 194 | function webVrOn(obj){ 195 | var vrDiv = document.getElementById('webvr'); 196 | 197 | if(!obj.checked){ 198 | vrModeOn = false; 199 | renderer.vr.enabled = false; 200 | vrDiv.innerHTML = ''; 201 | } else { 202 | vrModeOn = true; 203 | renderer.vr.enabled = true; 204 | vrDiv.append(WEBVR.createButton(renderer)); 205 | var user = new THREE.Group(); 206 | user.position.set( 0, 0, canvaswidth/2 ); 207 | scene.add( user ); 208 | user.add( camera ); 209 | } 210 | recalcMaps(); 211 | } 212 | 213 | document.getElementById('selectFilesGraph').onchange = function() { 214 | var files = document.getElementById('selectFilesGraph').files; 215 | if (files.length <= 0) { 216 | return false; 217 | }; 218 | document.getElementById('fileGraph').innerHTML = (files[0].name); 219 | var fr = new FileReader(); 220 | fr.readAsText(files[0]); 221 | fr.onload = function(e) { 222 | rawText = fr.result; 223 | GraphXMLfromString(fr.result); 224 | }; 225 | } 226 | 227 | document.getElementById('importImage').onclick = function() { 228 | var files = document.getElementById('selectFilesImage').files; 229 | /* 230 | var fr = new FileReader(); 231 | 232 | fr.readAsBinaryString(files[0]); 233 | var rawData = fr.result; 234 | document.getElementById('fileImage').innerHTML = (files[0].name); 235 | if (files.length <= 0) { 236 | return false; 237 | }*/ 238 | console.log("Current file: "+files[0].name); 239 | myfile = new p5.File(files[0]); 240 | console.log("Current file as p5 file: "+myfile); 241 | console.log("Current type of p5 file: "+myfile.type); 242 | var myfiledata = myfile.data; 243 | console.log(myfiledata); 244 | loadImage(myfiledata,addMap); 245 | //myp5.addMap(myfile); 246 | console.log("Made it past loadImage and addMap.") 247 | mapImages.push(myfile); 248 | } 249 | 250 | function mirrorMesh() { 251 | scene.children.forEach( 252 | function(object) { 253 | if (object.type === "Mesh") { 254 | object.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1)); 255 | } 256 | } 257 | ); 258 | } 259 | -------------------------------------------------------------------------------- /js/graph-io.js: -------------------------------------------------------------------------------- 1 | // comment 2 | // testing 3 | 4 | function GraphXMLfromString(xmlString) { 5 | // 6 | console.log("XML data seen: "+xmlString); 7 | 8 | parser = new DOMParser(); 9 | xmlDoc = parser.parseFromString(xmlString,"text/xml"); 10 | 11 | // get the keys 12 | 13 | console.log("KEYS:"); 14 | var keychildren = xmlDoc.getElementsByTagName('key'); 15 | console.log('keychildren has this number of elements: '+keychildren.length); 16 | console.log(keychildren); 17 | 18 | var keyid, fortype, attrtype; 19 | var keys = {}; 20 | for (var i = 0; i < keychildren.length; i++) { 21 | keyid = keychildren[i].getAttribute('id'); 22 | fortype = keychildren[i].getAttribute('for'); 23 | attrname = keychildren[i].getAttribute('attr.name'); 24 | keys[keyid] = {'for': fortype, 'attrname': attrname}; 25 | console.log(keyid + ', ' + fortype + ', ' + attrname); 26 | }; 27 | 28 | 29 | // parse nodes 30 | 31 | console.log("NODES:"); 32 | var nodechildren = xmlDoc.getElementsByTagName('node'); 33 | var datachildren; 34 | var nodes = {}; 35 | for (var currnode = 0; currnode < nodechildren.length; currnode++) { 36 | nodeid = nodechildren[currnode].getAttribute('id'); 37 | datachildren = nodechildren[currnode].getElementsByTagName('data'); 38 | var current_node = {}; 39 | for (var currdata = 0; currdata < datachildren.length; currdata++) { 40 | keyid = datachildren[currdata].getAttribute('key'); 41 | keycontent = datachildren[currdata].childNodes[0].nodeValue; 42 | console.log("Node " + nodeid + " has key " + keys[keyid].attrname + ' of ' + keycontent); 43 | current_node[keys[keyid].attrname] = keycontent; 44 | }; 45 | nodes[nodeid] = current_node; 46 | }; 47 | console.log(nodes); 48 | 49 | // add the nodes to enfolding 50 | 51 | // parse edges 52 | 53 | console.log("EDGES:"); 54 | var edgechildren = xmlDoc.getElementsByTagName('edge'); 55 | var datachildren; 56 | var edges = {}; 57 | for (var curredge = 0; curredge < edgechildren.length; curredge++) { 58 | edgeid = edgechildren[curredge].getAttribute('id'); 59 | edgesource = edgechildren[curredge].getAttribute('source'); 60 | edgetarget = edgechildren[curredge].getAttribute('target'); 61 | datachildren = edgechildren[curredge].getElementsByTagName('data'); 62 | var current_edge = {'source': edgesource, 'target':edgetarget}; 63 | for (var currdata = 0; currdata < datachildren.length; currdata++) { 64 | keyid = datachildren[currdata].getAttribute('key'); 65 | keycontent = datachildren[currdata].childNodes[0].nodeValue; 66 | console.log("Edge " + edgeid + " has key " + keys[keyid].attrname + ' of ' + keycontent); 67 | current_edge[keys[keyid].attrname] = keycontent; 68 | }; 69 | edges[edgeid] = current_edge; 70 | }; 71 | console.log(edges); 72 | }; 73 | 74 | 75 | // add edges to enfolding 76 | 77 | 78 | 79 | /* 80 | 81 | function XMLnotfound(myerror){ 82 | console.log("loadXML current error is: " + myerror) 83 | } 84 | 85 | function XMLfound(myXML){ 86 | console.log("loadXML worked with value: " + myXML) 87 | } 88 | 89 | function inputGraphXML(xmlURI){ 90 | // 91 | console.log("XML URI to load: "+xmlURI) 92 | var xml = myp5.loadXML(xmlURI,XMLfound,XMLnotfound); 93 | 94 | 95 | // get the keys 96 | var keychildren = xml.getChildren('key'); 97 | var keyid, fortype, attrtype; 98 | var keys = {}; 99 | for (var i = 0; i < keychildren.length; i++) { 100 | keyid = keychildren[i].getString('id'); 101 | fortype = keychildren[i].getString('for'); 102 | attrname = keychildren[i].getString('attr.name'); 103 | keys[keyid] = {'for': fortype, 'attrname': attrname} 104 | console.log(keyid + ', ' + fortype + ', ' + attrtype); 105 | } 106 | 107 | // parse nodes 108 | var nodechildren = xml.getChildren('node'); 109 | var datachildren; 110 | for (var currnode = 0; currnode < nodechildren.length; currnode++) { 111 | datachildren = nodechildren[currnode].getChildren('data'); 112 | for (var currdata = 0; currdata < datachildren.length; currdata++) { 113 | keyid = datachildren[currdata].getString('key'); 114 | keycontent = datachildren[currdata].getContent(); 115 | console.log("Node " + currnode + " has key " + keyid + ' of ' + keycontent); 116 | console.log("Node " + currnode + " has key " + keys[keyid] + ' is ' + keycontent) 117 | } 118 | } 119 | 120 | // add the nodes to enfolding 121 | 122 | // parse edges 123 | 124 | // add edges to enfolding 125 | 126 | } 127 | */ 128 | -------------------------------------------------------------------------------- /js/iro.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * iro.js v4.5.1 3 | * 2016-2019 James Daniel 4 | * Licensed under MPL 2.0 5 | * github.com/jaames/iro.js 6 | */ 7 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.iro=e()}(this,function(){"use strict";var c=function(){},i={},h=[],u=[];function p(t,e){var n,o,r,i,s=arguments,a=u;for(i=arguments.length;2 0 ) { 106 | 107 | showEnterVR( displays[ 0 ] ); 108 | 109 | } else { 110 | 111 | showVRNotFound(); 112 | 113 | } 114 | 115 | } ); 116 | 117 | return button; 118 | 119 | } else { 120 | 121 | var message = document.createElement( 'a' ); 122 | message.href = 'https://webvr.info'; 123 | message.innerHTML = 'WEBVR NOT SUPPORTED'; 124 | 125 | message.style.left = 'calc(50% - 90px)'; 126 | message.style.width = '180px'; 127 | message.style.textDecoration = 'none'; 128 | 129 | stylizeElement( message ); 130 | 131 | return message; 132 | 133 | } 134 | 135 | }, 136 | 137 | // DEPRECATED 138 | 139 | checkAvailability: function () { 140 | console.warn( 'WEBVR.checkAvailability has been deprecated.' ); 141 | return new Promise( function () {} ); 142 | }, 143 | 144 | getMessageContainer: function () { 145 | console.warn( 'WEBVR.getMessageContainer has been deprecated.' ); 146 | return document.createElement( 'div' ); 147 | }, 148 | 149 | getButton: function () { 150 | console.warn( 'WEBVR.getButton has been deprecated.' ); 151 | return document.createElement( 'div' ); 152 | }, 153 | 154 | getVRDisplay: function () { 155 | console.warn( 'WEBVR.getVRDisplay has been deprecated.' ); 156 | } 157 | 158 | }; 159 | -------------------------------------------------------------------------------- /models/obj/vive-controller/onepointfive_spec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/models/obj/vive-controller/onepointfive_spec.png -------------------------------------------------------------------------------- /models/obj/vive-controller/onepointfive_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoldingSpace/enfolding/cfe1060b48f374895c23d4cd31da056f158db771/models/obj/vive-controller/onepointfive_texture.png -------------------------------------------------------------------------------- /style/style.css: -------------------------------------------------------------------------------- 1 | /*general*/ 2 | body{font-size:12px;font-family:sans-serif;margin:0;} 3 | input{width:75px;opacity:.8;} 4 | 5 | button:disabled { 6 | opacity: 0.3; 7 | } 8 | 9 | /*navigation*/ 10 | #nav{ 11 | overflow: hidden; 12 | background-color: #000; 13 | position: fixed; 14 | top: 0; 15 | width: 100%; 16 | color: #ddd; 17 | font-family: Baskerville, Georgia, serif; 18 | font-size:14px; 19 | } 20 | #nav .button{ 21 | background-color: #333; 22 | border:none;text-align:center; 23 | display:inline-block; 24 | padding:5px 10px; 25 | border-radius: 8px; 26 | -webkit-transition-duration: 0.4s; /* Safari */ 27 | transition-duration: 0.4s; 28 | margin:5px 5px;color:#ddd;} 29 | #nav .button:hover {color: white;} 30 | #nav #goInterface{margin-left:150px;background-color:#666;} 31 | 32 | /*menu*/ 33 | .menu{ 34 | width:18%;height:900px;float:left;background-color: #ccc; 35 | padding:50px 10px; 36 | } 37 | 38 | .menu header{ 39 | font-size:18px; 40 | font-weight: bold; 41 | font-family: Helvetica, Arial, sans-serif; 42 | margin-bottom:10px; 43 | } 44 | 45 | .menu hr{ 46 | margin-top:10px; 47 | } 48 | 49 | #menuRender{ 50 | display:none; 51 | } 52 | .menu button { 53 | background-color: #08519c; 54 | border: none; 55 | color: white; 56 | padding: 5px 10px; 57 | text-align: center; 58 | text-decoration: none; 59 | display: inline-block; 60 | font-size: 12px; 61 | box-shadow: 1px 1px 1px #333; 62 | } 63 | 64 | .menu button.colors { 65 | border: none; 66 | color: white; 67 | padding: 5px 5px; 68 | width: 7px; 69 | display: inline-block; 70 | box-shadow: 1px 1px 1px #333; 71 | } 72 | 73 | .menu select{ 74 | background-color: #08519c; 75 | color: white; 76 | padding: 3px 5px; 77 | font-size: 10px; 78 | 79 | } 80 | 81 | #grids{ 82 | position:relative; 83 | height:130px; 84 | } 85 | 86 | #yval{ 87 | position:absolute; 88 | top:95px; 89 | left:30px; 90 | } 91 | #xval{ 92 | position:absolute; 93 | top:30px; 94 | left:95px; 95 | } 96 | 97 | 98 | /*main section*/ 99 | #leftCanv{width:80%;height:900px;float:left;} 100 | #rightCanv{width:80%;height:900px;float:left;display: none;} 101 | #dataResults{font-size:10px;color:#999;margin-top:350px;width:200px;float:left;} 102 | --------------------------------------------------------------------------------