From de55afe8b1101615c43906c807989be62d84485d Mon Sep 17 00:00:00 2001 From: Adrian Egli Date: Wed, 11 Dec 2024 11:52:04 +0100 Subject: [PATCH] fix: if the ctrl + mouse wheel lets scale the netzgrafik or multi-selected nodes (local scale) (#376) * fix: import 3rd party performance * fix: ctrl + mouse wheel -> scale netzgrafik or multi-select node and scale * fix: ctrl + mouse wheel -> scale netzgrafik or multi-select node and scale --- .../data-views/editor.view.ts | 75 +++++++++++++++++++ .../view/util/svg.mouse.controller.spec.ts | 26 ++++--- src/app/view/util/svg.mouse.controller.ts | 32 +++++++- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/app/view/editor-main-view/data-views/editor.view.ts b/src/app/view/editor-main-view/data-views/editor.view.ts index 12d2e7c3..ebb38d25 100644 --- a/src/app/view/editor-main-view/data-views/editor.view.ts +++ b/src/app/view/editor-main-view/data-views/editor.view.ts @@ -577,6 +577,11 @@ export class EditorView implements SVGMouseControllerObserver { this.trainrunSectionPreviewLineView.stopPreviewLine(); } + + onScaleNetzgrafik(factor: number, scaleCenter: Vec2D) { + this.scaleNetzgrafikArea(factor, scaleCenter); + } + zoomFactorChanged(newZoomFactor: number) { this.controller.zoomFactorChanged(newZoomFactor); this.viewportCullService.onViewportChangeUpdateRendering(true); @@ -687,4 +692,74 @@ export class EditorView implements SVGMouseControllerObserver { el.classed("ShowCellCursor", false); } } + + + private scaleFullNetzgrafikArea(factor: number, zoomCenter: Vec2D) { + const vp = this.uiInteractionService.getViewboxProperties(EditorView.svgName); + const scaleCenterCoordinates = new Vec2D( + vp.panZoomLeft + zoomCenter.getX() * vp.panZoomWidth, + vp.panZoomTop + zoomCenter.getY() * vp.panZoomHeight, + ); + + // get the node under the mouse cursos and update the scaleCenter + const focalNode = this.nodeService.getNodes().find((n) => + scaleCenterCoordinates.getX() > n.getPositionX() && scaleCenterCoordinates.getX() < (n.getPositionX() + n.getNodeWidth()) && + scaleCenterCoordinates.getY() > n.getPositionY() && scaleCenterCoordinates.getY() < (n.getPositionY() + n.getNodeHeight()) + ); + + this.nodeService.getNodes().forEach((n, index) => { + let newPos = new Vec2D( + (n.getPositionX() - scaleCenterCoordinates.getX()) * factor + scaleCenterCoordinates.getX(), + (n.getPositionY() - scaleCenterCoordinates.getY()) * factor + scaleCenterCoordinates.getY() + ); + + if (focalNode?.getId() === n.getId()) { + const delta = Vec2D.sub(newPos, new Vec2D(focalNode.getPositionX(), focalNode.getPositionY(),)); + newPos = Vec2D.sub(newPos, delta); + } + n.setPosition(newPos.getX(), newPos.getY()); + }); + } + + private scaleNetzgrafikSelectedNodesArea(factor: number, nodes: Node[]) { + /* + * if more than one node is selected (multi-selected nodes) transform the nodes with center of + * mass + */ + let cx = 0; + let cy = 0; + nodes.forEach(n => { + cx += n.getPositionX() + n.getNodeWidth() / 2.0; + cy += n.getPositionY() + n.getNodeHeight() / 2.0; + }); + cx /= nodes.length; + cy /= nodes.length; + const v = Vec2D.normalize(new Vec2D(cx, cy)); + nodes.forEach((n, index) => { + const posX = (n.getPositionX() + n.getNodeWidth() / 2.0 - cx) * factor + cx - n.getNodeWidth() / 2.0; + const posY = (n.getPositionY() + n.getNodeHeight() / 2.0 - cy) * factor + cy - n.getNodeHeight() / 2.0; + n.setPosition(posX, posY); + }); + } + + private scaleNetzgrafikArea(factor: number, zoomCenter: Vec2D) { + const nodes: Node[] = this.nodeService.getSelectedNodes(); + + if (nodes.length < 2) { + this.scaleFullNetzgrafikArea(factor, zoomCenter); + } else { + this.scaleNetzgrafikSelectedNodesArea(factor, nodes); + } + + this.trainrunSectionService.getTrainrunSections().forEach(ts => { + ts.routeEdgeAndPlaceText(); + ts.getSourceNode().updateTransitionsAndConnections(); + }); + + this.nodeService.initPortOrdering(); + + this.viewportCullService.onViewportChangeUpdateRendering(true); + } + + } diff --git a/src/app/view/util/svg.mouse.controller.spec.ts b/src/app/view/util/svg.mouse.controller.spec.ts index 50701687..9ec77791 100644 --- a/src/app/view/util/svg.mouse.controller.spec.ts +++ b/src/app/view/util/svg.mouse.controller.spec.ts @@ -1,7 +1,4 @@ -import { - SVGMouseController, - SVGMouseControllerObserver, -} from "./svg.mouse.controller"; +import {SVGMouseController, SVGMouseControllerObserver,} from "./svg.mouse.controller"; import {Vec2D} from "../../utils/vec2D"; import {ViewboxProperties} from "../../services/ui/ui.interaction.service"; @@ -10,17 +7,26 @@ class DummySVGMouseControllerObserver implements SVGMouseControllerObserver { return true; } - onGraphContainerMouseup(mousePosition: Vec2D, onPaning: boolean) {} + onGraphContainerMouseup(mousePosition: Vec2D, onPaning: boolean) { + } + + zoomFactorChanged(newZoomFactor: number) { + } - zoomFactorChanged(newZoomFactor: number) {} + onViewboxChanged(viewboxProperties: ViewboxProperties) { + } - onViewboxChanged(viewboxProperties: ViewboxProperties) {} + onStartMultiSelect() { + } - onStartMultiSelect() {} + updateMultiSelect(topLeft: Vec2D, bottomRight: Vec2D) { + } - updateMultiSelect(topLeft: Vec2D, bottomRight: Vec2D) {} + onEndMultiSelect() { + } - onEndMultiSelect() {} + onScaleNetzgrafik(factor: number, scaleCenter: Vec2D) { + } } describe("general view functions", () => { diff --git a/src/app/view/util/svg.mouse.controller.ts b/src/app/view/util/svg.mouse.controller.ts index efc4d86b..83a01150 100644 --- a/src/app/view/util/svg.mouse.controller.ts +++ b/src/app/view/util/svg.mouse.controller.ts @@ -17,6 +17,8 @@ export interface SVGMouseControllerObserver { updateMultiSelect(topLeft: Vec2D, bottomRight: Vec2D); onEndMultiSelect(); + + onScaleNetzgrafik(factor: number, scaleCenter: Vec2D); } export class SVGMouseController { @@ -36,7 +38,8 @@ export class SVGMouseController { constructor( private svgName: string, private svgMouseControllerObserver: SVGMouseControllerObserver, - ) {} + ) { + } init(viewboxProperties: ViewboxProperties) { this.viewboxProperties = viewboxProperties; @@ -100,6 +103,15 @@ export class SVGMouseController { ); } + + scaleIn(scaleCenter: Vec2D) { + this.svgMouseControllerObserver.onScaleNetzgrafik(1.125, scaleCenter); + } + + scaleOut(scaleCenter: Vec2D) { + this.svgMouseControllerObserver.onScaleNetzgrafik(1.0 / 1.125, scaleCenter); + } + zoomIn(zoomCenter: Vec2D, factor = 1.0) { if (this.viewboxIsFixed) { return; @@ -312,11 +324,23 @@ export class SVGMouseController { d3.event.offsetX / this.viewboxProperties.origWidth, d3.event.offsetY / this.viewboxProperties.origHeight, ); - if (d3.event.deltaY > 0) { - this.zoomOut(zoomCenter); + + if (!d3.event.ctrlKey) { + // mouse wheel + if (d3.event.deltaY > 0) { + this.zoomOut(zoomCenter); + } else { + this.zoomIn(zoomCenter); + } } else { - this.zoomIn(zoomCenter); + // ctrl and mouse wheel + if (d3.event.deltaY > 0) { + this.scaleOut(zoomCenter); + } else { + this.scaleIn(zoomCenter); + } } + d3.event.preventDefault(); d3.event.stopPropagation(); }