From af932a99115a9657ca2cc787ec6a37a50c724d20 Mon Sep 17 00:00:00 2001 From: Uwe Oestermeier Date: Thu, 4 Jul 2019 09:09:19 +0200 Subject: [PATCH 1/2] Removed the stylus folder and added stylus.js with doctest to lib/pixi --- dist/iwmlib.pixi.js | 526 ++++++++++++++++++++++++++++++++++++++----- lib/pixi/bundle.js | 2 + lib/pixi/stylus.html | 149 ++++++++++++ lib/pixi/stylus.js | 422 ++++++++++++++++++++++++++++++++++ 4 files changed, 1048 insertions(+), 51 deletions(-) create mode 100644 lib/pixi/stylus.html create mode 100755 lib/pixi/stylus.js diff --git a/dist/iwmlib.pixi.js b/dist/iwmlib.pixi.js index d208ec2..1fe4da9 100644 --- a/dist/iwmlib.pixi.js +++ b/dist/iwmlib.pixi.js @@ -1402,6 +1402,8 @@ Events$1.simulated = []; Events$1.simulationRunning = false; + /* global PIXI TweenLite */ + /** * Callback for the button action. * @@ -1559,7 +1561,7 @@ } if (this.opts.style === 'link') { - Object.assign(this.opts, {strokeAlpha: 0, strokeActiveAlpha: 0, fillAlpha: 0, fillActiveAlpha: 0}); + Object.assign(this.opts, { strokeAlpha: 0, strokeActiveAlpha: 0, fillAlpha: 0, fillActiveAlpha: 0 }); } this._active = null; @@ -1631,7 +1633,7 @@ //----------------- this.button.on('pointerover', e => { this.capture(e); - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); + TweenLite.to([this.button, this.content], this.theme.fast, { alpha: .83, overwrite: 'none' }); }); this.button.on('pointermove', e => { @@ -1640,12 +1642,13 @@ this.button.on('pointerout', e => { this.capture(e); - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: 1, overwrite: 'none'}); + TweenLite.to([this.button, this.content], this.theme.fast, { alpha: 1, overwrite: 'none' }); }); + // eslint-disable-next-line no-unused-vars this.button.on('pointerdown', e => { //this.capture(e) - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .7, overwrite: 'none'}); + TweenLite.to([this.button, this.content], this.theme.fast, { alpha: .7, overwrite: 'none' }); }); this.button.on('pointerup', e => { @@ -1658,7 +1661,7 @@ this.opts.action.call(this, e, this); } - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); + TweenLite.to([this.button, this.content], this.theme.fast, { alpha: .83, overwrite: 'none' }); if (this.opts.type === 'checkbox') { this.active = !this.active; @@ -1681,9 +1684,9 @@ //----------------- if (this.opts.tooltip) { if (typeof this.opts.tooltip === 'string') { - this.tooltip = new Tooltip({object: this, content: this.opts.tooltip}); + this.tooltip = new Tooltip({ object: this, content: this.opts.tooltip }); } else { - this.opts.tooltip = Object.assign({}, {object: this}, this.opts.tooltip); + this.opts.tooltip = Object.assign({}, { object: this }, this.opts.tooltip); this.tooltip = new Tooltip(this.opts.tooltip); } } @@ -1698,7 +1701,7 @@ offsetTop: 0 }); if (typeof this.opts.badge === 'string') { - opts = Object.assign(opts, {content: this.opts.badge}); + opts = Object.assign(opts, { content: this.opts.badge }); } else { opts = Object.assign(opts, this.opts.badge); } @@ -1706,25 +1709,25 @@ const badge = new Badge(opts); switch (opts.align) { - case 'left': - badge.x = this.x - badge.width / 2 + opts.offsetLeft; - break - case 'center': - badge.x = this.x + this.width / 2 - badge.width / 2 + opts.offsetLeft; - break - case 'right': - badge.x = this.x + this.width - badge.width / 2 + opts.offsetLeft; + case 'left': + badge.x = this.x - badge.width / 2 + opts.offsetLeft; + break + case 'center': + badge.x = this.x + this.width / 2 - badge.width / 2 + opts.offsetLeft; + break + case 'right': + badge.x = this.x + this.width - badge.width / 2 + opts.offsetLeft; } switch (opts.verticalAlign) { - case 'top': - badge.y = this.y - badge.height / 2 + opts.offsetTop; - break - case 'middle': - badge.y = this.y + this.height / 2 - badge.height / 2 + opts.offsetTop; - break - case 'bottom': - badge.y = this.y + this.height - badge.height / 2 + opts.offsetTop; + case 'top': + badge.y = this.y - badge.height / 2 + opts.offsetTop; + break + case 'middle': + badge.y = this.y + this.height / 2 - badge.height / 2 + opts.offsetTop; + break + case 'bottom': + badge.y = this.y + this.height - badge.height / 2 + opts.offsetTop; } this.addChild(badge); @@ -1834,15 +1837,15 @@ for (let child of this.content.children) { switch (this.opts.verticalAlign) { - case 'top': - child.y = 0; - break - case 'middle': - child.y = this.content.height / 2 - child.height / 2; - break - case 'bottom': - child.y = this.content.height - child.height; - break + case 'top': + child.y = 0; + break + case 'middle': + child.y = this.content.height / 2 - child.height / 2; + break + case 'bottom': + child.y = this.content.height - child.height; + break } } @@ -1859,27 +1862,27 @@ layoutContent() { switch (this.opts.align) { - case 'left': - this.content.x = this.opts.padding; - break - case 'center': - this.content.x = ((this._width - this.content.width) / 2); - break - case 'right': - this.content.x = this._width - this.opts.padding - this.content.width; - break + case 'left': + this.content.x = this.opts.padding; + break + case 'center': + this.content.x = ((this._width - this.content.width) / 2); + break + case 'right': + this.content.x = this._width - this.opts.padding - this.content.width; + break } switch (this.opts.verticalAlign) { - case 'top': - this.content.y = this.opts.padding; - break - case 'middle': - this.content.y = (this._height - this.content.height) / 2; - break - case 'bottom': - this.content.y = this._height - this.opts.padding - this.content.height; - break + case 'top': + this.content.y = this.opts.padding; + break + case 'middle': + this.content.y = (this._height - this.content.height) / 2; + break + case 'bottom': + this.content.y = this._height - this.opts.padding - this.content.height; + break } return this @@ -13819,6 +13822,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. * @@ -14997,6 +15420,7 @@ window.ButtonGroup = ButtonGroup; window.Scrollview = Scrollview; window.Slider = Slider; + window.Stylus = Stylus; window.Switch = Switch; window.Popup = Popup; window.PopupMenu = PopupMenu$1; diff --git a/lib/pixi/bundle.js b/lib/pixi/bundle.js index 2aeabb0..ff7b180 100755 --- a/lib/pixi/bundle.js +++ b/lib/pixi/bundle.js @@ -12,6 +12,7 @@ import Button from './button.js' import ButtonGroup from './buttongroup.js' import Scrollview from './scrollview.js' import Slider from './slider.js' +import Stylus from './stylus.js' import Switch from './switch.js' import Popup from './popup.js' import PopupMenu from './popupmenu.js' @@ -44,6 +45,7 @@ window.Button = Button window.ButtonGroup = ButtonGroup window.Scrollview = Scrollview window.Slider = Slider +window.Stylus = Stylus window.Switch = Switch window.Popup = Popup window.PopupMenu = PopupMenu diff --git a/lib/pixi/stylus.html b/lib/pixi/stylus.html new file mode 100644 index 0000000..912c96b --- /dev/null +++ b/lib/pixi/stylus.html @@ -0,0 +1,149 @@ + + + + + + + PIXI Stylus + + + + + + + + + + + + +

Stylus

+

The Stylus class extends the PIXI.Graphics class and allows to draw into + a graphics object. Select the pen tool in the following example app and draw into the canvas. +

+ +

+ What you should see: A blank sytlus canvas. +

+ + \ No newline at end of file diff --git a/lib/pixi/stylus.js b/lib/pixi/stylus.js new file mode 100755 index 0000000..95d38c9 --- /dev/null +++ b/lib/pixi/stylus.js @@ -0,0 +1,422 @@ +/* eslint-disable no-undef */ +/* eslint-disable no-console */ +import Events from '../events.js' +import { Angle } from '../utils.js' + +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() + } +} + + +export default 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.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() + } +} From b6fc04a41155587992f057ce194c01c9e3f80867 Mon Sep 17 00:00:00 2001 From: Uwe Oestermeier Date: Thu, 4 Jul 2019 09:09:58 +0200 Subject: [PATCH 2/2] Uups, forgot to remove files. --- lib/stylus/README.txt | 13 -- lib/stylus/index.html | 73 ------- lib/stylus/main.js | 90 --------- lib/stylus/stylus.js | 398 --------------------------------------- lib/stylus/thumbnail.png | Bin 6878 -> 0 bytes 5 files changed, 574 deletions(-) delete mode 100644 lib/stylus/README.txt delete mode 100644 lib/stylus/index.html delete mode 100644 lib/stylus/main.js delete mode 100644 lib/stylus/stylus.js delete mode 100644 lib/stylus/thumbnail.png diff --git a/lib/stylus/README.txt b/lib/stylus/README.txt deleted file mode 100644 index e4da17e..0000000 --- a/lib/stylus/README.txt +++ /dev/null @@ -1,13 +0,0 @@ - - -https://mattdesl.svbtle.com/drawing-lines-is-hard -http://perfectionkills.com/exploring-canvas-drawing-techniques/ - - - -https://github.com/mattdesl/polyline-normals - -var path = [ [0, 122], [0, 190], [90, 190] ] - -//get the normals as a closed loop -var normals = getNormals(path, true) diff --git a/lib/stylus/index.html b/lib/stylus/index.html deleted file mode 100644 index 1a38d4a..0000000 --- a/lib/stylus/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - Stylus Functional Tests - - - - - - -
- - Get a better browser, bro. - -
- - - diff --git a/lib/stylus/main.js b/lib/stylus/main.js deleted file mode 100644 index d3ff5ce..0000000 --- a/lib/stylus/main.js +++ /dev/null @@ -1,90 +0,0 @@ -import PIXIApp from '../../lib/pixi/app.js' -import Button from '../../lib/pixi/button.js' -import ButtonGroup from '../../lib/pixi/buttongroup.js' -import Stylus from './stylus.js' - -class StylusApp extends PIXIApp { - - sceneFactory() { - return new Stylus(this.renderer) - } - - setup() { - let buttonColor = 0x666666 - super.setup() - - this.tools = new ButtonGroup({ - type: 'checkbox', - margin: 0, - x: 16, - y: 16, - fill: buttonColor, - buttons: [{icon: 'edit', - iconColorActive: 0xFFFF00, - action: (event, button) => this.toggleEditMode() }, - {icon: 'undo', - action: (event, button) => this.undo(button) }, - {icon: 'redo', - action: (event, button) => this.redo(button) }, - {icon: 'delete', - action: (event, button) => this.clear(button) } - ] - }) - this.scene.addChild(this.tools) - - let defaults = { icon: 'brightness_1', - action: (event, button) => this.selectColor(button), - fillAlpha: 0, - strokeAlpha: 0, - fillActiveAlpha: 0, - strokeActiveAlpha: 0} - - this.palette = new ButtonGroup( { - type: "radio", - x: 200, - y: 16, - margin: 0, - strokeAlpha: 0, - fill: buttonColor, - buttons: [ - Object.assign({}, defaults, { iconColor: 0x111111, - iconColorActive: 0x111111}), // tooltip: "Black", - Object.assign({}, defaults, { iconColor: 0xFFFF00, - iconColorActive: 0xFFFF00}), // tooltip: "Yellow", - Object.assign({}, defaults, { iconColor: 0x00FF00, - iconColorActive:0x00FF00}), // tooltip: "Green", - Object.assign({}, defaults, { iconColor: 0xFF00FF, - iconColorActive:0xFF00FF}) // tooltip: "Violet", - ] - }) - this.scene.addChild(this.palette) - } - - selectColor(button) { - this.scene.color = button.opts.iconColor - } - - undo(button) { - this.scene.undo() - setTimeout(() => { - button.active = false}, 200) - } - - redo(button) { - this.scene.redo() - setTimeout(() => { - button.active = false}, 200) - } - - clear(button) { - this.scene.clearAll() - setTimeout(() => { - button.active = false}, 200) - } -} - -const app = new StylusApp({ view: canvas }) -window.app = app -app.setup() -app.run() - diff --git a/lib/stylus/stylus.js b/lib/stylus/stylus.js deleted file mode 100644 index dbbbbe0..0000000 --- a/lib/stylus/stylus.js +++ /dev/null @@ -1,398 +0,0 @@ -import Events from '../events.js' -import { Angle } from '../utils.js' - -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() - } -} - - -export default 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.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 = [] - 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 - } - } - } - - 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.startStroke(this.toStroke(e)) - }) - this.on('pointermove', (e) => { - if (Events.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.moveStroke(this.toStroke(e)) - } - }) - this.on('pointerup', (e) => { - if (this.debug) console.log("pointerup", e) - if (this.eventInside(e)) - this.endStroke(this.toStroke(e)) - }) - this.on('pointerleave', (e) => { - this.endStroke(this.toStroke(e)) - }) - this.on('pointercancel', (e) => { - 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() - } - - endStroke(info) { - if (this.stroke.length > 1) { - 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() - } -} diff --git a/lib/stylus/thumbnail.png b/lib/stylus/thumbnail.png deleted file mode 100644 index e2c7b2bf8cfa2674fb2bf3366a2d74517527d917..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6878 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