src/app/Services/graphe.service.ts
Service for managing and interacting with the Cytoscape graph.
constructor(translate: TranslateService)
|
Constructor for the GrapheService.
Parameters :
|
changeNodeEnum |
changeNodeEnum(container: any)
|
Changes the node enumeration.
Parameters :
Returns:
void
|
changeChanges |
changeChanges(container: any)
|
Changes a setting or performs an action based on the selected option(chnage colors & sizes).
Parameters :
Returns:
void
|
changeTypeGraphe |
changeTypeGraphe(container: any)
|
Changes the type of graph (e.g., Directed, Undirected, Weighted).
Parameters :
Returns:
void
|
onChangeButtonClicked |
onChangeButtonClicked(container: any)
|
Handles button clicks and performs actions based on the selected option.
Parameters :
Returns:
void
|
changeSizeScreen |
changeSizeScreen(container: any, container2: any)
|
Changes the size of the screen.
Parameters :
Returns:
void
|
RejeterChangeSizeScreen |
RejeterChangeSizeScreen(container: any, container2: any)
|
Rejects the changes made to the screen size and restores the previous size.
Parameters :
Returns:
void
|
addWeightedEdge |
addWeightedEdge(container: any, container2: any)
|
Adds a weighted edge between selected nodes.
Parameters :
Returns:
void
|
RejeterAddEdgeWeighted |
RejeterAddEdgeWeighted(container: any, container2: any)
|
Rejects the addition of a weighted edge and resets the form.
Parameters :
Returns:
void
|
changeNodeId |
changeNodeId(container: any, container2: any)
|
Changes the ID of a node in the graph.
Parameters :
Returns:
void
|
RejeterChangeNodeId |
RejeterChangeNodeId(container: any, container2: any)
|
Rejects the node ID change and resets the form.
Parameters :
Returns:
void
|
changeColor |
changeColor(container: any, container2: any)
|
Changes the colors of nodes, edges, or the screen based on user selection.
Parameters :
Returns:
void
|
RejeterChangeColor |
RejeterChangeColor(container: any)
|
Rejects the color changes and resets the form.
Parameters :
Returns:
void
|
addNode |
addNode(container: any, container2: any)
|
Adds a new node to the graph if it doesn't already exist.
Parameters :
Returns:
void
|
RejeterAddNode |
RejeterAddNode(container: any, container2: any)
|
Rejects the addition of a new node and resets the form.
Parameters :
Returns:
void
|
OnScreenTap |
OnScreenTap(container: any)
|
Listens for a tap event on the screen background and adds a new node when conditions are met.
Parameters :
Returns:
void
|
OnEdgeTap |
OnEdgeTap(container: any)
|
Listens for a tap event on edges and removes edges when the 'remove edges' or 'remove all' action is selected.
Parameters :
Returns:
void
|
OnNodeTap |
OnNodeTap(container: any)
|
Listens for a tap event on nodes and handles various actions based on the container state.
Parameters :
Returns:
void
|
OnInit |
OnInit(Container: any)
|
Initializes the graph using Cytoscape and sets up the initial style and layout.
Parameters :
Returns:
void
|
resetColors |
resetColors()
|
Resets the visual styles of nodes and edges in the graph to their default colors.
Returns:
void
|
changeColorNode |
changeColorNode(node: any, bgcolor: string, color: string, time: number)
|
Changes the background color and text color of a given node.
Parameters :
Returns:
void
|
changeColorEdge |
changeColorEdge(edge: any, color: string, lineEdgeColor: string, targetArrowColor: string, time: number)
|
Changes the visual styles of a given edge, including line color, data color, and target arrow color.
Parameters :
Returns:
void
|
changeColorNodes |
changeColorNodes()
|
Resets the visual styles of all nodes in the graph to their default colors.
Returns:
void
|
changeColorEdges |
changeColorEdges()
|
Resets the visual styles of all edges in the graph to their default colors.
Returns:
void
|
onRemoveChange |
onRemoveChange(container: any)
|
Handles actions related to removing elements from the graph and resetting the graph properties.
Parameters :
Returns:
void
|
searcheEdgeChnageBC |
searcheEdgeChnageBC(container: any, source: string, target: string, color: string, lineEdgeColor: string, targetArrowColor: string, time: number)
|
Searches for an edge between two specified nodes and changes its line color.
Parameters :
Returns:
void
|
restoreGraphe |
restoreGraphe(container: any)
|
Restores the graph by adding or removing elements based on the restoreArray.
Parameters :
Returns:
void
|
getListeOfEdge |
getListeOfEdge()
|
Generates a formatted list of edges in the graph.
Returns:
string
|
getListOfNode |
getListOfNode()
|
Generates a formatted list of nodes in the graph.
Returns:
string
|
OnDestroy |
OnDestroy()
|
Performs cleanup operations when the component is destroyed.
Returns:
void
|
matrixAdjancy |
matrixAdjancy()
|
Generates an adjacency matrix representation of the graph.
Returns:
any[]
|
isEdgeRemove |
isEdgeRemove(node1: any, node2: any, container: any)
|
Checks if an edge between two nodes exists and removes it from the graph if found.
Parameters :
Returns:
void
|
getDegreeNodes |
getDegreeNodes()
|
Get the degrees of all nodes in the graph.
Returns:
any[]
|
createGrapheFromAdjancyMatrix |
createGrapheFromAdjancyMatrix(elements: any, directed: boolean, weighted: boolean, container: any)
|
Create a graph from an adjacency matrix.
Parameters :
Returns:
void
|
rejeterGenerateGraphFromMatrixAdjancy |
rejeterGenerateGraphFromMatrixAdjancy(container: any, container2: any)
|
Reject the generation of a graph from an adjacency matrix.
Parameters :
Returns:
void
|
changeStyleGraphe |
changeStyleGraphe(typeGraphe: string)
|
Change the style of the graph based on its type.
Parameters :
Returns:
void
|
createGrapheFromListEdges |
createGrapheFromListEdges(container: any, container2: any)
|
Create a graph from a list of edges.
Parameters :
Returns:
void
|
rejeterGenerateGraphFromListEdges |
rejeterGenerateGraphFromListEdges(container: any, container2: any)
|
Reject the generation of a graph from a list of edges.
Parameters :
Returns:
void
|
getDensityOfGraphe |
getDensityOfGraphe()
|
Get the density of the graph.
Returns:
number
|
getNodeIds |
getNodeIds()
|
Get an array of node IDs in the graph.
Returns:
any[]
|
incidenceMatrix |
incidenceMatrix()
|
Calculate the incidence matrix of the graph.
Returns:
any[]
|
getEdgeIds |
getEdgeIds()
|
Get an array of edge IDs in the graph.
Returns:
any[]
|
removeEdge |
removeEdge(container: any, container2: any)
|
Remove an edge from the graph.
Parameters :
Returns:
void
|
RejeterRemoveEdge |
RejeterRemoveEdge(container: any, container2: any)
|
Reject the removal of an edge from the graph.
Parameters :
Returns:
void
|
removeNode |
removeNode(container: any, container2: any)
|
Remove a node from the graph.
Parameters :
Returns:
void
|
RejeterRemoveNode |
RejeterRemoveNode(container: any, container2: any)
|
Reject the removal of a node from the graph.
Parameters :
Returns:
void
|
getAdjancyList |
getAdjancyList()
|
Get the adjacency list of the graph.
Returns:
any[]
|
alphabets |
alphabets: |
Array of lowercase alphabet characters followed by '1'. |
Alphabets |
Alphabets: |
Array of lowercase alphabet characters followed by '2'. |
alphabets0 |
alphabets0: |
Array of lowercase alphabet characters. |
BACKGROUND_COLOR_NODE |
BACKGROUND_COLOR_NODE: |
Default value: black
|
The background color of nodes. |
Private BACKGROUND_COLOR_NODE_ALGO |
BACKGROUND_COLOR_NODE_ALGO: |
Default value: yellow
|
The background color of nodes in algorithm animations. |
COLOR_LINE_EDGE |
COLOR_LINE_EDGE: |
Default value: black
|
The color of edges. |
Private COLOR_LINE_EDGE_ALGO |
COLOR_LINE_EDGE_ALGO: |
Default value: yellow
|
The color of edges in algorithm animations. |
COLOR_NODE |
COLOR_NODE: |
Default value: white
|
The color of nodes. |
Private COLOR_NODE_ALGO |
COLOR_NODE_ALGO: |
Default value: black
|
The color of nodes in algorithm animations. |
counter |
counter: |
Default value: 0
|
A counter variable for tracking. |
Public cy |
cy: |
The Cytoscape instance used to manage and render the graph. |
DATA_EDGE_COLOR |
DATA_EDGE_COLOR: |
Default value: red
|
The color of data associated with edges. |
Private DATA_EDGE_COLOR_ALGO |
DATA_EDGE_COLOR_ALGO: |
Default value: blue
|
The color of data associated with edges in algorithm animations. |
numbersArray |
numbersArray: |
position |
position: |
The position of something (needs a description). |
POSITIONS |
POSITIONS: |
TARGET_ARROW_COLOR |
TARGET_ARROW_COLOR: |
Default value: blue
|
The color of the target arrow on edges. |
Private TARGET_ARROW_COLOR_ALGO |
TARGET_ARROW_COLOR_ALGO: |
Default value: black
|
The color of the target arrow in algorithm animations. |
Public typeGraphe |
typeGraphe: |
The type of the graph (e.g., Directed, Undirected, Weighted). |
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import * as cytoscape from 'cytoscape';
/**
* Represents a 2D position with x and y coordinates.
* @interface
*/
interface IPosition {
/**
* The x-coordinate of the position.
* @type {number}
*/
x: number;
/**
* The y-coordinate of the position.
* @type {number}
*/
y: number;
}
/**
* Service for managing and interacting with the Cytoscape graph.
*/
@Injectable({
providedIn: 'root'
})
export class GrapheService {
/** The Cytoscape instance used to manage and render the graph. */
public cy: any;
/** The type of the graph (e.g., Directed, Undirected, Weighted). */
public typeGraphe: string = "";
/** Array of lowercase alphabet characters. */
alphabets0: string[] = Array.from({ length: 26 }, (_, i) =>
String.fromCharCode(65 + i)
);
/** Array of lowercase alphabet characters followed by '1'. */
alphabets: string[] = this.alphabets0.concat(
this.alphabets0.map(letter => letter + '1')
);
/** Array of lowercase alphabet characters followed by '2'. */
Alphabets: string[] = this.alphabets.concat(
this.alphabets0.map(letter => letter + '2')
);
/** A counter variable for tracking. */
counter: number = 0;
numbersArray:number[] = Array.from(Array(200), (_, i) => i + 1);
/** The position of something (needs a description). */
position: any;
POSITIONS:IPosition[] =
[
{x: 241, y: 46},{x: 493, y: 47},{x: 261, y: 174},{x: 541, y: 174},
{x: 719, y: 41},{x: 736, y: 165},{x: 295, y: 314},{x: 530, y: 301},
{x: 772, y: 299},{x: 845, y: 43},{x: 885, y: 178},{x: 875, y: 276},
{x: 82, y: 5},{x: 106, y: 144},{x: 121.28128725008331, y: 263.51311061937736},
{x: 382, y: 87},{x: 617, y: 105},{x: 592, y: 217},{x: 323, y: 264},{x: 835, y: 319},
{x: 82, y: 24},{x: 98, y: 274},{x: 88, y: 173},{x: 380, y: 23},{x: 739, y: 73},
{x: 702, y: 211},{x: 577, y: 287},{x: 351, y: 323},{x: 350, y: 200},{x: 533, y: 108},
{x: 1221, y: 49},{x: 970, y: 133},{x: 1227, y: 227},{x: 1154, y: 353},{x: 919, y: 378},
{x: 887, y: 239},{x: 958, y: 48}
];
/** The color of nodes. */
COLOR_NODE: any = "white";
/** The background color of nodes. */
BACKGROUND_COLOR_NODE: any = "black";
/** The color of edges. */
COLOR_LINE_EDGE: any = "black";
/** The color of the target arrow on edges. */
TARGET_ARROW_COLOR: any = "blue";
/** The color of data associated with edges. */
DATA_EDGE_COLOR: any = "red";
//----------------------------------------------------------------
// Private Constants for Algorithm Colors
//----------------------------------------------------------------
/** The color of the target arrow in algorithm animations. */
private TARGET_ARROW_COLOR_ALGO: any = "black";
/** The color of edges in algorithm animations. */
private COLOR_LINE_EDGE_ALGO: any = "yellow";
/** The color of nodes in algorithm animations. */
private COLOR_NODE_ALGO: any = "black";
/** The background color of nodes in algorithm animations. */
private BACKGROUND_COLOR_NODE_ALGO: any = "yellow";
/** The color of data associated with edges in algorithm animations. */
private DATA_EDGE_COLOR_ALGO: any = "blue";
//----------------------------------------------------------------
/**
* Constructor for the GrapheService.
*
* @param {TranslateService} translate - The translation service for internationalization.
*/
constructor(private translate: TranslateService) {
// Constructor logic here
}
/**
* Changes the node enumeration.
* @param {ScreenboxComponent} container - The container object.
*/
changeNodeEnum(container:any):void{
if(this.typeGraphe!=""){
//DRY
container.selectedNode=[];
if(container.algorithm!="degrenodes"&&container.algorithm!="matrixAdjancy"&&container.algorithm!="matrixIncident"&&container.algorithm!="listeAdjancy"){
container.algorithm="";
}
container.saveUpload = "";
container.remove="";
container.containerHeight=50;
this.resetColors();
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode=container.el.nativeElement.querySelector('.formAddNode');
formAddNode.style.display="none";
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
container.changeSelect="";
this.position=null;
//
}else{
container.message=this.translate.instant("screenbox.msg23");
container.nodeName="numerique";
}
}
/**
* Changes a setting or performs an action based on the selected option(chnage colors & sizes).
*
* @param {ScreenboxComponent} container - The container object.
*/
changeChanges(container:any){
//DRY
container.selectedNode=[];
if(container.algorithm!="degrenodes"&&container.algorithm!="matrixAdjancy"&&container.algorithm!="matrixIncident"&&container.algorithm!="listeAdjancy"){
container.algorithm="";
}
container.saveUpload = "";
container.remove="";
container.containerHeight=50;
container.buttonClicked="";
container.restoreArray=[];
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
formRemoveNode.style.display="none";
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
this.position="";
this.resetColors();
//
if(container.changeSelect=="changeColorNodes" || container.changeSelect=="changeColorEdges" || container.changeSelect=="changeColorNodesAlgo" || container.changeSelect=="changeColorEdgesAlgo" || container.changeSelect=="changeColorScreen"){
if(container.changeSelect=="changeColorNodes"){
container.message=this.translate.instant("grapheS.msg26")
}else if(container.changeSelect=="changeColorEdges") {
container.message=this.translate.instant("grapheS.msg27")
}if(container.changeSelect=="changeColorNodesAlgo"){
container.message=this.translate.instant("grapheS.msg34")
}else if(container.changeSelect=="changeColorEdgesAlgo") {
container.message=this.translate.instant("grapheS.msg35")
}else if(container.changeSelect=="changeColorScreen") {
container.message=this.translate.instant("grapheS.msg36")
}
formChangeColor.style.display="block";
}else if(container.changeSelect=="addGrapheFromMatrix"){
const screen=container.el.nativeElement.querySelector('.screen');
const buttonManupilation=container.el.nativeElement.querySelector('.buttonManupilation');
const addGrapheWithMatrix=container.el.nativeElement.querySelector('.addGrapheWithMatrix');
const addGrapheFromEdgesList=container.el.nativeElement.querySelector('.addGrapheFromEdgesList');
screen.style.display="none";
addGrapheFromEdgesList.style.display="none";
buttonManupilation.style.display="none";
addGrapheWithMatrix.style.display="block";
container.message=this.translate.instant("grapheFromMatrix.msg6");
}else if(container.changeSelect=="addGrapheFromEdgesList"){
const screen=container.el.nativeElement.querySelector('.screen');
const buttonManupilation=container.el.nativeElement.querySelector('.buttonManupilation');
const addGrapheWithMatrix=container.el.nativeElement.querySelector('.addGrapheWithMatrix');
const addGrapheFromEdgesList=container.el.nativeElement.querySelector('.addGrapheFromEdgesList');
screen.style.display="none";
addGrapheFromEdgesList.style.display="block";
buttonManupilation.style.display="none";
addGrapheWithMatrix.style.display="none";
container.message=this.translate.instant("grapheFromMatrix.msg6");///////////////////////Chnge this Message
}else if(container.changeSelect=="changeSizeScreen"){
container.message=this.translate.instant("grapheS.msg39")
formAChangeSizeScreen.style.display="block";
}else if(container.changeSelect=="changeIdNode"){
if(this.typeGraphe && this.cy.nodes().length){
container.message=this.translate.instant("grapheS.msg24")
}else{
if(this.typeGraphe==""){
container.message=this.translate.instant("screenbox.msg23");
}else if(this.cy.nodes().length==0){
container.message=this.translate.instant("grapheS.msg41");
}
container.changeSelect="";
}
}
}
/**
* Changes the type of graph (e.g., Directed, Undirected, Weighted).
*
* @param {ScreenboxComponent} container - The container object.
*/
changeTypeGraphe(container:any):void{
//DRY
container.selectedNode=[];
container.buttonClicked="";
if(container.algorithm!="degrenodes"&&container.algorithm!="matrixAdjancy"&&container.algorithm!="matrixIncident"&&container.algorithm!="listeAdjancy"){
container.algorithm="";
}
container.saveUpload = "";
container.remove="";
container.changeSelect="";
this.Alphabets=this.alphabets.concat(this.alphabets0.map(letter => letter + '2'));
this.counter=0;
//
this.typeGraphe=container.typeGraphe;
this.resetColors();
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
formRemoveNode.style.display="none";
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
this.position="";
container.containerHeight=50;
this.cy.remove(this.cy.elements());
container.message=this.translate.instant("grapheS.msg1");
if (this.typeGraphe === "Directed Weighted") {
this.changeStyleGraphe(this.typeGraphe);
container.message+=this.translate.instant("grapheS.msg18")+" "+this.translate.instant("grapheS.msg20");
}else if (this.typeGraphe === "Directed Unweighted") {
this.changeStyleGraphe(this.typeGraphe);
container.message+=this.translate.instant("grapheS.msg18")+" "+this.translate.instant("grapheS.msg21");
} else if (this.typeGraphe === "Undirected Weighted") {
this.changeStyleGraphe(this.typeGraphe);
container.message+=this.translate.instant("grapheS.msg19")+" "+this.translate.instant("grapheS.msg20");;
} else if (this.typeGraphe === "Undirected Unweighted") {
this.changeStyleGraphe(this.typeGraphe);
container.message+=this.translate.instant("grapheS.msg19")+" "+this.translate.instant("grapheS.msg21");;
}
}
/**
* Handles button clicks and performs actions based on the selected option.
*
* @param {ScreenboxComponent} container - The container object.
*/
onChangeButtonClicked(container:any):void{
if(this.typeGraphe!=""){
//DRY
container.changeSelect="";
container.selectedNode=[];
if(container.algorithm!="degrenodes"&&container.algorithm!="matrixAdjancy"&&container.algorithm!="matrixIncident"&&container.algorithm!="listeAdjancy"){
container.algorithm="";
}
container.saveUpload = "";
container.remove="";
container.containerHeight=50;
//
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
formRemoveNode.style.display="none";
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
this.position="";
if(container.buttonClicked=="default"){
container.message=this.translate.instant("grapheS.msg2");
this.cy.fit();
}else if(container.buttonClicked=="addVertices"){
container.message=this.translate.instant("grapheS.msg3");
}else if(container.buttonClicked=="addEdges"){
if(this.cy.nodes().length){
container.message=this.translate.instant("grapheS.msg4");
}else{
container.message=this.translate.instant("grapheS.msg41");
container.buttonClicked="";
}
}else if(container.buttonClicked=="restore"){
if(!container.restoreArray.length){
container.message=this.translate.instant("grapheS.msg60")
}else{
this.Alphabets=this.alphabets.concat(this.alphabets0.map(letter => letter + '2'));
this.counter=0;
this.restoreGraphe(container);
}
}
}else{
container.message=this.translate.instant("screenbox.msg23");
container.buttonClicked="";
}
}
/**
* Changes the size of the screen.
*
* @param {ScreenboxComponent} container - The container object.
* @param {ChangeSizeScreenComponent} container2 - Another container object.
*/
changeSizeScreen(container:any,container2:any):void{
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const screen=container.el.nativeElement.querySelector('.scr');
if(container2.height){
screen.style.height =container2.height+'px';
localStorage.setItem('screenHeight', screen.style.height.toString());
container.message=this.translate.instant("grapheS.msg37",{height:container2.height});
}else{
container2.height=screen.style.height =400+'px';//default value
localStorage.setItem('screenHeight', screen.style.height.toString());
container.message=this.translate.instant("grapheS.msg38");
}
container.changeSelect="";
formAChangeSizeScreen.style.display="none";
}
/**
* Rejects the changes made to the screen size and restores the previous size.
*
* @param {ScreenboxComponent} container - The container object.
* @param {ChangeSizeScreenComponent} container2 - Another container object.
*/
RejeterChangeSizeScreen(container:any,container2:any):void{
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const screen=container.el.nativeElement.querySelector('.scr');
container2.height=screen.style.height.slice(0,-2)||400;
container.changeSelect="";
formAChangeSizeScreen.style.display="none";
container.message=this.translate.instant("grapheS.msg40")
}
/**
* Adds a weighted edge between selected nodes.
*
* @param {ScreenboxComponent} container - The container object.
* @param {AddWeightedEdgeComponent} container2 - Another container object.
*/
addWeightedEdge(container:any,container2:any):void{
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
if(container.weight!=0){
this.isEdgeRemove(container.selectedNode[0],container.selectedNode[1],container);
let data={
source:container.selectedNode[0],
target:container.selectedNode[1],
weight:container.weight
}
let edge=this.cy.add({
data
});
//NEW CODE
let elem={status:"add",element:edge};
container.restoreArray.push(elem);
//
formAddEdge.style.display='none';
container.message=this.translate.instant("grapheS.msg7",{selectedNode1:container.selectedNode[0],selectedNode2:container.selectedNode[1],weight:container.weight});
container.weight=null;
container2.weightForm=null;
container.selectedNode=[];
this.resetColors();
}else{
container.message=this.translate.instant("grapheS.msg8")
}
}
/**
* Rejects the addition of a weighted edge and resets the form.
*
* @param {ScreenboxComponent} container - The container object.
* @param {AddWeightedEdgeComponent} container2 - Another container object.
*/
RejeterAddEdgeWeighted(container:any,container2:any):void{
container.selectedNode=[];
container.weight=null;
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
formAddEdge.style.display='none';
this.resetColors();
container.message=this.translate.instant("grapheS.msg9");
container2.weightForm=null;
}
/**
* Changes the ID of a node in the graph.
*
* @param {ScreenboxComponent} container - The container object.
* @param {ChangeIdNodeComponent} container2 - Another container object.
*/
changeNodeId(container:any,container2:any):void{
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
let idExists=false;
this.cy.nodes().forEach((node:any)=>{
if(node.data('id')==container2.newNodeId){
container.message=this.translate.instant("grapheS.msg23");
idExists=true;
}
})
if(idExists==false){
let node = this.cy.getElementById(container.selectedNode[0]);
let elem:any;
let nodeNew=this.cy.add({ group: 'nodes', data: { id: container2.newNodeId}, position: { x: node.position('x'),y: node.position('y'),}});
this.cy.edges().forEach((edge:any)=>{
if(this.typeGraphe.split(" ")[1]=="Unweighted"){
if (edge.source().id() === node.id()) {
elem={status:"remove",element:edge};
container.restoreArray.push(elem);
this.cy.add({
group: 'edges',
data: {
source: nodeNew.id(),
target: edge.target().id(),
}
});
} else if (edge.target().id() === node.id()) {
elem={status:"remove",element:edge};
container.restoreArray.push(elem);
this.cy.add({
group: 'edges',
data: {
source: edge.source().id(),
target: nodeNew.id(),
}
});
}
}else if(this.typeGraphe.split(" ")[1]=="Weighted"){
if (edge.source().id() === node.id()) {
elem={status:"remove",element:edge};
container.restoreArray.push(elem);
this.cy.add({
group: 'edges',
data: {
source: nodeNew.id(),
target: edge.target().id(),
weight: edge.data('weight')
}
});
} else if (edge.target().id() === node.id()) {
elem={status:"remove",element:edge};
container.restoreArray.push(elem);
this.cy.add({
group: 'edges',
data: {
source: edge.source().id(),
target: nodeNew.id(),
weight: edge.data('weight')
}
});
}
}
})
//Restore
elem={status:"remove",element:node};
container.restoreArray.push(elem);
this.cy.remove(node);
elem={status:"add",element:nodeNew};
container.restoreArray.push(elem);
//Log message
container.message=this.translate.instant("grapheS.msg25",{last:container.selectedNode[0],new:container2.newNodeId});
//Initialiation
container.nodeIdChanged=container2.newNodeId;
formChangeNodeId.style.display = 'none';
container.selectedNode=[];
container2.newNodeId=null;
this.resetColors();
}
}
/**
* Rejects the node ID change and resets the form.
*
* @param {ScreenboxComponent} container - The container object.
* @param {ChangeIdNodeComponent} container2 - Another container object.
*/
RejeterChangeNodeId(container:any,container2:any):void{
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
formChangeNodeId.style.display = 'none';
container.selectedNode=[];
container2.newNodeId=null;
this.resetColors();
container.message=this.translate.instant("grapheS.msg22");
}
/**
* Changes the colors of nodes, edges, or the screen based on user selection.
*
* @param {ScreenboxComponent} container - The container object.
* @param {ChangeColorComponent} container2 - Another container object.
*/
changeColor(container:any,container2:any){
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const screen=container.el.nativeElement.querySelector('.scr');
if(container.changeSelect=="changeColorNodes"){
this.COLOR_NODE=container2.color;
this.BACKGROUND_COLOR_NODE=container2.bgColor;
this.cy.style()
.selector('node')
.style({
'background-color': this.BACKGROUND_COLOR_NODE,
'color': this.COLOR_NODE
})
.update();
this.changeColorNodes();
container.message=this.translate.instant("grapheS.msg30")
}else if(container.changeSelect=="changeColorEdges"){
this.COLOR_LINE_EDGE=container2.bgColor;
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'line-color': this.COLOR_LINE_EDGE,
})
.update();
if(this.typeGraphe.split(" ")[1]=="Weighted"){
this.DATA_EDGE_COLOR=container2.color;
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'color': this.DATA_EDGE_COLOR
})
.update();
}
if(this.typeGraphe.split(" ")[0]=="Directed"){
this.TARGET_ARROW_COLOR=container2.fColor;
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'target-arrow-color': this.TARGET_ARROW_COLOR,
})
.update();
}
this.changeColorEdges();
container.message=this.translate.instant("grapheS.msg31")
}else if(container.changeSelect=="changeColorNodesAlgo"){
this.COLOR_NODE_ALGO=container2.color;
this.BACKGROUND_COLOR_NODE_ALGO=container2.bgColor;
container.message=this.translate.instant("grapheS.msg32")
}else if(container.changeSelect=="changeColorEdgesAlgo"){
this.COLOR_LINE_EDGE_ALGO=container2.bgColor;
if(this.typeGraphe.split(" ")[1]=="Weighted"){
this.DATA_EDGE_COLOR_ALGO=container2.color;
}
if(this.typeGraphe.split(" ")[0]=="Directed"){
this.TARGET_ARROW_COLOR_ALGO=container2.fColor;
}
container.message=this.translate.instant("grapheS.msg33")
}else if(container.changeSelect=="changeColorScreen"){
screen.style.backgroundColor=container2.bgColor;
localStorage.setItem("ScreenColor",screen.style.backgroundColor.toString());
}
container.changeSelect="";
formChangeColor.style.display="none";
}
/**
* Rejects the color changes and resets the form.
*
* @param {ScreenboxComponent} container - The container object.
*/
RejeterChangeColor(container:any):void {
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
if(container.changeSelect=="changeColorNodes"){
container.message=this.translate.instant("grapheS.msg28")
}else{
container.message=this.translate.instant("grapheS.msg29")
}
container.changeSelect="";
formChangeColor.style.display="none";
}
/**
* Adds a new node to the graph if it doesn't already exist.
*
* @param {ScreenboxComponent} container - The container object.
* @param {AddNodeComponent} container2 - Another container object containing the new node ID.
*/
addNode(container:any,container2:any):void{
const formAddNode=container.el.nativeElement.querySelector('.formAddNode');
let exist:boolean=false;
if(container2.nodeId){
this.cy.nodes().forEach((node:any)=>{
if(node.data('id')==container2.nodeId){
container.message=this.translate.instant('grapheS.msg23');
exist=true;
}
})
if(!exist){
let node=this.cy.add({ group: 'nodes', data: { id: container2.nodeId}, position: this.position });
let elem={status:"add",element:node};
container.restoreArray.push(elem);
container.message=this.translate.instant("grapheS.msg10",{nodeId:node.data('id')});
formAddNode.style.display="none";
this.position="";
container2.nodeId=null;
}
}else{
container.message=this.translate.instant('screenbox.msg41');
}
}
/**
* Rejects the addition of a new node and resets the form.
*
* @param {ScreenboxComponent} container - The container object.
* @param {AddNodeComponent} container2 - Another container object containing the new node ID.
*/
RejeterAddNode(container:any,container2:any):void{
const formAddNode=container.el.nativeElement.querySelector('.formAddNode');
formAddNode.style.display="none";
this.position="";
container2.nodeId=null;
}
/**
* Listens for a tap event on the screen background and adds a new node when conditions are met.
*
* @param {ScreenboxComponent} container - The container object.
*/
OnScreenTap(container:any):void{
this.cy.on('tap', (evt:any)=> {
if (evt.target === this.cy && container.buttonClicked==="addVertices" && this.typeGraphe!="") {
//Use all possile name of node , case of node deleted id
this.Alphabets=this.alphabets.concat(this.alphabets0.map(letter => letter + '2'));
this.counter=0;
//
let pos = evt.position || evt.cyPosition;
let node:any;
const formAddNode=container.el.nativeElement.querySelector('.formAddNode');
if(container.nodeName=="numerique"){
formAddNode.style.display="none";
this.cy.nodes().forEach((node:any)=>{
if(node.data('id')==this.counter+1){
++this.counter;
}
})
node=this.cy.add({ group: 'nodes', data: { id: ++this.counter}, position: pos });
}else if(container.nodeName=="alphabic"){
formAddNode.style.display="none";
this.cy.nodes().forEach((node:any)=>{
if(node.data('id')==this.Alphabets[0]){
this.Alphabets.shift();
}
})
node=this.cy.add({ group: 'nodes', data: { id: this.Alphabets.shift()}, position: pos });
}else if(container.nodeName=="customText"){
formAddNode.style.display="block";
this.position=pos;
}
if(container.nodeName!="customText"){
let elem={status:"add",element:node};
container.restoreArray.push(elem);
container.message=this.translate.instant("grapheS.msg10",{nodeId:node.data('id')});
}
}
});
}
/**
* Listens for a tap event on edges and removes edges when the 'remove edges' or 'remove all' action is selected.
*
* @param {ScreenboxComponent} container - The container object.
*/
OnEdgeTap(container:any):void{
this.cy.on('tap', 'edge', (evt:any)=> {
var edge = evt.target;
if(container.remove=="remove edges" || container.remove=="remove all"){
this.resetColors();
container.message=this.translate.instant("grapheS.msg11",{sourceId:edge.source().id(),targetId:edge.target().id()});
edge.remove();
//NEW CODE
let elem={status:"remove",element:edge};
container.restoreArray.push(elem);
//
}
});
}
/**
* Listens for a tap event on nodes and handles various actions based on the container state.
*
* @param {ScreenboxComponent} container - The container object.
*/
OnNodeTap(container:any):void{
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
this.cy.on('tap', 'node', (evt:any)=> {
var node = evt.target;
if(container.remove=="remove nodes" || container.remove=="remove all"){
this.resetColors();
node.remove();
//NEW CODE
let elem={status:"remove",element:node};
container.restoreArray.push(elem);
//
container.message=this.translate.instant("grapheS.msg12",{nodeId:node.data('id')});
}else if(container.changeSelect==="changeIdNode"){
if(container.selectedNode.length!=0){
container.selectedNode=[];
}
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.selectedNode.push(node.data('id'));
formChangeNodeId.style.display = 'block';
}else if(container.buttonClicked==="addEdges"){
this.resetColors();
if(container.selectedNode.length<1){
container.message=this.translate.instant("grapheS.msg13",{nodeId:node.data('id')});
container.selectedNode.push(node.data('id'));
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
}else if(container.selectedNode.length==1){
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.selectedNode.push(node.data('id'));
if(this.typeGraphe=="Directed Weighted" || this.typeGraphe=="Undirected Weighted"){
formAddEdge.style.display = 'block';
container.message=this.translate.instant("grapheS.msg14",{selectedNode1:container.selectedNode[0],selectedNode2:container.selectedNode[1]});
}else{
this.isEdgeRemove(container.selectedNode[0],container.selectedNode[1],container);
let edge=this.cy.add({
data: {
source: container.selectedNode[0],
target: container.selectedNode[1],
}
});
//NEW CODE
let elem={status:"add",element:edge};
container.restoreArray.push(elem);
container.message=this.translate.instant("grapheS.msg15",{selectedNode1:container.selectedNode[0],selectedNode2:container.selectedNode[1]});
container.selectedNode=[];
this.resetColors();
formAddEdge.style.display='none';
}
}
}else if(container.algorithm=="bfs"){
this.resetColors();
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.algoS.bfsAnimation(node.data('id'),container);
}else if(container.algorithm=="dfs"){
this.resetColors();
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.algoS.dfsAnimation(node.data('id'),container);
}else if(container.algorithm=="dijkstra"){
this.resetColors();
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.algoS.dijkstraAlgorithm(node.data('id'),container);
}else if(container.algorithm=="dijkstraAB"){
this.resetColors();
if(container.selectedNode.length<1){
container.message=this.translate.instant("grapheS.msg16",{nodeId:node.data('id')});
container.selectedNode.push(node.data('id'));
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
}else if(container.selectedNode.length==1){
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.selectedNode.push(node.data('id'));
container.algoS.dijkstraAnimation(container.selectedNode[0],container.selectedNode[1],container);
container.selectedNode=[];
}
}else if(container.algorithm=="bellmanFordAB"){
this.resetColors();
if(container.selectedNode.length<1){
container.message=this.translate.instant("grapheS.msg42",{nodeId:node.data('id')});
container.selectedNode.push(node.data('id'));
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
}else if(container.selectedNode.length==1){
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.selectedNode.push(node.data('id'));
container.algoS.bellmanFordAnimation(container.selectedNode[0],container.selectedNode[1],container);
container.selectedNode=[];
}
}else if(container.algorithm=="bellmanFord"){
this.resetColors();
this.changeColorNode(node, this.BACKGROUND_COLOR_NODE_ALGO,this.COLOR_NODE_ALGO);
container.algoS.bellmanFordAlgorithm(node.data('id'),container);
}
});
this.cy.emit("tap");
}
/**
* Initializes the graph using Cytoscape and sets up the initial style and layout.
*
* @param {ScreenboxComponent} Container - The container element for the graph.
*/
OnInit(Container:any):void{
const screen=Container.el.nativeElement.querySelector('.scr');
if(localStorage.getItem('screenHeight')){
screen.style.height=localStorage.getItem('screenHeight');
}else{
localStorage.setItem('screenHeight', screen.style.height.toString());
}
if(localStorage.getItem("ScreenColor")){
screen.style.backgroundColor=localStorage.getItem("ScreenColor");
}else{
localStorage.setItem("ScreenColor",screen.style.backgroundColor.toString());
}
const container = Container.el.nativeElement.querySelector('.scr');
this.cy = cytoscape({
container,
elements: [
],
style: [
{
selector: 'node',
style: {
'background-color': this.BACKGROUND_COLOR_NODE,
'color': this.COLOR_NODE,
'label': 'data(id)',
'width': '50px',
'height': '50px',
'text-valign': 'center',
'text-halign': 'center'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': this.COLOR_LINE_EDGE,
'target-arrow-color': this.TARGET_ARROW_COLOR,
'target-arrow-shape': 'triangle',
'color': this.DATA_EDGE_COLOR,
}
}
],
layout: {
name: 'preset'
}
});
}
/**
* Resets the visual styles of nodes and edges in the graph to their default colors.
*/
resetColors(): void {
setTimeout(() => {
this.cy.nodes().style('background-color', this.BACKGROUND_COLOR_NODE);
this.cy.nodes().style('color', this.COLOR_NODE);
this.cy.edges().style('line-color', this.COLOR_LINE_EDGE);
this.cy.edges().style('color', this.DATA_EDGE_COLOR);
this.cy.edges().style('target-arrow-color', this.TARGET_ARROW_COLOR);
},10)
}
/**
* Changes the background color and text color of a given node.
*
* @param {any} node - The node to change the colors of.
* @param {string} bgcolor - The new background color.
* @param {string} color - The new text color.
* @param {number} time - Optional. Time delay before applying the color change.
*/
changeColorNode(node:any,bgcolor:string,color:string,time:number=10): void {
setTimeout(() => {
node.style('background-color',bgcolor);
node.style('color',color);
},time);
}
/**
* Changes the visual styles of a given edge, including line color, data color, and target arrow color.
*
* @param {any} edge - The edge to change the visual styles of.
* @param {string} color - The new data color (used for weighted graphs).
* @param {string} lineEdgeColor - The new line color.
* @param {string} targetArrowColor - The new target arrow color (used for directed graphs).
* @param {number} time - Optional. Time delay before applying the color change.
*/
changeColorEdge(edge:any,color:string,lineEdgeColor:string,targetArrowColor:string,time:number=10): void {
setTimeout(() => {
edge.style('line-color',lineEdgeColor);
if(this.typeGraphe.split(" ")[1]=="Weighted"){
edge.style('color',color);
}
if(this.typeGraphe.split(" ")[0]=="Directed"){
edge.style('target-arrow-color', targetArrowColor);
}
},time)
}
/**
* Resets the visual styles of all nodes in the graph to their default colors.
*/
changeColorNodes(): void {
setTimeout(() => {
this.cy.nodes().forEach((node:any)=>{
node.style('background-color',this.BACKGROUND_COLOR_NODE);
node.style('color',this.COLOR_NODE);
})
},10)
}
/**
* Resets the visual styles of all edges in the graph to their default colors.
*/
changeColorEdges(): void {
setTimeout(() => {
this.cy.edges().forEach((edge:any)=>{
edge.style('line-color',this.COLOR_LINE_EDGE);
if(this.typeGraphe.split(" ")[1]=="Weighted"){
edge.style('color',this.DATA_EDGE_COLOR);
}
if(this.typeGraphe.split(" ")[0]=="Directed"){
edge.style('target-arrow-color', this.TARGET_ARROW_COLOR);
}
})
},10)
}
/**
* Handles actions related to removing elements from the graph and resetting the graph properties.
*
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
*/
onRemoveChange(container:any):void{
if(container.typeGraphe!=""){
//DRY
container.changeSelect="";
container.buttonClicked="";
container.containerHeight=50;
container.selectedNode=[];
container.saveUpload="";
if(container.algorithm!="degrenodes"&&container.algorithm!="matrixAdjancy"&&container.algorithm!="matrixIncident"&&container.algorithm!="listeAdjancy"){
container.algorithm="";
}
//
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
formRemoveNode.style.display="none";
container.grapheS.position="";
this.resetColors();
if(container.remove=="reset graphe"){
this.Alphabets=this.alphabets.concat(this.alphabets0.map(letter => letter + '2'));
this.counter=0;
this.cy.remove(this.cy.elements());
container.message=this.translate.instant("grapheS.msg17");
}else if(container.remove=="remove edges"){
container.message=this.translate.instant("grapheS.msg44");
}else if(container.remove=="remove nodes"){
container.message=this.translate.instant("grapheS.msg43");
}else if(container.remove=="remove all"){
container.message=this.translate.instant("grapheS.msg45");
}else if(container.remove=="special edges"){
formRemoveEdge.style.display="block";
container.message=this.translate.instant("grapheS.msg49");
}else if(container.remove=="special nodes"){
formRemoveNode.style.display="block";
container.message=this.translate.instant("grapheS.msg50");
}
}else{
container.message=this.translate.instant("screenbox.msg23");
container.remove="";
}
}
/**
* Searches for an edge between two specified nodes and changes its line color.
*
* @param {any} container - The container object containing graph-related properties.
* @param {string} source - The source node ID.
* @param {string} target - The target node ID.
* @param {string} lineColor - The new line color for the edge.
*/
searcheEdgeChnageBC(container:any,source:string,target:string,color:string,lineEdgeColor:string,targetArrowColor:string,time:number=10):void{
const edges = this.cy.elements('edge'); // Select only edges
edges.forEach((edge:any) => {
const edgeSourceId = edge.source().id();
const edgeTargetId = edge.target().id();
if (edgeSourceId === source && edgeTargetId === target) {
this.changeColorEdge(edge,color,lineEdgeColor,targetArrowColor,time);
}
});
}
/**
* Restores the graph by adding or removing elements based on the restoreArray.
*
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
*/
restoreGraphe(container:any):void{
if(container.restoreArray.length!=0){
let elem=container.restoreArray.pop();
let element=elem.element;
if(elem.status=="add"){
if(element.isNode() || element.isEdge()){
element.remove();
if(element.isNode()){
container.message=this.translate.instant("grapheS.msg55",{nodeId:element.data('id')});
}else{
if(this.typeGraphe.split(" ")[1]=="Weighted"){
container.message=this.translate.instant("grapheS.msg58",{source:element.source().id(),target:element.target().id(),weight:element.data("weight")});
}else{
container.message=this.translate.instant("grapheS.msg59",{source:element.source().id(),target:element.target().id()});
}
}
}
}else{
if(element.isNode() || element.isEdge()){
this.cy.add(element);
if(element.isNode()){
container.message=this.translate.instant("grapheS.msg54",{nodeId:element.data('id')});
}else{
if(this.typeGraphe.split(" ")[1]=="Weighted"){
container.message=this.translate.instant("grapheS.msg57",{source:element.source().id(),target:element.target().id(),weight:element.data("weight")});
}else{
container.message=this.translate.instant("grapheS.msg56",{source:element.source().id(),target:element.target().id()});
}
}
}
}
}
}
/**
* Generates a formatted list of edges in the graph.
*
* @returns {string} - A formatted string representing the list of edges.
*/
getListeOfEdge():string {
let listOfEdge:string="";
let i:number=0;
this.cy?.edges().forEach((edge:any) => {
i++;
let element:string=`(${this.translate.instant("info.s")}: ${edge.source().id()},${this.translate.instant("info.t")}: ${edge.target().id()}`;
if(this.typeGraphe.split(" ")[1]=="Weighted"){
element+=`,${this.translate.instant("info.w")}: ${edge.data('weight')}) `;
}else{
element+=") ";
}
if(i!=this.cy?.edges().length){
element+=" --- ";
}
listOfEdge+=element;
});
return listOfEdge;
}
/**
* Generates a formatted list of nodes in the graph.
*
* @returns {string} - A formatted string representing the list of nodes.
*/
getListOfNode():string{
let listOfNode:string="";
let i:number=0;
this.cy?.nodes().forEach((node:any) => {
i++;
listOfNode+=`${node.data("id")} `;
if(i!=this.cy?.nodes().length){
listOfNode+=" --- ";
}
});
return listOfNode;
}
/**
* Performs cleanup operations when the component is destroyed.
* Destroys the CytoScape instance.
*/
OnDestroy():void{
this.cy.destroy();
}
/**
* Generates an adjacency matrix representation of the graph.
*
* @returns {Array<any>} - A 2D array representing the adjacency matrix.
*/
matrixAdjancy():Array<any>{
let adjacencyMatrix:Array<any>=[];
const nodes = this.cy.nodes();
const numNodes = nodes.length;
for (let i = 0; i < numNodes; i++) {
const row = [];
for (let j = 0; j < numNodes; j++) {
let edge:any ;
if(this.typeGraphe.split(' ')[0]=="Directed"){
edge = nodes[i].edgesTo(nodes[j]);
row.push(edge.length ? (edge.data('weight')?edge.data('weight'):1): 0);
}else{
edge = nodes[i].edgesWith(nodes[j]);
row.push(edge.length ? (edge.data('weight')?edge.data('weight'):1): 0);
}
}
adjacencyMatrix.push(row);
}
return adjacencyMatrix;
}
/**
* Checks if an edge between two nodes exists and removes it from the graph if found.
*
* @param {any} node1 - The ID of the first node.
* @param {any} node2 - The ID of the second node.
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
*/
isEdgeRemove(node1:any,node2:any,container:any):void{
this.cy.edges().forEach((edge:any)=>{
if((edge.source().id()==node1 && edge.target().id()==node2)/* || (edge.source().id()==node2 && edge.target().id()==node1)*/){
this.cy.remove(edge);
let elem={status:"remove",element:edge};
container.restoreArray.push(elem);
}
})
}
/**
* Get the degrees of all nodes in the graph.
*
* @returns {Array<any>} - An array of objects containing node IDs, in-degrees, out-degrees, and degrees.
*/
getDegreeNodes():Array<any>{
let nodesDegre:Array<any> = [];
let obj:any;
if(this.cy.nodes().length){
this.cy.nodes().forEach((node:any) => {
obj={
id:node.data('id'),
indegree:node.indegree(),
outdegree:node.outdegree(),
degree:node.degree()
};
nodesDegre.push(obj);
})
}
return nodesDegre;
}
/**
* Create a graph from an adjacency matrix.
*
* @param {any} elements - Graph elements, including nodes and edges.
* @param {boolean} directed - Whether the graph is directed.
* @param {boolean} weighted - Whether the graph is weighted.
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
*/
createGrapheFromAdjancyMatrix(elements:any,directed:boolean,weighted:boolean,container:any):void{
this.cy.elements().remove();
if(directed && weighted){
container.typeGraphe=this.typeGraphe="Directed Weighted";
this.changeStyleGraphe(this.typeGraphe);
}else if (directed) {
container.typeGraphe=this.typeGraphe="Directed Unweighted";
this.changeStyleGraphe(this.typeGraphe);
}else if (weighted) {
container.typeGraphe=this.typeGraphe="Undirected Weighted";
this.changeStyleGraphe(this.typeGraphe);
}else{
container.typeGraphe=this.typeGraphe="Undirected Unweighted";
this.changeStyleGraphe(this.typeGraphe);
}
let i:number=0;
elements.forEach((element:any)=>{
if(element.id){
const pos = this.POSITIONS[i++];
this.cy.add({ group: 'nodes', data: { id: element.id}, position: pos });
}else{
if(weighted){
if(element.source && element.target && element.weight){
if(directed){
this.cy.add({data:{
source: element.source,
target: element.target,
weight: element.weight
}
});
}else{
let exist:boolean=false;
this.cy.edges().forEach((edge:any)=>{
if(edge.source().id()==element.target && edge.target().id()==element.source && edge.data("weight")==element.weight){
exist=true;
}
})
if(!exist){
this.cy.add({data:{
source: element.source,
target: element.target,
weight: element.weight
}
});
}
}
}
}else{
if(element.source && element.target){
if(directed){
this.cy.add({
data: {
source: element.source,
target: element.target,
}
});
}else{
let exist:boolean=false;
this.cy.edges().forEach((edge:any)=>{
if(edge.source().id()==element.target && edge.target().id()==element.source){
exist=true;
}
})
if(!exist){
this.cy.add({
data: {
source: element.source,
target: element.target,
}
});
}
}
}
}
}
});
}
/**
* Reject the generation of a graph from an adjacency matrix.
*
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
* @param {GrapheFromMatrixAdjaComponent} container2 - Another container object.
*/
rejeterGenerateGraphFromMatrixAdjancy(container:any,container2:any):void {
const screen=container.el.nativeElement.querySelector('.screen');
const buttonManupilation=container.el.nativeElement.querySelector('.buttonManupilation');
const addGrapheWithMatrix=container.el.nativeElement.querySelector('.addGrapheWithMatrix');
screen.style.display="block";
buttonManupilation.style.display="block";
addGrapheWithMatrix.style.display="none";
container2.matrixText="";
container.message=this.translate.instant("grapheFromMatrix.msg5");
container.changeSelect="";
}
/**
* Change the style of the graph based on its type.
*
* @param {string} typeGraphe - The type of the graph (e.g., Directed Weighted).
*/
changeStyleGraphe(typeGraphe:string):void{
if(typeGraphe=="Directed Weighted"){
this.cy.style()
.selector('edge')
.style({
'width': 4,
'line-color': this.COLOR_LINE_EDGE,
'target-arrow-color': this.TARGET_ARROW_COLOR,
'target-arrow-shape': 'triangle',
'color': this.DATA_EDGE_COLOR,
'curve-style': 'bezier',
'label': "data(weight)",
'text-margin-y': -12
})
.update();
}else if(typeGraphe=="Directed Unweighted"){
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'width': 4,
'line-color': this.COLOR_LINE_EDGE,
'target-arrow-color': this.TARGET_ARROW_COLOR,
'target-arrow-shape': 'triangle',
'color': this.DATA_EDGE_COLOR,
'curve-style': 'bezier',
'label': ""
})
.update();
}else if(typeGraphe=="Undirected Weighted"){
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'width': 4,
'line-color': this.COLOR_LINE_EDGE,
'target-arrow-color': this.TARGET_ARROW_COLOR,
'target-arrow-shape': 'triangle',
'color': this.DATA_EDGE_COLOR,
'curve-style': 'haystack',
'label': "data(weight)",
'text-margin-y': -12
})
.update();
}else if(typeGraphe=="Undirected Unweighted"){
this.cy.style()
.selector('edge') // Apply the style to all edges
.style({
'width': 4,
'line-color': this.COLOR_LINE_EDGE,
'target-arrow-color': this.TARGET_ARROW_COLOR,
'target-arrow-shape': 'triangle',
'color': this.DATA_EDGE_COLOR,
'curve-style': 'haystack',
'label': ""
})
.update();
}
}
/**
* Create a graph from a list of edges.
*
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
* @param {GrapheFromEdgesListComponent} container2 - Another container object.
*/
createGrapheFromListEdges(container:any,container2:any):void{
const elements:any=container2.sendElements();
let err:boolean=false;
let cyElements:any=this.cy.elements();
let cyType:string=this.typeGraphe;
if(elements){
this.cy.elements().remove();
let typeGraphe:string=elements[0].type;
container.typeGraphe=this.typeGraphe=typeGraphe;
this.changeStyleGraphe(typeGraphe);
let i:number=0;
for(let element of elements){
const nodeExists = this.cy.getElementById(element.source).nonempty();
const nodeExists1 = this.cy.getElementById(element.target).nonempty();
if(element.source==""){
container.message=this.translate.instant("grapheFromEdgeList.msg1");
err=true;
break;
}else if(!nodeExists){
const pos = this.POSITIONS[i++];
this.cy.add({ group: 'nodes', data: { id: element.source}, position: pos });
}
if(element.target==""){
err=true;
container.message=this.translate.instant("grapheFromEdgeList.msg1");
break;
}else if(!nodeExists1){
const pos = this.POSITIONS[i++];
this.cy.add({ group: 'nodes', data: { id: element.target}, position: pos });
}
}
if(!err){
elements.forEach((element:any)=>{
if(typeGraphe.split(' ')[1]=="Weighted"){
if(element.source && element.target && element.weight){
this.cy.add({data:{
source: element.source,
target: element.target,
weight: element.weight
}
});
}
}else{
if(element.source && element.target){
this.cy.add({
data: {
source: element.source,
target: element.target,
}
});
}
}
})
const screen=container.el.nativeElement.querySelector('.screen');
const buttonManupilation=container.el.nativeElement.querySelector('.buttonManupilation');
const addGrapheFromEdgesList=container.el.nativeElement.querySelector('.addGrapheFromEdgesList');
screen.style.display="block";
buttonManupilation.style.display="block";
addGrapheFromEdgesList.style.display="none";
container2.listEdgeTextArea="";
container.message=this.translate.instant("grapheFromEdgeList.msg7");////
container.restoreArray=[];
container.changeSelect="";
}else{
container.typeGraphe=this.typeGraphe=cyType;
this.changeStyleGraphe(typeGraphe);
this.cy.elements().remove();
this.cy.add(cyElements);
}
}else{
container.message=this.translate.instant("grapheFromEdgeList.msg1");
}
}
/**
* Reject the generation of a graph from a list of edges.
*
* @param {ScreenboxComponent} container - The container object containing graph-related properties.
* @param {GrapheFromEdgesListComponent} container2 - Another container object.
*/
rejeterGenerateGraphFromListEdges(container:any,container2:any):void {
const screen=container.el.nativeElement.querySelector('.screen');
const buttonManupilation=container.el.nativeElement.querySelector('.buttonManupilation');
const addGrapheFromEdgesList=container.el.nativeElement.querySelector('.addGrapheFromEdgesList');
screen.style.display="block";
buttonManupilation.style.display="block";
addGrapheFromEdgesList.style.display="none";
container2.listEdgeTextArea="";
container.message=this.translate.instant("grapheFromEdgeList.msg6");///////Change this message
container.changeSelect="";
}
/**
* Get the density of the graph.
*
* @returns {number} - The density of the graph.
*/
getDensityOfGraphe():number{
if(this.cy.nodes().length){
if(this.typeGraphe.split(' ')[0] == 'Directed'){
return this.cy.edges().length/(this.cy.nodes().length*(this.cy.nodes().length-1));
}else{
return (2*this.cy.edges().length)/(this.cy.nodes().length*(this.cy.nodes().length-1));
}
}else{
return 0;
}
}
/**
* Get an array of node IDs in the graph.
*
* @returns {Array<string>} - An array of node IDs.
*/
getNodeIds():Array<any>{
const nodes:Array<any> = [];
this.cy.nodes().forEach((node:any)=>{
nodes.push(node.data('id'));
})
return nodes;
}
/**
* Calculate the incidence matrix of the graph.
*
* @returns {Array<any>} - The incidence matrix of the graph.
*/
incidenceMatrix():Array<any> {
const nodes = this.cy.nodes();
const edges = this.cy.edges();
// Create an empty incidence matrix
const incidenceMatrix:Array<any> = [];
// Initialize an empty matrix with rows corresponding to nodes and columns to edges
for (let i = 0; i < nodes.length; i++) {
const row = [];
for (let j = 0; j < edges.length; j++) {
row.push(0); // Initialize all elements to 0
}
incidenceMatrix.push(row);
}
// Iterate over edges and populate the incidence matrix
edges.forEach((edge:any, edgeIndex:any) => {
const sourceNode = edge.source();
const targetNode = edge.target();
// Find the indices of the source and target nodes in the nodes array
const sourceIndex = nodes.indexOf(sourceNode);
const targetIndex = nodes.indexOf(targetNode);
// Set the corresponding cells in the matrix to 1 or -1 to indicate incidence
if(sourceIndex==targetIndex){
incidenceMatrix[sourceIndex][edgeIndex] = 2;
}else if(this.typeGraphe.split(' ')[0]=="Directed"){
incidenceMatrix[targetIndex][edgeIndex] = 1;
incidenceMatrix[sourceIndex][edgeIndex] = -1;
}else{
incidenceMatrix[sourceIndex][edgeIndex] = 1;
incidenceMatrix[targetIndex][edgeIndex] = 1;
}
});
return incidenceMatrix;
}
/**
* Get an array of edge IDs in the graph.
*
* @returns {Array<string>} - An array of edge IDs.
*/
getEdgeIds():Array<any>{
const edges:Array<any> = [];
this.cy.edges().forEach((edge:any)=>{
let edgeText:string="";
if(this.typeGraphe.split(' ')[0]=="Directed"){
edgeText=`${edge.source().id()} > ${edge.target().id()}`;
}else{
edgeText=`${edge.source().id()} - ${edge.target().id()}`;
}
edges.push(edgeText);
})
return edges;
}
/**
* Remove an edge from the graph.
*
* @param {ScreenboxComponent} container - The container for the graph.
* @param {RemoveEdgeComponent} container2 - Additional container.
*/
removeEdge(container:any,container2:any):void{
let exist: boolean=false;
const edges = this.cy.edges();
let edgeI:any;
if(this.typeGraphe.split(' ')[0]=="Undirected"){
for(let edge of edges){
if((edge.source().id()==container2.sourceId && edge.target().id()==container2.targetId)||(edge.target().id()==container2.sourceId && edge.source().id()==container2.targetId)){
edgeI=edge;
exist=true;
break;
}
}
}else{
for(let edge of edges){
if(edge.source().id()==container2.sourceId && edge.target().id()==container2.targetId){
edgeI=edge;
exist=true;
break;
}
}
}
if(exist){
this.cy.remove(edgeI);
let elem={status:"remove",element:edgeI};
container.restoreArray.push(elem);
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
formRemoveEdge.style.display="none";
container.message=this.translate.instant("grapheS.msg48",{source:container2.sourceId,target:container2.targetId});
container2.sourceId="";
container2.targetId="";
container.remove="";
}else{
container.message=this.translate.instant("grapheS.msg47",{source:container2.sourceId,target:container2.targetId});
}
}
/**
* Reject the removal of an edge from the graph.
*
* @param {ScreenboxComponent} container - The container for the graph.
* @param {RemoveEdgeComponent} container2 - Additional container.
*/
RejeterRemoveEdge(container:any,container2:any):void{
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
formRemoveEdge.style.display="none";
container2.sourceId="";
container2.targetId="";
container.remove="";
container.message=this.translate.instant("grapheS.msg46");
}
/**
* Remove a node from the graph.
*
* @param {ScreenboxComponent} container - The container for the graph.
* @param {RemoveNodeComponent} container2 - Additional container.
*/
removeNode(container:any,container2:any):void{
let node:any=this.cy.getElementById(container2.nodeId);
let exist:boolean=node.isNode();
if(exist){
node.remove();
let elem={status:"remove",element:node};
container.restoreArray.push(elem);
//
container.message=this.translate.instant("grapheS.msg52",{nodeId:container2.nodeId});
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
formRemoveNode.style.display="none";
container2.nodeId="";
container.remove="";
}else{
container.message=this.translate.instant("grapheS.msg53",{nodeId:container2.nodeId});
}
}
/**
* Reject the removal of a node from the graph.
*
* @param {ScreenboxComponent} container - The container for the graph.
* @param {RemoveNodeComponent} container2 - Additional container.
*/
RejeterRemoveNode(container:any,container2:any):void{
const formChangeNodeId=container.el.nativeElement.querySelector('.formChangeNodeId');
const formAddEdge = container.el.nativeElement.querySelector('.formAddEdges');
const formAChangeSizeScreen = container.el.nativeElement.querySelector('.formAChangeSizeScreen');
const formChangeColor = container.el.nativeElement.querySelector('.formChangeColor');
const formAddNode = container.el.nativeElement.querySelector('.formAddNode');
const formRemoveEdge = container.el.nativeElement.querySelector('.formRemoveEdge');
const formRemoveNode = container.el.nativeElement.querySelector('.formRemoveNode');
container.message=this.translate.instant("grapheS.msg51");
formChangeNodeId.style.display="none";
formAddEdge.style.display="none";
formChangeColor.style.display="none";
formAChangeSizeScreen.style.display="none";
formAddNode.style.display="none";
formRemoveEdge.style.display="none";
formRemoveNode.style.display="none";
container2.nodeId="";
container.remove="";
}
/**
* Get the adjacency list of the graph.
*
* @returns {Array<any>} - The adjacency list of the graph.
*/
getAdjancyList():Array<any>{
const adjacencyList:Array<any> = [];
this.cy.nodes().forEach((node:any) => {
const outgoingEdges = node.outgoers();
const incomersEdges = node.incomers();
let adjNode:Array<any>=outgoingEdges.map((edge:any) => edge.target().id());
let elements:Array<any>=incomersEdges.map((edge:any) => edge.source().id());
let adjanString:string="";
adjNode = adjNode.filter(item => item !== undefined);
elements = elements.filter(item => item !== undefined);
for(let i:number=0;i<adjNode.length;i++){
if(this.typeGraphe.split(" ")[1]!="Weighted"){
if(i!=adjNode.length-1){
adjanString+=`${adjNode[i]} ~> `;
}else{
adjanString+=`${adjNode[i]}`
}
}else{
let edge:any;
for(let element of this.cy.edges()){
if(element.target().id()==adjNode[i] && element.source().id()==node.id()){
edge=element;
break;
}
}
if(i!=adjNode.length-1){
adjanString+=`${adjNode[i]} (${edge.data("weight")}) ~> `;
}else{
adjanString+=`${adjNode[i]} (${edge.data("weight")})`
}
}
}
if(this.typeGraphe.split(" ")[0]!="Directed"){
for(let i:number=0;i<elements.length;i++){
if(this.typeGraphe.split(" ")[1]!="Weighted"){
if(i==0 && adjNode.length){
adjanString+=` ~> ${elements[i]} ~> `;
}else if(i!=elements.length-1){
adjanString+=` ${elements[i]} ~> `;
}else{
adjanString+=`${elements[i]}`;
}
}else{
let edge:any;
for(let element of this.cy.edges()){
if(element.source().id()==adjNode[i] && element.target().id()==node.id()){
edge=element;
break;
}
}
if(i==0 && adjNode.length){
adjanString+=` ~> ${elements[i]} (${edge.data("weight")}) ~> `;
}else if(i!=elements.length-1){
adjanString+=` ${elements[i]} (${edge.data("weight")}) ~> `;
}else{
adjanString+=`${elements[i]} (${edge.data("weight")})`;
}
}
}
}
if(adjanString){
if(adjanString.slice(-3)=="~> "){
adjanString+=" NULL";
}else {
adjanString+=" ~> NULL";
}
}
const adjacencyListEntry = {
node: node.id(),
adjacentNodes: adjanString
};
adjacencyList.push(adjacencyListEntry);
});
return adjacencyList;
}
}