scratch-paint/src/helper/layer.js

284 lines
8.7 KiB
JavaScript
Raw Normal View History

2017-10-12 18:35:30 -04:00
import paper from '@scratch/paper';
import log from '../log/log';
2020-02-08 00:49:48 -05:00
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER} from './view';
import {isGroupItem} from './item';
2020-02-08 00:49:48 -05:00
const CROSSHAIR_SIZE = 16;
2017-09-11 14:23:30 -04:00
const _getLayer = function (layerString) {
for (const layer of paper.project.layers) {
if (layer.data && layer.data[layerString]) {
2017-09-11 14:23:30 -04:00
return layer;
}
}
};
2017-09-11 14:23:30 -04:00
const _getPaintingLayer = function () {
return _getLayer('isPaintingLayer');
2017-09-11 14:23:30 -04:00
};
2018-06-21 10:22:24 -04:00
/**
* Creates a canvas with width and height matching the art board size.
* @param {?number} width Width of the canvas. Defaults to ART_BOARD_WIDTH.
* @param {?number} height Height of the canvas. Defaults to ART_BOARD_HEIGHT.
* @return {HTMLCanvasElement} the canvas
*/
const createCanvas = function (width, height) {
const canvas = document.createElement('canvas');
canvas.width = width ? width : ART_BOARD_WIDTH;
canvas.height = height ? height : ART_BOARD_HEIGHT;
canvas.getContext('2d').imageSmoothingEnabled = false;
return canvas;
};
2018-04-06 14:41:30 -04:00
const clearRaster = function () {
2018-04-05 17:37:40 -04:00
const layer = _getLayer('isRasterLayer');
2018-04-06 14:41:30 -04:00
layer.removeChildren();
2018-04-05 17:37:40 -04:00
// Generate blank raster
2018-06-21 10:22:24 -04:00
const raster = new paper.Raster(createCanvas());
raster.canvas.getContext('2d').imageSmoothingEnabled = false;
2018-04-06 14:41:30 -04:00
raster.parent = layer;
raster.guide = true;
raster.locked = true;
raster.position = new paper.Point(ART_BOARD_WIDTH / 2, ART_BOARD_HEIGHT / 2);
2018-04-06 14:41:30 -04:00
};
const getRaster = function () {
const layer = _getLayer('isRasterLayer');
// Generate blank raster
if (layer.children.length === 0) {
clearRaster();
}
2018-04-05 17:37:40 -04:00
return _getLayer('isRasterLayer').children[0];
};
const getDragCrosshairLayer = function () {
return _getLayer('isDragCrosshairLayer');
};
2018-08-08 18:27:01 -04:00
const getBackgroundGuideLayer = function () {
return _getLayer('isBackgroundGuideLayer');
};
2018-04-09 17:47:11 -04:00
const _makeGuideLayer = function () {
const guideLayer = new paper.Layer();
guideLayer.data.isGuideLayer = true;
return guideLayer;
};
const getGuideLayer = function () {
2018-04-09 17:47:11 -04:00
let layer = _getLayer('isGuideLayer');
if (!layer) {
layer = _makeGuideLayer();
_getPaintingLayer().activate();
}
return layer;
};
const setGuideItem = function (item) {
item.locked = true;
item.guide = true;
if (isGroupItem(item)) {
for (let i = 0; i < item.children.length; i++) {
setGuideItem(item.children[i]);
}
}
};
/**
* Removes the guide layers, e.g. for purposes of exporting the image. Must call showGuideLayers to re-add them.
2018-04-09 17:47:11 -04:00
* @param {boolean} includeRaster true if the raster layer should also be hidden
* @return {object} an object of the removed layers, which should be passed to showGuideLayers to re-add them.
*/
2018-04-09 17:47:11 -04:00
const hideGuideLayers = function (includeRaster) {
2018-08-08 18:27:01 -04:00
const backgroundGuideLayer = getBackgroundGuideLayer();
const dragCrosshairLayer = getDragCrosshairLayer();
const guideLayer = getGuideLayer();
dragCrosshairLayer.remove();
guideLayer.remove();
backgroundGuideLayer.remove();
2018-04-09 17:47:11 -04:00
let rasterLayer;
if (includeRaster) {
rasterLayer = _getLayer('isRasterLayer');
rasterLayer.remove();
}
return {
dragCrosshairLayer: dragCrosshairLayer,
guideLayer: guideLayer,
2018-04-09 17:47:11 -04:00
backgroundGuideLayer: backgroundGuideLayer,
rasterLayer: rasterLayer
};
};
/**
* Add back the guide layers removed by calling hideGuideLayers. This must be done before any editing operations are
* taken in the paint editor.
* @param {!object} guideLayers object of the removed layers, which was returned by hideGuideLayers
*/
const showGuideLayers = function (guideLayers) {
const backgroundGuideLayer = guideLayers.backgroundGuideLayer;
const dragCrosshairLayer = guideLayers.dragCrosshairLayer;
const guideLayer = guideLayers.guideLayer;
2018-04-09 17:47:11 -04:00
const rasterLayer = guideLayers.rasterLayer;
if (rasterLayer && !rasterLayer.index) {
paper.project.addLayer(rasterLayer);
rasterLayer.sendToBack();
}
if (!backgroundGuideLayer.index) {
paper.project.addLayer(backgroundGuideLayer);
backgroundGuideLayer.sendToBack();
}
if (!dragCrosshairLayer.index) {
paper.project.addLayer(dragCrosshairLayer);
dragCrosshairLayer.bringToFront();
}
if (!guideLayer.index) {
paper.project.addLayer(guideLayer);
guideLayer.bringToFront();
}
if (paper.project.activeLayer !== _getPaintingLayer()) {
log.error(`Wrong active layer`);
log.error(paper.project.activeLayer.data);
2017-10-24 13:21:57 -04:00
}
};
const _makePaintingLayer = function () {
const paintingLayer = new paper.Layer();
paintingLayer.data.isPaintingLayer = true;
return paintingLayer;
};
2018-04-05 17:37:40 -04:00
const _makeRasterLayer = function () {
const rasterLayer = new paper.Layer();
rasterLayer.data.isRasterLayer = true;
2018-04-06 14:41:30 -04:00
clearRaster();
2018-04-05 17:37:40 -04:00
return rasterLayer;
};
const _makeBackgroundPaper = function (width, height, color) {
// creates a checkerboard path of width * height squares in color on white
let x = 0;
let y = 0;
const pathPoints = [];
while (x < width) {
pathPoints.push(new paper.Point(x, y));
x++;
pathPoints.push(new paper.Point(x, y));
y = y === 0 ? height : 0;
}
y = height - 1;
x = width;
while (y > 0) {
pathPoints.push(new paper.Point(x, y));
x = (x === 0 ? width : 0);
pathPoints.push(new paper.Point(x, y));
y--;
}
const vRect = new paper.Shape.Rectangle(new paper.Point(0, 0), new paper.Point(120, 90));
vRect.fillColor = '#fff';
vRect.guide = true;
vRect.locked = true;
const vPath = new paper.Path(pathPoints);
vPath.fillRule = 'evenodd';
vPath.fillColor = color;
vPath.guide = true;
vPath.locked = true;
const vGroup = new paper.Group([vRect, vPath]);
return vGroup;
};
// Helper function for drawing a crosshair
const _makeCrosshair = function (opacity, parent) {
2020-02-08 00:49:48 -05:00
const crosshair = new paper.Group();
const vLine2 = new paper.Path.Line(new paper.Point(0, -7), new paper.Point(0, 7));
vLine2.strokeWidth = 4;
vLine2.strokeColor = 'white';
vLine2.strokeCap = 'round';
crosshair.addChild(vLine2);
const hLine2 = new paper.Path.Line(new paper.Point(-7, 0), new paper.Point(7, 0));
hLine2.strokeWidth = 4;
hLine2.strokeColor = 'white';
hLine2.strokeCap = 'round';
crosshair.addChild(hLine2);
const circle2 = new paper.Shape.Circle(new paper.Point(0, 0), 5);
circle2.strokeWidth = 4;
circle2.strokeColor = 'white';
crosshair.addChild(circle2);
const vLine = new paper.Path.Line(new paper.Point(0, -7), new paper.Point(0, 7));
vLine.strokeWidth = 2;
vLine.strokeColor = '#ccc';
vLine.strokeCap = 'round';
crosshair.addChild(vLine);
const hLine = new paper.Path.Line(new paper.Point(-7, 0), new paper.Point(7, 0));
hLine.strokeWidth = 2;
hLine.strokeColor = '#ccc';
hLine.strokeCap = 'round';
crosshair.addChild(hLine);
const circle = new paper.Shape.Circle(new paper.Point(0, 0), 5);
circle.strokeWidth = 2;
circle.strokeColor = '#ccc';
crosshair.addChild(circle);
setGuideItem(crosshair);
crosshair.position = CENTER;
crosshair.opacity = opacity;
crosshair.parent = parent;
crosshair.applyMatrix = false;
parent.dragCrosshair = crosshair;
crosshair.scale(CROSSHAIR_SIZE / crosshair.bounds.width / paper.view.zoom);
};
const _makeDragCrosshairLayer = function () {
const dragCrosshairLayer = new paper.Layer();
_makeCrosshair(1, dragCrosshairLayer);
dragCrosshairLayer.data.isDragCrosshairLayer = true;
dragCrosshairLayer.visible = false;
return dragCrosshairLayer;
};
const _makeBackgroundGuideLayer = function () {
const guideLayer = new paper.Layer();
guideLayer.locked = true;
const vBackground = _makeBackgroundPaper(120, 90, '#E5E5E5');
vBackground.position = new paper.Point(ART_BOARD_WIDTH / 2, ART_BOARD_HEIGHT / 2);
vBackground.scaling = new paper.Point(8, 8);
vBackground.guide = true;
vBackground.locked = true;
_makeCrosshair(0.25, guideLayer);
2017-10-24 13:21:57 -04:00
guideLayer.data.isBackgroundGuideLayer = true;
return guideLayer;
};
const setupLayers = function () {
const backgroundGuideLayer = _makeBackgroundGuideLayer();
2018-04-05 17:37:40 -04:00
_makeRasterLayer();
const paintLayer = _makePaintingLayer();
const dragCrosshairLayer = _makeDragCrosshairLayer();
const guideLayer = _makeGuideLayer();
backgroundGuideLayer.sendToBack();
dragCrosshairLayer.bringToFront();
guideLayer.bringToFront();
paintLayer.activate();
2017-10-24 13:21:57 -04:00
};
export {
CROSSHAIR_SIZE,
2018-06-21 10:22:24 -04:00
createCanvas,
hideGuideLayers,
showGuideLayers,
getDragCrosshairLayer,
2017-10-24 13:21:57 -04:00
getGuideLayer,
2018-08-08 18:27:01 -04:00
getBackgroundGuideLayer,
2018-04-06 14:41:30 -04:00
clearRaster,
2018-04-05 17:37:40 -04:00
getRaster,
setGuideItem,
2017-10-24 13:21:57 -04:00
setupLayers
};