diff options
-rw-r--r-- | package-lock.json | 6 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 10 | ||||
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 70 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 6 |
4 files changed, 61 insertions, 31 deletions
diff --git a/package-lock.json b/package-lock.json index 128d6fba8..d03f8af6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7709,14 +7709,14 @@ "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz", "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=", "requires": { - "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "image-size": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", "readable-stream": "^1.0.33", "tryit": "^1.0.1" }, "dependencies": { "image-size": { - "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", - "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" + "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" }, "isarray": { "version": "0.0.1", diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 5b44a0552..70dffd7a1 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -198,10 +198,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P const pt = { x: (this.Bounds.x + this.Bounds.r) / 2, 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] }; - const angle = Math.max(1, Math.abs(movement.Y / 10)); + 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); const selectedInk = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); - InkStrokeProperties.Instance?.rotateInk(selectedInk, 2 * movement.X / angle * (Math.PI / 180), pt); + angle && InkStrokeProperties.Instance?.rotateInk(selectedInk, -angle, pt); return false; }, () => { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 33e25bbbb..156bb1a6a 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -239,33 +239,63 @@ export class InkStrokeProperties { snapControl = (inkView: DocumentView, controlIndex: number) => { const inkDoc = inkView.rootDoc; const ink = Cast(inkDoc.data, InkField)?.inkData; - if (ink) { - const closed = InkingStroke.IsClosed(ink); - - // figure out which segments we don't want to snap to - avoid the dragged control point's segment and the next and prev segments (when they exist -- ie not for endpoints of unclosed curve) - const thisseg = Math.floor(controlIndex / 4) * 4; - const which = controlIndex % 4; - const nextseg = which > 1 && (closed || controlIndex < ink.length - 1) ? (thisseg + 4) % ink.length : -1; - const prevseg = which < 2 && (closed || controlIndex > 0) ? (thisseg - 4 + ink.length) % ink.length : -1; - const refPt = ink[controlIndex]; - const { nearestPt } = InkStrokeProperties.nearestPtToStroke(ink, refPt, [thisseg, prevseg, nextseg]); - - // nearestPt is in inkDoc coordinates -- we need to compute the distance in screen coordinates. - // so we scale the X & Y distances by the internal ink scale factor and then transform the final distance by the ScreenToLocal.Scale of the inkDoc itself. - const oldXrange = (xs => ({ coord: NumCast(inkDoc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); - const oldYrange = (ys => ({ coord: NumCast(inkDoc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); - const ptsXscale = ((NumCast(inkDoc._width) - NumCast(inkDoc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1; - const ptsYscale = ((NumCast(inkDoc._height) - NumCast(inkDoc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1; - const near = Math.sqrt((nearestPt.X - refPt.X) * (nearestPt.X - refPt.X) * ptsXscale * ptsXscale + - (nearestPt.Y - refPt.Y) * (nearestPt.Y - refPt.Y) * ptsYscale * ptsYscale); + if (ink) { + const { near, nearestPt, ptsXscale, ptsYscale } = this.snapWithinCurve(ink, inkDoc, controlIndex); if (near / (inkView.props.ScreenToLocalTransform().Scale || 1) < 10) { - return this.moveControlPtHandle(inkView, (nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); + const deltaX = (nearestPt.X - ink[controlIndex].X) * ptsXscale; + const deltaY = (nearestPt.Y - ink[controlIndex].Y) * ptsYscale; + return this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex); + } else { + return this.snapBetweenCurves(ink, inkDoc, controlIndex); } } return false; } + snapWithinCurve = (ink: InkData, inkDoc: Doc, controlIndex: number) => { + const closed = InkingStroke.IsClosed(ink); + + // figure out which segments we don't want to snap to - avoid the dragged control point's segment and the next and prev segments (when they exist -- ie not for endpoints of unclosed curve) + const thisseg = Math.floor(controlIndex / 4) * 4; + const which = controlIndex % 4; + const nextseg = which > 1 && (closed || controlIndex < ink.length - 1) ? (thisseg + 4) % ink.length : -1; + const prevseg = which < 2 && (closed || controlIndex > 0) ? (thisseg - 4 + ink.length) % ink.length : -1; + const refPt = ink[controlIndex]; + const { nearestPt } = InkStrokeProperties.nearestPtToStroke(ink, refPt, [thisseg, prevseg, nextseg]); + + // nearestPt is in inkDoc coordinates -- we need to compute the distance in screen coordinates. + // so we scale the X & Y distances by the internal ink scale factor and then transform the final distance by the ScreenToLocal.Scale of the inkDoc itself. + const { ptsXscale, ptsYscale } = this.inkToScreenScale(ink, inkDoc); + const near = Math.sqrt((nearestPt.X - refPt.X) * (nearestPt.X - refPt.X) * ptsXscale * ptsXscale + + (nearestPt.Y - refPt.Y) * (nearestPt.Y - refPt.Y) * ptsYscale * ptsYscale); + + return { near, nearestPt, ptsXscale, ptsYscale }; + } + + snapBetweenCurves = (ink: InkData, inkDoc: Doc, controlIndex: number) => { + const inkContext = Cast(inkDoc.context, Doc, null); + // Cast(inkContext.data) + + // .filter(doc => doc.type === DocumentType.INK) + // .map(doc => { + // if (InkStrokeProperties.Instance?._lock) { + // Doc.SetNativeHeight(doc, NumCast(doc._height)); + // Doc.SetNativeWidth(doc, NumCast(doc._width)); + // } + // return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }); + // }); + return false; + } + + inkToScreenScale = (ink: InkData, inkDoc: Doc) => { + const oldXrange = (xs => ({ coord: NumCast(inkDoc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); + const oldYrange = (ys => ({ coord: NumCast(inkDoc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); + const ptsXscale = ((NumCast(inkDoc._width) - NumCast(inkDoc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1; + const ptsYscale = ((NumCast(inkDoc._height) - NumCast(inkDoc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1; + return { ptsXscale, ptsYscale }; + } + /** * Snaps a control point with broken tangency back to synced rotation. * @param handleIndexA The handle point that retains its current position. diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index d312331d0..6ea3e108c 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -197,15 +197,11 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" ? "none" : fillColor, startMarker, endMarker, StrCast(this.layoutDoc.strokeDash), inkScaleX, inkScaleY, "", "none", 1.0, false); - // Thin blue line indicating that the current ink stroke is selected. - // const selectedLine = InteractionUtils.CreatePolyline(data, left, top, Colors.MEDIUM_BLUE, strokeWidth, 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", 1.0, false); - // Invisible polygonal line that enables the ink to be selected by the user. const highlightIndex = BoolCast(this.props.Document.isLinkButton) && Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString const highlightColor = !highlightIndex ? StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== "transparent" ? StrCast(this.layoutDoc.color, "transparent") : "transparent") : ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex]; - + // Invisible polygonal line that enables the ink to be selected by the user. const clickableLine = InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor, inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && (new Color(fillColor)).alpha() < 1 ? 6 : 15), StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), |