diff --git a/src/index.ejs b/src/index.ejs
index 57d3d93..2c96cb2 100644
--- a/src/index.ejs
+++ b/src/index.ejs
@@ -1,31 +1,31 @@
-
-
-
-
-
- <%= htmlWebpackPlugin.options.title %>
-
-
-
-
-
-
-
-
GEduNet
-
-
-
-
-
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
+
GEduNet
+
+
+
+
+
diff --git a/src/index.ts b/src/index.ts
index 7f2f22a..738eeea 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -198,7 +198,7 @@ export class RightBar {
}
}
- // Adds a specific button to the right-bar
+ // Adds a standard button to the right-bar
addButton(
text: string,
onClick: () => void,
@@ -216,6 +216,40 @@ export class RightBar {
};
this.rightBar.appendChild(button);
}
+
+ // Adds a select dropdown to the right-bar
+ addDropdown(
+ label: string,
+ options: { value: string; text: string }[],
+ onChange: (selectedValue: string) => void,
+ selectId?: string,
+ ) {
+ const container = document.createElement("div");
+ container.classList.add("dropdown-container");
+
+ const labelElement = document.createElement("label");
+ labelElement.textContent = label;
+ labelElement.classList.add("right-bar-label");
+
+ const select = document.createElement("select");
+ select.classList.add("right-bar-select");
+ if (selectId) select.id = selectId; // Assigns ID if provided
+
+ options.forEach((optionData) => {
+ const option = document.createElement("option");
+ option.value = optionData.value;
+ option.textContent = optionData.text;
+ select.appendChild(option);
+ });
+
+ select.onchange = () => {
+ onChange(select.value);
+ };
+
+ container.appendChild(labelElement);
+ container.appendChild(select);
+ this.rightBar.appendChild(container);
+ }
}
// > index.ts
diff --git a/src/style.css b/src/style.css
index 6e9c92b..eb27600 100644
--- a/src/style.css
+++ b/src/style.css
@@ -76,31 +76,6 @@ body {
object-fit: contain; /* Maintains image proportion without distortion */
}
-/* Style for the right sidebar */
-.right-bar {
- flex: 0 0 250px; /* Fixed width of 250px for the right bar */
- background-color: #f1f1f1; /* Light background for the bar */
- box-shadow: -2px 0px 5px rgba(0, 0, 0, 0.2); /* Left shadow to highlight the bar */
- padding: 20px; /* Internal space around content */
-}
-
-/* Title of the information section in the right bar */
-.right-bar h2 {
- text-align: center; /* Center the title horizontally */
- margin: 0; /* Remove default margins */
- padding-bottom: 15px; /* Space below the title */
- font-size: 22px; /* Font size of the title */
- font-weight: bold; /* Makes the title text thicker */
- color: #333333; /* Dark gray text color */
-}
-
-/* Style for the information content in the right bar */
-.info-content {
- font-size: 15px; /* Adjust font size */
- font-weight: bold; /* Makes the text thicker */
- color: #333333; /* Dark gray text color */
-}
-
/* Container for the canvas (central graphic) */
.canvas-container {
flex: 1; /* Canvas takes up the remaining space between bars */
@@ -146,27 +121,91 @@ canvas {
background-color: #357abd; /* Darker background on hover */
}
+/* Style for the right sidebar */
+.right-bar {
+ flex: 0 0 250px; /* Fixed width of 250px for the right bar */
+ background-color: #f1f1f1; /* Light background for the bar */
+ box-shadow: -2px 0px 5px rgba(0, 0, 0, 0.2); /* Left shadow to highlight the bar */
+ padding: 20px; /* Internal space around content */
+}
+
+/* Title of the information section in the right bar */
+.right-bar h2 {
+ text-align: center; /* Center the title horizontally */
+ margin: 0; /* Remove default margins */
+ padding-bottom: 15px; /* Space below the title */
+ font-size: 22px; /* Font size of the title */
+ font-weight: bold; /* Makes the title text thicker */
+ color: #333333; /* Dark gray text color */
+}
+
+/* Style for the information content in the right bar */
+.info-content {
+ font-size: 15px; /* Adjust font size */
+ font-weight: bold; /* Makes the text thicker */
+ color: #333333; /* Dark gray text color */
+}
+
/* Style for buttons in the right bar */
.right-bar-button {
- background-color: #4caf50; /* Background color */
+ background-color: #4caf50;
border: none;
color: white;
- padding: 10px 20px;
+ padding: 6px 12px; /* Reduce padding */
text-align: center;
text-decoration: none;
display: inline-block;
- font-size: 16px;
- margin: 4px 2px;
+ font-size: 14px; /* Reduce font size */
+ margin: 3px 1px; /* Reduce margin */
cursor: pointer;
- border-radius: 5px;
- width: 200px; /* Fixed width */
+ border-radius: 4px;
+ width: 180px; /* Reduce width slightly */
}
.right-bar-button:hover {
- background-color: #45a049; /* Color when hovering */
+ background-color: #45a049;
}
.right-bar-button.selected-button {
- background-color: #007bff; /* Blue or your preferred color */
+ background-color: #007bff;
color: white;
}
+
+/* Container for packet options */
+.packet-options-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px; /* Reduce gap */
+ width: 100%;
+ margin-top: 8px; /* Reduce top margin */
+}
+
+/* Style for labels in the right bar */
+.right-bar-label {
+ font-weight: bold;
+ margin-bottom: 2px; /* Reduce bottom margin */
+ font-size: 12px; /* Reduce font size */
+ color: #333333;
+}
+
+/* Style for dropdowns (selectors) */
+.right-bar-select {
+ width: 100%;
+ padding: 6px; /* Reduce padding */
+ font-size: 14px; /* Reduce font size */
+ border-radius: 4px;
+ border: 1px solid #cccccc;
+ background-color: #f1f1f1;
+ cursor: pointer;
+}
+
+.right-bar-select:focus {
+ outline: none;
+ border-color: #007bff;
+ box-shadow: 0 0 4px rgba(0, 123, 255, 0.4); /* Slightly reduce shadow */
+}
+
+/* Optional: Style to make the entire dropdown area compact */
+.dropdown-container {
+ margin-bottom: 8px; /* Reduce bottom margin */
+}
diff --git a/src/types/device.ts b/src/types/device.ts
index 3bfe8fe..6729326 100644
--- a/src/types/device.ts
+++ b/src/types/device.ts
@@ -2,6 +2,7 @@ import { Texture, Sprite, FederatedPointerEvent, Graphics } from "pixi.js";
import RouterImage from "../assets/router.svg";
import ServerImage from "../assets/server.svg";
import PcImage from "../assets/pc.svg";
+import { Packet } from "./packet";
import { ViewGraph } from "./graphs/viewgraph";
import {
deselectElement,
@@ -82,11 +83,40 @@ export class Device extends Sprite {
}
resize(sprite: Sprite): void {
- // Setup the size of the new element
sprite.width = sprite.width / 70;
sprite.height = sprite.height / DEVICE_SIZE;
}
+ sendPacket(packetType: string, destinationId: number): void {
+ console.log(
+ `Sending ${packetType} packet from ${this.id} to ${destinationId}`,
+ );
+
+ const packetColors: Record = {
+ IP: 0x0000ff, // Verde para paquetes IP
+ ICMP: 0xff0000, // Rojo para paquetes ICMP
+ };
+
+ const color = packetColors[packetType] || 0xffffff; // Color por defecto blanco
+ const speed = 200; // Velocidad en píxeles por segundo
+
+ const pathEdgeIds = this.viewgraph.getPathBetween(this.id, destinationId);
+
+ if (pathEdgeIds.length === 0) {
+ console.warn(
+ `No se encontró un camino entre ${this.id} y ${destinationId}.`,
+ );
+ return;
+ }
+
+ const pathEdges = pathEdgeIds.map((id) => this.viewgraph.getEdge(id));
+
+ const packet = new Packet(color, speed);
+ const stage = this.viewgraph.getViewport();
+ stage.addChild(packet);
+ packet.animateAlongPath(pathEdges, this.id);
+ }
+
deleteDevice(): void {
this.viewgraph.removeDevice(this.id);
// Clear connections
@@ -223,6 +253,52 @@ export class Device extends Sprite {
true,
);
this.rightbar.addButton("Delete device", () => this.deleteDevice());
+
+ // Dropdown for selecting packet type
+ this.rightbar.addDropdown(
+ "Packet Type",
+ [
+ { value: "IP", text: "IP" },
+ { value: "ICMP", text: "ICMP" },
+ ],
+ (selectedValue) => {
+ console.log("Selected Packet Type:", selectedValue);
+ },
+ "packet-type",
+ );
+
+ // Dropdown for selecting destination
+ const adjacentDevices = this.viewgraph
+ .getDeviceIds()
+ .filter((id) => id !== this.id)
+ .map((id) => ({ value: id.toString(), text: `Device ${id}` }));
+
+ this.rightbar.addDropdown(
+ "Destination",
+ adjacentDevices,
+ (selectedValue) => {
+ console.log("Selected Destination:", selectedValue);
+ },
+ "destination",
+ );
+
+ // Button to send the packet
+ this.rightbar.addButton("Send Packet", () => {
+ // Get the selected packet type and destination ID
+ const packetType = (
+ document.getElementById("packet-type") as HTMLSelectElement
+ )?.value;
+ const destinationId = Number(
+ (document.getElementById("destination") as HTMLSelectElement)?.value,
+ );
+
+ // Call the sendPacket method with the selected values
+ if (packetType && !isNaN(destinationId)) {
+ this.sendPacket(packetType, destinationId);
+ } else {
+ console.warn("Please select both a packet type and a destination.");
+ }
+ });
}
showInfo() {
@@ -261,10 +337,6 @@ export class Router extends Device {
? "[" + Array.from(this.connections.values()).join(", ") + "]"
: "None",
},
- { label: "Model", value: "TP-Link AX6000" },
- { label: "IP Address", value: "192.168.1.1" },
- { label: "Firmware Version", value: "1.2.3" },
- { label: "Uptime", value: "5 days, 4 hours, 23 minutes" },
]);
this.addCommonButtons();
@@ -292,11 +364,6 @@ export class Server extends Device {
? "[" + Array.from(this.connections.values()).join(", ") + "]"
: "None",
},
- { label: "Operating System", value: "Ubuntu 20.04 LTS" },
- { label: "CPU Usage", value: "42%" },
- { label: "Memory Usage", value: "8 GB / 16 GB" },
- { label: "Disk Space", value: "500 GB / 1 TB" },
- { label: "Last Backup", value: "2024-11-01 02:30 AM" },
]);
this.addCommonButtons();
@@ -324,10 +391,6 @@ export class Pc extends Device {
? "[" + Array.from(this.connections.values()).join(", ") + "]"
: "None",
},
- { label: "Operating System", value: "Windows 10 Pro" },
- { label: "Antivirus Status", value: "Active" },
- { label: "IP Address", value: "192.168.1.100" },
- { label: "Storage Available", value: "250 GB / 512 GB" },
]);
this.addCommonButtons();
diff --git a/src/types/devices/device.ts b/src/types/devices/device.ts
new file mode 100644
index 0000000..832986c
--- /dev/null
+++ b/src/types/devices/device.ts
@@ -0,0 +1,316 @@
+// src/devices/device.ts
+
+import { Texture, Sprite, FederatedPointerEvent, Graphics } from "pixi.js";
+import { ViewGraph } from "../graphs/viewgraph";
+import { RightBar } from "../../index";
+import {
+ deselectElement,
+ refreshElement,
+ selectElement,
+} from "../viewportManager";
+import { Packet } from "../packet";
+
+export const DEVICE_SIZE = 20;
+
+let selectedDeviceId: number | null = null; // Stores only the ID instead of 'this'
+
+export function setSelectedDeviceId(value: number | null) {
+ selectedDeviceId = value;
+}
+
+export class Device extends Sprite {
+ id: number;
+ dragging = false;
+ viewgraph: ViewGraph;
+ connections = new Map();
+ offsetX = 0;
+ offsetY = 0;
+ rightbar: RightBar;
+ highlightMarker: Graphics | null = null; // Marker to indicate selection
+
+ constructor(
+ id: number,
+ svg: string,
+ viewgraph: ViewGraph,
+ position: { x: number; y: number } | null = null,
+ ) {
+ const texture = Texture.from(svg);
+ super(texture);
+
+ this.rightbar = RightBar.getInstance();
+ this.id = id;
+ this.viewgraph = viewgraph;
+
+ this.anchor.set(0.5);
+
+ // Use specified coordinates or center of the world
+ const stage = this.viewgraph.getViewport();
+ if (position) {
+ this.x = position.x;
+ this.y = position.y;
+ } else {
+ const worldCenter = stage.toWorld(
+ stage.screenWidth / 2,
+ stage.screenHeight / 2,
+ );
+ this.x = worldCenter.x;
+ this.y = worldCenter.y;
+ }
+
+ this.eventMode = "static";
+ this.interactive = true;
+ this.cursor = "pointer";
+
+ this.on("pointerdown", this.onPointerDown, this);
+ this.on("click", this.onClick, this);
+ }
+
+ getConnections(): { edgeId: number; adyacentId: number }[] {
+ return Array.from(this.connections.entries()).map(
+ ([edgeId, adyacentId]) => {
+ return { edgeId, adyacentId };
+ },
+ );
+ }
+
+ addConnection(edgeId: number, adyacentId: number) {
+ this.connections.set(edgeId, adyacentId);
+ }
+
+ removeConnection(id: number) {
+ this.connections.delete(id);
+ }
+
+ resize(sprite: Sprite): void {
+ sprite.width = sprite.width / 70;
+ sprite.height = sprite.height / DEVICE_SIZE;
+ }
+
+ sendPacket(packetType: string, destinationId: number): void {
+ console.log(
+ `Sending ${packetType} packet from ${this.id} to ${destinationId}`,
+ );
+
+ const packetColors: Record = {
+ IP: 0x0000ff, // Verde para paquetes IP
+ ICMP: 0xff0000, // Rojo para paquetes ICMP
+ };
+
+ const color = packetColors[packetType] || 0xffffff; // Color por defecto blanco
+ const speed = 200; // Velocidad en píxeles por segundo
+
+ const pathEdgeIds = this.viewgraph.getPathBetween(this.id, destinationId);
+
+ if (pathEdgeIds.length === 0) {
+ console.warn(
+ `No se encontró un camino entre ${this.id} y ${destinationId}.`,
+ );
+ return;
+ }
+
+ const pathEdges = pathEdgeIds.map((id) => this.viewgraph.getEdge(id));
+
+ const packet = new Packet(color, speed);
+ const stage = this.viewgraph.getViewport();
+ stage.addChild(packet);
+ packet.animateAlongPath(pathEdges, this.id);
+ }
+
+ deleteDevice(): void {
+ this.viewgraph.removeDevice(this.id);
+ // Clear connections
+ this.connections.clear();
+ deselectElement();
+ }
+
+ onPointerDown(event: FederatedPointerEvent): void {
+ // console.log("Entered onPointerDown");
+ this.dragging = true;
+ event.stopPropagation();
+
+ // Get the pointer position in world (viewport) coordinates
+ const worldPosition = this.viewgraph
+ .getViewport()
+ .toWorld(event.clientX, event.clientY);
+
+ // Calculate the offset between the pointer and the sprite
+ this.offsetX = worldPosition.x - this.x;
+ this.offsetY = worldPosition.y - this.y;
+
+ // Listen to global pointermove and pointerup events
+ document.addEventListener("pointermove", this.onPointerMove.bind(this));
+ document.addEventListener("pointerup", this.onPointerUp.bind(this));
+ }
+
+ onPointerMove(event: FederatedPointerEvent): void {
+ // console.log("Entered onPointerMove");
+ if (this.dragging) {
+ // Get the new pointer position in world coordinates
+ const worldPosition = this.viewgraph
+ .getViewport()
+ .toWorld(event.clientX, event.clientY);
+
+ // Calculate the new sprite position using the calculated offset
+ const newPositionX = worldPosition.x - this.offsetX;
+ const newPositionY = worldPosition.y - this.offsetY;
+
+ // Update the sprite position
+ this.x = newPositionX;
+ this.y = newPositionY;
+
+ // Notify view graph about its movement
+ this.viewgraph.deviceMoved(this.id);
+ }
+ }
+
+ onPointerUp(): void {
+ // console.log("Entered onPointerUp");
+ this.dragging = false;
+ // Remove global pointermove and pointerup events
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ connectTo(adyacentId: number): boolean {
+ // Connects both devices with an edge.
+ // console.log("Entered connectTo");
+
+ const edgeId = this.viewgraph.addEdge(this.id, adyacentId);
+ if (edgeId) {
+ const adyacentDevice = this.viewgraph.getDevice(adyacentId);
+ this.addConnection(edgeId, adyacentId);
+ adyacentDevice.addConnection(edgeId, this.id);
+ return true;
+ }
+ return false;
+ }
+
+ onClick(e: FederatedPointerEvent) {
+ e.stopPropagation();
+
+ if (selectedDeviceId) {
+ // If the stored ID is the same as this device's, reset it
+ if (selectedDeviceId === this.id) {
+ return;
+ }
+ // The "LineStart" device ends up as the end of the drawing but it's the same
+ if (this.connectTo(selectedDeviceId)) {
+ // selectElement(this.viewgraph.getDevice(selectedDeviceId));
+ refreshElement();
+ selectedDeviceId = null;
+ }
+ } else {
+ selectElement(this);
+ }
+ }
+
+ selectToConnect(id: number) {
+ if (selectedDeviceId === id) {
+ setSelectedDeviceId(null);
+ } else {
+ setSelectedDeviceId(id);
+ }
+ }
+
+ highlight() {
+ if (!this.highlightMarker) {
+ // Create the square as a selection marker
+ this.highlightMarker = new Graphics();
+
+ // Increase the square size
+ const size = this.width; // Side length of the square, now larger
+
+ // Draw a square using moveTo and lineTo
+ this.highlightMarker.moveTo(-size / 2, -size / 2); // Move to the top left corner of the centered square
+ this.highlightMarker.lineTo(size / 2, -size / 2); // Top line
+ this.highlightMarker.lineTo(size / 2, size / 2); // Right line
+ this.highlightMarker.lineTo(-size / 2, size / 2); // Bottom line
+ this.highlightMarker.lineTo(-size / 2, -size / 2); // Left line, closes the square
+
+ // Change color to red and increase line thickness
+ this.highlightMarker.stroke({ width: 3, color: 0x4b0082 }); // Red and thicker
+
+ // Ensure the marker is in the same container as the viewport
+ this.addChild(this.highlightMarker);
+ }
+ }
+
+ removeHighlight() {
+ if (this.highlightMarker) {
+ this.highlightMarker.clear(); // Clear the graphic
+ this.removeChild(this.highlightMarker); // Remove the marker from the viewport
+ this.highlightMarker.destroy(); // Destroy the graphic object to free memory
+ this.highlightMarker = null;
+ }
+ }
+
+ addCommonButtons() {
+ this.rightbar.addButton(
+ "Connect device",
+ () => this.selectToConnect(this.id),
+ "right-bar-button",
+ true,
+ );
+ this.rightbar.addButton("Delete device", () => this.deleteDevice());
+
+ // Dropdown for selecting packet type
+ this.rightbar.addDropdown(
+ "Packet Type",
+ [
+ { value: "IP", text: "IP" },
+ { value: "ICMP", text: "ICMP" },
+ ],
+ (selectedValue) => {
+ console.log("Selected Packet Type:", selectedValue);
+ },
+ "packet-type",
+ );
+
+ // Dropdown for selecting destination
+ const adjacentDevices = this.viewgraph
+ .getDeviceIds()
+ .filter((id) => id !== this.id)
+ .map((id) => ({ value: id.toString(), text: `Device ${id}` }));
+
+ this.rightbar.addDropdown(
+ "Destination",
+ adjacentDevices,
+ (selectedValue) => {
+ console.log("Selected Destination:", selectedValue);
+ },
+ "destination",
+ );
+
+ // Button to send the packet
+ this.rightbar.addButton("Send Packet", () => {
+ // Get the selected packet type and destination ID
+ const packetType = (
+ document.getElementById("packet-type") as HTMLSelectElement
+ )?.value;
+ const destinationId = Number(
+ (document.getElementById("destination") as HTMLSelectElement)?.value,
+ );
+
+ // Call the sendPacket method with the selected values
+ if (packetType && !isNaN(destinationId)) {
+ this.sendPacket(packetType, destinationId);
+ } else {
+ console.warn("Please select both a packet type and a destination.");
+ }
+ });
+ }
+
+ showInfo() {
+ throw new Error("Method not implemented.");
+ }
+
+ select() {
+ this.highlight(); // Calls highlight on select
+ this.showInfo();
+ }
+
+ deselect() {
+ this.removeHighlight(); // Calls removeHighlight on deselect
+ setSelectedDeviceId(null);
+ }
+}
diff --git a/src/types/devices/index.ts b/src/types/devices/index.ts
new file mode 100644
index 0000000..5c4075e
--- /dev/null
+++ b/src/types/devices/index.ts
@@ -0,0 +1,6 @@
+// src/devices/index.ts
+
+export { Device } from "./device";
+export { Router } from "./router";
+export { Server } from "./server";
+export { Pc } from "./pc";
diff --git a/src/types/devices/pc.ts b/src/types/devices/pc.ts
new file mode 100644
index 0000000..3b133d8
--- /dev/null
+++ b/src/types/devices/pc.ts
@@ -0,0 +1,30 @@
+// src/devices/pc.ts
+
+import { Device } from "./device";
+import { ViewGraph } from "../graphs/viewgraph";
+import PcImage from "../../assets/pc.svg";
+
+export class Pc extends Device {
+ constructor(
+ id: number,
+ viewgraph: ViewGraph,
+ position: { x: number; y: number },
+ ) {
+ super(id, PcImage, viewgraph, position);
+ }
+
+ showInfo() {
+ this.rightbar.renderInfo("PC Information", [
+ { label: "ID", value: this.id.toString() },
+ {
+ label: "Connected Devices",
+ value:
+ this.connections.size !== 0
+ ? "[" + Array.from(this.connections.values()).join(", ") + "]"
+ : "None",
+ },
+ ]);
+
+ this.addCommonButtons();
+ }
+}
diff --git a/src/types/devices/router.ts b/src/types/devices/router.ts
new file mode 100644
index 0000000..b45cc7f
--- /dev/null
+++ b/src/types/devices/router.ts
@@ -0,0 +1,30 @@
+// src/devices/router.ts
+
+import { Device } from "./device";
+import { ViewGraph } from "../graphs/viewgraph";
+import RouterImage from "../../assets/router.svg";
+
+export class Router extends Device {
+ constructor(
+ id: number,
+ viewgraph: ViewGraph,
+ position: { x: number; y: number },
+ ) {
+ super(id, RouterImage, viewgraph, position);
+ }
+
+ showInfo() {
+ this.rightbar.renderInfo("Router Information", [
+ { label: "ID", value: this.id.toString() },
+ {
+ label: "Connected Devices",
+ value:
+ this.connections.size !== 0
+ ? "[" + Array.from(this.connections.values()).join(", ") + "]"
+ : "None",
+ },
+ ]);
+
+ this.addCommonButtons();
+ }
+}
diff --git a/src/types/devices/server.ts b/src/types/devices/server.ts
new file mode 100644
index 0000000..084436b
--- /dev/null
+++ b/src/types/devices/server.ts
@@ -0,0 +1,30 @@
+// src/devices/server.ts
+
+import { Device } from "./device";
+import { ViewGraph } from "../graphs/viewgraph";
+import ServerImage from "../../assets/server.svg";
+
+export class Server extends Device {
+ constructor(
+ id: number,
+ viewgraph: ViewGraph,
+ position: { x: number; y: number },
+ ) {
+ super(id, ServerImage, viewgraph, position);
+ }
+
+ showInfo() {
+ this.rightbar.renderInfo("Server Information", [
+ { label: "ID", value: this.id.toString() },
+ {
+ label: "Connected Devices",
+ value:
+ this.connections.size !== 0
+ ? "[" + Array.from(this.connections.values()).join(", ") + "]"
+ : "None",
+ },
+ ]);
+
+ this.addCommonButtons();
+ }
+}
diff --git a/src/types/edge.ts b/src/types/edge.ts
index f4ae7a1..752605e 100644
--- a/src/types/edge.ts
+++ b/src/types/edge.ts
@@ -1,19 +1,25 @@
import { Graphics } from "pixi.js";
import { ViewGraph } from "./graphs/viewgraph";
-import { Device } from "./device";
+import { Device } from "./devices/index"; // Import the Device class
import { deselectElement, selectElement } from "./viewportManager";
import { RightBar } from "..";
+export interface Position {
+ x: number;
+ y: number;
+}
+
export enum Colors {
Violet = 0x4b0082, // Violeta
Burgundy = 0x6d071a, // Bordo
+ Lightblue = 0x1e90ff, // celeste
}
export class Edge extends Graphics {
id: number;
connectedNodes: { n1: number; n2: number };
- startPos: { x: number; y: number };
- endPos: { x: number; y: number };
+ startPos: Position;
+ endPos: Position;
viewgraph: ViewGraph;
rightbar: RightBar;
@@ -39,12 +45,24 @@ export class Edge extends Graphics {
this.on("click", () => selectElement(this));
}
+ nodePosition(nodeId: number): Position | undefined {
+ return this.connectedNodes.n1 === nodeId
+ ? this.startPos
+ : this.connectedNodes.n2 === nodeId
+ ? this.endPos
+ : undefined;
+ }
+
+ otherEnd(nodeId: number): number | undefined {
+ return this.connectedNodes.n1 === nodeId
+ ? this.connectedNodes.n2
+ : this.connectedNodes.n2 === nodeId
+ ? this.connectedNodes.n1
+ : undefined;
+ }
+
// Method to draw the line
- public drawEdge(
- startPos: { x: number; y: number },
- endPos: { x: number; y: number },
- color: number,
- ) {
+ public drawEdge(startPos: Position, endPos: Position, color: number) {
this.clear();
this.moveTo(startPos.x, startPos.y);
this.lineTo(endPos.x, endPos.y);
@@ -69,7 +87,7 @@ export class Edge extends Graphics {
}
removeHighlight() {
- this.drawEdge(this.startPos, this.endPos, Colors.Burgundy);
+ this.drawEdge(this.startPos, this.endPos, Colors.Lightblue);
}
// Method to show the Edge information
@@ -114,10 +132,16 @@ export class Edge extends Graphics {
const offsetX2 = ((device2.width + 5) / 2) * Math.cos(angle);
const offsetY2 = ((device2.height + 5) / 2) * Math.sin(angle);
- const newStartPos = { x: device1.x + offsetX1, y: device1.y + offsetY1 };
- const newEndPos = { x: device2.x - offsetX2, y: device2.y - offsetY2 };
+ const newStartPos: Position = {
+ x: device1.x + offsetX1,
+ y: device1.y + offsetY1,
+ };
+ const newEndPos: Position = {
+ x: device2.x - offsetX2,
+ y: device2.y - offsetY2,
+ };
- this.drawEdge(newStartPos, newEndPos, 0x6d071a);
+ this.drawEdge(newStartPos, newEndPos, Colors.Lightblue);
}
public remove() {
diff --git a/src/types/graphs/viewgraph.ts b/src/types/graphs/viewgraph.ts
index afa4138..14deeec 100644
--- a/src/types/graphs/viewgraph.ts
+++ b/src/types/graphs/viewgraph.ts
@@ -1,4 +1,4 @@
-import { Device, Pc, Router, Server } from "./../device"; // Import the Device class
+import { Device, Pc, Router, Server } from "./../devices/index"; // Import the Device class
import { Edge } from "./../edge";
import { DataGraph } from "./datagraph";
import { Viewport } from "../..";
@@ -170,6 +170,11 @@ export class ViewGraph {
return Array.from(this.devices.values());
}
+ // Devuelve un array con solo los IDs de los dispositivos
+ getDeviceIds(): number[] {
+ return Array.from(this.devices.keys());
+ }
+
// Get the number of devices in the graph
getDeviceCount(): number {
return this.devices.size;
@@ -237,4 +242,46 @@ export class ViewGraph {
getViewport() {
return this.viewport;
}
+
+ // En ViewGraph
+ getEdge(edgeId: number): Edge | undefined {
+ return this.edges.get(edgeId);
+ }
+
+ /// Returns the IDs of the edges connecting the two devices
+ getPathBetween(idA: number, idB: number): number[] {
+ if (idA === idB) {
+ return [];
+ }
+ const a = this.devices.get(idA);
+ const b = this.devices.get(idB);
+ if (!a || !b) {
+ return [];
+ }
+ let current = a;
+ const unvisitedNodes = [];
+ const connectingEdges = new Map([[a.id, null]]);
+ while (current.id !== idB) {
+ for (const [edgeId, adyacentId] of current.connections) {
+ const edge = this.edges.get(edgeId);
+ if (!connectingEdges.has(adyacentId)) {
+ connectingEdges.set(adyacentId, edge.id);
+ unvisitedNodes.push(this.devices.get(adyacentId));
+ }
+ }
+ if (unvisitedNodes.length === 0) {
+ return [];
+ }
+ current = unvisitedNodes.shift();
+ }
+ const path = [];
+ while (current.id !== idA) {
+ const edgeId = connectingEdges.get(current.id);
+ path.push(edgeId);
+ const edge = this.edges.get(edgeId);
+ const parentId = edge.otherEnd(current.id);
+ current = this.devices.get(parentId);
+ }
+ return path.reverse();
+ }
}
diff --git a/src/types/packet.ts b/src/types/packet.ts
new file mode 100644
index 0000000..2cac521
--- /dev/null
+++ b/src/types/packet.ts
@@ -0,0 +1,68 @@
+import { Graphics, Ticker } from "pixi.js";
+import { Edge, Position } from "./edge";
+
+export class Packet extends Graphics {
+ speed: number;
+ progress = 0;
+ currentPath: Edge[];
+ currentEdge: Edge;
+ currentStart: number;
+
+ constructor(color: number, speed: number) {
+ super();
+ this.beginFill(color);
+ this.drawCircle(0, 0, 5); // Cambiar a un círculo con radio de 5
+ this.endFill();
+ this.speed = speed;
+ }
+
+ animateAlongPath(path: Edge[], start: number): void {
+ if (path.length === 0) {
+ console.error(
+ "No se puede animar un paquete a lo largo de un camino vacío",
+ );
+ return;
+ }
+ console.log(path);
+ this.currentPath = path;
+ this.currentEdge = this.currentPath.shift();
+ this.currentStart = start;
+ Ticker.shared.add(this.updateProgress, this);
+ }
+
+ updateProgress(ticker: Ticker) {
+ if (this.progress >= 1) {
+ this.progress = 0;
+ if (this.currentPath.length == 0) {
+ ticker.remove(this.updateProgress, this);
+ this.removeFromParent();
+ return;
+ }
+ this.currentStart = this.currentEdge.otherEnd(this.currentStart);
+ this.currentEdge = this.currentPath.shift();
+ }
+ this.progress += (ticker.deltaMS * this.speed) / 100000;
+
+ const current = this.currentEdge;
+ const start = this.currentStart;
+
+ console.log("current: ", current);
+ console.log("start: ", start);
+
+ const startPos = current.nodePosition(start);
+ console.log("startPos: ", startPos);
+ const endPos = current.nodePosition(current.otherEnd(start));
+ console.log("endPos: ", endPos);
+ this.setPositionAlongEdge(startPos, endPos, this.progress);
+ }
+
+ /// Updates the position according to the current progress.
+ setPositionAlongEdge(start: Position, end: Position, progress: number) {
+ const dx = end.x - start.x;
+ const dy = end.y - start.y;
+
+ // Mover el paquete
+ this.x = start.x + progress * dx;
+ this.y = start.y + progress * dy;
+ }
+}
diff --git a/src/types/viewportManager.ts b/src/types/viewportManager.ts
index 054af16..a4e4e6a 100644
--- a/src/types/viewportManager.ts
+++ b/src/types/viewportManager.ts
@@ -1,6 +1,6 @@
import { GlobalContext } from "./../index";
import { DataGraph, GraphNode } from "./graphs/datagraph";
-import { Device, Pc, Router, Server } from "./device";
+import { Device, Pc, Router, Server } from "./devices/index";
import { Edge } from "./edge";
import { RightBar } from "../index"; // Ensure the path is correct