diff options
-rw-r--r-- | src/client/documents/Documents.ts | 8 | ||||
-rw-r--r-- | src/client/views/GestureOverlay.tsx | 2 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 2 | ||||
-rw-r--r-- | src/client/views/InkControl.tsx | 101 | ||||
-rw-r--r-- | src/client/views/InkControls.tsx | 118 | ||||
-rw-r--r-- | src/client/views/InkHandles.tsx | 84 | ||||
-rw-r--r-- | src/client/views/InkStroke.scss (renamed from src/client/views/InkingStroke.scss) | 2 | ||||
-rw-r--r-- | src/client/views/InkStroke.tsx | 187 | ||||
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 205 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 271 | ||||
-rw-r--r-- | src/client/views/PropertiesButtons.tsx | 4 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/ColorBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 6 |
18 files changed, 468 insertions, 540 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 24682cbd0..188fe31c0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -31,7 +31,7 @@ import { CollectionView, CollectionViewType } from "../views/collections/Collect import { ContextMenu } from "../views/ContextMenu"; import { ContextMenuProps } from "../views/ContextMenuItem"; import { DFLT_IMAGE_NATIVE_DIM } from "../views/globalCssVariables.scss"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkStroke } from "../views/InkStroke"; import { AudioBox } from "../views/nodes/AudioBox"; import { ColorBox } from "../views/nodes/ColorBox"; import { ComparisonBox } from "../views/nodes/ComparisonBox"; @@ -432,7 +432,7 @@ export namespace Docs { options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true } }], [DocumentType.INK, { - layout: { view: InkingStroke, dataField: defaultDataKey }, + layout: { view: InkStroke, dataField: defaultDataKey }, options: { _fontFamily: "cursive", backgroundColor: "transparent", links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SCREENSHOT, { @@ -760,7 +760,7 @@ export namespace Docs { const I = new Doc(); I[Initializing] = true; I.type = DocumentType.INK; - I.layout = InkingStroke.LayoutString("data"); + I.layout = InkStroke.LayoutString("data"); I.color = color; I.fillColor = fillColor; I.strokeWidth = Number(strokeWidth); @@ -1144,7 +1144,7 @@ export namespace DocUtils { layout = AudioBox.LayoutString; } else if (field instanceof InkField) { created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); - layout = InkingStroke.LayoutString; + layout = InkStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); layout = CollectionView.LayoutString; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 491bf18b2..455b3a935 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -17,7 +17,7 @@ import { Scripting } from "../util/Scripting"; import { Transform } from "../util/Transform"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; import "./GestureOverlay.scss"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkStroke"; import { DocumentView } from "./nodes/DocumentView"; import { RadialMenu } from "./nodes/RadialMenu"; import HorizontalPalette from "./Palette"; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index cbaa706e0..c1130ce61 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -116,7 +116,7 @@ export class KeyManager { case "escape": DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; - InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false); + InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlButton = false); CurrentUserUtils.SelectedTool = InkTool.None; var doDeselect = true; if (SnappingManager.GetIsDragging()) { diff --git a/src/client/views/InkControl.tsx b/src/client/views/InkControl.tsx deleted file mode 100644 index accb16f61..000000000 --- a/src/client/views/InkControl.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React = require("react"); -import { observable, action } from "mobx"; -import { observer } from "mobx-react"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { setupMoveUpEvents, emptyFunction } from "../../Utils"; -import { UndoManager } from "../util/UndoManager"; -import { ControlPoint } from "../../fields/InkField"; -import { Transform } from "../util/Transform"; - -export interface InkControlProps { - control: ControlPoint; - left: number; - top: number; - scaleX: number; - scaleY: number; - strokeWidth: number; - ScreenToLocalTransform: () => Transform; -} - -@observer -export class InkControl extends React.Component<InkControlProps> { - @observable private _overControl = false; - - @action - onPointerEnter = () => { - this._overControl = true; - } - - @action - onPointerLeave = () => { - this._overControl = false; - } - - /** - * Handles the movement of a selected control point when the user clicks and drags. - * @param controlNum The index of the currently selected control point. - */ - @action - onControlDown = (e: React.PointerEvent, controlNum: number): void => { - if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance.moveControl(0, 0, 1); - const controlUndo = UndoManager.StartBatch("DocDecs set radius"); - const screenScale = this.props.ScreenToLocalTransform().Scale; - setupMoveUpEvents(this, e, - (e: PointerEvent, down: number[], delta: number[]) => { - InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlNum); - return false; - }, - () => controlUndo?.end(), emptyFunction); - } - } - - /** - * Deletes the currently selected point. - */ - @action - onDelete = (e: KeyboardEvent) => { - if (["-", "Backspace", "Delete"].includes(e.key)) { - if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation(); - } - } - - /** - * Changes the current selected control point. - */ - @action - changeCurrPoint = (i: number) => { - if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance._currPoint = i; - document.addEventListener("keydown", this.onDelete, true); - } - } - - - render() { - const control = this.props.control; - const left = this.props.left; - const top = this.props.top; - const scaleX = this.props.scaleX; - const scaleY = this.props.scaleY; - const strokeWidth = this.props.strokeWidth; - - return ( - <svg height="10" width="10" key={`ctrl${control.I}`}> - <rect - x={(control.X - left - strokeWidth / 2) * scaleX} - y={(control.Y - top - strokeWidth / 2) * scaleY} - height={this._overControl ? strokeWidth * 1.5 : strokeWidth} - width={this._overControl ? strokeWidth * 1.5 : strokeWidth} - strokeWidth={strokeWidth / 6} stroke="#1F85DE" - fill={InkStrokeProperties.Instance?._currPoint === control.I ? "#1F85DE" : "white"} - onPointerDown={(e) => { this.changeCurrPoint(control.I); this.onControlDown(e, control.I); }} - onMouseEnter={this.onPointerEnter} - onMouseLeave={this.onPointerLeave} - pointerEvents="all" - cursor="default" - /> - </svg> - ) - } -}
\ No newline at end of file diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx new file mode 100644 index 000000000..eeddfce4c --- /dev/null +++ b/src/client/views/InkControls.tsx @@ -0,0 +1,118 @@ +import React = require("react"); +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { setupMoveUpEvents, emptyFunction } from "../../Utils"; +import { UndoManager } from "../util/UndoManager"; +import { ControlPoint, InkData, PointData } from "../../fields/InkField"; +import { Transform } from "../util/Transform"; + +export interface InkControlProps { + data: InkData; + addedPoints: PointData[]; + format: number[]; + ScreenToLocalTransform: () => Transform; +} + +@observer +export class InkControls extends React.Component<InkControlProps> { + // @observable private _controlPoints: ControlPoint[] = []; + @observable private _overControl = -1; + @observable private _overAddPoint = -1; + + /** + * Handles the movement of a selected control point when the user clicks and drags. + * @param controlNum The index of the currently selected control point. + */ + @action + onControlDown = (e: React.PointerEvent, controlNum: number): void => { + if (InkStrokeProperties.Instance) { + InkStrokeProperties.Instance.moveControl(0, 0, 1); + const controlUndo = UndoManager.StartBatch("DocDecs set radius"); + const screenScale = this.props.ScreenToLocalTransform().Scale; + setupMoveUpEvents(this, e, + (e: PointerEvent, down: number[], delta: number[]) => { + InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlNum); + return false; + }, + () => controlUndo?.end(), emptyFunction); + } + } + + /** + * Deletes the currently selected point. + */ + @action + onDelete = (e: KeyboardEvent) => { + if (["-", "Backspace", "Delete"].includes(e.key)) { + if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation(); + } + } + + /** + * Changes the current selected control point. + */ + @action + changeCurrPoint = (i: number) => { + if (InkStrokeProperties.Instance) { + InkStrokeProperties.Instance._currentPoint = i; + document.addEventListener("keydown", this.onDelete, true); + } + } + + @action onEnterControl = (i: number) => { this._overControl = i; }; + @action onLeaveControl = () => { this._overControl = -1; }; + @action onEnterAddPoint = (i: number) => { this._overAddPoint = i; }; + @action onLeaveAddPoint = () => { this._overAddPoint = -1; }; + + render() { + const data = this.props.data; + const controlPoints: ControlPoint[] = []; + if (data.length >= 4) { + for (let i = 0; i <= data.length - 4; i += 4) { + controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i }); + controlPoints.push({ X: data[i + 3].X, Y: data[i + 3].Y, I: i + 3 }); + } + } + const addedPoints = this.props.addedPoints; + const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + + return ( + <> + {addedPoints.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={this._overAddPoint === i ? "#1F85DE" : "transparent"} + strokeWidth={dotsize / 4} fill={this._overAddPoint === i ? "#1F85DE" : "transparent"} + onPointerDown={() => { InkStrokeProperties.Instance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }} + onMouseEnter={() => this.onEnterAddPoint(i)} + onMouseLeave={this.onLeaveAddPoint} + pointerEvents="all" + cursor="all-scroll" + /> + </svg> + )} + {controlPoints.map((control, i) => + <svg height="10" width="10" key={`ctrl${i}`}> + <rect + x={(control.X - left - strokeWidth / 2) * scaleX} + y={(control.Y - top - strokeWidth / 2) * scaleY} + height={this._overControl === i ? strokeWidth * 1.5 : strokeWidth} + width={this._overControl === i ? strokeWidth * 1.5 : strokeWidth} + strokeWidth={strokeWidth / 6} stroke="#1F85DE" + fill={InkStrokeProperties.Instance?._currentPoint === control.I ? "#1F85DE" : "white"} + onPointerDown={(e) => { this.changeCurrPoint(control.I); this.onControlDown(e, control.I); }} + onMouseEnter={() => this.onEnterControl(i)} + onMouseLeave={this.onLeaveControl} + pointerEvents="all" + cursor="default" + /> + </svg> + )} + </> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx new file mode 100644 index 000000000..c2163c124 --- /dev/null +++ b/src/client/views/InkHandles.tsx @@ -0,0 +1,84 @@ +import React = require("react"); +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { setupMoveUpEvents, emptyFunction } from "../../Utils"; +import { UndoManager } from "../util/UndoManager"; +import { InkData, HandlePoint, HandleLine } from "../../fields/InkField"; +import { Transform } from "../util/Transform"; + +export interface InkControlProps { + data: InkData; + format: number[]; + ScreenToLocalTransform: () => Transform; +} + +@observer +export class InkHandles extends React.Component<InkControlProps> { + /** + * Handles the movement of a selected handle point when the user clicks and drags. + * @param handleNum The index of the currently selected handle point. + */ + onHandleDown = (e: React.PointerEvent, handleNum: number): void => { + if (InkStrokeProperties.Instance) { + InkStrokeProperties.Instance.moveControl(0, 0, 1); + const controlUndo = UndoManager.StartBatch("DocDecs set radius"); + const screenScale = this.props.ScreenToLocalTransform().Scale; + setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { + InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleNum); + return false; + }, () => controlUndo?.end(), emptyFunction + ); + } + } + + render() { + const formatInstance = InkStrokeProperties.Instance; + if (!formatInstance) return (null); + const data = this.props.data; + const handlePoints: HandlePoint[] = []; + const handleLines: HandleLine[] = []; + if (data.length >= 4) { + // adding first and last (single) handle lines + handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[1].X, Y2: data[1].Y, dot1: 0, dot2: 0 }); + handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 }); + for (let i = 0; i <= data.length - 4; i += 4) { + handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 }); + handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 }); + } + for (let i = 2; i < data.length - 4; i += 4) { + handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 3].X, Y2: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); + } + } + const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + + return ( + <> + {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 / 2} + strokeWidth={0} + fill="#1F85DE" + onPointerDown={(e) => this.onHandleDown(e, pts.I)} + pointerEvents="all" + cursor="default" + display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> + </svg>)} + {handleLines.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} + x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} + y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} + stroke="#1F85DE" + strokeWidth={dotsize / 8} + display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> + </svg>)} + </> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkStroke.scss index f67b1779d..812a79bd5 100644 --- a/src/client/views/InkingStroke.scss +++ b/src/client/views/InkStroke.scss @@ -1,4 +1,4 @@ -.inkingStroke { +.inkStroke { mix-blend-mode: multiply; stroke-linejoin: round; stroke-linecap: round; diff --git a/src/client/views/InkStroke.tsx b/src/client/views/InkStroke.tsx new file mode 100644 index 000000000..9ca8bdbea --- /dev/null +++ b/src/client/views/InkStroke.tsx @@ -0,0 +1,187 @@ +import React = require("react"); +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { Doc } from "../../fields/Doc"; +import { documentSchema } from "../../fields/documentSchemas"; +import { InkData, InkField, InkTool, ControlPoint, HandlePoint, HandleLine } from "../../fields/InkField"; +import { makeInterface } from "../../fields/Schema"; +import { Cast, StrCast } from "../../fields/Types"; +import { TraceMobx } from "../../fields/util"; +import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils"; +import { CognitiveServices } from "../cognitive_services/CognitiveServices"; +import { InteractionUtils } from "../util/InteractionUtils"; +import { Scripting } from "../util/Scripting"; +import { UndoManager } from "../util/UndoManager"; +import { ContextMenu } from "./ContextMenu"; +import { ViewBoxBaseComponent } from "./DocComponent"; +import "./InkStroke.scss"; +import { FieldView, FieldViewProps } from "./nodes/FieldView"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; +import { InkControls } from "./InkControls"; +import { InkHandles } from "./InkHandles"; + +type InkDocument = makeInterface<[typeof documentSchema]>; +const InkDocument = makeInterface(documentSchema); + +@observer +export class InkStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) { + static readonly MaskDim = 50000; + @observable private _properties?: InkStrokeProperties; + + constructor(props: FieldViewProps & InkDocument) { + super(props); + + this._properties = InkStrokeProperties.Instance; + } + + public static LayoutString(fieldStr: string) { + return FieldView.LayoutString(InkStroke, fieldStr); + } + + analyzeStrokes() { + const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; + CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]); + } + + @action + public static toggleMask = (inkDoc: Doc) => { + inkDoc.isInkMask = !inkDoc.isInkMask; + inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined; + inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined; + inkDoc.color = "#9b9b9bff"; + inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined; + } + + /** + * 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) => + doubleTap && this._properties && (this._properties._controlButton = true)) + ); + } + } + + render() { + TraceMobx(); + + const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; + const strokeWidth = Number(this.layoutDoc.strokeWidth); + const lineTop = Math.min(...data.map(p => p.Y)); + const lineBottom = Math.max(...data.map(p => p.Y)); + const lineLeft = Math.min(...data.map(p => p.X)); + const lineRight = Math.max(...data.map(p => p.X)); + const left = lineLeft - strokeWidth / 2; + const top = lineTop - strokeWidth / 2; + const right = lineRight + strokeWidth / 2; + const bottom = lineBottom + strokeWidth / 2; + const width = Math.max(1, right - left); + const height = Math.max(1, bottom - top); + const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth); + const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth); + const strokeColor = StrCast(this.layoutDoc.color, ""); + const dotsize = Math.max(width * scaleX, height * scaleY) / 40; + + // Visually renders the polygonal line made by the user. + const inkLine = 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 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, + false); + const selectedLine = InteractionUtils.CreatePolyline( + data, lineLeft - strokeWidth * 3, lineTop - strokeWidth * 3, "#1F85DE", strokeWidth / 6, + strokeWidth / 6,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 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, + false); + // Invisible polygonal line that enables the ink to be selected by the user. + const clickableLine = 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); + const addedPoints = 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), + StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", + this.props.isSelected() && strokeWidth <= 5, false); + + return ( + <svg className="inkStroke" + style={{ + pointerEvents: this.props.Document.isInkMask && this.props.layerProvider?.(this.props.Document) !== false ? "all" : "none", + transform: this.props.Document.isInkMask ? `translate(${InkStroke.MaskDim / 2}px, ${InkStroke.MaskDim / 2}px)` : undefined, + mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset", + overflow: "visible", + }} + onPointerDown={this.onPointerDown} + onContextMenu={() => { + const cm = ContextMenu.Instance; + if (cm) { + !Doc.UserDoc().noviceMode && cm.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" }); + cm.addItem({ description: "Toggle Mask", event: () => InkStroke.toggleMask(this.rootDoc), icon: "paint-brush" }); + cm.addItem({ description: "Edit Points", event: action(() => {if (this._properties) { this._properties._controlButton = !this._properties._controlButton; }} ), icon: "paint-brush" }); + } + }} + > + + {clickableLine} + {inkLine} + {this.props.isSelected() ? selectedLine : ""} + {this.props.isSelected() && this._properties?._controlButton ? + <> + <InkControls + data={data} + addedPoints={addedPoints} + format={[left, top, scaleX, scaleY, strokeWidth, dotsize]} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} /> + <InkHandles + data={data} + format={[left, top, scaleX, scaleY, strokeWidth, dotsize]} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} /> + </> : ""} + </svg> + ); + } +} + + +export function SetActiveInkWidth(width: string): void { !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); } +export function SetActiveBezierApprox(bezier: string): void { ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? "" : bezier); } +export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); } +export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); } +export function SetActiveArrowStart(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); } +export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); } +export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); } +export function ActiveInkPen(): Doc { return Doc.UserDoc(); } +export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); } +export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); } +export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); } +export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); } +export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); } +export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); } +export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } +Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { + CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None; + SetActiveInkWidth(width); + SetActiveInkColor(color); + SetActiveFillColor(fill); + SetActiveArrowStart(arrowStart); + SetActiveArrowEnd(arrowEnd); + SetActiveDash(dash); +}); +Scripting.addGlobal(function activateEraser(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Eraser : InkTool.None; }); +Scripting.addGlobal(function activateStamp(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Stamp : InkTool.None; }); +Scripting.addGlobal(function deactivateInk() { return CurrentUserUtils.SelectedTool = InkTool.None; }); +Scripting.addGlobal(function setInkWidth(width: any) { return SetActiveInkWidth(width); }); +Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); }); +Scripting.addGlobal(function setFillColor(fill: any) { return SetActiveFillColor(fill); }); +Scripting.addGlobal(function setActiveArrowStart(arrowStart: any) { return SetActiveArrowStart(arrowStart); }); +Scripting.addGlobal(function setActiveArrowEnd(arrowEnd: any) { return SetActiveArrowStart(arrowEnd); }); +Scripting.addGlobal(function setActiveDash(dash: any) { return SetActiveDash(dash); }); diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 720a89334..a5c028730 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -1,5 +1,4 @@ import { action, computed, observable } from "mobx"; -import { ColorState } from 'react-color'; import { Doc, Field, Opt } from "../../fields/Doc"; import { Document } from "../../fields/documentSchemas"; import { InkField, InkData, PointData } from "../../fields/InkField"; @@ -11,80 +10,62 @@ import { undoBatch } from "../util/UndoManager"; export class InkStrokeProperties { static Instance: InkStrokeProperties | undefined; - private _lastFill = "#D0021B"; - private _lastLine = "#D0021B"; - private _lastDash = "2"; - @observable _lock = false; - @observable _controlBtn = false; - @observable _currPoint = -1; + @observable _controlButton = false; + @observable _currentPoint = -1; constructor() { InkStrokeProperties.Instance = this; } + @computed get selectedInk() { + const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); + return inks.length ? inks : undefined; + } + getField(key: string) { return this.selectedInk?.reduce((p, i) => (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>); } - @computed get selectedInk() { - const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); - return inks.length ? inks : undefined; + /** + * 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. + */ + 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 => { + if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currentPoint !== -1)) { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK && doc.width && doc.height) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); + const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); + const ptsXscale = NumCast(doc._width) / (oldXrange.max - oldXrange.min); + const ptsYscale = NumCast(doc._height) / (oldYrange.max - oldYrange.min); + const newPoints = func(doc, ink, ptsXscale, ptsYscale); + if (newPoints) { + const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X)); + const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y)); + doc._width = (newXrange.max - newXrange.min) * ptsXscale; + doc._height = (newYrange.max - newYrange.min) * ptsYscale; + doc.x = (oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale); + doc.y = (oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale); + Doc.GetProto(doc).data = new InkField(newPoints); + appliedFunc = true; + } + } + } + } + })); + return appliedFunc; } - // @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } - // @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } - // @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } - // @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } - // @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } - // @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } - // @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } - // @computed get widthStk() { return this.getField("strokeWidth") || "1"; } - // @computed get markHead() { return this.getField("strokeStartMarker") || ""; } - // @computed get markTail() { return this.getField("strokeEndMarker") || ""; } - // @computed get shapeHgt() { return this.getField("_height"); } - // @computed get shapeWid() { return this.getField("_width"); } - // @computed get shapeXps() { return this.getField("x"); } - // @computed get shapeYps() { return this.getField("y"); } - // @computed get shapeRot() { return this.getField("rotation"); } - // set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } - // set solidFil(value) { this.unFilled = !value; } - // set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } - // set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } - // set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } - // set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } - // set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } - // set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } - // set dashdStk(value) { - // value && (this._lastDash = value) && (this.unStrokd = false); - // this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); - // } - // set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } - // set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } - // set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } - // set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } - // set shapeWid(value) { - // this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - // const oldWidth = NumCast(i.rootDoc._width); - // i.rootDoc._width = Number(value); - // this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); - // }); - // } - // set shapeHgt(value) { - // this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - // const oldHeight = NumCast(i.rootDoc._height); - // i.rootDoc._height = Number(value); - // this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); - // }); - // } - /** * 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 index The index of the new point. * @param control The list of all control points of the ink. */ @undoBatch @@ -114,7 +95,7 @@ export class InkStrokeProperties { for (var i = spNum; i < ink.length; i++) { newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } - this._currPoint = -1; + this._currentPoint = -1; Doc.GetProto(doc).data = new InkField(newPoints); } } @@ -123,56 +104,20 @@ 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 => { - if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currPoint !== -1)) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.width && doc.height) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); - const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); - const ptsXscale = NumCast(doc._width) / (oldXrange.max - oldXrange.min); - const ptsYscale = NumCast(doc._height) / (oldYrange.max - oldYrange.min); - const newPoints = func(doc, ink, ptsXscale, ptsYscale); - if (newPoints) { - const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X)); - const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y)); - doc._width = (newXrange.max - newXrange.min) * ptsXscale; - doc._height = (newYrange.max - newYrange.min) * ptsYscale; - doc.x = (oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale); - doc.y = (oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale); - Doc.GetProto(doc).data = new InkField(newPoints); - appliedFunc = true; - } - } - } - } - })); - 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) => { - var newPoints: { X: number, Y: number }[] = []; - const toRemove = Math.floor(((this._currPoint + 2) / 4)); - for (var i = 0; i < ink.length; i++) { + const newPoints: { X: number, Y: number }[] = []; + const toRemove = Math.floor(((this._currentPoint + 2) / 4)); + for (let i = 0; i < ink.length; i++) { if (Math.floor((i + 2) / 4) !== toRemove && (toRemove !== 0 || i > 3)) { newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } } - this._currPoint = -1; + this._currentPoint = -1; if (newPoints.length < 4) return undefined; if (newPoints.length === 4) { const newerPoints: { X: number, Y: number }[] = []; @@ -183,7 +128,7 @@ export class InkStrokeProperties { return newerPoints; } return newPoints; - }, true); + }, true) /** * Rotates the points of the current ink instance by a certain angle degree. @@ -193,7 +138,7 @@ export class InkStrokeProperties { @undoBatch @action rotateInk = (angle: number) => { - this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { + this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); const centerPoint = { X: (oldXrange.min + oldXrange.max) / 2, Y: (oldYrange.min + oldYrange.max) / 2 }; @@ -234,7 +179,7 @@ export class InkStrokeProperties { } } return newPoints; - }); + }) /** * Rotates the target point about the origin point for a given angle (radians). @@ -247,7 +192,7 @@ export class InkStrokeProperties { const newY = Math.sin(angle) * target.X + Math.cos(angle) * target.Y; target.X = newX + origin.X; target.Y = newY + origin.Y; - return target + return target; } /** @@ -277,54 +222,20 @@ export class InkStrokeProperties { @action moveHandle = (deltaX: number, deltaY: number, handleIndex: number) => this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { - const newPoints: { X: number, Y: number }[] = []; const order = handleIndex % 4; - let newHandlePoint = { X: 0, Y: 0 }; - - for (var i = 0; i < ink.length; i++) { - if (handleIndex === i) { - newHandlePoint = { X: ink[i].X - deltaX / xScale, Y: ink[i].Y - deltaY / yScale }; - newPoints.push({ X: newHandlePoint.X, Y: newHandlePoint.Y }); - } else { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - } - + const oldHandlePoint = ink[handleIndex]; + const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale }; + ink[handleIndex] = newHandlePoint; + + // Rotating opposite handle (first and final control point only have one handle). if (handleIndex !== 1 && handleIndex !== ink.length - 2) { - const oldHandlePoint = ink[handleIndex]; let oppositeHandlePoint = order === 1 ? ink[handleIndex - 3] : ink[handleIndex + 3]; const controlPoint = order === 1 ? ink[handleIndex - 1] : ink[handleIndex + 1]; const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint); oppositeHandlePoint = this.rotatePoint(oppositeHandlePoint, controlPoint, angle); - order === 1 ? newPoints[handleIndex - 3] = oppositeHandlePoint : newPoints[handleIndex + 3] = oppositeHandlePoint; + order === 1 ? ink[handleIndex - 3] = oppositeHandlePoint : ink[handleIndex + 3] = oppositeHandlePoint; } - 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) => { - // const val = String(color.hex); - // this.colorStk = val; - // 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) => { - // const val = String(color.hex); - // this.colorFil = val; - // return true; - // } + return ink; + }) }
\ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx deleted file mode 100644 index b2ef76882..000000000 --- a/src/client/views/InkingStroke.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import { action, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Doc } from "../../fields/Doc"; -import { documentSchema } from "../../fields/documentSchemas"; -import { InkData, InkField, InkTool, ControlPoint, HandlePoint, HandleLine } from "../../fields/InkField"; -import { makeInterface } from "../../fields/Schema"; -import { Cast, StrCast } from "../../fields/Types"; -import { TraceMobx } from "../../fields/util"; -import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils"; -import { CognitiveServices } from "../cognitive_services/CognitiveServices"; -import { InteractionUtils } from "../util/InteractionUtils"; -import { Scripting } from "../util/Scripting"; -import { UndoManager } from "../util/UndoManager"; -import { ContextMenu } from "./ContextMenu"; -import { ViewBoxBaseComponent } from "./DocComponent"; -import "./InkingStroke.scss"; -import { FieldView, FieldViewProps } from "./nodes/FieldView"; -import React = require("react"); -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { InkControl } from "./InkControl"; - -type InkDocument = makeInterface<[typeof documentSchema]>; -const InkDocument = makeInterface(documentSchema); - -@observer -export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) { - private _controlUndo?: UndoManager.Batch; - @observable private _overAddPoint = -1; - - static readonly MaskDim = 50000; - - public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } - - analyzeStrokes = () => { - const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; - CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]); - } - - @action - onPointerEnter = (i: number) => { - this._overAddPoint = i; - } - - @action - onPointerLeave = () => { - this._overAddPoint = -1; - } - - - @action - public static toggleMask = (inkDoc: Doc) => { - inkDoc.isInkMask = !inkDoc.isInkMask; - inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined; - inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined; - inkDoc.color = "#9b9b9bff"; - inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined; - }; - - /** - * Handles the movement of a selected control point when the user clicks and drags. - * @param controlNum The index of the currently selected control point. - */ - @action - onControlDown = (e: React.PointerEvent, controlNum: number): void => { - if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance.moveControl(0, 0, 1); - const controlUndo = UndoManager.StartBatch("DocDecs set radius"); - const screenScale = this.props.ScreenToLocalTransform().Scale; - setupMoveUpEvents(this, e, - (e: PointerEvent, down: number[], delta: number[]) => { - InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlNum); - return false; - }, - () => controlUndo?.end(), emptyFunction); - } - } - - /** - * Handles the movement of a selected handle point when the user clicks and drags. - * @param controlNum The index of the currently selected handle point. - */ - onHandleDown = (e: React.PointerEvent, handleNum: number): void => { - if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance.moveControl(0, 0, 1); - const controlUndo = UndoManager.StartBatch("DocDecs set radius"); - const screenScale = this.props.ScreenToLocalTransform().Scale; - setupMoveUpEvents(this, e, - (e: PointerEvent, down: number[], delta: number[]) => { - InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleNum); - return false; - }, - () => controlUndo?.end(), emptyFunction); - } - } - - /** - * 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) => - doubleTap && InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true))); - } - } - - render() { - TraceMobx(); - const formatInstance = InkStrokeProperties.Instance; - if (!formatInstance) return (null); - const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; - const strokeWidth = Number(this.layoutDoc.strokeWidth); - const xs = data.map(p => p.X); - const ys = data.map(p => p.Y); - const lineTop = Math.min(...ys); - const lineBot = Math.max(...ys); - const lineLft = Math.min(...xs); - const lineRgt = Math.max(...xs); - const left = lineLft - strokeWidth / 2; - const top = lineTop - strokeWidth / 2; - const right = lineRgt + strokeWidth / 2; - const bottom = lineBot + strokeWidth / 2; - const width = Math.max(1, right - left); - const height = Math.max(1, bottom - top); - const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth); - const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth); - const strokeColor = StrCast(this.layoutDoc.color, ""); - const dotsize = Math.max(width * scaleX, height * scaleY) / 40; - - // 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); - - const selectedLine = InteractionUtils.CreatePolyline(data, lineLft - strokeWidth * 3, lineTop - strokeWidth * 3, "#1F85DE", strokeWidth / 6, strokeWidth / 6, - 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); - - 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), - StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false); - - const controlPoints: ControlPoint[] = []; - const handlePoints: HandlePoint[] = []; - const handleLine: HandleLine[] = []; - // check the purpose of this conditional - if (data.length >= 4) { - - // create separate functions for these - for (var i = 0; i <= data.length - 4; i += 4) { - controlPoints.push({ X: data[i].X, Y: data[i].Y, I: i }); - controlPoints.push({ X: data[i + 3].X, Y: data[i + 3].Y, I: i + 3 }); - handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 }); - handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 }); - } - - // first (single) handle line - handleLine.push({ X1: data[0].X, Y1: data[0].Y, X2: data[1].X, Y2: data[1].Y, dot1: 0, dot2: 0 }); - for (var i = 2; i < data.length - 4; i += 4) { - - handleLine.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 3].X, Y2: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); - - } - // last (single) handle line - handleLine.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 }); - - for (var i = 0; i <= data.length - 4; i += 4) { - handlePoints.push({ X: data[i + 1].X, Y: data[i + 1].Y, I: i + 1, dot1: i, dot2: i === 0 ? i : i - 1 }); - handlePoints.push({ X: data[i + 2].X, Y: data[i + 2].Y, I: i + 2, dot1: i + 3, dot2: i === data.length ? i + 3 : i + 4 }); - } - } - - // 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={this._overAddPoint === i ? "#1F85DE" : "invisible"} strokeWidth={dotsize / 4} fill={this._overAddPoint === i ? "#1F85DE" : "invisible"} - onPointerDown={(e) => { formatInstance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} onMouseEnter={() => this.onPointerEnter(i)} onMouseLeave={this.onPointerLeave} pointerEvents="all" cursor="all-scroll" - /> - </svg>); - // Blue circles that allow the user to edit the curvature of the line using the selected control 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 / 2} strokeWidth={0} fill="#1F85DE" - onPointerDown={(e) => this.onHandleDown(e, pts.I)} pointerEvents="all" cursor="default" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> - </svg>); - // Control points of the ink (blue outlined squares) that are made visible to user when editing its format. - const controls = controlPoints.map((pts, i) => <InkControl control={pts} left={left} top={top} scaleX={scaleX} scaleY={scaleY} strokeWidth={strokeWidth} ScreenToLocalTransform={this.props.ScreenToLocalTransform} /> ); - // Set of two blue 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} - x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="#1F85DE" strokeWidth={dotsize / 10} - display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> - </svg>); - - return ( - <svg className="inkingStroke" - style={{ - pointerEvents: this.props.Document.isInkMask && this.props.layerProvider?.(this.props.Document) !== false ? "all" : "none", - transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined, - mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset", - overflow: "visible", - }} - onPointerDown={this.onPointerDown} - onContextMenu={() => { - const cm = ContextMenu.Instance; - if (cm) { - !Doc.UserDoc().noviceMode && cm.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" }); - cm.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" }); - cm.addItem({ description: "Edit Points", event: action(() => formatInstance._controlBtn = !formatInstance._controlBtn), icon: "paint-brush" }); - //cm.addItem({ description: "Format Shape...", event: this.formatShape, icon: "paint-brush" }); - } - }} - ><defs> - </defs> - {hpoints} - {points} - {formatInstance._controlBtn && this.props.isSelected() ? addpoints : ""} - {formatInstance._controlBtn && this.props.isSelected() ? handleLines : ""} - {formatInstance._controlBtn && this.props.isSelected() ? handles : ""} - {this.props.isSelected() ? selectedLine : ""} - {formatInstance._controlBtn && this.props.isSelected() ? controls : ""} - </svg> - ); - } -} - - -export function SetActiveInkWidth(width: string): void { !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); } -export function SetActiveBezierApprox(bezier: string): void { ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? "" : bezier); } -export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); } -export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); } -export function SetActiveArrowStart(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); } -export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); } -export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); } -export function ActiveInkPen(): Doc { return Doc.UserDoc(); } -export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); } -export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); } -export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); } -export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); } -export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); } -export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); } -export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } -Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { - CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None; - SetActiveInkWidth(width); - SetActiveInkColor(color); - SetActiveFillColor(fill); - SetActiveArrowStart(arrowStart); - SetActiveArrowEnd(arrowEnd); - SetActiveDash(dash); -}); -Scripting.addGlobal(function activateEraser(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Eraser : InkTool.None; }); -Scripting.addGlobal(function activateStamp(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Stamp : InkTool.None; }); -Scripting.addGlobal(function deactivateInk() { return CurrentUserUtils.SelectedTool = InkTool.None; }); -Scripting.addGlobal(function setInkWidth(width: any) { return SetActiveInkWidth(width); }); -Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); }); -Scripting.addGlobal(function setFillColor(fill: any) { return SetActiveFillColor(fill); }); -Scripting.addGlobal(function setActiveArrowStart(arrowStart: any) { return SetActiveArrowStart(arrowStart); }); -Scripting.addGlobal(function setActiveArrowEnd(arrowEnd: any) { return SetActiveArrowStart(arrowEnd); }); -Scripting.addGlobal(function setActiveDash(dash: any) { return SetActiveDash(dash); }); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 5c41a96d0..920244463 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -11,7 +11,7 @@ import { DocumentType } from '../documents/DocumentTypes'; import { SelectionManager } from '../util/SelectionManager'; import { undoBatch } from '../util/UndoManager'; import { CollectionViewType } from './collections/CollectionView'; -import { InkingStroke } from './InkingStroke'; +import { InkStroke } from './InkStroke'; import { DocumentView } from './nodes/DocumentView'; import './PropertiesButtons.scss'; import React = require("react"); @@ -56,7 +56,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { return this.propertyToggleBtn("Dictate", "_showAudio", on => `${on ? "Hide" : "Show"} dictation/recording controls`, on => "microphone"); } @computed get maskButton() { - return this.propertyToggleBtn("Mask", "isInkMask", on => on ? "Make plain ink" : "Make highlight mask", on => "paint-brush", (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)); + return this.propertyToggleBtn("Mask", "isInkMask", on => on ? "Make plain ink" : "Make highlight mask", on => "paint-brush", (dv, doc) => InkStroke.toggleMask(dv?.layoutDoc || doc)); } @computed get clustersButton() { return this.propertyToggleBtn("Clusters", "_useClusters", on => `${on ? "Hide" : "Show"} clusters`, on => "braille"); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index d09d949ff..4df3e4f00 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -86,7 +86,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { @observable openSlideOptions: boolean = false; @observable inOptions: boolean = false; - @observable _controlBtn: boolean = false; + @observable _controlButton: boolean = false; @observable _lock: boolean = false; componentDidMount() { @@ -540,7 +540,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { const formatInstance = InkStrokeProperties.Instance; return !formatInstance ? (null) : <div className="inking-button"> <Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}> - <div className="inking-button-points" onPointerDown={action(() => formatInstance._controlBtn = !formatInstance._controlBtn)} style={{ backgroundColor: formatInstance._controlBtn ? "black" : "" }}> + <div className="inking-button-points" onPointerDown={action(() => formatInstance._controlButton = !formatInstance._controlButton)} style={{ backgroundColor: formatInstance._controlButton ? "black" : "" }}> <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" /> </div> </Tooltip> diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 6e6fabd0d..65061d260 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -25,7 +25,7 @@ import { undoBatch } from "../../util/UndoManager"; import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { EditableView } from "../EditableView"; import { GestureOverlay } from "../GestureOverlay"; -import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke"; +import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkStroke"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index accb80c5a..9e81a6cda 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -32,7 +32,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss" import { Timeline } from "../../animationtimeline/Timeline"; import { ContextMenu } from "../../ContextMenu"; import { DocumentDecorations } from "../../DocumentDecorations"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkStroke"; import { LightboxView } from "../../LightboxView"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 092823603..744a9c5de 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -13,7 +13,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { DocComponent } from "../DocComponent"; -import { InkingStroke } from "../InkingStroke"; +import { InkStroke } from "../InkStroke"; import { StyleProp } from "../StyleProvider"; import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; @@ -38,7 +38,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive - get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; } + get maskCentering() { return this.props.Document.isInkMask ? InkStroke.MaskDim / 2 : 0; } get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; } get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); } get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); } diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 8da5cd1b1..2d466763b 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -12,7 +12,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; import { ViewBoxBaseComponent } from "../DocComponent"; -import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke"; +import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from "../InkStroke"; import "./ColorBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; import { RichTextMenu } from "./formattedText/RichTextMenu"; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f0a54e4ac..5da3a52d5 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -10,7 +10,7 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionView } from "../collections/CollectionView"; -import { InkingStroke } from "../InkingStroke"; +import { InkStroke } from "../InkStroke"; import { PresElementBox } from "../presentationview/PresElementBox"; import { SearchBox } from "../search/SearchBox"; import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo"; @@ -224,7 +224,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox, - ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, + ColorBox, DashWebRTCVideo, LinkAnchorBox, InkStroke, LinkBox, ScriptingBox, ScreenshotBox, HTMLtag, ComparisonBox }} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b861669f8..924b0e7d3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -34,7 +34,7 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; -import { InkingStroke } from "../InkingStroke"; +import { InkStroke } from "../InkStroke"; import { LightboxView } from "../LightboxView"; import { StyleLayers, StyleProp } from "../StyleProvider"; import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; @@ -1138,8 +1138,8 @@ export class DocumentView extends React.Component<DocumentViewProps> { render() { TraceMobx(); - const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); - const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); + const xshift = () => (this.props.Document.isInkMask ? InkStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); + const yshift = () => (this.props.Document.isInkMask ? InkStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); return (<div className="contentFittingDocumentView"> {!this.props.Document || !this.props.PanelWidth() ? (null) : ( <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef} |