• Jump To … +
    ./demo/canvas-001.js ./demo/canvas-002.js ./demo/canvas-003.js ./demo/canvas-004.js ./demo/canvas-005.js ./demo/canvas-006.js ./demo/canvas-007.js ./demo/canvas-008.js ./demo/canvas-009.js ./demo/canvas-010.js ./demo/canvas-011.js ./demo/canvas-012.js ./demo/canvas-013.js ./demo/canvas-014.js ./demo/canvas-015.js ./demo/canvas-016.js ./demo/canvas-017.js ./demo/canvas-018.js ./demo/canvas-019.js ./demo/canvas-020.js ./demo/canvas-021.js ./demo/canvas-022.js ./demo/canvas-023.js ./demo/canvas-024.js ./demo/canvas-025.js ./demo/canvas-026.js ./demo/canvas-027.js ./demo/canvas-028.js ./demo/canvas-029.js ./demo/canvas-030.js ./demo/canvas-031.js ./demo/canvas-032.js ./demo/canvas-033.js ./demo/canvas-034.js ./demo/canvas-035.js ./demo/canvas-036.js ./demo/canvas-037.js ./demo/canvas-038.js ./demo/canvas-039.js ./demo/canvas-040.js ./demo/canvas-041.js ./demo/canvas-042.js ./demo/canvas-043.js ./demo/canvas-044.js ./demo/canvas-045.js ./demo/canvas-046.js ./demo/canvas-047.js ./demo/canvas-048.js ./demo/canvas-049.js ./demo/canvas-050.js ./demo/canvas-051.js ./demo/canvas-052.js ./demo/canvas-053.js ./demo/canvas-054.js ./demo/canvas-055.js ./demo/canvas-056.js ./demo/canvas-057.js ./demo/canvas-058.js ./demo/canvas-059.js ./demo/canvas-060.js ./demo/canvas-061.js ./demo/canvas-062.js ./demo/canvas-063.js ./demo/canvas-064.js ./demo/canvas-065.js ./demo/canvas-066.js ./demo/canvas-067.js ./demo/canvas-068.js ./demo/canvas-069.js ./demo/canvas-070.js ./demo/canvas-071.js ./demo/canvas-072.js ./demo/canvas-201.js ./demo/canvas-202.js ./demo/canvas-203.js ./demo/canvas-204.js ./demo/canvas-205.js ./demo/canvas-206.js ./demo/canvas-207.js ./demo/canvas-208.js ./demo/canvas-209.js ./demo/canvas-210.js ./demo/canvas-211.js ./demo/canvas-212.js ./demo/delaunator-001.js ./demo/delaunator-002.js ./demo/dom-001.js ./demo/dom-002.js ./demo/dom-003.js ./demo/dom-004.js ./demo/dom-005.js ./demo/dom-006.js ./demo/dom-007.js ./demo/dom-008.js ./demo/dom-009.js ./demo/dom-010.js ./demo/dom-011.js ./demo/dom-012.js ./demo/dom-013.js ./demo/dom-015.js ./demo/dom-016.js ./demo/dom-017.js ./demo/dom-018.js ./demo/dom-019.js ./demo/dom-020.js ./demo/dom-021.js ./demo/filters-001.js ./demo/filters-002.js ./demo/filters-003.js ./demo/filters-004.js ./demo/filters-005.js ./demo/filters-006.js ./demo/filters-007.js ./demo/filters-008.js ./demo/filters-009.js ./demo/filters-010.js ./demo/filters-011.js ./demo/filters-012.js ./demo/filters-013.js ./demo/filters-014.js ./demo/filters-015.js ./demo/filters-016.js ./demo/filters-017.js ./demo/filters-018.js ./demo/filters-019.js ./demo/filters-020.js ./demo/filters-021.js ./demo/filters-022.js ./demo/filters-023.js ./demo/filters-024.js ./demo/filters-025.js ./demo/filters-026.js ./demo/filters-027.js ./demo/filters-028.js ./demo/filters-029.js ./demo/filters-030.js ./demo/filters-031.js ./demo/filters-032.js ./demo/filters-033.js ./demo/filters-034.js ./demo/filters-035.js ./demo/filters-036.js ./demo/filters-037.js ./demo/filters-101.js ./demo/filters-102.js ./demo/filters-103.js ./demo/filters-104.js ./demo/filters-105.js ./demo/filters-501.js ./demo/filters-502.js ./demo/filters-503.js ./demo/filters-504.js ./demo/filters-505.js ./demo/mediapipe-001.js ./demo/mediapipe-002.js ./demo/mediapipe-003.js ./demo/modules-001.js ./demo/modules-002.js ./demo/modules-003.js ./demo/modules-004.js ./demo/modules-005.js ./demo/modules-006.js ./demo/packets-001.js ./demo/packets-002.js ./demo/particles-001.js ./demo/particles-002.js ./demo/particles-003.js ./demo/particles-004.js ./demo/particles-005.js ./demo/particles-006.js ./demo/particles-007.js ./demo/particles-008.js ./demo/particles-009.js ./demo/particles-010.js ./demo/particles-011.js ./demo/particles-012.js ./demo/particles-013.js ./demo/particles-014.js ./demo/particles-015.js ./demo/particles-016.js ./demo/particles-017.js ./demo/snippets-001.js ./demo/snippets-002.js ./demo/snippets-003.js ./demo/snippets-004.js ./demo/snippets-005.js ./demo/snippets-006.js ./demo/temp-001.js ./demo/temp-shape-scale-investigation.js ./demo/tensorflow-001.js ./demo/tensorflow-002.js ./demo/utilities.js
  • utilities.js

  • §

    Function to display frames-per-second data, and other information relevant to the demo

    const reportSpeed = function (output = '', xtra = () => '') {
    
        if (!output) return function () {};
    
        const testMessage = document.querySelector(output),
            history = []
    
        let testTicker = Date.now(),
            testTime, testNow,
            averageTime = 0;
    
        const addTime = (t) => {
    
            if (history.length > 60) history.shift();
            history.push(t);
            averageTime = history.reduce((p, c) => p + c, 0);
            averageTime /= history.length;
        }
    
        return function () {
    
            testNow = Date.now();
    
            testTime = testNow - testTicker;
            testTicker = testNow;
    
            addTime(testTime);
    
            let text = `Screen refresh: ${Math.ceil(averageTime)}ms; fps: ${Math.floor(1000 / averageTime)}`;
    
            if (xtra) {
    
                text += `
    ${xtra()}`;
            }
    
            testMessage.textContent = text;
        };
    };
  • §

    Get a full, dynamic report on the Scrawl-canvas library’s current contents

    const reportFullLibrary = (scrawl) => {
    
        const {
            anchor, anchornames,
            animation, animationnames,
            animationtickers, animationtickersnames,
            artefact, artefactnames,
            asset, assetnames,
            canvas, canvasnames,
            cell, cellnames,
            element, elementnames,
            entity, entitynames,
            filter, filternames,
            force, forcenames,
            group, groupnames,
            particle, particlenames,
            spring, springnames,
            stack, stacknames,
            styles, stylesnames,
            tween, tweennames,
            unstackedelement, unstackedelementnames,
            world, worldnames,
        } = scrawl.library;
    
        const compareObjectToArray = (obj, arr) => {
    
            const keys = Object.keys(obj);
    
            if (keys.length !== arr.length) return false;
    
            return keys.every(k => arr.includes(k));
        };
    
        const getSectionOutput = (label, obj, arr) => {
    
            if ('animation' === label) {
                let t = `${label}: ${arr.length} (${compareObjectToArray(obj, arr)})`;
                for (const [key, value] of Object.entries(obj)) {
                    t += `\n    ${key} - ${value.isRunning() ? 'running' : 'halted'}`;
                }
                return t;
            }
    
            if ('animationtickers' === label) {
                let t = `${label}: ${arr.length} (${compareObjectToArray(obj, arr)})`;
                for (const [key, value] of Object.entries(obj)) {
                    t += `\n    ${key} - ${value.isRunning() && value.checkObserverRunningState() ? 'running' : 'halted'}`;
                }
                return t;
            }
    
            return `${label}: ${arr.length} (${compareObjectToArray(obj, arr)}) - ${arr.join(', ')}`;
        };
        return `
    ${getSectionOutput('anchor', anchor, anchornames)}
    ${getSectionOutput('animation', animation, animationnames)}
    ${getSectionOutput('animationtickers', animationtickers, animationtickersnames)}
    ${getSectionOutput('artefact', artefact, artefactnames)}
    ${getSectionOutput('asset', asset, assetnames)}
    ${getSectionOutput('canvas', canvas, canvasnames)}
    ${getSectionOutput('cell', cell, cellnames)}
    ${getSectionOutput('element', element, elementnames)}
    ${getSectionOutput('entity', entity, entitynames)}
    ${getSectionOutput('filter', filter, filternames)}
    ${getSectionOutput('force', force, forcenames)}
    ${getSectionOutput('group', group, groupnames)}
    ${getSectionOutput('particle', particle, particlenames)}
    ${getSectionOutput('spring', spring, springnames)}
    ${getSectionOutput('stack', stack, stacknames)}
    ${getSectionOutput('styles', styles, stylesnames)}
    ${getSectionOutput('tween', tween, tweennames)}
    ${getSectionOutput('unstackedelement', unstackedelement, unstackedelementnames)}
    ${getSectionOutput('world', world, worldnames)}
        `;
    }
  • §

    Test to check that artefacts are properly killed and resurrected

    const killArtefact = (scrawl, canvas, name, time, finishResurrection = () => {}) => {
    
        const L = scrawl.library;
    
        if (canvas && canvas.base && name && time) {
    
            const groupname = canvas.base.name;
    
            let res, packet;
    
            const checkGroupBucket = (name, groupname) => {
    
                res = L.group[groupname].artefactCalculateBuckets.filter(e => e.name === name );
                return (res.length) ? 'no' : 'yes';
            };
    
            setTimeout(() => {
    
                console.log(`${name} alive
            removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
            removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
            removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
                packet = L.artefact[name].saveAsPacket();
                console.log(packet);
    
                L.artefact[name].kill();
    
                setTimeout(() => {
    
                    console.log(`${name} killed
            removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
            removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
            removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
                    canvas.actionPacket(packet);
    
                    setTimeout(() => {
    
                        console.log(`${name} resurrected
            removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
            removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
            removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
            removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
                        if (finishResurrection) finishResurrection();
    
                    }, 100);
                }, 100);
            }, time);
        }
        else console.log('killArtefact test invoked improperly');
    };
  • §

    To test styles (Gradient) kill functionality

    const killStyle = (scrawl, canvas, name, time, finishResurrection = () => {}) => {
    
        const L = scrawl.library;
    
        if (canvas && canvas.base && name && time) {
    
            let packet;
    
            setTimeout(() => {
    
                console.log(`${name} alive
            removed from styles: ${(L.styles[name]) ? 'no' : 'yes'}
            removed from stylesnames: ${(L.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
                packet = L.styles[name].saveAsPacket();
                console.log(packet);
    
                L.styles[name].kill();
    
                setTimeout(() => {
    
                    console.log(`${name} killed
            removed from styles: ${(L.styles[name]) ? 'no' : 'yes'}
            removed from stylesnames: ${(L.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
                    canvas.actionPacket(packet);
    
                    setTimeout(() => {
    
                        console.log(`${name} resurrected
            removed from styles: ${(L.styles[name]) ? 'no' : 'yes'}
            removed from stylesnames: ${(L.stylesnames.indexOf(name) >= 0) ? 'no' : 'yes'}`);
    
                        finishResurrection();
    
                    }, 100);
                }, 100);
            }, time);
        }
        else console.log('killStyle test invoked improperly');
    };
  • §

    To test artefact + anchor kill functionality

    const killArtefactAndAnchor = (scrawl, canvas, name, anchorname, time, finishResurrection = () => {}) => {
    
        const L = scrawl.library;
    
        const groupname = 'mycanvas_base';
    
        let packet, res;
    
        const checkGroupBucket = (name, groupname) => {
    
            res = L.group[groupname].artefactCalculateBuckets.filter(e => e.name === name );
            return (res.length) ? 'no' : 'yes';
        };
    
        setTimeout(() => {
    
            console.log(`${name} alive
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(L.anchor[anchorname]) ? 'no' : 'yes'}`);
    
            packet = L.artefact[name].saveAsPacket();
            console.log(packet);
    
            L.artefact[name].kill();
    
            setTimeout(() => {
    
                console.log(`${name} killed
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(L.anchor[anchorname]) ? 'no' : 'yes'}`);
    
                canvas.actionPacket(packet);
    
                setTimeout(() => {
    
                    console.log(`${name} resurrected
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}
        anchor removed: ${(L.anchor[anchorname]) ? 'no' : 'yes'}`);
    
                    finishResurrection();
    
                }, 100);
            }, 100);
        }, time);
    };
  • §

    To test Polyline artefact kill functionality

    const killPolylineArtefact = (scrawl, canvas, name, time, myline, restore = () => {}) => {
    
        const L = scrawl.library;
    
        const groupname = 'mycanvas_base';
    
        let packet, res;
    
        const checkGroupBucket = (name, groupname) => {
    
            res = L.group[groupname].artefactCalculateBuckets.filter(e => e.name === name );
            return (res.length) ? 'no' : 'yes';
        };
    
        const checkPinsArray = (name) => {
    
            if (myline.pins.indexOf(name) >= 0) return 'no';
    
            res = myline.pins.filter(e => e && e.name === name );
            return (res.length) ? 'no' : 'yes';
        };
    
        setTimeout(() => {
    
            console.log(`${name} alive
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from polyline pins array: ${checkPinsArray(name)}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
            packet = L.artefact[name].saveAsPacket();
            console.log(packet);
    
            L.artefact[name].kill();
    
            setTimeout(() => {
    
                console.log(`${name} killed
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from polyline pins array: ${checkPinsArray(name)}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
                canvas.actionPacket(packet);
    
                setTimeout(() => {
    
                    if (restore) restore();
    
                    console.log(`${name} resurrected
        removed from artefact: ${(L.artefact[name]) ? 'no' : 'yes'}
        removed from artefactnames: ${(L.artefactnames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from entity: ${(L.entity[name]) ? 'no' : 'yes'}
        removed from entitynames: ${(L.entitynames.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from polyline pins array: ${checkPinsArray(name)}
        removed from group.artefacts: ${(L.group[groupname].artefacts.indexOf(name) >= 0) ? 'no' : 'yes'}
        removed from group.artefactCalculateBuckets: ${checkGroupBucket(name, groupname)}`);
    
                    console.log(myline.saveAsPacket());
    
                }, 500);
            }, 500);
        }, time);
    };
    
    const killTicker = (scrawl, stack, name, time) => {
    
        const L = scrawl.library;
    
        let packet;
    
        setTimeout(() => {
    
            console.log(`${name} alive
        removed from tickers: ${(L.animationtickers[name]) ? 'no' : 'yes'}`);
    
            packet = L.animationtickers[name].saveAsPacket();
            console.log(packet);
    
            L.animationtickers[name].kill();
    
            setTimeout(() => {
    
                console.log(`${name} killed
        removed from tickers: ${(L.animationtickers[name]) ? 'no' : 'yes'}`);
    
                stack.actionPacket(packet);
    
                setTimeout(() => {
    
                    console.log(`${name} resurrected
        removed from tickers: ${(L.animationtickers[name]) ? 'no' : 'yes'}`);
                }, 100);
            }, 100);
        }, time);
    };
    
    const addImageDragAndDrop = (scrawl, canvas, selector, targets, callback = () => {}) => {
    
        const L = scrawl.library;
  • §

    Drag-and-Drop image loading functionality

        const store = document.querySelector(selector);
        const timeoutDelay = 200;
    
        let counter = 0;
    
        const canvasArray = [];
        if (Array.isArray(canvas)) canvasArray.push(...canvas);
        else canvasArray.push(canvas);
    
        const wrappers = [];
        canvasArray.forEach(c => {
            if (c.substring) wrappers.push(scrawl.findCanvas(c));
            else wrappers.push(c);
        });
    
        if (!Array.isArray(targets)) targets = [targets];
    
        scrawl.addNativeListener(['dragenter', 'dragover', 'dragleave'], (e) => {
    
            e.preventDefault();
            e.stopPropagation();
    
        }, wrappers.map(w => w.domElement));
    
        scrawl.addNativeListener('drop', (e) => {
    
            e.preventDefault();
            e.stopPropagation();
    
            const dt = e.dataTransfer;
    
            if (dt) [...dt.files].forEach(addImageAsset);
    
        }, wrappers.map(w => w.domElement));
    
        const addImageAsset = (file) => {
    
            if (file.type.indexOf('image/') === 0) {
    
                const reader = new FileReader();
    
                reader.readAsDataURL(file);
    
                reader.onloadend = function() {
  • §

    Create a name for our new asset

                    const name = `user-upload-${counter}`;
                    counter++;
  • §

    Add the image to the DOM and create our asset from it

                    const img = document.createElement('img');
    
                    /** @ts-expect-error */
                    img.src = reader.result;
                    img.id = name;
                    store.appendChild(img);
    
                    scrawl.importDomImage(`#${name}`);
  • §

    Update our Picture entity’s asset attribute so it displays the new image

                    targets.forEach(target => {
    
                        if (target.type === 'Group') {
    
                            target.setArtefacts({
                                asset: name,
                            });
                        }
                        else {
    
                            target.set({
                                asset: name,
                            });
                        }
                    });
  • §

    HOW TO: set the Picture entity’s copy dimensions to take into account any difference between the old and new image’s dimensions

    • Because of asynch stuff, we need to wait for stuff to complete before performing this functionality
    • The Picture entity copies (for the sake of our sanity) a square part of the image. Thus we shall use the new image’s shorter dimension as the copy dimension and offset the longer copy start so we are viewing the middle of it
                    setTimeout(() => {
    
                        const asset = L.asset[name];
    
                        const width = asset.get('width'),
                            height = asset.get('height');
    
                        let copyStartX = 0,
                            copyStartY = 0,
                            dim = 0;
    
                        if (width > height) {
    
                            copyStartX = (width - height) / 2;
                            dim = height;
                        }
                        else {
    
                            copyStartY = (height - width) / 2;
                            dim = width;
                        }
    
                        targets.forEach(target => {
    
                            if (target.type === 'Group') {
    
                                target.setArtefacts({
                                    copyStartX,
                                    copyStartY,
                                    copyWidth: dim,
                                    copyHeight: dim,
                                });
                            }
                            else if (target.type !== 'Filter') {
    
                                target.set({
                                    copyStartX,
                                    copyStartY,
                                    copyWidth: dim,
                                    copyHeight: dim,
                                });
                            }
                        })
    
                        if (callback) setTimeout(callback, timeoutDelay);
                    }, timeoutDelay);
                }
            }
        };
    };
    
    const addCheckerboardBackground = (scrawl, canvas, namespace) => {
  • §

    Namespace boilerplate

        const name = (item) => `${namespace}-${item}`;
  • §

    Create the background

        const checkerboard = canvas.buildCell({
            name: name('checkerboard-background-cell'),
            dimensions: [16, 16],
            backgroundColor: '#999',
            cleared: false,
            compiled: false,
            shown: false,
        });
    
        scrawl.makeBlock({
            name: name('checkerboard-background-block-1'),
            group: name('checkerboard-background-cell'),
            dimensions: ['50%', '50%'],
            fillStyle: '#bbb',
        }).clone({
            name: name('checkerboard-background-block-2'),
            start: ['50%', '50%'],
        });
    
        checkerboard.clear();
        checkerboard.compile();
    
        scrawl.makeBlock({
            name: name('checkerboard-background'),
            group: canvas.get('baseName'),
            dimensions: ['100%', '100%'],
            fillStyle: name('checkerboard-background-cell'),
        });
    };
    
    export {
        reportSpeed,
        reportFullLibrary,
    
        killArtefact,
        killArtefactAndAnchor,
        killPolylineArtefact,
        killStyle,
        killTicker,
    
        addCheckerboardBackground,
        addImageDragAndDrop,
    }