Newer
Older
import ManagedData from "./manageddata";
import { PLUGIN_PATH, COLOR_PALETTE } from "../../config";
export const NODE_LABEL = "name";
export const NODE_ID = "id";
export const NODE_TYPE = "type";
export const NODE_DESCRIPTION = "description";
export const NODE_IMAGE = "image";
export const NODE_REFERENCES = "references";
export const NODE_VIDEOS = "videos";
export const LINK_SOURCE = "source";
export const LINK_TARGET = "target";
export const LINK_TYPE = "type";
export const LINK_PARTICLE_COUNT = 4;
export const GRAPH_NODES = "nodes";
export const GRAPH_LINKS = "links";
export const IMAGE_SIZE = 12;
export const IMAGE_SRC = PLUGIN_PATH + "datasets/images/";
export const LINK_PARAMS = [];
export const NODE_PARAMS = [
NODE_ID,
NODE_LABEL,
NODE_IMAGE,
NODE_DESCRIPTION,
NODE_REFERENCES,
NODE_VIDEOS,
NODE_TYPE,
];
export const LINK_SIM_PARAMS = ["index"];
export const NODE_SIM_PARAMS = ["index", "x", "y", "vx", "vy", "fx", "fy"]; // Based on https://github.com/d3/d3-force#simulation_nodes
export const JSON_CONFIG = PLUGIN_PATH + "datasets/aud1v2.json";
export const STOP_PHYSICS_DELAY = 5000; // ms
export class Graph extends ManagedData {
constructor(data) {
super(Graph.addIdentifiers(data));
this.calculateLinkTypes();
this.physicsDelay = STOP_PHYSICS_DELAY;
}
restartSimulation() {
}
triggerOnChange() {
this.onChangeCallbacks.forEach((fn) => fn(this.data));
}
onRedo() {
this.triggerOnChange();
}
onUndo() {
this.triggerOnChange();
}
/**
* Based on the function from the 3d-graph code from @JoschaRode
*/
calculateLinkTypes() {
const linkClasses = [];
this.data[GRAPH_LINKS].forEach((link) =>
linkClasses.push(link[LINK_TYPE])
);
this.linkTypes = [...new Set(linkClasses)];
}
getLinkColor(link) {
}
getLinkTypeColor(linkClass) {
var classIndex = this.linkTypes.indexOf(linkClass);
return COLOR_PALETTE[classIndex % COLOR_PALETTE.length];
}
this.data[GRAPH_NODES] = this.data[GRAPH_NODES].filter(
(n) => n[NODE_ID] !== nodeId
);
// Delete links with node
this.data[GRAPH_LINKS] = this.data[GRAPH_LINKS].filter(
(l) =>
l[LINK_SOURCE][NODE_ID] !== nodeId &&
l[LINK_TARGET][NODE_ID] !== nodeId
);
this.storeCurrentData("Deleted node with id [" + nodeId + "]");
}
static addIdentifiers(data) {
data[GRAPH_NODES].forEach((n) => {
deleteLink(sourceId, targetId) {
// Only keep links, of one of the nodes is different
this.data[GRAPH_LINKS] = this.data[GRAPH_LINKS].filter(
(l) =>
l[LINK_SOURCE][NODE_ID] !== sourceId ||
l[LINK_TARGET][NODE_ID] !== targetId
);
this.storeCurrentData(
"Deleted link connecting [" + sourceId + "] with [" + targetId + "]"
);
}
isLinkOnNode(link, node) {
if (link === undefined || node === undefined) {
return false;
}
if (link.link !== true || node.node !== true) {
return false;
}
return (
link[LINK_SOURCE][NODE_ID] === node[NODE_ID] ||
link[LINK_TARGET][NODE_ID] === node[NODE_ID]
);
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (
link[LINK_SOURCE][NODE_ID] === sourceId &&
link[LINK_TARGET][NODE_ID] === targetId
) {
return true;
}
}
return false;
changeDetails(selectionDetails) {
if (selectionDetails.node === true) {
this.changeNodeDetails(selectionDetails[NODE_ID], selectionDetails);
} else if (selectionDetails.link === true) {
this.changeLinkDetails(
selectionDetails[LINK_SOURCE][NODE_ID],
selectionDetails[LINK_TARGET][NODE_ID],
selectionDetails
);
}
}
changeNodeDetails(nodeId, newDetails) {
var nodes = this.data[GRAPH_NODES];
for (var i = 0; i < nodes.length; i++) {
// Is relevant node?
if (nodes[i][NODE_ID] !== nodeId) {
continue; // No
}
// Change details
nodes[i] = Object.assign(nodes[i], newDetails);
// All done
this.storeCurrentData("Changed node details");
return;
}
}
changeLinkDetails(sourceId, targetId, newDetails) {
var links = this.data[GRAPH_LINKS];
for (var i = 0; i < links.length; i++) {
// Is relevant link?
if (
links[i][LINK_SOURCE][NODE_ID] !== sourceId ||
links[i][LINK_TARGET][NODE_ID] !== targetId
) {
continue; // No
}
// Change details
links[i] = Object.assign(links[i], newDetails);
// All done
this.storeCurrentData("Changed link details");
return;
}
}
connectNodes(sourceId, targetIds) {
targetIds.forEach((targetId) => {
if (
this.existsLink(sourceId, targetId) ||
this.existsLink(targetId, sourceId)
getCleanData(data = undefined, simulationParameters = false) {
if (data === undefined) {
data = this.data;
}
var cleanData = {};
cleanData[GRAPH_LINKS] = [];
cleanData[GRAPH_NODES] = [];
cleanData[GRAPH_LINKS].push(
this.getCleanLink(link, simulationParameters)
)
cleanData[GRAPH_NODES].push(
this.getCleanNode(node, simulationParameters)
)
getCleanNode(node, simulationParameters) {
var cleanNode = {};
NODE_PARAMS.forEach((param) => {
cleanNode[param] = node[param];
});
if (simulationParameters) {
NODE_SIM_PARAMS.forEach((param) => {
cleanNode[param] = node[param];
});
getCleanLink(link, simulationParameters) {
// Assuming that all nodes are valid, there are two possible formats
// 1. source and target are node objects
if (link[LINK_SOURCE][NODE_ID] !== undefined) {
// Source and target nodes
// Node ids will be converted to complete node objects on running graphs, gotta convert back
cleanLink[LINK_SOURCE] = link[LINK_SOURCE][NODE_ID];
cleanLink[LINK_TARGET] = link[LINK_TARGET][NODE_ID];
} else {
// 2. source and target are just node ids
cleanLink[LINK_SOURCE] = link[LINK_SOURCE];
cleanLink[LINK_TARGET] = link[LINK_TARGET];
}
// Other parameters
LINK_PARAMS.forEach((param) => {
cleanLink[param] = link[param];
});
if (simulationParameters) {
LINK_SIM_PARAMS.forEach((param) => {
cleanLink[param] = link[param];
});
}
for (var i = 0; i < nodes.length; i++) {
if (nodes[i][NODE_ID] === nodeId) {
return true;
}
}
return false;
id = this.getRandomString();
} while (this.existsNodeId(id));
// Based on: https://stackoverflow.com/a/1349426/7376120
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
var result = "";
for (var i = 0; i < length; i++) {
result += characters.charAt(
Math.floor(Math.random() * charactersLength)
);
}
return result;
addLink(sourceId, targetId, linkDetails = {}) {
// Copy params
var newLink = linkDetails;
// Make sure the IDs exist
if (
sourceId === undefined ||
targetId === undefined ||
this.existsNodeId(sourceId) === false ||
this.existsNodeId(targetId) === false
) {
return;
}
// Make sure the link is unique
return;
}
newLink[LINK_SOURCE] = sourceId;
newLink[LINK_TARGET] = targetId;
// Basic node properties
newLink.link = true;
newLink.node = false;
// Add node
this.data[GRAPH_LINKS].push(newLink);
this.triggerOnChange();
this.storeCurrentData(
"Added custom link connecting [" +
sourceId +
"] with [" +
targetId +
"]"
);
addNode(nodeDetails) {
// Copy params
var newNode = nodeDetails;
// Make sure the ID is set and unique
if (newNode[NODE_ID] === undefined) {
newNode[NODE_ID] = this.getUnusedNodeId();
} else if (this.existsNodeId(newNode[NODE_ID])) {
return;
}
// Basic node properties
newNode.node = true;
newNode.link = false;
// Add node
this.data[GRAPH_NODES].push(newNode);
this.triggerOnChange();
this.storeCurrentData(
"Added custom node with id [" + newNode[NODE_ID] + "]"
);
static toStr(item) {
if (item === undefined) {
return "UNDEFINED";
}
if (item.node) {
);
} else {
return "UNDEFINED";
}
}