aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/InkControlPtHandles.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/InkControlPtHandles.tsx')
-rw-r--r--src/client/views/InkControlPtHandles.tsx99
1 files changed, 77 insertions, 22 deletions
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index f24dab949..24f796105 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -6,13 +6,14 @@ import { ControlPoint, InkData, PointData, InkField } from "../../fields/InkFiel
import { List } from "../../fields/List";
import { listSpec } from "../../fields/Schema";
import { Cast, NumCast } from "../../fields/Types";
-import { setupMoveUpEvents } from "../../Utils";
+import { setupMoveUpEvents, returnFalse } from "../../Utils";
import { Transform } from "../util/Transform";
import { UndoManager } from "../util/UndoManager";
import { Colors } from "./global/globalEnums";
import { InkingStroke } from "./InkingStroke";
import { InkStrokeProperties } from "./InkStrokeProperties";
import { DocumentView } from "./nodes/DocumentView";
+import { SelectionManager } from "../util/SelectionManager";
export interface InkControlProps {
inkDoc: Doc;
@@ -44,23 +45,24 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
@action
onControlDown = (e: React.PointerEvent, controlIndex: number): void => {
const ptFromScreen = this.props.inkView.ComponentView?.ptFromScreen;
- if (InkStrokeProperties.Instance && ptFromScreen) {
+ if (ptFromScreen) {
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"));
- const wasSelected = InkStrokeProperties.Instance?._currentPoint === controlIndex;
+ const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
+ const origInk = this.props.inkCtrlPoints.slice();
setupMoveUpEvents(this, e,
action((e: PointerEvent, down: number[], delta: number[]) => {
if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] });
const inkMoveStart = ptFromScreen({ X: 0, Y: 0 });
- InkStrokeProperties.Instance?.moveControlPtHandle(this.props.inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
+ InkStrokeProperties.Instance.moveControlPtHandle(this.props.inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk);
return false;
}),
action(() => {
if (this.controlUndo) {
- InkStrokeProperties.Instance?.snapControl(this.props.inkView, controlIndex);
+ InkStrokeProperties.Instance.snapControl(this.props.inkView, controlIndex);
}
this.controlUndo?.end();
this.controlUndo = undefined;
@@ -75,11 +77,11 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
} else {
if (brokenIndices?.includes(equivIndex)) {
if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth");
- InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, equivIndex, handleIndexA, handleIndexB);
+ InkStrokeProperties.Instance.snapHandleTangent(this.props.inkView, equivIndex, handleIndexA, handleIndexB);
}
if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) {
if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth");
- InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, controlIndex, handleIndexA, handleIndexB);
+ InkStrokeProperties.Instance.snapHandleTangent(this.props.inkView, controlIndex, handleIndexA, handleIndexB);
}
}
this.controlUndo?.end();
@@ -102,7 +104,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
@action
onDelete = (e: KeyboardEvent) => {
if (["-", "Backspace", "Delete"].includes(e.key)) {
- InkStrokeProperties.Instance?.deletePoints(this.props.inkView);
+ InkStrokeProperties.Instance.deletePoints(this.props.inkView, e.shiftKey);
e.stopPropagation();
}
}
@@ -111,11 +113,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
* Changes the current selected control point.
*/
@action
- changeCurrPoint = (i: number) => {
- if (InkStrokeProperties.Instance) {
- InkStrokeProperties.Instance._currentPoint = i;
- }
- }
+ changeCurrPoint = (i: number) => InkStrokeProperties.Instance._currentPoint = i
render() {
// Accessing the current ink's data and extracting all control points.
@@ -133,7 +131,6 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
inkCtrlPts.push({ ...inkData[i + 3], I: i + 3 });
}
- const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
const closed = InkingStroke.IsClosed(inkData);
const nearestScreenPt = this.props.nearestScreenPt();
const TagType = (broken?: boolean) => broken ? "rect" : "circle";
@@ -141,18 +138,18 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I);
const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements;
return <Tag key={control.I.toString() + scale}
- x={control.X - screenSpaceLineWidth * 2 * scale}
- y={control.Y - screenSpaceLineWidth * 2 * scale}
+ x={control.X - this.props.screenSpaceLineWidth * 2 * scale}
+ y={control.Y - this.props.screenSpaceLineWidth * 2 * scale}
cx={control.X}
cy={control.Y}
- r={screenSpaceLineWidth * 2 * scale}
+ r={this.props.screenSpaceLineWidth * 2 * scale}
opacity={this.controlUndo ? 0.15 : 1}
- height={screenSpaceLineWidth * 4 * scale}
- width={screenSpaceLineWidth * 4 * scale}
- strokeWidth={screenSpaceLineWidth / 2}
+ height={this.props.screenSpaceLineWidth * 4 * scale}
+ width={this.props.screenSpaceLineWidth * 4 * scale}
+ strokeWidth={this.props.screenSpaceLineWidth / 2}
stroke={Colors.MEDIUM_BLUE}
fill={broken ? Colors.MEDIUM_BLUE : color}
- onPointerDown={(e: any) => this.onControlDown(e, control.I)}
+ onPointerDown={(e: React.PointerEvent) => this.onControlDown(e, control.I)}
onMouseEnter={() => this.onEnterControl(control.I)}
onMouseLeave={this.onLeaveControl}
pointerEvents="all"
@@ -164,7 +161,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
<circle key={"npt"}
cx={nearestScreenPt.X}
cy={nearestScreenPt.Y}
- r={screenSpaceLineWidth * 2}
+ r={this.props.screenSpaceLineWidth * 2}
fill={"#00007777"}
stroke={"#00007777"}
strokeWidth={0}
@@ -175,4 +172,62 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
</svg>
);
}
+}
+
+
+export interface InkEndProps {
+ inkDoc: Doc;
+ inkView: DocumentView;
+ screenSpaceLineWidth: number;
+ startPt: PointData;
+ endPt: PointData;
+}
+@observer
+export class InkEndPtHandles extends React.Component<InkEndProps> {
+ @observable controlUndo: UndoManager.Batch | undefined;
+ @observable _overStart: boolean = false;
+ @observable _overEnd: boolean = false;
+
+ @action
+ dragRotate = (e: React.PointerEvent, p1: () => { X: number, Y: number }, p2: () => { X: number, Y: number }) => {
+ setupMoveUpEvents(this, e, (e) => {
+ if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("stretch ink");
+ // compute stretch factor by finding scaling along axis between start and end points
+ const v1 = { X: p1().X - p2().X, Y: p1().Y - p2().Y };
+ const v2 = { X: e.clientX - p2().X, Y: e.clientY - p2().Y };
+ const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y);
+ const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y);
+ const scaling = v2len / v1len;
+ const v1n = { X: v1.X / v1len, Y: v1.Y / v1len };
+ const v2n = { X: v2.X / v2len, Y: v2.Y / v2len };
+ const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y);
+ InkStrokeProperties.Instance.stretchInk(SelectionManager.Views(), scaling, p2(), v1n, e.shiftKey);
+ InkStrokeProperties.Instance.rotateInk(SelectionManager.Views(), angle, p2());
+ return false;
+ }, action(() => {
+ this.controlUndo?.end();
+ this.controlUndo = undefined;
+ UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
+ }), returnFalse);
+ }
+
+ render() {
+ const hdl = (key: string, pt: PointData, dragFunc: (e: React.PointerEvent) => void) => <circle key={key}
+ cx={pt.X}
+ cy={pt.Y}
+ r={this.props.screenSpaceLineWidth * 2}
+ fill={this._overStart ? "#aaaaaa" : "#99999977"}
+ stroke={"#00007777"}
+ strokeWidth={0}
+ onPointerLeave={action(() => this._overStart = false)}
+ onPointerEnter={action(() => this._overStart = true)}
+ onPointerDown={dragFunc}
+ pointerEvents="all"
+ />;
+ return (<svg>
+ {hdl("start", this.props.startPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.startPt, () => this.props.endPt))}
+ {hdl("end", this.props.endPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.endPt, () => this.props.startPt))}
+ </svg>
+ );
+ }
} \ No newline at end of file