From 7b1ca855196fbb3d104ac38c8772c116862cc7c9 Mon Sep 17 00:00:00 2001 From: Arno Kaimbacher Date: Wed, 25 Nov 2020 17:19:26 +0100 Subject: [PATCH] - Coordinates control: styleing in extra file - NorthArrow control - add browser utilities --- index.html | 6 +- src/css/page.css | 12 +- src/js/controls/Coordinates.css | 11 ++ src/js/controls/Coordinates.js | 6 +- src/js/controls/NorthArrow.css | 53 ++++++++ src/js/controls/NorthArrow.js | 214 ++++++++++++++++++++++++++++++++ src/js/core/Map.js | 1 + src/js/core/browser.js | 6 + src/js/core/domEvent.js | 84 +++++++++++-- src/js/core/utilities.js | 11 ++ src/js/main.js | 17 ++- 11 files changed, 387 insertions(+), 34 deletions(-) create mode 100644 src/js/controls/Coordinates.css create mode 100644 src/js/controls/NorthArrow.css create mode 100644 src/js/controls/NorthArrow.js create mode 100644 src/js/core/browser.js diff --git a/index.html b/index.html index 5dd1442..dd940fe 100644 --- a/index.html +++ b/index.html @@ -28,10 +28,10 @@
-
-
+ +
diff --git a/src/css/page.css b/src/css/page.css index 86f8c6a..9cb222c 100644 --- a/src/css/page.css +++ b/src/css/page.css @@ -92,15 +92,5 @@ display: block; } -/*Koordinatenanzeige*/ -.gba-control-coordinates { - background-color: rgb(250,250,250); - background-color: rgba(250,250,250,0.8); - text-align: center; - box-shadow: 0 0 5px #bbb; - padding: 0 5px; - margin: 0; - color: #333; - font: 11px/1.500 'Open Sans', sans-serif; -} + diff --git a/src/js/controls/Coordinates.css b/src/js/controls/Coordinates.css new file mode 100644 index 0000000..4849fb6 --- /dev/null +++ b/src/js/controls/Coordinates.css @@ -0,0 +1,11 @@ +/*Koordinatenanzeige*/ +.gba-control-coordinates { + background-color: rgb(250,250,250); + background-color: rgba(250,250,250,0.8); + text-align: center; + box-shadow: 0 0 5px #bbb; + padding: 0 5px; + margin: 0; + color: #333; + font: 11px/1.500 'Open Sans', sans-serif; +} \ No newline at end of file diff --git a/src/js/controls/Coordinates.js b/src/js/controls/Coordinates.js index 36072b3..36fd6ea 100644 --- a/src/js/controls/Coordinates.js +++ b/src/js/controls/Coordinates.js @@ -5,7 +5,9 @@ import proj4 from 'proj4/dist/proj4'; import * as util from '../core/utilities'; import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera'; -class Coordinates extends Control { +import './Coordinates.css'; + +export class Coordinates extends Control { options = { position: 'bottomright', @@ -110,5 +112,3 @@ class Coordinates extends Control { } } - -export { Coordinates }; \ No newline at end of file diff --git a/src/js/controls/NorthArrow.css b/src/js/controls/NorthArrow.css new file mode 100644 index 0000000..1609a38 --- /dev/null +++ b/src/js/controls/NorthArrow.css @@ -0,0 +1,53 @@ +#inset { + position: absolute; + bottom: 0; + left: 0; + border: none; + width: 120px; + height: 120px; + z-index: 9999999; +} + +/* label */ +.label { + padding: 2px; + text-shadow: -1px -1px #FFF, 0 -1px #FFF, 1px -1px #FFF, -1px 0 #FFF, 1px 0 #FFF, -1px 1px #FFF, 0 1px #FFF, 1px 1px #FFF; + position: absolute; + pointer-events: none; +} + +.red-label { + color: #f00; + padding: 2px; + text-shadow: -1px -1px #FFF, 0 -1px #FFF, 1px -1px #FFF, -1px 0 #FFF, 1px 0 #FFF, -1px 1px #FFF, 0 1px #FFF, 1px 1px #FFF; + position: absolute; + pointer-events: none; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} + +.green-label { + color: #7cfc00; + padding: 2px; + text-shadow: -1px -1px #FFF, 0 -1px #FFF, 1px -1px #FFF, -1px 0 #FFF, 1px 0 #FFF, -1px 1px #FFF, 0 1px #FFF, 1px 1px #FFF; + position: absolute; + pointer-events: none; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} + +.blue-label { + color: #00bfff; + padding: 2px; + text-shadow: -1px -1px #FFF, 0 -1px #FFF, 1px -1px #FFF, -1px 0 #FFF, 1px 0 #FFF, -1px 1px #FFF, 0 1px #FFF, 1px 1px #FFF; + position: absolute; + pointer-events: none; + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; +} diff --git a/src/js/controls/NorthArrow.js b/src/js/controls/NorthArrow.js new file mode 100644 index 0000000..b864666 --- /dev/null +++ b/src/js/controls/NorthArrow.js @@ -0,0 +1,214 @@ +import { Control } from "./Control"; +import { Group } from 'three/src/objects/Group'; +import { Vector3 } from 'three/src/math/Vector3'; +import { ArrowHelper } from 'three/src/helpers/ArrowHelper'; +import * as util from '../core/utilities'; +import * as dom from '../core/domUtil'; +import * as domEvent from '../core/domEvent'; +import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer'; +import { Scene } from 'three/src/scenes/Scene'; +import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera'; + +// import { SphereGeometry } from 'three/src/geometries/SphereGeometry'; +// import { MeshLambertMaterial } from 'three/src/materials/MeshLambertMaterial'; +// import { Mesh } from 'three/src/objects/Mesh'; + +import './NorthArrow.css'; + +export class NortArrow extends Control { + + renderer; + _mainMap; + _scene; + _camera; + _center; + objectGroup; + labels = []; + options = { + position: 'bottomleft', + width: 120, + height: 120, + headLength: 1, + headWidth: 1, + }; + + constructor(options) { + super(options); + + this.objectGroup = new Group(); + this.objectGroup.visible = true; + util.setOptions(this, options); + } + + onAdd(map) { + this._mainMap = map; + let container = this._container = dom.createDom("div", { "id": "inset", "class": "gba-control-minimap" }); + this._container.style.width = this.options.width + 'px'; + this._container.style.height = this.options.height + 'px'; + domEvent.disableClickPropagation(this._container); + domEvent.on(this._container, 'mousewheel', domEvent.stopPropagation); + + let renderer = this.renderer = new WebGLRenderer({ alpha: true }); + renderer.setSize(120, 120); + // renderer.setSize(container.innerWidth, container.innerHeight); + container.appendChild(renderer.domElement); + + this._scene = new Scene(); + this._camera = new PerspectiveCamera(60, 1, 0.1, 1000); + this._camera.position.copy(map.camera.position); + this._camera.up = map.camera.up; + let center = this._center = new Vector3(0, 0, 0); + this._camera.lookAt(center); + + // this._camera = map.camera.clone(); + this._createArrow(this._scene); + this._buildLabels(); + return container; + } + + animate() { + + this._camera.position.copy(this._mainMap.camera.position); + this._camera.position.setLength(18); + // this._camera.lookAt(this.options.center); + // this._camera.lookAt(this._mainMap.target); + this._camera.lookAt(this._scene.position); + + this.renderer.render(this._scene, this._camera); + this._updateInsetLabelPositions(); + } + + _createArrow(app_scene) { + let from = new Vector3(0, 0, 0); + let headLength = 1;//this.options.headLength;//1; + let headWidth = this.options.headWidth;//1; + + // let xTo = new Vector3(1, 0, 0); + let xTo = new Vector3(from.x + 1, from.y, from.z); + let xDirection = xTo.clone().sub(from); + this.objectGroup.add(new ArrowHelper(xDirection, from, 6, 0xf00000, headLength, headWidth)); // Red = x + + // let yTo = new Vector3(0, 1, 0); + let yTo = new Vector3(from.x, from.y + 1, from.z); + let yDirection = yTo.clone().sub(from); + this.objectGroup.add(new ArrowHelper(yDirection, from, 6, 0x7cfc00, headLength, headWidth)); // Green = y + + //let zTo = new Vector3(0, 0, 1);//blue z + let zTo = new Vector3(from.x, from.y, from.z + 1); + let zDirection = zTo.clone().sub(from); + this.objectGroup.add(new ArrowHelper(zDirection, from, 6, 0x00bfff, headLength, headWidth)); //8 is the length, Blue = z; 20 and 10 are head length and width + + let opt = { r: 200, c: 0x38eeff, o: 0.8 }; + // this._queryMarker = new Mesh(new SphereGeometry(opt.r), + // new MeshLambertMaterial({ color: opt.c, opacity: opt.o, transparent: false })); + // this._queryMarker.visible = true; + // this._queryMarker.position.set(0, 0, -1); + + if (app_scene) { + app_scene.add(this.objectGroup); + } + } + + _buildLabels() { + + let f = [ + { a: ["X"], cl: "red-label", centroid: [[7, 0, 0]] }, + { a: ["Y"], cl: "green-label", centroid: [[0, 7, 0]] }, + { a: ["Z"], cl: "blue-label", centroid: [[0, 0, 7]] } + ]; + + var zFunc, getPointsFunc = function (f) { return f.centroid; }; + + + // create parent element for labels + var e = document.createElement("div"); + this._container.appendChild(e); + e.style.display = (this.objectGroup.visible) ? "block" : "none"; + let labelParentElement = e; //lable parent div for this layer + + for (let i = 0, l = f.length; i < l; i++) { + var labelInfo = f[i]; + // labelInfo.aElems = []; + // labelInfo.aObjs = []; + var text = labelInfo.a[0]; + if (text === null || text === "") continue; + + var classLabel = labelInfo.cl; + if (classLabel === undefined || classLabel === "") classLabel = "label"; + + //var horizontalShiftLabel = f.hs; + //if (horizontalShiftLabel === undefined || horizontalShiftLabel === "") horizontalShiftLabel = 0; + + let pts = getPointsFunc(labelInfo); + for (let j = 0, m = pts.length; j < m; j++) { + let pt = pts[j]; + + // create div element for label + var e = document.createElement("div"); + e.appendChild(document.createTextNode(text)); + e.className = classLabel;// "label"; + labelParentElement.appendChild(e); + + var pt1 = new Vector3(pt[0], pt[1], pt[2]); // top + + this.labels.push({ labelDiv: e, pt: pt1 }); + } + } + + + + } + + _updateInsetLabelPositions() { + var widthHalf = this.options.width / 2; + var heightHalf = this.options.height / 2; + // var autosize = appSettings.Options.label.autoSize; + // var camera = app.camera2; + let camera_pos = this._camera.position; + let target = new Vector3(0, 0, 0); + let c2t = target.sub(camera_pos); + let c2l = new Vector3(); + let v = new Vector3(); + + // make a list of [label index, distance to camera] + var idx_dist = []; + for (let i = 0, l = this.labels.length; i < l; i++) { + idx_dist.push([i, camera_pos.distanceTo(this.labels[i].pt)]); + } + + // sort label indexes in descending order of distances + idx_dist.sort(function (a, b) { + if (a[1] < b[1]) return 1; + if (a[1] > b[1]) return -1; + return 0; + }); + + let label, labelDiv, x, y, dist, fontSize; + // var minFontSize = appSettings.Options.label.minFontSize; + for (let i = 0, l = idx_dist.length; i < l; i++) { + label = this.labels[idx_dist[i][0]]; + labelDiv = label.labelDiv; + if (c2l.subVectors(label.pt, camera_pos).dot(c2t) > 0) { + // label is in front + // calculate label position + v.copy(label.pt); + v.project(this._camera); + + x = (v.x * widthHalf) + widthHalf; + y = -(v.y * heightHalf) + heightHalf; + + // set label position + labelDiv.style.display = "block"; + labelDiv.style.left = (x - (labelDiv.offsetWidth / 2)) + "px"; + labelDiv.style.top = (y - (labelDiv.offsetHeight / 2)) + "px"; + labelDiv.style.zIndex = i + 1; + + } + else { + // label is in back + labelDiv.style.display = "none"; + } + } + } + +} \ No newline at end of file diff --git a/src/js/core/Map.js b/src/js/core/Map.js index 65664fd..9eaf209 100644 --- a/src/js/core/Map.js +++ b/src/js/core/Map.js @@ -15,6 +15,7 @@ class Map extends OrbitControls { // call parent constructor of OrbitControls super(size, center, camera, scene, domElement); + this.camera = camera; this.container = container; //init the control corners diff --git a/src/js/core/browser.js b/src/js/core/browser.js new file mode 100644 index 0000000..54bb844 --- /dev/null +++ b/src/js/core/browser.js @@ -0,0 +1,6 @@ + + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). +export var pointer = !!(window.PointerEvent || msPointer); + diff --git a/src/js/core/domEvent.js b/src/js/core/domEvent.js index 9f96e38..e196958 100644 --- a/src/js/core/domEvent.js +++ b/src/js/core/domEvent.js @@ -1,3 +1,6 @@ +import * as browser from '../core/browser'; +import * as util from '../core/utilities'; + //static function var stampForFn = (function () { var lastId = 0, @@ -74,6 +77,54 @@ export function addListener(obj, type, fn, context) { // (HTMLElement, String, F // Alias to [`L.DomEvent.on`](#domevent-on) export { addListener as on }; + +function addOne(obj, type, fn, context) { + + + var id = type + util.stamp(fn) + (context ? '_' + util.stamp(context) : ''); + var eventsKey = '_gba_' + type + id; + + if (obj[eventsKey] && obj[eventsKey][id]) { return this; } + + var handler = function (e) { + return fn.call(context || obj, e || window.event); + }; + + var originalHandler = handler; + + if (browser.pointer && type.indexOf('touch') === 0) { + // Needs DomEvent.Pointer.js + addPointerListener(obj, type, handler, id); + + } else if (browser.touch && (type === 'dblclick') && !browserFiresNativeDblClick()) { + addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' || type === 'mousewheel') { + obj.addEventListener(mouseSubst[type] || type, handler, Browser.passiveEvents ? {passive: false} : false); + + } else if (type === 'mouseenter' || type === 'mouseleave') { + handler = function (e) { + e = e || window.event; + if (isExternalTarget(obj, e)) { + originalHandler(e); + } + }; + obj.addEventListener(mouseSubst[type], handler, false); + + } else { + obj.addEventListener(type, originalHandler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[eventsKey] = obj[eventsKey] || {}; + obj[eventsKey][id] = handler; +} + export function removeListener(obj, type, fn) { // (HTMLElement, String, Function) var id = stampForFn(fn); @@ -81,13 +132,7 @@ export function removeListener(obj, type, fn) { // (HTMLElement, String, Functi 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); @@ -137,6 +182,16 @@ export function stopPropagation(e) { return this; } + +// @function disableClickPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, +// `'mousedown'` and `'touchstart'` events (plus browser variants). +export function disableClickPropagation(el) { + addListener(el, 'mousedown touchstart dblclick', stopPropagation); + addOne(el, 'click', fakeStop); + return this; +} + export function preventDefault(e) { if (e.preventDefault) { @@ -149,9 +204,22 @@ export function preventDefault(e) { var skipEvents = {}; +export function fakeStop(e) { + // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) + skipEvents[e.type] = true; +} 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; -} \ No newline at end of file +} + + +// @function stop(ev: DOMEvent): this +// // Does `stopPropagation` and `preventDefault` at the same time. +// export function stop(e) { +// preventDefault(e); +// stopPropagation(e); +// return this; +// } \ No newline at end of file diff --git a/src/js/core/utilities.js b/src/js/core/utilities.js index 8d32312..86517ac 100644 --- a/src/js/core/utilities.js +++ b/src/js/core/utilities.js @@ -155,3 +155,14 @@ export function stamp (obj){ return obj[key]; } + +export function hasTouch () { + var phantomjs = navigator.userAgent.toLowerCase().indexOf('phantom') !== -1; + + var isTouchDevice = phantomjs + || 'ontouchstart' in window + || (window.DocumentTouch && document instanceof window.DocumentTouch) + || ("onpointerdown" in document && navigator.maxTouchPoints > 0) + || window.navigator.msMaxTouchPoints; + return isTouchDevice; +} diff --git a/src/js/main.js b/src/js/main.js index 66259db..7c87c7e 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -8,6 +8,7 @@ import { TinLayer } from './layer/TinLayer'; import { Map } from './core/Map'; import * as domEvent from './core/domEvent'; import { Coordinates } from './controls/Coordinates'; +import { NortArrow } from './controls/NorthArrow'; import { Mesh } from 'three/src/objects/Mesh'; import { SphereGeometry } from 'three/src/geometries/SphereGeometry'; import { MeshLambertMaterial } from 'three/src/materials/MeshLambertMaterial'; @@ -87,7 +88,7 @@ class Application { let x = { min: 3955850, max: 4527300, avg: 4282010 }; let y = { min: 2183600, max: 2502700, avg: 2302070 }; let z = { min: -60066, max: 3574.94, avg: -13616.3 }; - const center = new Vector3(x.avg, y.avg, z.avg); + const center = new Vector3(x.avg, y.avg, 1000); const size = Math.max(x.max - x.min, y.max - y.min, z.max - z.min); const camDirection = new Vector3(-0.5, -Math.SQRT1_2, 0.5); const camOffset = camDirection.multiplyScalar(size * 2); @@ -98,16 +99,18 @@ class Application { this.camera.updateProjectionMatrix(); // create map - this.map = new Map(size, center, this.camera, this.scene, this.renderer.domElement, this.container); - // this.map.target = center; + this.map = new Map(size, center, this.camera, this.scene, this.renderer.domElement, this.container); // this.map.minDistance = size*0.75; // this.map.maxDistance = size*15; + + //add map controls: let coordinates = new Coordinates({ camera: this.camera, crs: "EPSG:3034" }).addTo(this.map); // coordinates.addListener('onPoint', (vector) => { // this.queryMarker.position.set(vector.x, vector.y, vector.z); - // this.queryMarker.updateMatrixWorld(); + // // this.queryMarker.updateMatrixWorld(); // this.animate(); // }, this); + this.northArrow = new NortArrow({ headLength: 1, headWidth: 1 }).addTo(this.map); let dxf134Layer = new TinLayer({ geomId: 134, q: true, type: "3dface", name: "South Alpine Superunit", description: "test", color: "907A5C" @@ -225,13 +228,9 @@ class Application { animate() { this.renderer.render(this.scene, this.camera); + this.northArrow.animate(); } } var container = document.getElementById("webgl"); new Application(container); -// app.add(new BoxLayer({ -// width: 10, -// height: 10, -// depth: 10 -// })); \ No newline at end of file