Added the maps-module to the iwmlib.
Migrated a ot of the content from the tuesch to the iwmlib. This is before the decoupeling of the layers.
This commit is contained in:
@@ -0,0 +1,482 @@
|
||||
import { Points } from '../../utils.js'
|
||||
|
||||
export class ObjectUtils {
|
||||
/**
|
||||
* Finds the previous key in an object.
|
||||
*
|
||||
* @param {string} targetKey - The target key, of whom the predecessor is wanted.
|
||||
*/
|
||||
static findPredecessor(targetKey, obj) {
|
||||
// Initialize pointers ...
|
||||
let prev = null
|
||||
let first = null
|
||||
// ... and a hit flag.
|
||||
let hit = false
|
||||
|
||||
//Iterate over all available items.
|
||||
for (let key in obj)
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (!hit) {
|
||||
//Assign both values,
|
||||
//while not hit
|
||||
prev = first
|
||||
first = key
|
||||
|
||||
//When hit, set flag.
|
||||
if (key == targetKey) {
|
||||
hit = true
|
||||
if (prev) return prev //If it was not hit on the first item, we can return prev.
|
||||
}
|
||||
} else {
|
||||
//Otherwise, when first item was hit, we iterate over the entire object.
|
||||
first = key
|
||||
}
|
||||
}
|
||||
|
||||
return first
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the successing key in an object.
|
||||
*
|
||||
* @param {string} targetKey - The key of whom the successor key should be found.
|
||||
* @param {object} obj - The object, that is scanned for the successor.
|
||||
*/
|
||||
static findSuccessor(targetKey, obj) {
|
||||
let first = null
|
||||
let next = false
|
||||
|
||||
for (let key in obj)
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (!first) first = key
|
||||
|
||||
if (next) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (targetKey == key) {
|
||||
next = true
|
||||
}
|
||||
}
|
||||
|
||||
return first
|
||||
}
|
||||
|
||||
static fromPath(obj, path, separator = '.') {
|
||||
let arr = path.split(separator)
|
||||
let result = obj
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (result[arr[i]] !== null) {
|
||||
result = result[arr[i]]
|
||||
} else return null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export class PathUtils {
|
||||
static fixTrailingSlash(url) {
|
||||
return url.replace(/\/?$/, '/')
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeUtils {
|
||||
static minutesToMs(minutes) {
|
||||
return TimeUtils.minutesToSeconds(minutes) * 1000
|
||||
}
|
||||
|
||||
static minutesToSeconds(minutes) {
|
||||
return minutes * 60
|
||||
}
|
||||
}
|
||||
|
||||
export class StringUtils {
|
||||
/* Used from: https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript */
|
||||
static capitalize(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
export class GeometryUtils {
|
||||
static RandomRange(min, max) {
|
||||
return GeometryUtils.toRadians(Math.random() * (max - min) + min)
|
||||
}
|
||||
|
||||
static RandomSign() {
|
||||
return Math.sign(Math.random() - 0.5)
|
||||
}
|
||||
|
||||
static toDegrees(radians) {
|
||||
return (radians * 180) / Math.PI
|
||||
}
|
||||
|
||||
static toRadians(degrees) {
|
||||
return (degrees / 180) * Math.PI
|
||||
}
|
||||
|
||||
/*
|
||||
Recursion is buggy.
|
||||
*/
|
||||
static subdivide(polygon, factor = 0.5, n = 0) {
|
||||
console.log(polygon.length, factor)
|
||||
let points = []
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
let prev = i - 1 < 0 ? polygon[polygon.length - 1] : polygon[i - 1]
|
||||
let next = i + 1 < polygon.length ? polygon[i + 1] : polygon[0]
|
||||
let current = polygon[i]
|
||||
points.push(GeometryUtils.linearInterpolation(prev, current, factor))
|
||||
points.push(GeometryUtils.linearInterpolation(current, next, 1 - factor))
|
||||
}
|
||||
if (n > 0) {
|
||||
n--
|
||||
points = GeometryUtils.subdivide(points, n)
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
static linearInterpolation(first, second, n) {
|
||||
let point = new PIXI.Point()
|
||||
|
||||
point.x = first.x * n + second.x * (1 - n)
|
||||
point.y = first.y * n + second.y * (1 - n)
|
||||
return point
|
||||
}
|
||||
}
|
||||
|
||||
export class Matrix {
|
||||
constructor(a, b, d, e, c = 0, f = 0) {
|
||||
Object.assign(this, {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
g: 0,
|
||||
h: 0,
|
||||
i: 1
|
||||
})
|
||||
}
|
||||
|
||||
static Transpose(m) {
|
||||
let first = ['b', 'c', 'f']
|
||||
let second = ['d', 'g', 'h']
|
||||
|
||||
for (let i = 0; i < first.length; i++) {
|
||||
let tmp = m[first[i]]
|
||||
first[i] = second[i]
|
||||
second[i] = tmp
|
||||
}
|
||||
}
|
||||
|
||||
static Rotation(deg) {
|
||||
let rad = (2 * Math.PI * deg) / 180
|
||||
return new Matrix(Math.cos(rad), -Math.sin(rad), Math.sin(rad), Math.cos(rad))
|
||||
}
|
||||
|
||||
static Transform(x, y) {
|
||||
return new Matrix(1, 0, 0, 1, x, y)
|
||||
}
|
||||
|
||||
static MultiplyPoint(m, p) {
|
||||
x = p.x * m.a + m.b * p.y + m.c * 1
|
||||
y = p.x * m.d + p.y * m.e + m.f * 1
|
||||
|
||||
return {
|
||||
x,
|
||||
y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DomUtils {
|
||||
static getCenter(element) {
|
||||
let x = (element.clientLeft + element.clientWidth) / 2
|
||||
let y = (element.clientTop + element.clientHeight) / 2
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
static getTransformedPosition(element) {
|
||||
let mat = this.getCSSMatrix(element)
|
||||
return { x: mat[4], y: mat[5] }
|
||||
}
|
||||
|
||||
static getCSSMatrix(element) {
|
||||
let matrix = window.getComputedStyle(element).transform
|
||||
|
||||
if (matrix == 'none') {
|
||||
return [1, 0, 0, 1, 0, 0]
|
||||
} else {
|
||||
const pre = 'matrix('
|
||||
let values = matrix.substring(pre.length, matrix.length - 1).split(',')
|
||||
for (let i in values) {
|
||||
values[i] = parseFloat(values[i])
|
||||
}
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
static positionOnElement(element, position) {
|
||||
const matrix = this.getCSSMatrix(element)
|
||||
|
||||
const inX = element.offsetWidth * (position.x - 0.5)
|
||||
const inY = element.offsetHeight * (position.y - 0.5)
|
||||
|
||||
let outX = matrix[0] * inX + matrix[2] * inY + matrix[4]
|
||||
let outY = matrix[1] * inX + matrix[3] * inY + matrix[5]
|
||||
|
||||
let out = {
|
||||
x: outX,
|
||||
y: outY
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
static applyTransform(target, transformedPosition, size = 0) {
|
||||
return {
|
||||
x: target.offsetLeft + target.offsetWidth / 2 + transformedPosition.x - size / 2,
|
||||
y: target.offsetTop + target.offsetHeight / 2 + transformedPosition.y - size / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Vector {
|
||||
static length(vector) {
|
||||
return Math.sqrt(vector.x * vector.x + vector.y * vector.y)
|
||||
}
|
||||
|
||||
static normalize(vector) {
|
||||
return Points.multiplyScalar(vector, 1 / this.length(vector))
|
||||
}
|
||||
|
||||
static scaleTo(vector, length) {
|
||||
let normalized = this.normalize(vector)
|
||||
return Points.multiplyScalar(normalized, length)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The EventHandler class is used to take care of a event based design
|
||||
* pattern. Callbacks can subscribe to an event and these unknown sources
|
||||
* get notified whenever the event changes.
|
||||
*
|
||||
* @export
|
||||
* @class EventHandler
|
||||
*/
|
||||
export class EventHandler {
|
||||
/**
|
||||
* Creates an instance of EventHandler.
|
||||
* @param {any} name
|
||||
* @param {any} [{
|
||||
* listeners = [] - With the listnerers parameter the user can specify a function, array of functions or null (no function - useful when used in constructor with optional parameter).
|
||||
* }={}]
|
||||
* @memberof EventHandler
|
||||
*/
|
||||
constructor(name, { listeners = [] } = {}) {
|
||||
this.name = name
|
||||
this.listeners = []
|
||||
this.onces = []
|
||||
|
||||
/**
|
||||
* One may initialize the eventListener using a parameter
|
||||
* that is either passed or null.
|
||||
*/
|
||||
|
||||
if (listeners == null) {
|
||||
// Null is a valid value as the EventHandler assumes no listener is passed on purpose.
|
||||
// This is useful, when a default parameter is passed as null.
|
||||
} else if (Array.isArray(listeners)) this.listeners = listeners
|
||||
else if (typeof listeners == 'function') {
|
||||
this.listeners = []
|
||||
this.add(listeners)
|
||||
} else {
|
||||
console.warn(
|
||||
"The provided 'listeners' is neither an Array of functions, nor a function. No eventcallback was added!",
|
||||
listeners,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
addMultiple(...callbacks) {
|
||||
for (let callback of callbacks) {
|
||||
this.listeners.push(callback)
|
||||
}
|
||||
}
|
||||
|
||||
add(callback) {
|
||||
this.listeners.push(callback)
|
||||
return callback
|
||||
}
|
||||
|
||||
once(callback) {
|
||||
this.onces.push(callback)
|
||||
}
|
||||
|
||||
remove(func) {
|
||||
for (const [idx, listener] of this.listeners.entries()) {
|
||||
if (listener === func) {
|
||||
this.listeners.splice(idx, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
empty() {
|
||||
this.listeners = []
|
||||
}
|
||||
|
||||
call(context, ...args) {
|
||||
this.listeners.forEach(listener => listener.call(context, ...args))
|
||||
this.onces.forEach(listener => listener.call(context, ...args))
|
||||
this.onces = []
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.listeners.length + this.onces.length
|
||||
}
|
||||
}
|
||||
|
||||
export class Dom {
|
||||
/**
|
||||
* Popups should be displayed right over the text.
|
||||
* Normally we would expect the popup to appear right over
|
||||
* the center of the text. A problem in HTML is, that it's hard
|
||||
* to determine the position of a text link, when it has a line-break
|
||||
* in it.
|
||||
*
|
||||
* This function solves this problem in the (so far) only possible way.
|
||||
*
|
||||
* 1. It removes the link from the dom tree.
|
||||
* 2. It adds an empty copy A of the link to the dom tree. (Copy is important, as the same styles have to be applied.)
|
||||
* 3. The contents of the link are added one by one to A.
|
||||
* 4. If the resulting boundingRect is bigger than the previous one, a line break is detected.
|
||||
* 5. The old line is tested, if the point was inside that boundingBox. If so save that bounding box (Goto: 7), else:
|
||||
* 6. Saves the content to a preceding clone B. And repeats from 3.
|
||||
* 7. Replace A with the initial content
|
||||
* 8. Return the found BoundingBox. If none found. Return the last bounding box.
|
||||
*/
|
||||
static getTextHitRect(link, point) {
|
||||
// We cannot use it as it produces axis aligned bounding boxes
|
||||
/* if (true) {
|
||||
let rects = link.getClientRects()
|
||||
let target = null
|
||||
for (let [idx, rect] of Object.entries(rects)) {
|
||||
target = rect
|
||||
if (Rect.contains(rect, point))
|
||||
break
|
||||
}
|
||||
return target
|
||||
|
||||
} else {*/
|
||||
let processedText = link.cloneNode(true)
|
||||
let content = processedText.innerHTML
|
||||
let words = content.split(/ /g)
|
||||
processedText.innerHTML = ''
|
||||
event.target.innerHTML = ''
|
||||
// let lineRect = event.target.getBoundingClientRect()
|
||||
let local = Points.fromPageToNode(event.target.parentNode, point)
|
||||
console.log(local)
|
||||
|
||||
let target = event.target
|
||||
let height = 0
|
||||
|
||||
while (words.length > 0) {
|
||||
let word = words.pop()
|
||||
target.innerHTML += word + ' '
|
||||
if (target.height != height) {
|
||||
// New line was reached.
|
||||
console.log('NEW LINE WAS REACHED!')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 100,
|
||||
bottom: 100,
|
||||
width: 100,
|
||||
height: 100
|
||||
}
|
||||
|
||||
// let total = words.length
|
||||
// while (words.length > 0) {
|
||||
// let lastRect = lineRect
|
||||
// let lastContent = event.target.innerHTML
|
||||
// let added = words.length == total ? "" : " "
|
||||
// added += words.shift()
|
||||
// event.target.innerHTML += added
|
||||
// lineRect = event.target.getBoundingClientRect()
|
||||
|
||||
// // When new line or last line:
|
||||
// if (lineRect.height != lastRect.height) {
|
||||
// //Reconstructure last line.
|
||||
// event.target.innerHTML = lastContent
|
||||
|
||||
// //Create Rect from last line.
|
||||
// lineRect = event.target.getBoundingClientRect()
|
||||
// rects.push(lineRect)
|
||||
|
||||
// //Copy last line content to processed text.
|
||||
// processedText.innerHTML += lastContent
|
||||
|
||||
// // Create content of new line.
|
||||
// event.target.innerHTML = added
|
||||
|
||||
// if (!processedText.parentNode) event.target.parentNode.insertBefore(processedText, event.target)
|
||||
|
||||
// if (Rect.contains(lineRect, point)) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (words.length == 0) {
|
||||
// lineRect = event.target.getBoundingClientRect()
|
||||
// processedText.innerHTML += event.target.innerHTML
|
||||
// rects.push(lineRect)
|
||||
// }
|
||||
// }
|
||||
|
||||
// event.target.innerHTML = content
|
||||
// if (processedText.parentNode) processedText.parentNode.removeChild(processedText)
|
||||
|
||||
// return lineRect
|
||||
// }
|
||||
}
|
||||
|
||||
static printDomRect(rect, { color = 'red', pad = 0, parent = document.body }) {
|
||||
let element = document.createElement('div')
|
||||
Object.assign(element.style, {
|
||||
padding: pad + 'px',
|
||||
borderColor: color,
|
||||
borderWidth: '2px',
|
||||
borderStyle: 'solid',
|
||||
top: rect.top + 'px',
|
||||
left: rect.left + 'px',
|
||||
width: rect.width + 'px',
|
||||
height: rect.height + 'px',
|
||||
position: 'absolute',
|
||||
zIndex: 10000,
|
||||
opacity: 0.3
|
||||
})
|
||||
parent.appendChild(element)
|
||||
}
|
||||
|
||||
static printDomPoint(point, { color = 'red', parent = document.body }) {
|
||||
let element = document.createElement('div')
|
||||
Object.assign(element.style, {
|
||||
position: 'absolute',
|
||||
top: point.y + 'px',
|
||||
left: point.x + 'px',
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
backgroundColor: color,
|
||||
zIndex: 10000
|
||||
})
|
||||
parent.appendChild(element)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user