mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-05 20:32:00 -05:00
More work on main boolean code.
Start addressing self-intersecting paths, and remove resulting open paths for now.
This commit is contained in:
parent
d656c96191
commit
da0d01ee09
1 changed files with 48 additions and 21 deletions
|
@ -80,6 +80,18 @@ PathItem.inject(new function() {
|
||||||
splitPath(Curve.filterIntersections(
|
splitPath(Curve.filterIntersections(
|
||||||
_path1._getIntersections(_path2, null, []), true));
|
_path1._getIntersections(_path2, null, []), true));
|
||||||
|
|
||||||
|
/*
|
||||||
|
console.time('inter');
|
||||||
|
var locations = _path1._getIntersections(_path2, null, []);
|
||||||
|
console.timeEnd('inter');
|
||||||
|
if (_path2 && false) {
|
||||||
|
console.time('self');
|
||||||
|
_path1._getIntersections(null, null, locations);
|
||||||
|
_path2._getIntersections(null, null, locations);
|
||||||
|
console.timeEnd('self');
|
||||||
|
}
|
||||||
|
splitPath(Curve.filterIntersections(locations, true));
|
||||||
|
*/
|
||||||
var chain = [],
|
var chain = [],
|
||||||
segments = [],
|
segments = [],
|
||||||
// Aggregate of all curves in both operands, monotonic in y
|
// Aggregate of all curves in both operands, monotonic in y
|
||||||
|
@ -417,24 +429,25 @@ PathItem.inject(new function() {
|
||||||
* not
|
* not
|
||||||
* @return {Path[]} the contours traced
|
* @return {Path[]} the contours traced
|
||||||
*/
|
*/
|
||||||
function tracePaths(segments, monoCurves, operation, selfOp) {
|
function tracePaths(segments, monoCurves, operation) {
|
||||||
var segmentCount = 0;
|
var segmentCount = 0;
|
||||||
var segmentOffset = {};
|
var segmentOffset = {};
|
||||||
var reportSegments = false;
|
var reportSegments = false && !window.silent;
|
||||||
var reportWindings = false;
|
var reportWindings = false && !window.silent;
|
||||||
|
var textAngle = 0;
|
||||||
|
var fontSize = 4 / paper.project.activeLayer.scaling.x;
|
||||||
|
|
||||||
function labelSegment(seg, text, color) {
|
function labelSegment(seg, text, color) {
|
||||||
var textAngle = 30;
|
|
||||||
var point = seg.point;
|
var point = seg.point;
|
||||||
var key = Math.round(point.x * 1000) + ',' + Math.round(point.y * 1000);
|
var key = Math.round(point.x / 10) + ',' + Math.round(point.y / 10);
|
||||||
var offset = segmentOffset[key] || 0;
|
var offset = segmentOffset[key] || 0;
|
||||||
segmentOffset[key] = offset + 1;
|
segmentOffset[key] = offset + 1;
|
||||||
var text = new PointText({
|
var text = new PointText({
|
||||||
point: point.add(new Point(8, 4).rotate(textAngle).add(0, offset * 10)),
|
point: point.add(new Point(fontSize, fontSize / 2).rotate(textAngle).add(0, offset * fontSize * 1.2)),
|
||||||
content: text,
|
content: text,
|
||||||
justification: 'left',
|
justification: 'left',
|
||||||
fillColor: color,
|
fillColor: color,
|
||||||
fontSize: 8
|
fontSize: fontSize
|
||||||
});
|
});
|
||||||
text.pivot = text.globalToLocal(text.point);
|
text.pivot = text.globalToLocal(text.point);
|
||||||
text.rotation = textAngle;
|
text.rotation = textAngle;
|
||||||
|
@ -445,15 +458,18 @@ PathItem.inject(new function() {
|
||||||
return;
|
return;
|
||||||
new Path.Circle({
|
new Path.Circle({
|
||||||
center: seg.point,
|
center: seg.point,
|
||||||
radius: 3,
|
radius: fontSize / 2,
|
||||||
strokeColor: color
|
strokeColor: color,
|
||||||
|
strokeScaling: false
|
||||||
});
|
});
|
||||||
var inter = seg._intersection;
|
var inter = seg._intersection;
|
||||||
labelSegment(seg, '#' + paths.length + '.'
|
labelSegment(seg, '#' + (paths.length + 1) + '.'
|
||||||
+ (path ? path._segments.length : 0)
|
+ (path ? path._segments.length + 1 : 1)
|
||||||
+ ' ' + (segmentCount++) + '/' + index + ': ' + text
|
+ ' ' + (segmentCount++) + '/' + index + ': ' + text
|
||||||
+ ' v: ' + !!seg._visited
|
+ ' v: ' + !!seg._visited
|
||||||
+ ' p: ' + seg._path._id
|
+ ' p: ' + seg._path._id
|
||||||
|
+ ' x: ' + seg._point.x
|
||||||
|
+ ' y: ' + seg._point.y
|
||||||
+ ' op: ' + operator(seg._winding)
|
+ ' op: ' + operator(seg._winding)
|
||||||
+ ' o: ' + (inter && inter._overlap || 0)
|
+ ' o: ' + (inter && inter._overlap || 0)
|
||||||
+ ' w: ' + seg._winding
|
+ ' w: ' + seg._winding
|
||||||
|
@ -467,6 +483,8 @@ PathItem.inject(new function() {
|
||||||
labelSegment(seg, i
|
labelSegment(seg, i
|
||||||
+ ' i: ' + !!inter
|
+ ' i: ' + !!inter
|
||||||
+ ' p: ' + seg._path._id
|
+ ' p: ' + seg._path._id
|
||||||
|
+ ' x: ' + seg._point.x
|
||||||
|
+ ' y: ' + seg._point.y
|
||||||
+ ' o: ' + (inter && inter._overlap || 0)
|
+ ' o: ' + (inter && inter._overlap || 0)
|
||||||
+ ' w: ' + seg._winding
|
+ ' w: ' + seg._winding
|
||||||
, 'green');
|
, 'green');
|
||||||
|
@ -499,11 +517,11 @@ PathItem.inject(new function() {
|
||||||
// If the intersection segment is valid, try switching to
|
// If the intersection segment is valid, try switching to
|
||||||
// it, with an appropriate direction to continue traversal.
|
// it, with an appropriate direction to continue traversal.
|
||||||
// Else, stay on the same contour.
|
// Else, stay on the same contour.
|
||||||
if (added && (selfOp || !operator(seg._winding))
|
if (added && (!operator(seg._winding))
|
||||||
&& (inter = seg._intersection)
|
&& (inter = seg._intersection)
|
||||||
&& (interSeg = inter._segment)
|
&& (interSeg = inter._segment)
|
||||||
&& interSeg !== startSeg) {
|
&& interSeg !== startSeg) {
|
||||||
if (selfOp) {
|
if (interSeg._path === seg._path) {
|
||||||
// Switch to the intersection segment, if we are
|
// Switch to the intersection segment, if we are
|
||||||
// resolving self-Intersections.
|
// resolving self-Intersections.
|
||||||
seg._visited = interSeg._visited;
|
seg._visited = interSeg._visited;
|
||||||
|
@ -515,7 +533,7 @@ PathItem.inject(new function() {
|
||||||
// leave the overlapping area.
|
// leave the overlapping area.
|
||||||
// NOTE: We cannot check the next (overlapping)
|
// NOTE: We cannot check the next (overlapping)
|
||||||
// segment since its winding number will always be 2
|
// segment since its winding number will always be 2
|
||||||
drawSegment(seg, 'overlap', i, 'orange');
|
drawSegment(seg, 'overlap ' + dir, i, 'orange');
|
||||||
var curve = interSeg.getCurve();
|
var curve = interSeg.getCurve();
|
||||||
if (getWinding(curve.getPointAt(0.5, true),
|
if (getWinding(curve.getPointAt(0.5, true),
|
||||||
monoCurves, isHorizontal(curve)) === 1) {
|
monoCurves, isHorizontal(curve)) === 1) {
|
||||||
|
@ -589,8 +607,9 @@ PathItem.inject(new function() {
|
||||||
if (seg) {
|
if (seg) {
|
||||||
new Path.Circle({
|
new Path.Circle({
|
||||||
center: seg.point,
|
center: seg.point,
|
||||||
radius: 4,
|
radius: fontSize / 2,
|
||||||
fillColor: 'red'
|
fillColor: 'red',
|
||||||
|
strokeScaling: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,18 +622,26 @@ PathItem.inject(new function() {
|
||||||
if (seg && (seg === startSeg || seg === startInterSeg)) {
|
if (seg && (seg === startSeg || seg === startInterSeg)) {
|
||||||
path.firstSegment.setHandleIn((seg === startInterSeg
|
path.firstSegment.setHandleIn((seg === startInterSeg
|
||||||
? startInterSeg : seg)._handleIn);
|
? startInterSeg : seg)._handleIn);
|
||||||
|
path.setClosed(true);
|
||||||
|
if (reportSegments) {
|
||||||
|
console.log('Boolean operation completed',
|
||||||
|
'#' + (paths.length + 1) + '.' +
|
||||||
|
(path ? path._segments.length + 1 : 1));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
path.lastSegment._handleOut.set(0, 0);
|
// path.lastSegment._handleOut.set(0, 0);
|
||||||
console.error('Boolean operation results in open path, length =',
|
console.error('Boolean operation results in open path, segs =',
|
||||||
path._segments.length);
|
path._segments.length, 'length = ', path.getLength(),
|
||||||
|
'#' + (paths.length + 1) + '.' +
|
||||||
|
(path ? path._segments.length + 1 : 1));
|
||||||
|
path = null;
|
||||||
}
|
}
|
||||||
path.setClosed(true);
|
|
||||||
// Add the path to the result, while avoiding stray segments and
|
// Add the path to the result, while avoiding stray segments and
|
||||||
// incomplete paths. The amount of segments for valid paths depend
|
// incomplete paths. The amount of segments for valid paths depend
|
||||||
// on their geometry:
|
// on their geometry:
|
||||||
// - Closed paths with only straight lines need more than 2 segments
|
// - Closed paths with only straight lines need more than 2 segments
|
||||||
// - Closed paths with curves can consist of only one segment
|
// - Closed paths with curves can consist of only one segment
|
||||||
if (path._segments.length > path.isLinear() ? 2 : 0)
|
if (path && path._segments.length > path.isLinear() ? 2 : 0)
|
||||||
paths.push(path);
|
paths.push(path);
|
||||||
}
|
}
|
||||||
return paths;
|
return paths;
|
||||||
|
|
Loading…
Reference in a new issue