Public
Edited
Jun 12, 2023
Fork of Untitled
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
timedEventGraph
Insert cell
Insert cell
function Toolbar({
onModeChange,
tegRef,
addNewMode,
deleteMode,
currentMode,
setCurrentTool,
currentTool
}) {
const toolbarRef = useRef();
//const [currentTool, setCurrentTool] = useState(null);

useEffect(() => {
initDragging();
}, []);

const selectTool = (tool) => {
setCurrentTool(tool);
//console.log("I have selected current Tool... " + tool);
// Emit a custom event to notify the Canvas about the tool change
};

const toggleVisibility = () => {
toolbarRef.current.classList.toggle("hidden");
};

const initDragging = () => {
let isDragging = false;
let offsetX = 0;
let offsetY = 0;

const onMouseDown = (event) => {
isDragging = true;
offsetX = event.clientX - toolbarRef.current.offsetLeft;
offsetY = event.clientY - toolbarRef.current.offsetTop;
};

const onMouseMove = (event) => {
if (!isDragging) return;
toolbarRef.current.style.left = `${event.clientX - offsetX}px`;
toolbarRef.current.style.top = `${event.clientY - offsetY}px`;
};

const onMouseUp = () => {
isDragging = false;
};

toolbarRef.current.addEventListener("mousedown", onMouseDown);
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
const [showNewModeInput, setShowNewModeInput] = useState(false);
const [newModeName, setNewModeName] = useState("");
var modes = Object.keys(tegRef.current.arcs);
// let currentMode = tegRef.current.mode;
const handleChange = (event) => {
if (event.target.value === "new") {
setShowNewModeInput(true);
} else {
onModeChange(event.target.value);
}
};

const handleNewModeChange = (event) => {
setNewModeName(event.target.value);
};

const handleNewModeSubmit = (event) => {
event.preventDefault();
if (newModeName) {
addNewMode(newModeName);
onModeChange(newModeName);
setShowNewModeInput(false);
setNewModeName("");
}
};

const handleDeleteMode = () => {
deleteMode(currentMode);
};
const tools = [
{ id: "cursor", label: "🖱️" },
{ id: "place", label: "⚪" },
{ id: "transition", label: "▭" },
{ id: "connect", label: "🔗" },
{ id: "edit", label: "✏️" }
];

return jsx`
<button onClick=${toggleVisibility}>Show/Hide</button>
<div ref=${toolbarRef} class="toolbar">

<div class="tool-selector">
${tools.map((tool) => {
return jsx`
<input
type="radio"
id=${tool.id}
name="tool"
value=${tool.id}
onChange=${() => selectTool(tool.id)}
/>
<label htmlFor=${tool.id}>${tool.label}</label>
`;
})}
</div>
${
!showNewModeInput
? jsx`<select value=${currentMode} onChange=${handleChange}>
${modes.map(
(mode, index) => jsx`<option value=${mode}>${mode}</option>`
)}
<option value="new">New Mode</option>
</select>`
: jsx`<form onSubmit=${handleNewModeSubmit}>
<input
type="text"
autoFocus
value=${newModeName}
onChange=${handleNewModeChange}
placeholder="Enter new mode name"
/>
</form>`
}
<button onClick=${handleDeleteMode}>Delete Mode</button>
</div>
`;
}
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
timedEventGraph.fireTransition(timedEventGraph.transitions[1])
Insert cell
{
//debugger;
// timedEventGraph.addPlace(new timedEventGraph._inner.Place("p3", 1));
var t0 = timedEventGraph.fromId("T1");
timedEventGraph.fireTransition(t0);
}
Insert cell
class Place {
/**
* Creates a Place instance
* @param {string} id - ID of the Place
* @param {Object<string, number>} holdingTime - Holding time per mode
* @param {number} [x] - X coordinate
* @param {number} [y] - Y coordinate
*/
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 {
/**
* Creates a Transition instance
* @param {string} id - ID of the Transition
* @param {Object<string, number>} holdingTime - Holding time per mode
* @param {number} [x] - X coordinate
* @param {number} [y] - Y coordinate
*/
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 = {
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
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 drawTimedEventGraph(timedEventGraph, containerSelector, tools) {
//const width = 800;
const height = 600;
const nodeRadius = 20;
const container = d3.select(containerSelector);

// Check if an SVG container already exists
let svg = container.select("svg");

// If there's no existing SVG container, create one
if (svg.empty()) {
svg = container.append("svg").attr("width", width).attr("height", height);
} else {
// If an SVG container exists, clear its contents
svg.selectAll("*").remove();
}

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(-30))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("link", d3.forceLink(links).distance(100).strength(0.3))
.force("collide", d3.forceCollide(nodeRadius * 2));
var changeable = { simulation, tickCount: tickCount2, svg, animate: true };
simulation.restart();
let toolf = tools(changeable);
var link = applyLink(svg.selectAll(".link"));
let place = applyPlace(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) {
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) {
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)
);
}

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");
place
.append("text")
.attr("class", "label")
.text((d) => d.id)
.attr("text-anchor", "middle")
.attr("dy", 1.8 * nodeRadius + 10); // Adjust this value to position the label below the place element

let transitionheight = nodeRadius * 4;
// Create transition elements
let transition = applyTransition(svg.selectAll(".transition"));
transition
.append("text")
.attr("class", "label")
.text((d) => d.id)
.attr("text-anchor", "middle")
.attr("dy", +0.6 * transitionheight + 10);

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() {
// 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.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);
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");

// 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"));

// Update transition elements
transition = applyTransition(transition);
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);
place
.selectAll(".label")
.data((d) => [d])
.text((d) => d.id);

transition
.selectAll(".label")
.data((d) => [d])
.text((d) => d.id);

// label = applyLabel(label);
}

function ticked() {
if (changeable.tickCount >= maxTicks) {
changeable.simulation.stop();
} else {
changeable.tickCount++;
}
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})`);

// label.attr("x", (d) => d.x).attr("y", (d) => d.y - transitionheight);
}
changeable.simulation.on("tick", ticked);
}
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