first commit
This commit is contained in:
commit
731ebe85b5
5
.babelrc
Normal file
5
.babelrc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }]
|
||||
]
|
||||
}
|
2
.env.example
Normal file
2
.env.example
Normal file
|
@ -0,0 +1,2 @@
|
|||
NODE_ENV=
|
||||
CONSTANT_VALUE=
|
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/node_modules
|
||||
/dist
|
||||
|
||||
/.vs/lms/v15
|
||||
/lms.user
|
||||
/lms.sln
|
||||
/lms.phpproj.user
|
||||
/lms.phpproj
|
||||
|
||||
# Composer
|
||||
/composer.lock
|
||||
/composer.phar
|
||||
/vendor
|
||||
|
||||
# Linux
|
||||
*~
|
||||
*.swp
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
|
||||
/.idea
|
||||
/.vscode
|
||||
/.vs
|
||||
/.vagrant
|
||||
/npm-debug.log
|
||||
/yarn-error.log
|
||||
|
||||
/notes.txt
|
||||
/.env
|
36
GeotiefExplore.code-workspace
Normal file
36
GeotiefExplore.code-workspace
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {},
|
||||
"launch": {
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
// "port": 9222,
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"breakOnLoad": true,
|
||||
// "sourceMapPathOverrides": {
|
||||
// "webpack:///./~/*": "${workspaceFolder}/node_modules/*",
|
||||
// "webpack:///./*": "${workspaceFolder}/*",
|
||||
// "webpack:///*": "*",
|
||||
// "webpack:///src/*": "${workspaceFolder}/*",
|
||||
// },
|
||||
|
||||
// "sourceMapPathOverrides": {
|
||||
// // "webpack:///src/*": "${workspaceFolder}/*",
|
||||
// "webpack:///./*": "${workspaceFolder}/*"
|
||||
// },
|
||||
"sourceMaps": true,
|
||||
"timeout": 15000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
BIN
images/map/loading.gif
Normal file
BIN
images/map/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
40
index.html
Normal file
40
index.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>3D Viewer</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<!-- <link href="content/img/favicon.ico" rel="shortcut icon" type="image/x-icon" /> -->
|
||||
<meta name="description" content="3D viewer Geological Survey of Austria">
|
||||
<meta name="author" content="Arno Kaimbacher">
|
||||
|
||||
<!-- <link rel="stylesheet" href="src/css/styles.css?v=1.0"> -->
|
||||
<link rel="stylesheet" type="text/css" href="dist/main.css" />
|
||||
|
||||
<!-- <style>
|
||||
.mapDesktop {
|
||||
width: 1174px;
|
||||
height: 1001px;
|
||||
}
|
||||
</style> -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="webgl" class="mapDesktop">
|
||||
<div id="inset"></div>
|
||||
<div id="progressArea">
|
||||
<img id="loadingImg" alt="" src="images/map/loading.gif" style="display:none;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="dist/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
6011
package-lock.json
generated
Normal file
6011
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
package.json
Normal file
30
package.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"three": "^0.119.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"concurrently": "^5.2.0",
|
||||
"css-loader": "^4.2.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"img-loader": "^3.0.1",
|
||||
"less-loader": "^6.2.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"style-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^4.0.0",
|
||||
"url-loader": "^4.1.0",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"babel": "babel --presets es2015 js/main.js -o build/main.bundle.js",
|
||||
"serve": "http-server",
|
||||
"prod": "rm -rf dist && webpack",
|
||||
"watch": "concurrently \"npm run serve\" \"npx webpack --source-maps --watch\" "
|
||||
}
|
||||
}
|
93
src/css/page.css
Normal file
93
src/css/page.css
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*@charset "UTF-8";*/
|
||||
|
||||
.mapDesktop {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
top: 30px;
|
||||
left: 30px;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
#inset {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: none;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
/*background-color:lightcyan;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* control positioning */
|
||||
.gba-control {
|
||||
position: relative;
|
||||
z-index: 7;
|
||||
pointer-events: auto;
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.gba-top,
|
||||
.gba-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.gba-top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.gba-right {
|
||||
right: 0;
|
||||
/*right: 5px;*/
|
||||
}
|
||||
|
||||
.gba-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.gba-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
||||
.gba-right .gba-control {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.gba-top .gba-control:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.gba-bottom .gba-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.gba-left .gba-control {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
|
||||
.gba-control-home span {
|
||||
/*padding: 2px;*/
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
/*background-color: rgba(102,102,102,0.80);*/
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAABDlBMVEWZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmXl5f///+VlZWZmZmWlpaUlJSYmJiSkpKampqzs7O3t7fMzMyTk5O2trarq6uysrLR0dHY2Njb29v8/PyMjIy4uLi5ubm6urq1tbSbm5uhoaGmpqanp6eoqKi7u7u+vr7BwcHFxcXGxsbHx8fIyMjJycmpqanNzc3Ozs7Pz8/Q0NCNjY3S0tLT09PV1dXW1tavr6/Z2dmxsbHc3Nzd3d3i4uLn5+fv7+/w8PD39/f5+fn7+/uPj4/9/f3+/v6QkJA5Pja9AAAAGnRSTlMABUdVVmxucX6MtLvHycrN2Nzi4+vs7e/3+OshyJcAAAECSURBVHherdLFcsQwEEXRDjLDJOkWGYcnzMzM/P8/Eo8V18i4yl091alSaSGAgclSdnMTPQB9vKBumCriYZg3j0qKGI9DyVRxVPVNHjVZyTN6u/VzGFmNiN4di3NmWWiw1itq9+Faoux5WxhjFNeka660grkkTUZxQ1Ff7glR2WTEO+rUfI0zqgbFM1moB0rm+BEL+5FS1XzUjOvPlFHdViELx3tKa8M7xr/LW7tpvr/A6Gn+HlHGy/+Xv8Ny+XCzUtmo5/LyD2Ofp7m8LYN9nuZ9gy/1djssd17CVlmwD/Susjbrj2yvhdnJPQbTPEjpeHKPwCAvqBe6hmYXs1uY6YdfRkdtRcSvwdgAAAAASUVORK5CYII=') /*img/home.png*/;
|
||||
/*opacity : 0.8;*/
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
66
src/js/controls/Control.js
Normal file
66
src/js/controls/Control.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
// import { Class } from '../core/Class';
|
||||
import * as util from '../core/utilities';
|
||||
// import * as dom from '../core/domUtil';
|
||||
|
||||
// export var Control = Class.extend({
|
||||
class Control {
|
||||
|
||||
// @section
|
||||
// @aka Control options
|
||||
options = {
|
||||
position: 'topright'
|
||||
};
|
||||
|
||||
constructor(defaults) {
|
||||
if (!(this instanceof Control)) {
|
||||
throw new TypeError("Control constructor cannot be called as a function.");
|
||||
}
|
||||
// properties
|
||||
util.setOptions(this, defaults);
|
||||
}
|
||||
|
||||
getPosition() {
|
||||
return this.options.position;
|
||||
}
|
||||
|
||||
getContainer() {
|
||||
return this._container;
|
||||
}
|
||||
|
||||
addTo(map) {
|
||||
this._map = map;
|
||||
|
||||
var container = this._container = this.onAdd(map);
|
||||
var pos = this.getPosition();//"topright"
|
||||
var corner = map._controlCorners[pos];
|
||||
if (container) {
|
||||
// $(container).addClass('gba-control');
|
||||
container.classList.add("gba-control");
|
||||
|
||||
if (pos.indexOf('bottom') !== -1) {
|
||||
corner.insertBefore(container, corner.firstChild);
|
||||
}
|
||||
else {
|
||||
corner.appendChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removeFrom(map) {
|
||||
var pos = this.getPosition(),
|
||||
corner = map._controlCorners[pos];
|
||||
|
||||
corner.removeChild(this._container);
|
||||
this._map = null;
|
||||
|
||||
if (this.onRemove) {
|
||||
this.onRemove(map);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Control };
|
102
src/js/controls/HomeButton.js
Normal file
102
src/js/controls/HomeButton.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
import { Control } from "./Control";
|
||||
import * as dom from '../core/domUtil';
|
||||
import * as domEvent from '../core/domEvent';
|
||||
|
||||
class HomeButton extends Control {
|
||||
options = {
|
||||
position: 'topright',
|
||||
homeText: '+',
|
||||
//homeTitle: 'Home Extent',
|
||||
visible: true
|
||||
};
|
||||
|
||||
constructor(defaults) {
|
||||
super(defaults);
|
||||
|
||||
// properties
|
||||
this.map = {};
|
||||
this.visible = this.options.visible;
|
||||
this.home = this.options.home;
|
||||
}
|
||||
|
||||
onAdd(map) {
|
||||
if (!this.map) {
|
||||
//self.destroy();
|
||||
logger.warning('HomeButton::map required', true);
|
||||
return;
|
||||
}
|
||||
this.map = map;
|
||||
//this.options.home.initialZoom = map.options.zoom;
|
||||
//this.options.home.initialCenter = map.options.center;
|
||||
var b = this._nls = "test"; //util.mixin({}, N.widgets.home);
|
||||
|
||||
var className = 'gba-control-home';
|
||||
// Create sidebar container
|
||||
//var container = this._container = L.DomUtil.create('div', className);
|
||||
var container = this._container = dom.createDom("div", { "class": className });
|
||||
//if (this.options.home) {
|
||||
this._homeButton = this._createButton(
|
||||
//this.options.zoomInText, this.options.zoomInTitle,
|
||||
"", "b.title",
|
||||
className + '-do', container, this._goHome, this);
|
||||
this._init();
|
||||
//}
|
||||
|
||||
//this._updateDisabled();
|
||||
//map.on('zoomend zoomlevelschange', this._updateDisabled, this);
|
||||
return container;
|
||||
}
|
||||
|
||||
_init() {
|
||||
// show or hide widget
|
||||
this._visible();
|
||||
|
||||
////// if no extent set, set extent to map extent
|
||||
//if (!this.home) {
|
||||
// this.home = this.map.getBounds();
|
||||
//}
|
||||
|
||||
//// widget is now loaded
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
_visible() {
|
||||
if (this.visible === true) {
|
||||
//domStyle.set(self.domNode, 'display', 'block');
|
||||
// $(this._container).css('display', 'block');
|
||||
this._container.style.display = 'block';
|
||||
} else {
|
||||
//domStyle.set(self.domNode, 'display', 'none');
|
||||
// $(this._container).css('display', 'none');
|
||||
this._container.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
_goHome() {
|
||||
//this._map.zoomIn(e.shiftKey ? 3 : 1);
|
||||
this._exitFired = false;
|
||||
|
||||
//var bounds = L.latLngBounds(this.options.home._southWest, this.options.home._northEast);
|
||||
//this.map.fitBounds(bounds);
|
||||
this.map.reset();
|
||||
}
|
||||
|
||||
_createButton(html, title, className, container, fn, context) {
|
||||
//var link = L.DomUtil.create('a', className, container);
|
||||
let link = dom.createDom("span", { "class": className, innerHTML: html, title: title }, container);
|
||||
|
||||
// let stop = domEvent.stopPropagation();
|
||||
domEvent.on(link, 'click', fn, context);
|
||||
// .on(link, 'click', stop)
|
||||
// .on(link, 'mousedown', stop)
|
||||
// .on(link, 'dblclick', stop)
|
||||
// .on(link, 'click', domEvent.preventDefault())
|
||||
// //.on(link, 'click', fn.bind(this));
|
||||
// .on(link, 'click', fn, context);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { HomeButton };
|
49
src/js/core/Class.js
Normal file
49
src/js/core/Class.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import * as util from './utilities';
|
||||
|
||||
export function Class() { }
|
||||
|
||||
// @function extend(props: Object): Function
|
||||
// [Extends the current class](#class-inheritance) given the properties to be included.
|
||||
Class.extend = function (props) {
|
||||
|
||||
var NewClass = function () {
|
||||
|
||||
// call the constructor
|
||||
if (this.init) {
|
||||
this.init.apply(this, arguments);
|
||||
}
|
||||
|
||||
//// call all constructor hooks
|
||||
//if (this._initHooks) {
|
||||
// this.callInitHooks();
|
||||
//}
|
||||
};
|
||||
|
||||
// instantiate class without calling constructor
|
||||
var F = function () { };
|
||||
F.prototype = this.prototype;
|
||||
|
||||
var proto = new F();
|
||||
proto.constructor = NewClass;
|
||||
|
||||
NewClass.prototype = proto;
|
||||
|
||||
//inherit parent's statics
|
||||
for (var i in this) {
|
||||
if (this.hasOwnProperty(i) && i !== 'prototype') {
|
||||
NewClass[i] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
// mix given properties into the prototype
|
||||
util.extend(proto, props);
|
||||
|
||||
return NewClass;
|
||||
};
|
||||
|
||||
// @function include(properties: Object): this
|
||||
// [Includes a mixin](#class-includes) into the current class.
|
||||
Class.include = function (props) {
|
||||
util.extend(this.prototype, props);
|
||||
return this;
|
||||
};
|
206
src/js/core/EventEmitter.js
Normal file
206
src/js/core/EventEmitter.js
Normal file
|
@ -0,0 +1,206 @@
|
|||
import * as util from './utilities';
|
||||
|
||||
var eventsKey = '_events';
|
||||
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
var test = "test";
|
||||
}
|
||||
|
||||
_getEvents() {
|
||||
return this._events || (this._events = {});
|
||||
}
|
||||
|
||||
getListeners(evt) {
|
||||
var events = this._getEvents();
|
||||
var response;
|
||||
var key;
|
||||
|
||||
// Return a concatenated array of all matching events if
|
||||
// the selector is a regular expression.
|
||||
if (evt instanceof RegExp) {
|
||||
response = {};
|
||||
for (key in events) {
|
||||
if (events.hasOwnProperty(key) && evt.test(key)) {
|
||||
response[key] = events[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = events[evt] || (events[evt] = []);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
getListenersAsObject(evt) {
|
||||
var listeners = this.getListeners(evt);
|
||||
var response;
|
||||
|
||||
if (listeners instanceof Array) {
|
||||
response = {};
|
||||
response[evt] = listeners;
|
||||
}
|
||||
|
||||
return response || listeners;
|
||||
}
|
||||
|
||||
addListener(evt, fn, context) { // (String, Function[, Object]) or (Object[, Object])
|
||||
|
||||
// types can be a map of types/handlers
|
||||
//if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; }
|
||||
|
||||
//var events = this.getListenersAsObject(evt);
|
||||
var events = this[eventsKey] = this[eventsKey] || {};
|
||||
var contextId = context && context !== this && util.stamp(context);
|
||||
var i, len, event, type, indexKey, indexLenKey, typeIndex;
|
||||
|
||||
//// types can be a string of space-separated words
|
||||
//types = util.splitWords(types);
|
||||
|
||||
//for (i = 0, len = types.length; i < len; i++) {
|
||||
event = {
|
||||
action: fn,
|
||||
context: context || this
|
||||
};
|
||||
type = evt;// types[i];
|
||||
|
||||
if (contextId) {
|
||||
// store listeners of a particular context in a separate hash (if it has an id)
|
||||
// gives a major performance boost when removing thousands of map layers
|
||||
|
||||
indexKey = type + '_idx';
|
||||
indexLenKey = indexKey + '_len';
|
||||
|
||||
typeIndex = events[indexKey] = events[indexKey] || {};
|
||||
|
||||
if (!typeIndex[contextId]) {
|
||||
typeIndex[contextId] = [];
|
||||
|
||||
// keep track of the number of keys in the index to quickly check if it's empty
|
||||
events[indexLenKey] = (events[indexLenKey] || 0) + 1;
|
||||
}
|
||||
|
||||
typeIndex[contextId].push(event);
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
events[type] = events[type] || [];
|
||||
events[type].push(event);
|
||||
}
|
||||
//}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removeListener(evt, fn, context) { // ([String, Function, Object]) or (Object[, Object])
|
||||
|
||||
if (!this[eventsKey]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
//if (!types) {
|
||||
// return this.clearAllEventListeners();
|
||||
//}
|
||||
|
||||
//if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; }
|
||||
|
||||
var events = this[eventsKey],
|
||||
contextId = context && context !== this && util.stamp(context),
|
||||
i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed;
|
||||
|
||||
//types = L.Util.splitWords(types);
|
||||
|
||||
//for (i = 0, len = types.length; i < len; i++) {
|
||||
type = evt;//types[i];
|
||||
indexKey = type + '_idx';
|
||||
indexLenKey = indexKey + '_len';
|
||||
|
||||
typeIndex = events[indexKey];
|
||||
|
||||
if (!fn) {
|
||||
// clear all listeners for a type if function isn't specified
|
||||
delete events[type];
|
||||
delete events[indexKey];
|
||||
delete events[indexLenKey];
|
||||
|
||||
}
|
||||
else {
|
||||
listeners = contextId && typeIndex ? typeIndex[contextId] : events[type];
|
||||
|
||||
if (listeners) {
|
||||
for (j = listeners.length - 1; j >= 0; j--) {
|
||||
if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) {
|
||||
removed = listeners.splice(j, 1);
|
||||
// set the old action to a no-op, because it is possible
|
||||
// that the listener is being iterated over as part of a dispatch
|
||||
//removed[0].action = util.falseFn;
|
||||
}
|
||||
}
|
||||
|
||||
if (context && typeIndex && (listeners.length === 0)) {
|
||||
delete typeIndex[contextId];
|
||||
events[indexLenKey]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
hasEventListeners(type) { // (String) -> Boolean
|
||||
var events = this[eventsKey];
|
||||
return !!events && ((type in events && events[type].length > 0) ||
|
||||
(type + '_idx' in events && events[type + '_idx_len'] > 0));
|
||||
}
|
||||
|
||||
// (String[, Object])
|
||||
emit(type, data) {
|
||||
if (!this.hasEventListeners(type)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var event = util.extend({}, data, { type: type, target: this });
|
||||
|
||||
var events = this[eventsKey],
|
||||
listeners, i, len, typeIndex, contextId;
|
||||
|
||||
if (events[type]) {
|
||||
// make sure adding/removing listeners inside other listeners won't cause infinite loop
|
||||
listeners = events[type].slice();
|
||||
|
||||
for (i = 0, len = listeners.length; i < len; i++) {
|
||||
listeners[i].action.call(listeners[i].context, event);
|
||||
}
|
||||
}
|
||||
|
||||
// fire event for the context-indexed listeners as well
|
||||
typeIndex = events[type + '_idx'];
|
||||
|
||||
for (contextId in typeIndex) {
|
||||
listeners = typeIndex[contextId].slice();
|
||||
|
||||
if (listeners) {
|
||||
for (i = 0, len = listeners.length; i < len; i++) {
|
||||
listeners[i].action.call(listeners[i].context, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// aliases; we should ditch those eventually
|
||||
|
||||
// @method on(…): this
|
||||
// Alias to [`on(…)`](#evented-on)
|
||||
EventEmitter.on = EventEmitter.addListener;
|
||||
|
||||
// @method off(…): this
|
||||
EventEmitter.off = EventEmitter.removeListener;
|
||||
|
||||
export { EventEmitter };
|
82
src/js/core/Map.js
Normal file
82
src/js/core/Map.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { OrbitControls } from '../lib/OrbitControls';
|
||||
import * as dom from './domUtil';
|
||||
import { HomeButton } from '../controls/HomeButton';
|
||||
import * as util from './utilities';
|
||||
|
||||
class Map extends OrbitControls {
|
||||
|
||||
container;
|
||||
_layers;
|
||||
_controlCorners;
|
||||
_controlContainer;
|
||||
_controls;
|
||||
|
||||
constructor(camera, scene, domElement, container) {
|
||||
// call parent constructor of OrbitControls
|
||||
super(camera, scene, domElement);
|
||||
|
||||
this.container = container;
|
||||
|
||||
//init the control corners
|
||||
if (this._initControlPos) {
|
||||
this._initControlPos();
|
||||
}
|
||||
this._layers = {};
|
||||
this.initControls();
|
||||
}
|
||||
|
||||
_initControlPos() {
|
||||
//var test = document.getElementById("webgl");
|
||||
|
||||
var corners = this._controlCorners = {};
|
||||
var l = 'gba-';
|
||||
var container = this._controlContainer =
|
||||
//util.create('div', l + 'control-container', this.domElement);
|
||||
dom.createDom("div", { "class": l + 'control-container' }, this.container);
|
||||
|
||||
function createCorner(vSide, hSide) {
|
||||
var className = l + vSide + ' ' + l + hSide;
|
||||
|
||||
//corners[vSide + hSide] = util.create('div', className, container);
|
||||
corners[vSide + hSide] = dom.createDom("div", { "class": className }, container);
|
||||
}
|
||||
|
||||
createCorner('top', 'left');
|
||||
createCorner('top', 'right');
|
||||
createCorner('bottom', 'left');
|
||||
createCorner('bottom', 'right');
|
||||
}
|
||||
|
||||
initControls() {
|
||||
this._controls = this._controls || {};
|
||||
// this._controls.homeControl = (new HomeButton()).addTo(this);
|
||||
let homeControl = this._controls.homeControl = new HomeButton();
|
||||
homeControl.addTo(this);
|
||||
}
|
||||
|
||||
addLayer(layer) {
|
||||
var id = util.stamp(layer);
|
||||
if (this._layers[id]) {
|
||||
return this;
|
||||
}
|
||||
this._layers[id] = layer;
|
||||
|
||||
//layer._mapToAdd = this;
|
||||
layer.index = id;
|
||||
|
||||
//if (layer.beforeAdd) {
|
||||
// layer.beforeAdd(this);
|
||||
//}
|
||||
//this.whenReady(layer._layerAdd, layer);
|
||||
layer._layerAdd(this);
|
||||
this.emit("change");
|
||||
return this;
|
||||
}
|
||||
|
||||
hasLayer (layer) {
|
||||
return !!layer && (util.stamp(layer) in this._layers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Map };
|
157
src/js/core/domEvent.js
Normal file
157
src/js/core/domEvent.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
//static function
|
||||
var stampForFn = (function () {
|
||||
var lastId = 0,
|
||||
key = '_id';
|
||||
return function (obj) {
|
||||
obj[key] = obj[key] || ++lastId;
|
||||
return obj[key];
|
||||
};
|
||||
}());
|
||||
|
||||
export function addListener(obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
|
||||
|
||||
var id = stampForFn(fn);
|
||||
var key = '_gba_' + type + id;
|
||||
var handler, originalHandler, newType;
|
||||
|
||||
if (obj[key]) { return this; }
|
||||
|
||||
handler = function (e) {
|
||||
return fn.call(context || obj, e || domEvent._getEvent());
|
||||
};
|
||||
|
||||
//if (L.Browser.pointer && type.indexOf('touch') === 0) {
|
||||
// return this.addPointerListener(obj, type, handler, id);
|
||||
//}
|
||||
//if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
|
||||
// this.addDoubleTapListener(obj, handler, id);
|
||||
//}
|
||||
|
||||
if ('addEventListener' in obj) {
|
||||
|
||||
if (type === 'mousewheel') {
|
||||
obj.addEventListener('DOMMouseScroll', handler, false);
|
||||
obj.addEventListener(type, handler, false);
|
||||
|
||||
}
|
||||
else if ((type === 'mouseenter') || (type === 'mouseleave')) {
|
||||
|
||||
originalHandler = handler;
|
||||
newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
|
||||
|
||||
handler = function (e) {
|
||||
if (!_checkMouse(obj, e)) { return; }
|
||||
return originalHandler(e);
|
||||
};
|
||||
|
||||
obj.addEventListener(newType, handler, false);
|
||||
|
||||
}
|
||||
//else if (type === 'click' && L.Browser.android) {
|
||||
// originalHandler = handler;
|
||||
// handler = function (e) {
|
||||
// return L.DomEvent._filterClick(e, originalHandler);
|
||||
// };
|
||||
|
||||
// obj.addEventListener(type, handler, false);
|
||||
//}
|
||||
else {
|
||||
obj.addEventListener(type, handler, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if ('attachEvent' in obj) {
|
||||
obj.attachEvent('on' + type, handler);
|
||||
}
|
||||
|
||||
obj[key] = handler;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// @function on(…): this
|
||||
// Alias to [`L.DomEvent.on`](#domevent-on)
|
||||
export { addListener as on };
|
||||
|
||||
export function removeListener(obj, type, fn) { // (HTMLElement, String, Function)
|
||||
|
||||
var id = stampForFn(fn);
|
||||
var key = '_gba_' + type + id;
|
||||
var handler = obj[key];
|
||||
|
||||
if (!handler) { return this; }
|
||||
|
||||
//if (L.Browser.pointer && type.indexOf('touch') === 0) {
|
||||
// this.removePointerListener(obj, type, id);
|
||||
//} else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
|
||||
// this.removeDoubleTapListener(obj, id);
|
||||
|
||||
//} else if ('removeEventListener' in obj) {
|
||||
if ('removeEventListener' in obj) {
|
||||
if (type === 'mousewheel') {
|
||||
obj.removeEventListener('DOMMouseScroll', handler, false);
|
||||
obj.removeEventListener(type, handler, false);
|
||||
|
||||
} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
|
||||
obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
|
||||
} else {
|
||||
obj.removeEventListener(type, handler, false);
|
||||
}
|
||||
} else if ('detachEvent' in obj) {
|
||||
obj.detachEvent('on' + type, handler);
|
||||
}
|
||||
|
||||
obj[key] = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// @function removeListener(…): this
|
||||
// Alias to [`L.DomEvent.off`](#domevent-off)
|
||||
export { removeListener as off };
|
||||
|
||||
// check if element really left/entered the event target (for mouseenter/mouseleave)
|
||||
function _checkMouse(el, e) {
|
||||
var related = e.relatedTarget;
|
||||
|
||||
if (!related) { return true; }
|
||||
|
||||
try {
|
||||
while (related && (related !== el)) {
|
||||
related = related.parentNode;
|
||||
}
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return (related !== el);
|
||||
}
|
||||
|
||||
export function stopPropagation(e) {
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
} else {
|
||||
e.cancelBubble = true;
|
||||
}
|
||||
skipped(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
export function preventDefault(e) {
|
||||
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
} else {
|
||||
e.returnValue = false;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
var skipEvents = {};
|
||||
|
||||
export function skipped(e) {
|
||||
var skipped = skipEvents[e.type];
|
||||
// reset when checking, as it's only used in map container and propagates outside of the map
|
||||
skipEvents[e.type] = false;
|
||||
return skipped;
|
||||
}
|
102
src/js/core/domUtil.js
Normal file
102
src/js/core/domUtil.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
import * as util from './utilities';
|
||||
/*
|
||||
* @namespace DomUtil
|
||||
*
|
||||
* Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
|
||||
* tree, used by Leaflet internally.
|
||||
*
|
||||
* Most functions expecting or returning a `HTMLElement` also work for
|
||||
* SVG elements. The only difference is that classes refer to CSS classes
|
||||
* in HTML and SVG classes in SVG.
|
||||
*/
|
||||
|
||||
// @function get(id: String|HTMLElement): HTMLElement
|
||||
// Returns an element given its DOM id, or returns the element itself
|
||||
// if it was passed directly.
|
||||
export function byId(id, doc) {
|
||||
// inline'd type check.
|
||||
// be sure to return null per documentation, to match IE branch.
|
||||
return ((typeof id == "string") ? (doc || document).getElementById(id) : id) || null; // DOMNode
|
||||
}
|
||||
|
||||
// @function empty(el: HTMLElement)
|
||||
// Removes all of `el`'s children elements from `el`
|
||||
export function empty(el) {
|
||||
while (el.firstChild) {
|
||||
el.removeChild(el.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function createDom(tagName, opt_attributes, parent_node) {
|
||||
return _createDom(document, arguments);
|
||||
}
|
||||
|
||||
function _createDom(doc, args) {
|
||||
var tagName = args[0];
|
||||
var attributes = args[1];
|
||||
//var var_args = args[2];
|
||||
|
||||
var element = doc.createElement(tagName);
|
||||
//var element = $(tagName);
|
||||
|
||||
if (attributes) {
|
||||
if (typeof attributes === "string") {
|
||||
element.className = attributes;
|
||||
}
|
||||
//else if ($.isArray(attributes)) {
|
||||
// //goog.dom.classes.add.apply(null, [element].concat(attributes));
|
||||
//}
|
||||
else {
|
||||
setProperties(element, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length > 2) {
|
||||
var parent_node = args[2];
|
||||
parent_node.appendChild(element);
|
||||
//parent_node.insertBefore(element, parent_node.firstChild)
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
export function setProperties(element, properties) {
|
||||
//goog.object.forEach(properties, function(val, key) {
|
||||
// $.each(properties, function (key, val) {
|
||||
for (let key in properties) {
|
||||
// object.update();
|
||||
// });
|
||||
let val = properties[key];
|
||||
if (key === 'style') {
|
||||
element.style.cssText = val;
|
||||
}
|
||||
else if (key === 'class') {
|
||||
element.className = val;
|
||||
}
|
||||
else if (key === 'for') {
|
||||
element.htmlFor = val;
|
||||
}
|
||||
else if (key in ATTRIBUTE_MAP) {
|
||||
element.setAttribute(ATTRIBUTE_MAP[key], val);
|
||||
}
|
||||
else {
|
||||
element[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ATTRIBUTE_MAP = {
|
||||
'cellpadding': 'cellPadding',
|
||||
'cellspacing': 'cellSpacing',
|
||||
'colspan': 'colSpan',
|
||||
'frameborder': 'frameBorder',
|
||||
'height': 'height',
|
||||
'maxlength': 'maxLength',
|
||||
'role': 'role',
|
||||
'rowspan': 'rowSpan',
|
||||
'type': 'type',
|
||||
'usemap': 'useMap',
|
||||
'valign': 'vAlign',
|
||||
'width': 'width'
|
||||
};
|
11
src/js/core/index.js
Normal file
11
src/js/core/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export { Class } from './Class';
|
||||
|
||||
import { EventEmitter } from './Events';
|
||||
export { EventEmitter };
|
||||
|
||||
import * as util from './utilities';
|
||||
export { util };
|
||||
export { setLoading, extend, showLoading, hideLoading, setOptions } from './utilities';
|
||||
|
||||
import * as dom from './dom';
|
||||
export { dom };
|
143
src/js/core/utilities.js
Normal file
143
src/js/core/utilities.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
import * as dom from './domUtil';
|
||||
/*
|
||||
* @namespace util
|
||||
*
|
||||
* Various utility functions, used by GeptiefExplore internally.
|
||||
*/
|
||||
|
||||
const LOADING_ID_PREFIX = "loading_";
|
||||
|
||||
export function setLoading(elemID) {
|
||||
//debug("setting loading " + elemID);
|
||||
|
||||
//// 1) create the jQuery Deferred object that will be used
|
||||
//var deferred = $.Deferred();
|
||||
//var ownerDocumentBody = document.getElementById(elemID);
|
||||
|
||||
var loadingDivID = LOADING_ID_PREFIX + elemID;
|
||||
//var loadingDivID = "loading_" + elemID;
|
||||
var existingDiv = document.getElementById(loadingDivID);
|
||||
//var existingDiv = dom.byId(loadingDivID);
|
||||
|
||||
|
||||
// if the loading div for given element already exists,
|
||||
// increment the lock attribute value (or create the attribute,
|
||||
// if not exists)
|
||||
if (existingDiv != null) {
|
||||
//existingDiv.css("display", "inline");
|
||||
//dom.setProperties(existingDiv, {
|
||||
// style: "display: block"
|
||||
//});
|
||||
if (typeof (existingDiv.attr("lock")) === "undefined") {
|
||||
existingDiv.attr("lock", 1);
|
||||
} else {
|
||||
existingDiv.attr("lock", parseInt(existingDiv.attr("lock")) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
// otherwise, create the div and append it to body
|
||||
else {
|
||||
// construct the div from markup
|
||||
//var loadingDiv = dom.createDom("div", { id: loadingDivID, "class": "loading" }, ownerDocumentBody);
|
||||
//var loadingDiv = $("<div id=\"" + loadingDivID + "\" class=\"loading\"></div>");
|
||||
var loadingDiv = document.createElement('div');
|
||||
loadingDiv.setAttribute("id", loadingDivID);
|
||||
loadingDiv.setAttribute("class", "loading");
|
||||
|
||||
//var loadingDivContent = dom.createDom("div", { id: loadingDivID, innerHTML: myLabels.viewer.messages.waitMessage, "class": "loading-content" }, loadingDiv);
|
||||
// var loadingDivContent = $("<div class=\"loading-content\">" + myLabels.viewer.messages.waitMessage + "</div>");
|
||||
var loadingDivContent = document.createElement("div");
|
||||
loadingDivContent.innerHTML = "Hi there and greetings!";
|
||||
loadingDiv.appendChild(loadingDivContent);
|
||||
|
||||
// get the element to be covered with the loading div...
|
||||
//var targetElement = $("#" + elemID);
|
||||
var targetElement = document.getElementById(elemID);
|
||||
|
||||
// ... and get its proportions
|
||||
// var offset = targetElement.offset();
|
||||
var width = targetElement.offsetWidth
|
||||
var height = targetElement.offsetHeight;
|
||||
|
||||
// make the div fit the target element
|
||||
// loadingDiv.css({
|
||||
// "left": targetElement.offsetLeft + "px",
|
||||
// "top": targetElement.offsetTop + "px",
|
||||
// "width": width + "px",
|
||||
// "height": height + "px"
|
||||
// });
|
||||
//dom.setProperties(loadingDiv, {
|
||||
// style: "width:" + width + "px; height:" + height + "px;"// left:" + offset.left + "px; top:" + offset.top + "px"
|
||||
//});
|
||||
loadingDiv.style.left = targetElement.offsetLeft + "px";
|
||||
loadingDiv.style.top = targetElement.offsetTop + "px";
|
||||
loadingDiv.style.width = width + "px";
|
||||
loadingDiv.style.height = height + "px";
|
||||
|
||||
|
||||
// make the text appear in the middle of the loading div
|
||||
//dom.setProperties(loadingDivContent, {
|
||||
// style: "line-height:" + height + "px;"
|
||||
//});
|
||||
// loadingDivContent.css({
|
||||
// "line-height": height + "px"
|
||||
// });
|
||||
loadingDivContent.style.lineHeight = height + "px";
|
||||
|
||||
|
||||
// $("body").append(loadingDiv);
|
||||
//loadingDiv.appendTo('body');
|
||||
document.body.appendChild(loadingDiv);
|
||||
|
||||
return loadingDiv;
|
||||
}
|
||||
}
|
||||
|
||||
// @function extend(dest: Object, src?: Object): Object
|
||||
// extend an object with properties of one or more other objects
|
||||
export function extend(dest) {
|
||||
var i, j, len, src;
|
||||
|
||||
for (j = 1, len = arguments.length; j < len; j++) {
|
||||
src = arguments[j];
|
||||
for (i in src) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
export function showLoading () {
|
||||
var element = dom.byId("loadingImg");
|
||||
//domUtil.show(_loading);
|
||||
if (element) {
|
||||
element.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
export function hideLoading () {
|
||||
var element = dom.byId("loadingImg");
|
||||
if (element) {
|
||||
element.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`
|
||||
export function setOptions (obj, options) {
|
||||
if (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
|
||||
obj.options = obj.options ? create(obj.options) : {};
|
||||
}
|
||||
for (var i in options) {
|
||||
obj.options[i] = options[i];
|
||||
}
|
||||
return obj.options;
|
||||
}
|
||||
|
||||
export function stamp (){
|
||||
var lastId = 0,
|
||||
key = '_gba_id';
|
||||
return function (obj) {
|
||||
obj[key] = obj[key] || ++lastId;
|
||||
return obj[key];
|
||||
};
|
||||
}
|
35
src/js/layer/BoxLayer.js
Normal file
35
src/js/layer/BoxLayer.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { BoxGeometry } from 'three/src/geometries/BoxGeometry';
|
||||
import { MeshBasicMaterial } from 'three/src/materials/MeshBasicMaterial';
|
||||
import { Mesh } from 'three/src/objects/Mesh';
|
||||
import { Layer } from './Layer'
|
||||
|
||||
class BoxLayer extends Layer {
|
||||
|
||||
constructor(size) {
|
||||
super();
|
||||
|
||||
this.geometry = new BoxGeometry(size.width, size.height, size.depth);
|
||||
this.material = new MeshBasicMaterial({
|
||||
color: 800080
|
||||
});
|
||||
|
||||
this.mesh = new Mesh(this.geometry, this.material);
|
||||
}
|
||||
|
||||
onAdd(map) {
|
||||
this.build(this.getScene());
|
||||
//this.update();
|
||||
this.emit('add');
|
||||
}
|
||||
|
||||
build(app_scene) {
|
||||
// this.objects.push(layer);
|
||||
app_scene.add(this.getMesh());
|
||||
}
|
||||
|
||||
getMesh() {
|
||||
return this.mesh;
|
||||
}
|
||||
}
|
||||
|
||||
export { BoxLayer };
|
16
src/js/layer/DxfLayer.js
Normal file
16
src/js/layer/DxfLayer.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { Group } from 'three/src/objects/Group';
|
||||
|
||||
class DxfLayer extends Layer {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.objectGroup = new Group();
|
||||
this.queryableObjects = [];
|
||||
this.borderVisible = false;
|
||||
this.declaredClass = "DxfLayer";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { DxfLayer };
|
47
src/js/layer/Layer.js
Normal file
47
src/js/layer/Layer.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { EventEmitter } from '../core/EventEmitter';
|
||||
|
||||
class Layer extends EventEmitter {
|
||||
|
||||
options = {
|
||||
pane: 'overlayPane',
|
||||
nonBubblingEvents: [] // Array of events that should not be bubbled to DOM parents (like the map)
|
||||
};
|
||||
|
||||
constructor(size) {
|
||||
super();
|
||||
}
|
||||
|
||||
addTo(map) {
|
||||
map.addLayer(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
_layerAdd(e) {
|
||||
var map = e;//.target;
|
||||
|
||||
// check in case layer gets added and then removed before the map is ready
|
||||
if (!map.hasLayer(this)) { return; }
|
||||
|
||||
this._map = map;
|
||||
//this._zoomAnimated = map._zoomAnimated;
|
||||
|
||||
//if (this.getEvents) {
|
||||
// map.on(this.getEvents(), this);
|
||||
//}
|
||||
|
||||
this.onAdd(map);
|
||||
|
||||
//if (this.getAttribution && this._map.attributionControl) {
|
||||
// this._map.attributionControl.addAttribution(this.getAttribution());
|
||||
//}
|
||||
//this.fire('add');
|
||||
//map.fire('layeradd', { layer: this });
|
||||
}
|
||||
|
||||
getScene() {
|
||||
return this._map.scene;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Layer };
|
794
src/js/lib/OrbitControls.js
Normal file
794
src/js/lib/OrbitControls.js
Normal file
|
@ -0,0 +1,794 @@
|
|||
import { Vector2 } from 'three/src/math/Vector2';
|
||||
import { Vector3 } from 'three/src/math/Vector3';
|
||||
import { EventEmitter } from '../core/EventEmitter';
|
||||
import * as domEvent from '../core/domEvent';
|
||||
|
||||
/**
|
||||
* INTERNALS: Any functions not added to the OrbitControls reference won't be visible, or accessible outside of
|
||||
* this file (closure); however, these methods and functions don't belong to the OrbitControls class either
|
||||
* and are static as a result.
|
||||
*/
|
||||
var EPS = 0.000001;
|
||||
var mouseDownPoint = new Vector2();
|
||||
var mouseUpPoint = new Vector2();
|
||||
var rotateStart = new Vector2();
|
||||
var rotateEnd = new Vector2();
|
||||
var rotateDelta = new Vector2();
|
||||
var panStart = new Vector2();
|
||||
var panEnd = new Vector2();
|
||||
var panDelta = new Vector2();
|
||||
|
||||
var panOffset = new Vector3();
|
||||
var offset = new Vector3();
|
||||
|
||||
var dollyStart = new Vector2();
|
||||
var dollyEnd = new Vector2();
|
||||
var dollyDelta = new Vector2();
|
||||
|
||||
var phiDelta = 0;
|
||||
var thetaDelta = 0;
|
||||
var scale = 1;
|
||||
var pan = new Vector3();
|
||||
|
||||
var lastPosition = new Vector3();
|
||||
|
||||
|
||||
|
||||
var STATE = { NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5, CAMERA_ROTATE: 6 };
|
||||
var state = STATE.NONE;
|
||||
|
||||
class OrbitControls extends EventEmitter {
|
||||
|
||||
object;
|
||||
domElement;
|
||||
scene;
|
||||
enabled;
|
||||
target;
|
||||
center;
|
||||
noZoom;
|
||||
zoomSpeed;
|
||||
minDistance;
|
||||
maxDistance;
|
||||
upsideDown;
|
||||
|
||||
|
||||
noRotate;
|
||||
rotateSpeed;
|
||||
keyRotateAngle;
|
||||
noPan;
|
||||
keyPanSpeed;
|
||||
|
||||
autoRotateSpeed;
|
||||
|
||||
minPolarAngle;
|
||||
maxPolarAngle;
|
||||
|
||||
noKeys;
|
||||
keys;
|
||||
|
||||
|
||||
target0;
|
||||
position0;
|
||||
|
||||
rotateDifference;
|
||||
|
||||
constructor(camera, scene, domElement) {
|
||||
super();
|
||||
|
||||
this.object = camera;
|
||||
this.domElement = (domElement !== undefined) ? domElement : document;
|
||||
this.scene = scene;
|
||||
|
||||
// Set to false to disable this control
|
||||
this.enabled = true;
|
||||
|
||||
// "target" sets the location of focus, where the control orbits around
|
||||
// and where it pans with respect to.
|
||||
this.target = new Vector3();
|
||||
// center is old, deprecated; use "target" instead
|
||||
this.center = this.target;
|
||||
|
||||
// This option actually enables dollying in and out; left as "zoom" for
|
||||
// backwards compatibility
|
||||
this.noZoom = false;
|
||||
this.zoomSpeed = 1.0;
|
||||
|
||||
// Limits to how far you can dolly in and out
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
// Set to true for upside down
|
||||
this.upsideDown = false;
|
||||
this.object.up.set(0, 0, 1);
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noRotate = false;
|
||||
this.rotateSpeed = 1.0;
|
||||
this.keyRotateAngle = Math.PI / 90;
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noPan = false;
|
||||
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
|
||||
|
||||
// // Set to true to automatically rotate around the target
|
||||
// this.autoRotate = false;
|
||||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
|
||||
|
||||
// How far you can orbit vertically, upper and lower limits.
|
||||
// Range is 0 to Math.PI radians.
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
// Set to true to disable use of the keys
|
||||
this.noKeys = false;
|
||||
|
||||
// The four arrow keys and additional keys
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, AUTOROTATE: 82, UPSIDEDOWN: 85, RESET: 82 };
|
||||
|
||||
// for reset
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
|
||||
this._initEvents('on');
|
||||
}
|
||||
|
||||
_initEvents(onOff) {
|
||||
if (!domEvent) { return; }
|
||||
|
||||
onOff = onOff || 'on';
|
||||
|
||||
//this.domElement.addEventListener('contextmenu', function (event) { event.preventDefault(); }, false);
|
||||
domEvent
|
||||
[onOff](this.domElement, 'contextmenu', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'contextmenu', domEvent.preventDefault);
|
||||
|
||||
|
||||
//this.domElement.addEventListener('mousedown', onMouseDown, false);
|
||||
domEvent
|
||||
[onOff](this.domElement, 'mousedown', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'click', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'dblclick', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'mousedown', domEvent.preventDefault)
|
||||
[onOff](this.domElement, 'mousedown', this.onMouseDown, this);
|
||||
|
||||
//this.domElement.addEventListener('mousewheel', onMouseWheel, false);
|
||||
domEvent
|
||||
[onOff](this.domElement, 'mousewheel', domEvent.preventDefault)
|
||||
[onOff](this.domElement, 'mousewheel', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'mousewheel', this.onMouseWheel, this);
|
||||
|
||||
//this.domElement.addEventListener('DOMMouseScroll', onMouseWheel, false); // firefox
|
||||
domEvent
|
||||
[onOff](this.domElement, 'DOMMouseScroll', domEvent.preventDefault)
|
||||
[onOff](this.domElement, 'DOMMouseScroll', domEvent.stopPropagation)
|
||||
[onOff](this.domElement, 'DOMMouseScroll', this.onMouseWheel, this);
|
||||
|
||||
|
||||
//window.addEventListener('keydown', onKeyDown, false);
|
||||
domEvent
|
||||
[onOff](window, 'keydown', domEvent.stopPropagation)
|
||||
[onOff](window, 'keydown', domEvent.preventDefault)
|
||||
[onOff](window, 'keydown', this.onKeyDown, this);
|
||||
|
||||
//this.domElement.addEventListener('mousemove', onMouseMove, false);
|
||||
domEvent[onOff](this.domElement, 'mousemove', this.onMouseMove, this);
|
||||
|
||||
//this.domElement.addEventListener('touchstart', touchstart, false);
|
||||
domEvent[onOff](this.domElement, 'touchstart', this.onTouchStart, this);
|
||||
//this.domElement.addEventListener('touchend', touchend, false);
|
||||
domEvent[onOff](this.domElement, 'touchend', this.onTouchEnd, this);
|
||||
//this.domElement.addEventListener('touchmove', touchmove, false);
|
||||
domEvent[onOff](this.domElement, 'touchmove', this.onTouchMove, this);
|
||||
|
||||
domEvent[onOff](this.domElement, 'mouseleave', this.onMouseLeave, this);
|
||||
}
|
||||
|
||||
_getZoomScale() {
|
||||
return Math.pow(0.95, this.zoomSpeed);
|
||||
}
|
||||
|
||||
_AutoRotationAngle() {
|
||||
return 2 * Math.PI / 60 / 60 * this.autoRotateSpeed;
|
||||
}
|
||||
|
||||
rotateLeft(angle) {
|
||||
if (angle === undefined) {
|
||||
angle = this._AutoRotationAngle();
|
||||
}
|
||||
thetaDelta += angle;
|
||||
}
|
||||
|
||||
rotateUp(angle) {
|
||||
if (angle === undefined) {
|
||||
angle = this._AutoRotationAngle();
|
||||
}
|
||||
phiDelta -= angle;
|
||||
}
|
||||
|
||||
// pass in distance in world space to move left
|
||||
panLeft(distance) {
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get X column of matrix
|
||||
panOffset.set(te[0], te[1], te[2]);
|
||||
panOffset.multiplyScalar(-distance);
|
||||
|
||||
pan.add(panOffset);
|
||||
}
|
||||
|
||||
// pass in distance in world space to move up
|
||||
panUp(distance) {
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get Y column of matrix
|
||||
panOffset.set(te[4], te[5], te[6]);
|
||||
panOffset.multiplyScalar(distance);
|
||||
|
||||
pan.add(panOffset);
|
||||
}
|
||||
|
||||
// pass in x,y of change desired in pixel space,
|
||||
// right and down are positive
|
||||
pan(deltaX, deltaY) {
|
||||
var element = this.domElement === document ? this.domElement.body : this.domElement;
|
||||
|
||||
if (this.object.fov !== undefined) {
|
||||
|
||||
// perspective
|
||||
var position = this.object.position;
|
||||
var offset = position.clone().sub(this.target);
|
||||
var targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan((this.object.fov / 2) * Math.PI / 180.0);
|
||||
|
||||
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
||||
this.panLeft(2 * deltaX * targetDistance / element.clientHeight);
|
||||
this.panUp(2 * deltaY * targetDistance / element.clientHeight);
|
||||
|
||||
}
|
||||
else if (this.object.top !== undefined) {
|
||||
|
||||
// orthographic
|
||||
this.panLeft(deltaX * (this.object.right - this.object.left) / element.clientWidth);
|
||||
this.panUp(deltaY * (this.object.top - this.object.bottom) / element.clientHeight);
|
||||
|
||||
}
|
||||
else {
|
||||
// camera neither orthographic or perspective
|
||||
console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.');
|
||||
}
|
||||
}
|
||||
|
||||
moveForward(delta) {
|
||||
var element = this.domElement === document ? this.domElement.body : this.domElement;
|
||||
|
||||
panOffset.copy(this.object.position).sub(this.target);
|
||||
var targetDistance = panOffset.length() * Math.tan((this.object.fov / 2) * Math.PI / 180.0);
|
||||
panOffset.z = 0;
|
||||
panOffset.normalize();
|
||||
panOffset.multiplyScalar(-2 * delta * targetDistance / element.clientHeight);
|
||||
|
||||
pan.add(panOffset);
|
||||
}
|
||||
|
||||
dollyIn(dollyScale) {
|
||||
if (dollyScale === undefined) {
|
||||
dollyScale = this._getZoomScale();
|
||||
}
|
||||
scale /= dollyScale;
|
||||
this.update();
|
||||
}
|
||||
|
||||
dollyOut(dollyScale) {
|
||||
if (dollyScale === undefined) {
|
||||
dollyScale = this._getZoomScale();
|
||||
}
|
||||
scale *= dollyScale;
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
var position = this.object.position; //x = 0, y = -100, z = 200;
|
||||
|
||||
// move target to panned location
|
||||
this.target.add(pan);//target ist am Anfang immer x = 0; y = 0; z = 0;
|
||||
//auch pan ist am Anfang immer x = 0; y = 0; z = 0;
|
||||
|
||||
//if (this.autoRotate) { //R
|
||||
// this.rotateLeft(getAutoRotationAngle());
|
||||
//}
|
||||
var theta, phi, radius;
|
||||
//if rotate:
|
||||
if (thetaDelta || phiDelta) {
|
||||
|
||||
offset.copy(position).sub(this.target);
|
||||
|
||||
// angle from y-axis around z-axis
|
||||
theta = Math.atan2(offset.x, offset.y);
|
||||
|
||||
// angle from z-axis
|
||||
phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z);
|
||||
|
||||
theta += thetaDelta;
|
||||
phi += phiDelta;
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));
|
||||
|
||||
// restrict phi to be betwee EPS and PI-EPS
|
||||
phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));
|
||||
|
||||
radius = offset.length() * scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));
|
||||
|
||||
offset.x = radius * Math.sin(phi) * Math.sin(theta);
|
||||
offset.y = radius * Math.sin(phi) * Math.cos(theta);
|
||||
offset.z = radius * Math.cos(phi);
|
||||
|
||||
position.copy(this.target).add(offset);
|
||||
|
||||
}
|
||||
//else if (cameraThetaDelta || cameraPhiDelta) {
|
||||
|
||||
// offset.copy(this.target).sub(position);
|
||||
|
||||
// // angle from y-axis around z-axis
|
||||
// theta = Math.atan2(offset.x, offset.y);
|
||||
|
||||
// // angle from z-axis
|
||||
// phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z);
|
||||
|
||||
// theta += cameraThetaDelta;
|
||||
// phi += cameraPhiDelta;
|
||||
|
||||
// // restrict phi to be between desired limits
|
||||
// phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));
|
||||
|
||||
// // restrict phi to be betwee EPS and PI-EPS
|
||||
// phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));
|
||||
|
||||
// radius = offset.length() * scale;
|
||||
|
||||
// // restrict radius to be between desired limits
|
||||
// radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));
|
||||
|
||||
// offset.x = radius * Math.sin(phi) * Math.sin(theta);
|
||||
// offset.y = radius * Math.sin(phi) * Math.cos(theta);
|
||||
// offset.z = radius * Math.cos(phi);
|
||||
|
||||
// this.target.copy(position).add(offset);
|
||||
|
||||
//}
|
||||
else if (scale !== 1) {
|
||||
|
||||
offset.copy(position).sub(this.target);
|
||||
|
||||
offset.multiplyScalar(scale);
|
||||
position.copy(this.target).add(offset);
|
||||
|
||||
}
|
||||
//pan
|
||||
else {
|
||||
position.add(pan);
|
||||
}
|
||||
|
||||
this.object.lookAt(this.target);
|
||||
|
||||
thetaDelta = 0;
|
||||
phiDelta = 0;
|
||||
// cameraThetaDelta = 0;
|
||||
// cameraPhiDelta = 0;
|
||||
scale = 1;
|
||||
pan.set(0, 0, 0);
|
||||
|
||||
if (lastPosition.distanceTo(this.object.position) > 0) {
|
||||
//this.dispatchEvent(changeEvent);
|
||||
//scope.emit("change");
|
||||
lastPosition.copy(this.object.position);
|
||||
}
|
||||
this.emit("change", lastPosition);
|
||||
}
|
||||
|
||||
reset() {
|
||||
state = STATE.NONE;
|
||||
this.target.copy(this.target0);//0 0 0
|
||||
this.object.position.copy(this.position0);
|
||||
//this.object.position.set(0, 0, 180);
|
||||
this.update();
|
||||
}
|
||||
|
||||
onMouseDown(event) {
|
||||
if (this.enabled === false) {
|
||||
return;
|
||||
}
|
||||
this.rotateDifference = 0;
|
||||
event.preventDefault();
|
||||
//2d vector:
|
||||
mouseDownPoint.set(event.clientX, event.clientY);
|
||||
|
||||
//linke Maustaste
|
||||
if (event.button === 0) {
|
||||
if (this.noRotate === true) {
|
||||
return;
|
||||
}
|
||||
state = STATE.ROTATE;
|
||||
rotateStart.set(event.clientX, event.clientY);
|
||||
this.emit("mousedown", event);
|
||||
}
|
||||
else if (event.button === 1) {
|
||||
if (this.noZoom === true) {
|
||||
return;
|
||||
}
|
||||
state = STATE.DOLLY;
|
||||
//2d vector:
|
||||
dollyStart.set(event.clientX, event.clientY);
|
||||
}
|
||||
//rechte Maustaste
|
||||
else if (event.button === 2) {
|
||||
if (this.noPan === true) {
|
||||
return;
|
||||
}
|
||||
state = STATE.PAN;
|
||||
panStart.set(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
//this.domElement.addEventListener('mousemove', onMouseDrag, false);
|
||||
domEvent.on(this.domElement, 'mousemove', this.onMouseDrag, this);
|
||||
|
||||
//this.domElement.addEventListener('mouseup', onMouseUp, false);
|
||||
domEvent.on(this.domElement, 'mouseup', this.onMouseUp, this);
|
||||
|
||||
//scope.dispatchEvent(startEvent);
|
||||
this.emit('movestart', null);
|
||||
}
|
||||
|
||||
onMouseMove(event) {
|
||||
this.emit('mouse-move', event);
|
||||
}
|
||||
|
||||
onMouseDrag(event) {
|
||||
if (this.enabled === false) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
var element = this.domElement === document ? this.domElement.body : this.domElement;
|
||||
|
||||
if (state === STATE.ROTATE) {
|
||||
|
||||
if (this.noRotate === true) return;
|
||||
|
||||
rotateEnd.set(event.clientX, event.clientY);
|
||||
rotateDelta.subVectors(rotateEnd, rotateStart);
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
this.rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth * this.rotateSpeed);
|
||||
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
this.rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * this.rotateSpeed);
|
||||
|
||||
this.rotateDifference = rotateStart.distanceTo(rotateEnd);
|
||||
rotateStart.copy(rotateEnd);
|
||||
this.emit("mouse-pan", event);
|
||||
}
|
||||
else if (state === STATE.DOLLY) {
|
||||
|
||||
if (this.noZoom === true) return;
|
||||
|
||||
dollyEnd.set(event.clientX, event.clientY);
|
||||
dollyDelta.subVectors(dollyEnd, dollyStart);
|
||||
|
||||
if (dollyDelta.y > 0) {
|
||||
this.dollyIn();
|
||||
|
||||
} else {
|
||||
this.dollyOut();
|
||||
}
|
||||
dollyStart.copy(dollyEnd);
|
||||
|
||||
}
|
||||
else if (state === STATE.PAN) {
|
||||
|
||||
if (this.noPan === true) return;
|
||||
panEnd.set(event.clientX, event.clientY);
|
||||
panDelta.subVectors(panEnd, panStart);
|
||||
|
||||
this.pan(panDelta.x, panDelta.y);
|
||||
panStart.copy(panEnd);
|
||||
|
||||
this.emit("mouse-pan", event);
|
||||
}
|
||||
//scope.emit('mouse-move', event);
|
||||
this.update();
|
||||
} //onMouseDrag end
|
||||
|
||||
onMouseUp(event) {
|
||||
if (this.enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//this.domElement.removeEventListener('mousemove', onMouseDrag, false);
|
||||
domEvent.off(this.domElement, 'mousemove', this.onMouseDrag);
|
||||
//this.domElement.removeEventListener('mouseup', onMouseUp, false);
|
||||
domEvent.off(this.domElement, 'mouseup', this.onMouseUp);
|
||||
|
||||
|
||||
//scope.dispatchEvent(endEvent);
|
||||
state = STATE.NONE;
|
||||
|
||||
mouseUpPoint.set(event.clientX, event.clientY);
|
||||
|
||||
//var distance = mouseDownPoint.distanceTo(mouseUpPoint);
|
||||
//linke Maustaste
|
||||
if (event.button === 0 && mouseDownPoint.equals(mouseUpPoint)) {// && Math.abs(this.rotateDifference) === 0 ) {
|
||||
//if (event.button === 0 && (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) ) {
|
||||
|
||||
|
||||
this.emit("clicked", event);
|
||||
//Q3D.application.canvasClicked(event);
|
||||
}
|
||||
} //onMoseUp end
|
||||
|
||||
onMouseWheel(event) {
|
||||
if (this.enabled === false || this.noZoom === true) return;
|
||||
|
||||
event.preventDefault();
|
||||
var delta = 0;
|
||||
if (event.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
|
||||
delta = event.wheelDelta;
|
||||
}
|
||||
else if (event.detail !== undefined) { // Firefox
|
||||
delta = - event.detail;
|
||||
}
|
||||
if (delta > 0) {
|
||||
this.dollyOut();
|
||||
}
|
||||
else {
|
||||
this.dollyIn();
|
||||
}
|
||||
this.update();
|
||||
//scope.dispatchEvent(startEvent);
|
||||
//scope.dispatchEvent(endEvent);
|
||||
}
|
||||
|
||||
onMouseLeave(event) {
|
||||
domEvent.off(this.domElement, 'mousemove', this.onMouseDrag);
|
||||
//this.domElement.removeEventListener('mouseup', onMouseUp, false);
|
||||
domEvent.off(this.domElement, 'mouseup', this.onMouseUp);
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
if (this.enabled === false || this.noKeys === true || this.noPan === true) return;
|
||||
|
||||
if (event.shiftKey && event.ctrlKey) {
|
||||
//zoom +
|
||||
if (event.keyCode === this.keys.UP) this.dollyOut();
|
||||
else if (event.keyCode === this.keys.BOTTOM) this.dollyIn();
|
||||
else return;
|
||||
|
||||
}
|
||||
else if (event.shiftKey) {
|
||||
|
||||
switch (event.keyCode) {
|
||||
|
||||
case this.keys.UP:
|
||||
this.rotateUp(this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.BOTTOM:
|
||||
this.rotateUp(-this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.LEFT:
|
||||
this.rotateLeft(-this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.RIGHT:
|
||||
this.rotateLeft(this.keyRotateAngle);
|
||||
break;
|
||||
//neu
|
||||
case this.keys.RESET:
|
||||
this.reset();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (event.ctrlKey) {
|
||||
|
||||
switch (event.keyCode) {
|
||||
|
||||
case this.keys.UP:
|
||||
this.rotateUp(this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.BOTTOM:
|
||||
this.rotateUp(-this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.LEFT:
|
||||
this.rotateLeft(this.keyRotateAngle);
|
||||
break;
|
||||
case this.keys.RIGHT:
|
||||
this.rotateLeft(-this.keyRotateAngle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
switch (event.keyCode) {
|
||||
|
||||
case this.keys.UP:
|
||||
this.moveForward(-this.keyPanSpeed);
|
||||
break;
|
||||
case this.keys.BOTTOM:
|
||||
this.moveForward(+this.keyPanSpeed);
|
||||
break;
|
||||
case this.keys.LEFT:
|
||||
this.pan(-this.keyPanSpeed, 0);
|
||||
break;
|
||||
case this.keys.RIGHT:
|
||||
this.pan(+this.keyPanSpeed, 0);
|
||||
break;
|
||||
//case scope.keys.AUTOROTATE: //R
|
||||
// scope.autoRotate = !scope.autoRotate;
|
||||
// break;
|
||||
case this.keys.UPSIDEDOWN:
|
||||
this.upsideDown = !this.upsideDown;
|
||||
if (this.upsideDown) this.object.up.set(0, 0, -1);
|
||||
else this.object.up.set(0, 0, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.update();
|
||||
}
|
||||
|
||||
onTouchStart(event) {
|
||||
if (this.enabled === false) return;
|
||||
|
||||
switch (event.touches.length) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
//if (scope.enableRotate === false) return;
|
||||
if (this.noRotate === true) return;
|
||||
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
|
||||
rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
//if ( scope.enableZoom === false ) return;
|
||||
if (this.noZoom === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = STATE.TOUCH_DOLLY;
|
||||
|
||||
var dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
var dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
var distance = Math.sqrt(dx * dx + dy * dy);
|
||||
dollyStart.set(0, distance);
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
//if ( scope.enablePan === false ) return;
|
||||
if (this.noPan === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = STATE.TOUCH_PAN;
|
||||
|
||||
panStart.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
//if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent );
|
||||
if (state !== STATE.NONE) {
|
||||
this.emit('touchstart', null);
|
||||
}
|
||||
}
|
||||
|
||||
onTouchMove(event) {
|
||||
if (this.enabled === false) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var element = this.domElement === document ? this.domElement.body : this.domElement;
|
||||
|
||||
switch (event.touches.length) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
//if (scope.enableRotate === false) return;
|
||||
if (this.noRotate === true) return;
|
||||
if (state !== STATE.TOUCH_ROTATE) return;
|
||||
|
||||
rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
rotateDelta.subVectors(rotateEnd, rotateStart);
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
this.rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth * this.rotateSpeed);
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
this.rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * this.rotateSpeed);
|
||||
|
||||
rotateStart.copy(rotateEnd);
|
||||
this.emit("mouse-pan", event);
|
||||
|
||||
this.update();
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
//if (scope.enableZoom === false) return;
|
||||
if (this.noZoom === true) return;
|
||||
if (state !== STATE.TOUCH_DOLLY) return;
|
||||
|
||||
var dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
var dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
var distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
dollyEnd.set(0, distance);
|
||||
dollyDelta.subVectors(dollyEnd, dollyStart);
|
||||
|
||||
if (dollyDelta.y > 0) {
|
||||
|
||||
this.dollyOut(this._getZoomScale());
|
||||
|
||||
}
|
||||
else if (dollyDelta.y < 0) {
|
||||
this.dollyIn(this._getZoomScale());
|
||||
}
|
||||
dollyStart.copy(dollyEnd);
|
||||
this.emit("mouse-pan", event);
|
||||
|
||||
this.update();
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
//if (scope.enablePan === false) return;
|
||||
if (this.noPan === true) return;
|
||||
if (state !== STATE.TOUCH_PAN) return;
|
||||
|
||||
panEnd.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
panDelta.subVectors(panEnd, panStart);
|
||||
|
||||
this.pan(panDelta.x, panDelta.y);
|
||||
|
||||
panStart.copy(panEnd);
|
||||
|
||||
this.update();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onTouchEnd( /* event */) {
|
||||
if (this.enabled === false) {
|
||||
return;
|
||||
}
|
||||
//scope.dispatchEvent(endEvent);
|
||||
this.emit('touchend', null);
|
||||
state = STATE.NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OrbitControls };
|
158
src/js/main.js
Normal file
158
src/js/main.js
Normal file
|
@ -0,0 +1,158 @@
|
|||
// import { DirectionalLight, AmbientLight, WebGLRenderer, PerspectiveCamera, Scene } from 'three';
|
||||
import { DirectionalLight } from 'three/src/lights/DirectionalLight';
|
||||
import { AmbientLight } from 'three/src/lights/AmbientLight';
|
||||
import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer';
|
||||
import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera';
|
||||
import { Scene } from 'three/src/scenes/Scene';
|
||||
import { BoxLayer } from './layer/BoxLayer';
|
||||
// import * as util from './core/utilities';
|
||||
// import { OrbitControls } from './lib/OrbitControls.js'
|
||||
import { Map } from './core/Map';
|
||||
import * as domEvent from './core/domEvent';
|
||||
|
||||
import '../css/page.css'; /* style loader will import it */
|
||||
|
||||
class Application {
|
||||
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this.running = false; // this is public
|
||||
|
||||
this.objects = [];
|
||||
|
||||
if (container.clientWidth && container.clientHeight) {
|
||||
this.width = container.clientWidth;
|
||||
this.height = container.clientHeight;
|
||||
this._fullWindow = false;
|
||||
} else {
|
||||
this.width = window.innerWidth;
|
||||
this.height = window.innerHeight;
|
||||
this._fullWindow = true;
|
||||
}
|
||||
|
||||
this.createScene();
|
||||
}
|
||||
|
||||
createScene() {
|
||||
|
||||
/* Renderer */
|
||||
var bgcolor = 0xfdfdfd;
|
||||
this.renderer = new WebGLRenderer({ alpha: true, antialias: true });
|
||||
// this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
// document.body.appendChild(this.renderer.domElement);
|
||||
this.renderer.setSize(this.width, this.height);
|
||||
this.renderer.setClearColor(bgcolor, 1); // second param is opacity, 0 => transparent
|
||||
this.renderer.render
|
||||
this.container.appendChild(this.renderer.domElement);
|
||||
|
||||
/* Scene: that will hold all our elements such as objects, cameras and lights. */
|
||||
this.scene = new Scene();
|
||||
//app.scene.add(new THREE.AmbientLight(0xeeeeee));
|
||||
this._buildDefaultLights(this.scene);
|
||||
//app.scene.autoUpdate = false;
|
||||
//// show axes in the screen
|
||||
//app.scene.add(new THREE.AxisHelper(100));
|
||||
|
||||
/* Camera */
|
||||
var angle = 45;
|
||||
var aspect = this.width / this.height;
|
||||
var near = 0.1; //This is the distance at which the camera will start rendering scene objects
|
||||
var far = 2000; //Anything beyond this distance will not be rendered
|
||||
this.camera = new PerspectiveCamera(angle, aspect, near, far);
|
||||
//this.camera.position.z = 20;
|
||||
this.camera.position.set(0, -0.1, 150);
|
||||
// this.camera.lookAt(new THREE.Vector3(0, 0, 0));
|
||||
|
||||
// this.controls = new OrbitControls(this.camera, this.scene, this.renderer.domElement);
|
||||
this.map = new Map(this.camera, this.scene, this.renderer.domElement, this.container);
|
||||
let boxLayer = new BoxLayer({ width: 10, height: 10, depth: 10 });
|
||||
this.map.addLayer(boxLayer);
|
||||
|
||||
domEvent.on(window, 'click', this.onWindowResize, this);
|
||||
|
||||
// util.setLoading("webgl");
|
||||
this.start();
|
||||
}
|
||||
|
||||
onWindowResize() {
|
||||
if (this._fullWindow) {
|
||||
this._setCanvasSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
else {
|
||||
this._setCanvasSize(this.container.clientWidth, this.container.clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
_setCanvasSize (width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.camera.aspect = width / height;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(width, height);
|
||||
this.animate();
|
||||
}
|
||||
|
||||
_buildDefaultLights(scene) {
|
||||
var deg2rad = Math.PI / 180;
|
||||
|
||||
// ambient light
|
||||
scene.add(new AmbientLight(0x999999));
|
||||
//scene.add(new THREE.AmbientLight(0xeeeeee));
|
||||
|
||||
// directional lights
|
||||
var opt = {
|
||||
azimuth: 220, // note: default light azimuth of gdaldem hillshade is 315.
|
||||
altitude: 45 // altitude angle
|
||||
};
|
||||
//appSettings.Options.light.directional;
|
||||
var lambda = (90 - opt.azimuth) * deg2rad;
|
||||
var phi = opt.altitude * deg2rad;
|
||||
|
||||
var x = Math.cos(phi) * Math.cos(lambda),
|
||||
y = Math.cos(phi) * Math.sin(lambda),
|
||||
z = Math.sin(phi);
|
||||
|
||||
var light1 = new DirectionalLight(0xffffff, 0.5);
|
||||
light1.position.set(x, y, z);
|
||||
scene.add(light1);
|
||||
|
||||
// thin light from the opposite direction
|
||||
var light2 = new DirectionalLight(0xffffff, 0.1);
|
||||
light2.position.set(-x, -y, -z);
|
||||
scene.add(light2);
|
||||
}
|
||||
|
||||
start() {
|
||||
this.running = true;
|
||||
|
||||
this.map.addListener('change', this.animate, this); // add this only if there is no animation loop (requestAnimationFrame)
|
||||
this.animate();
|
||||
}
|
||||
|
||||
animate() {
|
||||
if (this.running) {
|
||||
// requestAnimationFrame(() => {
|
||||
// this.animate();
|
||||
// }, 1000 / 30);
|
||||
|
||||
// this.objects.forEach((object) => {
|
||||
// object.update();
|
||||
// });
|
||||
}
|
||||
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
add(layer) {
|
||||
this.objects.push(layer);
|
||||
this.scene.add(layer.getMesh());
|
||||
}
|
||||
}
|
||||
|
||||
var container = document.getElementById("webgl");
|
||||
let app = new Application(container);
|
||||
// app.add(new BoxLayer({
|
||||
// width: 10,
|
||||
// height: 10,
|
||||
// depth: 10
|
||||
// }));
|
5
typings.json
Normal file
5
typings.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"globalDevDependencies": {
|
||||
"three": "registry:dt/three#0.84.0+20170323144537"
|
||||
}
|
||||
}
|
6875
typings/globals/three/index.d.ts
vendored
Normal file
6875
typings/globals/three/index.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8
typings/globals/three/typings.json
Normal file
8
typings/globals/three/typings.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"resolution": "main",
|
||||
"tree": {
|
||||
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ccda793e883ecb20bd396121f1e89df8c1dd00ce/three/index.d.ts",
|
||||
"raw": "registry:dt/three#0.84.0+20170323144537",
|
||||
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ccda793e883ecb20bd396121f1e89df8c1dd00ce/three/index.d.ts"
|
||||
}
|
||||
}
|
1
typings/index.d.ts
vendored
Normal file
1
typings/index.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference path="globals/three/index.d.ts" />
|
163
webpack.config.js
Normal file
163
webpack.config.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
require('dotenv').config();
|
||||
const path = require('path');
|
||||
const webpack = require('webpack'); //e.g. for iusing DefinePlugin
|
||||
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
// https://www.sitepoint.com/es6-babel-webpack/
|
||||
|
||||
/**
|
||||
* flag Used to check if the environment is production or not
|
||||
*/
|
||||
const isProduction = (process.env.NODE_ENV === 'production');
|
||||
const devMode = (process.env.NODE_ENV !== 'production');
|
||||
|
||||
/**
|
||||
* Include hash to filenames for cache busting - only at production
|
||||
*/
|
||||
const fileNamePrefix = isProduction ? '[chunkhash].' : '';
|
||||
|
||||
module.exports = {
|
||||
mode: process.env.NODE_ENV,
|
||||
context: __dirname,
|
||||
entry: './src/js/main.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
//filename: fileNamePrefix + '[name].js',
|
||||
publicPath: '/dist/',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(svg|eot|ttf|woff|woff2)$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'fonts/[name].[ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif)$/,
|
||||
loaders: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'images/[name].[ext]'
|
||||
}
|
||||
},
|
||||
'img-loader'
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(less|css)$/,
|
||||
use: [
|
||||
{
|
||||
// loader: (isProduction === true) ? MiniCssExtractPlugin.loader : 'style-loader',
|
||||
// loader: 'style-loader',
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// loader: MiniCssExtractPlugin.loader,
|
||||
// options: {
|
||||
// hmr: process.env.NODE_ENV === 'development',
|
||||
// },
|
||||
},
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
sourceMap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: "less-loader",
|
||||
options: {
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
stats: {
|
||||
colors: true
|
||||
},
|
||||
// devtool: 'source-map',
|
||||
|
||||
optimization: {
|
||||
minimize: isProduction,
|
||||
minimizer: [new TerserPlugin({
|
||||
parallel: true,
|
||||
sourceMap: true,
|
||||
terserOptions: {
|
||||
extractComments: 'all',
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
keep_classnames: false,
|
||||
keep_fnames: false,
|
||||
},
|
||||
}
|
||||
})],
|
||||
},
|
||||
|
||||
plugins: [
|
||||
|
||||
new webpack.DefinePlugin({ // Remove this plugin if you don't plan to define any global constants
|
||||
ENVIRONMENT: JSON.stringify(process.env.NODE_ENV),
|
||||
CONSTANT_VALUE: JSON.stringify(process.env.CONSTANT_VALUE),
|
||||
}),
|
||||
// extractLess,
|
||||
new MiniCssExtractPlugin({ // Make sure MiniCssExtractPlugin instance is included in array before the PurifyCSSPlugin
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
// filename: '[name].css',
|
||||
// chunkFilename: '[id].css',
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
}),
|
||||
// new PurifyCSSPlugin({
|
||||
// paths: glob.sync(__dirname + '/*.html'),
|
||||
// minimize: true,
|
||||
// }),
|
||||
// new webpack.HotModuleReplacementPlugin(),
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Production only plugins
|
||||
*/
|
||||
if (isProduction === true) {
|
||||
module.exports.plugins.push(
|
||||
// module.exports.optimization.minimizer.push(
|
||||
// new UglifyJsPlugin({ sourceMap: true })
|
||||
// );
|
||||
function () { // Create a manifest.json file that contain the hashed file names of generated static resources
|
||||
this.plugin("done", function (status) {
|
||||
require("fs").writeFileSync(
|
||||
__dirname + "/dist/manifest.json",
|
||||
JSON.stringify(status.toJson().assetsByChunkName)
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
);
|
||||
//development
|
||||
} else {
|
||||
module.exports.plugins.push(
|
||||
//auto updating on dev server
|
||||
new webpack.HotModuleReplacementPlugin()// HMR plugin will cause problems with [chunkhash]
|
||||
);
|
||||
}
|
7
webpack.prod.config.js
Normal file
7
webpack.prod.config.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.config.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
});
|
Loading…
Reference in New Issue
Block a user