aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts7
-rw-r--r--src/client/util/InteractionUtils.tsx139
-rw-r--r--src/client/views/GestureOverlay.tsx123
-rw-r--r--src/client/views/InkingControl.tsx46
-rw-r--r--src/client/views/InkingStroke.tsx3
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss36
-rw-r--r--src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx274
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx126
-rw-r--r--src/client/views/nodes/ColorBox.tsx6
-rw-r--r--src/fields/documentSchemas.ts1
-rw-r--r--src/pen-gestures/GestureUtils.ts5
-rw-r--r--src/pen-gestures/ndollar.ts17
-rw-r--r--src/typings/index.d.ts2
15 files changed, 763 insertions, 26 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f7e19eecd..71bf8a516 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -630,12 +630,13 @@ export namespace Docs {
return doc;
}
- export function InkDocument(color: string, tool: number, strokeWidth: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+ export function InkDocument(color: string, tool: number, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
const I = new Doc();
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString("data");
I.color = color;
I.strokeWidth = strokeWidth;
+ I.strokeBezier = strokeBezier;
I.tool = tool;
I.title = "ink";
I.x = options.x;
@@ -936,8 +937,8 @@ export namespace Docs {
created = Docs.Create.AudioDocument((field).url.href, resolved);
layout = AudioBox.LayoutString;
} else if (field instanceof InkField) {
- const { selectedColor, selectedWidth, selectedTool } = InkingControl.Instance;
- created = Docs.Create.InkDocument(selectedColor, selectedTool, selectedWidth, (field).inkData, resolved);
+ const { selectedColor, selectedWidth, selectedTool, selectedBezier } = InkingControl.Instance;
+ created = Docs.Create.InkDocument(selectedColor, selectedTool, selectedWidth, selectedBezier, (field).inkData, resolved);
layout = InkingStroke.LayoutString;
} else if (field instanceof List && field[0] instanceof Doc) {
created = Docs.Create.StackingDocument(DocListCast(field), resolved);
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3a5345c80..ab1ccb25a 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,4 +1,7 @@
import React = require("react");
+import * as beziercurve from 'bezier-curve';
+import * as fitCurve from 'fit-curve';
+import InkOptionsMenu from "../views/collections/collectionFreeForm/InkOptionsMenu";
export namespace InteractionUtils {
export const MOUSETYPE = "mouse";
@@ -87,8 +90,45 @@ export namespace InteractionUtils {
return myTouches;
}
- export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string) {
- const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
+ export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string) {
+ var pts = "";
+ var shape = "";
+ if (InkOptionsMenu.Instance._circle) {
+ shape = "circle";
+ } else if (InkOptionsMenu.Instance._rectangle) {
+ shape = "rectangle";
+ } else if (InkOptionsMenu.Instance._triangle) {
+ shape = "triangle";
+ } else if (InkOptionsMenu.Instance._arrow) {
+ shape = "arrow";
+ } else if (InkOptionsMenu.Instance._line) {
+ shape = "line";
+ }
+ if (shape !== "") {
+ //if any of the shape are true
+ const shapePts = makePolygon(shape, points);
+ pts = shapePts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
+ }
+ else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) {
+ //pointer is up (first and last points are the same)
+ const newPoints: number[][] = [];
+ const newPts: { X: number; Y: number; }[] = [];
+ //convert to [][] for fitcurve module
+ for (var i = 0; i < points.length - 1; i++) {
+ newPoints.push([points[i].X, points[i].Y]);
+ }
+ const bezierCurves = fitCurve(newPoints, parseInt(bezier));
+ for (var i = 0; i < bezierCurves.length; i++) {
+ for (var t = 0; t < 1; t += 0.01) {
+ const point = beziercurve(t, bezierCurves[i]);
+ newPts.push({ X: point[0], Y: point[1] });
+ }
+ }
+ pts = newPts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
+ } else {
+ //in the middle of drawing
+ pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
+ }
return (
<polyline
points={pts}
@@ -103,6 +143,101 @@ export namespace InteractionUtils {
);
}
+ export function makePolygon(shape: string, points: { X: number, Y: number }[]) {
+ if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
+ //pointer is up (first and last points are the same)
+ if (shape === "arrow" || shape === "line") {
+ //if arrow or line, the two end points should be the starting and the ending point
+ var left = points[0].X;
+ var top = points[0].Y;
+ var right = points[1].X;
+ var bottom = points[1].Y;
+ } else {
+ //otherwise take max and min
+ const xs = points.map(p => p.X);
+ const ys = points.map(p => p.Y);
+ right = Math.max(...xs);
+ left = Math.min(...xs);
+ bottom = Math.max(...ys);
+ top = Math.min(...ys);
+ }
+ } else {
+ //if in the middle of drawing
+ //take first and last points
+ right = points[points.length - 1].X;
+ left = points[0].X;
+ bottom = points[points.length - 1].Y;
+ top = points[0].Y;
+ if (shape !== "arrow" && shape !== "line") {
+ //switch left/right and top/bottom if needed
+ if (left > right) {
+ const temp = right;
+ right = left;
+ left = temp;
+ }
+ if (top > bottom) {
+ const temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ }
+ }
+ points = [];
+ switch (shape) {
+ case "rectangle":
+ points.push({ X: left, Y: top });
+ points.push({ X: right, Y: top });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: left, Y: bottom });
+ points.push({ X: left, Y: top });
+ return points;
+ case "triangle":
+ points.push({ X: left, Y: bottom });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: left, Y: bottom });
+ return points;
+ case "circle":
+ const centerX = (right + left) / 2;
+ const centerY = (bottom + top) / 2;
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ points.push({ X: newX, Y: y });
+ }
+ points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ return points;
+ case "arrow":
+ const x1 = left;
+ const y1 = top;
+ const x2 = right;
+ const y2 = bottom;
+ const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+ const L2 = L1 / 5;
+ const angle = 0.785398;
+ const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
+ const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle));
+ const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle));
+ const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle));
+ points.push({ X: x1, Y: y1 });
+ points.push({ X: x2, Y: y2 });
+ points.push({ X: x3, Y: y3 });
+ points.push({ X: x4, Y: y4 });
+ points.push({ X: x2, Y: y2 });
+ return points;
+ case "line":
+ points.push({ X: left, Y: top });
+ points.push({ X: right, Y: bottom });
+ return points;
+ default:
+ return points;
+ }
+ }
/**
* Returns whether or not the pointer event passed in is of the type passed in
* @param e - pointer event. this event could be from a mouse, a pen, or a finger
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 4352ac52c..5714970c1 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -32,6 +32,7 @@ import { MobileInkOverlayContent } from "../../server/Message";
import MobileInkOverlay from "../../mobile/MobileInkOverlay";
import { RadialMenu } from "./nodes/RadialMenu";
import { SelectionManager } from "../util/SelectionManager";
+import InkOptionsMenu from "./collections/collectionFreeForm/InkOptionsMenu";
@observer
@@ -581,7 +582,8 @@ export default class GestureOverlay extends Touchable {
if (this._points.length > 1) {
const B = this.svgBounds;
const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
-
+ //push first points to so interactionUtil knows pointer is up
+ this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
if (MobileInterface.Instance && MobileInterface.Instance.drawingInk) {
const { selectedColor, selectedWidth } = InkingControl.Instance;
DocServer.Mobile.dispatchGesturePoints({
@@ -630,6 +632,23 @@ export default class GestureOverlay extends Touchable {
break;
}
}
+ //if any of the shape is activated in the InkOptionsMenu
+ else if (InkOptionsMenu.Instance._circle || InkOptionsMenu.Instance._triangle || InkOptionsMenu.Instance._rectangle || InkOptionsMenu.Instance._line || InkOptionsMenu.Instance._arrow) {
+ if (InkOptionsMenu.Instance._circle) {
+ this.makePolygon("circle", false);
+ } else if (InkOptionsMenu.Instance._triangle) {
+ this.makePolygon("triangle", false);
+ } else if (InkOptionsMenu.Instance._rectangle) {
+ this.makePolygon("rectangle", false);
+ } else if (InkOptionsMenu.Instance._line) {
+ this.makePolygon("line", false);
+ } else if (InkOptionsMenu.Instance._arrow) {
+ this.makePolygon("arrow", false);
+ }
+ this.dispatchGesture(GestureUtils.Gestures.Stroke);
+ this._points = [];
+ InkOptionsMenu.Instance.allFalse();
+ }
// if we're not drawing in a toolglass try to recognize as gesture
else {
const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points));
@@ -651,6 +670,15 @@ export default class GestureOverlay extends Touchable {
case GestureUtils.Gestures.Line:
actionPerformed = this.handleLineGesture();
break;
+ case GestureUtils.Gestures.Triangle:
+ this.makePolygon("triangle", true);
+ break;
+ case GestureUtils.Gestures.Circle:
+ this.makePolygon("circle", true);
+ break;
+ case GestureUtils.Gestures.Rectangle:
+ this.makePolygon("rectangle", true);
+ break;
case GestureUtils.Gestures.Scribble:
console.log("scribble");
break;
@@ -671,6 +699,95 @@ export default class GestureOverlay extends Touchable {
document.removeEventListener("pointerup", this.onPointerUp);
}
+ makePolygon = (shape: string, gesture: boolean) => {
+ const xs = this._points.map(p => p.X);
+ const ys = this._points.map(p => p.Y);
+ var right = Math.max(...xs);
+ var left = Math.min(...xs);
+ var bottom = Math.max(...ys);
+ var top = Math.min(...ys);
+
+ if (!gesture) {
+ //if shape options is activated in inkOptionMenu
+ //take second to last point because _point[length-1] is _points[0]
+ right = this._points[this._points.length - 2].X;
+ left = this._points[0].X;
+ bottom = this._points[this._points.length - 2].Y;
+ top = this._points[0].Y;
+ if (shape !== "arrow" && shape !== "line") {
+ if (left > right) {
+ const temp = right;
+ right = left;
+ left = temp;
+ }
+ if (top > bottom) {
+ const temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ }
+ }
+ this._points = [];
+ switch (shape) {
+ //must push an extra point in the end so InteractionUtils knows pointer is up.
+ //must be (points[0].X,points[0]-1)
+ case "rectangle":
+ this._points.push({ X: left, Y: top });
+ this._points.push({ X: right, Y: top });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: top });
+ this._points.push({ X: left, Y: top - 1 });
+ break;
+ case "triangle":
+ this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: (right + left) / 2, Y: top });
+ this._points.push({ X: left, Y: bottom });
+ this._points.push({ X: left, Y: bottom - 1 });
+ break;
+ case "circle":
+ const centerX = (right + left) / 2;
+ const centerY = (bottom + top) / 2;
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ this._points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ this._points.push({ X: newX, Y: y });
+ }
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+ break;
+ case "line":
+ this._points.push({ X: left, Y: top });
+ this._points.push({ X: right, Y: bottom });
+ this._points.push({ X: right, Y: bottom - 1 });
+ break;
+ case "arrow":
+ const x1 = left;
+ const y1 = top;
+ const x2 = right;
+ const y2 = bottom;
+ const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+ const L2 = L1 / 5;
+ const angle = 0.785398;
+ const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
+ const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle));
+ const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle));
+ const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle));
+ this._points.push({ X: x1, Y: y1 });
+ this._points.push({ X: x2, Y: y2 });
+ this._points.push({ X: x3, Y: y3 });
+ this._points.push({ X: x4, Y: y4 });
+ this._points.push({ X: x2, Y: y2 });
+ this._points.push({ X: x1, Y: y1 - 1 });
+ }
+ }
+
dispatchGesture = (gesture: "box" | "line" | "startbracket" | "endbracket" | "stroke" | "scribble" | "text", stroke?: InkData, data?: any) => {
const target = document.elementFromPoint((stroke ?? this._points)[0].X, (stroke ?? this._points)[0].Y);
target?.dispatchEvent(
@@ -710,11 +827,11 @@ export default class GestureOverlay extends Touchable {
[this._strokes.map(l => {
const b = this.getBounds(l);
return <svg key={b.left} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
- {InteractionUtils.CreatePolyline(l, b.left, b.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
+ {InteractionUtils.CreatePolyline(l, b.left, b.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth, InkingControl.Instance.selectedBezier)}
</svg>;
}),
this._points.length <= 1 ? (null) : <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
- {InteractionUtils.CreatePolyline(this._points, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
+ {InteractionUtils.CreatePolyline(this._points, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth, InkingControl.Instance.selectedBezier)}
</svg>]
];
}
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 41ee36d05..83109db1c 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -9,14 +9,15 @@ import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
import GestureOverlay from "./GestureOverlay";
import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
+import InkOptionsMenu from "./collections/collectionFreeForm/InkOptionsMenu";
export class InkingControl {
@observable static Instance: InkingControl;
@computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; }
@computed private get _selectedColor(): string { return CurrentUserUtils.ActivePen ? FieldValue(StrCast(CurrentUserUtils.ActivePen.backgroundColor)) ?? "rgb(0, 0, 0)" : "rgb(0, 0, 0)"; }
@computed private get _selectedWidth(): string { return FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "2"; }
+ @computed private get _selectedBezier(): string { return FieldValue(StrCast(Doc.UserDoc().inkBezier)) ?? "2"; }
@observable public _open: boolean = false;
-
constructor() {
InkingControl.Instance = this;
}
@@ -32,10 +33,21 @@ export class InkingControl {
return (number < 16 ? "0" : "") + number.toString(16).toUpperCase();
}
+ @action
+ inkOptionsMenuChangeColor = (color: string) => {
+ const col: ColorState = {
+ hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" },
+ rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "",
+ };
+ this.switchColor(col);
+ InkOptionsMenu.Instance._colorBt = false;
+ }
+
@undoBatch
switchColor = action((color: ColorState): void => {
Doc.UserDoc().backgroundColor = color.hex.startsWith("#") ?
color.hex + (color.rgb.a ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+ InkOptionsMenu.Instance._color = StrCast(Doc.UserDoc().backgroundColor);
CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = color.hex);
if (InkingControl.Instance.selectedTool === InkTool.None) {
@@ -60,6 +72,23 @@ export class InkingControl {
if (!isNaN(parseInt(width))) {
Doc.UserDoc().inkWidth = width;
}
+ InkOptionsMenu.Instance._widthBt = false;
+ }
+
+ @action
+ switchBezier = (bezier: string): void => {
+ if (!isNaN(parseInt(bezier))) {
+ Doc.UserDoc().inkBezier = bezier;
+ }
+ }
+
+ @action
+ inkOptionsMenuChangeBezier = (e: React.PointerEvent): void => {
+ if (InkOptionsMenu.Instance._bezierBt === true) {
+ Doc.UserDoc().inkBezier = "300";
+ } else {
+ Doc.UserDoc().inkBezier = "0";
+ }
}
@computed
@@ -83,8 +112,21 @@ export class InkingControl {
return this._selectedWidth;
}
+ @computed
+ get selectedBezier() {
+ return this._selectedBezier;
+ }
}
-Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Pen : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); });
+Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) {
+ InkingControl.Instance.switchTool(pen ? InkTool.Pen : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color);
+ //setup InkOptionsMenu(change jumpto value if necessary.Currenlty hardcoded to 300,300)
+ pen ? InkOptionsMenu.Instance.jumpTo(300, 300) : InkOptionsMenu.Instance.fadeOut(true);
+ InkOptionsMenu.Instance.changeColor = InkingControl.Instance.inkOptionsMenuChangeColor;
+ InkOptionsMenu.Instance.changeBezier = InkingControl.Instance.inkOptionsMenuChangeBezier;
+ InkOptionsMenu.Instance.changeWidth = InkingControl.Instance.switchWidth;
+ InkOptionsMenu.Instance._widthSelected = width;
+ InkOptionsMenu.Instance._color = color;
+});
Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Highlighter : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); });
Scripting.addGlobal(function activateEraser(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Eraser : InkTool.None); });
Scripting.addGlobal(function activateStamp(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Stamp : InkTool.None); });
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 8938e8b6c..3dc0a5b20 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -40,7 +40,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const bottom = Math.max(...ys);
const points = InteractionUtils.CreatePolyline(data, left, top,
StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor),
- StrCast(this.layoutDoc.strokeWidth, InkingControl.Instance.selectedWidth));
+ StrCast(this.layoutDoc.strokeWidth, InkingControl.Instance.selectedWidth),
+ StrCast(this.layoutDoc.strokeBezier, InkingControl.Instance.selectedBezier));
const width = right - left;
const height = bottom - top;
const scaleX = this.props.PanelWidth() / width;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a1d1b0ece..3677746cd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -33,6 +33,7 @@ import SharingManager from '../util/SharingManager';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
+import InkOptionsMenu from './collections/collectionFreeForm/InkOptionsMenu';
import { CollectionLinearView } from './collections/CollectionLinearView';
import { CollectionView, CollectionViewType } from './collections/CollectionView';
import { ContextMenu } from './ContextMenu';
@@ -567,6 +568,7 @@ export class MainView extends React.Component {
<RadialMenu />
<PDFMenu />
<MarqueeOptionsMenu />
+ <InkOptionsMenu />
<RichTextMenu />
<OverlayView />
<TimelineMenu />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c753a703d..fb7784b58 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, InkingControl.Instance.selectedWidth, points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
+ const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, InkingControl.Instance.selectedWidth, InkingControl.Instance.selectedBezier, points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
this.addDocument(inkDoc);
e.stopPropagation();
break;
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
new file mode 100644
index 000000000..a7f4d4e53
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss
@@ -0,0 +1,36 @@
+.antimodeMenu-button {
+ .color-preview {
+ width: 100%;
+ height: 100%;
+ }
+
+
+}
+
+.sketch-picker {
+ background: #323232;
+
+ .flexbox-fit {
+ background: #323232;
+ }
+}
+
+.btn-group {
+ display: grid;
+ grid-template-columns: auto auto auto auto;
+ /* Make the buttons appear below each other */
+}
+
+.btn2-group {
+ display: block;
+ background: #323232;
+ grid-template-columns: auto;
+
+ /* Make the buttons appear below each other */
+ .antimodeMenu-button {
+ background: #323232;
+ display: block;
+
+
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
new file mode 100644
index 000000000..44488cbcf
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -0,0 +1,274 @@
+import React = require("react");
+import AntimodeMenu from "../../AntimodeMenu";
+import { observer } from "mobx-react";
+import { unimplementedFunction } from "../../../../Utils";
+import { observable, action } from "mobx";
+import "./InkOptionsMenu.scss";
+
+
+@observer
+export default class InkOptionsMenu extends AntimodeMenu {
+ static Instance: InkOptionsMenu;
+ public changeColor: (color: string) => void = unimplementedFunction;
+ public changeBezier: (e: React.PointerEvent) => void = unimplementedFunction;
+ public changeWidth: (color: string) => void = unimplementedFunction;
+
+ private _palette: (string)[];
+ private _width: (string)[];
+
+
+ public _circle: boolean;
+ public _triangle: boolean;
+ public _rectangle: boolean;
+ public _arrow: boolean;
+ public _line: boolean;
+ public _widthSelected: string;
+
+ @observable public _circleBt: boolean;
+ @observable public _triangleBt: boolean;
+ @observable public _rectangleBt: boolean;
+ @observable public _arrowBt: boolean;
+ @observable public _lineBt: boolean;
+ @observable public _colorBt: boolean;
+ @observable public _color: string;
+ @observable public _bezierBt: boolean;
+ @observable public _widthBt: boolean;
+
+
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+ InkOptionsMenu.Instance = this;
+ this._canFade = false;
+
+ this._circle = false;
+ this._triangle = false;
+ this._rectangle = false;
+ this._arrow = false;
+ this._line = false;
+ this._circleBt = false;
+ this._triangleBt = false;
+ this._rectangleBt = false;
+ this._arrowBt = false;
+ this._lineBt = false;
+ this._colorBt = false;
+ this._bezierBt = false;
+ this._widthBt = false;
+
+ this._color = "";
+ this._widthSelected = "";
+
+
+ this._palette = [
+ "D0021B", "F5A623", "F8E71C", "8B572A", "7ED321", "417505", "9013FE", "4A90E2", "50E3C2", "B8E986", "000000", "4A4A4A", "9B9B9B", "FFFFFF",
+ ];
+
+ this._width = [
+ "1", "5", "10", "100", "200", "300"
+ ];
+
+ }
+
+
+
+ drag = (e: React.PointerEvent) => {
+ this.dragStart(e);
+ }
+
+
+
+
+
+ @action
+ toggleCircle = (e: React.PointerEvent) => {
+ const curr = this._circle;
+ this.allFalse();
+ curr ? this._circle = false : this._circle = true;
+ this._circleBt = this._circle;
+ }
+ @action
+ toggleTriangle = (e: React.PointerEvent) => {
+ const curr = this._triangle;
+ this.allFalse();
+ curr ? this._triangle = false : this._triangle = true;
+ this._triangleBt = this._triangle;
+ }
+ @action
+ toggleRectangle = (e: React.PointerEvent) => {
+ const curr = this._rectangle;
+ this.allFalse();
+ curr ? this._rectangle = false : this._rectangle = true;
+ this._rectangleBt = this._rectangle;
+ }
+ @action
+ toggleArrow = (e: React.PointerEvent) => {
+ const curr = this._arrow;
+ this.allFalse();
+ curr ? this._arrow = false : this._arrow = true;
+ this._arrowBt = this._arrow;
+ }
+ @action
+ toggleLine = (e: React.PointerEvent) => {
+ const curr = this._line;
+ this.allFalse();
+ curr ? this._line = false : this._line = true;
+ this._lineBt = this._line;
+ }
+
+ @action
+ changeBezierClick = (e: React.PointerEvent) => {
+ const curr = this._bezierBt;
+ this.allFalse();
+ curr ? this._bezierBt = false : this._bezierBt = true;
+ this.changeBezier(e);
+ }
+
+ @action
+ changeWidthClick = (e: React.PointerEvent) => {
+ this._widthBt ? this._widthBt = false : this._widthBt = true;
+ }
+ @action
+ changeColorClick = (e: React.PointerEvent) => {
+ this._colorBt ? this._colorBt = false : this._colorBt = true;
+ }
+
+ allFalse = () => {
+ this._circle = false;
+ this._triangle = false;
+ this._rectangle = false;
+ this._arrow = false;
+ this._line = false;
+ this._circleBt = false;
+ this._triangleBt = false;
+ this._rectangleBt = false;
+ this._arrowBt = false;
+ this._lineBt = false;
+ this._bezierBt = false;
+ }
+
+ render() {
+ var widthPicker;
+ if (this._widthBt) {
+ widthPicker = <div className="btn2-group">
+ <button
+ className="antimodeMenu-button"
+ key="width"
+ onPointerDown={this.changeWidthClick}
+ style={this._widthBt ? { backgroundColor: "121212" } : {}}>
+ W
+ </button>
+ {this._width.map(wid => {
+ return <button
+ className="antimodeMenu-button"
+ key={wid}
+ onPointerDown={() => this.changeWidth(wid)}
+ style={this._colorBt ? { backgroundColor: "121212" } : {}}>
+ {wid}
+ </button>;
+
+ })}
+ </div>;
+ } else {
+ widthPicker = <button
+ className="antimodeMenu-button"
+ key="width"
+ onPointerDown={this.changeWidthClick}
+ style={this._widthBt ? { backgroundColor: "121212" } : {}}>
+ W
+ </button>;
+ }
+
+ var colorPicker;
+ if (this._colorBt) {
+ colorPicker = <div className="btn-group">
+ <button
+ className="antimodeMenu-button"
+ key="color"
+ onPointerDown={this.changeColorClick}
+ style={this._colorBt ? { backgroundColor: "121212" } : {}}>
+ <div className="color-preview" style={this._color === "" ? { backgroundColor: "121212" } : { backgroundColor: this._color }}></div>
+ </button>
+ {this._palette.map(color => {
+ return <button
+ className="antimodeMenu-button"
+ key={color}
+ onPointerDown={() => this.changeColor(color)}
+ style={this._colorBt ? { backgroundColor: "121212" } : {}}>
+ <div className="color-preview" style={{ backgroundColor: color }}></div>
+ </button>;
+ })}
+ </div>;
+ } else {
+ colorPicker = <button
+ className="antimodeMenu-button"
+ title="colorChanger"
+ key="color"
+ onPointerDown={this.changeColorClick}
+ style={this._colorBt ? { backgroundColor: "121212" } : {}}>
+ <div className="color-preview" style={this._color === "" ? { backgroundColor: "121212" } : { backgroundColor: this._color }}></div>
+ </button>;
+ }
+
+
+ const buttons = [
+ <button
+ className="antimodeMenu-button"
+ title="Drag"
+ key="drag"
+ onPointerDown={this.drag}>
+ ✜
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Draw Circle"
+ key="circle"
+ onPointerDown={this.toggleCircle}
+ style={this._circleBt ? { backgroundColor: "121212" } : {}}>
+ O
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Draw Traingle"
+ key="triangle"
+ onPointerDown={this.toggleTriangle}
+ style={this._triangleBt ? { backgroundColor: "121212" } : {}}>
+ ∆
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Draw Rectangle"
+ key="rectangle"
+ onPointerDown={this.toggleRectangle}
+ style={this._rectangleBt ? { backgroundColor: "121212" } : {}}>
+ ロ
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Draw Arrow"
+ key="arrow"
+ onPointerDown={this.toggleArrow}
+ style={this._arrowBt ? { backgroundColor: "121212" } : {}}>
+ ➜
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Draw Line"
+ key="line"
+ onPointerDown={this.toggleLine}
+ style={this._lineBt ? { backgroundColor: "121212" } : {}}>
+ –
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Bezier changer"
+ key="bezier"
+ onPointerDown={this.changeBezierClick}
+ style={this._bezierBt ? { backgroundColor: "121212" } : {}}>
+ B
+ </button>,
+ widthPicker,
+ colorPicker,
+ ];
+ return this.getElement(buttons);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index cdfeeaa6b..97244ed06 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -42,6 +42,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@observable _downY: number = 0;
@observable _visible: boolean = false;
_commandExecuted = false;
+ @observable _pointsX: number[] = [];
+ @observable _pointsY: number[] = [];
+ @observable _freeHand: boolean = false;
componentDidMount() {
this.props.setPreviewCursor?.(this.setPreviewCursor);
@@ -57,6 +60,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (hideMarquee) {
this._visible = false;
}
+ this._pointsX = [];
+ this._pointsY = [];
+ this._freeHand = false;
}
@undoBatch
@@ -191,6 +197,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
onPointerMove = (e: PointerEvent): void => {
this._lastX = e.pageX;
this._lastY = e.pageY;
+ this._pointsX.push(e.clientX);
+ this._pointsY.push(e.clientY);
if (!e.cancelBubble) {
if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD ||
Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
@@ -519,6 +527,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
this.cleanupInteractions(false);
}
+ if (e.key === "r") {
+ this._commandExecuted = true;
+ e.stopPropagation();
+ e.preventDefault();
+ this.changeFreeHand(true);
+ }
+ }
+
+ @action
+ changeFreeHand = (x: boolean) => {
+ this._freeHand = x;
}
// @action
// marqueeInkSelect(ink: Map<any, any>) {
@@ -559,7 +578,51 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// this.ink = new InkField(idata);
// }
// }
+ touchesLine(r1: { left: number, top: number, width: number, height: number }) {
+ for (var i = 0; i < this._pointsX.length; i++) {
+ const topLeft = this.props.getTransform().transformPoint(this._pointsX[i], this._pointsY[i]);
+ if (topLeft[0] > r1.left &&
+ topLeft[0] < r1.left + r1.width &&
+ topLeft[1] > r1.top &&
+ topLeft[1] < r1.top + r1.height) {
+ return true;
+ }
+ }
+ return false;
+ }
+ boundingShape(r1: { left: number, top: number, width: number, height: number }) {
+ const trueLeft = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[0];
+ const trueTop = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[1];
+ const trueRight = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[0];
+ const trueBottom = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[1];
+
+ if (r1.left > trueLeft && r1.top > trueTop && r1.left + r1.width < trueRight && r1.top + r1.height < trueBottom) {
+ var hasTop = false;
+ var hasLeft = false;
+ var hasBottom = false;
+ var hasRight = false;
+ for (var i = 0; i < this._pointsX.length; i++) {
+ const truePoint = this.props.getTransform().transformPoint(this._pointsX[i], this._pointsY[i]);
+ if (!hasLeft && (truePoint[0] > trueLeft && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) {
+ hasLeft = true;
+ }
+ if (!hasTop && (truePoint[1] > trueTop && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) {
+ hasTop = true;
+ }
+ if (!hasRight && (truePoint[0] < trueRight && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) {
+ hasRight = true;
+ }
+ if (!hasBottom && (truePoint[1] < trueBottom && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) {
+ hasBottom = true;
+ }
+ if (hasTop && hasLeft && hasBottom && hasRight) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
marqueeSelect(selectBackgrounds: boolean = true) {
const selRect = this.Bounds;
const selection: Doc[] = [];
@@ -569,8 +632,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const y = NumCast(doc.y);
const w = NumCast(layoutDoc._width);
const h = NumCast(layoutDoc._height);
- if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
- selection.push(doc);
+ if (this._freeHand === false) {
+ if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+ selection.push(doc);
+ }
+ } else {
+ if (this.touchesLine({ left: x, top: y, width: w, height: h }) ||
+ this.boundingShape({ left: x, top: y, width: w, height: h })) {
+ selection.push(doc);
+ }
}
});
if (!selection.length && selectBackgrounds) {
@@ -597,8 +667,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const y = NumCast(doc.y);
const w = NumCast(layoutDoc._width);
const h = NumCast(layoutDoc._height);
- if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
- selection.push(doc);
+ if (this._freeHand === false) {
+ if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+ selection.push(doc);
+ }
+ } else {
+ if (this.touchesLine({ left: x, top: y, width: w, height: h }) ||
+ this.boundingShape({ left: x, top: y, width: w, height: h })) {
+ selection.push(doc);
+ }
}
});
}
@@ -614,13 +691,40 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
* This contains the "C for collection, ..." text on marquees.
* Commented out by syip2 when the marquee menu was added.
*/
- return <div className="marquee" style={{
- transform: `translate(${p[0]}px, ${p[1]}px)`,
- width: `${Math.abs(v[0])}`,
- height: `${Math.abs(v[1])}`, zIndex: 2000
- }} >
- {/* <span className="marquee-legend" /> */}
- </div>;
+ if (!this._freeHand) {
+ return <div className="marquee" style={{
+ transform: `translate(${p[0]}px, ${p[1]}px)`,
+ width: `${Math.abs(v[0])}`,
+ height: `${Math.abs(v[1])}`, zIndex: 2000
+ }} >
+ {/* <span className="marquee-legend" /> */}
+ </div>;
+
+ } else {
+ //subtracted 250 for offset
+ var str: string = "";
+ for (var i = 0; i < this._pointsX.length; i++) {
+ var x = 0;
+ x = this._pointsX[i] - 250;
+ str += x.toString();
+ str += ",";
+ str += this._pointsY[i].toString();
+ str += (" ");
+ }
+
+ //hardcoded height and width.
+ return <div className="marquee" style={{ zIndex: 2000 }}>
+ <svg height={2000} width={2000}>
+ <polyline
+ points={str}
+ fill="none"
+ stroke="black"
+ strokeWidth="1"
+ strokeDasharray="3"
+ />
+ </svg>
+ </div>;
+ }
}
render() {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 6d53915ea..2ddf2c74a 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -28,8 +28,12 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
color={StrCast(CurrentUserUtils.ActivePen ? CurrentUserUtils.ActivePen.backgroundColor : undefined,
StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
<div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
- <div>{InkingControl.Instance.selectedWidth ?? 2}</div>
+ <div> {InkingControl.Instance.selectedWidth ?? 2}</div>
<input type="range" value={InkingControl.Instance.selectedWidth ?? 2} defaultValue={2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => InkingControl.Instance.switchWidth(e.target.value)} />
+ <div> {InkingControl.Instance.selectedBezier ?? 2}</div>
+ <input type="range" value={InkingControl.Instance.selectedBezier ?? 2} defaultValue={2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => InkingControl.Instance.switchBezier(e.target.value)} />
+ <br />
+ <br />
</div>
</div>;
}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e7031cc39..31f589ec0 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -63,6 +63,7 @@ export const documentSchema = createSchema({
letterSpacing: "string",
opacity: "number", // opacity of document
strokeWidth: "number",
+ strokeBezier: "number",
textTransform: "string",
treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden
treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index 3b6170f68..a50cca2b0 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -39,7 +39,10 @@ export namespace GestureUtils {
EndBracket = "endbracket",
Stroke = "stroke",
Scribble = "scribble",
- Text = "text"
+ Text = "text",
+ Triangle = "triangle",
+ Circle = "circle",
+ Rectangle = "rectangle",
}
export const GestureRecognizer = new NDollarRecognizer(false);
diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts
index e5740d105..9d42035d1 100644
--- a/src/pen-gestures/ndollar.ts
+++ b/src/pen-gestures/ndollar.ts
@@ -142,7 +142,7 @@ export class Result {
//
// NDollarRecognizer constants
//
-const NumMultistrokes = 4;
+const NumMultistrokes = 7;
const NumPoints = 96;
const SquareSize = 250.0;
const OneDThreshold = 0.25; // customize to desired gesture set (usually 0.20 - 0.35)
@@ -190,6 +190,21 @@ export class NDollarRecognizer {
// new Array(new Point(150, 150), new Point(150, 0), new Point(150, 150), new Point(0, 150))
new Array(new Point(10, 100), new Point(100, 100), new Point(150, 12), new Point(200, 103), new Point(300, 100))
));
+ this.Multistrokes[4] = new Multistroke(GestureUtils.Gestures.Triangle, useBoundedRotationInvariance, new Array(
+ new Array(new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100))
+ ));
+ this.Multistrokes[5] = new Multistroke(GestureUtils.Gestures.Circle, useBoundedRotationInvariance, new Array(
+ new Array(new Point(200, 250), new Point(240, 230), new Point(248, 210), new Point(248, 190), new Point(240, 170), new Point(200, 150), new Point(160, 170), new Point(151, 190), new Point(151, 210), new Point(160, 230), new Point(201, 250))
+ ));
+ this.Multistrokes[6] = new Multistroke(GestureUtils.Gestures.Rectangle, useBoundedRotationInvariance, new Array(
+ new Array(
+ new Point(30, 146), //new Point(29, 160), new Point(30, 180), new Point(31, 200),
+ new Point(30, 222), //new Point(50, 219), new Point(70, 225), new Point(90, 230),
+ new Point(106, 225), //new Point(100, 200), new Point(106, 180), new Point(110, 160),
+ new Point(106, 146), //new Point(80, 150), new Point(50, 146),
+ new Point(30, 143),
+ new Point(29, 220))
+ ));
//
// PREDEFINED STROKES
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 850c533fc..452882e09 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -5,6 +5,8 @@ declare module 'react-image-lightbox-with-rotate';
declare module 'cors';
declare module 'webrtc-adapter';
+declare module 'bezier-curve';
+declare module 'fit-curve'
declare module '@react-pdf/renderer' {