Skip to content

Commit beefa2b

Browse files
V-SANTMegaRedHand
andauthored
fix: Save canvas position (#187)
This pull request enhances viewport management in the GlobalContext and Viewport classes by adding support for saving, restoring, and centering the view. ## Key Changes: ### On code - GlobalContext: Added restorePosition and centerView to manage viewport state. - Viewport: Implemented methods to save/restore position and zoom, and to set the center. - viewportManager.ts: Automatically centers the viewport after loading a file. ### On functionalities - The viewport's position and zoom now persist after reloading. - Pressing the "New" button centers the viewport in the middle of the canvas. - Loading a graph from a file centers the viewport on the graph's centroid (Similar to a center of mass of a particle system). ## Known Bugs and Enhancements - Pressing the "New" or "Load" buttons updates the layer in the selector, but not in the left sidebar, causing inconsistencies in the UI. - The canvas boundaries are not visually indicated, which can be problematic when building a network near the edges without realizing you've reached the limit. #### Before pressing new ![image](https://github.com/user-attachments/assets/9f52ccf0-acb6-48e1-a3ed-17451d6fe905) #### Afther pressing new ![image](https://github.com/user-attachments/assets/cb377e5f-8f20-4f98-8c0e-12a18aadd8a6) #### Before load ![image](https://github.com/user-attachments/assets/bbd943bf-e05b-48ba-8b6c-f463fb9a5307) #### After load ![image](https://github.com/user-attachments/assets/0b37d5b5-30ee-4fc7-a272-9cf4170dca5d) On both cases, the link layer is selected but the left bar only shows hosts Closes #92 --------- Co-authored-by: Tomás Grüner <[email protected]>
1 parent ed0c452 commit beefa2b

File tree

4 files changed

+74
-10
lines changed

4 files changed

+74
-10
lines changed

src/context.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export class GlobalContext {
7979
speedMultiplier: SpeedMultiplier = new SpeedMultiplier(1),
8080
) {
8181
this.setNetwork(datagraph, layer);
82+
this.viewport.restorePosition();
8283
this.setSpeedMultiplier(speedMultiplier);
8384
this.setupAutoSave();
8485
saveToLocalStorage(this);
@@ -118,6 +119,29 @@ export class GlobalContext {
118119
this.speedMultiplier.unpause();
119120
}
120121

122+
centerView() {
123+
const deviceCount = this.datagraph.getDeviceCount();
124+
125+
if (deviceCount === 0) {
126+
this.viewport.setCenter();
127+
return;
128+
}
129+
130+
const devices = this.datagraph.getDevices();
131+
let sumX = 0,
132+
sumY = 0;
133+
134+
for (const [, device] of devices) {
135+
sumX += device.x;
136+
sumY += device.y;
137+
}
138+
139+
const centerX = sumX / deviceCount;
140+
const centerY = sumY / deviceCount;
141+
142+
this.viewport.setCenter(centerX, centerY);
143+
}
144+
121145
changeSpeedMultiplier(speedMultiplier: number) {
122146
this.speedMultiplier.setSpeed(speedMultiplier);
123147
saveToLocalStorage(this);

src/graphics/viewport.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,47 +28,82 @@ export class Viewport extends pixi_viewport.Viewport {
2828
worldHeight: WORLD_HEIGHT,
2929
events: events,
3030
});
31-
this.moveCenter(WORLD_WIDTH / 2, WORLD_HEIGHT / 2);
31+
3232
this.sortableChildren = true;
3333
this.initializeMovement();
34-
3534
this.addChild(new Background());
3635

37-
// Track drag start
36+
// Restore saved position
37+
this.restorePosition();
38+
3839
this.on("drag-start", () => {
3940
this.isDragging = true;
4041
});
4142

42-
// Track drag end
4343
this.on("drag-end", () => {
4444
setTimeout(() => {
4545
this.isDragging = false;
46-
}, 50); // Small delay to ensure click doesn't trigger after drag
46+
}, 50);
47+
this.savePosition(); // Save position after movement
48+
});
49+
50+
this.on("zoomed-end", () => {
51+
this.savePosition(); // Save position after zoom
4752
});
4853

49-
// Only deselect if it's a genuine click, not a drag
5054
const onClick = (event: FederatedPointerEvent) => {
5155
if (!this.isDragging && event.target === this) {
5256
deselectElement();
5357
}
5458
};
5559
this.on("click", onClick, this);
56-
// NOTE: this is "click" for mobile devices
5760
this.on("tap", onClick, this);
5861
}
5962

63+
// Save the current position of the viewport
64+
private savePosition() {
65+
localStorage.setItem(
66+
"viewportPosition",
67+
JSON.stringify({ x: this.x, y: this.y }),
68+
);
69+
localStorage.setItem(
70+
"viewportZoom",
71+
JSON.stringify({ x: this.scale.x, y: this.scale.y }),
72+
);
73+
}
74+
75+
// Restore the saved position of the viewport
76+
public restorePosition() {
77+
const savedPosition = localStorage.getItem("viewportPosition");
78+
const savedZoom = localStorage.getItem("viewportZoom");
79+
80+
if (savedPosition) {
81+
const { x, y } = JSON.parse(savedPosition);
82+
this.position.set(x, y);
83+
} else {
84+
this.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2);
85+
}
86+
87+
if (savedZoom) {
88+
const { x, y } = JSON.parse(savedZoom);
89+
this.scale.set(x, y);
90+
} else {
91+
this.scale.set(1);
92+
}
93+
}
94+
6095
clear() {
6196
this.removeChildren();
6297
this.addChild(new Background());
63-
this.moveCenter(WORLD_WIDTH / 2, WORLD_HEIGHT / 2);
98+
localStorage.removeItem("viewportPosition");
99+
localStorage.removeItem("viewportZoom");
64100
}
65101

66102
private initializeMovement() {
67103
this.drag()
68104
.pinch()
69105
.wheel()
70106
.clamp({ direction: "all" })
71-
// TODO: revisit when all icons are finalized
72107
.clampZoom({
73108
minHeight: 200,
74109
minWidth: 200,
@@ -88,4 +123,8 @@ export class Viewport extends pixi_viewport.Viewport {
88123
this.plugins.pause(plugin);
89124
}
90125
}
126+
127+
setCenter(x: number = WORLD_WIDTH / 2, y: number = WORLD_HEIGHT / 2) {
128+
this.moveCenter(x, y);
129+
}
91130
}

src/handlers/triggers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { DataGraph } from "../types/graphs/datagraph";
1313
export const triggerNew = (ctx: GlobalContext) => {
1414
deselectElement(); // Deselect any currently selected element
1515
ctx.load(new DataGraph(ctx)); // Load a new empty DataGraph into the context
16+
ctx.centerView(); // Center the view on the new network
1617
};
1718

1819
// Function to save the network

src/types/viewportManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ export function loadFromFile(ctx: GlobalContext) {
176176
const jsonData = readerEvent.target.result as string;
177177
const graphData: GraphData = JSON.parse(jsonData);
178178
ctx.load(DataGraph.fromData(graphData, ctx));
179-
180179
console.log("Graph loaded successfully.");
180+
ctx.centerView();
181181
};
182182
};
183183

0 commit comments

Comments
 (0)