├── .gitignore
├── LICENSE
├── README.md
├── dist
├── .gitkeep
├── aframe-heightgrid-component.js
└── aframe-heightgrid-component.min.js
├── examples
├── advanced
│ ├── index.html
│ ├── index2.html
│ ├── index3.html
│ └── lib
│ │ └── usgsNed.js
├── assets
│ ├── gist_earth.png
│ ├── gist_earth.xcf
│ └── gist_earth10.png
├── basic
│ └── index.html
├── index.html
└── main.js
├── index.js
├── package.json
└── scripts
├── unboil.js
└── unboil.js~
/.gitignore:
--------------------------------------------------------------------------------
1 | .sw[ponm]
2 | examples/build.js
3 | examples/node_modules/
4 | gh-pages
5 | node_modules/
6 | npm-debug.log
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Kevin Ngo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## aframe-heightgrid-component
2 |
3 | A Height Grid (Terrain) component for [A-Frame](https://aframe.io). The component allows to use a gridded height field for defining a surface. The grid is aligned with the x-z plane, the heights define y coordinates.
4 |
5 | Chrome requires aframe > 0.2.0 for this component to function properly.
6 |
7 | ### Properties
8 |
9 | | Property | Description | Default Value |
10 | | -------- | ----------- | ------------- |
11 | | xdimension | number of grid points in x direction | 2 (the minimum) |
12 | | zdimension | number of grid points in z direction | 2 (the minimum) |
13 | | xspacing | the distance between two grid points in x direction | 1 |
14 | | zspacing | the distance between two grid points in z direction | 1 |
15 | | heights | a list of heights (ordering below) | 0 |
16 | | origin | the location of the first grid point | 0 0 0 |
17 | | uvs | a list of 2d texture coordinates | empty |
18 |
19 | The ordering of heights is from the first grid point to the last. The ordering is such that y(x, z) = heights(x + z * xdimension); eg. first the points along the x direction are assigned heights, then the z position is incremented and those points along the x direction are assigned heights.
20 |
21 | The (local) origin of the grid can be shifted by providing the origin property. This is for special applications only. Usually it is preferrable to wrap in another entity for translation and rotation.
22 |
23 | ### Defaults
24 |
25 | The minimum value x/zdimension is 2. x/zspacing values are unconstrained and can probably be negative.
26 |
27 | If the heights property contains less values than the number of grid points, heights are recycled from the beginning of the list to fill in missing values. By default the heights list only has a single 0 value. Omitting a heights field therefore will produce a horizontal plane.
28 |
29 | The default value for the uvs property is an empty list. An empty list will lead to texture coordinates which correspond directly to the x and z coordinates of the grid points, scaled to vary between 0 and 1.
30 |
31 | The demos have examples of how to use custom uvs.
32 |
33 | ### Usage
34 |
35 | #### Browser Installation
36 |
37 | Install and use by directly including the [browser files](dist):
38 |
39 | ```html
40 |
41 | My A-Frame Scene
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ```
54 |
55 | #### NPM Installation
56 |
57 | Install via NPM:
58 |
59 | ```bash
60 | npm install aframe-heightgrid-component
61 | ```
62 |
63 | Then register and use.
64 |
65 | ```js
66 | require('aframe');
67 | require('aframe-faceset-component');
68 | require('aframe-heightgrid-component');
69 | ```
70 |
--------------------------------------------------------------------------------
/dist/.gitkeep:
--------------------------------------------------------------------------------
1 | `npm run dist` to generate browser files.
2 |
--------------------------------------------------------------------------------
/dist/aframe-heightgrid-component.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports) {
46 |
47 | if (typeof AFRAME === 'undefined') {
48 | throw new Error('Component attempted to register before AFRAME was available.');
49 | }
50 |
51 | /**
52 | * Height Grid (Terrain) component for A-Frame.
53 | */
54 | AFRAME.registerComponent('heightgrid', {
55 | schema: {
56 | dependencies: ['faceset'],
57 | origin: {
58 | type: 'vec3',
59 | default: { x: 0, y: 0, z: 0 } },
60 | xdimension: {
61 | default: 2 },
62 | zdimension: {
63 | default: 2 },
64 | xspacing: {
65 | default: 1 },
66 | zspacing: {
67 | default: 1 },
68 | heights: {
69 | default: [0],
70 | parse: function (value) { return parseNumbers (value) } ,
71 | //stringify: function (value) { return value.toString() }
72 | },
73 | yscale: {
74 | default: 1.0 },
75 | uvs: { // texture coordinates as list
76 | default: [],
77 | parse: function (value) { return parseVec2s (value) } ,
78 | stringify: function (data) {
79 | return data.map( function stringify (data) {
80 | if (typeof data !== 'object') { return data; }
81 | return [data.x, data.y].join(' ');
82 | }).join(',');
83 | }
84 | },
85 | },
86 |
87 | /**
88 | * Called once when component is attached. Generally for initial setup.
89 | */
90 | init: function () { },
91 |
92 | /**
93 | * Called when component is attached and when component data changes.
94 | * Generally modifies the entity based on the data.
95 | */
96 | update: function (oldData) {
97 | var el = this.el;
98 | var data = this.data;
99 | var origin = data.origin;
100 | var xdimension = Math.max(2, data.xdimension);
101 | var zdimension = Math.max(2, data.zdimension);
102 | var xspacing = data.xspacing;
103 | var zspacing = data.zspacing;
104 | var yscale = data.yscale;
105 | var h = data.heights;
106 | var uvs = data.uvs;
107 | //var h = h.length ? h : [0];
108 |
109 | //vertices
110 | var vts = [];
111 | for (var z = 0; z < zdimension; z++) {
112 | for (var x = 0; x < xdimension; x++) {
113 | vts.push( new THREE.Vector3 (
114 | origin.x + x * xspacing,
115 | origin.y + yscale * h[(x + z * xdimension)%h.length],
116 | origin.z + z * zspacing ) );
117 | }
118 | }
119 | //faces
120 | var faces = [];
121 | for (var z = 0; z < zdimension-1; z++) {
122 | for (var x = 0; x < xdimension-1; x++) {
123 | var i = x + z * xdimension;
124 | //alternate diagonal
125 | if ( (x+z)%2 == 0 ) {
126 | faces.push( new THREE.Face3( i, i + 1 + xdimension, i + 1 ) );
127 | faces.push( new THREE.Face3( i, i + xdimension, i + 1 + xdimension) );
128 | }
129 | else {
130 | faces.push( new THREE.Face3( i, i + xdimension, i + 1 ) );
131 | faces.push( new THREE.Face3( i + xdimension, i + 1 + xdimension, i + 1) );
132 | }
133 | }
134 | }
135 | //uvs
136 | var _uvs = uvs.map(function copy (uv) {return uv});
137 | if (_uvs.length == 0) {
138 | //default aligned with x,z
139 | for (var z = 0; z < zdimension; z++) {
140 | for (var x = 0; x < xdimension; x++) {
141 | _uvs.push(new THREE.Vector2( x/(xdimension-1), z/(zdimension-1) ));
142 | }
143 | }
144 | }
145 | el.setAttribute('faceset', { triangles: faces, vertices: vts, uvs: _uvs } );
146 |
147 | },
148 |
149 | /**
150 | * Called when a component is removed (e.g., via removeAttribute).
151 | * Generally undoes all modifications to the entity.
152 | */
153 | remove: function () { this.el.removeAttribute('faceset'); }
154 |
155 | });
156 |
157 | function parseNumbers (value) {
158 | if (typeof value === 'object') {return value}
159 | var mc = value.match(/([+\-0-9eE\.]+)/g);
160 | var numbers = [];
161 | //var vec = {};
162 | for (var i=0, n=mc?mc.length:0; ir;r++)n.push(parseFloat(t[r],10));return n}function r(e){if("object"==typeof e)return e;for(var t=e.match(/([+\-0-9eE\.]+)/g),n=[],r=0,a=t?t.length:0;a>r;r+=2)n.push(new THREE.Vector2(+t[r+0],+t[r+1]));return n}if("undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");AFRAME.registerComponent("heightgrid",{schema:{dependencies:["faceset"],origin:{type:"vec3","default":{x:0,y:0,z:0}},xdimension:{"default":2},zdimension:{"default":2},xspacing:{"default":1},zspacing:{"default":1},heights:{"default":[0],parse:function(e){return n(e)}},yscale:{"default":1},uvs:{"default":[],parse:function(e){return r(e)},stringify:function(e){return e.map(function(e){return"object"!=typeof e?e:[e.x,e.y].join(" ")}).join(",")}}},init:function(){},update:function(e){for(var t=this.el,n=this.data,r=n.origin,a=Math.max(2,n.xdimension),i=Math.max(2,n.zdimension),o=n.xspacing,u=n.zspacing,s=n.yscale,f=n.heights,c=n.uvs,p=[],h=0;i>h;h++)for(var d=0;a>d;d++)p.push(new THREE.Vector3(r.x+d*o,r.y+s*f[(d+h*a)%f.length],r.z+h*u));for(var l=[],h=0;i-1>h;h++)for(var d=0;a-1>d;d++){var g=d+h*a;(d+h)%2==0?(l.push(new THREE.Face3(g,g+1+a,g+1)),l.push(new THREE.Face3(g,g+a,g+1+a))):(l.push(new THREE.Face3(g,g+a,g+1)),l.push(new THREE.Face3(g+a,g+1+a,g+1)))}var v=c.map(function(e){return e});if(0==v.length)for(var h=0;i>h;h++)for(var d=0;a>d;d++)v.push(new THREE.Vector2(d/(a-1),h/(i-1)));t.setAttribute("faceset",{triangles:l,vertices:p,uvs:v})},remove:function(){this.el.removeAttribute("faceset")}})}]);
--------------------------------------------------------------------------------
/examples/advanced/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A-Frame Height Grid (Terrain) Component - Advanced
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Use keyboard and mouse to fly
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
74 |
75 |
--------------------------------------------------------------------------------
/examples/advanced/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A-Frame Height Grid (Terrain) Component - Advanced
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Use keyboard and mouse to fly
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
47 |
281 |
282 |
--------------------------------------------------------------------------------
/examples/advanced/index3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A-Frame Height Grid (Terrain) Component - Advanced
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Use keyboard and mouse to fly
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
47 |
305 |
306 |
--------------------------------------------------------------------------------
/examples/advanced/lib/usgsNed.js:
--------------------------------------------------------------------------------
1 | /*global define, module */
2 | (function (root, factory) {
3 | if (typeof define === 'function' && define.amd) {
4 | // AMD. Register as an anonymous module.
5 | define(function () {
6 | return (root.usgsNed = factory());
7 | });
8 | } else if (typeof exports === 'object') {
9 | // Node. Does not work with strict CommonJS, but
10 | // only CommonJS-like enviroments that support module.exports,
11 | // like Node.
12 | module.exports = factory();
13 | } else {
14 | // Browser globals
15 | root.usgsNed = factory();
16 | }
17 | }(this, function () {
18 | "use strict";
19 |
20 | /*
21 | {
22 | "USGS_Elevation_Point_Query_Service": {
23 | "Elevation_Query": {
24 | "x": -122.9009843371,
25 | "y": 46.973556842123,
26 | "Data_Source": "NED 1\/3 arc-second",
27 | "Elevation": 200.480279,
28 | "Units": "Feet"
29 | }
30 | }
31 | }
32 | */
33 |
34 | /**
35 | * @external {ArcGisFeature}
36 | * @see {@link http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Feature_object/02r3000000n8000000/ Feature}
37 | */
38 |
39 | /**
40 | * @external {GeoJsonFeature}
41 | * @see {@link http://geojson.org/geojson-spec.html#feature-objects Feature Objects}
42 | */
43 |
44 | /**
45 | * An object that represents the results of a query to the USGS Elevation service.
46 | * @param {Object} json
47 | * @member {Number} x
48 | * @member {Number} y
49 | * @member {string} dataSource
50 | * @member {Number} elevation
51 | * @member {string} units - Measurement unit of elevation: "Feet" or "Meters".
52 | */
53 | function ElevationQueryResult(json) {
54 | var resultObj;
55 | if (!json) {
56 | throw new TypeError("The 'json' parameter cannot be null or undefined.");
57 | }
58 | if (json.USGS_Elevation_Point_Query_Service && json.USGS_Elevation_Point_Query_Service.Elevation_Query) {
59 | resultObj = json.USGS_Elevation_Point_Query_Service.Elevation_Query;
60 | } else {
61 | throw new TypeError("The 'json' parameter object did not have expected properties.");
62 | }
63 | this.x = resultObj.x;
64 | this.y = resultObj.y;
65 | this.dataSource = resultObj.Data_Source;
66 | this.elevation = resultObj.Elevation;
67 | this.units = resultObj.Units;
68 | }
69 |
70 | /**
71 | * Returns an {ArcGisFeature} equivalent of this object.
72 | * @returns {ArcGisFeature}
73 | */
74 | ElevationQueryResult.prototype.toArcGisFeature = function () {
75 | var point, feature;
76 | point = {
77 | x: this.x,
78 | y: this.y,
79 | z: this.elevation,
80 | spatialReference: {
81 | wkid: 4326
82 | }
83 | };
84 | feature = {
85 | geometry: point,
86 | attributes: {
87 | elevationUnits: this.units,
88 | dataSource: this.dataSource
89 | }
90 | };
91 |
92 | return feature;
93 | };
94 |
95 | /**
96 | * Creates a GeoJSON feature equivalent to this object.
97 | * @returns {GeoJsonFeature}
98 | */
99 | ElevationQueryResult.prototype.toGeoJson = function () {
100 | var geometry, feature;
101 | geometry = {
102 | type: "Point",
103 | coordinates: [this.x, this.y, this.elevation]
104 | };
105 | feature = {
106 | type: "Feature",
107 | geometry: geometry,
108 | properties: {
109 | elevationUnits: this.units,
110 | dataSource: this.dataSource
111 | }
112 | };
113 |
114 | return feature;
115 | };
116 |
117 | var exports = {};
118 |
119 | /**
120 | * @typedef NedElevationInfo
121 | * @property {number} x
122 | * @property {number} y
123 | * @property {string} Data_Source
124 | * @property {number} Elevation
125 | * @property {string} Units - 'Feet' or 'Meters'
126 | */
127 |
128 | /**
129 | * Converts an object into a query string
130 | * @returns {string}
131 | */
132 | function objectToQueryString(/**{Object}*/ o) {
133 | var output = [], v;
134 | for (var name in o) {
135 | if (o.hasOwnProperty(name)) {
136 | v = o[name];
137 | if (typeof v === "object") {
138 | v = JSON.stringify(v);
139 | }
140 | output.push([name, v].map(encodeURIComponent).join("="));
141 | }
142 | }
143 | return output.join("&");
144 | }
145 |
146 | /**
147 | * Creates a request to the USGS NED point service
148 | * @param {number} x
149 | * @param {number} y
150 | * @param {string} [units='Feet']
151 | * @returns {Promise}
152 | */
153 | exports.getElevation = function (x, y, units) {
154 | return new Promise(function (resolve, reject) {
155 | var baseUrl = "http://ned.usgs.gov/epqs/pqs.php";
156 | var params = {
157 | x: x,
158 | y: y,
159 | units: units || "Feet",
160 | output: "json"
161 | };
162 | var request = new XMLHttpRequest();
163 | request.open("get", [baseUrl, objectToQueryString(params)].join("?"));
164 | request.onload = function () {
165 | if (request.status === 200) {
166 | /*
167 | {
168 | "USGS_Elevation_Point_Query_Service": {
169 | "Elevation_Query": {
170 | "x": -123,
171 | "y": 45,
172 | "Data_Source": "NED 1/3 arc-second",
173 | "Elevation": 177.965854,
174 | "Units": "Feet"
175 | }
176 | }
177 | }
178 | */
179 | var response = JSON.parse(this.responseText);
180 | resolve(response.USGS_Elevation_Point_Query_Service.Elevation_Query);
181 | }
182 | else { reject(request.statusText); }
183 | };
184 | request.onerror = function (e) {
185 | reject(e);
186 | };
187 | request.send();
188 | });
189 | };
190 |
191 | exports.ElevationQueryResult = ElevationQueryResult;
192 |
193 | return exports;
194 | }));
--------------------------------------------------------------------------------
/examples/assets/gist_earth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andreasplesch/aframe-heightgrid-component/1ddd6801a8326009fc6afe53868c25c3c7ed74f6/examples/assets/gist_earth.png
--------------------------------------------------------------------------------
/examples/assets/gist_earth.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andreasplesch/aframe-heightgrid-component/1ddd6801a8326009fc6afe53868c25c3c7ed74f6/examples/assets/gist_earth.xcf
--------------------------------------------------------------------------------
/examples/assets/gist_earth10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andreasplesch/aframe-heightgrid-component/1ddd6801a8326009fc6afe53868c25c3c7ed74f6/examples/assets/gist_earth10.png
--------------------------------------------------------------------------------
/examples/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A-Frame Height Grid (Terrain) Component - Basic
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | A-Frame Height Grid (Terrain) Component
4 |
5 |
22 |
23 |
24 |