add segment brush

This commit is contained in:
DD Liu 2017-07-27 17:36:17 -04:00
parent 4a5c9f0b54
commit 27f7102b06
3 changed files with 110 additions and 75 deletions

View file

@ -1,7 +1,13 @@
import paper from 'paper';
import log from '../log/log';
import broadBrushHelper from './broad-brush-helper';
import segmentBrushHelper from './segment-brush-helper';
/**
* Shared code for the brush and eraser tool. Adds functions on the paper tool object
* to handle mouse events, which are delegated to broad-brush-helper and segment-brush-helper
* based on the brushSize in the state.
*/
class BlobTool {
static get BROAD () {
@ -15,7 +21,7 @@ class BlobTool {
// Segment brush has performance issues at low threshold, but broad brush has weird corners
// which get more obvious the bigger it is
static get THRESHOLD () {
return 100000;
return 9;
}
setOptions (options) {
@ -62,10 +68,8 @@ class BlobTool {
path.strokeWidth = 1;
}
} else {
// TODO keep a separate active toolbar style for brush vs pen?
//path = pg.stylebar.applyActiveToolbarStyle(path);
//TODO FIX
// TODO: Add back brush styling. Keep a separate active toolbar style for brush vs pen.
// path = pg.stylebar.applyActiveToolbarStyle(path);
path.fillColor = 'black';
if (path === this.cursorPreview) {
@ -80,8 +84,7 @@ class BlobTool {
tool.fixedDistance = 1;
broadBrushHelper(tool);
// TODO add
//pg.segmentbrushhelper(tool, options);
segmentBrushHelper(tool);
tool.onMouseMove = function (event) {
tool.resizeCursorIfNeeded(event.point);
@ -111,10 +114,10 @@ class BlobTool {
if (event.event.button > 0) return; // only first mouse button
if (this.brush === BlobTool.BROAD) {
this.onBroadMouseDrag(event);
} else if (this.brush === Blob.SEGMENT) {
} else if (this.brush === BlobTool.SEGMENT) {
this.onSegmentMouseDrag(event);
} else {
log.warning(`Brush type does not exist: ${this.brush}`);
log.warn(`Brush type does not exist: ${this.brush}`);
}
this.cursorPreview.bringToFront();
@ -132,7 +135,7 @@ class BlobTool {
} else if (this.brush === BlobTool.SEGMENT) {
lastPath = this.onSegmentMouseUp(event);
} else {
log.warning(`Brush type does not exist: ${this.brush}`);
log.warn(`Brush type does not exist: ${this.brush}`);
}
if (isEraser) {
@ -195,8 +198,8 @@ class BlobTool {
paths.splice(i, 1);
}
}
// TODO FIX
//pg.undo.snapshot('broadbrush');
// TODO: Add back undo
// pg.undo.snapshot('broadbrush');
};
tool.mergeEraser = function (lastPath) {
@ -210,8 +213,8 @@ class BlobTool {
// Eraser didn't hit anything selected, so assume they meant to erase from all instead of from subset
// and deselect the selection
if (items.length === 0) {
// TODO FIX
//pg.selection.clearSelection();
// TODO: Add back selection handling
// pg.selection.clearSelection();
items = paper.project.getItems({
match: function (item) {
return tool.isMergeable(lastPath, item) && tool.touches(lastPath, item);
@ -225,7 +228,7 @@ class BlobTool {
// Gather path segments
const subpaths = [];
// TODO handle compound path
// TODO: Handle compound path
if (items[i] instanceof paper.Path && !items[i].closed) {
const firstSeg = items[i].clone();
const intersections = firstSeg.getIntersections(lastPath);
@ -290,8 +293,8 @@ class BlobTool {
items[i].remove();
}
lastPath.remove();
// TODO FIX
//pg.undo.snapshot('eraser');
// TODO: Add back undo handling
// pg.undo.snapshot('eraser');
};
tool.colorMatch = function (existingPath, addedPath) {
@ -318,7 +321,7 @@ class BlobTool {
path1.hitTest(path2.firstSegment.point)) {
return true;
}
// TODO clean up these no point paths
// TODO: clean up these no point paths
return false;
};
@ -338,4 +341,4 @@ class BlobTool {
}
}
module.exports = BlobTool;
export default BlobTool;

View file

@ -1,8 +1,14 @@
// Broadbrush based on http://paperjs.org/tutorials/interaction/working-with-mouse-vectors/
const paper = require('paper');
import paper from 'paper';
/**
* Applies segment brush functions to the tool.
* Applies broad brush functions to the tool. Call them when the corresponding mouse event happens
* to get the broad brush behavior.
*
* Broad brush draws strokes by drawing points equidistant from the mouse event, perpendicular to the
* direction of motion. Shortcomings are that this path can cross itself, and 180 degree turns result
* in a flat edge.
*
* @param {!Tool} tool paper.js mouse object
*/
const broadBrushHelper = function (tool) {
@ -102,4 +108,4 @@ const broadBrushHelper = function (tool) {
};
};
module.exports = broadBrushHelper;
export default broadBrushHelper;

View file

@ -1,59 +1,85 @@
// Applies segment brush functions to the tool
pg.segmentbrushhelper = function(tool, options) {
var lastPoint, finalPath;
import paper from 'paper';
tool.onSegmentMouseDown = function(event) {
tool.minDistance = options.brushSize/4;
tool.maxDistance = options.brushSize;
finalPath = new Path.Circle({
center: event.point,
radius: options.brushSize/2
});
tool.stylePath(finalPath);
lastPoint = event.point;
};
tool.onSegmentMouseDrag = function(event) {
var step = (event.delta).normalize(options.brushSize/2);
var handleVec = step.clone();
handleVec.length = options.brushSize/2;
handleVec.angle += 90;
/**
* Applies segment brush functions to the tool. Call them when the corresponding mouse event happens
* to get the broad brush behavior.
*
* Segment brush draws by creating a rounded rectangle for each mouse move event and merging all of
* those shapes. Unlike the broad brush, the resulting shape will not self-intersect and when you make
* 180 degree turns, you will get a rounded point as expected. Shortcomings include that performance is
* worse, especially as the number of segments to join increase, and that there are problems in paper.js
* with union on shapes with curves, so that chunks of the union tend to disappear.
* (https://github.com/paperjs/paper.js/issues/1321)
*
* @param {!Tool} tool paper.js mouse object
*/
const segmentBrushHelper = function (tool) {
let lastPoint;
let finalPath;
var path = new Path();
path = pg.stylebar.applyActiveToolbarStyle(path);
path.strokeColor = null;
// Add handles to round the end caps
path.add(new Segment(lastPoint - step, -handleVec, handleVec));
step.angle += 90;
tool.onSegmentMouseDown = function (event) {
if (event.event.button > 0) return; // only first mouse button
path.add(event.lastPoint + step);
path.insert(0, event.lastPoint - step);
path.add(event.point + step);
path.insert(0, event.point - step);
tool.minDistance = this.options.brushSize / 4;
tool.maxDistance = this.options.brushSize;
finalPath = new paper.Path.Circle({
center: event.point,
radius: this.options.brushSize / 2
});
tool.stylePath(finalPath);
lastPoint = event.point;
};
tool.onSegmentMouseDrag = function (event) {
if (event.event.button > 0) return; // only first mouse button
// Add end cap
step.angle -= 90;
path.add(new Segment(event.point + step, handleVec, -handleVec));
path.closed = true;
// The unite function on curved paths does not always work (sometimes deletes half the path)
// so we have to flatten.
path.flatten(options.brushSize/5);
lastPoint = event.point;
var newPath = finalPath.unite(path);
path.remove();
finalPath.remove();
finalPath = newPath;
};
const step = (event.delta).normalize(this.options.brushSize / 2);
const handleVec = step.clone();
handleVec.length = this.options.brushSize / 2;
handleVec.angle += 90;
tool.onSegmentMouseUp = function(event) {
// TODO: This smoothing tends to cut off large portions of the path! Would like to eventually
// add back smoothing, maybe a custom implementation that only applies to a subset of the line?
const path = new paper.Path();
// TODO: Add back brush styling
// path = pg.stylebar.applyActiveToolbarStyle(path);
path.fillColor = 'black';
// Smooth the path.
finalPath.simplify(2);
// console.log(finalPath.segments);
return finalPath;
};
}
// Add handles to round the end caps
path.add(new paper.Segment(lastPoint.subtract(step), handleVec.multiply(-1), handleVec));
step.angle += 90;
path.add(event.lastPoint.add(step));
path.insert(0, event.lastPoint.subtract(step));
path.add(event.point.add(step));
path.insert(0, event.point.subtract(step));
// Add end cap
step.angle -= 90;
path.add(new paper.Segment(event.point.add(step), handleVec, handleVec.multiply(-1)));
path.closed = true;
// The unite function on curved paths does not always work (sometimes deletes half the path)
// so we have to flatten.
path.flatten(this.options.brushSize / 5);
lastPoint = event.point;
const newPath = finalPath.unite(path);
path.remove();
finalPath.remove();
finalPath = newPath;
};
tool.onSegmentMouseUp = function (event) {
if (event.event.button > 0) return; // only first mouse button
// TODO: This smoothing tends to cut off large portions of the path! Would like to eventually
// add back smoothing, maybe a custom implementation that only applies to a subset of the line?
// Smooth the path.
finalPath.simplify(2);
// console.log(finalPath.segments);
return finalPath;
};
};
export default segmentBrushHelper;