57 Commits
Author SHA1 Message Date
Sebastian Kupke f87b19140b Removed header from test images. 2022-05-05 12:31:58 +02:00
Sebastian Kupke 63ebbba42b Added second resolution test. 2022-05-05 12:29:27 +02:00
Sebastian Kupke 72c6abf031 Resolution tests. 2022-05-04 15:18:11 +02:00
Sebastian Kupke 9db52a004a Added scatter resolution test. 2022-05-04 13:27:31 +02:00
Sebastian Kupke 527fcb993c Removed videos from resolution test. 2022-05-04 09:53:55 +02:00
Sebastian Kupke c41209d0fc Added description to resolution. 2022-05-04 09:10:01 +02:00
Sebastian Kupke 7e9f0159e8 Added resolution test. 2022-05-03 16:50:19 +02:00
Uwe Oestermeier 074fb67906 Fixed problem with outdated loader and error handler.s 2022-05-02 16:41:22 +02:00
Sebastian Kupke 51b0ef4b1b Added VSCode workspace file. 2022-05-02 11:13:03 +02:00
Sebastian Kupke 1efe94ef97 Bump version number. 2022-04-29 10:30:05 +02:00
Sebastian Kupke 7f068c5d94 Fixed resize fullscreen bug. 2022-04-29 10:28:59 +02:00
Uwe Oestermeier eff934e8b5 Moved the addVelocity method after the transform to allow a modification of the delta within the transform method. To make this destructive side effect more explicit transform could return a modified value. But that could effect several projects. 2021-04-28 17:02:54 +02:00
Sebastian Kupke 266a770c8e Merge branch 'master' into obersalzberg
# Conflicts:
#	dist/iwmlib.js
#	dist/iwmlib.pixi.js
2021-03-15 17:02:08 +01:00
Uwe Oestermeier a5f94e0a5f Reverted changes from commit fa0256d782 and moved addVelocity from _move back to old locations. 2021-03-15 15:39:07 +01:00
Uwe Oestermeier fff7a4f685 Fixed import order. 2021-03-01 08:58:44 +01:00
Sebastian Kupke d0ae8893cc Version 1.1.1. 2021-02-24 16:15:20 +01:00
Sebastian Kupke 1b4eee6498 Merge branch 'master' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib 2021-02-24 16:14:26 +01:00
Sebastian Kupke ac779af339 Popup close buttons can take a URL. 2021-02-24 16:13:50 +01:00
Severin Opel 1316a727f3 Fixed offset error. 2021-02-01 13:54:40 +01:00
Severin Opel be950ec8c1 Tried fix scatter. 2021-02-01 13:40:57 +01:00
Severin Opel ca5a39d661 Changed wrapper and events to allow other EventListeners. 2021-01-26 08:01:51 +01:00
Severin Opel fa0256d782 Moved addVelocity to _move. 2021-01-05 16:25:55 +01:00
Severin Opel 114c217ffa Improved logging and adjusted card's click handler
+ Logging now also works when ipcRenderer is appended to the window element.
+ Card's callback replacement method was changed from click to pointerdown.
2020-09-15 11:36:59 +02:00
Severin Opel 28cfdc180c Fixt highlight order on creation. 2020-09-01 13:38:22 +02:00
Uwe Oestermeier 116058ba77 Fixed a problem with clicking transformed rects in card wrappers. 2020-08-27 07:38:35 +02:00
Uwe Oestermeier 4211548b8b Removed console logs. 2020-08-04 18:13:24 +02:00
Uwe Oestermeier 12b17fd7af Fixed call of popup menu commands with parameters.s 2020-08-04 18:07:48 +02:00
Uwe Oestermeier 47b178fbc1 Added event parameter to PopupMenu perform. 2020-08-04 17:35:15 +02:00
Severin Opel 6ab63eb32c Now calling 'this' to allow overloading of methods. 2020-04-21 12:43:55 +02:00
Severin Opel 6445ab3f57 CardLoader.load now checks for ready state. 2020-04-19 13:30:35 +02:00
Severin Opel 129672eb0f Added options object to toggle function. 2020-04-14 12:14:40 +02:00
Uwe Oestermeier 70031d3bfa Removed comment since border case appeared in Edge. 2020-02-25 15:19:01 +01:00
Uwe Oestermeier 9db5723746 Updated dist 2020-02-17 16:20:16 +01:00
Uwe Oestermeier d4ab5c7e9c Merge branch 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib
* 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib:
  Changed scale
  Updated to PixiJS 5.2.1.
2020-02-17 16:19:33 +01:00
Uwe Oestermeier b068fd176f Added lineWidth support. 2020-02-17 16:18:55 +01:00
Uwe Oestermeier f70968fe01 Changed scale 2020-02-07 20:08:44 +01:00
Sebastian Kupke 1c70433b86 Updated to PixiJS 5.2.1. 2020-02-04 16:48:36 +01:00
Uwe Oestermeier 54b685ab42 Changed calling order 2020-02-03 10:06:27 +01:00
Uwe Oestermeier e6f6f6b185 Changed calling order 2020-02-03 10:06:01 +01:00
Uwe Oestermeier 7bb3b696f7 Created new build. 2020-01-31 10:26:41 +01:00
Sebastian Kupke 6cdf5310ba Added graphics alpha workaround. 2020-01-30 15:41:31 +01:00
Sebastian Kupke 1967b791e0 Updated dependencies. 2020-01-22 13:50:40 +01:00
Sebastian Kupke ebb4b9154e Updated pixi-compressed-textures. 2020-01-20 11:33:53 +01:00
Sebastian Kupke b32132c71f Removed stopEvents from RigidContainer. 2020-01-10 14:18:28 +01:00
Sebastian Kupke f398fe4824 Fixed reference bug. 2020-01-09 15:14:27 +01:00
Severin Opel d5b87f23cf Updated generated content (Docs & dist). 2019-12-12 19:04:11 +01:00
Severin Opel 7f2fc93fa3 Reworked overlay itself and doctest. 2019-12-12 19:02:58 +01:00
Severin Opel 32e913c629 Fixed wrongly assigned parameters 2019-12-12 17:23:39 +01:00
Severin Opel 87c966062c Map's cover state now updates every time cover is requested. 2019-12-12 17:01:51 +01:00
Severin Opel d7f867f1a6 Added maps graphics. 2019-12-11 17:30:11 +01:00
Severin Opel e188f3474f Reverted index.css to previous version. 2019-12-11 16:52:23 +01:00
Severin Opel 1e80845aa6 Fixed cover bug. Renamed mapdata to mapprojection. 2019-12-11 16:45:26 +01:00
Severin Opel a85569e54d commited for presentation. 2019-12-11 15:29:59 +01:00
Severin Opel 65fac2f406 Renamed 'MapView' to 'MapViewport'. Added documentation to the maps module. 2019-12-09 18:15:28 +01:00
Severin Opel 60e28f8fe5 Improved doctest css. 2019-12-09 14:29:26 +01:00
Severin Opel 792892cb82 Removed PIXIUtils from geographics. 2019-12-09 14:27:32 +01:00
Severin Opel 9159073483 Fixed GeoGraphics doctest. 2019-12-05 17:06:50 +01:00
181 changed files with 178002 additions and 49442 deletions
+5 -1
View File
@@ -77,6 +77,10 @@ typings/
.fusebox/
# own
*.code-workspace
.history/
.vscode/
# ignore generated contents-
/doc/out/*
**/thumbnails
**/thumbnail.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

+128 -10
View File
@@ -1,15 +1,47 @@
:root {
--white: rgb(250, 250, 250);
--light-gray: rgb(219, 219, 219);
--gray: rgb(66, 66, 66);
--black: rgb(20, 20, 20);
--dark-background: rgb(50, 50, 50);
--border-radius: 5px;
}
html {
padding: 0;
font-size: 16px;
background: white;
color: #000;
background: var(--white);
color: var(--black);
font-family: 'Open Sans', Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
}
.dark-mode nav{
border-color: var(--white);
}
nav {
margin-top: 50px;
min-width: 30%;
border-radius: var(--border-radius);
display: inline-block;
padding: 20px 30px;
/* background-color: var(--light-gray); */
border: 1px solid var(--black);
}
nav > h3 {
margin: 0;
}
nav > ol a {
font-weight: bold;
}
html.dark-mode {
background: rgb(51, 51, 51);
background: var(--dark-background);
color: #ccc;
}
@@ -22,16 +54,102 @@ html.dark-mode {
color: white;
}
.dark-mode a{
color: #569CD6;
a {
color: #569cd6;
font-weight: bold;
}
.dark-mode a:hover{
.dark-mode a:hover {
color: white;
}
a{
.dark-mode button {
border: 1px solid rgb(211, 211, 211);
color: rgb(211, 211, 211);
background: linear-gradient(to top, rgb(53, 53, 53), rgb(73, 73, 73));
}
button {
background-color: var(--white);
border: 1px solid var(--gray);
color: var(--gray);
padding: 10px 20px;
border-radius: 5px;
transition: transform 0.2s;
}
label {
display: flex;
align-items: center;
user-select: none;
cursor: pointer;
}
input[type='checkbox'] {
display: none;
}
input[type='checkbox'] + .checkbox {
position: relative;
margin-right: 10px;
width: 32px;
height: 32px;
}
.controls {
display: flex;
}
.controls > * {
margin-right: 1em;
}
.dark-mode .checkbox {
background-color: var(--gray);
border: 1px solid var(--white);
}
.checkbox {
display: inline-block;
border-radius: 3px;
background-color: var(--white);
border: 1px solid var(--black);
}
.checkbox:after {
content: '';
position: absolute;
}
input[type='checkbox']:checked + .checkbox {
background-color: rgb(174, 238, 78);
}
input[type='checkbox']:checked + .checkbox:after {
left: 11px;
top: 6px;
width: 5px;
height: 12px;
border: solid var(--white);
border-width: 0 4px 4px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
button:active {
transform: scale(0.98);
}
button:hover {
filter: brightness(0.95);
}
button:focus {
box-shadow: 0 0 3px 1px #569cd6;
outline: none;
}
a {
text-decoration: none;
}
@@ -53,8 +171,8 @@ a{
.doctest {
margin: 0;
padding-top: 50px;
padding-bottom: 50px;
padding-top: 20px;
padding-bottom: 20px;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.5);
}
.collapsed .doctest {
@@ -71,7 +189,7 @@ a{
margin-bottom: 1em;
}
.doctest-collapsible-toggle {
.doctest-titlebar {
display: flex;
align-items: center;
color: whitesmoke;
+20141 -22549
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+141 -130
View File
@@ -9,9 +9,9 @@
static implementationError(klass) {
let interfaceKeys = Reflect.ownKeys(this.prototype);
let classKeys = Reflect.ownKeys(klass.prototype);
Reflect.ownKeys(klass.prototype);
for (let key of interfaceKeys) {
let interfaceDesc = this.prototype[key];
this.prototype[key];
let classDesc = klass.prototype[key];
if (typeof classDesc == 'undefined') return 'Missing ' + key
}
@@ -239,18 +239,29 @@
let container = document.createElement('div');
container.className = 'doctest-wrapper';
let titleParent = container;
if (doctest.hasAttribute('data-collapsible')) {
let collapsibleToggle = document.createElement('div');
if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
let titlebar = document.createElement('div');
titlebar.className = 'doctest-titlebar';
titlebar.style = 'min-height: 10px;';
container.appendChild(titlebar);
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6');
title.innerText = doctest.getAttribute('data-title');
title.className = 'doctest-section-title';
titlebar.appendChild(title);
}
if (doctest.hasAttribute('data-collapsible')) {
let icon = document.createElement('i');
icon.className = 'material-icons';
collapsibleToggle.appendChild(icon);
titlebar.classList.add('doctest-collapsible-toggle');
collapsibleToggle.className = 'doctest-collapsible-toggle';
collapsibleToggle.style = 'min-height: 10px;';
titleParent = collapsibleToggle;
container.appendChild(collapsibleToggle);
if (titlebar.childNodes.length > 0) {
titlebar.insertBefore(icon, titlebar.childNodes[0]);
} else {
titlebar.appendChild(icon);
}
const collapsedClass = 'collapsed';
@@ -269,13 +280,8 @@
}
setToggleMode(doctest.hasAttribute('data-collapsed'));
collapsibleToggle.addEventListener('click', determineToggleMode);
titlebar.addEventListener('click', determineToggleMode);
}
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6');
title.innerText = doctest.getAttribute('data-title');
title.className = 'doctest-section-title';
titleParent.appendChild(title);
}
let pre = document.createElement('pre');
@@ -423,9 +429,12 @@
window.addEventListener(
'error',
event => {
// if (typeof(event.error) == 'undefined') {
// console.info("Catched undefined error", event)
// }
if (typeof event.error == 'undefined') {
// This sometimes happens in Edge. Since we have no error
// position, we cannot do much beside an info log.
console.info('Catched undefined error', event);
return
}
this.appendError(event.error, event.filename);
},
true
@@ -455,7 +464,10 @@
class Events {
static stop(event) {
event.preventDefault();
event.stopPropagation();
// I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
}
static extractPoint(event) {
@@ -608,19 +620,6 @@
static toLine(event) {
return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}`
let result = event.type;
let selector = this.selector(event.target);
result += ' selector: ' + selector;
if (event.target != document.querySelector(selector)) console.log('Cannot resolve', selector);
let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY'];
for (let key of keys) {
try {
result += ' ' + key + ':' + event[key];
} catch (e) {
console.log('Invalid key: ' + key);
}
}
return result
}
static compareExtractedWithSimulated() {
@@ -1086,9 +1085,18 @@
// Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x);
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y);
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
let dx = 0;
let dy = 0;
if (p.x < r.x) dx = p.x - r.x;
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width);
if (p.y < r.y) dy = p.y - r.y;
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height);
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
}
static fromPageToNode(element, p) {
@@ -1716,7 +1724,7 @@
};
try {
ipc = require('electron').ipcRenderer;
ipc = window.ipcRenderer || require('electron').ipcRenderer;
logHandlers.log = message => ipc.send('log', message);
logHandlers.warn = message => ipc.send('warn', message);
logHandlers.error = message => ipc.send('error', message);
@@ -2614,8 +2622,8 @@
if (Events.isCaptured(event)) {
return false
}
let captured = this.target.capture(event);
return captured
return this.target.capture ? this.target.capture(event) : false
}
getPosition(event) {
@@ -2640,25 +2648,6 @@
result[id] = this.getPosition(event);
break
}
// case 'TouchEvent':
// // Needs to be observed: Perhaps changedTouches are all we need. If so
// // we can remove the touchEventKey default parameter
// if (touchEventKey == 'all') {
// for(let t of event.targetTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// else {
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// break
default:
break
}
return result
}
@@ -3159,8 +3148,7 @@
*/
class ScatterEvent extends BaseEvent {
constructor(
target,
{ translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
target, { translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
) {
super('scatterTransformed', { target: target });
this.translate = translate;
@@ -3232,6 +3220,9 @@
let t = performance.now();
let dt = t - this.lastframe;
this.lastframe = t;
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1;
if (dt > 0) {
// Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the
@@ -3239,13 +3230,14 @@
let velocity = {
t: t,
dt: dt,
dx: delta.x / delta.number,
dy: delta.y / delta.number
dx: delta.x / number,
dy: delta.y / number
};
this.velocities.push(velocity);
while (this.velocities.length > buffer) {
this.velocities.shift();
}
}
}
@@ -3493,7 +3485,7 @@
let delta = interaction.delta();
if (delta != null) {
this.addVelocity(delta);
let rotate = delta.rotate;
let zoom = delta.zoom;
if (this.maxRotation != null) {
@@ -3514,6 +3506,8 @@
}
this.transform(delta, zoom, rotate, delta.about);
this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
if (zoom != 1) this.interactionAnchor = delta.about;
}
}
@@ -3581,7 +3575,7 @@
// UO: since keepOnStage is called in nextVelocity we need to
// ensure a return value
if (!stagePolygon) return { x: 0, y: 0 }
let polygon = this.polygon;
this.polygon;
let bounced = this.bouncing();
if (bounced) {
let stage = this.containerBounds;
@@ -3693,6 +3687,7 @@
}
_move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0;
this.y += this.movableX ? delta.y : 0;
}
@@ -3730,14 +3725,14 @@
let newOrigin = Points$1.arc(anchor, beta + rotate, distance * thresholdedZoom);
let extra = Points$1.subtract(newOrigin, origin);
let offset = Points$1.subtract(anchor, origin);
this._move(offset);
const anchorOffset = Points$1.subtract(anchor, origin);
// this._move(offset)
this.scale = newScale;
this.rotation += rotate;
offset = Points$1.negate(offset);
let offset = Points$1.negate(anchorOffset);
offset = Points$1.add(offset, extra);
offset = Points$1.add(offset, translate);
this._move(offset);
this._move(Points$1.add(anchorOffset, offset));
delta.x += extra.x;
delta.y += extra.y;
@@ -4021,8 +4016,7 @@
* @param {DOM node} debugCanvas - Shows debug infos about touches if not null
*/
constructor(
element,
{
element, {
stopEvents = 'auto',
claimEvents = true,
useCapture = true,
@@ -4186,8 +4180,7 @@
class DOMScatter$1 extends AbstractScatter {
constructor(
element,
container,
{
container, {
startScale = 1.0,
minScale = 0.1,
maxScale = 1.0,
@@ -4507,7 +4500,8 @@
}
this._x = x;
this._y = y;
TweenLite.set(this.element, { x: x, y: y });
// this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y });
}
resizeAfterTransform(zoom) {
@@ -4714,16 +4708,24 @@
let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect();
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points$1.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
});
let topLeft = Points$1.fromPageToNode(element, rect);
let width = Math.abs(bottomRight.x - topLeft.x);
let height = Math.abs(bottomRight.y - topLeft.y);
let center = Points$1.fromPageToNode(element, {
x: rect.x + rect.width / 2,
y: rect.y + rect.height / 2
x: rect.x + width / 2,
y: rect.y + height / 2
});
return {
x: topLeft.x,
y: topLeft.y,
width: rect.width,
height: rect.height,
width,
height,
center,
link
}
@@ -4751,15 +4753,20 @@
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) {
// if (this.isSVGNode(node)) {
// if (this.triggerSVGClicks) {
// let click = new Event('click')
// node.dispatchEvent(click)
// }
// return
// }
// node.click()
let click = new Event('click');
click.clientX = event.clientX;
click.clientY = event.clientY;
node.dispatchEvent(click);
}
return
}
node.click();
}
nodeTapped(node, event) {
console.log('nodeTapped', node, this.isClickable(node));
@@ -4946,13 +4953,15 @@
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', this.src, false);
xhr.onload = e => {
xhr.onreadystatechange = e => {
if (xhr.readyState == 4) {
domNode.innerHTML = this.prepare(xhr.response);
this.addedNode = domNode.firstElementChild;
let { width, height } = this.size(this.addedNode);
if (width) this.wantedWidth = width || this.wantedWidth;
if (height) this.wantedHeight = height || this.wantedHeight;
resolve(this);
}
};
xhr.onerror = e => {
reject(this);
@@ -5753,8 +5762,6 @@
let bottom = parseFloat(this.element.style.bottom);
this.element.style.bottom = bottom - delta.y + 'px';
break
default:
break
}
//console.log("onResize", this.onResize)
if (this.onResize) {
@@ -6177,8 +6184,6 @@
x = bbRight;
if (!this.useEventPosWithBoundingBox) y = (bbTop + bbBottom) / 2;
break
default:
break
}
}
@@ -6234,8 +6239,6 @@
x += this.notchSize * 2;
x += this.posOffset;
break
default:
break
}
this.placeOrigin(x, y);
}
@@ -6254,7 +6257,7 @@
/** Configuration object. Return default styles as CSS values.
*/
defaultStyle() {
let padding = this.padding;
this.padding;
let style = {
maxWidth: this.maxWidth + 'px',
zIndex: this.zIndex,
@@ -6533,7 +6536,7 @@
* @memberof Popup
*/
static targetCenter(event) {
let target = event.target;
event.target;
let x = event.pageX;
let y = event.pageY;
let rect = Popup$1.targetRect(event);
@@ -6626,10 +6629,10 @@
Elements$1.addClass(item, 'popupMenuItem');
this.items[key] = item;
item.onclick = event => {
this.perform(key);
this.perform(event, key);
};
item.ontap = event => {
this.perform(key);
this.perform(event, key);
};
item.onmouseover = event => {
this.over(event, key);
@@ -6648,16 +6651,22 @@
}
/** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key.
*/
perform(key) {
perform(event, key) {
let func = this.commands[key];
if (this.autoClose) {
this.close();
}
setTimeout(() => {
func.call();
}, 20);
setTimeout(
(event, key) => {
func(event, key);
},
20,
event,
key
);
}
/** Update the menu item denoted by key.
@@ -7573,7 +7582,7 @@
reorderArguments(params) {
// first parameter
//--------------------
const element = params[0];
params[0];
// other parameter
//--------------------
@@ -7983,10 +7992,13 @@
* @class Highlight
* @extends {Object}
*/
const SCALE = 1.5;
class Highlight extends Object {
static disableAnimations() {
_HighlightEnabled = false;
let expanded = document.querySelectorAll('.' + Highlight.expandedClass);
let expanded = document.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) {
this.shrink(obj);
}
@@ -7997,11 +8009,11 @@
}
static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass);
let expanded = svgRoot.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 });
obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass);
obj.classList.remove(this.expandedClass);
}
let defs = svgRoot.querySelector('defs');
while (defs.firstChild) {
@@ -8031,12 +8043,12 @@
}
}
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) {
static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return
//console.log("expand")
obj.classList.add('zooming');
TweenLite.to(obj, duration, {
scale: scale,
scale,
onUpdate: () => {
let scale = obj._gsTransform.scaleX;
obj.setAttribute('stroke-width', stroke / scale);
@@ -8044,7 +8056,7 @@
onComplete: () => {
console.log('expand complete');
obj.classList.remove('zooming');
obj.classList.add(Highlight.expandedClass);
obj.classList.add(this.expandedClass);
obj.setAttribute('stroke-width', stroke / scale);
if (onComplete) onComplete();
}
@@ -8064,7 +8076,7 @@
onComplete: () => {
//console.log("shrink complete")
obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass);
obj.classList.remove(this.expandedClass);
obj.setAttribute('stroke-width', stroke);
}
});
@@ -8103,7 +8115,7 @@
let maskImageId = 'maskImage' + id;
let maskImage = svgRoot.getElementById(maskImageId);
if (circle.classList.contains(Highlight.expandedClass)) {
if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke });
this.shrink(maskImage, { stroke });
@@ -8126,7 +8138,7 @@
this.shrink(m, { stroke });
}
Highlight._createSVGMask(svgRoot, image, id);
this._createSVGMask(svgRoot, image, id);
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -8136,8 +8148,8 @@
return false
}
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) {
static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (this._isExpanded(target)) {
console.log('Target is already expanded!');
return
} else {
@@ -8161,11 +8173,11 @@
let image = svgRoot.querySelector('image');
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, {
let [mask, maskImage] = this._getSVGMask(target, {
svgRoot,
image
});
let center = Highlight._calculateCenterRelativeTo(target, image);
let center = this._calculateCenterRelativeTo(target, image);
console.log('_calculateCenterRelativeTo', center);
TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`
@@ -8177,16 +8189,16 @@
onComplete: onExpanded
});
target.classList.add(Highlight.expandedClass);
target.classList.add(this.expandedClass);
}
}
static toggleHighlight(node) {
console.log('toggleHighlight', Highlight._isExpanded(node));
if (Highlight._isExpanded(node)) {
Highlight.closeHighlight(node);
static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', this._isExpanded(node));
if (this._isExpanded(node)) {
this.closeHighlight(node, options);
} else {
Highlight.openHighlight(node);
this.openHighlight(node, options);
}
}
@@ -8209,7 +8221,7 @@
let maskImage = svgRoot.getElementById(maskImageId);
if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, {
[mask, maskImage] = this._createSVGMask(circle, {
svgRoot,
image,
id
@@ -8240,7 +8252,7 @@
let svg = 'http://www.w3.org/2000/svg';
let xlink = 'http://www.w3.org/1999/xlink';
let svgGroup = element.parentNode;
element.parentNode;
let src = image.getAttributeNS(xlink, 'href');
@@ -8275,12 +8287,11 @@
maskImage.setAttribute('width', width);
maskImage.setAttribute('height', height);
maskImage.setAttribute('class', 'addedImage');
svgGroup.insertBefore(maskImage, element); // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 });
maskImage.style.mask = 'url(#' + maskId + ')';
}
svgGroup.appendChild(maskImage);
element.insertAdjacentElement('beforebegin', maskImage); // image.nextSibling)
// svgGroup.appendChild(element)
return [mask, maskImage]
@@ -8298,14 +8309,14 @@
}
static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass)
return target.classList.contains(this.expandedClass)
}
static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass);
target.classList.remove(this.expandedClass);
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target);
console.log('Close Highlight', maskImage);
let [mask, maskImage] = this._getSVGMask(target);
// console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, {
scale: 1
});
@@ -8315,7 +8326,7 @@
if (!_HighlightEnabled) return
event.stopPropagation();
Highlight.animateCircle(event.target);
this.animateCircle(event.target);
return false
}
@@ -8481,7 +8492,7 @@
*/
static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') {
element.addEventListener('click', event => {
element.addEventListener('pointerdown', event => {
event.preventDefault();
});
}
@@ -9068,7 +9079,7 @@
let cx = target.getAttribute('cx');
let cy = target.getAttribute('cy');
let r = target.getAttribute('r');
let radius = r.endsWith('%') ? (parseFloat(r) / 100) * width : parseFloat(r);
r.endsWith('%') ? (parseFloat(r) / 100) * width : parseFloat(r);
let x = cx.endsWith('%') ? (parseFloat(cx) / 100) * width : cx;
let y = cy.endsWith('%') ? (parseFloat(cy) / 100) * height : cx;
@@ -11291,4 +11302,4 @@
window.Highlight = Highlight;
window.Theme = Theme;
}());
})();
+1309 -627
View File
File diff suppressed because it is too large Load Diff
+19 -2
View File
@@ -36,7 +36,21 @@
"./lib/pixi/switch.js",
"./lib/pixi/theme.js",
"./lib/pixi/tooltip.js",
"./lib/pixi/volatile.js"
"./lib/pixi/volatile.js",
"./lib/pixi/maps/mapapp.js",
"./lib/pixi/maps/mapviewport.js",
"./lib/pixi/maps/geographics.js",
"./lib/pixi/maps/geojson.js",
"./lib/pixi/maps/geolayer.js",
"./lib/pixi/maps/mapprojection.js",
"./lib/pixi/maps/maplist.js",
"./lib/pixi/maps/overlay.js",
"./lib/pixi/maps/scatter.js",
"./lib/pixi/maps/utils.js",
"./lib/pixi/maps/map.js",
"./lib/pixi/maps/projections/projection.js",
"./lib/pixi/maps/projections/mercator.js",
"./lib/pixi/maps/projections/robinson.js"
],
"exclude": [],
"includePattern": ".+\\.js(doc|x)?$",
@@ -45,7 +59,10 @@
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"]
"dictionaries": [
"jsdoc",
"closure"
]
},
"templates": {
"applicationName": "iwmlib",
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1793 -202
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1788 -197
View File
File diff suppressed because it is too large Load Diff
+1788 -197
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1788 -197
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1787 -206
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1787 -206
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1791 -200
View File
File diff suppressed because it is too large Load Diff
+1792 -208
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1788 -213
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1792 -199
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1788 -197
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+1792 -201
View File
File diff suppressed because it is too large Load Diff
+1789 -198
View File
File diff suppressed because it is too large Load Diff
+2544 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1796 -207
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1808 -200
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+1784 -197
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
(function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('.item[data-name*="'+n+'"]:eq(0)');if(s.length){s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}});
(function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('a[href*="'+n+'"]:eq(0)').closest(".item");if(s.length){if(s.parents(".children").length){s.addClass("current");s.find("li.item").addClass("notCurrent");s=s.parents("ul.list>li.item")}s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}});
File diff suppressed because one or more lines are too long
+1784 -197
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
+1 -1
View File
@@ -176,7 +176,7 @@ export default class Card {
*/
static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') {
element.addEventListener('click', event => {
element.addEventListener('pointerdown', event => {
event.preventDefault()
})
}
+29 -27
View File
@@ -27,10 +27,13 @@ function round(value) {
* @class Highlight
* @extends {Object}
*/
const SCALE = 1.5
export default class Highlight extends Object {
static disableAnimations() {
_HighlightEnabled = false
let expanded = document.querySelectorAll('.' + Highlight.expandedClass)
let expanded = document.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) {
this.shrink(obj)
}
@@ -41,11 +44,11 @@ export default class Highlight extends Object {
}
static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass)
let expanded = svgRoot.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 })
obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass)
obj.classList.remove(this.expandedClass)
}
let defs = svgRoot.querySelector('defs')
while (defs.firstChild) {
@@ -75,12 +78,12 @@ export default class Highlight extends Object {
}
}
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) {
static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return
//console.log("expand")
obj.classList.add('zooming')
TweenLite.to(obj, duration, {
scale: scale,
scale,
onUpdate: () => {
let scale = obj._gsTransform.scaleX
obj.setAttribute('stroke-width', stroke / scale)
@@ -88,7 +91,7 @@ export default class Highlight extends Object {
onComplete: () => {
console.log('expand complete')
obj.classList.remove('zooming')
obj.classList.add(Highlight.expandedClass)
obj.classList.add(this.expandedClass)
obj.setAttribute('stroke-width', stroke / scale)
if (onComplete) onComplete()
}
@@ -108,7 +111,7 @@ export default class Highlight extends Object {
onComplete: () => {
//console.log("shrink complete")
obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass)
obj.classList.remove(this.expandedClass)
obj.setAttribute('stroke-width', stroke)
}
})
@@ -147,7 +150,7 @@ export default class Highlight extends Object {
let maskImageId = 'maskImage' + id
let maskImage = svgRoot.getElementById(maskImageId)
if (circle.classList.contains(Highlight.expandedClass)) {
if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke })
this.shrink(maskImage, { stroke })
@@ -170,7 +173,7 @@ export default class Highlight extends Object {
this.shrink(m, { stroke })
}
Highlight._createSVGMask(svgRoot, image, id)
this._createSVGMask(svgRoot, image, id)
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -180,8 +183,8 @@ export default class Highlight extends Object {
return false
}
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) {
static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (this._isExpanded(target)) {
console.log('Target is already expanded!')
return
} else {
@@ -205,11 +208,11 @@ export default class Highlight extends Object {
let image = svgRoot.querySelector('image')
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, {
let [mask, maskImage] = this._getSVGMask(target, {
svgRoot,
image
})
let center = Highlight._calculateCenterRelativeTo(target, image)
let center = this._calculateCenterRelativeTo(target, image)
console.log('_calculateCenterRelativeTo', center)
TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`
@@ -221,16 +224,16 @@ export default class Highlight extends Object {
onComplete: onExpanded
})
target.classList.add(Highlight.expandedClass)
target.classList.add(this.expandedClass)
}
}
static toggleHighlight(node) {
console.log('toggleHighlight', Highlight._isExpanded(node))
if (Highlight._isExpanded(node)) {
Highlight.closeHighlight(node)
static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', this._isExpanded(node))
if (this._isExpanded(node)) {
this.closeHighlight(node, options)
} else {
Highlight.openHighlight(node)
this.openHighlight(node, options)
}
}
@@ -253,7 +256,7 @@ export default class Highlight extends Object {
let maskImage = svgRoot.getElementById(maskImageId)
if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, {
[mask, maskImage] = this._createSVGMask(circle, {
svgRoot,
image,
id
@@ -319,12 +322,11 @@ export default class Highlight extends Object {
maskImage.setAttribute('width', width)
maskImage.setAttribute('height', height)
maskImage.setAttribute('class', 'addedImage')
svgGroup.insertBefore(maskImage, element) // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 })
maskImage.style.mask = 'url(#' + maskId + ')'
}
svgGroup.appendChild(maskImage)
element.insertAdjacentElement('beforebegin', maskImage) // image.nextSibling)
// svgGroup.appendChild(element)
return [mask, maskImage]
@@ -342,14 +344,14 @@ export default class Highlight extends Object {
}
static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass)
return target.classList.contains(this.expandedClass)
}
static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass)
target.classList.remove(this.expandedClass)
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target)
console.log('Close Highlight', maskImage)
let [mask, maskImage] = this._getSVGMask(target)
// console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, {
scale: 1
})
@@ -359,7 +361,7 @@ export default class Highlight extends Object {
if (!_HighlightEnabled) return
event.stopPropagation()
Highlight.animateCircle(event.target)
this.animateCircle(event.target)
return false
}
+1 -1
View File
@@ -9,8 +9,8 @@
<link rel="stylesheet" href="../../css/highlight.css">
<link rel="stylesheet" href="../../css/popup.css">
<script src="../3rdparty/highlight/highlight.pack.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
</head>
<body onload="Doctest.run();">
+23 -10
View File
@@ -101,16 +101,24 @@ export default class CardWrapper extends Object {
let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect()
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
})
let topLeft = Points.fromPageToNode(element, rect)
let width = Math.abs(bottomRight.x - topLeft.x)
let height = Math.abs(bottomRight.y - topLeft.y)
let center = Points.fromPageToNode(element, {
x: rect.x + rect.width / 2,
y: rect.y + rect.height / 2
x: rect.x + width / 2,
y: rect.y + height / 2
})
return {
x: topLeft.x,
y: topLeft.y,
width: rect.width,
height: rect.height,
width,
height,
center,
link
}
@@ -138,15 +146,20 @@ export default class CardWrapper extends Object {
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) {
// if (this.isSVGNode(node)) {
// if (this.triggerSVGClicks) {
// let click = new Event('click')
// node.dispatchEvent(click)
// }
// return
// }
// node.click()
let click = new Event('click')
click.clientX = event.clientX
click.clientY = event.clientY
node.dispatchEvent(click)
}
return
}
node.click()
}
nodeTapped(node, event) {
console.log('nodeTapped', node, this.isClickable(node))
+20 -14
View File
@@ -145,18 +145,29 @@ export default class Doctest {
let container = document.createElement('div')
container.className = 'doctest-wrapper'
let titleParent = container
if (doctest.hasAttribute('data-collapsible')) {
let collapsibleToggle = document.createElement('div')
if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
let titlebar = document.createElement('div')
titlebar.className = 'doctest-titlebar'
titlebar.style = 'min-height: 10px;'
container.appendChild(titlebar)
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6')
title.innerText = doctest.getAttribute('data-title')
title.className = 'doctest-section-title'
titlebar.appendChild(title)
}
if (doctest.hasAttribute('data-collapsible')) {
let icon = document.createElement('i')
icon.className = 'material-icons'
collapsibleToggle.appendChild(icon)
titlebar.classList.add('doctest-collapsible-toggle')
collapsibleToggle.className = 'doctest-collapsible-toggle'
collapsibleToggle.style = 'min-height: 10px;'
titleParent = collapsibleToggle
container.appendChild(collapsibleToggle)
if (titlebar.childNodes.length > 0) {
titlebar.insertBefore(icon, titlebar.childNodes[0])
} else {
titlebar.appendChild(icon)
}
const collapsedClass = 'collapsed'
@@ -175,13 +186,8 @@ export default class Doctest {
}
setToggleMode(doctest.hasAttribute('data-collapsed'))
collapsibleToggle.addEventListener('click', determineToggleMode)
titlebar.addEventListener('click', determineToggleMode)
}
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6')
title.innerText = doctest.getAttribute('data-title')
title.className = 'doctest-section-title'
titleParent.appendChild(title)
}
let pre = document.createElement('pre')
+6 -3
View File
@@ -117,9 +117,12 @@ export default class Errors {
window.addEventListener(
'error',
event => {
// if (typeof(event.error) == 'undefined') {
// console.info("Catched undefined error", event)
// }
if (typeof event.error == 'undefined') {
// This sometimes happens in Edge. Since we have no error
// position, we cannot do much beside an info log.
console.info('Catched undefined error', event)
return
}
this.appendError(event.error, event.filename)
},
true
+4 -1
View File
@@ -1,7 +1,10 @@
export default class Events {
static stop(event) {
event.preventDefault()
event.stopPropagation()
// I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
}
static extractPoint(event) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 755 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.
+3 -1
View File
@@ -141,7 +141,8 @@ export class HTMLLoader extends CardLoader {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', this.src, false)
xhr.onload = e => {
xhr.onreadystatechange = e => {
if (xhr.readyState == 4) {
domNode.innerHTML = this.prepare(xhr.response)
this.addedNode = domNode.firstElementChild
let { width, height } = this.size(this.addedNode)
@@ -149,6 +150,7 @@ export class HTMLLoader extends CardLoader {
if (height) this.wantedHeight = height || this.wantedHeight
resolve(this)
}
}
xhr.onerror = e => {
reject(this)
}
+2 -2
View File
@@ -849,8 +849,8 @@ export class InteractionDelegate {
if (Events.isCaptured(event)) {
return false
}
let captured = this.target.capture(event)
return captured
return this.target.capture ? this.target.capture(event) : false
}
getPosition(event) {
+1 -1
View File
@@ -10,7 +10,7 @@ let logHandlers = {
}
try {
ipc = require('electron').ipcRenderer
ipc = window.ipcRenderer || require('electron').ipcRenderer
logHandlers.log = message => ipc.send('log', message)
logHandlers.warn = message => ipc.send('warn', message)
logHandlers.error = message => ipc.send('error', message)
+36
View File
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Fullscreen Application Doctest</title>
<link rel="stylesheet" href=".././3rdparty/highlight/styles/default.css" />
<link rel="stylesheet" href="../../css/doctest.css" />
<script src=".././3rdparty/highlight/highlight.pack.js"></script>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body style="margin: 0; padding: 0;">
<canvas id="canvas" style="width: 100%; height: 100%;"></canvas>
<script class="doctest">
const app = new PIXIApp({
view: canvas,
fpsLogging: true,
transparent: false
})
app.setup()
app.run()
let highlightBtn = new PIXI.Graphics()
highlightBtn.lineStyle(4, 0xff6900)
highlightBtn.drawRoundedRect(150, 40, 30, 30, 4)
highlightBtn.endFill()
app.stage.addChild(highlightBtn)
</script>
</body>
</html>
+14 -1
View File
@@ -137,7 +137,7 @@ export default class PIXIApp extends PIXI.Application {
this.graphql = graphql
if (fullScreen || autoResize) {
console.log('App is in fullScreen mode or autoResize mode')
const resizeDebounced = debounce(event => this.resize(event), 50)
const resizeDebounced = debounce(event => this.resizeApp(event), 50)
window.addEventListener('resize', resizeDebounced)
document.body.addEventListener('orientationchange', this.checkOrientation.bind(this))
}
@@ -312,6 +312,19 @@ export default class PIXIApp extends PIXI.Application {
* @return {PIXIApp} - Returns the PIXIApp for chaining.
*/
resize(event, { width = window.innerWidth, height = window.innerHeight } = {}) {
return this.resizeApp(event, { width, height })
}
/**
* Resizes the renderer to fit into the window or given width and height.
*
* @param {object} [event] - The event.
* @param {object=} [opts={}] - The event.
* @param {number} [opts.width=window.innerWidth] - The width of the app to resize to.
* @param {number} [opts.height=window.innerHeight] - The height of the app to resize to.
* @return {PIXIApp} - Returns the PIXIApp for chaining.
*/
resizeApp(event, { width = window.innerWidth, height = window.innerHeight } = {}) {
this.width = width
this.height = height
this.expandRenderer()
+8 -6
View File
@@ -62,12 +62,11 @@ window.FontInfo = FontInfo
window.Text = Text
//Maps
import MapView from './maps/mapview.js'
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
import { MapData, DeepZoomMapData } from './maps/mapdata.js'
import { MapProjection, DeepZoomMapProjection } from './maps/mapprojection'
window.MapData = MapData
window.DeepZoomMapData = DeepZoomMapData
window.MapProjection = MapProjection
window.DeepZoomMapProjection = DeepZoomMapProjection
window.GeoMap = GeoMap
window.ImageMap = ImageMap
@@ -80,7 +79,8 @@ window.Projection = {
Robinson
}
window.MapView = MapView
import MapViewport from './maps/mapviewport.js'
window.MapViewport = MapViewport
import MapApp from './maps/mapapp.js'
@@ -100,7 +100,6 @@ window.CompactScatter = CompactScatter
window.CoverScatter = CoverScatter
window.MapObjectScatter = MapObjectScatter
import { GeoLayer, MapLayer } from './maps/geolayer.js'
window.GeoLayer = GeoLayer
window.MapLayer = MapLayer
@@ -118,5 +117,8 @@ import Overlay from './maps/overlay.js'
window.Overlay = Overlay
import { MapList } from './maps/maplist.js'
import GeoJson, { GeoUtils } from './maps/geojson.js'
window.MapList = MapList
window.GeoJson = GeoJson
window.GeoUtils = GeoUtils
+4 -1
View File
@@ -248,7 +248,10 @@ const button20 = new Button({
y: 310,
label: 'Link Button',
type: 'checkbox',
style: 'link'
style: 'link',
action: event => {
console.log('Link button clicked')
}
})
const button21 = new Button({
+12 -10
View File
@@ -113,6 +113,8 @@ export default class Button extends PIXI.Container {
const theme = Theme.fromString(opts.theme)
this.theme = theme
this.HIDDEN_ALPHA = 0.000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction
this.opts = Object.assign(
{},
{
@@ -172,10 +174,10 @@ export default class Button extends PIXI.Container {
if (this.opts.style === 'link') {
Object.assign(this.opts, {
strokeAlpha: 0,
strokeActiveAlpha: 0,
fillAlpha: 0,
fillActiveAlpha: 0
strokeAlpha: this.HIDDEN_ALPHA,
strokeActiveAlpha: this.HIDDEN_ALPHA,
fillAlpha: this.HIDDEN_ALPHA,
fillActiveAlpha: this.HIDDEN_ALPHA
})
}
@@ -286,10 +288,6 @@ export default class Button extends PIXI.Container {
this.opts.beforeAction.call(this, e, this)
}
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
TweenLite.to([this.button, this.content], this.theme.fast, {
alpha: 0.83,
overwrite: 'none'
@@ -299,6 +297,10 @@ export default class Button extends PIXI.Container {
this.active = !this.active
}
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
if (this.opts.afterAction) {
this.opts.afterAction.call(this, e, this)
}
@@ -640,8 +642,8 @@ export default class Button extends PIXI.Container {
hide() {
this.opts.strokeAlpha = 0.0
this.opts.strokeActiveAlpha = 0.0
this.opts.fillAlpha = 0.0000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction
this.opts.fillActiveAlpha = 0.0000000001
this.opts.fillAlpha = this.HIDDEN_ALPHA
this.opts.fillActiveAlpha = this.HIDDEN_ALPHA
this.layout()
+2 -2
View File
@@ -85,8 +85,8 @@ export class PIXITileLoader extends TileLoader {
super(tiles)
this.destroyed = false
this.loader = new PIXI.Loader()
this.loader.on('load', this._onLoaded.bind(this))
this.loader.on('error', this._onError.bind(this))
this.loader.onLoad.add(this._onLoaded.bind(this))
this.loader.onError.add(this._onError.bind(this))
if (compression) {
this.loader.use(PIXI.compressedTextures.ImageParser.use)
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

+1
View File
@@ -37,6 +37,7 @@ const index = new Index(itemTemplate, [
['Flippable', 'flippable.html'],
['LabeledGraphics', 'labeledgraphics.html'],
['List', 'list.html'],
['Maps', 'maps/index.html'],
['Message', 'message.html'],
['Modal', 'modal.html'],
['Tooltip', 'tooltip.html'],
File diff suppressed because one or more lines are too long
+75 -42
View File
@@ -2,9 +2,10 @@ import { Points } from '../../utils.js'
import { EventHandler } from './utils.js'
import { FlagPolygon } from '../graphics/label.js'
import { DeepZoomMap } from './map.js'
import { PIXIUtils } from './utils.js'
/**
* GeoGraphics are graphical objects, that does not store the graphics information
* * GeoGraphics are graphical objects, that does not store the graphics information
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
* placed on GeoLayers to work properly.
*
@@ -14,11 +15,12 @@ import { DeepZoomMap } from './map.js'
* The geolayers forward this 'adaptTo' to all children that are GeoGraphics.
* Which adjust their so called 'point' data to the new map.
*
* @abstract
* @export
* @class GeoGraphics
*/
export class GeoGraphics {
constructor(coordinates, { scale = 1, onDraw = null, onDrawEnd = null, debug = false } = {}) {
this.coordinates = coordinates
this._coordinates = coordinates
this.debug = debug
this.graphics = new PIXI.Graphics()
this.scale = scale
@@ -28,6 +30,17 @@ export class GeoGraphics {
this._position = null
}
/**
* The coordinates of the geographics.
*
* @member {array}
* @readonly
* @memberof GeoGraphics
*/
get coordinates() {
return this._coordinates
}
clone() {
console.error(`Call of abstract method clone(). Overwrite in subclass.`, this)
}
@@ -133,6 +146,12 @@ export class GeoGraphics {
this._layer = layer
}
/**
* Map of the containing layer. Null if on no layer.
*
* @readonly
* @memberof GeoGraphics
*/
get map() {
let map = null
if (this.mapLayer) {
@@ -141,6 +160,13 @@ export class GeoGraphics {
return map
}
/**
* MapLayer of the containing layer. Null if on no layer.
*
* @member {MapLayer}
* @readonly
* @memberof GeoGraphics
*/
get mapLayer() {
let mapLayer = null
if (this.layer) {
@@ -199,6 +225,10 @@ export class GeoGraphics {
*
* This GeoGraphics does not provide any visual representation.
* Draw the desired shape in the onDraw callback.
*
* @export
* @class GeoPoint
* @extends {GeoGraphics}
*/
export class GeoPoint extends GeoGraphics {
clone() {
@@ -228,6 +258,13 @@ export class GeoPoint extends GeoGraphics {
_draw() {}
}
/**
* Represensts a line between two locations.
*
* @export
* @class GeoLine
* @extends {GeoGraphics}
*/
export class GeoLine extends GeoGraphics {
/**
* @param {object} opts - Optional values
@@ -330,6 +367,13 @@ export class GeoLine extends GeoGraphics {
}
}
/**
* Represents a shape on a map.
*
* @export
* @class GeoShape
* @extends {GeoGraphics}
*/
export class GeoShape extends GeoGraphics {
clone() {
return new GeoShape(this.coordinates, this._cloneOptions)
@@ -441,15 +485,12 @@ export class GeoShape extends GeoGraphics {
*/
_drawShape(polygon, hole = []) {
// We save the fill specified in the onDraw event handler.
//
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
// callback to make the hole customizable. Maybe you want
// to fill it with a different color or an mediocre alpha value.
// then feel free to implement it.
let { fill, alpha } = PIXIUtils.saveFill(this.graphics)
/**
* This may seem redundant, but it's required
* a) Draw the hole with a polygon.
*
* This may seem redundant to (c), but it's required (in this order(!))
* to make the hole clickable.
*
* It was a bit confusing, so I made a CodePen
@@ -460,9 +501,15 @@ export class GeoShape extends GeoGraphics {
this.graphics.drawPolygon(hole)
}
/**
* b) Draw the shape.
*/
this.graphics.beginFill(fill, alpha)
this.graphics.drawPolygon(polygon)
/**
* c) Add the hole.
*/
if (hole.length > 0) {
this.graphics.beginHole()
this.graphics.drawPolygon(hole)
@@ -475,6 +522,9 @@ export class GeoShape extends GeoGraphics {
* The MultiGraphics makes use of the other GeoGraphics to
* create a single graphics element out of multiple different
* GeoGraphics.
*
* @class GeoMultiGraphics
* @extends {GeoGraphics}
*/
class GeoMultiGraphics extends GeoGraphics {
/**
@@ -524,6 +574,13 @@ class GeoMultiGraphics extends GeoGraphics {
}
}
/**
* Text that is attatched to a GeoPoint.
*
* @export
* @class GeoText
* @extends {GeoPoint}
*/
export class GeoText extends GeoPoint {
constructor(coordinates, text, opts) {
super(coordinates, opts)
@@ -531,14 +588,6 @@ export class GeoText extends GeoPoint {
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
this._text = new PIXI.Text(text, this.textStyle)
//TODO: Make this more generic:
// We have 8 layers (12-20) for each map so this temporarily works fine.
// Outsource it to the map class.
//let textScale = Math.pow(2, 7)
// let textScale = 5
// this.text.scale.set(1 / textScale, 1 / textScale)
switch (this.align) {
case 'left':
break
@@ -570,6 +619,8 @@ export class GeoText extends GeoPoint {
/**
* A geotext with a notch at a certain Position.
*
* TODO: This is a specialization for the Tüsch. This should be inside the Tüsch project.
*/
export class GeoFlagLabel extends GeoText {
constructor(coordinates, text, opts) {
@@ -675,6 +726,13 @@ export class GeoFlagLabel extends GeoText {
}
}
/**
* The GeoMultiShape displays multiple forms.
*
* @export
* @class GeoMultiShape
* @extends {GeoShape}
*/
export class GeoMultiShape extends GeoShape {
static _manipulatePoints(points, func) {
points.forEach(shape => {
@@ -714,28 +772,3 @@ export class GeoMultiShape extends GeoShape {
})
}
}
class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform()
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
}
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y }
if (fontSize) pixiText.style.fontSize = fontSize
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}
+2 -2
View File
@@ -156,7 +156,7 @@
// As map an image of europe is used.
let europe = "../../../../var/examples/maps/europe/europe.jpg"
let europeData = new MapData(MERCATOR, {
let europeMapProjection = new MapProjection(MERCATOR, {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -182,7 +182,7 @@
window.readyHandler = new EventHandler("onReady")
function ready(textures) {
let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeData)
let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeMapProjection)
app.setMap("germany", europeMap)
app.setup().run()
+26 -7
View File
@@ -6,8 +6,7 @@ import { GeoGraphics, GeoShape, GeoMultiShape, GeoLine, GeoPoint } from './geogr
*
* @static
* @export
* @class GeoJsonGraphics
* @extends {GeoGraphics}
* @class
*/
export default class GeoJson {
static isLineType(type) {
@@ -40,6 +39,15 @@ export default class GeoJson {
return ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']
}
/**
* Takes a JSON object that contains a FeatureCollection and returns an array
* of GeoJson objects.
*
* @static
* @param {array} featureCollection - Array of GeoJSON objects that were contained in the feature collection.
* @returns {array} Returns an array of geo json objects.
* @memberof GeoJson
*/
static unwrapFeatureCollection(featureCollection) {
if (featureCollection.features == null) {
console.error(
@@ -59,18 +67,27 @@ export default class GeoJson {
}
list.push({ type, coordinates })
// console.log({type, coordinates})
})
return list
}
/**
* Validates and converts one set of coordinates of a specific type.
*
* @static
* @param {string} type - Type of the GeoJson.
* @param {array} coordinates - array of points.
* @returns {array}
* @memberof GeoJson
*/
static validateAndConvert(type, coordinates) {
let converted = null
if (!GeoJson.validateType(type)) throw new GeoJson.InvalidTypeError(type)
else {
if (GeoJson.validateCoordinates(type, coordinates)) {
let converted = GeoJson.convert(type, coordinates)
return converted
converted = GeoJson.convert(type, coordinates)
} else {
console.error(
`Coordinates are invalid. They must be in format of type '${type} - ${GeoJson._getFormatStringOfType(
@@ -79,6 +96,8 @@ export default class GeoJson {
)
}
}
return converted
}
static validateType(type) {
@@ -123,7 +142,7 @@ export default class GeoJson {
* considered valid. A complete list is provided in the GeoUtils.
*
* @param {object} point - The point that is tested for validity.
* @returns
* @returns {boolean}
* @memberof GeoJson
*/
static validateAndConvertPoint(point) {
@@ -302,7 +321,7 @@ export class GeoUtils {
* {latitude: lat, longitude: lng}
*
* @static
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate.
* @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
* @memberof GeoGraphics
*/
+88 -21
View File
@@ -1,14 +1,17 @@
import { GeoMap } from './map.js'
import MapView from './mapview.js'
import { EventHandler } from './utils.js'
import { GeoGraphics } from './geographics.js'
import { MapList } from './maplist.js'
//import { GeoGraphics } from "../pixi/geographics.js"
import MapViewport from './mapviewport.js'
import { ScatterContainer } from '../scatter.js'
/**
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
* GeoGraphics. The layer can be adapted to a map and notifies all Geo-Children
* of the Adaption.
*
* @export
* @class GeoLayer
*/
export class GeoLayer {
constructor(displayObject, opts = {}) {
@@ -93,6 +96,13 @@ export class GeoLayer {
return this._visibility
}
/**
* Alias for geoLayer.displayObject.addChild.
*
* @public
* @param {GeoGraphics | PIXI.DisplayObject} element - Element to add to the displayObject.
* @memberof GeoLayer
*/
addChild(element) {
this.displayObject.addChild(element)
}
@@ -117,15 +127,6 @@ export class GeoLayer {
} else console.error('There was no map specified.', this)
}
// place(geographic) {
// if (geographic.constructor.name.startsWith('Geo') && geographic.graphics) {
// // Fix to remove the rollupjs circular dependency
// //if (geographic instanceof GeoGraphics) {
// this.geographics.push(geographic)
// super.place(geographic.graphics)
// } else super.place(geographic)
// }
removeFromParent() {
if (this.parent) {
this.parent.removeLayer(this)
@@ -165,8 +166,15 @@ export class GeoLayer {
return this._parent
}
/**
* Adds a GeoLayer as child to the GeoLayer.
*
* @public
* @param {GeoLayer} layer - GeoLayer to add.
* @memberof GeoLayer
*/
addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) {
if (layer instanceof GeoLayer) {
layer.removeFromParent()
this.layers.push(layer)
@@ -218,6 +226,18 @@ export class GeoLayer {
// }
}
/**
* The map layer is responsible for showing certain maps, at a specific position It contains
* a list of available maps and can switch between them seamlessly. GeoGraphics placed on the MapLayer itself
* or child Geolayers will be adapted to maps and adjusted on map change automatically.
*
* The map layer is the 'king' of the geo layers. Every geolayer
* needs a map layer at it's root. Otherwise they won't work-
*
* @export
* @class MapLayer
* @extends {GeoLayer}
*/
export class MapLayer extends GeoLayer {
constructor(
mapList,
@@ -247,7 +267,7 @@ export class MapLayer extends GeoLayer {
listeners: onChange
})
this.mapview = new MapView({
this._mapview = new MapViewport({
zoom,
focus,
viewport
@@ -267,6 +287,18 @@ export class MapLayer extends GeoLayer {
this.changeMap(mapList.active)
}
//Todo: rename to mapviewport.
/**
* Returns the MapViewport of this map layer.
*
* @readonly
* @member {MapViewport}
* @memberof MapLayer
*/
get mapview() {
return this._mapview
}
get mapChangeLocked() {
return this._mapChangeLocked
}
@@ -279,22 +311,34 @@ export class MapLayer extends GeoLayer {
this._mapChangeLocked = false
}
/**
* Adapts all child layers and their GeoGraphics.
*
* This is called primarily on a map change.
*
* @private
* @memberof MapLayer
*/
adapt() {
this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map)
})
}
focus(coordinates, zoom) {
this.mapview.updateFocusPoint(this.map)
}
transformed(e) {
this.mapview.transformed(this.map)
this.mapview.update(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
this.transformHandler.call(this)
}
/**
* Clones the map layer-
*
* @param {ScatterContainer} scatterContainer - ScatterContainer of the app.
* @param {PIXI.DisplayObject} [container=null] - Container of the newly created MapLayer. If null, an empty PIXI.Container will be created.
* @returns {MapLayer} - Returns the cloned MapLayer.
* @memberof MapLayer
*/
clone(scatterContainer, container = null) {
let mapList = this.mapList.clone()
container = container == null ? new PIXI.Container() : container
@@ -369,23 +413,46 @@ export class MapLayer extends GeoLayer {
}
}
/**
* Applies the mapviews focus to the map.
* This may be useful, if the container was modified.
*
* @memberof MapLayer
*/
refocus() {
this.mapview.apply(this.map)
}
/**
* @public
* @returns {GeoMap} - Returns the active map.
* @readonly
* @memberof MapLayer
*/
get map() {
return this.mapList.map
}
/**
* This is required for the consistency of georelated layers.
* The request traverses up to the mapLayer where it then returns
* the responsible map layer.
*
* This is required for the geo layers.
* MapLayer requests from the geoLayers traverse up to the next MapLayer.
*
* @public
* @returns {MapLayer} - Returns this MapLayer.
* @readonly
* @memberof MapLayer
*/
get mapLayer() {
return this
}
/**
* Cleans up the MapLayer.
*
* @public
* @memberof MapLayer
*/
cleanup() {
this.mapList.cleanup()
}
+112
View File
@@ -0,0 +1,112 @@
<html>
<head>
<title>PIXI Maps Doctests</title>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0" />
<link rel="stylesheet" href="../../../css/index.css">
<script src="../../../dist/iwmlib.js"></script>
<template id="itemTemplate">
<a class="wrapper" href="">
<div class="preview">
<div class="thumbnail-container">
<div class="thumbnail">
<img class="icon" src="thumbnails/notfound.png">
<!-- <iframe src="" frameborder="0"></iframe> -->
</div>
</div>
<div class="title"></div>
</div>
</a>
</template>
<style>
body {
font-size: 20px;
display: flex;
flex-direction: column;
}
#logo {
left: 0;
position: absolute;
top: 30px;
}
#logo>img {
width: 80px;
}
header>h1 {
text-transform: uppercase;
letter-spacing: 0.05em;
word-spacing: 0.25em;
margin-top: 0;
}
header>p {
max-width: 720px;
line-height: 1.5em;
color: rgb(207, 207, 207);
}
header {
font-family: "Open Sans", sans-serif;
background-color: #4c4f4f;
color: whitesmoke;
padding: 68px 50px 10px 150px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}
.container {
justify-content: center;
flex: 1;
height: auto;
min-height: auto;
width: auto;
min-width: auto;
margin: 0 60px;
}
</style>
</head>
<body>
<header>
<a id="logo" target="_blank" href="http://www.iwm-tuebingen.de">
<img src="../../../assets/logos/iwm_logo_2015_twitter.png">
</a>
<h1>Maps Module</h1>
<p>The maps module provides a handy toolkit to easily integrate maps in an application. Create a full screen map
application by using the mapapp. Utilize the GeoLayer-system to integrate maps in an existing application.
Draw graphics onto the map using geographical positions instead of pixel positions with the GeoGraphics.
Or just use an Overlay to quickly draw icons for each point on a map.
</p>
</header>
<div id="container" class="container">
</div>
<script>
const index = new Index(itemTemplate, [
["GeoGraphics", "geographics.html"],
["GeoJson", "geojson.html"],
["GeoMap", "map.html"],
["MapApp", "mapapp.html"],
["MapProjection", "mapprojection.html"],
["MapViewport", "mapviewport.html"],
["Overlay", "overlay.html"],
["Scatter", "scatter.html"]
],
null)
index.load()
</script>
</body>
</html>
+4 -4
View File
@@ -71,10 +71,10 @@
const projection = new Projection.Mercator()
// Create a map data object.
const osmDeepZoomMapData = new DeepZoomMapData(projection, tilesConfig, { app })
const osmDeepZoomMapProjection = new DeepZoomMapProjection(projection, tilesConfig, { app })
// Create the map
const osmMap = new DeepZoomMap(osmDeepZoomMapData, tilesConfig)
const osmMap = new DeepZoomMap(osmDeepZoomMapProjection, tilesConfig)
// Add the map to the app.
app.addMap("osm", osmMap)
@@ -122,10 +122,10 @@
const projection = new Projection.Robinson(10)
// Create a map data object.
let mapData = new MapData(projection)
let mapProjection = new MapProjection(projection)
// Create the map
let imageMap = new ImageMap(sprites.get(mapTexture), mapData)
let imageMap = new ImageMap(sprites.get(mapTexture), mapProjection)
// Add the map to the app.
+148 -67
View File
@@ -1,12 +1,12 @@
import { MapObjectScatter } from './scatter.js'
import { DeepZoomImage, DeepZoomInfo } from '../deepzoom/image.js'
import { MapData, DeepZoomMapData } from './mapdata.js'
import { MapProjection, DeepZoomMapProjection } from './mapprojection.js'
import { Points } from '../../utils.js'
import { EventHandler } from './utils.js'
import Mercator from './projections/mercator.js'
/**
* The GeoMap class displays a map, that it gets from MapData object.
* The GeoMap class displays a map, that it gets from MapProjection object.
* It handles the current location on the map, the zoom factor, interactions and
* the viewport, the area of the map, the user can see and navigate to.
*
@@ -26,12 +26,12 @@ export class GeoMap {
* Creates instance of GeoMap
*
* @constructor
* @param {MapData}[mapdata={}] - The mapdata describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
* @param {MapProjection}[mapProjection={}] - The map projection describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
* @param {object}[opts={}] - With the opts, the created MapObjectScatter can be adjusted.
* @param {boolean}[opts.cover=false] - Enables covering behaviour of a map object. Normally maps should cover the whole app.
*/
constructor(
mapdata = {},
mapProjection = {},
{
debug = true,
cover = true,
@@ -43,7 +43,7 @@ export class GeoMap {
translatable = true,
scalable = true,
rotatable = false, // Many functionalities are not supported when rotating the map. Mainly the cover mechanism.
viewport = mapdata.maxViewport,
viewport = mapProjection.maxViewport,
// Events
onLoad = null,
onTransform = null
@@ -54,7 +54,7 @@ export class GeoMap {
this.onTransform = new EventHandler('transform', { listeners: onTransform })
this._alpha = alpha
this.cover = cover
this._cover = cover
this.debug = debug
//TODO discuss if this is required here.
@@ -68,15 +68,15 @@ export class GeoMap {
this.scalable = scalable
this.viewport = viewport
this.mapdata = mapdata
this._mapProjection = mapProjection
this.overlays = {}
/**
* Adjust the viewport depending on the mapdata clipping.
* Adjust the viewport depending on the mapProjection clipping.
*/
if (this.mapdata.clip) {
if (this.mapProjection.clip) {
const vp = this.viewport
const cp = this.mapdata.clip
const cp = this.mapProjection.clip
let bounds = {
min: {
x: vp.min.x > cp.min.x ? vp.min.x : cp.min.x,
@@ -92,29 +92,64 @@ export class GeoMap {
}
}
/**
* Determines if the scatter covers the container.
* @member {boolean}
* @readonly
* @memberof GeoMap
*/
get cover() {
// For cloning we want to store the cover inside the map.
// But the scatter is responsible for the cover state.
// So we update the app's cover state every time
// we get the value.
if (this.image && this.image.scatter && this.image.scatter.cover != null) {
this._cover = this.image.scatter.cover
}
return this._cover
}
/**
* Returns the image object used by the GeoMap.
*
* @readonly
* @memberof GeoMap
*/
get image() {
return this._image
}
/**
* The mapProjection of the map.
*
* @member {MapProjection}
* @readonly
* @memberof GeoMap
*/
get mapProjection() {
return this._mapProjection
}
/**
* Clears all EventHandlers.
*
* @memberof GeoMap
*/
flushHandlers() {
// this.onLoaded
this.onLoad.empty()
this.onTransform.empty()
}
/**
* Locks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable
this.image.scatter.movableX = translatable
this.image.scatter.movableY = movableX
this.image.scatter.rotatable = movableY
this.image.scatter.scalable = scalable
}
// Issue #001: This causes the map to not be displayed at the correct position on
// map change.
// // Rotation does not yet work with the cover mechanism.
// //this.rotatable = false
// this.translatable = false
// this.scalable = false
}
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = translatable
this.image.scatter.movableX = movableX
@@ -122,13 +157,26 @@ export class GeoMap {
this.image.scatter.rotatable = rotatable
this.image.scatter.scalable = scalable
}
// Issue #001
// // Rotation does not yet work with the cover mechanism.
// //this.rotatable = true
// this.translatable = true
// this.scalable = true
}
/**
* Unlocks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
this.lock({ rotatable, translatable, movableX, movableY, scalable })
}
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() {
if (this.image) {
if (this.image.parent) {
@@ -142,6 +190,12 @@ export class GeoMap {
}
}
/**
* Removes the map, freeing all memory ba flushing handlers and removing the image.
*
* @public
* @memberof GeoMap
*/
remove() {
if (this.image) this.image.mask = null
@@ -164,6 +218,7 @@ export class GeoMap {
/**
* Wrapps the display object around a scatter object.
* creates the image.
*
* @private
* @param {DisplayObject} displayObject - Defines the display object that will be wrapped inside the scatter object.
@@ -172,11 +227,9 @@ export class GeoMap {
load(image, renderer, frame = null, scatter = null) {
if (this.debug) console.log('Load image: ', image, frame)
this.image = image
this._image = image
if (frame) this.setFrame(frame)
let boundaries = this.mapdata.getBoundaries()
let scatterOpts = Object.assign({
cover: this.cover,
scaleable: this.scaleable,
@@ -186,7 +239,6 @@ export class GeoMap {
startScale: this.startScale,
minScale: this.minScale,
maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this)
})
@@ -242,11 +294,11 @@ export class GeoMap {
* to a coordinate with latitude and longitude.
*
*
* @param {object} point - Point in form of {x: x_val, y: y_val}.
* @param {object} point - Point in form of {x, y}.
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
*/
coordinatesFromPoint(point) {
let coords = this.mapdata.toCoordinates(this.toRelativePosition(point))
let coords = this.mapProjection.toCoordinates(this.toRelativePosition(point))
return coords
}
@@ -254,10 +306,10 @@ export class GeoMap {
* Transform coordinates in the map into pixel positions on the deep zoom image.
*
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
* @return - Returns a image position in form of {x: x_val, y: y_val}.
* @return {Point} - Returns a image position in form of {x: x, y: y}.
*/
coordinatesToPoint(coordinates) {
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
return this.toAbsolutePixelCoordinates(this.mapProjection.toPixel(coordinates))
}
toRelativePosition(point) {
@@ -324,15 +376,41 @@ export class GeoMap {
this.frame = null
}
/**
* The complete Triforce, or one or more components of the Triforce.
* @typedef {Object} Frame
* @property {number} x - X position of the frame.
* @property {number} y - Y position of the frame.
* @property {number} width - Width of the frame.
* @property {number} height - Height od the frame.
* @property {Point} localCenter - Local center of the map.
* @property {Point} center - Global center of the map.
*/
/**
* Sets the frame if the map.
*
* Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element,
* for example when in a submap.
*
* @param {Frame} frame
* @memberof GeoMap
*/
setFrame(frame) {
if (this.debug) console.log('Set Frame: ', frame)
this.frame = frame
}
/**
* Gets the frame if the map.
*
* Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element,
* for example when in a submap.
*
* @returns {Frame} - Returns the frame of the map.
* @memberof GeoMap
*/
getFrame() {
let frame = {
@@ -418,6 +496,7 @@ export class GeoMap {
* for creating the maps.
*
* @static
* @private
* @param {object} json - The object containing multiple map data sets.
* @param {error-object} error - An object that contains an parameter message: {message = ""}. This is faking a call by reference.
* @returns {boolean} - True if all sets were valid. False otherwise.
@@ -453,6 +532,7 @@ export class GeoMap {
*Validates of a single data set contains the valid data for creating a map.
*
* @static
* @private
* @param {object} json - The object containing a single set of map data.
* @returns {boolean} - True if valid, otherwise false.
* @memberof GeoMap
@@ -488,8 +568,8 @@ export class GeoMap {
const tilesConfig = data.tiles
const options = data.options
const mapdata = new DeepZoomMapData(projection, tilesConfig)
return new DeepZoomMap(mapdata, tilesConfig, options)
const mapProjection = new DeepZoomMapProjection(projection, tilesConfig)
return new DeepZoomMap(mapProjection, tilesConfig, options)
}
static _getProjectionByName(projection) {
@@ -509,40 +589,39 @@ GeoMap.counter = 0
* The DeepZoomMap class extends the GeoMap to create
* maps as deepzoom images from maptiles.
*
* @extends GeoMap
* @class
* @see {@link maps.html}
* @export
* @class DeepZoomMap
* @extends {GeoMap}
*/
export class DeepZoomMap extends GeoMap {
/**
* @constructor
* @param {object} tilesConfig - The tiles config object, that defines at what path and in which format the tiles are.
* @param {MapData} mapdata - A MapData object, that contains informations of how the given map has to be interpreted.
* @param {MapProjection} mapProjection - A MapProjection object, that contains informations of how the given map has to be interpreted.
* @param {object} opts - Additional options to specify the behaviour of the deep zoom image.
*/
constructor(mapdata, tilesConfig, opts = {}) {
constructor(mapProjection, tilesConfig, opts = {}) {
opts = Object.assign(
{
maxScale: Math.min(tilesConfig.width, tilesConfig.height) / tilesConfig.tileSize,
minScale: mapdata.getMinScale,
minScale: mapProjection.getMinScale,
highResolution: true,
debug: false
},
opts
)
super(mapdata, opts)
super(mapProjection, opts)
this.tilesConfig = tilesConfig
this._verifyMapdata()
this._verifyMapProjection()
}
_verifyMapdata() {
if (!(this.mapdata instanceof MapData)) {
console.error('Use the MapData object for creating maps!')
_verifyMapProjection() {
if (!(this.mapProjection instanceof MapProjection)) {
console.error('Use the MapProjection object for creating maps!')
} else {
if (!(this.mapdata instanceof DeepZoomMapData)) {
console.error('Use the DeepZoomMapData object.')
if (!(this.mapProjection instanceof DeepZoomMapProjection)) {
console.error('Use the DeepZoomMapProjection object.')
}
}
}
@@ -553,10 +632,10 @@ export class DeepZoomMap extends GeoMap {
* @private
*/
load(container = null, scatter = null) {
if (!this.mapdata.app) console.error('App was not set in the mapdata.')
if (!this.mapProjection.app) console.error('App was not set in the mapProjection.')
this.info = new DeepZoomInfo(this.tilesConfig)
let image = new DeepZoomImage(this.info, {
app: this.mapdata.app,
app: this.mapProjection.app,
alpha: this.alpha,
debug: this.debug,
world: scatter == null ? scatter : scatter.getWorldScatter()
@@ -674,7 +753,7 @@ export class DeepZoomMap extends GeoMap {
}
clone(container, scatter = null) {
const map = new DeepZoomMap(this.mapdata, this.tilesConfig, {
const map = new DeepZoomMap(this.mapProjection, this.tilesConfig, {
alpha: this.alpha,
cover: this.cover,
debug: this.debug,
@@ -734,15 +813,17 @@ DeepZoomMap.tintcolors = [0xff0000, 0xff00ff, 0xffff00, 0x00ff00, 0x00ffff, 0x00
DeepZoomMap.tintcolor = 0
/**
*
* ImageMap extends GeoMap to display simple images
* as maps.
*
* @export
* @class ImageMap
* @extends {GeoMap}
*/
export class ImageMap extends GeoMap {
constructor(sprite, mapdata, opts = {}) {
super(mapdata, opts)
if (this.debug) console.log('Construct Image Map', sprite, mapdata, opts)
constructor(sprite, mapProjection, opts = {}) {
super(mapProjection, opts)
if (this.debug) console.log('Construct Image Map', sprite, mapProjection, opts)
this.sprite = sprite
@@ -756,7 +837,7 @@ export class ImageMap extends GeoMap {
}
clone(container = null, scatter = null) {
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapdata, {
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapProjection, {
alpha: this.alpha,
cover: this.cover,
debug: this.debug,
+6 -6
View File
@@ -98,11 +98,11 @@
resolutionDependent: false
})
// The mapdata object contains informations,
// The map projection object contains informations,
// how the displayed map has to be interpreted.
// e.g. which projection is used or how the
// image is clipped.
let europeData = new MapData(new Projection.Mercator(), {
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -120,16 +120,16 @@
cover
})
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, {
let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover })
let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
+162 -50
View File
@@ -7,12 +7,35 @@ import { Points } from '../../utils.js'
import Logging from '../../logging.js'
import { MapList } from './maplist.js'
/**
* A PIXI.Point or object in form {x,y}.
*
* @typedef {Object} Point
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/**
* A coordinate point is a PIXI.Point or object in form {x,y} that contains map coordinates
* instead of pixel values, where x represents the latitude and y the longitude.
* @typedef {Object} CoordinatePoint
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/**
* MapApp is responsible for showing fullscreen
* map applications.
*
* @export
* @class MapApp
* @extends {PIXIApp}
*/
export default class MapApp extends PIXIApp {
/**
*Creates an instance of MapApp.
* @memberof MapApp
*/
constructor(opts = {}) {
super(opts)
@@ -27,7 +50,6 @@ export default class MapApp extends PIXIApp {
coordsLogging: false,
overlays: {},
keycodes: {},
showHotkeys: false,
imageMapZoomHeight: 256, //Defines the zoomvalue 1 for all image maps inside the mapapp.
focus: null,
zoom: 1,
@@ -44,7 +66,6 @@ export default class MapApp extends PIXIApp {
this.overlayElements = new Map()
this.debug = opts.debug
this.fpsLogging = opts.fpsLogging
this.showHotkeys = opts.showHotkeys
this.keycodes = this._extractKeyCodes(opts.keycodes)
this.coordsLogging = opts.coordsLogging
this.overlays = opts.overlays
@@ -106,9 +127,15 @@ export default class MapApp extends PIXIApp {
console.log(JSON.stringify(boundaries))
}
/**
* Creates the MapLayer.
*
* @private
* @memberof MapApp
*/
_setupMapLayer() {
this.mapContainer = new PIXI.Container()
this.mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, {
this._mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, {
name: 'Root Map Layer',
focus: this.focus,
zoom: this.zoom,
@@ -174,26 +201,49 @@ export default class MapApp extends PIXIApp {
}
}
/**
* Relayouts the app. E.g. called when the window is resized.
*
* @param {number} width - Desired width of the app.
* @param {number} height - Desired height of the app.
* @memberof MapApp
*/
layout(width, height) {
this.scene.resize(width, height)
this.mapLayer.mapview.update()
this.onSizeChanged.call(this)
}
/**
* Overrides the sceneFactory of the PIXIApp to create a RigidScatterContainer instead of
* a regular PIXI.Container()
*
* @private
* @returns {RigidScatterContainer} - Returns the newly created RigidScatterContainer.
* @memberof MapApp
*/
sceneFactory() {
return new RigidScatterContainer(this.width, this.height, this.renderer, {
app: this,
showBounds: true,
showTouches: true,
showPolygon: true,
container: this
showBounds: false,
showTouches: false,
showPolygon: false,
container: this,
stopEvents: false,
claimEvents: false
})
}
/**
* Changes the map to the given key.
*
* @param {string} key - Identifier of the map to change to.
* @memberof MapApp
*/
selectMap(key) {
if (this.debug) console.log('Select map', key, result)
let result = this.mapList.select(key)
console.log('Select map', key, result)
if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map)
}
@@ -211,14 +261,30 @@ export default class MapApp extends PIXIApp {
this.selectMap(key)
}
/**
* Adds a map to the maplist.
* If no map is set, the added map will be set as default.
*
* @param {string} key - Identifier for the map.
* @param {GeoMap} map - Map object to add.
* @memberof MapApp
*/
addMap(key, map) {
if (this.mapList) this.mapList.add(key, map)
else console.error('Cannot access mapLayer. It was not initialized yet.')
}
/**
* Adds multiple maps at once.
*
* @param {object} mapObject
* @memberof MapApp
*/
addMaps(mapObject) {
for (let [key, val] of Object.entries(mapObject)) {
if (val instanceof GeoMap) {
this.addMap(key, val)
} else console.warn('Tried adding maps that are not og Type GeoMap.')
}
}
@@ -226,6 +292,14 @@ export default class MapApp extends PIXIApp {
this.onTransform.call(this, event)
}
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) {
if (lastMap) {
lastMap.flushHandlers()
@@ -243,6 +317,16 @@ export default class MapApp extends PIXIApp {
}
}
/**
* Returns the mapLayer of the map.
*
* @member {MapLayer}
* @memberof MapApp
*/
get mapLayer() {
return this._mapLayer
}
_doesOverlayElementExist(layer, type, name) {
let layerElements = this.overlayElements.get(layer)
return layerElements != undefined && layerElements[type] != null && layerElements[type][name] != null
@@ -265,7 +349,9 @@ export default class MapApp extends PIXIApp {
}
/**
* Copies the current coordinates to the clipboard.
* Copies the current location to the clipboard.
*
* @memberof MapApp
*/
locationToClipboard() {
let hidden = document.createElement('input')
@@ -278,6 +364,15 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden)
}
/**
* Can be used to copy polygons to the clipboard.
*
* Useful for debugging or to roughly trace a shape in the map.
* The generated pointarray can be used as geometry of a geographic
* or inside an overlay to draw that shape onto the map.
*
* @memberof MapApp
*/
pathToClipboard() {
let hidden = document.createElement('input')
document.body.appendChild(hidden)
@@ -301,25 +396,27 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden)
}
/**
* Returns the active map.
*
* @readonly
* @memberof MapApp
*/
get map() {
return this.mapList.map
}
get activeMapKey() {
return this.mapLayer.active
}
getRelativePosition(x, y) {
return {
x: x * app.width,
y: y * app.height
}
}
clearDrawData() {
this.drawData = []
}
/**
* Logs a text field on the map.
* The text element is a DOMElement.
*
* @param {string} msg - Message to log.
* @memberof MapApp
*/
showNotification(msg) {
let notification = document.createElement('div')
notification.classList.add('notification')
@@ -362,8 +459,25 @@ export default class MapApp extends PIXIApp {
})
}
_currentLocationToString() {}
/**
* @typedef KeyCode
* @type {object}
* @property {string} key - an ID.
* @property {boolean} altKey - Defines if KeyCode requires the alt key to be pressed.
* @property {boolean} shiftKey - Defines if KeyCode requires the shift key to be pressed.
* @property {boolean} ctrlKey - Defines if KeyCode requires the ctrl key to be pressed.
*
*/
/**
* Check's if a key event matches a defined KeyCode.
*
* @private
* @param {KeyboardEvent} event - Event that is fired on keydown, -pressed or -up.
* @param {KeyCode} keyCode - KeyCode is an object in the form of : {key:number, altKey:boolean,shiftKey:boolean,ctrlKey:boolean }
* @returns {boolean} - True, when the event matches the keycode, false otherwise.
* @memberof MapApp
*/
_matchKeyCode(event, keyCode) {
// If keycode does not exist or is invalid - return.
if (!keyCode || keyCode.key == null) return false
@@ -385,6 +499,13 @@ export default class MapApp extends PIXIApp {
})
}
/**
* Checks on every key down if it matches a keycode.
*
* @private
* @param {KeyboardEvent} event
* @memberof MapApp
*/
_checkForKeyCode(event) {
if (this._matchKeyCode(event, this.keycodes.copyCoordinate)) {
event.preventDefault()
@@ -443,10 +564,28 @@ export default class MapApp extends PIXIApp {
this.drawMode = this.DRAW_MODES.PIXI_POINT
}
_extractKeyCodes(keycodeText) {
/**
* @typedef KeyCodePairs
* @type {object}
* @property {string} name - Name of the KeyCode.
* @property {KeyCode} keyCode - KeyCode
*
*/
/**
* Extracts keycodes from a string.
*
* KeycodeStrings may look like this 'ctrl+shift+b'
*
* @private
* @param {KeyCodePairs} nameKeyCodePairs
* @returns {array} - Returns an array of KeyCode objects.
* @memberof MapApp
*/
_extractKeyCodes(nameKeyCodePairs) {
let out = {}
for (let [name, combinationString] of Object.entries(keycodeText)) {
for (let [name, combinationString] of Object.entries(nameKeyCodePairs)) {
let keys = combinationString.split('+')
out[name] = {
key: null,
@@ -517,35 +656,8 @@ export default class MapApp extends PIXIApp {
const center = submap.center
const radius = submap.container.width / 2
const distance = Points.distance(center, event.data.global) / submap.scatter.scale
if (distance > radius) {
//submap.resize((distance) * 2, .2)
//submap.centerAt(myevent.center)
}
}
// for (const submap of this.submaps) {
// const center = submap.center
// const radius = submap.container.width / 2
// const distance = Points.distance(center, event.data.global) / submap.scatter.scale
// const inside = distance < radius + 10
// console.log(distance, radius)
// if (inside) {
// // (this.width + 80) / 2 * this.scatter.scale
// //const width = (submap.width + 80) / 2 * submap.scatter.scale
// //console.log(width)
// if (distance > radius) {
// submap.resize((distance) * 2, .2)
// }
// } else {
// if (distance < radius + 20) {
// //submap.resize((distance - 30) * 2, .2)
// }
// }
// }
}
}
__onEnd(event) {
+73 -5
View File
@@ -1,7 +1,14 @@
/**
* MapList is a list of maps with one active index.
* It contains some utility functions to change the map.
*
* @export
* @class MapList
*/
export class MapList {
constructor(active = null, maps = {}) {
this.maps = maps
this.active = active
this._maps = maps
this._active = active
if (Object.keys(maps).length > 0) this.select(active)
}
@@ -9,8 +16,9 @@ export class MapList {
/**
* Selects a map from the map list.
*
* @public
* @param {string} active - Name of the map to select.
* @returns
* @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
* @memberof MapList
*/
select(active) {
@@ -29,7 +37,7 @@ export class MapList {
}
if (this.active !== active) {
this.active = active
this._active = active
map = this.maps[active]
}
} else {
@@ -40,6 +48,13 @@ export class MapList {
return map
}
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() {
let maps = {}
@@ -50,17 +65,63 @@ export class MapList {
return new MapList(this.active, maps)
}
/**
* Adds a new map to the map list.
*
* @public
* @param {string} key - Key to identify the map.
* @param {GeoMap} map - The GeoMap to add.
* @memberof MapList
*/
add(key, map) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
if (this.active == null) this.active = key
if (this.active == null) this._active = key
map.name = key
this.maps[key] = map
}
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @member {GeoMap}
* @memberof MapList
*/
get map() {
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
}
/**
* Returns the list of addedd cards.
*
* @member {object}
* @readonly
* @memberof MapList
*/
get maps() {
return this._maps
}
/**
* Returns the active key.
*
* @member {string}
* @readonly
* @memberof MapList
*/
get active() {
return this._active
}
/**
* Selects the next map in the map array.
*
* @public
* @returns {GeoMap} - Returns the next map in the list.
* @memberof MapList
*/
next() {
let keys = Object.keys(this.maps)
let idx = keys.indexOf(this.active)
@@ -69,6 +130,13 @@ export class MapList {
return next
}
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() {
for (let key in this.maps) {
let map = this.maps[key]
@@ -3,7 +3,7 @@
<head>
<meta charset='UTF-8'>
<title>MapData</title>
<title>MapProjection</title>
<link rel='stylesheet' href='../../3rdparty/highlight/styles/default.css'>
@@ -37,8 +37,8 @@
</head>
<body onload='Doctest.run()'>
<h1>MapData</h1>
<p>Mapdata calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p>
<h1>MapProjection</h1>
<p>The map projection calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p>
<h2>Static Squared World Map</h2>
<p>The most simple example is a squared world map, thats projected with mercator transformation. Ranging from
@@ -73,9 +73,9 @@
//Helper function to print the coordinates to a dom element.
function printCoordinates(element, mapdata, out_dom) {
function printCoordinates(element, mapProjection, out_dom) {
element.addEventListener("mousemove", (event) => {
let coords = mapdata.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height })
let coords = mapProjection.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height })
out_dom.innerHTML = "Lat: " + coords.x.toFixed(4) + " Lng: " + coords.y.toFixed(4)
})
}
@@ -99,8 +99,8 @@
</script>
<script class="doctest">
// Instantiate our mapdata.
let squared_world = new MapData(new Projection.Mercator());
// Instantiate our map projection.
let squared_world = new MapProjection(new Projection.Mercator());
// Define a position, we are interested in in {x: latitude, y: longitude}
let iceland = { x: 64.514979, y: -19.020796 }
@@ -116,7 +116,7 @@
printCoordinates(map_image_0, squared_world, map_coords_0)
</script>
<h2 id="germany">Clipped Map</h2>
<p>Often we don't use the whole map, or our map is a subsection of the world. MapData can clip those cases, using
<p>Often we don't use the whole map, or our map is a subsection of the world. MapProjection can clip those cases, using
the a
bounding box of min and max coordinates.</p>
<p>Coordinates:
@@ -127,8 +127,8 @@
</div>
<script class="doctest">
//Same procedure as above, just add a clipping parameter to the MapData object.
let europe = new MapData(new Projection.Mercator(), {
//Same procedure as above, just add a clipping parameter to the MapProjection object.
let europe = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -159,7 +159,7 @@
const size = 256
// Fake offset by using the old mapdata.
// Fake offset by using the old map projection.
let translation = squared_world.toPixel({ x: 90-10, y: -140 })
console.log(translation)
@@ -167,7 +167,7 @@
//This map is clipped at the bottom
// And also translated in hoizontal direction.
// The translate option corrects that.
let translated_world = new MapData(new Projection.Mercator(), {
let translated_world = new MapProjection(new Projection.Mercator(), {
translate: { x: -10, y: 40 },
})
@@ -1,20 +1,20 @@
/**
* MapData contains the informations about how
* MapProjection contains the informations about how
* a Map has to be interpreted. What are the bounds of the
* map and how to translate coordinates into
* image positions.
*
* @class
* @see {@link mapdata.html}
* @see {@link mapprojection.html}
*/
export class MapData {
export class MapProjection {
/**
* Creates instance of MapData
* Creates instance of MapProjection
*
* @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @param {object}[opts] - Addiditonal options.
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map
* @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
*/
constructor(projection, opts = {}) {
@@ -25,7 +25,7 @@ export class MapData {
opts
)
this.projection = projection
this._projection = projection
if (this.opts.clip) {
let _cmin = this.projection.forward(this.opts.clip.min)
@@ -49,12 +49,24 @@ export class MapData {
}
}
/**
* The projection used by the map projection.
*
* @member {Projection}
* @readonly
* @memberof MapProjection
*/
get projection() {
return this._projection
}
/**
* Transforms a pixel point on the map to a geographical coordinate.
*
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
* @memberof MapData
* @public
* @param {Point} point - A pixel position on the map.
* @returns {CoordinatePoint} A geographical coordinate.
* @memberof MapProjection
*/
toCoordinates(point) {
if (this.opts.clip) {
@@ -84,9 +96,10 @@ export class MapData {
/**
* Transform a geographical coordinate to a pixel point on the map.
*
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
* @memberof MapData
* @public
* @param {CoordinatePoint} coordinates - A point in the form of {x:lat,y:lng}.
* @returns {Point} A pixel position on the map.
* @memberof MapProjection
*/
toPixel(coordinates) {
let coords = { x: coordinates.x, y: coordinates.y }
@@ -116,6 +129,19 @@ export class MapData {
return point
}
/**
* Clipping describes the
* piece of the map that is shown. The returned object contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
*
* E.g. if we just show a map of
* europe, then we have to set the clipping properly, otherwise
* the preojection would produce the wrong results when transforming
* from a point to coordinates or the other way around.
*
* @readonly
* @member {object}
* @memberof MapProjection
*/
get clip() {
let unclipped = {
min: { x: 0, y: 0 },
@@ -126,30 +152,28 @@ export class MapData {
}
/**
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}}
* to pixel coordinates.
* Returns the biggest viewport the map projection allows.
* This is determined by the projecton or the clipping on the mapapp.
*
* @readonly
* @memberof MapProjection
*/
getBoundaries() {
// let min = this.toPixel(bounds.min)
// let max = this.toPixel(bounds.max)
// Y values needs to be swapped, as PIXI has it's origin
// in the top-left corner and a regular map in the bottom-left corner.
let boundaries = {
min: { x: 0, y: 0 },
max: { x: 1, y: 1 }
}
return boundaries
}
get maxViewport() {
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
}
}
export class DeepZoomMapData extends MapData {
/**
* Special map projection for DeepZoomMap objects.
*
* Note: It just transform the clipping parameter of the tiles config
* to the clipping of the mapapp.
*
* @export
* @class DeepZoomMapProjection
* @extends {MapProjection}
*/
export class DeepZoomMapProjection extends MapProjection {
constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) {
opts.clip = {
+9 -9
View File
@@ -81,11 +81,11 @@
})
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true })
app.setMap("deepzoom", deepZoomMap)
@@ -127,11 +127,11 @@
const app = new MapApp(opts)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns))
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns))
app.setMap("deepzoom", deepZoomMap)
@@ -216,8 +216,8 @@
// Called when all textures are loaded.
let ready = (sprites) => {
// Define the mapdata for the map.
let europeData = new MapData(new Projection.Mercator(), {
// Define the map projection for the map.
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -225,7 +225,7 @@
})
// Create the map using the texture and the mapdata.
// Create the map using the texture and the map projection.
// Optionally customize the map by supplying secific options.
let imageMap = new ImageMap(sprites.get(europe), europeData, {
cover: false
@@ -270,12 +270,12 @@
})
// Create the deepzoomdata.
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), mapConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), mapConfig.tiles, {
app
})
// Create the app.
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, mapConfig.tiles, { app }), { cover: false })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, mapConfig.tiles, { app }), { cover: false })
// Set the map in the app.
app.setMap("deepzoom", deepZoomMap)
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

+163
View File
@@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en" class="dark-mode">
<head>
<meta charset="UTF-8" />
<title>MapViewport</title>
<script src="../../3rdparty/highlight/highlight.pack.js"></script>
<link rel="stylesheet" href="../../../fonts/material-icon-font/material-icons.css">
<link rel='stylesheet' href='../../3rdparty/highlight/styles/vs2015.css'>
<link rel='stylesheet' href='../../../css/doctest.css'>
<script src="../../../dist/iwmlib.3rdparty.js"></script>
<script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script>
<style>
.controls {
display: flex;
}
</style>
</head>
<body onload="Doctest.run()">
<h1>MapViewport</h1>
<p>
The MapViewport works under the hood of a map layer to track the informations about the current focus point and
zoom position.
This is important to maintain the same view when maps are changed.
</p>
<canvas id="canvas"></canvas>
<div id="mapControl"></div>
<div id="cityControl" class="controls"></div>
<p><strong>WHAT TO SEE:</strong> The map should focus Paris.</p>
<script>
let osmConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 256,
"format": "png",
"overlap": 0,
"type": "map",
"height": 1024,
"width": 1024,
"path": "../assets/maps/osm",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
let testConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 128,
"format": "jpg",
"overlap": 0,
"type": "map",
"height": 4096,
"width": 4096,
"path": "../assets/maps/test",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
</script>
<script class="doctest">
let capitals = {
london: { x: 51.5, y: -0.083333 },
rome: { x: 41.9, y: 12.483333 },
madrid: { x: 40.4, y: -3.683333 },
paris: { x: 48.833986, y: 2.346989 }
}
// You may define a focus point ...
let focus = capitals.paris
// ... and a zoom level.
let zoom = 1
// Name has to be app (like all other PIXIApps).
const app = (window.app = new MapApp({
focus,
zoom,
view: canvas,
coordsLogging: true,
width: 512,
height: 512
}))
// As map an image of europe is used.
let europe = '../assets/maps/pixabay/europe.jpg'
//Preload all required sprites for the image map.
app.loadSprites([europe], sprites => ready(sprites), {
resolutionDependent: false
})
// The map projection object contains informations,
// how the displayed map has to be interpreted.
// e.g. which projection is used or how the
// image is clipped.
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
}
})
function ready(sprites) {
const cover = true
// When resources are loaded, the ImageMap can be instantiated.
let imageMap = new ImageMap(sprites.get(europe), europeData, {
coordsLogging: true,
maxScale: 1,
cover
})
let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
app.setMap('europe', imageMap)
// The app requires a map before beeing able to run.
// So start the app here.
app.setup().run()
for (let [key, val] of Object.entries(app.mapList.maps)) {
let mapBtn = document.createElement("button")
mapBtn.innerText = key
mapBtn.addEventListener("click", () => {
app.mapLayer.changeMap(val)
})
mapControl.appendChild(mapBtn)
}
}
for (let [key, val] of Object.entries(capitals)) {
let cityBtn = document.createElement("button")
cityBtn.innerText = key
cityBtn.addEventListener("click", () => {
app.mapLayer.map.moveTo(val)
})
cityControl.appendChild(cityBtn)
}
</script>
</body>
</html>
@@ -1,17 +1,17 @@
import { DeepZoomMap } from './map.js'
/**
* The MapView class is responsible for a consistent map view.
* The MapViewport class is responsible for a consistent map view.
* It is aware of the current viewposition, the scale and viewport.
* It ensures, that maps can be changed, without the user noticing it.
*
*/
export default class MapView {
export default class MapViewport {
/**
*
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.
* @param {number} [zoom = 0] - Defines the startup zoom of the app. Note that this is just a request.
* The MapView will prioritize a full scale app, than displaying the demanded zoom factor
* The MapViewport will prioritize a full scale app, than displaying the demanded zoom factor
*/
constructor({ focus = null, zoom = null, viewport = { min: { x: -85, y: -180 }, max: { x: 85, y: 180 } } } = {}) {
this.viewport = viewport
@@ -20,32 +20,71 @@ export default class MapView {
this.referenceHeight = 256
}
/**
* The current focus point as map coordinates.
*
* @member {CoordinatePoint}
* @readonly
* @memberof MapViewport
*/
get focus() {
return this._focus
}
/**
* The current zoom distance.
* On DeepZoomMaps this is equal to the zoom level.
*
* @member {number}
* @readonly
* @memberof MapViewport
*/
get zoom() {
return this._zoom
}
/**
* Applies the current view to a map.
*
*
* @param {GeoMap} map - Map the viewport should be applied to.
* @memberof MapViewport
*/
apply(map) {
map.moveTo(this._focus, this._zoom)
}
transformed(map) {
/**
* Update the focus point and zoom according to the map.
* This is commonly called when the map is transformed.
*
* @param {GeoMap} map - Map to update.
* @memberof MapViewport
*/
update(map) {
this.updateZoom(map)
this.updateFocusPoint(map)
}
updatePosition(map) {
this.updateFocusPoint(map)
this.updateZoom(map)
}
/**
* Updates the focus point.
* This is automatically called when calling update.
*
* @param {GeoMap} map
* @memberof MapViewport
*/
updateFocusPoint(map) {
const frame = map.getFrame()
this._focus = this.coordinatesFromWindowPoint(map, frame.localCenter)
}
/**
* Updates the zoom.
* This is automatically called when calling update.
*
* @param {GeoMap} map
* @memberof MapViewport
*/
updateZoom(map) {
/**
* TODO: This relies on the fact, that all maps have the same tileSize,
@@ -57,6 +96,16 @@ export default class MapView {
}
}
/**
* Transforms a position on the map to a position in the window.
*
* Inverse of windowPointToMapPoint.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point - Position on the map in pixels.
* @returns {Point} - Pixel position in the window.
* @memberof MapViewport
*/
mapPointToWindowPoint(map, point) {
let windowPoint = { x: 0, y: 0 }
@@ -76,6 +125,16 @@ export default class MapView {
return windowPoint
}
/**
* Transforms in the window to a position on the map.
*
* Inverse of mapPointToWindowPoint(map, point) {.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point -Pixel position in the window.
* @returns {Point} - Position on the map in pixels.
* @memberof MapViewport
*/
windowPointToMapPoint(map, point) {
let pointOnMap = { x: 0, y: 0 }
if (map['image'] && map.image['parent']) {
@@ -111,7 +170,7 @@ export default class MapView {
y: position.y / (map.height * map.scatter.scale)
}
let coordinates = map.mapdata.toCoordinates(normalized)
let coordinates = map.mapProjection.toCoordinates(normalized)
return coordinates
}
+45 -12
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark-mode">
<head>
<meta charset="UTF-8" />
@@ -17,6 +17,8 @@
<script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script>
<link rel="shortcut icon" type="image/x-icon" href="../../../assets/icons/map.png">
<style>
.inline-showcase {
@@ -33,6 +35,7 @@
<body onload="Doctest.run()">
<h1 class="title">Overlay</h1>
<!-- <a href="../../../" class="Documentation"></a> -->
<p class="description">
The overlayclass creates a convenient way to create and design
complex map overlays.
@@ -46,7 +49,7 @@
coordsLogging: true
})
var osmworld = '../assets/maps/osm/0/0/0.png'
let worlOSMData = new MapData(new Projection.Mercator())
let worlOSMData = new MapProjection(new Projection.Mercator())
function setupMap(textures) {
@@ -108,17 +111,22 @@
let exampleOverlayJSON = {
icon: '../../../assets/icons/place.png',
iconColor: '0x000000',
iconColor: "0x35aaea",
iconAnchor: { x: 0.5, y: 1 },
size: 5,
scale: 0.2,
color: '0xFF00FF',
fillAlpha: 0,
disabledColor: 0x000000,
disabledIconColor: 0xCCCCCC,
disabledScale: 0.5,
color: '0x3FA7EE',
items: [
{
name: 'Abidjan',
name: 'Custom Icon',
fontWeight: "bold",
icon: '../../../assets/icons//beenhere.png',
icon: '../../../assets/icons/beenhere.png',
iconColor: 0x00ff00,
iconAlpha: 0.5,
size: 0,
labelVerticalAlignment: "underneath",
label: 'Abidjan',
location: {
@@ -126,17 +134,19 @@
y: -4.006472
},
information:
'... der größte städtische Ballungsraum der Elfenbeinküste.'
'Here a custom icon is used. It overrides the icon setting in the global section.'
},
{
name: 'Berlin',
label: "enabled",
disabledLabel: "disabled",
location: {
x: 52.52543,
y: 13.385291
},
information:
'... ist die Bundeshauptstadt der Bundesrepublik Deutschland.'
'... ist die Bundeshauptstadt der Bundesrepublik Deutschland.',
enabled: false
},
{
name: 'Canberra',
@@ -156,7 +166,7 @@
y: 18.416962
},
information:
'... ist eine Hafenstadt an der Südwestküste Südafrikas auf einer Halbinsel, die vom beeindruckenden Tafelberg dominiert wird.'
`This item adjusts it's size according to the map.`
},
{
name: 'Moskau',
@@ -190,7 +200,8 @@
},
{
name: 'Tokio',
type: "factory",
label: "factory",
location: {
x: 35.696278,
y: 139.731366
@@ -220,6 +231,7 @@
function texturesLoaded(textures) {
/** When all textures are loaded .... */
setupMap(textures)
@@ -231,6 +243,27 @@
let cleaner = null
const vanishingTime = 1000
// Factories must return a geographics object.
Overlay.createFactory("factory", (item) => {
// This is applied to every item in the overlay that has
// the type factory'
let geographics = new GeoPoint(item.location)
geographics.drawHandler.add((graphics) => {
graphics.beginFill(item.color, item.fillAlpha)
graphics.drawRect(0, 0, 10, 10)
})
let text = new PIXI.Text(item.name, {fontSize: 5})
geographics.graphics.addChild(text)
return geographics
})
/**
*
* The actual PIXI elements are created when the overlay.create() is called.
+321 -128
View File
@@ -1,5 +1,5 @@
import { GeoLayer } from './geolayer.js'
import { GeoPoint, GeoMultiShape } from './geographics.js'
import { GeoPoint, GeoMultiShape, GeoGraphics } from './geographics.js'
import GeoJson from './geojson.js'
import Popup from '../popup.js'
@@ -11,46 +11,83 @@ import Popup from '../popup.js'
*
* It's highly configurable and adaptable to a variety of scenarios.
*
*/
export default class Overlay {
/**
*Creates an instance of Overlay.
* @param {*} [{
* name = "unnamed", {string}
* connector = false,
* connectorType = "line",
* location = null,
* label = false,
* labelType = null,
* labelLocation = null,
* labelAlignment = "center",
* labelVerticalAlign = "underneath",
* labelTarget = null,
* shape = "geometry", {string} - Defines the shape to be used: geometry, circle, rectangle
* geometry = null,
* geometryType = "point",
* geometryFallback = "circle",
* informationPopup = "infocard",
* @export
* @class Overlay
*
* }={}]
* @memberof Overlay
*
* @param {object} options
* @param {object} options.name Name of the item.
* @param {object} options.borderColor
* @param {object} options.borderThickness
* @param {object} options.enabled Determines if enabled, disabled when false.
* @param {object} options.labelScale
* @param {object} options.location
* @param {object} options.label
* @param {object} options.labelType
* @param {object} options.labelLocation
* @param {object} options.labelAlignment
* @param {object} options.labelVerticalAlignment
* @param {object} options.labelSpacing
* @param {object} options.labelTarget
* @param {object} options.geometry
* @param {object} options.information
* @param {object} options.informationPath
* @param {object} options.informationType
* @param {object} options.icon
* @param {object} options.iconAlpha
* @param {object} options.iconAnchor
* @param {object} options.iconColor
* @param {object} options.iconScale
* @param {object} options.iconOffset
* @param {object} options.items
* @param {object} options.color Color of te point.
* @param {object} options.fillAlpha FillAlpha of the point.
* @param {object} options.size Size of the point.
* @param {object} options.scale Determines the scale of the graphics.
* @param {object} options.rescale Rescales the graphics relative to the map, that they keep their original scale. Only works for the entire overlay atm. Therefore must be set in the global param eters and not inside the children.
* @param {object} options.popoverOffset
* @param {object} options.zoomVisibility
* @param {object} options.labelMultiLineAlign
* @param {object} options.labelBreakWords
* @param {object} options.labelDropShadow
* @param {object} options.labelDropShadowAlpha
* @param {object} options.labelDropShadowAngle
* @param {object} options.labelDropShadowBlur
* @param {object} options.labelDropShadowColor
* @param {object} options.labelDropShadowDistance
* @param {object} options.fontColor
* @param {object} options.fontFamily
* @param {object} options.fontSize
* @param {object} options.fontStyle
* @param {object} options.fontWeight
* @param {object} options.labelStroke
* @param {object} options.labelStrokeThickness
* @param {object} options.wordWrap
* @param {object} options.wordWrapWidth
* @param {object} options.labelFill
* @param {object} options.labelFillGradientType
* @param {object} options.labelFillGradientStops
* @param {object} options.fontVariant
* @param {object} options.labelLeading
* @param {object} options.letterSpacing
* @param {object} options.labelLineHeight
* @param {object} options.labelLineJoin
* @param {object} options.labelMiterLimit
* @param {object} options.labelPadding
* @param {object} options.labelTrim
* @param {object} options.textBaseline
* @param {object} options.labelWhiteSpace
*/
constructor(opts = {}) {
export default class Overlay {
constructor(options = {}) {
let defaultTextStyle = new PIXI.TextStyle()
Object.assign(
this,
{
let defaultOptions = {
type: 'auto',
name: 'unnamed',
borderColor: 0x000000,
borderThickness: 5,
disabledAlpha: 1,
disabledColor: 0xaa1111,
disabledBorderColor: 0x000000,
enabled: true,
labelScale: 1,
location: null,
@@ -58,7 +95,7 @@ export default class Overlay {
labelType: null,
labelLocation: null,
labelAlignment: 'center',
labelVerticalAlign: 'underneath',
labelVerticalAlignment: 'underneath',
labelSpacing: 10,
labelTarget: null,
geometry: null,
@@ -114,15 +151,76 @@ export default class Overlay {
labelTrim: defaultTextStyle.trim,
textBaseline: defaultTextStyle.textBaseline,
labelWhiteSpace: defaultTextStyle.whiteSpace
},
opts
)
}
defaultOptions = this.addDisabled(defaultOptions)
console.log('DISABLED VERTICALA.', defaultOptions.disabledLabelVerticalAlignment)
options = this.addDisabled(options)
options = Object.assign({}, defaultOptions, options)
Object.assign(this, options)
}
/**
* Some parameters shall not have a disabled option.
* These are defined here.
*
* @readonly
* @memberof Overlay
*/
get excludedDisableParameters() {
return ['items', 'rescale', 'name', 'type', 'enabled']
}
/**
* Copies the normal properties to the disabled version of the property,
* while conserving already set disabled properties.
*
* @param {*} options
* @returns {object} - Returns the modified options object.
* @memberof Overlay
*/
addDisabled(options) {
for (let [key, value] of Object.entries(options)) {
if (this.excludedDisableParameters.indexOf(key) == -1) {
let disabledProperty = this.toDisabledPropertyString(key)
if (options[disabledProperty] == undefined) {
options[disabledProperty] = value
}
}
}
return options
}
/**
* Textures need to be loaded by the app. Texture parameters return all parameters that need to be evaluated for textures.
* @readonly
* @member {array}
* @memberof Overlay
*/
get textureParameters() {
return ['icon']
const textureParameters = ['icon']
textureParameters.forEach(textureParameter => {
textureParameters.push(this.toDisabledPropertyString(textureParameter))
})
return textureParameters
}
/**
* The required textures are saved inside the single items.
* Their texture string gets replaced by a texture object
* containing the path and the texture.
*
* @param {*} parameter
* @param {*} key
* @param {*} path
* @memberof Overlay
*/
setTexture(parameter, key, path) {
let obj = (this[parameter] = {})
obj._key = key
@@ -150,6 +248,14 @@ export default class Overlay {
})
}
/**
* Recursively calls a function on child items.
*
* @param {*} parameter
* @param {*} func
* @param {*} [obj=null]
* @memberof Overlay
*/
apply(parameter, func, obj = null) {
if (obj == null) return
if (Array.isArray(obj)) {
@@ -187,13 +293,21 @@ export default class Overlay {
adjustItems = null,
cleanupItems = null
} = {}) {
const name = this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
console.log(this)
const name = this.name
? this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
: 'Unnamed Overlay'
let geoLayer = new GeoLayer(new PIXI.Container(), { name })
geoLayer.visibility = this.zoomVisibility
if (this.rescale) geoLayer.rescale = this.rescale
this.items.forEach(item => {
if (!excludeItems(item)) {
//Copies all values to a disabled state.
item = this.addDisabled(item)
if (adjustItems) {
adjustItems(item)
}
@@ -208,11 +322,60 @@ export default class Overlay {
})
return geoLayer
}
/**
* Used to pick a property or disabled property using an enabled parameter.
*
* @param {*} item
* @param {*} property
* @param {boolean} [enabled=true]
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
pickItemProperty(item, property, enabled = true) {
return enabled ? this.getItemProperty(item, property) : this.getDisabledItemProperty(item, property)
}
toDisabledPropertyString(propertyName) {
const prefix = 'disabled'
return prefix + propertyName[0].toUpperCase() + propertyName.slice(1)
}
/**
* Get's the disabled version of the property as long as it is not excluded.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getDisabledItemProperty(item, property) {
if (this.excludedDisableParameters.indexOf(property) === -1) {
property = this.toDisabledPropertyString(property)
}
const propertyValue = this.getItemProperty(item, property)
return propertyValue
}
/**
* Tries to get the infromation from an overlay item.
* If the item does not contain the property, the overlay definition will be used.
* When the overlay definition is not set, the default value is used.
*
* @param {OverlayItem} item - Item to get the informations from.
* @param {string} property - Name of the property.
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getItemProperty(item, property) {
let propertyValue = null
const propertyExistsOnItem = item[property] !== undefined
const propertyExistsOnOverlay = this[property] !== undefined
if (property == 'disabledLabelVerticalAlignment') {
console.log(this)
console.log(this['disabledLabelVerticalAlignment'], propertyExistsOnItem, propertyExistsOnOverlay)
}
if (propertyExistsOnItem) propertyValue = item[property]
else if (propertyExistsOnOverlay) propertyValue = this[property]
else {
@@ -229,6 +392,15 @@ export default class Overlay {
else return false
}
/**
* An overlay can have a reference to another file.
* That single overlays are more easy to maintain.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the reference as object or the property.
* @memberof Overlay
*/
_resolveReference(item, property) {
if (this._isReference(property)) {
let referencedProperty = property['@property']
@@ -281,18 +453,30 @@ export default class Overlay {
})
}
/**
* Creates a geographic for each item.
* By default it uses the regular routine but you may
* specify a factory for the defined type.
*
* @param {*} item
* @param {*} [informationCallback=null]
* @returns {GeoGraphics} - Returns the created geographics.
* @memberof Overlay
*/
createItem(item, informationCallback = null) {
let geographics
let type = this.getItemProperty(item, 'type')
/**
* Use a factory to draw the items, if a type is specified.
*/
if (this.type != 'auto') {
let geographicsFactory = Overlay.requestFactory(this.type)
if (type != 'auto') {
let geographicsFactory = Overlay.requestFactory(type)
if (!geographicsFactory)
console.error(
'Invalid Overlay Mode: ' +
this.type +
type +
'. Fallback to auto mode. Overlaymodes must be registeres beforehand. Valid modes are: ' +
Overlay.listFactories().join(',') +
'.'
@@ -318,84 +502,100 @@ export default class Overlay {
this._drawRoutine(geographics, item, informationCallback)
}
this._drawLabel(item, geographics)
let label = this._createLabel(item, geographics)
if (label != null && geographics != null) {
console.log(geographics)
geographics.graphics.addChild(label)
}
}
return geographics
}
_drawLabel(item, geographics) {
let label = this.getItemProperty(item, 'label')
if (label) {
let textStyle = this._gatherFontStyle(item)
let text = new PIXI.Text(label, textStyle)
/**
*
* Draws a label that is shown next to the item on the map.
*
* @param {*} item
* @returns {PIXI.Text} - Returns the created label or null.
* @memberof Overlay
*/
_createLabel(item) {
let enabled = this.getItemProperty(item, 'enabled')
let labelText = this.pickItemProperty(item, 'label', enabled)
let label = null
let labelScale = this.getItemProperty(item, 'labelScale')
text.scale.set(labelScale, labelScale)
if (labelText) {
let textStyle = this._gatherFontStyle(item)
label = new PIXI.Text(labelText, textStyle)
let labelScale = this.pickItemProperty(item, 'labelScale', enabled)
label.scale.set(labelScale, labelScale)
let position = new PIXI.Point()
let align = this.getItemProperty(item, 'labelAlignment')
let align = this.pickItemProperty(item, 'labelAlignment', enabled)
if (align == 'left');
else if (align == 'center') position.set(text.position.x - text.width / 2, text.position.y)
else if (align == 'right') position.set(text.position.x - text.width, text.position.y)
else if (align == 'center') position.set(label.position.x - label.width / 2, label.position.y)
else if (align == 'right') position.set(label.position.x - label.width, label.position.y)
else this._logPropertyNotImplemented('labelAlignment', align)
let verticalAlign = this.getItemProperty(item, 'labelVerticalAlignment')
let verticalAlign = this.pickItemProperty(item, 'labelVerticalAlignment', enabled)
if (verticalAlign == 'underneath') {
let size = this.getItemProperty(item, 'size')
let scale = this.getItemProperty(item, 'scale')
let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing')
let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y += size * scale + labelSpacing
} else if (verticalAlign == 'above') {
let size = this.getItemProperty(item, 'size')
let scale = this.getItemProperty(item, 'scale')
let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing')
let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y -= size * scale + text.height + labelSpacing
position.y -= size * scale + label.height + labelSpacing
} else this._logPropertyNotImplemented('labelVerticalAlignment', verticalAlign)
text.position.set(position.x, position.y)
geographics.graphics.addChild(text)
label.position.set(position.x, position.y)
}
return label
}
_gatherFontStyle(item) {
const enabled = this.getItemProperty(item, 'enabled')
return {
align: this.getItemProperty(item, 'labelMultiLineAlign'),
breakWords: this.getItemProperty(item, 'labelBreakWords'),
dropShadow: this.getItemProperty(item, 'labelDropShadow'),
dropShadowAlpha: this.getItemProperty(item, 'labelDropShadowAlpha'),
dropShadowAngle: this.getItemProperty(item, 'labelDropShadowAngle'),
dropShadowBlur: this.getItemProperty(item, 'labelDropShadowBlur'),
dropShadowColor: this.getItemProperty(item, 'labelDropShadowColor'),
dropShadowDistance: this.getItemProperty(item, 'labelDropShadowDistance'),
fontFamily: this.getItemProperty(item, 'fontFamily'),
fontSize: this.getItemProperty(item, 'fontSize'),
fontStyle: this.getItemProperty(item, 'fontStyle'),
fontWeight: this.getItemProperty(item, 'fontWeight'),
stroke: this.getItemProperty(item, 'labelStroke'),
strokeThickness: this.getItemProperty(item, 'labelStrokeThickness'),
wordWrap: this.getItemProperty(item, 'wordWrap'),
wordWrapWidth: this.getItemProperty(item, 'wordWrapWidth'),
fill: this.getItemProperty(item, 'labelFill'),
fillGradientType: this.getItemProperty(item, 'labelFillGradientType'),
fillGradientStops: this.getItemProperty(item, 'labelFillGradientStops'),
fontVariant: this.getItemProperty(item, 'fontVariant'),
leading: this.getItemProperty(item, 'labelLeading'),
letterSpacing: this.getItemProperty(item, 'letterSpacing'),
lineHeight: this.getItemProperty(item, 'labelLineHeight'),
lineJoin: this.getItemProperty(item, 'labelLineJoin'),
miterLimit: this.getItemProperty(item, 'labelMiterLimit'),
padding: this.getItemProperty(item, 'labelPadding'),
trim: this.getItemProperty(item, 'labelTrim'),
textBaseline: this.getItemProperty(item, 'textBaseline'),
whiteSpace: this.getItemProperty(item, 'labelWhiteSpace')
align: this.pickItemProperty(item, 'labelMultiLineAlign', enabled),
breakWords: this.pickItemProperty(item, 'labelBreakWords', enabled),
dropShadow: this.pickItemProperty(item, 'labelDropShadow', enabled),
dropShadowAlpha: this.pickItemProperty(item, 'labelDropShadowAlpha', enabled),
dropShadowAngle: this.pickItemProperty(item, 'labelDropShadowAngle', enabled),
dropShadowBlur: this.pickItemProperty(item, 'labelDropShadowBlur', enabled),
dropShadowColor: this.pickItemProperty(item, 'labelDropShadowColor', enabled),
dropShadowDistance: this.pickItemProperty(item, 'labelDropShadowDistance', enabled),
fontFamily: this.pickItemProperty(item, 'fontFamily', enabled),
fontSize: this.pickItemProperty(item, 'fontSize', enabled),
fontStyle: this.pickItemProperty(item, 'fontStyle', enabled),
fontWeight: this.pickItemProperty(item, 'fontWeight', enabled),
stroke: this.pickItemProperty(item, 'labelStroke', enabled),
strokeThickness: this.pickItemProperty(item, 'labelStrokeThickness', enabled),
wordWrap: this.pickItemProperty(item, 'wordWrap', enabled),
wordWrapWidth: this.pickItemProperty(item, 'wordWrapWidth', enabled),
fill: this.pickItemProperty(item, 'labelFill', enabled),
fillGradientType: this.pickItemProperty(item, 'labelFillGradientType', enabled),
fillGradientStops: this.pickItemProperty(item, 'labelFillGradientStops', enabled),
fontVariant: this.pickItemProperty(item, 'fontVariant', enabled),
leading: this.pickItemProperty(item, 'labelLeading', enabled),
letterSpacing: this.pickItemProperty(item, 'letterSpacing', enabled),
lineHeight: this.pickItemProperty(item, 'labelLineHeight', enabled),
lineJoin: this.pickItemProperty(item, 'labelLineJoin', enabled),
miterLimit: this.pickItemProperty(item, 'labelMiterLimit', enabled),
padding: this.pickItemProperty(item, 'labelPadding', enabled),
trim: this.pickItemProperty(item, 'labelTrim', enabled),
textBaseline: this.pickItemProperty(item, 'textBaseline', enabled),
whiteSpace: this.pickItemProperty(item, 'labelWhiteSpace', enabled)
}
}
@@ -440,25 +640,27 @@ export default class Overlay {
}
_createIcon(geographics, item) {
let icon = this.getItemProperty(item, 'icon')
let enabled = this.getItemProperty(item, 'enabled')
let icon = this.pickItemProperty(item, 'icon', enabled)
if (icon) {
if (icon.texture) {
let sprite = new PIXI.Sprite(icon.texture)
const iconAnchor = this.getItemProperty(item, 'iconAnchor')
const iconAnchor = this.pickItemProperty(item, 'iconAnchor', enabled)
sprite.anchor.set(iconAnchor.x, iconAnchor.y)
const iconScale = this.getItemProperty(item, 'iconScale')
const iconScale = this.pickItemProperty(item, 'iconScale', enabled)
if (iconScale) sprite.scale.set(iconScale, iconScale)
const iconOffset = this.getItemProperty(item, 'iconOffset')
const iconOffset = this.pickItemProperty(item, 'iconOffset', enabled)
if (iconOffset && iconOffset.x != null && iconOffset.y != null)
sprite.position.set(iconOffset.x, iconOffset.y)
const iconColor = this.getItemProperty(item, 'iconColor')
const iconColor = this.pickItemProperty(item, 'iconColor', enabled)
if (iconColor) sprite.tint = iconColor
const iconAlpha = this.getItemProperty(item, 'iconAlpha')
const iconAlpha = this.pickItemProperty(item, 'iconAlpha', enabled)
if (iconAlpha) sprite.alpha = iconAlpha
geographics.graphics.addChild(sprite)
@@ -468,28 +670,6 @@ export default class Overlay {
_createInformation(geographics, item, callback = null) {
if (item.information) {
/**
* SO: The overlay class is quite convenient.
* But managing the information creation solely inside this
* class restricts us massively.
*
* Maybe a restructuring would be good, that we can handle
* the creation of information with a callback and can adapt
* to any occuring situation.
*
* e.g.
*
* overlay.informationHandler((geo, type, item)=>{
* switch(type){
* case "popup":
* createPopup(item.information)
* break;
*
* ....
* }
* })
*/
geographics.graphics.interactive = true
const informationType = this.getItemProperty(item, 'informationType')
@@ -538,17 +718,21 @@ export default class Overlay {
_fill(geographics, item) {
const enabled = this.getItemProperty(item, 'enabled')
const color = enabled ? this.getItemProperty(item, 'color') : this.getItemProperty(item, 'disabledColor')
let alpha = enabled ? this.getItemProperty(item, 'fillAlpha') : this.getItemProperty(item, 'disabledAlpha')
const color = this.pickItemProperty(item, 'color', enabled)
const alpha = this.pickItemProperty(item, 'fillAlpha', enabled)
geographics.graphics.beginFill(color, alpha)
}
_drawPoint(item, informationCallback = null) {
const overlay = this
const enabled = this.getItemProperty(item, 'enabled')
let that = this
let geographic = new GeoPoint(item.location, {
onDraw: function() {
overlay._fill.call(overlay, this, item)
const size = overlay.getItemProperty(item, 'size') ? overlay.getItemProperty(item, 'size') : 0
const size = that.pickItemProperty(item, 'size', enabled)
this.graphics.drawCircle(0, 0, size)
overlay._drawRoutine.call(overlay, this, item, informationCallback)
}
@@ -563,10 +747,19 @@ export default class Overlay {
Overlay.Modes = { auto: null }
Overlay.createFactory = function(name, geographicsFactory) {
if (Overlay.Modes.hasOwnProperty(name))
console.warn(`The mode ${name} was already implemented and was overwritten!`)
Overlay.Modes[name] = geographicsFactory
/**
* Creates a factory for a specific type.
*
* @param {string} type - Name of the factory. When overlay items match the type, the factory is applied.
* @param {function} geographicsFactory - A function that is called with every item. Can be used to draw the geographics individually.
*
* @static
* @memberof {Overlay}
*/
Overlay.createFactory = function(type, geographicsFactory) {
if (Overlay.Modes.hasOwnProperty(type))
console.warn(`The mode ${type} was already implemented and was overwritten!`)
Overlay.Modes[type] = geographicsFactory
}
Overlay.requestFactory = function(name) {
+1 -1
View File
@@ -77,7 +77,7 @@
<body onload="Doctest.run()">
<h1>Projections</h1>
<p>
Projections are used on the mapdata to translate coordinates to pixelpositions. There are various
Projections are used on the map projection to translate coordinates to pixelpositions. There are various
projections that can be used. All implemented ones are showcased here.
</p>
+25
View File
@@ -258,6 +258,31 @@ export class Vector {
}
}
export class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform();
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
};
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y };
if (fontSize) pixiText.style.fontSize = fontSize;
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}
/**
* The EventHandler class is used to take care of a event based design
* pattern. Callbacks can subscribe to an event and these unknown sources
+24 -3
View File
@@ -18,7 +18,7 @@ export class InteractivePopup extends AbstractPopup {
* @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup?
* @param {boolean} [opts.closeButton=true] - Should a close button be displayed on the upper right corner?
* @param {boolean|string} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {object} [opts.button] - A Button object to be display on the lower right corner.
* @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner.
*/
@@ -73,7 +73,16 @@ export class InteractivePopup extends AbstractPopup {
// closeButton
//-----------------
if (this.opts.closeButton) {
let closeButton = PIXI.Sprite.from('../../assets/icons/close.png')
let url = null
if (typeof this.opts.closeButton === 'string' || this.opts.closeButton instanceof String) {
url = InteractivePopup.iconIsUrl(this.opts.closeButton)
? this.opts.closeButton
: `../../assets/icons/${this.opts.closeButton}.png`
} else {
url = '../../assets/icons/close.png'
}
let closeButton = PIXI.Sprite.from(url)
closeButton.width = this.headerStyle.fontSize
closeButton.height = closeButton.width
closeButton.tint = this.theme.color2
@@ -172,6 +181,18 @@ export class InteractivePopup extends AbstractPopup {
return size
}
/**
* Tests if an icon string is an url.
*
* @private
* @static
* @param {string} url - The url to test.
* @return {boolean} true if the url is an url to an image.
*/
static iconIsUrl(url) {
return /\.(png|svg|gif|jpg|jpeg|tif|tiff)$/i.test(url)
}
}
/**
@@ -197,7 +218,7 @@ export default class Popup extends InteractivePopup {
*
* @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeButton=false] - Should a close button be displayed on the upper right corner?
* @param {boolean|string} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {number} [opts.minWidth=0] - The minimum width of the popup.
* @param {number} [opts.minHeight=0] - The minimum height of the popup.
*/
+102
View File
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mousetrap/1.6.5/mousetrap.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script class="doctest">
const app = new PIXIApp({
view: canvas,
fpsLogging: true,
transparent: false
})
app.setup()
app.run()
const textProps = {fontSize: 72, fill: 0xffffff}
Mousetrap.bind(['1', '2', '3', '4', '5'], (event, key) => {
app.stage.removeChildren()
let sprite = null
let label = null
if (key === '1') {
sprite = PIXI.Sprite.from('resolution/image-4k.jpg')
label = `4096 * 2732 ~ 4K`
} else if (key === '2') {
sprite = PIXI.Sprite.from('resolution/image-8k.jpg')
label = `7952 * 5304 ~ 8K`
} else if (key === '3') {
const texture = PIXI.Texture.from('resolution/video-1k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `1920 * 1080 = 1K`
} else if (key === '4') {
const texture = PIXI.Texture.from('resolution/video-4k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `3840 * 2160 = 4K`
} else if (key === '5') {
const texture = PIXI.Texture.from('resolution/video-8k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `7680 * 4320 ~ 8K`
}
app.stage.addChild(sprite)
// texts
//--------------------
const shortcuts = new PIXI.Text(`Press 1 - 5 to change Sprites`, textProps)
shortcuts.x = 100
shortcuts.y = 100
const width = new PIXI.Text(`Width: ${app.renderer.width}`, textProps)
width.x = 100
width.y = shortcuts.y + shortcuts.height * 2
const height = new PIXI.Text(`Height: ${app.renderer.height}`, textProps)
height.x = 100
height.y = width.y + width.height + 10
const resolution = new PIXI.Text(`Resolution: ${app.renderer.resolution}`, textProps)
resolution.x = 100
resolution.y = height.y + height.height + 10
const devicePixelRatio = new PIXI.Text(`devicePixelRatio: ${window.devicePixelRatio}`, textProps)
devicePixelRatio.x = 100
devicePixelRatio.y = resolution.y + resolution.height + 10
const text = new PIXI.Text(label, textProps)
text.x = 100
text.y = devicePixelRatio.y + devicePixelRatio.height * 2
app.stage.addChild(shortcuts, width, height, resolution, devicePixelRatio, text)
})
Mousetrap.trigger('1')
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 MiB

+133
View File
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Scatter Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body>
<canvas id="canvas" />
<script>
const app = new PIXIApp({
view: canvas,
roundPixels: true,
resolution: 2
})
.setup()
.run()
app.loadSprites(
[
'../examples/front__1_dpi75.png',
'../examples/front__1_dpi150.png',
'../examples/front__1_dpi300.png',
'../examples/front__1_dpi600.png',
'../examples/front__1_dpi1200.png'
],
sprites => {
app.scatterContainerFront = new ScatterContainer(app.renderer, { app })
app.scatterContainerBack = new ScatterContainer(app.renderer, { app })
app.scene.addChild(app.scatterContainerBack)
app.scene.addChild(app.scatterContainerFront)
let startScale = 0.5
// sprite1
//--------------------
let sprite1 = sprites.get('../examples/front__1_dpi75.png')
sprite1.interactive = true
let scatter1 = new DisplayObjectScatter(sprite1, app.renderer, {
x: 20,
y: 20,
startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerBack.addChild(sprite1)
// sprite2
//--------------------
let sprite2 = sprites.get('../examples/front__1_dpi150.png')
sprite2.interactive = true
let scatter2 = new DisplayObjectScatter(sprite2, app.renderer, {
x: 320,
y: 20,
startScale: 0.5 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite2)
// sprite3
//--------------------
let sprite3 = sprites.get('../examples/front__1_dpi300.png')
sprite3.interactive = true
let scatter3 = new DisplayObjectScatter(sprite3, app.renderer, {
x: 620,
y: 20,
startScale: 0.25 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerBack.addChild(sprite3)
// sprite4
//--------------------
let sprite4 = sprites.get('../examples/front__1_dpi600.png')
sprite4.interactive = true
let scatter4 = new DisplayObjectScatter(sprite4, app.renderer, {
x: 920,
y: 20,
startScale: 0.125 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite4)
// sprite5
//--------------------
let sprite5 = sprites.get('../examples/front__1_dpi1200.png')
sprite5.interactive = true
let scatter5 = new DisplayObjectScatter(sprite5, app.renderer, {
x: 1220,
y: 20,
startScale: 0.0625 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite5)
// renderTexture
//--------------------
sprites.forEach(value => {
const matrix = new PIXI.Matrix()
matrix.translate(-value.x, -value.y)
const texture = PIXI.RenderTexture.create({ width: value.width, height: value.height, resolution: 2 })
app.renderer.render(value, texture, true, matrix)
const sprite = new PIXI.Sprite(texture)
sprite.interactive = true
new DisplayObjectScatter(sprite, app.renderer, {
x: value.x,
y: 400,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite)
})
},
{ resolutionDependent: false }
)
</script>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Scatter Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body>
<canvas id="canvas2" class="interactive">Canvas not supported</canvas>
<script>
class ScatterApp extends PIXIApp {
setup() {
super.setup()
// Obey order in which ScatterContainer are created because the
// InteractionMapper register event handlers in a first come first serve
// order
this.scatterContainerFront = new ScatterContainer(this.renderer, { app: this })
this.scatterContainerBack = new ScatterContainer(this.renderer, { app: this })
// Note that the addChild order is different because later children
// are placed in front of earlier children.
this.scene.addChild(this.scatterContainerBack)
this.scene.addChild(this.scatterContainerFront)
// Add the queen to ScatterContainer one
let sprite1 = PIXI.Sprite.from('../examples/test-1280-720.png')
sprite1.interactive = true
let scatter1 = new DisplayObjectScatter(sprite1, this.renderer, {
x: 20,
y: 40,
startScale: 0.5,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerBack.addChild(sprite1)
// Add the king to ScatterContainer two
let sprite2 = PIXI.Sprite.from('../examples/test-1280-720.png')
sprite2.interactive = true
let scatter2 = new DisplayObjectScatter(sprite2, this.renderer, {
x: 280,
y: 40,
startScale: 1,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerFront.addChild(sprite2)
// Add the queen to ScatterContainer one
let sprite3 = PIXI.Sprite.from('../examples/test-800-450.png')
sprite3.interactive = true
let scatter3 = new DisplayObjectScatter(sprite3, this.renderer, {
x: 140,
y: 400,
startScale: 0.5,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerBack.addChild(sprite3)
// Add the king to ScatterContainer two
let sprite4 = PIXI.Sprite.from('../examples/test-800-450.png')
sprite4.interactive = true
let scatter4 = new DisplayObjectScatter(sprite4, this.renderer, {
x: 280,
y: 400,
startScale: 1,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerFront.addChild(sprite4)
return this
}
}
const scatterApp = new ScatterApp({
view: canvas2,
roundPixels: true,
antialias: true
})
.setup()
.run()
</script>
</body>
</html>
-1
View File
@@ -315,7 +315,6 @@ export class DisplayObjectScatter extends AbstractScatter {
if (y != null) this.y = y
}
getWorldScatter() {
return this
}
+15 -11
View File
@@ -80,7 +80,9 @@ export default class Stylus extends PIXI.Graphics {
backgroundFill = 0xffffff,
colorAlpha = 1,
captureEvents = true,
acceptMouseEvents = true
acceptMouseEvents = true,
lineWidth = 16,
minStrokeLength = 4
} = {}) {
super()
this.activePointers = 0
@@ -99,7 +101,8 @@ export default class Stylus extends PIXI.Graphics {
this.undoCommandStack = []
this.strokes = []
this.stroke = []
this.minStrokeLength = 4
this.lineWidth = lineWidth
this.minStrokeLength = minStrokeLength
if (captureEvents) this.registerEventHandler(acceptMouseEvents)
this.drawBackground()
}
@@ -273,7 +276,8 @@ export default class Stylus extends PIXI.Graphics {
pressure: event.pressure || null,
tiltX: this.tiltX,
tiltY: this.tiltY,
color: this.color
color: this.color,
lineWidth: this.tiltToLineWidth(this.tiltY)
}
return desc
}
@@ -297,7 +301,7 @@ export default class Stylus extends PIXI.Graphics {
}
tiltToLineWidth(value) {
return 16 //Math.round(Math.abs(value / 10) + 1)
return this.lineWidth * Math.round(Math.abs(value / 10) + 1)
}
drawStroke(stroke) {
@@ -305,11 +309,11 @@ export default class Stylus extends PIXI.Graphics {
let start = stroke[0]
this.beginFill(0, 0)
this.moveTo(start.x, start.y)
let lineWidth = this.tiltToLineWidth(start.tiltY)
let lineWidth = start.lineWidth
this.lineStyle(lineWidth, start.color, this.colorAlpha)
for (let i = 1; i < stroke.length; i++) {
let info = stroke[i]
let lw = this.tiltToLineWidth(info.tiltY)
let lw = info.lineWidth
if (lw != lineWidth) {
lineWidth = lw
this.lineStyle(lineWidth, info.color, this.colorAlpha)
@@ -328,7 +332,7 @@ export default class Stylus extends PIXI.Graphics {
drawStrokes() {
this.drawBackground()
this.lineStyle(1.0, 0xff0000, 1)
this.lineStyle(this.lineWidth, 0xff0000, 1)
for (let stroke of this.iterStrokes()) {
this.drawStroke(stroke)
}
@@ -356,17 +360,17 @@ export default class Stylus extends PIXI.Graphics {
}
normalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info
let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x /= this.wantedWidth
y /= this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color }
return { x, y, pressure, tiltX, tiltY, color, lineWidth }
}
denormalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info
let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x = x * this.wantedWidth
y = y * this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color }
return { x, y, pressure, tiltX, tiltY, color, lineWidth }
}
// Convert strokes into an object that can be stored in an Indexed DB.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

+2 -2
View File
@@ -34,8 +34,8 @@ oncontextmenu="PopupMenu.open(cmds,
oncontextmenu="PopupMenu.open(cmds, {x: event.offsetX, y: event.offsetY}, { parent: example, keepWithin: example}); return false;"></div>
<script class="doctest">
let cmds = {
"Start": () => alert("Start"),
"Stop": () => alert("Stop")
"Start": (event, key) => {console.log("start", event, key); alert(key) },
"Stop": (event, key) => alert(key)
}
let app = new App()
let popup = new PopupMenu({ parent: example, fontSize: "2em", autoClose: false })
+12 -6
View File
@@ -83,10 +83,10 @@ export default class PopupMenu extends Popup {
Elements.addClass(item, 'popupMenuItem')
this.items[key] = item
item.onclick = event => {
this.perform(key)
this.perform(event, key)
}
item.ontap = event => {
this.perform(key)
this.perform(event, key)
}
item.onmouseover = event => {
this.over(event, key)
@@ -105,16 +105,22 @@ export default class PopupMenu extends Popup {
}
/** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key.
*/
perform(key) {
perform(event, key) {
let func = this.commands[key]
if (this.autoClose) {
this.close()
}
setTimeout(() => {
func.call()
}, 20)
setTimeout(
(event, key) => {
func(event, key)
},
20,
event,
key
)
}
/** Update the menu item denoted by key.
+19 -14
View File
@@ -44,8 +44,7 @@ const END = 'onEnd'
*/
export class ScatterEvent extends BaseEvent {
constructor(
target,
{ translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
target, { translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
) {
super('scatterTransformed', { target: target })
this.translate = translate
@@ -117,6 +116,9 @@ class Throwable {
let t = performance.now()
let dt = t - this.lastframe
this.lastframe = t
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1
if (dt > 0) {
// Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the
@@ -124,13 +126,14 @@ class Throwable {
let velocity = {
t: t,
dt: dt,
dx: delta.x / delta.number,
dy: delta.y / delta.number
dx: delta.x / number,
dy: delta.y / number
}
this.velocities.push(velocity)
while (this.velocities.length > buffer) {
this.velocities.shift()
}
}
}
@@ -378,7 +381,7 @@ export class AbstractScatter extends Throwable {
let delta = interaction.delta()
if (delta != null) {
this.addVelocity(delta)
let rotate = delta.rotate
let zoom = delta.zoom
if (this.maxRotation != null) {
@@ -399,6 +402,8 @@ export class AbstractScatter extends Throwable {
}
this.transform(delta, zoom, rotate, delta.about)
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
if (zoom != 1) this.interactionAnchor = delta.about
}
}
@@ -578,6 +583,7 @@ export class AbstractScatter extends Throwable {
}
_move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0
this.y += this.movableX ? delta.y : 0
}
@@ -615,14 +621,14 @@ export class AbstractScatter extends Throwable {
let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom)
let extra = Points.subtract(newOrigin, origin)
let offset = Points.subtract(anchor, origin)
this._move(offset)
const anchorOffset = Points.subtract(anchor, origin)
// this._move(offset)
this.scale = newScale
this.rotation += rotate
offset = Points.negate(offset)
let offset = Points.negate(anchorOffset)
offset = Points.add(offset, extra)
offset = Points.add(offset, translate)
this._move(offset)
this._move(Points.add(anchorOffset, offset))
delta.x += extra.x
delta.y += extra.y
@@ -906,8 +912,7 @@ export class DOMScatterContainer {
* @param {DOM node} debugCanvas - Shows debug infos about touches if not null
*/
constructor(
element,
{
element, {
stopEvents = 'auto',
claimEvents = true,
useCapture = true,
@@ -1071,8 +1076,7 @@ export class DOMScatterContainer {
export class DOMScatter extends AbstractScatter {
constructor(
element,
container,
{
container, {
startScale = 1.0,
minScale = 0.1,
maxScale = 1.0,
@@ -1392,7 +1396,8 @@ export class DOMScatter extends AbstractScatter {
}
this._x = x
this._y = y
TweenLite.set(this.element, { x: x, y: y })
// this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y })
}
resizeAfterTransform(zoom) {
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 55 KiB

+11 -2
View File
@@ -396,9 +396,18 @@ export class Points {
// Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let dx = 0
let dy = 0
if (p.x < r.x) dx = p.x - r.x
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width)
if (p.y < r.y) dy = p.y - r.y
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height)
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
}
static fromPageToNode(element, p) {
+12686 -1167
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "iwmlib",
"version": "1.1.0",
"version": "1.1.2",
"description": "An Open Source library for multi-touch, WebGL powered applications.",
"main": "index.js",
"directories": {
@@ -27,10 +27,10 @@
"author": "",
"license": "LGPL-3.0-or-later",
"devDependencies": {
"@pixi/jsdoc-template": "^2.4.3",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-prettier": "^3.1.1",
"@pixi/jsdoc-template": "^2.5.1",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-prettier": "^3.1.2",
"fs-extra": "^8.0.1",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
@@ -48,13 +48,13 @@
"gsap": "^2.1.3",
"hammerjs": "^2.0.8",
"optimal-select": "^4.0.1",
"pixi-compressed-textures": "^2.0.3",
"pixi-compressed-textures": "^2.0.5",
"pixi-ease": "^2.4.2",
"pixi-filters": "^3.0.3",
"pixi-particles": "^4.2.0",
"pixi-projection": "^0.3.5",
"pixi-viewport": "^4.3.3",
"pixi.js": "^5.2.0",
"pixi-projection": "^0.3.7",
"pixi-viewport": "^4.8.0",
"pixi.js": "^5.2.1",
"propagating-hammerjs": "^1.4.7"
}
}