2018-04-25 15:26:48 -04:00
|
|
|
import paper from '@scratch/paper';
|
|
|
|
import {getRaster} from '../layer';
|
2018-04-26 14:54:44 -04:00
|
|
|
import {forEachLinePoint, getBrushMark} from '../bitmap';
|
2018-06-21 10:22:24 -04:00
|
|
|
import {createCanvas, getGuideLayer} from '../layer';
|
2018-04-25 15:26:48 -04:00
|
|
|
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from '../view';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tool for drawing lines with the bitmap brush.
|
|
|
|
*/
|
|
|
|
class LineTool extends paper.Tool {
|
|
|
|
/**
|
2018-04-26 18:45:50 -04:00
|
|
|
* @param {!function} onUpdateImage A callback to call when the image visibly changes
|
2018-04-25 15:26:48 -04:00
|
|
|
*/
|
2018-04-26 18:45:50 -04:00
|
|
|
constructor (onUpdateImage) {
|
2018-04-25 15:26:48 -04:00
|
|
|
super();
|
2018-04-26 18:45:50 -04:00
|
|
|
this.onUpdateImage = onUpdateImage;
|
2018-04-25 15:26:48 -04:00
|
|
|
|
|
|
|
// We have to set these functions instead of just declaring them because
|
|
|
|
// paper.js tools hook up the listeners in the setter functions.
|
|
|
|
this.onMouseMove = this.handleMouseMove;
|
|
|
|
this.onMouseDown = this.handleMouseDown;
|
|
|
|
this.onMouseDrag = this.handleMouseDrag;
|
|
|
|
this.onMouseUp = this.handleMouseUp;
|
|
|
|
|
|
|
|
this.colorState = null;
|
|
|
|
this.active = false;
|
|
|
|
this.startPoint = null;
|
|
|
|
this.cursorPreview = null;
|
|
|
|
// Raster to which to draw
|
|
|
|
this.drawTarget = null;
|
|
|
|
}
|
|
|
|
setColor (color) {
|
|
|
|
this.color = color;
|
2018-06-14 12:01:01 -04:00
|
|
|
this.tmpCanvas = getBrushMark(this.size, this.color);
|
2018-04-25 15:26:48 -04:00
|
|
|
}
|
|
|
|
setLineSize (size) {
|
|
|
|
// For performance, make sure this is an integer
|
|
|
|
this.size = Math.max(1, ~~size);
|
2018-06-14 12:01:01 -04:00
|
|
|
this.tmpCanvas = getBrushMark(this.size, this.color);
|
2018-04-25 15:26:48 -04:00
|
|
|
}
|
|
|
|
// Draw a brush mark at the given point
|
|
|
|
draw (x, y) {
|
|
|
|
const roundedUpRadius = Math.ceil(this.size / 2);
|
|
|
|
this.drawTarget.drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));
|
|
|
|
}
|
|
|
|
updateCursorIfNeeded () {
|
|
|
|
if (!this.size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// The cursor preview was unattached from the view by an outside process,
|
|
|
|
// such as changing costumes or undo.
|
|
|
|
if (this.cursorPreview && !this.cursorPreview.parent) {
|
|
|
|
this.cursorPreview = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.cursorPreview || !(this.lastSize === this.size && this.lastColor === this.color)) {
|
|
|
|
if (this.cursorPreview) {
|
|
|
|
this.cursorPreview.remove();
|
|
|
|
}
|
|
|
|
|
2018-04-26 14:54:44 -04:00
|
|
|
this.tmpCanvas = getBrushMark(this.size, this.color);
|
2018-04-25 15:26:48 -04:00
|
|
|
this.cursorPreview = new paper.Raster(this.tmpCanvas);
|
|
|
|
this.cursorPreview.guide = true;
|
|
|
|
this.cursorPreview.parent = getGuideLayer();
|
|
|
|
this.cursorPreview.data.isHelperItem = true;
|
|
|
|
}
|
|
|
|
this.lastSize = this.size;
|
|
|
|
this.lastColor = this.color;
|
|
|
|
}
|
|
|
|
handleMouseMove (event) {
|
|
|
|
this.updateCursorIfNeeded();
|
|
|
|
this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);
|
|
|
|
}
|
|
|
|
handleMouseDown (event) {
|
|
|
|
if (event.event.button > 0) return; // only first mouse button
|
|
|
|
this.active = true;
|
2018-06-13 16:45:11 -04:00
|
|
|
|
|
|
|
if (this.cursorPreview) this.cursorPreview.remove();
|
2018-04-25 15:26:48 -04:00
|
|
|
|
2018-06-21 10:22:24 -04:00
|
|
|
const tmpCanvas = createCanvas();
|
2018-04-25 15:26:48 -04:00
|
|
|
this.drawTarget = new paper.Raster(tmpCanvas);
|
|
|
|
this.drawTarget.parent = getGuideLayer();
|
|
|
|
this.drawTarget.guide = true;
|
|
|
|
this.drawTarget.locked = true;
|
|
|
|
this.drawTarget.position = getRaster().position;
|
|
|
|
|
|
|
|
this.draw(event.point.x, event.point.y);
|
|
|
|
this.startPoint = event.point;
|
|
|
|
}
|
|
|
|
handleMouseDrag (event) {
|
|
|
|
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
|
|
|
|
|
|
|
// Clear
|
|
|
|
const context = this.drawTarget.canvas.getContext('2d');
|
|
|
|
context.clearRect(0, 0, ART_BOARD_WIDTH, ART_BOARD_HEIGHT);
|
|
|
|
|
|
|
|
forEachLinePoint(this.startPoint, event.point, this.draw.bind(this));
|
|
|
|
}
|
|
|
|
handleMouseUp (event) {
|
|
|
|
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
|
|
|
|
|
|
|
this.drawTarget.remove();
|
|
|
|
this.drawTarget = getRaster();
|
|
|
|
forEachLinePoint(this.startPoint, event.point, this.draw.bind(this));
|
|
|
|
this.drawTarget = null;
|
2018-04-26 18:45:50 -04:00
|
|
|
this.onUpdateImage();
|
2018-04-25 15:26:48 -04:00
|
|
|
|
|
|
|
this.lastPoint = null;
|
|
|
|
this.active = false;
|
|
|
|
|
|
|
|
this.updateCursorIfNeeded();
|
|
|
|
this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);
|
|
|
|
}
|
|
|
|
deactivateTool () {
|
|
|
|
this.active = false;
|
|
|
|
this.tmpCanvas = null;
|
|
|
|
if (this.cursorPreview) {
|
|
|
|
this.cursorPreview.remove();
|
|
|
|
this.cursorPreview = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default LineTool;
|