diff options
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 181 |
1 files changed, 171 insertions, 10 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e4c71a086..70b21309e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,3 +1,4 @@ +import { findSegment } from '@turf/turf'; import { Bezier } from 'bezier-js'; import { Colors } from 'browndash-components'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; @@ -739,7 +740,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // create a new curve by appending all curves of the current segment together in order to render a single new stroke. if (!e.shiftKey) { this._eraserLock++; - const segments = this.segmentInkStroke(intersect.inkView, intersect.t); + const segments = this.segmentInkStroke(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it + // we delete the original ink stroke, then create new strokes for each of the remaining segments + // we do this through the hack of calling the stroke Gesture code as if the strokes had just be drawn by hand + // [[(0,0), (3,7), (9,4), (4,5)],[(4,5), (13,17), (19,14), (14,15)], [(23,19),(...}]] + // [[(0,0), (3,7), (9,4), (4,5), (13,17), (19,14), (14,15)], [(23,19),(...}]] + // segments.map(segment => segment.map(pt => pt)) + // segments[0][3].x === segments[1][0].x + + // 3 == "3" true + // 3 === "3" falsen !== + + // for (var i = 0; i < segments.length; i ++) { + // if (segments[i][0].points.lastElement().x === segments[i+1][0].points.lastElement().x && segments[i][0].points.lastElement().y === segments[i+1][0].points.lastElement().y) { + // segments[i][0].points.pop(); + // segments[i][0].points = segments[i][0].points.concat(segments[i+1][0].points); + // segments.splice(i+1, 1); + // } + // i++; + // } segments.forEach(segment => this.forceStrokeGesture( e, @@ -750,7 +769,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection setTimeout(() => this._eraserLock--); } // Lower ink opacity to give the user a visual indicator of deletion. - intersect.inkView.layoutDoc.opacity = 0.5; + intersect.inkView.layoutDoc.opacity = 0.0; intersect.inkView.layoutDoc.dontIntersect = true; } }); @@ -796,12 +815,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ) .reduce( (intersections, { inkStroke, inkView }) => { - const { inkData } = inkStroke.inkScaledData(); + const { inkData } = inkStroke.inkScaledData(); // get bezier curve as set of control points // Convert from screen space to ink space for the intersection. const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint); const currPointInkSpace = inkStroke.ptFromScreen(currPoint); for (var i = 0; i < inkData.length - 3; i += 4) { + // iterate over each segment of bezier curve const rawIntersects = InkField.Segment(inkData, i).intersects({ + // segment's are indexed by 0, 4, 8, // compute all unique intersections p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y }, p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }, @@ -825,9 +846,106 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection */ @action segmentInkStroke = (ink: DocumentView, excludeT: number): Segment[] => { + // const segments: Segment[] = []; + // var segment: Segment = []; + // var segment2: Segment = []; + // var startSegmentT = 0; + // const { inkData } = (ink?.ComponentView as InkingStroke).inkScaledData(); + // This iterates through all segments of the curve and splits them where they intersect another curve. + // if 'excludeT' is specified, then any segment containing excludeT will be skipped (ie, deleted) + + // var hasSplit = false; + // var continueErasing = false; + // for (var i = 0; i < inkData.length - 3; i += 4) { + // const inkSegment: Bezier = InkField.Segment(inkData, i); + // const tVals: number[] = this.getInkIntersections(i, ink, inkSegment).sort(); + // console.log("TVALS", tVals); + // if (tVals.length) { + // if ((!hasSplit) && ((excludeT <= tVals[0]) || + // (excludeT > tVals[0] && excludeT < tVals.lastElement()) || continueErasing)) { + + // const closestTs = this.getClosestTs(tVals, excludeT, 0, tVals.length - 1); + // if (closestTs[0] != -1 && closestTs[1] != -1) { + // segment1.push(inkSegment.split(0, closestTs[0])); + // segment2.push(inkSegment.split(closestTs[1]).right); + // } else if (closestTs[0] == -1) { + // segment2.push(inkSegment.split(closestTs[1]).right); + // continueErasing = !continueErasing; + // } else { + // segment1.push(inkSegment.split(0, closestTs[0])); + // continueErasing = !continueErasing; + // } + // if (!continueErasing) { + // hasSplit = true; + // } + // // segment1 = []; + // // segment2 = []; + // } + // else { + // if (hasSplit) { + // segment2.push(inkSegment); + // } else { + // segment1.push(inkSegment); + // } + // } + // } else if (hasSplit) { + // segment2.push(inkSegment); + // } else { + // continueErasing = true; + // } + // } + + // if (segment1.length && (Math.abs(segment1[0].points[0].x - segment1[0].points.lastElement().x) > 0.5 || Math.abs(segment1[0].points[0].y - segment1[0].points.lastElement().y) > 0.5)) { + // segments.push(segment1); + // } + // if (segment2.length && (Math.abs(segment2[0].points[0].x - segment2[0].points.lastElement().x) > 0.5 || Math.abs(segment2[0].points[0].y - segment2[0].points.lastElement().y) > 0.5)) { + // segments.push(segment2); + // } + // if (excludeT < startSegmentT || excludeT > inkData.length / 4) { + // segment1.length && segments.push(segment1); + // } + // const tVals = [] + // for (var i = 0; i < inkData.length - 3; i += 4) { + // const inkSegment = InkField.Segment(inkData, i); + // // Getting all t-value intersections of the current curve with all other curves. + // tVals.push(this.getInkIntersections(i, ink, inkSegment)); + // tVals.sort(); + + // if (tVals.length) { + // const docCurveTVal = tVals.lastElement() + Math.floor(i / 4); + // if (excludeT > docCurveTVal) { + // continue; + // } else { + // const closestTs = this.getClosestTs(tVals, excludeT, 0, tVals.length - 1); + // segment.push(inkSegment.split(0, closestTs[0])); + // if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) { + // segments.push(segment); + // } + // } + + // const closestT = this.getClosestT(tVals, excludeT, 0, tVals.length - 1); + // if (closestT != undefined) { + // var startSegmentT = closestT[0] + // const localStartTVal = startSegmentT - Math.floor(i / 4); + // segment.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, startSegmentT)) + // if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) { + // segments.push(segment); + // } + // const split = inkSegment.split(closestT[1]).right; + // if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) { + // segments.push([split]); + // } + // } + // } else { + // segment.push(inkSegment); + // segments.push(segment); + // } + // } const segments: Segment[] = []; - var segment: Segment = []; + var segment1: Segment = []; + var segment2: Segment = []; var startSegmentT = 0; + var hasSplit = false; const { inkData } = (ink?.ComponentView as InkingStroke).inkScaledData(); // This iterates through all segments of the curve and splits them where they intersect another curve. // if 'excludeT' is specified, then any segment containing excludeT will be skipped (ie, deleted) @@ -840,23 +958,66 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const docCurveTVal = t + Math.floor(i / 4); if (excludeT < startSegmentT || excludeT > docCurveTVal) { const localStartTVal = startSegmentT - Math.floor(i / 4); - t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t)); - segment.length && segments.push(segment); + if (!hasSplit) { + t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment1.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t)); + } else { + t !== (localStartTVal < 0 ? 0 : localStartTVal) && segment2.push(inkSegment.split(localStartTVal < 0 ? 0 : localStartTVal, t)); + } + // if (segment.length && (Math.abs(segment[0].points[0].x - segment[0].points.lastElement().x) > 0.5 || Math.abs(segment[0].points[0].y - segment[0].points.lastElement().y) > 0.5)) segments.push(segment); } // start a new segment from the intersection t value - segment = tVals.length - 1 === index ? [inkSegment.split(t).right] : []; + if (tVals.length - 1 === index) { + const split = inkSegment.split(t).right; + if (!hasSplit) { + if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) segment1.push(split); + } else { + if (split && (Math.abs(split.points[0].x - split.points.lastElement().x) > 0.5 || Math.abs(split.points[0].y - split.points.lastElement().y) > 0.5)) segment2.push(split); + } + } startSegmentT = docCurveTVal; }); } else { - segment.push(inkSegment); + if (!hasSplit) { + segment1.push(inkSegment); + } else { + segment2.push(inkSegment); + } } } - if (excludeT < startSegmentT || excludeT > inkData.length / 4) { - segment.length && segments.push(segment); + if (segment1.length && (Math.abs(segment1[0].points[0].x - segment1[0].points.lastElement().x) > 0.5 || Math.abs(segment1[0].points[0].y - segment1[0].points.lastElement().y) > 0.5)) { + segments.push(segment1); + } + if (segment2.length && (Math.abs(segment2[0].points[0].x - segment2[0].points.lastElement().x) > 0.5 || Math.abs(segment2[0].points[0].y - segment2[0].points.lastElement().y) > 0.5)) { + segments.push(segment2); } return segments; }; + getClosestTs = (tVals: number[], excludeT: number, startIndex: number, endIndex: number): number[] => { + if (tVals[startIndex] >= excludeT) { + return [-1, tVals[startIndex]]; + } + else if (tVals[endIndex] < excludeT) { + return [tVals[endIndex], -1]; + } + else { + const mid = Math.floor(startIndex + endIndex / 2); + if (excludeT >= tVals[mid]) { + if (mid + 1 <= endIndex && tVals[mid + 1] > excludeT) { + return [tVals[mid], tVals[mid + 1]]; + } else { + return this.getClosestTs(tVals, excludeT, mid + 1, endIndex); + } + } else { + if (mid - 1 <= startIndex && tVals[mid - 1] < excludeT) { + return [tVals[mid - 1], tVals[mid]]; + } else { + return this.getClosestTs(tVals, excludeT, startIndex, mid - 1); + } + } + } + }; + // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection // call in a test for linearity bintersects = (curve: Bezier, otherCurve: Bezier) => { |