482 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			482 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/* eslint-disable no-unused-vars */
 | 
						|
import Events from '../events.js'
 | 
						|
import { AbstractScatter } from '../scatter.js'
 | 
						|
import { Angle, Points, Polygon } from '../utils.js'
 | 
						|
import { InteractionMapper } from '../interaction.js'
 | 
						|
 | 
						|
/** A container for scatter objects, which uses a single InteractionMapper
 | 
						|
 * for all children. This reduces the number of registered event handlers
 | 
						|
 * and covers the common use case that multiple objects are scattered
 | 
						|
 * on the same level.
 | 
						|
 */
 | 
						|
export class ScatterContainer extends PIXI.Graphics {
 | 
						|
    /**
 | 
						|
     * @constructor
 | 
						|
     * @param {PIXI.Renderer} renderer - PIXI renderer, needed for hit testing
 | 
						|
     * @param {Bool} stopEvents - Whether events should be stopped or propagated
 | 
						|
     * @param {Bool} claimEvents - Whether events should be marked as claimed
 | 
						|
     *                             if findTarget return as non-null value.
 | 
						|
     * @param {PIXI.Container} container - A container for the scatter
 | 
						|
     * @param {Bool} showBounds - Show bounds for debugging purposes.
 | 
						|
     * @param {Bool} showTouches - Show touches and pointer for debugging purposes.
 | 
						|
     * @param {Color} backgroundColor - Set background color if specified.
 | 
						|
     * @param {PIXIApp} app - Needed if showBounds is true to register
 | 
						|
     *                                update handler.
 | 
						|
     */
 | 
						|
    constructor(
 | 
						|
        renderer,
 | 
						|
        {
 | 
						|
            stopEvents = true,
 | 
						|
            claimEvents = true,
 | 
						|
            container = null,
 | 
						|
            showBounds = false,
 | 
						|
            showPolygon = false,
 | 
						|
            showTouches = false,
 | 
						|
            backgroundColor = null,
 | 
						|
            app = window.app,
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        super()
 | 
						|
        this.container = container
 | 
						|
        if (this.container)
 | 
						|
            this.containerDimensions = {
 | 
						|
                x: this.container.width,
 | 
						|
                y: this.container.height,
 | 
						|
            }
 | 
						|
        this.backgroundWidth = null
 | 
						|
        this.backgroundHeight = null
 | 
						|
        this.app = app
 | 
						|
        this.renderer = renderer
 | 
						|
        this.stopEvents = stopEvents
 | 
						|
        this.claimEvents = claimEvents
 | 
						|
        this.delegate = new InteractionMapper(this.eventReceiver, this)
 | 
						|
        this.showBounds = showBounds
 | 
						|
        this.showTouches = showTouches
 | 
						|
        this.showPolygon = showPolygon
 | 
						|
        this.backgroundColor = backgroundColor
 | 
						|
        if (showBounds || showTouches || showPolygon) {
 | 
						|
            //console.log("Show TOUCHES!!!")
 | 
						|
            this.app.ticker.add((delta) => this.update(delta), this)
 | 
						|
        }
 | 
						|
        if (backgroundColor) {
 | 
						|
            this.updateBackground()
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    updateBackground() {
 | 
						|
        this.clear()
 | 
						|
        let rect = this.bounds
 | 
						|
        this.beginFill(this.backgroundColor, 1)
 | 
						|
        this.drawRect(0, 0, rect.width, rect.height)
 | 
						|
        this.endFill()
 | 
						|
    }
 | 
						|
 | 
						|
    get eventReceiver() {
 | 
						|
        return this.renderer.plugins.interaction.interactionDOMElement
 | 
						|
    }
 | 
						|
 | 
						|
    get bounds() {
 | 
						|
        let x = 0
 | 
						|
        let y = 0
 | 
						|
 | 
						|
        // @container: We need to call the constant values, as the container
 | 
						|
        // gets resized, when a child moves outside the original boundaries.
 | 
						|
        let w = this.container ? this.containerDimensions.x : this.backgroundWidth || this.app.width
 | 
						|
        let h = this.container ? this.containerDimensions.y : this.backgroundHeight || this.app.height
 | 
						|
 | 
						|
        if (this.app.fullscreen && this.app.monkeyPatchMapping) {
 | 
						|
            let fixed = this.mapPositionToPoint({ x: w, y: 0 })
 | 
						|
            if (fixed.x < w) {
 | 
						|
                w = fixed.x
 | 
						|
            }
 | 
						|
            if (fixed.y > 0) {
 | 
						|
                y += fixed.y
 | 
						|
                h -= fixed.y
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return new PIXI.Rectangle(x, y, w, h)
 | 
						|
    }
 | 
						|
 | 
						|
    get center() {
 | 
						|
        let r = this.bounds
 | 
						|
        return { x: r.width / 2, y: r.height / 2 }
 | 
						|
    }
 | 
						|
 | 
						|
    get polygon() {
 | 
						|
        let r = this.bounds
 | 
						|
        let w2 = r.width / 2
 | 
						|
        let h2 = r.height / 2
 | 
						|
        let center = { x: w2, y: h2 }
 | 
						|
        let polygon = new Polygon(center)
 | 
						|
        polygon.addPoint({ x: -w2, y: -h2 })
 | 
						|
        polygon.addPoint({ x: w2, y: -h2 })
 | 
						|
        polygon.addPoint({ x: w2, y: h2 })
 | 
						|
        polygon.addPoint({ x: -w2, y: h2 })
 | 
						|
        return polygon
 | 
						|
    }
 | 
						|
 | 
						|
    update(dt) {
 | 
						|
        this.clear()
 | 
						|
        this.lineStyle(1, 0x0000ff)
 | 
						|
        if (this.showBounds) {
 | 
						|
            for (let child of this.children) {
 | 
						|
                if (child.scatter) {
 | 
						|
                    //let {x, y, width, height} = child.scatter.throwBounds()
 | 
						|
                    // new PIXI.Rectangle(x, y, width, height)
 | 
						|
                    this.drawShape(child.scatter.bounds)
 | 
						|
                    let center = child.scatter.center
 | 
						|
                    this.drawCircle(center.x, center.y, 4)
 | 
						|
                    this.drawCircle(child.scatter.x, child.scatter.y, 4)
 | 
						|
                }
 | 
						|
            }
 | 
						|
            this.lineStyle(2, 0x0000ff)
 | 
						|
            this.drawShape(this.bounds)
 | 
						|
        }
 | 
						|
        if (this.showPolygon) {
 | 
						|
            this.lineStyle(2, 0xff0000)
 | 
						|
            for (let child of this.children) {
 | 
						|
                if (child.scatter) {
 | 
						|
                    let polygon = child.scatter.polygon
 | 
						|
                    let shape = new PIXI.Polygon(polygon.flatAbsolutePoints())
 | 
						|
                    //shape.close() not possible in PixiJS v5
 | 
						|
                    this.drawShape(shape)
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (this.showTouches) {
 | 
						|
            let current = this.delegate.interaction.current
 | 
						|
            for (let [key, point] of current.entries()) {
 | 
						|
                let local = this.mapPositionToPoint(point)
 | 
						|
                this.drawCircle(local.x, local.y, 12)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    capture(event) {
 | 
						|
        if (this.stopEvents) Events.stop(event)
 | 
						|
        return true
 | 
						|
    }
 | 
						|
 | 
						|
    fakeInteractionEvent(point, key) {
 | 
						|
        return { data: { global: point, key: key } }
 | 
						|
    }
 | 
						|
 | 
						|
    findHitScatter(data, displayObject, hit) {
 | 
						|
        if (hit && this.hitScatter === null && typeof displayObject != undefined) {
 | 
						|
            this.hitScatter = displayObject.scatter ? displayObject.scatter : null
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    mapPositionToPoint(point, element = null) {
 | 
						|
        // In case of nested scatters we get an additional parameter that
 | 
						|
        // contains the found scatter
 | 
						|
        let local = new PIXI.Point()
 | 
						|
        let interactionManager = this.renderer.plugins.interaction
 | 
						|
        interactionManager.mapPositionToPoint(local, point.x, point.y)
 | 
						|
        if (element instanceof DisplayObjectScatter && element.displayObject.parent != null) {
 | 
						|
            return element.displayObject.parent.toLocal(local)
 | 
						|
        }
 | 
						|
        return local
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * New method hitTest implemented (in InteractionManager, since 4.5.0).
 | 
						|
     * See https://github.com/pixijs/pixi.js/pull/3878
 | 
						|
     */
 | 
						|
    findTarget(event, local, global) {
 | 
						|
        if (event.claimedByScatter) {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
        this.hitScatter = null
 | 
						|
        let interactionManager = this.renderer.plugins.interaction
 | 
						|
        let fakeEvent = this.fakeInteractionEvent(local)
 | 
						|
        interactionManager.processInteractive(fakeEvent, this, this.findHitScatter.bind(this), true)
 | 
						|
        if (this.claimEvents) event.claimedByScatter = this.hitScatter
 | 
						|
        return this.hitScatter
 | 
						|
    }
 | 
						|
 | 
						|
    findTargetNew(event, local, global) {
 | 
						|
        // UO: still problematic. Does not find non interactive elements
 | 
						|
        // which are needed for some stylus applications
 | 
						|
        if (event.claimedByScatter) {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
 | 
						|
        this.hitScatter = null
 | 
						|
        let interactionManager = this.renderer.plugins.interaction
 | 
						|
        let displayObject = interactionManager.hitTest(local, this)
 | 
						|
        if (displayObject != null && displayObject.scatter != null) this.hitScatter = displayObject.scatter
 | 
						|
        if (this.claimEvents) event.claimedByScatter = this.hitScatter
 | 
						|
 | 
						|
        return this.hitScatter
 | 
						|
    }
 | 
						|
 | 
						|
    onStart(event, interaction) {}
 | 
						|
 | 
						|
    onMove(event, interaction) {}
 | 
						|
 | 
						|
    onEnd(event, interaction) {
 | 
						|
        for (let key of interaction.ended.keys()) {
 | 
						|
            let point = interaction.ended.get(key)
 | 
						|
            if (interaction.isLongPress(key)) {
 | 
						|
                this.onLongPress(key, point)
 | 
						|
            }
 | 
						|
            if (interaction.isTap(key)) {
 | 
						|
                this.onTap(key, point)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    onTap(key, point) {
 | 
						|
        console.info('ScatterContainer.onTap')
 | 
						|
    }
 | 
						|
 | 
						|
    onLongPress(key, point) {
 | 
						|
        console.info('ScatterContainer.onLongPress')
 | 
						|
    }
 | 
						|
 | 
						|
    bringToFront(displayObject) {
 | 
						|
        this.addChild(displayObject)
 | 
						|
    }
 | 
						|
 | 
						|
    layout(width, height) {
 | 
						|
        this.backgroundWidth = width
 | 
						|
        this.backgroundHeight = height
 | 
						|
        if (this.backgroundColor) {
 | 
						|
            this.updateBackground()
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/** A wrapper for child elements of a ScatterContainer. Can be used
 | 
						|
 *  to combine scattered objects with non-scattered objects. Any
 | 
						|
 *  PIXI.DisplayObject can be wrapped.
 | 
						|
 */
 | 
						|
export class DisplayObjectScatter extends AbstractScatter {
 | 
						|
    constructor(
 | 
						|
        displayObject,
 | 
						|
        renderer,
 | 
						|
        {
 | 
						|
            x = null,
 | 
						|
            y = null,
 | 
						|
            minScale = 0.1,
 | 
						|
            maxScale = 1.0,
 | 
						|
            startScale = 1.0,
 | 
						|
            autoBringToFront = true,
 | 
						|
            translatable = true,
 | 
						|
            scalable = true,
 | 
						|
            rotatable = true,
 | 
						|
            resizable = false,
 | 
						|
            movableX = true,
 | 
						|
            movableY = true,
 | 
						|
            throwVisibility = 44,
 | 
						|
            throwDamping = 0.95,
 | 
						|
            autoThrow = true,
 | 
						|
            rotationDegrees = null,
 | 
						|
            rotation = null,
 | 
						|
            overdoScaling = 1.5,
 | 
						|
            onTransform = null,
 | 
						|
            onResize,
 | 
						|
            onThrowFinished = null,
 | 
						|
        } = {}
 | 
						|
    ) {
 | 
						|
        // For the simulation of named parameters,
 | 
						|
        // see: http://exploringjs.com/es6/ch_parameter-handling.html
 | 
						|
        super({
 | 
						|
            overdoScaling,
 | 
						|
            minScale,
 | 
						|
            maxScale,
 | 
						|
            startScale,
 | 
						|
            autoBringToFront,
 | 
						|
            translatable,
 | 
						|
            scalable,
 | 
						|
            rotatable,
 | 
						|
            resizable,
 | 
						|
            movableX,
 | 
						|
            movableY,
 | 
						|
            throwVisibility,
 | 
						|
            throwDamping,
 | 
						|
            autoThrow,
 | 
						|
            onThrowFinished,
 | 
						|
            rotationDegrees,
 | 
						|
            rotation,
 | 
						|
            onTransform,
 | 
						|
        })
 | 
						|
        this.onResize = onResize
 | 
						|
        this.displayObject = displayObject
 | 
						|
        this.displayObject.scatter = this
 | 
						|
        this.renderer = renderer
 | 
						|
        this.scale = startScale
 | 
						|
        this.rotationDegrees = this.startRotationDegrees
 | 
						|
 | 
						|
        // Only set x and y if they are specified.
 | 
						|
        // Otherwise the displayobject gets corrupted.
 | 
						|
        if (x != null) this.x = x
 | 
						|
        if (y != null) this.y = y
 | 
						|
    }
 | 
						|
 | 
						|
    getWorldScatter() {
 | 
						|
        return this
 | 
						|
    }
 | 
						|
 | 
						|
    /** Returns geometry data as object. **/
 | 
						|
    getState() {
 | 
						|
        return {
 | 
						|
            scale: this.scale,
 | 
						|
            x: this.x,
 | 
						|
            y: this.y,
 | 
						|
            rotation: this.rotation,
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    setup() {
 | 
						|
        this.setupMouseWheelInteraction()
 | 
						|
    }
 | 
						|
 | 
						|
    roundPixel(value) {
 | 
						|
        // UO: Should be obsolete because Renderer supports roundPixels by default
 | 
						|
        return value
 | 
						|
    }
 | 
						|
 | 
						|
    get container() {
 | 
						|
        // return this.displayObject.parent
 | 
						|
        let obj = this.displayObject
 | 
						|
        while (obj.parent != null && !(obj.parent instanceof ScatterContainer)) obj = obj.parent
 | 
						|
        return obj.parent
 | 
						|
    }
 | 
						|
 | 
						|
    get x() {
 | 
						|
        return this.position.x
 | 
						|
    }
 | 
						|
 | 
						|
    set x(value) {
 | 
						|
        this.position.x = value
 | 
						|
    }
 | 
						|
 | 
						|
    get y() {
 | 
						|
        return this.position.y
 | 
						|
    }
 | 
						|
 | 
						|
    set y(value) {
 | 
						|
        this.position.y = value
 | 
						|
    }
 | 
						|
 | 
						|
    get polygon() {
 | 
						|
        let polygon = new Polygon(this.center)
 | 
						|
        let w2 = this.width / 2
 | 
						|
        let h2 = this.height / 2
 | 
						|
        polygon.addPoint({ x: -w2, y: -h2 })
 | 
						|
        polygon.addPoint({ x: w2, y: -h2 })
 | 
						|
        polygon.addPoint({ x: w2, y: h2 })
 | 
						|
        polygon.addPoint({ x: -w2, y: h2 })
 | 
						|
        polygon.rotate(this.rotation)
 | 
						|
        return polygon
 | 
						|
    }
 | 
						|
 | 
						|
    get containerBounds() {
 | 
						|
        return this.container.bounds
 | 
						|
    }
 | 
						|
 | 
						|
    get containerPolygon() {
 | 
						|
        let container = this.container
 | 
						|
        if (container == null) return null
 | 
						|
        return container.polygon
 | 
						|
    }
 | 
						|
 | 
						|
    get position() {
 | 
						|
        return this.displayObject.position
 | 
						|
    }
 | 
						|
 | 
						|
    set position(value) {
 | 
						|
        this.displayObject.position = value
 | 
						|
    }
 | 
						|
 | 
						|
    get scale() {
 | 
						|
        return this.displayObject.scale.x
 | 
						|
    }
 | 
						|
 | 
						|
    set scale(value) {
 | 
						|
        this.displayObject.scale.x = value
 | 
						|
        this.displayObject.scale.y = value
 | 
						|
    }
 | 
						|
 | 
						|
    get width() {
 | 
						|
        return this.displayObject.width
 | 
						|
    }
 | 
						|
 | 
						|
    get height() {
 | 
						|
        return this.displayObject.height
 | 
						|
    }
 | 
						|
 | 
						|
    get bounds() {
 | 
						|
        return this.displayObject.getBounds()
 | 
						|
    }
 | 
						|
 | 
						|
    get pivot() {
 | 
						|
        return this.displayObject.pivot
 | 
						|
    }
 | 
						|
 | 
						|
    get rotation() {
 | 
						|
        return this.displayObject.rotation
 | 
						|
    }
 | 
						|
 | 
						|
    set rotation(value) {
 | 
						|
        this.displayObject.rotation = value
 | 
						|
    }
 | 
						|
 | 
						|
    get rotationDegrees() {
 | 
						|
        return Angle.radian2degree(this.displayObject.rotation)
 | 
						|
    }
 | 
						|
 | 
						|
    set rotationDegrees(value) {
 | 
						|
        this.displayObject.rotation = Angle.degree2radian(value)
 | 
						|
    }
 | 
						|
 | 
						|
    get center() {
 | 
						|
        let w2 = this.width / 2
 | 
						|
        let h2 = this.height / 2
 | 
						|
        let dist = Math.sqrt(w2 * w2 + h2 * h2)
 | 
						|
        let angle = Points.angle({ x: w2, y: h2 }, { x: 0, y: 0 })
 | 
						|
        let p = this.displayObject.x
 | 
						|
        let c = Points.arc(this.position, this.rotation + angle, dist)
 | 
						|
        return c // Points.subtract(c, this.pivot)
 | 
						|
    }
 | 
						|
 | 
						|
    get rotationOrigin() {
 | 
						|
        // In PIXI the default rotation and scale origin is the position
 | 
						|
        return this.position // Points.add(this.position, this.pivot)
 | 
						|
    }
 | 
						|
 | 
						|
    mapPositionToContainerPoint(point) {
 | 
						|
        // UO: We need the coordinates related to this scatter in case
 | 
						|
        // of nested scatters
 | 
						|
        if (this.container != null) return this.container.mapPositionToPoint(point, this)
 | 
						|
        return point
 | 
						|
    }
 | 
						|
 | 
						|
    capture(event) {
 | 
						|
        return true
 | 
						|
    }
 | 
						|
 | 
						|
    bringToFront() {
 | 
						|
        if (this.autoBringToFront) {
 | 
						|
            if (this.displayObject.parent instanceof ScatterContainer) {
 | 
						|
                let scatterContainer = this.displayObject.parent
 | 
						|
                scatterContainer.bringToFront(this.displayObject)
 | 
						|
            } else if (this.displayObject.parent != null && this.displayObject.parent.scatter) {
 | 
						|
                this.displayObject.parent.scatter.toFront(this.displayObject)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    toFront(displayObject) {
 | 
						|
        this.displayObject.addChild(displayObject)
 | 
						|
    }
 | 
						|
 | 
						|
    validScale(scale) {
 | 
						|
        scale = Math.max(scale, this.minScale)
 | 
						|
        scale = Math.min(scale, this.maxScale)
 | 
						|
        return scale
 | 
						|
    }
 | 
						|
}
 |