aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-06-11 11:15:25 -0400
committereleanor-park <eleanor_park@brown.edu>2024-06-11 11:15:25 -0400
commit33761fc2227458acf36a5cc4b1f08eaae6e58695 (patch)
tree040cd6388d37f77bd9b3704d1b80443e0b3784ed /src/client/views/collections/collectionFreeForm
parent2277349fc4d5460e94a7a6b705b56488c0efb184 (diff)
some changes
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx170
-rw-r--r--src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx31
2 files changed, 121 insertions, 80 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d22b3569e..e66dbd796 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -144,7 +144,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
: this._props.childPointerEvents?.() ??
(this._props.viewDefDivClick || //
(this.layoutEngine === computePassLayout.name && !this._props.isSelected()) ||
- this.isContentActive() === false
+ this.isContentActive() === false ||
+ Doc.ActiveTool === InkTool.RadiusEraser ||
+ Doc.ActiveTool === InkTool.SegmentEraser ||
+ Doc.ActiveTool === InkTool.StrokeEraser
? 'none'
: this._props.pointerEvents?.());
}
@@ -501,27 +504,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
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.StrokeEraser:
case InkTool.SegmentEraser:
- this._batch = UndoManager.StartBatch('collectionErase');
- this._eraserPts.length = 0;
- setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false);
- break;
case InkTool.RadiusEraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
- setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction, hit !== -1, false);
+ setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, this.onEraserClick, hit !== -1);
+ e.stopPropagation();
break;
case InkTool.SmartDraw:
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, this.createDrawing, hit !== -1, false);
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, this.createDrawing, hit !== -1);
+ e.stopPropagation();
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1);
+ e.stopPropagation();
}
break;
default:
@@ -608,50 +608,83 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_eraserLock = 0;
_eraserPts: number[][] = []; // keep track of the last few eraserPts to make the eraser circle 'stretch'
- /**
- * Erases strokes by intersecting them with an invisible "eraser stroke".
- * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
- * and deletes the original stroke.
- */
- @action
- onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ erase = (e: PointerEvent, delta: number[]) => {
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));
- // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
- this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
- if (!this._deleteList.includes(intersect.inkView)) {
- this._deleteList.push(intersect.inkView);
- 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 (Doc.ActiveTool !== InkTool.StrokeEraser) {
- // this._eraserLock++;
- const 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
- const newStrokes = segments?.map(segment => {
- const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
- const bounds = InkField.getBounds(points);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- return Docs.Create.InkDocument(
- points,
- { title: 'stroke',
+ if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ const strokeMap: Map<DocumentView, number[]> = 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());
+ segments?.forEach(segment =>
+ this.forceStrokeGesture(
+ e,
+ Gestures.Stroke,
+ segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
+ )
+ );
+ }
+ stroke.layoutDoc.opacity = 0;
+ stroke.layoutDoc.dontIntersect = true;
+ });
+ } else {
+ this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
+ if (!this._deleteList.includes(intersect.inkView)) {
+ this._deleteList.push(intersect.inkView);
+ 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 (Doc.ActiveTool !== InkTool.StrokeEraser) {
+ // this._eraserLock++;
+ const 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
+ const newStrokes = segments?.map(segment => {
+ const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
+ const bounds = InkField.getBounds(points);
+ const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ return Docs.Create.InkDocument(
+ points,
+ { title: 'stroke',
x: B.x - inkWidth / 2,
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
_height: B.height + inkWidth,
stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth
- );
- });
- newStrokes && this.addDocument?.(newStrokes);
- // setTimeout(() => this._eraserLock--);
+ inkWidth
+ );
+ });
+ newStrokes && this.addDocument?.(newStrokes);
+ // setTimeout(() => this._eraserLock--);
+ }
+ // Lower ink opacity to give the user a visual indicator of deletion.
+ intersect.inkView.layoutDoc.opacity = 0;
+ intersect.inkView.layoutDoc.dontIntersect = true;
}
- // Lower ink opacity to give the user a visual indicator of deletion.
- intersect.inkView.layoutDoc.opacity = 0;
- intersect.inkView.layoutDoc.dontIntersect = true;
- }
- });
+ });
+ }
+ return false;
+ };
+
+ /**
+ * Erases strokes by intersecting them with an invisible "eraser stroke".
+ * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
+ * and deletes the original stroke.
+ */
+ @action
+ onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ this.erase(e, delta);
+ // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
+ return false;
+ };
+
+ @action
+ onEraserClick = (e: PointerEvent, doubleTap?: boolean) => {
+ this.erase(e, [0, 0]);
return false;
};
@@ -720,7 +753,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// increase radius slightly based on the erased stroke's width, added to make eraser look more realistic
const 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 movement = { x: Math.max(endInkCoordsIn.X - startInkCoordsIn.X, 1), y: Math.max(endInkCoordsIn.Y - startInkCoordsIn.Y, 1) };
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
@@ -1232,28 +1265,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
createDrawing = (e: PointerEvent, doubleTap?: boolean) => {
- SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY);
- if (SmartDrawHandler.Instance.strokes.length > 0) {
- const strokeList: InkData[] = SmartDrawHandler.Instance.strokes;
- strokeList.forEach(coords => {
- // const stroke = new InkField(coords);
- // const points = coords.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.X, Y: p.Y }) ?? { X: 0, Y: 0 }), [] as PointData[]);
- const bounds = InkField.getBounds(coords);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- coords,
- { title: 'stroke',
+ SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY, this.createInkStroke);
+ };
+
+ @action
+ createInkStroke = (strokeList: InkData[]) => {
+ strokeList.forEach(coords => {
+ // const stroke = new InkField(coords);
+ // const points = coords.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.X, Y: p.Y }) ?? { X: 0, Y: 0 }), [] as PointData[]);
+ const bounds = InkField.getBounds(coords);
+ const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ const inkDoc = Docs.Create.InkDocument(
+ coords,
+ { title: 'stroke',
x: B.x - inkWidth / 2,
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
_height: B.height + inkWidth,
stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth
- );
- this.addDocument(inkDoc);
- });
- }
+ inkWidth
+ );
+ this.addDocument(inkDoc);
+ });
};
@action
@@ -1849,8 +1883,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onCursorMove = (e: React.PointerEvent) => {
- this._eraserX = e.clientX;
- this._eraserY = e.clientY;
+ const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
+ this._eraserX = locPt[0];
+ this._eraserY = locPt[1];
+ // Doc.ActiveTool === InkTool.RadiusEraser ? this._childPointerEvents = 'none' : this._childPointerEvents = 'all'
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@@ -2161,8 +2197,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onPointerMove={this.onCursorMove}
style={{
position: 'fixed',
- left: this._eraserX - 60,
- top: this._eraserY - 100,
+ left: this._eraserX,
+ top: this._eraserY,
width: (ActiveEraserWidth() + 5) * 2,
height: (ActiveEraserWidth() + 5) * 2,
borderRadius: '50%',
diff --git a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
index 7e66a62d4..fc8f7a429 100644
--- a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
+++ b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx
@@ -10,7 +10,6 @@ import { AiOutlineSend } from 'react-icons/ai';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './ImageLabelHandler.scss';
import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
-import { InkingStroke } from '../../InkingStroke';
import { InkData } from '../../../../fields/InkField';
@observer
@@ -23,7 +22,8 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
@observable private _yRelativeToTop: boolean = true;
@observable private _isLoading: boolean = false;
@observable private _userInput: string = '';
- @observable public strokes: InkData[] = [];
+ // @observable public strokes: InkData[] = [];
+ private _addToDocFunc: (strokeList: InkData[]) => void = () => {};
constructor(props: any) {
super(props);
@@ -42,10 +42,11 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
};
@action
- displaySmartDrawHandler = (x: number, y: number) => {
+ displaySmartDrawHandler = (x: number, y: number, addToDoc: (strokeList: InkData[]) => void) => {
this._pageX = x;
this._pageY = y;
this._display = true;
+ this._addToDocFunc = addToDoc;
};
@action
@@ -54,8 +55,11 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
};
@action
- drawWithGPT = async (startPoint: {X: number, Y: number}, input: string) => {
- console.log("start point is", startPoint);
+ waitForCoords = async () => {};
+
+ @action
+ drawWithGPT = async (startPoint: { X: number; Y: number }, input: string) => {
+ console.log('start point is', startPoint);
this.setIsLoading(true);
try {
const res = await gptAPICall(input, GPTCallType.DRAW);
@@ -63,8 +67,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
console.error('GPT call failed');
return;
}
- console.log("GPT response:", res);
- try {
+ console.log('GPT response:', res);
// const controlPts: [number, number][][] = JSON.parse(res) as [number, number][][];
// console.log("Control Points", controlPts);
// const transformedPts: { X: number; Y: number }[][] = [];
@@ -74,15 +77,17 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
// });
// transformedPts.push(stroke);
// });
+ const simplifiedRes: string = res.replace(/[^\d\[\],]/g, '');
+ console.log(simplifiedRes)
+ try {
+ const controlPts: { X: number; Y: number }[][] = JSON.parse(simplifiedRes).map((stroke: [number, number][]) => stroke.map(([X, Y]) => ({ X: X + startPoint.X, Y: Y + startPoint.Y })));
+ console.log('transformed points', controlPts);
- const controlPts: { X: number; Y: number }[][] = JSON.parse(res).map((stroke: [number, number][]) =>
- stroke.map(([X, Y]) => ({ X: X + startPoint.X, Y: Y + startPoint.Y })));
- console.log("transformed points", controlPts);
- this.strokes = controlPts;
+ // this.strokes = controlPts;
+ this._addToDocFunc(controlPts);
} catch (err) {
console.error('Incompatible GPT output type');
}
-
} catch (err) {
console.error('GPT call failed', err);
}
@@ -124,7 +129,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
iconPlacement="right"
color={MarqueeOptionsMenu.Instance.userColor}
onClick={e => {
- this.drawWithGPT({X: e.clientX, Y: e.clientY}, this._userInput);
+ this.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._userInput);
}}
/>
{/* <IconButton