diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/InteractionUtils.tsx | 2 | ||||
-rw-r--r-- | src/client/views/InkControlPtHandles.tsx | 95 | ||||
-rw-r--r-- | src/client/views/InkTangentHandles.tsx | 55 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 1 |
4 files changed, 82 insertions, 71 deletions
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 633876683..d43ebd692 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -99,7 +99,7 @@ export namespace InteractionUtils { const toScr = (p: { X: number, Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `; const strpts = bezier ? - pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : "M" + toScr(pt) + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") : + pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : (i === 0 ? "M" + toScr(pt) : "") + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") : pts.reduce((acc: string, pt) => acc + `${toScr(pt)} `, ""); const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined; diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 898c3bf26..f80aca268 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -10,6 +10,7 @@ import { Transform } from "../util/Transform"; import { UndoManager } from "../util/UndoManager"; import { Colors } from "./global/globalEnums"; import { InkStrokeProperties } from "./InkStrokeProperties"; +import { List } from "../../fields/List"; export interface InkControlProps { inkDoc: Doc; @@ -24,6 +25,8 @@ export interface InkControlProps { export class InkControlPtHandles extends React.Component<InkControlProps> { @observable private _overControl = -1; + + @observable controlUndo: UndoManager.Batch | undefined; /** * Handles the movement of a selected control point when the user clicks and drags. * @param controlIndex The index of the currently selected control point. @@ -31,34 +34,40 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { @action onControlDown = (e: React.PointerEvent, controlIndex: number): void => { if (InkStrokeProperties.Instance) { - var controlUndo: UndoManager.Batch | undefined; const screenScale = this.props.ScreenToLocalTransform().Scale; const order = controlIndex % 4; const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this.props.inkCtrlPoints.length) % this.props.inkCtrlPoints.length; const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length; const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); setupMoveUpEvents(this, e, - (e: PointerEvent, down: number[], delta: number[]) => { - if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); + action((e: PointerEvent, down: number[], delta: number[]) => { + if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; - }, - () => { - controlUndo?.end(); + }), + action(() => { + this.controlUndo?.end(); + this.controlUndo = undefined; UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); - }, + }), action((e: PointerEvent, doubleTap: boolean | undefined) => { const equivIndex = controlIndex === 0 ? this.props.inkCtrlPoints.length - 1 : controlIndex === this.props.inkCtrlPoints.length - 1 ? 0 : controlIndex; - if (doubleTap) { - if (brokenIndices?.includes(equivIndex)) { - if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); - } - if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { - if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + if (doubleTap || e.button === 2) { + if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) { + if (brokenIndices) brokenIndices.push(controlIndex); + else this.props.inkDoc.brokenInkIndices = new List<number>([controlIndex]); + } else { + if (brokenIndices?.includes(equivIndex)) { + if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); + InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); + } + if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { + if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } } - controlUndo?.end(); + this.controlUndo?.end(); + this.controlUndo = undefined; } })); } @@ -92,9 +101,6 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { } render() { - const formatInstance = InkStrokeProperties.Instance; - if (!formatInstance) return (null); - // Accessing the current ink's data and extracting all control points. const scrData = this.props.screenCtrlPoints; const sreenCtrlPoints: ControlPoint[] = []; @@ -111,41 +117,46 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { } const screenSpaceLineWidth = this.props.screenSpaceLineWidth; - const rectHdlSize = (i: number) => this._overControl === i ? screenSpaceLineWidth * 6 : screenSpaceLineWidth * 4; - const nearestScreenPt = this.props.nearestScreenPt(); + const TagType = (broken?: boolean) => broken ? "rect" : "circle"; + const hdl = (control: { X: number, Y: number, I: number }, scale: number, color: string) => { + const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I); + const Tag = TagType(broken) as keyof JSX.IntrinsicElements; + return <circle key={control.I.toString() + scale} + x={control.X - screenSpaceLineWidth * 3 * scale} + y={control.Y - screenSpaceLineWidth * 3 * scale} + cx={control.X} + cy={control.Y} + r={screenSpaceLineWidth * 2 * scale} + opacity={this.controlUndo ? 0.15 : 1} + height={screenSpaceLineWidth * 6 * scale} + width={screenSpaceLineWidth * 6 * scale} + strokeWidth={screenSpaceLineWidth / 2} + stroke={Colors.MEDIUM_BLUE} + fill={broken ? Colors.MEDIUM_BLUE : color} + onPointerDown={(e: any) => { + this.changeCurrPoint(control.I); + this.onControlDown(e, control.I); + }} + onMouseEnter={() => this.onEnterControl(control.I)} + onMouseLeave={this.onLeaveControl} + pointerEvents="all" + cursor="default" + />; + } return (<svg> {!nearestScreenPt ? (null) : <circle key={"npt"} cx={nearestScreenPt.X} cy={nearestScreenPt.Y} - r={screenSpaceLineWidth * 4} + r={screenSpaceLineWidth * 2} fill={"#00007777"} stroke={"#00007777"} strokeWidth={0} pointerEvents="none" - cursor="all-scroll" /> } - {sreenCtrlPoints.map((control, i) => - <rect key={i} - x={control.X - rectHdlSize(i) / 2} - y={control.Y - rectHdlSize(i) / 2} - height={rectHdlSize(i)} - width={rectHdlSize(i)} - strokeWidth={screenSpaceLineWidth / 2} - stroke={Colors.MEDIUM_BLUE} - fill={formatInstance?._currentPoint === control.I ? Colors.MEDIUM_BLUE : Colors.WHITE} - onPointerDown={e => { - this.changeCurrPoint(control.I); - this.onControlDown(e, control.I); - }} - onMouseEnter={e => this.onEnterControl(i)} - onMouseLeave={this.onLeaveControl} - pointerEvents="all" - cursor="default" - /> - )} + {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))} </svg> ); } diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index 759e48134..4f1a406c2 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -68,62 +68,61 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> { // Accessing the current ink's data and extracting all handle points and handle lines. const data = this.props.screenCtrlPoints; - const handlePoints: HandlePoint[] = []; - const handleLines: HandleLine[] = []; + const tangentHandles: HandlePoint[] = []; + const tangentLines: HandleLine[] = []; const closed = data.lastElement().X === data[0].X && data.lastElement().Y === data[0].Y; if (data.length >= 4) { for (let i = 0; i <= data.length - 4; i += 4) { - handlePoints.push({ ...data[i + 1], I: i + 1, dot1: i, dot2: i === 0 ? (closed ? data.length - 1 : i) : i - 1 }); - handlePoints.push({ ...data[i + 2], I: i + 2, dot1: i + 3, dot2: i === data.length ? (closed ? (i + 4) % data.length : i + 3) : i + 4 }); + tangentHandles.push({ ...data[i + 1], I: i + 1, dot1: i, dot2: i === 0 ? (closed ? data.length - 1 : i) : i - 1 }); + tangentHandles.push({ ...data[i + 2], I: i + 2, dot1: i + 3, dot2: i === data.length ? (closed ? (i + 4) % data.length : i + 3) : i + 4 }); } // Adding first and last (single) handle lines. if (closed) { - handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: data.length - 1 }); + tangentLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: data.length - 1 }); } else { - handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: 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, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 }); + tangentLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 }); + tangentLines.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, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 }); } for (let i = 2; i < data.length - 4; i += 4) { - handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); + tangentLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); } } const screenSpaceLineWidth = this.props.screenSpaceLineWidth; return ( <> - {handlePoints.map((pts, i) => + {tangentHandles.map((pts, i) => <svg height="10" width="10" key={`hdl${i}`}> <circle cx={pts.X} cy={pts.Y} r={screenSpaceLineWidth * 2} - strokeWidth={0} - fill={Colors.MEDIUM_BLUE} + fill={"transparent"} + strokeWidth={1} + stroke={Colors.MEDIUM_BLUE} + strokeDasharray={"1 1"} 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} - y1={pts.Y1} - x2={pts.X2} - y2={pts.Y2} - stroke={Colors.MEDIUM_BLUE} - strokeWidth={screenSpaceLineWidth} - display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> + {tangentLines.map((pts, i) => { + const tangentLine = (x1: number, y1: number, x2: number, y2: number) => <line - x1={pts.X2} - y1={pts.Y2} - x2={pts.X3} - y2={pts.Y3} + x1={x1} + y1={y1} + x2={x2} + y2={y2} stroke={Colors.MEDIUM_BLUE} - strokeWidth={screenSpaceLineWidth} - display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> - </svg>)} + strokeDasharray={"1 1"} + strokeWidth={1} + display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />; + return <svg height="100" width="100" key={`line${i}`}> + {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)} + {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)} + </svg>; + })} </> ); } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 867677005..4cd7f7698 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -210,6 +210,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume 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", + cursor: this.props.isSelected() ? "default" : undefined }} onPointerLeave={action(e => this._nearestScrPt = undefined)} onPointerMove={this.props.isSelected() ? this.onPointerMove : undefined} |