2017-10-12 18:35:30 -04:00
|
|
|
import paper from '@scratch/paper';
|
2017-09-11 14:23:30 -04:00
|
|
|
|
2017-12-22 11:32:06 -05:00
|
|
|
/** The ratio of the curve length to use for the handle length to convert squares into approximately circles. */
|
|
|
|
const HANDLE_RATIO = 0.3902628565;
|
|
|
|
|
2017-09-11 14:23:30 -04:00
|
|
|
const checkPointsClose = function (startPos, eventPoint, threshold) {
|
|
|
|
const xOff = Math.abs(startPos.x - eventPoint.x);
|
|
|
|
const yOff = Math.abs(startPos.y - eventPoint.y);
|
|
|
|
if (xOff < threshold && yOff < threshold) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getRandomInt = function (min, max) {
|
|
|
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getRandomBoolean = function () {
|
|
|
|
return getRandomInt(0, 2) === 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Thanks Mikko Mononen! https://github.com/memononen/stylii
|
|
|
|
const snapDeltaToAngle = function (delta, snapAngle) {
|
|
|
|
let angle = Math.atan2(delta.y, delta.x);
|
|
|
|
angle = Math.round(angle / snapAngle) * snapAngle;
|
|
|
|
const dirx = Math.cos(angle);
|
|
|
|
const diry = Math.sin(angle);
|
|
|
|
const d = (dirx * delta.x) + (diry * delta.y);
|
|
|
|
return new paper.Point(dirx * d, diry * d);
|
|
|
|
};
|
|
|
|
|
2017-12-08 18:27:23 -05:00
|
|
|
const _getDepth = function (item) {
|
|
|
|
let temp = item;
|
|
|
|
let depth = 0;
|
|
|
|
while (!(temp instanceof paper.Layer)) {
|
|
|
|
depth++;
|
|
|
|
if (temp.parent === null) {
|
|
|
|
// This item isn't attached to a layer, so it's not on the canvas and can't be compared.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
temp = temp.parent;
|
|
|
|
}
|
|
|
|
return depth;
|
|
|
|
};
|
|
|
|
|
2017-11-03 17:55:02 -04:00
|
|
|
const sortItemsByZIndex = function (a, b) {
|
|
|
|
if (a === null || b === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2017-12-08 18:27:23 -05:00
|
|
|
|
|
|
|
// Get to the same depth in the project tree
|
2017-11-03 17:55:02 -04:00
|
|
|
let tempA = a;
|
|
|
|
let tempB = b;
|
2017-12-08 18:27:23 -05:00
|
|
|
let aDepth = _getDepth(a);
|
|
|
|
let bDepth = _getDepth(b);
|
|
|
|
while (bDepth > aDepth) {
|
|
|
|
tempB = tempB.parent;
|
|
|
|
bDepth--;
|
|
|
|
}
|
|
|
|
while (aDepth > bDepth) {
|
|
|
|
tempA = tempA.parent;
|
|
|
|
aDepth--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step up until they share parents. When they share parents, compare indices.
|
|
|
|
while (tempA && tempB) {
|
|
|
|
if (tempB === tempA) {
|
|
|
|
return 0;
|
|
|
|
} else if (tempB.parent === tempA.parent) {
|
|
|
|
if (tempB.parent instanceof paper.CompoundPath) {
|
|
|
|
// Neither is on top of the other in a compound path. Return in order of decreasing size.
|
|
|
|
return Math.abs(tempB.area) - Math.abs(tempA.area);
|
2017-11-03 17:55:02 -04:00
|
|
|
}
|
2017-12-08 18:27:23 -05:00
|
|
|
return parseFloat(tempA.index) - parseFloat(tempB.index);
|
2017-11-03 17:55:02 -04:00
|
|
|
}
|
2017-12-08 18:27:23 -05:00
|
|
|
tempB = tempB.parent;
|
2017-11-03 17:55:02 -04:00
|
|
|
tempA = tempA.parent;
|
|
|
|
}
|
2017-12-08 18:27:23 -05:00
|
|
|
|
2017-11-03 17:55:02 -04:00
|
|
|
// No shared hierarchy
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2018-03-14 18:12:37 -04:00
|
|
|
// Expand the size of the path by amount all around
|
|
|
|
const expandBy = function (path, amount) {
|
2017-11-06 16:57:53 -05:00
|
|
|
const center = path.position;
|
2017-12-08 18:27:23 -05:00
|
|
|
let pathArea = path.area;
|
2017-11-06 16:57:53 -05:00
|
|
|
for (const seg of path.segments) {
|
2018-03-14 18:12:37 -04:00
|
|
|
const delta = seg.point.subtract(center)
|
2017-12-08 18:27:23 -05:00
|
|
|
.normalize()
|
2018-03-14 18:12:37 -04:00
|
|
|
.multiply(amount);
|
|
|
|
seg.point = seg.point.add(delta);
|
2017-12-08 18:27:23 -05:00
|
|
|
// If that made the path area smaller, go the other way.
|
2018-03-14 18:12:37 -04:00
|
|
|
if (path.area < pathArea) seg.point = seg.point.subtract(delta.multiply(2));
|
2017-12-08 18:27:23 -05:00
|
|
|
pathArea = path.area;
|
2017-11-06 16:57:53 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-16 18:08:17 -04:00
|
|
|
// Do for all nested items in groups
|
|
|
|
const _doRecursively = function (item, func) {
|
2017-12-22 11:48:42 -05:00
|
|
|
if (item instanceof paper.Group) {
|
|
|
|
for (const child of item.children) {
|
2018-04-16 18:08:17 -04:00
|
|
|
_doRecursively(child, func);
|
2017-12-22 11:48:42 -05:00
|
|
|
}
|
2018-04-16 18:08:17 -04:00
|
|
|
} else {
|
|
|
|
func(item);
|
2017-12-22 11:48:42 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-16 18:08:17 -04:00
|
|
|
// Make item clockwise. Drill down into groups.
|
|
|
|
const ensureClockwise = function (root) {
|
|
|
|
_doRecursively(root, item => {
|
|
|
|
if (item instanceof paper.PathItem) {
|
|
|
|
item.clockwise = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// Scale item and its strokes by factor
|
|
|
|
const scaleWithStrokes = function (root, factor, pivot) {
|
|
|
|
_doRecursively(root, item => {
|
|
|
|
if (item.strokeWidth) {
|
|
|
|
item.strokeWidth = item.strokeWidth * factor;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
root.scale(factor, pivot);
|
|
|
|
};
|
|
|
|
|
2017-09-11 14:23:30 -04:00
|
|
|
export {
|
2017-12-22 11:32:06 -05:00
|
|
|
HANDLE_RATIO,
|
2017-09-11 14:23:30 -04:00
|
|
|
checkPointsClose,
|
2017-12-22 11:48:42 -05:00
|
|
|
ensureClockwise,
|
2018-03-14 18:12:37 -04:00
|
|
|
expandBy,
|
2017-09-11 14:23:30 -04:00
|
|
|
getRandomInt,
|
|
|
|
getRandomBoolean,
|
2018-04-16 18:08:17 -04:00
|
|
|
scaleWithStrokes,
|
2017-11-03 17:55:02 -04:00
|
|
|
snapDeltaToAngle,
|
|
|
|
sortItemsByZIndex
|
2017-09-11 14:23:30 -04:00
|
|
|
};
|