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