Ghost.js module

This commit is contained in:
Tim Mickel 2016-01-13 18:43:56 -05:00
parent 7e7a890d86
commit b1aad9ce58

View file

@ -1,19 +1,31 @@
var Ghost = function () {}; import Snap from 'snap';
Ghost.maskCanvas = document.createElement('canvas');
Ghost.maskData = {};
Ghost.linemask = 16;
Ghost.maskColor = 16;
Ghost.highlight = function (group) { import ScratchJr from '../editor/ScratchJr';
import SVGTools from './SVGTools';
import Paint from './Paint';
import PaintAction from './PaintAction';
import Layer from './Layer';
import Vector from '../geom/Vector';
import Transform from './Transform';
import SVG2Canvas from '../utils/SVG2Canvas';
import {gn, setCanvasSize, newDiv} from '../utils/lib';
export let maskCanvas = document.createElement('canvas');
export let maskData = {};
export let linemask = 16;
let maskColor = 16;
export default class Ghost {
static highlight (group) {
Ghost.clearLayer(); Ghost.clearLayer();
var g = SVGTools.createGroup(gn('draglayer'), 'ghostgroup'); var g = SVGTools.createGroup(gn('draglayer'), 'ghostgroup');
g.setAttribute('class', 'active3d'); g.setAttribute('class', 'active3d');
for (var i = 0; i < group.length; i++) { for (var i = 0; i < group.length; i++) {
Ghost.hightlightElem(g, group[i], 0.5, '5,5', 'black', 3); Ghost.hightlightElem(g, group[i], 0.5, '5,5', 'black', 3);
} }
}; }
Ghost.hightlightElem = function (p, elem, opacity, space, c, sw) { static hightlightElem (p, elem, opacity, space, c, sw) {
if (elem.tagName == 'g') { if (elem.tagName == 'g') {
for (var i = 0; i < elem.childElementCount; i++) { for (var i = 0; i < elem.childElementCount; i++) {
Ghost.hightlightElem(p, elem.childNodes[i], opacity, space, c, sw); Ghost.hightlightElem(p, elem.childNodes[i], opacity, space, c, sw);
@ -23,9 +35,9 @@ Ghost.hightlightElem = function (p, elem, opacity, space, c, sw) {
Ghost.getKid(p, elem, opacity, space, c, sw); Ghost.getKid(p, elem, opacity, space, c, sw);
} }
} }
}; }
Ghost.hasGhost = function (elem) { // too slow if there are too many--> doing this to minimize overlapping ghosts static hasGhost (elem) { // too slow if there are too many--> doing this to minimize overlapping ghosts
if (!elem.id) { if (!elem.id) {
return true; return true;
} }
@ -37,9 +49,9 @@ Ghost.hasGhost = function (elem) { // too slow if there are too many--> doing th
return true; return true;
} }
return (!gn(mfill)); return (!gn(mfill));
}; }
Ghost.clearLayer = function () { static clearLayer () {
var p = gn('draglayer'); var p = gn('draglayer');
if (!p) { if (!p) {
return; return;
@ -47,48 +59,48 @@ Ghost.clearLayer = function () {
while (p.childElementCount > 0) { while (p.childElementCount > 0) {
p.removeChild(p.childNodes[0]); p.removeChild(p.childNodes[0]);
} }
}; }
/////////////////////////////////// ///////////////////////////////////
// Ghost Management // Ghost Management
/////////////////////////////////// ///////////////////////////////////
Ghost.findTarget = function (evt) { static findTarget (evt) {
Ghost.clearLayer(); Ghost.clearLayer();
if (evt == null) { if (evt == null) {
return null; return null;
} }
var pt = PaintAction.getScreenPt(evt); var pt = PaintAction.getScreenPt(evt);
if (Ghost.outsideArea(Vector.floor(Vector.scale(pt, Paint.currentZoom)), Ghost.maskCanvas)) { if (Ghost.outsideArea(Vector.floor(Vector.scale(pt, Paint.currentZoom)), maskCanvas)) {
return null; return null;
} else { } else {
return Ghost.allTools(pt); return Ghost.allTools(pt);
} }
}; }
Ghost.findWho = function (evt) { // just gets the object clicked static findWho (evt) { // just gets the object clicked
Ghost.clearLayer(); Ghost.clearLayer();
if (evt == null) { if (evt == null) {
return null; return null;
} }
var pt = PaintAction.getScreenPt(evt); var pt = PaintAction.getScreenPt(evt);
var color = Ghost.getPtColor(pt); var color = Ghost.getPtColor(pt);
var id = Ghost.maskData[color]; var id = maskData[color];
var mt = (id && gn(id)) ? gn(id) : null; var mt = (id && gn(id)) ? gn(id) : null;
return (mt && mt.getAttribute('fixed') != 'yes') ? mt : Ghost.getHitObject(pt); return (mt && mt.getAttribute('fixed') != 'yes') ? mt : Ghost.getHitObject(pt);
}; }
Ghost.allTools = function (pt) { static allTools (pt) {
var color = Ghost.getPtColor(pt); var color = Ghost.getPtColor(pt);
var id = Ghost.maskData[color]; var id = maskData[color];
if (id) { if (id) {
return Ghost.hitSomething(pt, id, color); return Ghost.hitSomething(pt, id, color);
} else { } else {
return Ghost.notHitted(pt); return Ghost.notHitted(pt);
} }
}; }
Ghost.hitSomething = function (pt, id) { static hitSomething (pt, id) {
var mt = gn(id); var mt = gn(id);
var dogohst = true; var dogohst = true;
if (mt && mt.getAttribute('relatedto')) { if (mt && mt.getAttribute('relatedto')) {
@ -114,9 +126,9 @@ Ghost.hitSomething = function (pt, id) {
return Ghost.setGhostTo(mt); return Ghost.setGhostTo(mt);
} }
return null; return null;
}; }
Ghost.svgHit = function (pt) { static svgHit (pt) {
var rpos = Paint.root.createSVGRect(); var rpos = Paint.root.createSVGRect();
rpos.x = pt.x; rpos.x = pt.x;
rpos.y = pt.y; rpos.y = pt.y;
@ -132,14 +144,14 @@ Ghost.svgHit = function (pt) {
// the intersection lists. // the intersection lists.
return Ghost.svgHitHelper(gn('layer1'), pt); return Ghost.svgHitHelper(gn('layer1'), pt);
} }
}; }
/** /**
* Iterates all the path elements of the root and checks if 'pt' * Iterates all the path elements of the root and checks if 'pt'
* is inside the path. * is inside the path.
* This method uses the SnapSVG library (Apache 2 license) to perform the hit test. * This method uses the SnapSVG library (Apache 2 license) to perform the hit test.
*/ */
Ghost.svgHitHelper = function (root, pt) { static svgHitHelper (root, pt) {
var matches = []; var matches = [];
if (!root) { if (!root) {
return matches; return matches;
@ -154,16 +166,16 @@ Ghost.svgHitHelper = function (root, pt) {
} }
return matches; return matches;
}; }
Ghost.setGhostTo = function (mt) { static setGhostTo (mt) {
var g = SVGTools.createGroup(gn('draglayer'), 'ghostlayer'); var g = SVGTools.createGroup(gn('draglayer'), 'ghostlayer');
Ghost.setDashBorder(g, mt, 0.7, '5,5', 'black', 3); Ghost.setDashBorder(g, mt, 0.7, '5,5', 'black', 3);
return mt; return mt;
}; }
Ghost.notHitted = function (pt) { static notHitted (pt) {
var mt; var mt;
switch (Paint.mode) { switch (Paint.mode) {
case 'select': case 'select':
@ -198,9 +210,9 @@ Ghost.notHitted = function (pt) {
return Ghost.setGhostTo(mt); return Ghost.setGhostTo(mt);
} }
return null; return null;
}; }
Ghost.getActualHit = function (mt, pt) { static getActualHit (mt, pt) {
if (!mt) { if (!mt) {
return null; return null;
} }
@ -217,9 +229,9 @@ Ghost.getActualHit = function (mt, pt) {
mt = obj; mt = obj;
} }
return mt; return mt;
}; }
Ghost.contains = function (e1, e2) { static contains (e1, e2) {
var box1 = SVGTools.getBox(e1); var box1 = SVGTools.getBox(e1);
var box2 = SVGTools.getBox(e2); var box2 = SVGTools.getBox(e2);
var boxi = box1.intersection(box2); var boxi = box1.intersection(box2);
@ -227,9 +239,9 @@ Ghost.contains = function (e1, e2) {
return false; return false;
} }
return boxi.isEqual(box2); return boxi.isEqual(box2);
}; }
Ghost.hittedSingleObject = function (obj, pt) { static hittedSingleObject (obj, pt) {
var ctx = ScratchJr.workingCanvas.getContext('2d'); var ctx = ScratchJr.workingCanvas.getContext('2d');
ctx.clearRect(0, 0, ScratchJr.workingCanvas.width, ScratchJr.workingCanvas.height); ctx.clearRect(0, 0, ScratchJr.workingCanvas.width, ScratchJr.workingCanvas.height);
ctx.save(); ctx.save();
@ -237,22 +249,22 @@ Ghost.hittedSingleObject = function (obj, pt) {
ctx.restore(); ctx.restore();
var pixel = ctx.getImageData(pt.x, pt.y, 1, 1).data; var pixel = ctx.getImageData(pt.x, pt.y, 1, 1).data;
return pixel[3] != 0; return pixel[3] != 0;
}; }
Ghost.getPtColor = function (pt) { static getPtColor (pt) {
pt = Vector.floor(Vector.scale(pt, Paint.currentZoom)); pt = Vector.floor(Vector.scale(pt, Paint.currentZoom));
if (Ghost.outsideArea(pt, Ghost.maskCanvas)) { if (Ghost.outsideArea(pt, maskCanvas)) {
return 0; return 0;
} }
var ctx = Ghost.maskCanvas.getContext('2d'); var ctx = maskCanvas.getContext('2d');
var pixel = ctx.getImageData(pt.x, pt.y, 1, 1).data; var pixel = ctx.getImageData(pt.x, pt.y, 1, 1).data;
var r = pixel[0]; var r = pixel[0];
var g = pixel[1]; var g = pixel[1];
var b = pixel[2]; var b = pixel[2];
return Ghost.getHex([r, g, b]); return Ghost.getHex([r, g, b]);
}; }
Ghost.outsideArea = function (node, canvas) { static outsideArea (node, canvas) {
if ((node.x < 0) || (node.x > (canvas.width - 1))) { if ((node.x < 0) || (node.x > (canvas.width - 1))) {
return true; return true;
} }
@ -260,9 +272,9 @@ Ghost.outsideArea = function (node, canvas) {
return true; return true;
} }
return false; return false;
}; }
Ghost.setDashBorder = function (p, elem, opacity, space, c, sw) { static setDashBorder (p, elem, opacity, space, c, sw) {
if (elem.tagName == 'g') { if (elem.tagName == 'g') {
for (var i = 0; i < elem.childElementCount; i++) { for (var i = 0; i < elem.childElementCount; i++) {
Ghost.setDashBorder(p, elem.childNodes[i], opacity, space, c, sw); Ghost.setDashBorder(p, elem.childNodes[i], opacity, space, c, sw);
@ -270,9 +282,9 @@ Ghost.setDashBorder = function (p, elem, opacity, space, c, sw) {
} else { } else {
Ghost.getKid(p, elem, opacity, space, c, sw); Ghost.getKid(p, elem, opacity, space, c, sw);
} }
}; }
Ghost.getKid = function (p, elem, opacity, space, c, sw) { static getKid (p, elem, opacity, space, c, sw) {
if (!sw) { if (!sw) {
sw = elem.getAttribute('stroke-width'); sw = elem.getAttribute('stroke-width');
} }
@ -329,30 +341,30 @@ Ghost.getKid = function (p, elem, opacity, space, c, sw) {
Transform.applyRotation(dash, ang); Transform.applyRotation(dash, ang);
} }
return dash; return dash;
}; }
////////////////////////////////////////////////// //////////////////////////////////////////////////
// Offscreen for cursor // Offscreen for cursor
////////////////////////////////////////////////// //////////////////////////////////////////////////
Ghost.drawOffscreen = function () { static drawOffscreen () {
setCanvasSize( setCanvasSize(
Ghost.maskCanvas, maskCanvas,
Math.round(Number(Paint.root.getAttribute('width')) * Paint.currentZoom), Math.round(Number(Paint.root.getAttribute('width')) * Paint.currentZoom),
Math.round(Number(Paint.root.getAttribute('height')) * Paint.currentZoom) Math.round(Number(Paint.root.getAttribute('height')) * Paint.currentZoom)
); );
var ctx = Ghost.maskCanvas.getContext('2d'); var ctx = maskCanvas.getContext('2d');
ctx.clearRect(0, 0, Ghost.maskCanvas.width, Ghost.maskCanvas.height); ctx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
var p = gn('layer1'); var p = gn('layer1');
if (!p) { if (!p) {
return; return;
} }
Ghost.maskData = {}; maskData = {};
Ghost.maskColor = 16; maskColor = 16;
Ghost.drawElements(p, ctx); Ghost.drawElements(p, ctx);
}; }
Ghost.drawElements = function (p, ctx) { static drawElements (p, ctx) {
for (var i = 0; i < p.childElementCount; i++) { for (var i = 0; i < p.childElementCount; i++) {
var elem = p.childNodes[i]; var elem = p.childNodes[i];
if (elem.id == 'pathdots') { if (elem.id == 'pathdots') {
@ -370,12 +382,12 @@ Ghost.drawElements = function (p, ctx) {
Ghost.drawElement(elem, ctx); Ghost.drawElement(elem, ctx);
} }
} }
}; }
Ghost.drawElement = function (elem, ctx) { static drawElement (elem, ctx) {
var c = Ghost.getRGB(Ghost.maskColor); var c = Ghost.getRGB(maskColor);
var bc = Ghost.getRGB(Ghost.maskColor + 8); var bc = Ghost.getRGB(maskColor + 8);
Ghost.maskColor += 16; maskColor += 16;
ctx.save(); ctx.save();
var nostroke = (!elem.getAttribute('stroke')) || (elem.getAttribute('stroke') == 'none'); var nostroke = (!elem.getAttribute('stroke')) || (elem.getAttribute('stroke') == 'none');
var n = Number(elem.getAttribute('stroke-width')); var n = Number(elem.getAttribute('stroke-width'));
@ -393,8 +405,8 @@ Ghost.drawElement = function (elem, ctx) {
if (!elem.getAttribute('fill') && !elem.getAttribute('stroke')) { if (!elem.getAttribute('fill') && !elem.getAttribute('stroke')) {
ctx.fillStyle = 'rgba(' + bc[0] + ',' + bc[1] + ',' + bc[2] + ',255)'; ctx.fillStyle = 'rgba(' + bc[0] + ',' + bc[1] + ',' + bc[2] + ',255)';
} }
Ghost.maskData[Ghost.getHex(c)] = elem.id; maskData[Ghost.getHex(c)] = elem.id;
Ghost.maskData[Ghost.getHex(bc)] = elem.id; maskData[Ghost.getHex(bc)] = elem.id;
var rot = Transform.extract(elem, 4); var rot = Transform.extract(elem, 4);
if (rot.angle != 0) { if (rot.angle != 0) {
Layer.rotateFromCenter(ctx, elem, rot.angle); Layer.rotateFromCenter(ctx, elem, rot.angle);
@ -407,7 +419,7 @@ Ghost.drawElement = function (elem, ctx) {
} }
ctx.save(); ctx.save();
ctx.scale(Paint.currentZoom, Paint.currentZoom); ctx.scale(Paint.currentZoom, Paint.currentZoom);
ctx.lineWidth = (Ghost.linemask) < n ? n : Ghost.linemask; ctx.lineWidth = (linemask) < n ? n : linemask;
ctx.fillStyle = 'rgba(' + c[0] + ',' + c[1] + ',' + c[2] + ',255)'; ctx.fillStyle = 'rgba(' + c[0] + ',' + c[1] + ',' + c[2] + ',255)';
ctx.strokeStyle = 'rgba(' + bc[0] + ',' + bc[1] + ',' + bc[2] + ',255)'; ctx.strokeStyle = 'rgba(' + bc[0] + ',' + bc[1] + ',' + bc[2] + ',255)';
rot = Transform.extract(elem, 4); rot = Transform.extract(elem, 4);
@ -416,13 +428,13 @@ Ghost.drawElement = function (elem, ctx) {
} }
SVG2Canvas.renderPathTips(elem, ctx); SVG2Canvas.renderPathTips(elem, ctx);
ctx.restore(); ctx.restore();
}; }
Ghost.getRGB = function (color) { static getRGB (color) {
return [Number((color >> 16) & 255), Number((color >> 8) & 255), Number(color & 255)]; return [Number((color >> 16) & 255), Number((color >> 8) & 255), Number(color & 255)];
}; }
Ghost.getHex = function (rgb) { static getHex (rgb) {
var r = rgb[0].toString(16); var r = rgb[0].toString(16);
if (r.length < 2) { if (r.length < 2) {
r = '0' + r; r = '0' + r;
@ -436,9 +448,9 @@ Ghost.getHex = function (rgb) {
b = '0' + b; b = '0' + b;
} }
return r + g + b; return r + g + b;
}; }
Ghost.getHitObject = function (pt, isTip, exclude) { static getHitObject (pt, isTip, exclude) {
var list = Ghost.svgHit(pt); var list = Ghost.svgHit(pt);
pt = Vector.floor(Vector.scale(pt, Paint.currentZoom)); pt = Vector.floor(Vector.scale(pt, Paint.currentZoom));
if (!Paint.root) { if (!Paint.root) {
@ -454,9 +466,9 @@ Ghost.getHitObject = function (pt, isTip, exclude) {
} }
ctx.clearRect(0, 0, ScratchJr.workingCanvas.width, ScratchJr.workingCanvas.height); ctx.clearRect(0, 0, ScratchJr.workingCanvas.width, ScratchJr.workingCanvas.height);
return Ghost.findHit(list, pt, ScratchJr.workingCanvas.getContext('2d'), isTip, exclude); return Ghost.findHit(list, pt, ScratchJr.workingCanvas.getContext('2d'), isTip, exclude);
}; }
Ghost.findHit = function (list, pt, ctx, isTip, exclude) { static findHit (list, pt, ctx, isTip, exclude) {
for (var i = list.length - 1; i >= 0; i--) { for (var i = list.length - 1; i >= 0; i--) {
var elem = list[i]; var elem = list[i];
if (exclude && (elem == exclude)) { if (exclude && (elem == exclude)) {
@ -472,26 +484,27 @@ Ghost.findHit = function (list, pt, ctx, isTip, exclude) {
} }
} }
return null; return null;
}; }
//////////////////// ////////////////////
// Debugging hit masks // Debugging hit masks
//////////////////////// ////////////////////////
Ghost.showmask = function () { static showmask () {
var mask = newDiv(Paint.frame, 0, 0, Ghost.maskCanvas.width, Ghost.maskCanvas.height, var mask = newDiv(Paint.frame, 0, 0, maskCanvas.width, maskCanvas.height,
{ {
position: 'absolute', position: 'absolute',
zIndex: ScratchJr.layerTop + 20 zIndex: ScratchJr.layerTop + 20
}); });
mask.setAttribute('id', 'ghostmask'); mask.setAttribute('id', 'ghostmask');
mask.appendChild(Ghost.maskCanvas); mask.appendChild(maskCanvas);
}; }
Ghost.off = function () { static off () {
gn('ghostmask').style.visibility = 'hidden'; gn('ghostmask').style.visibility = 'hidden';
}; }
Ghost.on = function () { static on () {
gn('ghostmask').style.visibility = 'visible'; gn('ghostmask').style.visibility = 'visible';
}; }
}