From b0efa4a390415072eaeb06c8719ea57d73e10466 Mon Sep 17 00:00:00 2001 From: vkalev <50213748+vkalev@users.noreply.github.com> Date: Wed, 30 Jun 2021 12:52:52 -0500 Subject: ink Bézier handle movement fixed + small visual changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/views/InkStrokeProperties.ts | 119 ++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 30 deletions(-) (limited to 'src/client/views/InkStrokeProperties.ts') diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 533fdf006..5c5395984 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -2,12 +2,11 @@ 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 } from "../../fields/InkField"; +import { InkField, InkData, PointData } from "../../fields/InkField"; import { Cast, NumCast } from "../../fields/Types"; import { DocumentType } from "../documents/DocumentTypes"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; -import { bool } from "sharp"; export class InkStrokeProperties { static Instance: InkStrokeProperties | undefined; @@ -15,15 +14,15 @@ export class InkStrokeProperties { private _lastFill = "#D0021B"; private _lastLine = "#D0021B"; private _lastDash = "2"; - private _inkDocs: { x: number, y: number, width: number, height: number }[] = []; - // Indicates whether the ink is locked. @observable _lock = false; - // Indicates whether the ink's format is being currently edited (displaying of control points). @observable _controlBtn = false; - // Stores the index of the current selected control point of the ink instance. @observable _currPoint = -1; + constructor() { + InkStrokeProperties.Instance = this; + } + 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); @@ -79,10 +78,6 @@ export class InkStrokeProperties { }); } - constructor() { - InkStrokeProperties.Instance = this; - } - /** * Adds a new control point to the ink instance when editing its format. * @param x The x-coordinate of the current new point. @@ -213,35 +208,99 @@ export class InkStrokeProperties { } /** - * Handles the movement / scaling of control points of an ink instance. - * @param xDiff The movement of the control point's x-coordinate. - * @param yDiff The movement of the control point's y-coordinate. - * @param controlNum The index of the current control point selected. - * @returns The changed x- and y-coordinates of the control points. + * Handles the movement/scaling of a control point. */ @undoBatch @action - control = (xDiff: number, yDiff: number, controlNum: number) => - this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { + moveControl = (deltaX: number, deltaY: number, controlIndex: number) => + this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { const newPoints: { X: number, Y: number }[] = []; - const order = controlNum % 4; + const order = controlIndex % 4; for (var i = 0; i < ink.length; i++) { - newPoints.push( - (controlNum === i || - (order === 0 && i === controlNum + 1) || - (order === 0 && controlNum !== 0 && i === controlNum - 2) || - (order === 0 && controlNum !== 0 && i === controlNum - 1) || - (order === 3 && i === controlNum - 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) || - ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1)) - ) ? - { X: ink[i].X - xDiff / ptsXscale, Y: ink[i].Y - yDiff / ptsYscale } : - { X: ink[i].X, Y: ink[i].Y }); + const leftHandlePoint = order === 0 && i === controlIndex + 1; + const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2; + if (controlIndex === i || + leftHandlePoint || + rightHandlePoint || + (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || + (order === 3 && i === controlIndex - 1) || + (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) || + (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) || + ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) { + newPoints.push({ X: ink[i].X - deltaX / xScale, Y: ink[i].Y - deltaY / yScale }); + } else { + newPoints.push({ X: ink[i].X, Y: ink[i].Y }); + } } return newPoints; }); + /** + * Rotates the target point about the origin point for a given angle (radians). + */ + @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 + } + + /** + * Finds the angle difference (in radians) between two vectors relative to an arbitrary origin. + */ + angleChange = (a: PointData, b: PointData, origin: PointData) => { + // Finding vector representation of inputted points relative to new origin. + let vectorA = { X: a.X - origin.X, Y: a.Y - origin.Y }; + let vectorB = { X: b.X - origin.X, Y: b.Y - origin.Y }; + const crossProduct = vectorB.X * vectorA.Y - vectorB.Y * vectorA.X; + // Determining whether rotation is clockwise or counterclockwise. + const sign = crossProduct < 0 ? 1 : -1; + const magnitudeA = Math.sqrt(vectorA.X * vectorA.X + vectorA.Y * vectorA.Y); + const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y); + // Normalizing the vectors. + vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA }; + vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB }; + const dotProduct = vectorB.X * vectorA.X + vectorB.Y * vectorA.Y; + const theta = Math.acos(dotProduct); + return sign * theta; + } + + /** + * Handles the movement/scaling of a handle point. + */ + @undoBatch + @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 }); + } + } + + 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; + } + + return newPoints; + }); + /** * Changes the color of the border of the ink instance. * @param color The new hex value to change the border to. -- cgit v1.2.3-70-g09d2