From 16c2a900e9116474caa67e4d5de15e2c51477292 Mon Sep 17 00:00:00 2001 From: vkalev Date: Tue, 27 Jul 2021 18:54:53 -0400 Subject: changing format of controls/handles + adding color enums --- src/client/views/InkControls.tsx | 73 ++++++++++++++++++--------------- src/client/views/InkHandles.tsx | 13 +++--- src/client/views/InkStrokeProperties.ts | 38 +++++++++-------- src/client/views/InkingStroke.tsx | 40 +++++++----------- 4 files changed, 84 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx index 23f22c774..090af10cc 100644 --- a/src/client/views/InkControls.tsx +++ b/src/client/views/InkControls.tsx @@ -6,8 +6,13 @@ import { setupMoveUpEvents, emptyFunction } from "../../Utils"; import { UndoManager } from "../util/UndoManager"; import { ControlPoint, InkData, PointData } from "../../fields/InkField"; import { Transform } from "../util/Transform"; +import { Colors } from "./global/globalEnums"; +import { Doc } from "../../fields/Doc"; +import { listSpec } from "../../fields/Schema"; +import { Cast } from "../../fields/Types"; export interface InkControlProps { + inkDoc: Doc; data: InkData; addedPoints: PointData[]; format: number[]; @@ -30,19 +35,22 @@ export class InkControls extends React.Component { const controlUndo = UndoManager.StartBatch("DocDecs set radius"); const screenScale = this.props.ScreenToLocalTransform().Scale; const order = controlIndex % 4; - const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; + const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; const handleIndexB = order === 2 ? controlIndex + 2 : controlIndex + 1; + const brokenIndices = Cast(this.props.inkDoc, listSpec("number")); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; }, - () => controlUndo?.end(), - emptyFunction); - // action((e: PointerEvent, doubleTap: boolean | undefined) => - // { if (doubleTap && InkStrokeProperties.Instance?._brokenIndices.includes(controlIndex)) { - // InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); - // }})); + () => controlUndo?.end(), + brokenIndices ? + action((e: PointerEvent, doubleTap: boolean | undefined) => { + if (doubleTap && brokenIndices.includes(controlIndex)) { + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } + }) + : emptyFunction); } } @@ -75,7 +83,7 @@ export class InkControls extends React.Component { @action onLeaveControl = () => { this._overControl = -1; }; @action onEnterAddPoint = (i: number) => { this._overAddPoint = i; }; @action onLeaveAddPoint = () => { this._overAddPoint = -1; }; - + render() { const formatInstance = InkStrokeProperties.Instance; if (!formatInstance) return (null); @@ -90,41 +98,42 @@ export class InkControls extends React.Component { } } const addedPoints = this.props.addedPoints; - const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + const [left, top, scaleX, scaleY, strokeWidth] = this.props.format; return ( <> {addedPoints.map((pts, i) => - { formatInstance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }} - onMouseEnter={() => this.onEnterAddPoint(i)} - onMouseLeave={this.onLeaveAddPoint} - pointerEvents="all" + { formatInstance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }} + onMouseEnter={() => this.onEnterAddPoint(i)} + onMouseLeave={this.onLeaveAddPoint} + pointerEvents="all" cursor="all-scroll" /> )} {controlPoints.map((control, i) => - { - this.changeCurrPoint(control.I); - this.onControlDown(e, control.I); }} - onMouseEnter={() => this.onEnterControl(i)} - onMouseLeave={this.onLeaveControl} - pointerEvents="all" + { + this.changeCurrPoint(control.I); + this.onControlDown(e, control.I); + }} + onMouseEnter={() => this.onEnterControl(i)} + onMouseLeave={this.onLeaveControl} + pointerEvents="all" cursor="default" /> diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx index 28b6dd820..6e890e184 100644 --- a/src/client/views/InkHandles.tsx +++ b/src/client/views/InkHandles.tsx @@ -10,6 +10,7 @@ import { Doc } from "../../fields/Doc"; import { listSpec } from "../../fields/Schema"; import { List } from "../../fields/List"; import { Cast } from "../../fields/Types"; +import { Colors } from "./global/globalEnums"; export interface InkHandlesProps { inkDoc: Doc; @@ -80,7 +81,7 @@ export class InkHandles extends React.Component { 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 }); } } - const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + const [left, top, scaleX, scaleY, strokeWidth] = this.props.format; return ( <> @@ -91,7 +92,7 @@ export class InkHandles extends React.Component { cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} strokeWidth={0} - fill="#1F85DE" + fill={Colors.MEDIUM_BLUE} onPointerDown={(e) => this.onHandleDown(e, pts.I)} pointerEvents="all" cursor="default" @@ -104,16 +105,16 @@ export class InkHandles extends React.Component { 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} + stroke={Colors.MEDIUM_BLUE} + strokeWidth={strokeWidth / 4} display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> )} diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 1a3585f3e..de8cf80bd 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -35,7 +35,7 @@ export class InkStrokeProperties { * @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) => { + 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)) { @@ -91,16 +91,16 @@ export class InkStrokeProperties { }); } } - if (end === 0) end = points.length-1; + if (end === 0) end = points.length - 1; // Index of new control point with regards to the ink data. const newIndex = Math.floor(counter / 2) * 4 + 2; // Creating new ink data with the new control point and handle points inputted. for (let i = 0; i < ink.length; i++) { - if (i === newIndex) { - const [handleA, handleB] = this.getNewHandlePoints(points.slice(start, index+1), points.slice(index, end), newControl); + if (i === newIndex) { + const [handleA, handleB] = this.getNewHandlePoints(points.slice(start, index + 1), points.slice(index, end), newControl); newPoints.push(handleA, newControl, newControl, handleB); // Adjusting the magnitude of the left handle line of the right neighboring control point. - const [rightControl, rightHandle] = [points[end], ink[i]]; + const [rightControl, rightHandle] = [points[end], ink[i]]; const scaledVector = this.getScaledHandlePoint(false, start, end, index, rightControl, rightHandle); rightHandle && newPoints.push({ X: rightControl.X - scaledVector.X, Y: rightControl.Y - scaledVector.Y }); } else if (i === newIndex - 1) { @@ -111,14 +111,14 @@ export class InkStrokeProperties { } else { ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } - + } let brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); // Updating the indices of the control points whose handle tangency has been broken. if (brokenIndices) { - brokenIndices = new List(brokenIndices.map((control) => { + brokenIndices = new List(brokenIndices.map((control) => { if (control >= newIndex) { - return control + 4; + return control + 4; } else { return control; } @@ -158,7 +158,7 @@ export class InkStrokeProperties { getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => { const [m, n] = [C.length, D.length]; let handleSizeA = Math.sqrt((Math.pow(newControl.X - C[0].X, 2)) + (Math.pow(newControl.Y - C[0].Y, 2))); - let handleSizeB = Math.sqrt((Math.pow(D[n-1].X - newControl.X, 2)) + (Math.pow(D[n-1].Y - newControl.Y, 2))); + let handleSizeB = Math.sqrt((Math.pow(D[n - 1].X - newControl.X, 2)) + (Math.pow(D[n - 1].Y - newControl.Y, 2))); // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines. // (Ensures that the new point added doesn't augment the inital shape of the curve much). if (handleSizeA < 75 && handleSizeB < 75) { @@ -173,7 +173,7 @@ export class InkStrokeProperties { handleSizeB *= 2; } // Finding the last leg of the derivative curve of C. - const dC = { X: (handleSizeA / n) * (C[m-1].X - C[m-2].X), Y: (handleSizeA / n) * (C[m-1].Y - C[m-2].Y) }; + const dC = { X: (handleSizeA / n) * (C[m - 1].X - C[m - 2].X), Y: (handleSizeA / n) * (C[m - 1].Y - C[m - 2].Y) }; // Finding the first leg of the derivative curve of D. const dD = { X: (handleSizeB / m) * (D[1].X - D[0].X), Y: (handleSizeB / m) * (D[1].Y - D[0].Y) }; const handleA = { X: newControl.X - dC.X, Y: newControl.Y - dC.Y }; @@ -259,13 +259,17 @@ export class InkStrokeProperties { snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction((doc: Doc, ink: InkData) => { - // doc.brokenIndices.splice(this._brokenIndices.indexOf(controlIndex), 1); - const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]]; - const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI); - const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint); - const newHandleB = this.rotatePoint(handleB, controlPoint, angleDifference); - ink[handleIndexB] = newHandleB; - return ink; + const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); + if (brokenIndices) { + brokenIndices.splice(brokenIndices.indexOf(controlIndex), 1); + doc.brokenInkIndices = brokenIndices; + const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]]; + const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI); + const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint); + const newHandleB = this.rotatePoint(handleB, controlPoint, angleDifference); + ink[handleIndexB] = newHandleB; + return ink; + } }); } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index bd71aaf19..a8f32118c 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -19,6 +19,7 @@ import { InkStrokeProperties } from "./InkStrokeProperties"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InkControls } from "./InkControls"; import { InkHandles } from "./InkHandles"; +import { Colors } from "./global/globalEnums"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -66,6 +67,9 @@ export class InkingStroke extends ViewBoxBaseComponent 1 && lineRight - lineLeft > 1, - false); + 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); // Thin blue line indicating that the current ink stroke is selected. - 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); + const selectedLine = InteractionUtils.CreatePolyline(data, left - strokeWidth / 3, top - strokeWidth / 3, Colors.MEDIUM_BLUE, 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 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); // Set of points rendered upon the ink that can be added if a user clicks on one. - 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); + 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 ( : ""} -- cgit v1.2.3-70-g09d2 From ea6000690022d43b6bc8e1a546d28729a59faf7b Mon Sep 17 00:00:00 2001 From: vkalev Date: Wed, 28 Jul 2021 13:14:10 -0400 Subject: snapping broken tangency added --- src/client/views/InkControls.tsx | 14 ++++++-------- src/client/views/InkHandles.tsx | 1 + src/client/views/InkStrokeProperties.ts | 18 +++++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx index 090af10cc..6213a4075 100644 --- a/src/client/views/InkControls.tsx +++ b/src/client/views/InkControls.tsx @@ -37,20 +37,18 @@ export class InkControls extends React.Component { const order = controlIndex % 4; const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; const handleIndexB = order === 2 ? controlIndex + 2 : controlIndex + 1; - const brokenIndices = Cast(this.props.inkDoc, listSpec("number")); + const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; }, () => controlUndo?.end(), - brokenIndices ? - action((e: PointerEvent, doubleTap: boolean | undefined) => { - if (doubleTap && brokenIndices.includes(controlIndex)) { - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); - } - }) - : emptyFunction); + action((e: PointerEvent, doubleTap: boolean | undefined) => { + if (doubleTap && brokenIndices && brokenIndices.includes(controlIndex)) { + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } + })); } } diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx index 6e890e184..f1eb4b9db 100644 --- a/src/client/views/InkHandles.tsx +++ b/src/client/views/InkHandles.tsx @@ -51,6 +51,7 @@ export class InkHandles extends React.Component { onBreakTangent = (e: KeyboardEvent, controlIndex: number) => { const doc: Doc = this.props.inkDoc; if (["Alt"].includes(e.key)) { + e.stopPropagation(); if (doc) { const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")) || new List; if (brokenIndices && !brokenIndices.includes(controlIndex)) { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index de8cf80bd..76ca5b5ec 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -257,6 +257,11 @@ export class InkStrokeProperties { return newPoints; }) + /** + * Snaps a control point with broken tangency back to synced rotation. + * @param handleIndexA The handle point that retains its current position. + * @param handleIndexB The handle point that is rotated to be 180 degrees from its opposite. + */ snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction((doc: Doc, ink: InkData) => { const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); @@ -278,13 +283,12 @@ export class InkStrokeProperties { */ @action rotatePoint = (target: PointData, origin: PointData, angle: number) => { - target.X -= origin.X; - target.Y -= origin.Y; - const newX = Math.cos(angle) * target.X - Math.sin(angle) * target.Y; - const newY = Math.sin(angle) * target.X + Math.cos(angle) * target.Y; - target.X = newX + origin.X; - target.Y = newY + origin.Y; - return target; + let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; + const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y; + const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y; + rotatedTarget.X = newX + origin.X; + rotatedTarget.Y = newY + origin.Y; + return rotatedTarget; } /** -- cgit v1.2.3-70-g09d2 From 09e5f2c58ca87ded318627cbc5dea436989fcff2 Mon Sep 17 00:00:00 2001 From: vkalev Date: Thu, 29 Jul 2021 09:51:50 -0400 Subject: deselecting an ink stroke properly exits edit view --- src/client/views/InkingStroke.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index a8f32118c..21059b330 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -65,11 +65,19 @@ export class InkingStroke extends ViewBoxBaseComponent { + if (!this.props.isSelected() && this._properties) { + this._properties._controlButton = false; + } + } + render() { TraceMobx(); - // if (!this.props.isSelected() && this._properties) { - // this._properties._controlButton = false; - // } + this.toggleControlButton(); // Extracting the ink data and formatting information of the current ink stroke. const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; const inkDoc: Doc = this.layoutDoc; -- cgit v1.2.3-70-g09d2