Public
Edited
Apr 18, 2023
Fork of Untitled
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
timedEventGraph
Insert cell
Insert cell
Insert cell
d3
.selectAll("g") // selecting both circles (places) and rectangles (transitions)
.each(function (node) {
console.log("please", node, this);
})
Insert cell
Insert cell
{
return render((_) => jsx`<${TimedEventGraphComponent} />`);
}
Insert cell
/*{
// Draw the timed event graph using D3.js
let div = html`<div><div>`;
drawTimedEventGraph(timedEventGraph, div);
return div;
}*/
Insert cell
{
//debugger;
// timedEventGraph.addPlace(new timedEventGraph._inner.Place("p3", 1));
var t0 = timedEventGraph.fromId("T1");
timedEventGraph.fireTransition(t0);
}
Insert cell
class Place {
constructor(id, holdingTime = 0, x = undefined, y = undefined) {
this.id = id;
this.holdingTime = holdingTime;
this.tokens = 0;
this.x = x;
this.y = y;
}
}
Insert cell
class Transition {
constructor(id, holdingTime = 0, x = undefined, y = undefined) {
this.id = id;
this.holdingTime = holdingTime;
this.x = x;
this.y = y;
}
}
Insert cell
class Arc {
constructor(source, target) {
this.source = source;
this.target = target;
}
}
Insert cell
timedEventGraph = {
class TimedEventGraph {
constructor() {
this.places = [];
this.transitions = [];
this.arcs = { a: [] };
this.updateCallback = null;
this.mode = "a";
this.obj={};
}

setUpdateCallback(callback) {
this.updateCallback = callback;
}
fireTransition(transition) {
// Find all input and output places for the given transition
const inputPlaces = this.arcs[this.mode]
.filter((arc) => arc.target === transition)
.map((arc) => arc.source);
const outputPlaces = this.arcs[this.mode]
.filter((arc) => arc.source === transition)
.map((arc) => arc.target);

// Check if the transition is enabled (i.e., if all input places have at least one token)
const isEnabled = inputPlaces.every((place) => place.tokens > 0);

if (isEnabled) {
// Subtract a token from each input place and add a token to each output place
inputPlaces.forEach((place) => place.tokens--);
outputPlaces.forEach((place) => place.tokens++);

// Update the visualization to reflect the new token counts
this.update();
} else {
console.log(`Transition ${transition.id} is not enabled.`);
}
}
fromId(id) {
// Search for a matching Place object
const place = this.places.find((p) => p.id === id);
if (place) {
return place;
}

// Search for a matching Transition object
const transition = this.transitions.find((t) => t.id === id);
if (transition) {
return transition;
}

// Search for a matching Arc object
const arc = this.arcs.find((t) => t.id === id);
if (arc) {
return arc;
}

// Return null if no matching object is found
return null;
}
addPlace(place, position) {
if (position) {
this.places.x = position.x;
this.places.y = position.y;
}
this.places.push(place);
this.update();
}
addMode(newMode) {
if (!this.arcs[newMode]) {
this.arcs[newMode] = [];
this.update();
} else {
console.log(`Mode "${newMode}" already exists.`);
}
}

removeMode(modeName) {
if (this.arcs[modeName]) {
delete this.arcs[modeName];
this.update();
} else {
console.log(`Mode "${modeName}" does not exist.`);
}
}
addTransition(transition) {
this.transitions.push(transition);
this.update();
}

addArc(arc) {
this.arcs[this.mode].push(arc);
this.update();
}

update() {
if (this.updateCallback) {
this.updateCallback(this.obj);
}
}
}

// Create the timed event graph
const timedEventGraph = new TimedEventGraph();
timedEventGraph._inner = { Arc, Place, Transition };

const place1 = new Place("P1", 5);
const place2 = new Place("P2", 0);
const transition1 = new Transition("T1", 0);
const input = new Transition("T0", 0);
timedEventGraph.addPlace(place1);
timedEventGraph.addPlace(place2);
timedEventGraph.addTransition(transition1);
timedEventGraph.addTransition(input);
timedEventGraph.addArc(new Arc(place1, transition1));
timedEventGraph.addArc(new Arc(transition1, place2));
timedEventGraph.addArc(new Arc(input, place1));

return timedEventGraph;
}
Insert cell
tools=function(ref){
function dragstarted(event, d) {
if (!event.active) {
ref.tickCount = 0;
ref.simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
}

function dragged(event, d) {
ref.tickCount = 0;
d.fx = event.x;
d.fy = event.y;
}

function dragended(event, d) {
ref.tickCount = 0;
if (!event.active) ref.simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return {dragstarted,dragged,dragended}
}
Insert cell
function drawTimedEventGraph2(timedEventGraph) {
//const width = 800;
const height = 600;
const nodeRadius = 30;
// Create a wrapper DOM element
const wrapper = document.createElement("div");
wrapper.setAttribute("class", "teg-wrapper");

// Create an SVG container
const svg = d3.create("svg").attr("width", width).attr("height", height);

// Append the SVG container to the wrapper
wrapper.appendChild(svg.node());

let nodes = [...timedEventGraph.places, ...timedEventGraph.transitions];
let links = timedEventGraph.arcs[timedEventGraph.mode];
let tickCount2 = 0;
const maxTicks = 200;

let simulation = d3
.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-900))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("link", d3.forceLink(links).distance(200).strength(1))
.force("collide", d3.forceCollide(nodeRadius * 2));
var changeable = { simulation, tickCount: tickCount2, svg, animate: true };
var link = svg.selectAll(".link");
let place = svg.selectAll(".place");
function applyLink(link) {
// console.log("links", links);
return link
.data(links)
.join("line")
.attr("class", "link")
.attr("marker-end", "url(#arrowhead)");
}

function applyPlace(place, toolf) {
return place
.data(timedEventGraph.places)
.join("g")
.attr("class", "place")
.call(
d3
.drag()
.on("start", toolf.dragstarted)
.on("drag", toolf.dragged)
.on("end", toolf.dragended)
);
}

function applyTransition(transition, toolf) {
console.log(toolf.dragstarted.lol, "applyTransition update");
return transition
.data(timedEventGraph.transitions)
.join("g")
.attr("class", "transition-container")
.call(
d3
.drag()
.on("start", toolf.dragstarted)
.on("drag", toolf.dragged)
.on("end", toolf.dragended)
);
}

function applyLabel(label) {
return label
.data(nodes)
.join("text")
.attr("class", "label")
.text((d) => d.id)
.attr("text-anchor", "middle")
.attr("dy", ".3em");
}

/* place
.append("circle")
.attr("class", "outer-circle")
.attr("r", nodeRadius * 1.3)
.style("display", (d) => (d.holdingTime ? "block" : "none"));
place
.append("circle")
.attr("class", "inner-circle")
.attr("r", nodeRadius)
.style("display", (d) => "block");*/

let transitionheight = nodeRadius * 4;
// Create transition elements
let transition = svg.selectAll(".transition");
/* transition
.append("rect")
.attr("class", (d) => (d.holdingTime ? "transition" : "transition-filled"))
.attr("width", nodeRadius)
.attr("height", transitionheight)
.attr("rx", 3)
.attr("ry", 3);*/

const marker = svg
.append("defs")
.append("marker")
.attr("id", "arrowhead")
.attr("viewBox", "0 -5 10 10")
.attr("refX", nodeRadius * 1.3 + 5) // adjust the position of the arrowhead along the line
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("class", "arrowhead");
let label = applyLabel(svg.selectAll(".label"));

// console.log("before setUpdateCallback", timedEventGraph);
timedEventGraph.setUpdateCallback(updateVisualization);
// updateVisualization();
function updateVisualization({ tools }) {
let toolf = tools(changeable);
console.log("toolf.dragstarted", toolf.dragstarted.lol);

// Update nodes and links data
nodes = [...timedEventGraph.places, ...timedEventGraph.transitions];
links = timedEventGraph.arcs[timedEventGraph.mode];

// Update the simulation's nodes and links
changeable.simulation.nodes(nodes);
changeable.simulation.force("link").links(links);
changeable.simulation.alphaTarget(0.3).restart();
if (changeable.animate) {
// Restart the simulation
changeable.tickCount = 0;
} else {
changeable.tickCount = maxTicks - 2; //it needs to animate at least a bit
}

// Update the visualization elements
link = applyLink(link);

place = applyPlace(place, toolf);
place
.selectAll(".outer-circle")
.data((d) => [d])
.join("circle")
.attr("class", "outer-circle")
.attr("r", nodeRadius * 1.3)
.style("display", (d) => (d.holdingTime ? "block" : "none"));

place
.selectAll(".inner-circle")
.data((d) => [d])
.join("circle")
.attr("class", "inner-circle")
.attr("r", nodeRadius)
.style("display", (d) => "block");
/*transition
.append("rect")
.attr("class", (d) => (d.holdingTime ? "transition" : "transition-filled"))
.attr("width", nodeRadius)
.attr("height", transitionheight)
.attr("rx", 3)
.attr("ry", 3);*/
// Update transition elements
transition = applyTransition(transition, toolf);
transition
.selectAll("rect")
.data((d) => [d])
.join("rect")
.attr("class", (d) =>
d.holdingTime ? "transition" : "transition-filled"
)
.attr("width", nodeRadius)
.attr("height", transitionheight)
.attr("rx", 3)
.attr("ry", 3)
.attr("x", -nodeRadius / 2)
.attr("y", -transitionheight / 2);

// Draw tokens as dots inside the inner circle
// console.log("does this get executed");
place
.selectAll(".token-dot")
.data((d) => (d.tokens < 6 ? Array(d.tokens).fill(d) : []))
.join("circle")
.attr("class", "token-dot")
.attr("r", 3)
.attr(
"cx",
(d, i) => nodeRadius * 0.5 * Math.cos((i * 2 * Math.PI) / d.tokens)
)
.attr(
"cy",
(d, i) => nodeRadius * 0.5 * Math.sin((i * 2 * Math.PI) / d.tokens)
)
.style("display", (d) => "block");

// Display the numeric value for 6 or more tokens in the center
place
.selectAll(".token-count")
.data((d) => [d])
.join("text")
.attr("class", "token-count")
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.text((d) => (d.tokens >= 6 ? d.tokens : ""))
.style("display", (d) => (d.tokens >= 6 ? "block" : "none"));

label = applyLabel(label);
}

function ticked() {
if (changeable.tickCount >= maxTicks) {
changeable.simulation.stop();
} else {
changeable.tickCount++;
}
// console.log("WHAT is link", link);
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);

place.attr("transform", (d) => `translate(${d.x}, ${d.y})`);
transition.attr("transform", (d) => `translate(${d.x}, ${d.y})`);
/*transition
.attr("x", (d) => d.x - nodeRadius / 2)
.attr("y", (d) => d.y - transitionheight / 2);*/
//node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);

label.attr("x", (d) => d.x).attr("y", (d) => d.y - transitionheight);
}
changeable.simulation.on("tick", ticked);
return wrapper;
}
Insert cell
toolnext = {
//absolute hack because of d3, drag functions are uneditable, so it must save state, somehow.
var obj = {};
function tools3(ref) {
// Object.assign(obj, ref);
obj.ref=ref;
if (!obj.called) {
obj.called = true;
Object.assign(obj, tools2(obj));
}
return {
dragstarted: obj.dragstarted,
dragged: obj.dragged,
dragended: obj.dragended
};
}
return { obj, toolfn: tools3 };
}
Insert cell
function tools2(ref) {
//call only once
const svgPoint = ref.ref.svg.node().createSVGPoint();
//let arrow;
if (ref.currentTool === "connect") {
ref.ref.simulation.stop();
ref.ref.animate = false;
}

function dragstarted(event, d) {
if (ref.currentTool === "connect") {
ref.ref.simulation.stop();
//startNode = d;
ref.arrow = ref.ref.svg
.append("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("marker-end", "url(#arrowend)");
} else {
if (!event.active) {
ref.ref.tickCount = 0;
ref.ref.simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
}
}

function dragged(event, d) {
console.log(ref.arrow, ref.currentTool, "dragged");
if (ref.currentTool === "connect" && ref.arrow) {
var x = event.x;
var y = event.y;
// Remove highlight class from all elements
d3.selectAll("g").classed("highlight", false);
let endNode = d3
.selectAll("g") // selecting both circles (places) and rectangles (transitions)
.filter((node) => node !== d)
.filter(function (node) {
const bbox = this.getBBox();
var res =
x >= bbox.x + node.x &&
x <= bbox.x + node.x + bbox.width &&
y >= bbox.y + node.y &&
y <= bbox.y + node.y + bbox.height;
return res;
});
endNode.classed("highlight", true);
// console.log("hightlight added");
ref.arrow
.attr("x1", d.x)
.attr("y1", d.y)
.attr("x2", event.x)
.attr("y2", event.y);
} else {
ref.ref.tickCount = 0;
d.fx = event.x;
d.fy = event.y;
}
}

function dragended(event, d) {
if (ref.currentTool === "connect" && ref.arrow) {
var x = event.x;
var y = event.y;
// Remove highlight class from all elements
d3.selectAll("g").classed("highlight", false);
let endNode2 = d3
.selectAll("g") // selecting both circles (places) and rectangles (transitions)
.filter((node) => node !== d)
.each(function (node) {
// console.log("for all nides", node, this);
})
.filter(function (node) {
const bbox = this.getBBox();
var res =
x >= bbox.x + node.x &&
x <= bbox.x + node.x + bbox.width &&
y >= bbox.y + node.y &&
y <= bbox.y + node.y + bbox.height;
//console.log(res, node, bbox, "WHAt???", event, x, y);
return res;
});
ref.arrow.remove();
if (endNode2.node()) {
var endNode = endNode2.datum();
if (d !== endNode) {
//what && d.type !== endNode.type
ref.tegRef.current.addArc(new Arc(d, endNode));
ref.tegRef.current.update();
}
} else {
// console.log("NO ENDNODE found!!!", endNode2);
}

// console.log("CONNECT", d, endNode);

ref.arrow = null;
} else {
ref.tickCount = 0;
if (!event.active) ref.ref.simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
ref.ref.svg.on("click", (event) => ref.onClick(event, ref.tegRef.current));
return { dragstarted, dragged, dragended };
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more