From 687b51a3de67eca11fcc1bd11964d93acc1f56d9 Mon Sep 17 00:00:00 2001 From: vkalev Date: Thu, 7 Oct 2021 15:34:25 -0400 Subject: ink rotation based on angle change --- src/client/views/DocumentDecorations.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bd9c3509b..b1c147747 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -197,9 +197,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._rotateUndo = UndoManager.StartBatch("rotatedown"); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { - const movement = { X: delta[0], Y: e.clientY - down[1] }; - const angle = Math.max(1, Math.abs(movement.Y / 10)); - InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180)); + let origin; + SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK) + .map(doc => { + const inkData = Cast(doc.rootDoc.data, InkField)?.inkData ?? []; + const inkStrokeWidth = NumCast(doc.rootDoc.strokeWidth, 1); + const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2; + const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2; + const inkLeft = Math.min(...inkData.map(p => p.X)) - inkStrokeWidth / 2; + const inkRight = Math.max(...inkData.map(p => p.X)) + inkStrokeWidth / 2; + origin = { X: (inkLeft + inkRight) / 2, Y: (inkTop + inkBottom) / 2 }; + }); + if (origin) { + const previousPoint = { X: e.clientX, Y: e.clientY }; + const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] }; + const angle = InkStrokeProperties.Instance?.angleChange(previousPoint, movedPoint, origin); + if (angle) InkStrokeProperties.Instance?.rotateInk(-angle); + } return false; }, () => { -- cgit v1.2.3-70-g09d2 From bf896f7f7c287c9a78cca91d7ceab88f66613b61 Mon Sep 17 00:00:00 2001 From: vkalev Date: Thu, 21 Oct 2021 16:44:12 -0400 Subject: changing rotation --- src/client/views/DocumentDecorations.tsx | 24 +++++++----------------- src/client/views/InkStrokeProperties.ts | 6 ++++++ 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b1c147747..207c7fcac 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -197,23 +197,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._rotateUndo = UndoManager.StartBatch("rotatedown"); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { - let origin; - SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK) - .map(doc => { - const inkData = Cast(doc.rootDoc.data, InkField)?.inkData ?? []; - const inkStrokeWidth = NumCast(doc.rootDoc.strokeWidth, 1); - const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2; - const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2; - const inkLeft = Math.min(...inkData.map(p => p.X)) - inkStrokeWidth / 2; - const inkRight = Math.max(...inkData.map(p => p.X)) + inkStrokeWidth / 2; - origin = { X: (inkLeft + inkRight) / 2, Y: (inkTop + inkBottom) / 2 }; - }); - if (origin) { - const previousPoint = { X: e.clientX, Y: e.clientY }; - const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] }; - const angle = InkStrokeProperties.Instance?.angleChange(previousPoint, movedPoint, origin); - if (angle) InkStrokeProperties.Instance?.rotateInk(-angle); - } + const docView = SelectionManager.Views()[0]; + const { left, top, right, bottom } = docView.getBounds() || { left: 0, top: 0, right: 0, bottom: 0 }; + const centerPoint = { X: (left + right) / 2, Y: (top + bottom) / 2 }; + const previousPoint = { X: e.clientX, Y: e.clientY }; + const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] }; + const angle = InkStrokeProperties.Instance?.angleChange(previousPoint, movedPoint, centerPoint); + if (angle) InkStrokeProperties.Instance?.rotateInk(-angle); return false; }, () => { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index ee30caa3d..ba85b851c 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -259,11 +259,17 @@ export class InkStrokeProperties { if (near / (this.selectedInk?.lastElement().props.ScreenToLocalTransform().Scale || 1) < 10) { return this.moveControlPtHandle((nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); + } else { + return } } return false; } + // snapControlBetweenCurves = (ink, controlIndex) => { + + // } + /** * Snaps a control point with broken tangency back to synced rotation. * @param handleIndexA The handle point that retains its current position. -- cgit v1.2.3-70-g09d2 From b30b1cb301ed742a128d5e17eea9c954d1fe0c22 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 26 Oct 2021 20:40:58 -0400 Subject: fix to ink rotation to preserve rotation center between rotations. --- src/client/views/DocumentDecorations.tsx | 11 +++++------ src/client/views/InkingStroke.tsx | 17 +++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 5 +++-- 3 files changed, 25 insertions(+), 8 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 5b44a0552..3b992e0d1 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; -import { action, computed, observable, reaction, runInAction } from "mobx"; +import { action, computed, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { DateField } from '../../fields/DateField'; import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from "../../fields/Doc"; @@ -27,8 +27,6 @@ import { LightboxView } from './LightboxView'; import { DocumentView } from "./nodes/DocumentView"; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import React = require("react"); -import { dark } from '@material-ui/core/styles/createPalette'; -import { color } from 'd3-color'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> { @@ -71,9 +69,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P x: Math.min(rect.left, bounds.x), y: Math.min(rect.top, bounds.y), r: Math.max(rect.right, bounds.r), - b: Math.max(rect.bottom, bounds.b) + b: Math.max(rect.bottom, bounds.b), + c: rect.center }, - { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) }); } @action @@ -195,7 +194,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P @action onRotateDown = (e: React.PointerEvent): void => { this._rotateUndo = UndoManager.StartBatch("rotatedown"); - const pt = { x: (this.Bounds.x + this.Bounds.r) / 2, y: (this.Bounds.y + this.Bounds.b) / 2 }; + const pt = { x: this.Bounds.c?.X ?? (this.Bounds.x + this.Bounds.r) / 2, y: this.Bounds.c?.Y ?? (this.Bounds.y + this.Bounds.b) / 2 }; setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { const movement = { X: delta[0], Y: e.clientY - down[1] }; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index d312331d0..b039579ce 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -21,6 +21,7 @@ import { InkStrokeProperties } from "./InkStrokeProperties"; import { InkTangentHandles } from "./InkTangentHandles"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import Color = require("color"); +import { Transform } from "../util/Transform"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -56,6 +57,22 @@ export class InkingStroke extends ViewBoxBaseComponent { + const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData(); + const angle = -NumCast(this.layoutDoc.rotation); + const newPoints = inkData.map(pt => { + const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * inkScaleY / inkScaleX; + const newY = Math.sin(angle) * pt.X * inkScaleX / inkScaleY + Math.cos(angle) * pt.Y; + return { X: newX, Y: newY }; + }); + const crx = (Math.max(...newPoints.map(np => np.X)) + Math.min(...newPoints.map(np => np.X))) / 2; + const cry = (Math.max(...newPoints.map(np => np.Y)) + Math.min(...newPoints.map(np => np.Y))) / 2; + const cx = Math.cos(-angle) * crx - Math.sin(-angle) * cry * inkScaleY / inkScaleX; + const cy = Math.sin(-angle) * crx * inkScaleX / inkScaleY + Math.cos(-angle) * cry; + const tc = xf.transformPoint((cx - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (cy - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2); + return { X: tc[0], Y: tc[1] }; + } + analyzeStrokes() { const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 949e0e168..cbb77f369 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -97,6 +97,7 @@ export interface DocComponentView { annotationKey?: string; getTitle?: () => string; getScrollHeight?: () => number; + getCenter?: (xf: Transform) => { X: number, Y: number }; search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; } export interface DocumentViewSharedProps { @@ -1179,9 +1180,9 @@ export class DocumentView extends React.Component { const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { const docuBox = this.docView.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); - if (docuBox.length) return docuBox[0].getBoundingClientRect(); + if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined }; } - return { left, top, right, bottom }; + return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) }; } public iconify() { -- cgit v1.2.3-70-g09d2 From c7aea59ca8cd24bf218be581ea241670fee6cc49 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 26 Oct 2021 21:03:46 -0400 Subject: use center of selection bounds for rotation if more than one stroke is selected. --- src/client/views/DocumentDecorations.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3b2661145..522995479 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -63,14 +63,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P @computed get Bounds() { - return SelectionManager.Views().map(dv => dv.getBounds()).reduce((bounds, rect) => + const views = SelectionManager.Views(); + return views.map(dv => dv.getBounds()).reduce((bounds, rect) => !rect ? bounds : { x: Math.min(rect.left, bounds.x), y: Math.min(rect.top, bounds.y), r: Math.max(rect.right, bounds.r), b: Math.max(rect.bottom, bounds.b), - c: rect.center + c: views.length === 1 ? rect.center : undefined }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) }); } -- cgit v1.2.3-70-g09d2