diff options
author | bobzel <zzzman@gmail.com> | 2021-11-25 21:45:06 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-11-25 21:45:06 -0500 |
commit | 8405ed2e3ebaf7ba7842a5619e9b252bf5eb9c4c (patch) | |
tree | e0ab0bd3af7c77910ea36267d8238a6fb56128f0 | |
parent | cc7c677399d1aafa4078741922ffcd0b81ad720f (diff) |
fixed up erasing ink by segments
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 156 |
1 files changed, 65 insertions, 91 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4aac49469..df690da49 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,7 +34,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/global/globalCssVariable import { Timeline } from "../../animationtimeline/Timeline"; import { ContextMenu } from "../../ContextMenu"; import { DocumentDecorations } from "../../DocumentDecorations"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkWidth, SetActiveFillColor, SetActiveInkColor } from "../../InkingStroke"; import { LightboxView } from "../../LightboxView"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; @@ -610,11 +610,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.removeMoveListeners(); this.removeEndListeners(); if (CurrentUserUtils.SelectedTool !== InkTool.None) { - if (this._deleteList.length > 0) { - this._deleteList.forEach(ink => { - ink.props.removeDocument?.(ink.props.Document); - }); - } + this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc)); this._prevPoint = this._currPoint = { X: -1, Y: -1 }; this._deleteList = []; this._batch?.end(); @@ -650,8 +646,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (CurrentUserUtils.SelectedTool !== InkTool.None) { this._currPoint = { X: e.clientX, Y: e.clientY }; // Erasing ink strokes if intersections occur. - const eraserIntersections: Intersection[] = this.getEraserIntersections(); - if (eraserIntersections) this.eraseInkStrokes(eraserIntersections); + this.eraseInkStrokes(this.getEraserIntersections()); } if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { if (this.props.isContentActive(true)) e.stopPropagation(); @@ -676,21 +671,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P eraseInkStrokes = (eraserIntersections: Intersection[]) => { eraserIntersections.forEach(intersect => { const ink = intersect.ink; - if (ink) { - if (!this._deleteList.includes(ink)) { - const segments: Segment[] = this.segmentInkStroke(ink, intersect.index ?? 0); - segments.forEach(segment => { - const newInkData: PointData[] = []; - // Appending all curves of the current segment together in order to render a single new stroke. - segment.forEach(curve => { - newInkData.push(...curve.points.map(p => ({ X: p.x, Y: p.y }))); - }); - GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, newInkData); - }); - this._deleteList.push(ink); - // Lowering ink opacity to give the user a visual indicator of deletion. - ink.Document.opacity = 0.5; - } + if (ink && !this._deleteList.includes(ink)) { + this._deleteList.push(ink); + SetActiveInkWidth(StrCast(ink.rootDoc.strokeWidth?.toString()) || "1"); + SetActiveInkColor(StrCast(ink.rootDoc.color?.toString()) || "black"); + // create a new curve by appending all curves of the current segment together in order to render a single new stroke. + this.segmentInkStroke(ink, intersect.t ?? 0).forEach(segment => + GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, + segment.reduce((data, curve) => [...data, ...curve.points + .map(p => ink.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 }) + ], [] as PointData[]))); + // Lower ink opacity to give the user a visual indicator of deletion. + ink.layoutDoc.opacity = 0.5; } }); } @@ -705,12 +697,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { const inkView = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView); - const ctrlPoints = Cast(inkView?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? []; - for (var i = 0; i < ctrlPoints.length - 3; i += 4) { - const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]]; + const inkStroke = inkView?.ComponentView as InkingStroke; + const { inkData } = inkStroke?.inkScaledData(); + for (var i = 0; i < inkData.length - 3; i += 4) { + const array = inkData.slice(i, i + 4); // Converting from screen space to ink space for the intersection. - const prevPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._prevPoint); - const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint); + const prevPointInkSpace = inkStroke?.ptFromScreen?.(this._prevPoint); + const currPointInkSpace = inkStroke?.ptFromScreen?.(this._currPoint); if (prevPointInkSpace && currPointInkSpace) { const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))); const intersects = curve.intersects({ @@ -720,7 +713,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (inkView && intersects) { for (const val of intersects) { // Casting t-value from type: (string | number) to number for comparisons. - const t = +val.toString(); + const t = +(Number(val) + Math.floor(i / 4)).toString(); // add start of curve segment to convert from local t value to t value along complete curve var unique: boolean = true; // Ensuring there are no duplicate intersections in the list returned. for (const prevIntersect of intersections) { @@ -729,7 +722,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P break; } } - if (unique) intersections.push({ t: +t.toString(), ink: inkView, curve: curve, index: i }); + if (unique) intersections.push({ t: +t.toString(), ink: inkView, curve: curve }); } } } @@ -742,57 +735,40 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P * Performs segmentation of the ink stroke - creates "segments" or subsections of the current ink stroke at points in which the * ink stroke intersects any other ink stroke (including itself). * @param ink The ink DocumentView intersected by the eraser. - * @param excludeCurve The index of the curve in the ink document that the eraser intersection occurred. + * @param excludeT The index of the curve in the ink document that the eraser intersection occurred. * @returns The ink stroke represented as a list of segments, excluding the segment in which the eraser intersection occurred. */ @action - segmentInkStroke = (ink: DocumentView, excludeCurve: number): Segment[] => { + segmentInkStroke = (ink: DocumentView, excludeT: number): Segment[] => { const segments: Segment[] = []; var segment: Segment = []; - var excludeSegment: boolean = false; - const ctrlPoints = Cast(ink?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? []; - // Iterating through all of the curves of the intersected ink stroke. - for (var i = 0; i < ctrlPoints.length - 3; i += 4) { - // Performing "deletion" of a segment by excluding the segment in which intersection with the eraser occurred. - if (i === excludeCurve) excludeSegment = true; - const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]]; - const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))); + 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) + for (var i = 0; i < inkData.length - 3; i += 4) { + const curve = new Bezier(inkData.slice(i, i + 4).map(p => ({ x: p.X, y: p.Y }))); // Getting all t-value intersections of the current curve with all other curves. - const tVals: number[] = this.getInkIntersections(i, ink, curve); - if (tVals.length > 0) { - tVals.sort(); - const curveCopy = new Bezier(curve.points); - // Splitting only by the first t-value. - const pair = curveCopy.split(tVals[0]); - segment.push(pair.left); - if (!excludeSegment) { - segments.push(segment); - segment = [pair.right]; - excludeSegment = false; - } - // Splitting by the number of t-values returned. - // for (var index = 0; index < tVals.length; index++) { - // const curveCopy = new Bezier(curve.points); - // if (index === 0) { - // const pair = curveCopy.split(tVals[index]); - // segment.push(pair.left); - // segments.push(segment); - // segment = [pair.right]; - // } else if (index === tVals.length - 1) { - // segments.push(segment); - // const pair = curveCopy.split(tVals[index]); - // segment = [pair.right]; - // } else { - // segments.push(segment); - // const curve = curveCopy.split(index, index + 1); - // segment = [curve]; - // } - // } + const tVals = this.getInkIntersections(i, ink, curve).sort(); + if (tVals.length) { + tVals.forEach((t, index) => { + const docCurveTVal = t + Math.floor(i / 4); + if (excludeT < startSegmentT || excludeT > docCurveTVal) { + const localStartTVal = startSegmentT - Math.floor(i / 4); + segment.push(curve.split(localStartTVal < 0 ? 0 : localStartTVal, t)); + segment.length && segments.push(segment); + } + // start a new segment from the intersection t value + segment = tVals.length - 1 === index ? [curve.split(t).right] : []; + startSegmentT = docCurveTVal; + }); } else { segment.push(curve); } } - if (!excludeSegment) segments.push(segment); + if (excludeT < startSegmentT || excludeT > (inkData.length / 4)) { + segment.length && segments.push(segment); + } return segments; } @@ -810,23 +786,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.childDocs .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { - const currInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView); - const currCtrls = Cast(currInk?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? []; - for (var j = 0; j < currCtrls.length - 3; j += 4) { - const exclude = i === j || i === j - 4 || i === j + 4; - const sameDoc = ink?.Document === currInk?.Document; + const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke; + const { inkData: otherInkData } = otherInk.inkScaledData(); + const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); + const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt)); + for (var j = 0; j < otherCtrlPts.length - 3; j += 4) { + const neighboringSegment = i === j || i === j - 4 || i === j + 4; // Ensuring that the curve intersected by the eraser is not checked for further ink intersections. - if (sameDoc && exclude) continue; - const currArray = [currCtrls[j], currCtrls[j + 1], currCtrls[j + 2], currCtrls[j + 3]]; - const currCurve = new Bezier(currArray.map(p => ({ x: p.X, y: p.Y }))); - const intersect = curve.intersects(currCurve); - if (intersect.length > 0) { - intersect.forEach(val => { - // Converting the Bezier.js Split type to a t-value number. - const t = +val.toString().split("/")[0]; - if (!tVals.includes(t)) tVals.push(t); - }); - } + if (ink?.Document === otherInk.props.Document && neighboringSegment) continue; + + const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y }))); + curve.intersects(otherCurve).forEach((val: string | number, i: number) => { + // Converting the Bezier.js Split type to a t-value number. + const t = +val.toString().split("/")[0]; + if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical). + }); } }); return tVals; @@ -1006,10 +980,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! })); if (measuredDocs.length) { const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content - ({ - xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, - yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } - }) + ({ + xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, + yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } + }) , { xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE } |