init
This commit is contained in:
commit
806baf0043
3 changed files with 417 additions and 0 deletions
166
graphics.js
Normal file
166
graphics.js
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
class Tragable {
|
||||||
|
initTrag(element, cb) {
|
||||||
|
let selected = false;
|
||||||
|
element.addEventListener('mousedown', () => {
|
||||||
|
selected = true;
|
||||||
|
});
|
||||||
|
svg.addEventListener('mousemove', event => {
|
||||||
|
if (selected) cb(event.x, event.y);
|
||||||
|
});
|
||||||
|
svg.addEventListener('mouseup', () => {
|
||||||
|
selected = false;
|
||||||
|
});
|
||||||
|
svg.addEventListener('mouseleave', event => {
|
||||||
|
selected = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SVGNode extends Tragable {
|
||||||
|
group;
|
||||||
|
circle;
|
||||||
|
circleText;
|
||||||
|
aboveText;
|
||||||
|
constructor(node) {
|
||||||
|
super();
|
||||||
|
// Create the group element
|
||||||
|
this.group = document.createElementNS(svgNamespace, 'g');
|
||||||
|
this.group.classList.add("PNode");
|
||||||
|
this.group.setAttribute('transform', `translate(${node.x}, ${node.y})`);
|
||||||
|
|
||||||
|
// Create the circle
|
||||||
|
this.circle = document.createElementNS(svgNamespace, 'circle');
|
||||||
|
this.circle.setAttribute('cx', '0');
|
||||||
|
this.circle.setAttribute('cy', '0');
|
||||||
|
this.circle.setAttribute('r', '25');
|
||||||
|
|
||||||
|
// Create the text inside the circle
|
||||||
|
this.circleText = document.createElementNS(svgNamespace, 'text');
|
||||||
|
this.circleText.classList.add("circleText");
|
||||||
|
this.circleText.setAttribute('x', '0');
|
||||||
|
this.circleText.setAttribute('y', '5');
|
||||||
|
this.circleText.textContent = '0/1';
|
||||||
|
|
||||||
|
// Create the text above the circle
|
||||||
|
this.aboveText = document.createElementNS(svgNamespace, 'text');
|
||||||
|
this.aboveText.classList.add("aboveText");
|
||||||
|
this.aboveText.setAttribute('x', '0');
|
||||||
|
this.aboveText.setAttribute('y', '-35');
|
||||||
|
this.aboveText.textContent = node.name;
|
||||||
|
|
||||||
|
// Append the circle and texts to the group
|
||||||
|
this.group.appendChild(this.circle);
|
||||||
|
this.group.appendChild(this.circleText);
|
||||||
|
this.group.appendChild(this.aboveText);
|
||||||
|
|
||||||
|
this.initTrag(this.group, (x, y) => node.updatePosition(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendIt(svg) {
|
||||||
|
svg.appendChild(this.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(x, y) {
|
||||||
|
this.group.setAttribute('transform', `translate(${x}, ${y})`);
|
||||||
|
}
|
||||||
|
setName(name) {
|
||||||
|
this.aboveText.textContent = name;
|
||||||
|
}
|
||||||
|
setMarks(amount, max) {
|
||||||
|
this.circleText.textContent = `${amount}/${max}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SVGTRansition extends Tragable {
|
||||||
|
group;
|
||||||
|
rect;
|
||||||
|
rectText;
|
||||||
|
constructor(trans) {
|
||||||
|
super();
|
||||||
|
// Create the group element
|
||||||
|
this.group = document.createElementNS(svgNamespace, 'g');
|
||||||
|
this.group.classList.add("PTransition");
|
||||||
|
this.group.setAttribute('transform', `translate(${trans.x}, ${trans.y})`);
|
||||||
|
|
||||||
|
// Create the circle
|
||||||
|
this.rect = document.createElementNS(svgNamespace, 'rect');
|
||||||
|
this.rect.setAttribute('x', '-12.5');
|
||||||
|
this.rect.setAttribute('y', '-25');
|
||||||
|
this.rect.setAttribute('height', '50');
|
||||||
|
this.rect.setAttribute('width', '25');
|
||||||
|
|
||||||
|
// Create the text inside the circle
|
||||||
|
this.rectText = document.createElementNS(svgNamespace, 'text');
|
||||||
|
this.rectText.classList.add("rectText");
|
||||||
|
this.rectText.setAttribute('x', '-5');
|
||||||
|
this.rectText.setAttribute('y', '-35');
|
||||||
|
this.rectText.textContent = trans.name;
|
||||||
|
|
||||||
|
// Append the circle and texts to the group
|
||||||
|
this.group.appendChild(this.rect);
|
||||||
|
this.group.appendChild(this.rectText);
|
||||||
|
this.initTrag(this.group, (x, y) => trans.updatePosition(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendIt(svg) {
|
||||||
|
svg.appendChild(this.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(x, y) {
|
||||||
|
this.group.setAttribute('transform', `translate(${x}, ${y})`);
|
||||||
|
}
|
||||||
|
setName(name) {
|
||||||
|
this.aboveText.textContent = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCanFire(can){
|
||||||
|
if (can)this.group.classList.add("canFire");
|
||||||
|
else this.group.classList.remove("canFire");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SVGArrow {
|
||||||
|
group;
|
||||||
|
lineText;
|
||||||
|
line;
|
||||||
|
from;
|
||||||
|
to;
|
||||||
|
constructor(from, to) {
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
this.group = document.createElementNS(svgNamespace, 'g');
|
||||||
|
this.group.classList.add("PEdge");
|
||||||
|
|
||||||
|
this.line = document.createElementNS(svgNamespace, 'line');
|
||||||
|
this.line.setAttribute('marker-end', "url(#arrowhead)");
|
||||||
|
this.group.setAttribute('transform', `translate(0, 0)`);
|
||||||
|
|
||||||
|
// Create the text inside the circle
|
||||||
|
this.lineText = document.createElementNS(svgNamespace, 'text');
|
||||||
|
this.lineText.classList.add("lineText");
|
||||||
|
this.lineText.setAttribute('x', '0');
|
||||||
|
this.lineText.setAttribute('y', '0');
|
||||||
|
this.lineText.textContent = "";
|
||||||
|
|
||||||
|
this.group.appendChild(this.line);
|
||||||
|
this.group.appendChild(this.lineText);
|
||||||
|
|
||||||
|
this.updatePosition();
|
||||||
|
}
|
||||||
|
updatePosition() {
|
||||||
|
let dx = this.to.x - this.from.x;
|
||||||
|
let dy = this.to.y - this.from.y;
|
||||||
|
let l = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
dx /= l;
|
||||||
|
dy /= l;
|
||||||
|
this.line.setAttribute('x1', this.from.x + dx * 30);
|
||||||
|
this.line.setAttribute('y1', this.from.y + dy * 30);
|
||||||
|
this.line.setAttribute('x2', this.to.x - dx * 40);
|
||||||
|
this.line.setAttribute('y2', this.to.y - dy * 40);
|
||||||
|
}
|
||||||
|
appendIt(svg) {
|
||||||
|
svg.appendChild(this.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
index.html
Normal file
91
index.html
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
body,
|
||||||
|
html,
|
||||||
|
svg {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PNode circle {
|
||||||
|
fill: #f1c40f;
|
||||||
|
stroke: #3498db;
|
||||||
|
stroke-width: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PNode circle {
|
||||||
|
fill: #f1c40f;
|
||||||
|
stroke: #3498db;
|
||||||
|
stroke-width: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PNode .circleText {
|
||||||
|
text-anchor: middle;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PNode .aboveText {
|
||||||
|
text-anchor: middle;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PNode .rectText {
|
||||||
|
text-anchor: middle;
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PTransition rect {
|
||||||
|
fill: #c0392b;
|
||||||
|
stroke: #34495e;
|
||||||
|
stroke-width: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PTransition.canFire rect{
|
||||||
|
fill: #27ae60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PEdge line {
|
||||||
|
stroke: gray;
|
||||||
|
stroke-width: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#arrowhead {
|
||||||
|
z-index: -1;
|
||||||
|
fill: gray;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<svg viewBox="0 0 500 200" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<marker id="arrowhead" markerWidth="5" markerHeight="3.5" refX="2.5" refY="1.75" orient="auto">
|
||||||
|
<polygon points="0 0, 5 1.75, 0 3.5" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
<script>const svgNamespace = 'http://www.w3.org/2000/svg';</script>
|
||||||
|
<script src="graphics.js"></script>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
160
main.js
Normal file
160
main.js
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
|
||||||
|
let svg = document.querySelector("svg");
|
||||||
|
|
||||||
|
let nods = document.createElementNS(svgNamespace, 'g');
|
||||||
|
let arrows = document.createElementNS(svgNamespace, 'g');
|
||||||
|
|
||||||
|
svg.appendChild(arrows);
|
||||||
|
svg.appendChild(nods);
|
||||||
|
|
||||||
|
class Edge {
|
||||||
|
renderedElement;
|
||||||
|
end;
|
||||||
|
trans;
|
||||||
|
inEdge;
|
||||||
|
dimension = 1;
|
||||||
|
constructor(end, trans, inEdge = true) {
|
||||||
|
this.end = end;
|
||||||
|
this.trans = trans;
|
||||||
|
this.inEdge = inEdge;
|
||||||
|
this.renderedElement = inEdge ? new SVGArrow(end, trans) : new SVGArrow(trans, end);
|
||||||
|
this.renderedElement.appendIt(arrows);
|
||||||
|
end.updateList.push(this);
|
||||||
|
}
|
||||||
|
canSuck() {
|
||||||
|
return this.end.marks >= this.dimension;
|
||||||
|
}
|
||||||
|
suck() {
|
||||||
|
this.end.marks -= this.dimension;
|
||||||
|
this.end.updatePoints();
|
||||||
|
}
|
||||||
|
canPush() {
|
||||||
|
return this.end.capacity >= this.end.marks + this.dimension;
|
||||||
|
}
|
||||||
|
push() {
|
||||||
|
this.end.marks += this.dimension;
|
||||||
|
this.end.updatePoints();
|
||||||
|
}
|
||||||
|
updatePosition() {
|
||||||
|
this.renderedElement.updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
renderedElement;
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
name;
|
||||||
|
capacity = 1;
|
||||||
|
marks = 0;
|
||||||
|
|
||||||
|
updateList = [];
|
||||||
|
|
||||||
|
constructor(name, x, y) {
|
||||||
|
this.name = name;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.renderedElement = new SVGNode(this);
|
||||||
|
this.renderedElement.appendIt(nods);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePoints() {
|
||||||
|
this.renderedElement.setMarks(this.marks, this.capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePosition(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.renderedElement.translate(x, y);
|
||||||
|
for (const elem of this.updateList) {
|
||||||
|
elem.updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
renderedElement;
|
||||||
|
name = "noname";
|
||||||
|
inEdges = [];
|
||||||
|
outEdges = [];
|
||||||
|
x;
|
||||||
|
y;
|
||||||
|
constructor(name, x, y) {
|
||||||
|
this.name = name;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.renderedElement = new SVGTRansition(this);
|
||||||
|
this.renderedElement.appendIt(nods);
|
||||||
|
}
|
||||||
|
canFire() {
|
||||||
|
for (const inE of this.inEdges) {
|
||||||
|
if (!inE.canSuck()) {
|
||||||
|
this.renderedElement.setCanFire(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const outE of this.outEdges) {
|
||||||
|
if (!outE.canPush()) {
|
||||||
|
this.renderedElement.setCanFire(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.renderedElement.setCanFire(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
fire() {
|
||||||
|
if (!this.canFire()) return false;
|
||||||
|
for (const inE of this.inEdges) {
|
||||||
|
inE.suck();
|
||||||
|
}
|
||||||
|
for (const outE of this.outEdges) {
|
||||||
|
outE.push();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
updatePosition(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.renderedElement.translate(x, y);
|
||||||
|
for (const inE of this.inEdges) {
|
||||||
|
inE.updatePosition();
|
||||||
|
}
|
||||||
|
for (const outE of this.outEdges) {
|
||||||
|
outE.updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let transitions = [];
|
||||||
|
let nodes = [];
|
||||||
|
|
||||||
|
function loop() {
|
||||||
|
for (const trans of transitions) {
|
||||||
|
console.log(trans.name, trans.fire());
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
for (const nod of nodes) {
|
||||||
|
nod.updatePoints();
|
||||||
|
}
|
||||||
|
for (const trans of transitions) {
|
||||||
|
trans.canFire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onresize = () => {
|
||||||
|
svg.setAttribute("viewBox", `0 0 ${window.innerWidth} ${window.innerHeight}`);
|
||||||
|
}
|
||||||
|
window.onresize();
|
||||||
|
|
||||||
|
|
||||||
|
let n1 = new Node("n1", 100, 100);
|
||||||
|
let n2 = new Node("n2", 100, 300);
|
||||||
|
let t1 = new Transition("t1", 100, 200);
|
||||||
|
t1.inEdges.push(new Edge(n1, t1, true))
|
||||||
|
t1.outEdges.push(new Edge(n2, t1, false));
|
||||||
|
|
||||||
|
nodes.push(n1, n2);
|
||||||
|
transitions.push(t1);
|
Loading…
Reference in a new issue