Merge branch 'master' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib
This commit is contained in:
Vendored
+421
@@ -13825,6 +13825,426 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
class StylusCommand extends Object {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
do(stylus) {
|
||||
stylus.commandStack.push(this);
|
||||
}
|
||||
|
||||
undo(stylus) {
|
||||
stylus.undoCommandStack.push(this);
|
||||
}
|
||||
|
||||
redo(stylus) {
|
||||
this.do(stylus);
|
||||
}
|
||||
}
|
||||
|
||||
class StrokeCommand extends StylusCommand {
|
||||
|
||||
constructor(stroke) {
|
||||
super();
|
||||
this.stroke = stroke;
|
||||
}
|
||||
|
||||
do(stylus) {
|
||||
if (this.stroke.length > 0) {
|
||||
super.do(stylus);
|
||||
stylus.stroke = [];
|
||||
stylus.strokes.push(this.stroke);
|
||||
stylus.redraw();
|
||||
stylus.changed();
|
||||
}
|
||||
}
|
||||
|
||||
undo(stylus) {
|
||||
if (this.stroke.length > 0) {
|
||||
super.undo(stylus);
|
||||
stylus.strokes.pop();
|
||||
stylus.redraw();
|
||||
stylus.changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClearCommand extends StylusCommand {
|
||||
|
||||
do(stylus) {
|
||||
// Clears the command stack
|
||||
stylus.commandStack = [];
|
||||
super.do(stylus);
|
||||
this.strokes = stylus.strokes;
|
||||
stylus.stroke = [];
|
||||
stylus.strokes = [];
|
||||
stylus.redraw();
|
||||
stylus.changed();
|
||||
}
|
||||
|
||||
undo(stylus) {
|
||||
//super.undo(stylus) // Clear all is not redoable
|
||||
stylus.stroke = [];
|
||||
stylus.strokes = this.strokes;
|
||||
stylus.redraw();
|
||||
stylus.changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Stylus extends PIXI.Graphics {
|
||||
|
||||
constructor({ width = window.innerWidth,
|
||||
height = window.innerHeight,
|
||||
interactive = true,
|
||||
color = 0x000000,
|
||||
tiltX = 0,
|
||||
tiltY = 0,
|
||||
backgroundAlpha = 1,
|
||||
backgroundFill = 0xFFFFFF,
|
||||
colorAlpha = 1,
|
||||
captureEvents = true,
|
||||
acceptMouseEvents = true } = {}) {
|
||||
super();
|
||||
this.activePointers = 0;
|
||||
this.wantedWidth = width;
|
||||
this.wantedHeight = height;
|
||||
this.backgroundAlpha = backgroundAlpha;
|
||||
this.backgroundFill = backgroundFill;
|
||||
this.colorAlpha = colorAlpha;
|
||||
this.color = color;
|
||||
this.interactive = interactive;
|
||||
this.debug = false;
|
||||
this.tiltX = tiltX; // degrees -90 ... 90
|
||||
this.tiltY = tiltY; // degrees -90 ... 90
|
||||
this.captureEvents = captureEvents;
|
||||
this.commandStack = [];
|
||||
this.undoCommandStack = [];
|
||||
this.strokes = [];
|
||||
this.stroke = [];
|
||||
this.minStrokeLength = 4;
|
||||
if (captureEvents)
|
||||
this.registerEventHandler(acceptMouseEvents);
|
||||
this.drawBackground();
|
||||
}
|
||||
|
||||
drawBackground() {
|
||||
this.clear();
|
||||
this.beginFill(this.backgroundFill, this.backgroundAlpha);
|
||||
this.drawRect(0, 0, this.wantedWidth, this.wantedHeight);
|
||||
this.endFill();
|
||||
}
|
||||
|
||||
touchToPoint(t) {
|
||||
return { x: t.clientX, y: t.clientY }
|
||||
}
|
||||
|
||||
isStylusPointer(event) {
|
||||
let identifier = event.data.identifier;
|
||||
if (typeof (event.data.originalEvent.changedTouches) !== 'undefined') {
|
||||
for (let touch of event.data.originalEvent.changedTouches) {
|
||||
if (touch.identifier === identifier && touch.touchType === 'stylus') {
|
||||
this.tiltX = Angle.radian2degree(touch.azimuthAngle);
|
||||
this.tiltY = 90.0 - Angle.radian2degree(touch.altitudeAngle);
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
// UO: Not tested since the Sprot delivered "mouse" events to Chrome
|
||||
if (event.data.originalEvent.pointerType === 'pen') {
|
||||
this.tiltX = event.data.originalEvent.tiltX;
|
||||
this.tiltY = event.data.originalEvent.tiltY;
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
isStylusTouch(event) {
|
||||
let identifier = event.data.identifier;
|
||||
if (typeof (event.data.originalEvent.changedTouches) !== 'undefined') {
|
||||
for (let touch of event.data.originalEvent.changedTouches) {
|
||||
if (touch.identifier === identifier && touch.pointerType === 'touch') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
getPointerID(event) {
|
||||
let identifier = event.data.identifier;
|
||||
for (let touch of event.data.originalEvent.changedTouches) {
|
||||
if (touch.identifier === identifier) {
|
||||
return touch.pointerId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
singlePointer() {
|
||||
return this.activePointers == 1
|
||||
}
|
||||
|
||||
registerEventHandler() {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
switch (e.keyCode) {
|
||||
case 38: // up arrow
|
||||
this.tiltX += 5;
|
||||
break
|
||||
case 40: // down arrow
|
||||
this.tiltX -= 5;
|
||||
break
|
||||
case 37: // left arrow
|
||||
this.tiltY -= 5;
|
||||
break
|
||||
case 39: // right arrow
|
||||
this.tiltY += 5;
|
||||
break
|
||||
}
|
||||
if (this.debug) console.log('keydown', e.keyCode, this.tiltX, this.tiltY);
|
||||
});
|
||||
|
||||
this.on('pointerdown', (e) => {
|
||||
if (this.debug) console.log('pointerdown', e);
|
||||
if (this.eventInside(e)) {
|
||||
this.activePointers += 1;
|
||||
if (this.singlePointer()) {
|
||||
this.startStroke(this.toStroke(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.on('pointermove', (e) => {
|
||||
if (Events$1.isPointerDown(e.data.originalEvent) || this.isStylusPointer(e) || this.isStylusTouch(e)) {
|
||||
if (this.debug) console.log('pointermove', e, this.eventInside(e));
|
||||
if (this.eventInside(e) && this.singlePointer())
|
||||
this.moveStroke(this.toStroke(e));
|
||||
}
|
||||
});
|
||||
this.on('pointerup', (e) => {
|
||||
if (this.eventInside(e)) {
|
||||
if (this.activePointers > 0) {
|
||||
this.activePointers -= 1;
|
||||
this.endStroke(this.toStroke(e));
|
||||
}
|
||||
}
|
||||
if (this.debug) console.log('pointerup', this.activePointers);
|
||||
});
|
||||
this.on('pointerleave', (e) => {
|
||||
if (this.activePointers > 0) {
|
||||
this.activePointers -= 1;
|
||||
}
|
||||
this.endStroke(this.toStroke(e));
|
||||
});
|
||||
this.on('pointercancel', (e) => {
|
||||
if (this.activePointers > 0) {
|
||||
this.activePointers -= 1;
|
||||
}
|
||||
this.endStroke(this.toStroke(e));
|
||||
});
|
||||
}
|
||||
|
||||
undoable() {
|
||||
return this.commandStack.length > 0
|
||||
}
|
||||
|
||||
redoable() {
|
||||
return this.undoCommandStack.length > 0
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.undoable()) {
|
||||
let cmd = this.commandStack.pop();
|
||||
cmd.undo(this);
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this.redoable()) {
|
||||
let cmd = this.undoCommandStack.pop();
|
||||
cmd.redo(this);
|
||||
}
|
||||
}
|
||||
|
||||
eventInside(event) {
|
||||
|
||||
let local = this.toLocal(event.data.global);
|
||||
for (let child of this.children) {
|
||||
let r = child.getBounds();
|
||||
if (r.contains(local.x, local.y)) {
|
||||
console.log('Child touched');
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (local.x < 0 || local.x > this.wantedWidth)
|
||||
return false
|
||||
if (local.y < 0 || local.y > this.wantedHeight)
|
||||
return false
|
||||
event.stopPropagation();
|
||||
// if (this.debug) console.log('stopPropagation', event)
|
||||
if (event.data.originalEvent.claimedByScatter) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
toLocalPoint(event) {
|
||||
return this.toLocal(event.data.global)
|
||||
}
|
||||
|
||||
toStroke(event) {
|
||||
let local = this.toLocalPoint(event);
|
||||
let x = Math.max(0, Math.min(local.x, this.wantedWidth));
|
||||
let y = Math.max(0, Math.min(local.y, this.wantedHeight));
|
||||
let desc = {
|
||||
x, y,
|
||||
pressure: event.pressure || null,
|
||||
tiltX: this.tiltX, tiltY: this.tiltY,
|
||||
color: this.color
|
||||
};
|
||||
return desc
|
||||
}
|
||||
|
||||
startStroke(info) {
|
||||
this.stroke = [info];
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
moveStroke(info) {
|
||||
this.stroke.push(info);
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
endStroke(info) {
|
||||
if (this.stroke.length >= this.minStrokeLength) {
|
||||
let cmd = new StrokeCommand(this.stroke);
|
||||
cmd.do(this);
|
||||
}
|
||||
}
|
||||
|
||||
tiltToLineWidth(value) {
|
||||
return Math.round(Math.abs(value / 10) + 1)
|
||||
}
|
||||
|
||||
drawStroke(stroke) {
|
||||
if (stroke.length) {
|
||||
let start = stroke[0];
|
||||
this.beginFill(0, 0);
|
||||
this.moveTo(start.x, start.y);
|
||||
for (let i = 1; i < stroke.length; i++) {
|
||||
let info = stroke[i];
|
||||
this.lineStyle(this.tiltToLineWidth(info.tiltY),
|
||||
info.color, this.colorAlpha);
|
||||
this.lineTo(info.x, info.y);
|
||||
}
|
||||
this.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
drawTouch(point) {
|
||||
this.beginFill(0, 0);
|
||||
this.drawCircle(point.x, point.y, 22);
|
||||
this.endFill();
|
||||
}
|
||||
|
||||
drawStrokes() {
|
||||
this.drawBackground();
|
||||
this.lineStyle(1.0, 0xFF0000, 1);
|
||||
for (let stroke of this.iterStrokes()) {
|
||||
this.drawStroke(stroke);
|
||||
}
|
||||
}
|
||||
|
||||
redraw() {
|
||||
this.drawStrokes();
|
||||
}
|
||||
|
||||
// Can be overwritten if different levels of strokes are necessary
|
||||
*iterStrokes() {
|
||||
for (let stroke of this.strokes) {
|
||||
yield stroke;
|
||||
}
|
||||
yield this.stroke;
|
||||
}
|
||||
|
||||
changed() {
|
||||
// Can be overwritten
|
||||
}
|
||||
|
||||
clearAll() {
|
||||
let cmd = new ClearCommand();
|
||||
cmd.do(this);
|
||||
}
|
||||
|
||||
normalizeInfo(info) {
|
||||
let { x, y, pressure, tiltX, tiltY, color } = info;
|
||||
x /= this.wantedWidth;
|
||||
y /= this.wantedHeight;
|
||||
return { x, y, pressure, tiltX, tiltY, color }
|
||||
}
|
||||
|
||||
denormalizeInfo(info) {
|
||||
let { x, y, pressure, tiltX, tiltY, color } = info;
|
||||
x = x * this.wantedWidth;
|
||||
y = y * this.wantedHeight;
|
||||
return { x, y, pressure, tiltX, tiltY, color }
|
||||
}
|
||||
|
||||
// Convert strokes into an object that can be stored in an Indexed DB.
|
||||
// Returns normalized strokes
|
||||
toObject() {
|
||||
let result = [];
|
||||
for (let stroke of this.strokes) {
|
||||
let normalized = [];
|
||||
for (let info of stroke) {
|
||||
normalized.push(this.normalizeInfo(info));
|
||||
}
|
||||
result.push(normalized);
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Read normalized strokes from an object from an Indexed DB.
|
||||
fromObject(normalizedStrokes) {
|
||||
this.strokes = [];
|
||||
for (let stroke of normalizedStrokes) {
|
||||
let denormalized = [];
|
||||
for (let info of stroke) {
|
||||
denormalized.push(this.denormalizeInfo(info));
|
||||
}
|
||||
this.strokes.push(denormalized);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert strokes into a JSON object that can be stored in an Indexed DB
|
||||
toJSON() {
|
||||
return JSON.stringify(this.toObject())
|
||||
}
|
||||
|
||||
// Convert strokes from a JSON
|
||||
fromJSON(json) {
|
||||
this.fromObject(JSON.parse(json));
|
||||
}
|
||||
|
||||
// Returns a set of used colors
|
||||
usedColors() {
|
||||
let used = new Set();
|
||||
for (let info of this.stroke) {
|
||||
used.add(info.color);
|
||||
}
|
||||
for (let stroke of this.strokes) {
|
||||
for (let info of stroke) {
|
||||
used.add(info.color);
|
||||
}
|
||||
}
|
||||
return used.values()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the switch action.
|
||||
*
|
||||
@@ -15003,6 +15423,7 @@
|
||||
window.ButtonGroup = ButtonGroup;
|
||||
window.Scrollview = Scrollview;
|
||||
window.Slider = Slider;
|
||||
window.Stylus = Stylus;
|
||||
window.Switch = Switch;
|
||||
window.Popup = Popup;
|
||||
window.PopupMenu = PopupMenu$1;
|
||||
|
||||
Reference in New Issue
Block a user