From 84098c41aaa4970d43b88645489e64c1cac22934 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Wed, 15 May 2024 14:33:50 -0400 Subject: three eraser modes implemented --- src/client/views/InkingStroke.tsx | 49 ------- .../collectionFreeForm/CollectionFreeFormView.tsx | 141 +++++++++++---------- src/client/views/global/globalScripts.ts | 3 - 3 files changed, 73 insertions(+), 120 deletions(-) (limited to 'src') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 03acd5393..9e09c0aa9 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -331,55 +331,6 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() ); }; - splitByEraser = (startInkCoordsIn: { X: number; Y: number }, endInkCoordsIn: { X: number; Y: number }) => { - const radius = ActiveEraserWidth() / 2 + 3; // reduce values to avoid extreme radii - const c = 0.551915024494; // circle tangent length to side ratio - const movement = { x: endInkCoordsIn.X - startInkCoordsIn.X, y: endInkCoordsIn.Y - startInkCoordsIn.Y }; - const moveLen = Math.sqrt(movement.x ** 2 + movement.y ** 2); - const direction = { x: (movement.x / moveLen) * radius, y: (movement.y / moveLen) * radius }; - const normal = { x: -direction.y, y: direction.x }; // prettier-ignore - - const startCoords = { X: startInkCoordsIn.X - direction.x, Y: startInkCoordsIn.Y - direction.y }; - const endCoords = { X: endInkCoordsIn.X + direction.x, Y: endInkCoordsIn.Y + direction.y }; - return new InkField([ - // left bot arc - { X: startCoords.X, Y: startCoords.Y }, // prettier-ignore - { X: startCoords.X + normal.x * c, Y: startCoords.Y + normal.y * c }, // prettier-ignore - { X: startCoords.X + direction.x + normal.x - direction.x * c, Y: startCoords.Y + direction.y + normal.y - direction.y * c }, - { X: startCoords.X + direction.x + normal.x, Y: startCoords.Y + direction.y + normal.y }, // prettier-ignore - - // bot - { X: startCoords.X + direction.x + normal.x, Y: startCoords.Y + direction.y + normal.y }, // prettier-ignore - { X: startCoords.X + direction.x + normal.x + direction.x * c, Y: startCoords.Y + direction.y + normal.y + direction.y * c }, - { X: endCoords.X - direction.x + normal.x - direction.x * c, Y: endCoords.Y - direction.y + normal.y - direction.y * c }, // prettier-ignore - { X: endCoords.X - direction.x + normal.x, Y: endCoords.Y - direction.y + normal.y }, // prettier-ignore - - // right bot arc - { X: endCoords.X - direction.x + normal.x, Y: endCoords.Y - direction.y + normal.y }, // prettier-ignore - { X: endCoords.X - direction.x + normal.x + direction.x * c, Y: endCoords.Y - direction.y + normal.y + direction.y * c}, // prettier-ignore - { X: endCoords.X + normal.x * c, Y: endCoords.Y + normal.y * c }, // prettier-ignore - { X: endCoords.X, Y: endCoords.Y }, // prettier-ignore - - // right top arc - { X: endCoords.X, Y: endCoords.Y }, // prettier-ignore - { X: endCoords.X - normal.x * c, Y: endCoords.Y - normal.y * c }, // prettier-ignore - { X: endCoords.X - direction.x - normal.x + direction.x * c, Y: endCoords.Y - direction.y - normal.y + direction.y * c}, // prettier-ignore - { X: endCoords.X - direction.x - normal.x, Y: endCoords.Y - direction.y - normal.y }, // prettier-ignore - - // top - { X: endCoords.X - direction.x - normal.x, Y: endCoords.Y - direction.y - normal.y }, // prettier-ignore - { X: endCoords.X - direction.x - normal.x - direction.x * c, Y: endCoords.Y - direction.y - normal.y - direction.y * c}, // prettier-ignore - { X: startCoords.X + direction.x - normal.x + direction.x * c, Y: startCoords.Y + direction.y - normal.y + direction.y * c }, - { X: startCoords.X + direction.x - normal.x, Y: startCoords.Y + direction.y - normal.y }, // prettier-ignore - - // left top arc - { X: startCoords.X + direction.x - normal.x, Y: startCoords.Y + direction.y - normal.y }, // prettier-ignore - { X: startCoords.X + direction.x - normal.x - direction.x * c, Y: startCoords.Y + direction.y - normal.y - direction.y * c }, // prettier-ignore - { X: startCoords.X - normal.x * c, Y: startCoords.Y - normal.y * c }, // prettier-ignore - { X: startCoords.X, Y: startCoords.Y }, // prettier-ignore - ]); - }; - _subContentView: ViewBoxInterface | undefined; setSubContentView = (doc: ViewBoxInterface) => (this._subContentView = doc); @computed get fillColor() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 14d20eb4a..e2965c1ba 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -738,12 +738,12 @@ export class CollectionFreeFormView extends CollectionSubView { const currPoint = { X: e.clientX, Y: e.clientY }; @@ -776,7 +776,7 @@ export class CollectionFreeFormView extends CollectionSubView this._eraserLock--); + // setTimeout(() => this._eraserLock--); } // Lower ink opacity to give the user a visual indicator of deletion. intersect.inkView.layoutDoc.opacity = 0; @@ -797,23 +797,17 @@ export class CollectionFreeFormView extends CollectionSubView { - // const currZoom = this.zoomScaling(); - // console.log("curr zoom", currZoom); - // console.log("prev zoom", this.prevZoom); const currPoint = { X: e.clientX, Y: e.clientY }; this._eraserPts.push([currPoint.X, currPoint.Y]); this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5)); - const intersections: any[] = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint); - // if (intersections[0].size > 0 && currZoom === this.prevZoom) { - const strokeMap: Map = intersections[0]; - const eraserStroke = intersections[1]; + const strokeMap: Map = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint); strokeMap.forEach((intersects, stroke) => { if (!this._deleteList.includes(stroke)) { this._deleteList.push(stroke); SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1'); SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black'); - const segments = this.radiusErase(stroke, intersects.sort(), eraserStroke); + const segments = this.radiusErase(stroke, intersects.sort()); segments?.forEach(segment => this.forceStrokeGesture( e, @@ -825,12 +819,6 @@ export class CollectionFreeFormView extends CollectionSubView 0) { - // this.prevZoom = currZoom; - // console.log("skipping occurred"); - // } else if (this.prevZoom = currZoom) { - // this.prevZoom = currZoom; - // } return false; }; @@ -852,7 +840,8 @@ export class CollectionFreeFormView extends CollectionSubView { // increase radius slightly based on the erased stroke's width, added to make eraser look more realistic - var radius = ActiveEraserWidth() + inkStrokeWidth * 0.2; + var radius = ActiveEraserWidth() + 5 + inkStrokeWidth * 0.1; // add 5 to prevent eraser from being too small const c = 0.551915024494; // circle tangent length to side ratio const movement = { x: endInkCoordsIn.X - startInkCoordsIn.X, y: endInkCoordsIn.Y - startInkCoordsIn.Y }; const moveLen = Math.sqrt(movement.x ** 2 + movement.y ** 2); @@ -909,7 +898,7 @@ export class CollectionFreeFormView extends CollectionSubView { var isInside = false; if (!isNaN(eraserOutline[0].X) && !isNaN(eraserOutline[0].Y)) { - let minX = eraserOutline[0].X, maxX = eraserOutline[0].X; // prettier-ignore - let minY = eraserOutline[0].Y, maxY = eraserOutline[0].Y; // prettier-ignore + let minX = eraserOutline[0].X, maxX = eraserOutline[0].X; + let minY = eraserOutline[0].Y, maxY = eraserOutline[0].Y; for (let i = 1; i < eraserOutline.length; i++) { const currPoint: { X: number; Y: number } = eraserOutline[i]; minX = Math.min(currPoint.X, minX); @@ -985,14 +974,18 @@ export class CollectionFreeFormView extends CollectionSubView { - const eraserRadius = ActiveEraserWidth() / this.zoomScaling(); - const eraserMin = { X: Math.min(lastPoint.X, currPoint.X) - eraserRadius, Y: Math.min(lastPoint.Y, currPoint.Y) - eraserRadius }; - const eraserMax = { X: Math.max(lastPoint.X, currPoint.X) + eraserRadius, Y: Math.max(lastPoint.Y, currPoint.Y) + eraserRadius }; + // set distance of the eraser's bounding box based on the zoom + var boundingBoxDist = ActiveEraserWidth() + 5; + this.zoomScaling() < 1 ? boundingBoxDist = boundingBoxDist / (this.zoomScaling() * 1.5) : boundingBoxDist *= this.zoomScaling(); + + const eraserMin = { X: Math.min(lastPoint.X, currPoint.X) - boundingBoxDist, Y: Math.min(lastPoint.Y, currPoint.Y) - boundingBoxDist }; + const eraserMax = { X: Math.max(lastPoint.X, currPoint.X) + boundingBoxDist, Y: Math.max(lastPoint.Y, currPoint.Y) + boundingBoxDist }; const strokeToTVals = new Map(); const intersectingStrokes = this.childDocs .map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())) @@ -1006,13 +999,12 @@ export class CollectionFreeFormView extends CollectionSubView= inkViewBounds.left && eraserMax.Y >= inkViewBounds.top ); - const erasers: InkData[] = []; + intersectingStrokes.forEach(({ inkStroke, inkView }) => { const { inkData, inkStrokeWidth } = inkStroke.inkScaledData(); const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint); const currPointInkSpace = inkStroke.ptFromScreen(currPoint); const eraserInkData = this.createEraserOutline(prevPointInkSpace, currPointInkSpace, inkStrokeWidth).inkData; - // erasers.push(eraserInkData); // add the ends of the stroke in as "intersections" if (this.insideEraserOutline(eraserInkData, inkData[0])) { @@ -1039,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView Math.abs(val - (t + Math.floor(i / 4))) <= tValOffset); if (!inList) { inkList.push(t + Math.floor(i / 4)); @@ -1052,42 +1044,34 @@ export class CollectionFreeFormView extends CollectionSubView { + radiusErase = (ink: DocumentView, tVals: number[]): Segment[] => { const segments: Segment[] = []; - var eraser: Segment = []; - // for (var i = 0; i < erasers.length; i ++) { - // for (var j = 0; j < erasers[i].length - 3; j +=4) { - // eraser.push(InkField.Segment(erasers[i], j)); - // } - // segments.push(eraser); - // eraser = []; - // } - const inkStroke = ink?.ComponentView as InkingStroke; const { inkData } = inkStroke.inkScaledData(); var currSegment: Segment = []; + + // any radius erase stroke will always result in even tVals, since the ends are included if (tVals.length % 2 !== 0) { - // any radius erase stroke will always result in even tVals, since the ends are included for (var i = 0; i < inkData.length - 3; i += 4) { currSegment.push(InkField.Segment(inkData, i)); } segments.push(currSegment); - return segments; // want to return the full original stroke + return segments; // return the full original stroke } var continueErasing = false; // used to erase segments if they are completely enclosed in the eraser - var firstSegment: Segment = []; // used to keep track of the first segment for closed curves + var firstSegment: Segment = []; // used to keep track of the first segment for closed curves // early return if nothing to split on if (tVals.length === 0 || (tVals.length === 1 && tVals[0] === 0)) { @@ -1108,15 +1092,18 @@ export class CollectionFreeFormView extends CollectionSubView 0) { for (var j = 0; j < segmentTs.length; j++) { - if (segmentTs[j] === 0) { // if the first end of the segment is within the eraser + if (segmentTs[j] === 0) { + // if the first end of the segment is within the eraser continueErasing = true; - } else if (segmentTs[j] === Math.floor(inkData.length / 4) + 1) { // the last end + } else if (segmentTs[j] === Math.floor(inkData.length / 4) + 1) { + // the last end break; } else { if (!continueErasing) { currSegment.push(inkBezier.split(0, segmentTs[j] - currCurveT)); continueErasing = true; - } else { // we've reached the end of the part to take out... + } else { + // we've reached the end of the part to take out... continueErasing = false; if (currSegment.length > 0) { segments.push(currSegment); // ...so we add it to the list and reset currSegment @@ -1168,7 +1155,7 @@ export class CollectionFreeFormView extends CollectionSubView tVal > 0 && tVal < 1).map(tVal => tVal + Math.floor(i / 4)); if (currIntersects.length) { intersections = [...intersections, ...currIntersects]; @@ -1181,6 +1168,9 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -2045,14 +2043,19 @@ export class CollectionFreeFormView extends CollectionSubView { this._eraserX = e.clientX; this._eraserY = e.clientY; - if (this._eraserX < 0 || this._eraserY < 0) { - this._showEraserCircle = false; - } else { - this._showEraserCircle = true; - } // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); }; + @action + onMouseLeave = () => { + this._showEraserCircle = false; + }; + + @action + onMouseEnter = () => { + this._showEraserCircle = true; + }; + @undoBatch promoteCollection = () => { const childDocs = this.childDocs.slice(); @@ -2321,6 +2324,8 @@ export class CollectionFreeFormView extends CollectionSubView - {Doc.ActiveTool === InkTool.RadiusEraser && ( + {(Doc.ActiveTool === InkTool.RadiusEraser && this._showEraserCircle) && (
any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ ['inkMask', { -- cgit v1.2.3-70-g09d2