aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-04-07 13:35:40 -0400
committereleanor-park <eleanor_park@brown.edu>2024-04-07 13:35:40 -0400
commitba58b46b55527c23cd23b34c2ca13be945c4bf4e (patch)
tree2d0a5b90fe000c5db2211da6d9e17230dbc537e3
parent3c6707abda0054335a4924a453b945bb87723a59 (diff)
radius erase changes
-rw-r--r--src/client/util/CurrentUserUtils.ts10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx165
-rw-r--r--src/fields/InkField.ts3
3 files changed, 94 insertions, 84 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 714e33d25..96edce177 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -650,7 +650,15 @@ export class CurrentUserUtils {
return [
{ title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }},
{ title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
- { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", toolType: "eraser", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
+ // { title: "Eraser", toolTip: "Eraser (Ctrl+E)", width: 50, btnType: ButtonType.DropdownList, icon: "eraser", toolType: "eraser", ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" },
+ // btnList: new List<string>(["Stroke Erase", "Segment Erase", "Radius Erase"]) },
+ { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType:"eraser", scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'},
+ subMenu: [
+ { title: "Stroke", toolTip: "Stroke Erase", btnType: ButtonType.ToggleButton, icon: "eraser", toolType:"strokeeraser", ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
+ { title: "Segment", toolTip: "Segment Erase", btnType: ButtonType.ToggleButton, icon: "minus", toolType:"segmenteraser",ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
+ { title: "Radius", toolTip: "Radius Erase", btnType: ButtonType.ToggleButton, icon: "circle", toolType:"radiuseraser", ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
+ // { title: "Width", toolTip: "Eraser Width", btnType: ButtonType.NumberSliderButton, toolType:"strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
+ ]},
{ title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType:GestureUtils.Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
{ title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
{ title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType:GestureUtils.Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 18b75d8ff..da4c53d66 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -610,10 +610,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
switch (Doc.ActiveTool) {
- case InkTool.Highlighter: break;
- case InkTool.Write: break;
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
- case InkTool.Eraser:
+ case InkTool.Highlighter:
+ break;
+ case InkTool.Write:
+ break;
+ case InkTool.Pen:
+ break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.StrokeEraser:
+ this._batch = UndoManager.StartBatch('collectionErase');
+ setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
+ break;
+ case InkTool.SegmentEraser:
+ this._batch = UndoManager.StartBatch('collectionErase');
+ setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
+ break;
+ case InkTool.RadiusEraser:
this._batch = UndoManager.StartBatch('collectionErase');
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
break;
@@ -738,28 +749,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- if (!e.shiftKey) {
+ if (Doc.ActiveTool !== InkTool.StrokeEraser) {
this._eraserLock++;
- 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++;
+ var segments;
+ if (Doc.ActiveTool === InkTool.SegmentEraser) {
+ segments = this.segmentErase(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
+ } else if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ console.log("RADIUS");
+ segments = this.radiusErase(intersect.inkView, intersect.t);
+ }
+ // } else if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ // segments = undefined;
// }
- segments.forEach(segment =>
+ segments?.forEach(segment =>
this.forceStrokeGesture(
e,
GestureUtils.Gestures.Stroke,
@@ -837,6 +839,42 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
};
+ @action
+ radiusErase = (ink: DocumentView, eraseT: number): Segment[] => {
+ const segments: Segment[] = [];
+ var segment1: Segment = [];
+ var segment2: Segment = [];
+ const eraseWidth = ActiveInkWidth() / 100;
+ const { inkData } = (ink?.ComponentView as InkingStroke).inkScaledData();
+ for (var i = 0; i < inkData.length - 3; i += 4) {
+ const currCurveT = Math.floor(i/4);
+ const inkSegment: Bezier = InkField.Segment(inkData, i);
+ if (eraseT >= currCurveT && eraseT < currCurveT+1) {
+ if (eraseT - eraseWidth > currCurveT && eraseT + eraseWidth < currCurveT + 1) {
+ segment1.push(inkSegment.split(0, eraseT - currCurveT - eraseWidth));
+ segment2.push(inkSegment.split(eraseT - currCurveT + eraseWidth, 1));
+ } else if (eraseT - eraseWidth < currCurveT) {
+ segment2.push(inkSegment.split(eraseT - currCurveT + eraseWidth, 1));
+ } else if (eraseT + eraseWidth > currCurveT + 1) {
+ segment1.push(inkSegment.split(0, eraseT - currCurveT - eraseWidth));
+ }
+ } else if (eraseT > currCurveT + 1) {
+ segment1.push(inkSegment);
+ } else {
+ segment2.push(inkSegment);
+ }
+ }
+
+ // push 1 or both segments if they are not empty
+ 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;
+ }
+
/**
* Erases ink strokes by segments. Locates intersections of the current ink stroke with all other ink strokes (including itself),
* then erases the segment that was intersected by the eraser. This is done by creating either 1 or two resulting segments
@@ -846,7 +884,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @returns The ink stroke represented as a list of segments, excluding the segment in which the eraser intersection occurred.
*/
@action
- segmentInkStroke = (ink: DocumentView, excludeT: number): Segment[] => {
+ segmentErase = (ink: DocumentView, excludeT: number): Segment[] => {
const segments: Segment[] = [];
var segment1: Segment = [];
@@ -873,8 +911,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
var isClosedCurve = false;
if (intersections[0] === 0) {
isClosedCurve = true;
- intersections = intersections.slice(1, intersections.length ); // take the 0 intersection out
- segmentIndexes = segmentIndexes.slice(1, segmentIndexes.length); // same for indexes
+
+ // now reduce intersections list to the actual intersections because closed curves have additional ones with itself
+ const indices = intersections
+ .map((value, index) => value > 0.0001 && value < Math.floor(inkData.length / 4) ? index : -1)
+ .filter(index => index !== -1);
+
+ // Filter intersections and segmentIndexes based on validIndices
+ intersections = indices.map(index => intersections[index]);
+ segmentIndexes = indices.map(index => segmentIndexes[index]);
+ // intersections = intersections.slice(1, intersections.length ); // take the 0 intersection out
+ // segmentIndexes = segmentIndexes.slice(1, segmentIndexes.length); // same for indexes
}
if (intersections.length) {
@@ -938,13 +985,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
} else { // case where right end is erased
- if (currCurveT === splitSegment1) {
- segment1.push(inkSegment.split(0, intersections[closestTs[0]] - currCurveT));
- hasSplit = true;
- } else if (isClosedCurve && currCurveT === segmentIndexes[1]) {
+ if (currCurveT === segmentIndexes[0] && isClosedCurve) {
segment1.push(inkSegment.split(intersections[0] - currCurveT, 1));
+ hasSplit = true;
+ } else if (currCurveT === splitSegment1) {
+ segment1.push(inkSegment.split(0, intersections[closestTs[0]] - currCurveT));
+ hasSplit = false; // although this is not technically true, we set to false so the segments after don't get added
+ // } else if (isClosedCurve && currCurveT === segmentIndexes[1]) {
+ // segment1.push(inkSegment.split(intersections[0] - currCurveT, 1));
} else {
- if (!hasSplit && !isClosedCurve) {
+ if ((isClosedCurve && hasSplit) || (!isClosedCurve && !hasSplit)) {
segment1.push(inkSegment);
}
}
@@ -961,57 +1011,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return segments;
-
-
- // const segments: 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)
- // 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.
- // const tVals = this.getInkIntersections(i, ink, inkSegment).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);
- // 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
- // 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 {
- // if (!hasSplit) {
- // segment1.push(inkSegment);
- // } else {
- // segment2.push(inkSegment);
- // }
- // }
- // }
- // 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);
- // }
};
/**
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index b3e01229a..9c8c5df2c 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -11,6 +11,9 @@ export enum InkTool {
Pen = 'pen',
Highlighter = 'highlighter',
Eraser = 'eraser',
+ StrokeEraser = 'strokeeraser',
+ SegmentEraser = 'segmenteraser',
+ RadiusEraser = 'radiuseraser',
Stamp = 'stamp',
Write = 'write',
PresentationPin = 'presentationpin',