diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 43 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 26 | ||||
-rw-r--r-- | src/fields/InkField.ts | 4 |
3 files changed, 70 insertions, 3 deletions
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index b13b04f68..533fdf006 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -17,8 +17,11 @@ export class InkStrokeProperties { private _lastDash = "2"; private _inkDocs: { x: number, y: number, width: number, height: number }[] = []; + // Indicates whether the ink is locked. @observable _lock = false; + // Indicates whether the ink's format is being currently edited (displaying of control points). @observable _controlBtn = false; + // Stores the index of the current selected control point of the ink instance. @observable _currPoint = -1; getField(key: string) { @@ -80,6 +83,14 @@ export class InkStrokeProperties { InkStrokeProperties.Instance = this; } + /** + * Adds a new control point to the ink instance when editing its format. + * @param x The x-coordinate of the current new point. + * @param y The y-coordinate of the current new point. + * @param pts The list containing all of the points to be added in PointData form. + * @param index The index of the current new point. + * @param control The list of all control points of the ink. + */ @undoBatch @action addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => { @@ -115,6 +126,12 @@ export class InkStrokeProperties { })); } + /** + * Helper function that enables other functions to be applied to a particular ink instance. + * @param func The inputted function. + * @param requireCurrPoint Indicates whether the current selected point is needed. + * @returns The applied function. + */ applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { var appliedFunc = false; this.selectedInk?.forEach(action(inkView => { @@ -145,6 +162,10 @@ export class InkStrokeProperties { return appliedFunc; } + /** + * Deletes the points of the current ink instance. + * @returns The changed x- and y-coordinates of the control points. + */ @undoBatch @action deletePoints = () => this.applyFunction((doc: Doc, ink: InkData) => { @@ -168,6 +189,11 @@ export class InkStrokeProperties { return newPoints; }, true); + /** + * Rotates the points of the current ink instance by a certain angle degree. + * @param angle The angle at which to rotate the ink (all of its x- and y-coordinates). + * @returns The changed x- and y-coordinates of the control points. + */ @undoBatch @action rotate = (angle: number) => { @@ -186,6 +212,13 @@ export class InkStrokeProperties { }); } + /** + * Handles the movement / scaling of control points of an ink instance. + * @param xDiff The movement of the control point's x-coordinate. + * @param yDiff The movement of the control point's y-coordinate. + * @param controlNum The index of the current control point selected. + * @returns The changed x- and y-coordinates of the control points. + */ @undoBatch @action control = (xDiff: number, yDiff: number, controlNum: number) => @@ -209,6 +242,11 @@ export class InkStrokeProperties { return newPoints; }); + /** + * Changes the color of the border of the ink instance. + * @param color The new hex value to change the border to. + * @returns true. + */ @undoBatch @action switchStk = (color: ColorState) => { @@ -217,6 +255,11 @@ export class InkStrokeProperties { return true; } + /** + * Changes the color of the fill of the ink instance. + * @param color The new hex value to change the fill to. + * @returns true. + */ @undoBatch @action switchFil = (color: ColorState) => { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 449019ca8..859e53b97 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -41,6 +41,11 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined; }); + /** + * Handles the movement of a selected control point when the user clicks and drags. + * @param e React Pointer Event. + * @param controlNum The number of the currently selected control point. + */ @action onControlDown = (e: React.PointerEvent, controlNum: number): void => { if (InkStrokeProperties.Instance) { @@ -56,6 +61,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume } } + /** + * Changes the current selected control point. + * @param i The number of the point to be selected. + */ @action changeCurrPoint = (i: number) => { if (InkStrokeProperties.Instance) { @@ -64,6 +73,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume } } + /** + * Deletes the currently selected points. + * @param e Keyboard Event. + */ @action delPts = (e: KeyboardEvent) => { if (["-", "Backspace", "Delete"].includes(e.key)) { @@ -71,6 +84,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume } } + /** + * Handles the movement of the entire ink object when the user clicks and drags. + * @param e React Pointer Event. + */ onPointerDown = (e: React.PointerEvent) => { if (this.props.isSelected(true)) { setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e: PointerEvent, doubleTap: boolean | undefined) => @@ -102,17 +119,18 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth); const strokeColor = StrCast(this.layoutDoc.color, ""); + // Visually renders the polygonal line made by the user. const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false); + // Invisible polygonal line that enables the ink to be selected by the user. const hpoints = InteractionUtils.CreatePolyline(data, left, top, this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15), StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true); - //points for adding const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), @@ -153,24 +171,27 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume // } const dotsize = Math.max(width * scaleX, height * scaleY) / 40; + // Additional points (controls) added by the user via click when editing the ink's format. const addpoints = apoints.map((pts, i) => <svg height="10" width="10" key={`add${i}`}> <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} stroke="invisible" strokeWidth={dotsize / 2} fill="invisible" onPointerDown={(e) => { formatInstance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll" /> </svg>); + // Green circles that allow the user to edit the curvature of the line using the selected point as the anchor. const handles = handlePoints.map((pts, i) => <svg height="10" width="10" key={`hdl${i}`}> <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth} strokeWidth={0} fill="green" onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="default" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> </svg>); - + // Points (red circles) of the ink that are made visible to user when editing its format. const controls = controlPoints.map((pts, i) => <svg height="10" width="10" key={`ctrl${i}`}> <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} strokeWidth={0} fill="red" onPointerDown={(e) => { this.changeCurrPoint(pts.I); this.onControlDown(e, pts.I); }} pointerEvents="all" cursor="default" /> </svg>); + // Set of two green lines (each with a handle at the end) that are rendered perpendicular to the current selected point while editing. const handleLines = handleLine.map((pts, i) => <svg height="100" width="100" key={`line${i}`} > <line x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} @@ -181,7 +202,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> </svg>); - return ( <svg className="inkingStroke" style={{ diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index dbe51b24a..b79a03146 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -4,6 +4,7 @@ import { ObjectField } from "./ObjectField"; import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols"; import { Scripting } from "../client/util/Scripting"; +// Helps keep track of the current ink tool in use. export enum InkTool { None = "none", Pen = "pen", @@ -12,11 +13,13 @@ export enum InkTool { Stamp = "stamp" } +// Defines a point in an ink as a pair of x- and y-coordinates. export interface PointData { X: number; Y: number; } +// Defines an ink as an array of points. export type InkData = Array<PointData>; const pointSchema = createSimpleSchema({ @@ -28,6 +31,7 @@ const strokeDataSchema = createSimpleSchema({ "*": true }); +// Holistic class representing the store of an ink. @Deserializable("ink") export class InkField extends ObjectField { @serializable(list(object(strokeDataSchema))) |