aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/GestureOverlay.tsx7
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx136
-rw-r--r--src/fields/InkField.ts6
3 files changed, 117 insertions, 32 deletions
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 99829139f..e2193c9ac 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -490,18 +490,11 @@ export class GestureOverlay extends Touchable {
}
@action
- checkInkIntersection = (point: PointData) => {
-
- }
-
- @action
onPointerDown = (e: React.PointerEvent) => {
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
this._points.push({ X: e.clientX, Y: e.clientY });
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
// if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
- } else if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
- this.checkInkIntersection({ X: e.clientX, Y: e.clientY });
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6f79c5eab..ce4acb7ad 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { DateField } from "../../../../fields/DateField";
import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField";
+import { InkData, InkField, InkTool, PointData, Intersection } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
import { ObjectField } from "../../../../fields/ObjectField";
import { RichTextField } from "../../../../fields/RichTextField";
@@ -52,6 +52,7 @@ import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { ColorScheme } from "../../../util/SettingsManager";
import { Bezier } from "bezier-js";
+import { GestureOverlay } from "../../GestureOverlay";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -647,19 +648,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
if (CurrentUserUtils.SelectedTool !== InkTool.None) {
this._currPoint = { X: e.clientX, Y: e.clientY };
- const intersections: { [id: number]: DocumentView[] } = this.getEraserIntersections();
- if (intersections) {
- for (const t in intersections) {
- const inks = intersections[t];
- inks.forEach(ink => {
- if (!this._deleteList.includes(ink)) {
- this._deleteList.push(ink);
- // Lowering ink opacity to give the user a visual indicator of deletion.
- ink.Document.opacity = 0.5;
- }
- });
- }
- }
+ const eraserIntersections: Intersection[] = this.getEraserIntersections();
+ if (eraserIntersections) this.eraseInkStrokes(eraserIntersections);
}
if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
if (this.props.isContentActive(true)) e.stopPropagation();
@@ -676,12 +666,79 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
+ @action
+ eraseInkStrokes = (eraserIntersections: Intersection[]) => {
+ eraserIntersections.forEach(intersect => {
+ var t1, t2;
+ var distT1: number, distT2: number;
+ distT1 = distT2 = Number.MAX_SAFE_INTEGER;
+ const t = intersect.t;
+ const ink = intersect.ink;
+ const ctrlPoints = Cast(ink?.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 curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
+ const inkIntersections: number[] = this.getInkIntersections(curve);
+ inkIntersections.forEach(currentT => {
+ const diff = t - currentT;
+ if (diff > 0 && diff < distT1) {
+ distT1 = diff;
+ t1 = currentT;
+ } else if (diff < 0 && diff < distT2) {
+ distT2 = -diff;
+ t2 = currentT;
+ }
+ });
+
+ // Normal case --> deletion of entire stroke
+ if (!t1 && !t2) {
+ if (!this._deleteList.includes(ink)) {
+ this._deleteList.push(ink);
+ // Lowering ink opacity to give the user a visual indicator of deletion.
+ ink.Document.opacity = 0.5;
+ }
+ } else if (t1 && !t2) {
+ const splitCurve = curve.split(t1);
+ const leftInkData: PointData[] = splitCurve.left.points.map(p => ({ X: p.x, Y: p.y }));
+ GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, leftInkData);
+ if (!this._deleteList.includes(ink)) {
+ this._deleteList.push(ink);
+ // Lowering ink opacity to give the user a visual indicator of deletion.
+ ink.Document.opacity = 0.5;
+ }
+ } else if (!t1 && t2) {
+ const splitCurve = curve.split(t2);
+ const rightInkData: PointData[] = splitCurve.right.points.map(p => ({ X: p.x, Y: p.y }));
+ GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, rightInkData);
+ if (!this._deleteList.includes(ink)) {
+ this._deleteList.push(ink);
+ // Lowering ink opacity to give the user a visual indicator of deletion.
+ ink.Document.opacity = 0.5;
+ }
+ } else if (t1 && t2) {
+ const splitCurve1 = curve.split(t1);
+ const leftInkData: PointData[] = splitCurve1.left.points.map(p => ({ X: p.x, Y: p.y }));
+ GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, leftInkData);
+ const splitCurve2 = curve.split(t2);
+ const rightInkData: PointData[] = splitCurve2.right.points.map(p => ({ X: p.x, Y: p.y }));
+ GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, rightInkData);
+
+ if (!this._deleteList.includes(ink)) {
+ this._deleteList.push(ink);
+ // Lowering ink opacity to give the user a visual indicator of deletion.
+ ink.Document.opacity = 0.5;
+ }
+ }
+ }
+ });
+ }
+
/**
* Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
- * @returns A dictionary mapping the t-value intersection of the eraser with a list of ink DocumentViews.
+ * @returns A dictionary mapping the t-value intersection of the eraser with the corresponding ink DocumentView.
*/
- getEraserIntersections = (): { [id: number]: DocumentView[] } => {
- const intersections: { [id: number]: DocumentView[] } = {};
+ getEraserIntersections = (): Intersection[] => {
+ const intersections: Intersection[] = [];
this.childDocs
.filter(doc => doc.type === DocumentType.INK)
.forEach(doc => {
@@ -694,14 +751,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
if (prevPointInkSpace && currPointInkSpace) {
const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
- const t = curve.intersects(
- {
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
- });
- if (inkView && t) {
- t.forEach(val => {
- intersections[+t[0]] ? intersections[+t[0]].push(inkView) : intersections[+t[0]] = [inkView];
+ const intersects = curve.intersects({
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
+ });
+ if (inkView && intersects) {
+ intersects.forEach(t => {
+ intersections.push({ t: +t.toString(), ink: inkView });
});
}
}
@@ -710,6 +766,36 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return intersections;
}
+ getInkIntersections = (curve: Bezier): number[] => {
+ const intersections: number[] = [];
+ const selfIntersect = curve.selfintersects();
+ if (selfIntersect) {
+ selfIntersect.forEach(t => {
+ intersections.push(+t.toString());
+ });
+ }
+
+ 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 i = 0; i < currCtrls.length - 3; i += 4) {
+ const currArray = [currCtrls[i], currCtrls[i + 1], currCtrls[i + 2], currCtrls[i + 3]];
+ const currCurve = new Bezier(currArray.map(p => ({ x: p.X, y: p.Y })));
+ const intersect = curve.intersects(currCurve);
+ if (intersect) {
+ intersect.forEach(t => {
+ intersections.push(+this.parseTValue(t.toString()));
+ });
+ }
+ }
+ });
+ return intersections;
+ }
+
+ parseTValue = (t: string) => t.split("/", 1)[0];
+
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index f16e143d8..1d50b5e0d 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -3,6 +3,7 @@ import { Scripting } from "../client/util/Scripting";
import { Deserializable } from "../client/util/SerializationHelper";
import { Copy, ToScriptString, ToString } from "./FieldSymbols";
import { ObjectField } from "./ObjectField";
+import { DocumentView } from "../client/views/nodes/DocumentView";
// Helps keep track of the current ink tool in use.
export enum InkTool {
@@ -20,6 +21,11 @@ export interface PointData {
Y: number;
}
+export interface Intersection {
+ t: number;
+ ink: DocumentView;
+}
+
// Defines an ink as an array of points.
export type InkData = Array<PointData>;