More work on main boolean code.

Start addressing self-intersecting paths, and remove resulting open paths for now.
This commit is contained in:
Jürg Lehni 2015-08-26 17:36:20 +02:00
parent d656c96191
commit da0d01ee09

View file

@ -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;