diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DragManager.ts | 45 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 39 |
2 files changed, 60 insertions, 24 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1ee4c57a2..d1d7f2a8a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -255,9 +255,42 @@ export namespace DragManager { export function SetSnapLines(horizLines: number[], vertLines: number[]) { SnappingManager.setSnapLines(horizLines, vertLines); } + export function snapDragAspect(dragPt: number[], snapAspect: number) { + let closest = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10); + let near = dragPt; + const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => { + if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0 + const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + if (denominator === 0) return undefined; // Lines are parallel + let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator; + // let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator; + //if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return undefined; // is the intersection along the segments + + // Return a object with the x and y coordinates of the intersection + let x = x1 + ua * (x2 - x1) + let y = y1 + ua * (y2 - y1) + const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y)); + return { pt: [x, y], dist } + } + SnappingManager.vertSnapLines().forEach((xCoord, i) => { + const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]); + if (pt && pt.dist < closest) { + closest = pt.dist; + near = pt.pt; + } + }); + SnappingManager.horizSnapLines().forEach((yCoord, i) => { + const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, -1, yCoord, 1, yCoord, dragPt[0], dragPt[1]); + if (pt && pt.dist < closest) { + closest = pt.dist; + near = pt.pt; + } + }); + return { thisX: near[0], thisY: near[1] }; + } // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line - export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, oneAxis: boolean = false) { + export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) { const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10); const snapVal = (pts: number[], drag: number, snapLines: number[]) => { if (snapLines.length) { @@ -266,15 +299,13 @@ export namespace DragManager { const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest)); const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i])); const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2; - return closestDists[minIndex] < snapThreshold ? [closestDists[minIndex], closestPts[minIndex] + offs[minIndex]] : [Number.MAX_VALUE, drag]; + return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag; } - return [Number.MAX_VALUE, drag]; + return drag; }; - const xsnap = snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()); - const ysnap = snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()); return { - thisX: !oneAxis || xsnap[0] < ysnap[0] ? xsnap[1] : e.pageX, - thisY: !oneAxis || xsnap[0] > ysnap[0] ? ysnap[1] : e.pageY + thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()), + thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()) }; } export let docsBeingDragged: Doc[] = []; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b939d96b6..3cd7f8da3 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,7 +3,7 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym, Field } from "../../new_fields/Doc"; +import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../new_fields/Doc"; import { Document } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; @@ -262,24 +262,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { const first = SelectionManager.SelectedDocuments()[0]; - const fixedAspect = NumCast(first.layoutDoc._nativeWidth) !== 0; - let { thisX, thisY } = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY, fixedAspect); - if (fixedAspect) { // if aspect is set, then any snapped movement must be coerced to match the aspect ratio - const aspect = NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight); - const deltaX = thisX - this._snapX; - const deltaY = thisY - this._snapY; - if (thisX !== e.pageX) { - const snapY = deltaX / aspect + this._snapY; - thisY = Math.abs(deltaX / aspect) < 10 ? snapY : thisY; - } else { - const snapX = deltaY * aspect + this._snapX; - thisX = Math.abs(deltaY * aspect) < 10 ? snapX : thisX; + let thisPt = { thisX: e.clientX - this._offX, thisY: e.clientY - this._offY }; + const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0; + if (fixedAspect) { // need to generalize for bl and tr drag handles + const project = (p: number[], a: number[], b: number[]) => { + var atob = [b[0] - a[0], b[1] - a[1]]; + var atop = [p[0] - a[0], p[1] - a[1]]; + var len = atob[0] * atob[0] + atob[1] * atob[1]; + var dot = atop[0] * atob[0] + atop[1] * atob[1]; + var t = dot / len; + dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); + return [a[0] + atob[0] * t, a[1] + atob[1] * t]; } + const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]) + thisPt = DragManager.snapDragAspect(drag, fixedAspect); + } else { + thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY); } - move[0] = thisX - this._snapX; - move[1] = thisY - this._snapY; - this._snapX = thisX; - this._snapY = thisY; + + move[0] = thisPt.thisX - this._snapX; + move[1] = thisPt.thisY - this._snapY; + this._snapX = thisPt.thisX; + this._snapY = thisPt.thisY; let dX = 0, dY = 0, dW = 0, dH = 0; const unfreeze = () => |