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