Created
February 21, 2019 06:43
-
-
Save Ljzn/72373cd644620476dc2203a5943d9d50 to your computer and use it in GitHub Desktop.
JS Bin // source https://jsbin.com/fiwuted
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>JS Bin</title> | |
</head> | |
<body> | |
<canvas id="canvas"></canvas> | |
<button id="start">START</button> | |
<button id="replay">REPLAY</button> | |
<button id="clear">CLEAR</button> | |
<p>put and connect to make a GRAPH, then click START</p> | |
<script id="jsbin-javascript"> | |
// CONSTS | |
const CWidth = 400 | |
const CHeight = 400 | |
const VRadius = 20 | |
const ColorPlatte = ['orange', 'yellow', 'green', 'cyan', 'blue', 'purple'] | |
const canvas = document.getElementById('canvas') | |
const BClear = document.getElementById('clear') | |
const BStart = document.getElementById('start') | |
const BReplay = document.getElementById('replay') | |
const ctx = canvas.getContext('2d') | |
canvas.width = CWidth | |
canvas.height = CHeight | |
const ROUNDTIME = 10 | |
const ReROUNDTIME = 3000 | |
// fake STRUCTS | |
const Point = (event) => { | |
return {x: event.clientX, y: event.clientY} | |
} | |
const Edge = (v1, v2) => { | |
return {v1: v1, v2: v2} | |
} | |
const Msg = (type, data, timeStamp) => { | |
return {type: type, data: data, timeStamp: timeStamp} | |
} | |
// HELPER FUNCTIONS | |
const drawVertex = (v) => { | |
let color = v.selected ? 'black' : v.color | |
let loc = v.location | |
ctx.fillStyle = color | |
ctx.beginPath() | |
ctx.arc(loc.x, loc.y, VRadius, 0, 2 * Math.PI, 0) | |
ctx.fill() | |
} | |
const drawEdge = (e) => { | |
let a = e.v1.location | |
let b = e.v2.location | |
ctx.beginPath() | |
ctx.lineWidth = 3 | |
ctx.moveTo(a.x, a.y) | |
ctx.lineTo(b.x, b.y) | |
ctx.stroke() | |
} | |
const drawRound = (r) => { | |
ctx.fillStyle = 'cyan' | |
ctx.font = '28px serif' | |
ctx.fillText('ROUND: ' + r, 20, 40) | |
} | |
const drawMsgText = (p, text) => { | |
ctx.fillStyle = 'purple' | |
ctx.font = '14px serif' | |
ctx.fillText(text, p.x, p.y) | |
} | |
const drawMsg = (from, to, text, beginTime) => { | |
let time = new Date() | |
let currentTime = time.getTime() | |
let t = currentTime - beginTime | |
if(t < ReROUNDTIME) { | |
let getPosition = (a, b) => { | |
return { | |
x: (b.x - a.x) / ReROUNDTIME * t + a.x, | |
y: (b.y - a.y) / ReROUNDTIME * t + a.y | |
} | |
} | |
let p = getPosition(from.location, to.location) | |
drawMsgText(p, text) | |
} | |
} | |
// ACTORS | |
class GraphMaker { | |
constructor() { | |
this.vertexs = [] | |
this.edges = [] | |
this.timer = false | |
this.timeStamp = 0 | |
this.logQ = [] | |
} | |
start() { | |
this.timer = setInterval(() => { | |
this.round() | |
}, ROUNDTIME) | |
} | |
putVertex(loc) { | |
let id = this.vertexs.length | |
let v = new Vertex(id, loc) | |
this.vertexs.push(v) | |
drawVertex(v) | |
} | |
round() { | |
if(this.timeStamp !== 50) { | |
this.timeStamp += 1 | |
this.vertexs.forEach((c) => { | |
c.round(this.timeStamp) | |
}) | |
} else { | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
} | |
} | |
inVertex(p) { | |
return this.vertexs.find((c) => { | |
return c.inField(p) | |
}) | |
} | |
addEdge(v1, v2) { | |
let e = this.edges.find((e) => { | |
return (e.v1 === v1 && e.v2 === v2) || (e.v1 === v2 && e.v2 === v1) | |
}) | |
if(!e) { | |
let nEdge = Edge(v1, v2) | |
this.edges.push(nEdge) | |
v1.addNeighbor(v2) | |
v2.addNeighbor(v1) | |
drawEdge(nEdge) | |
} | |
} | |
render() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
drawRound(this.timeStamp) | |
this.vertexs.forEach((v) => drawVertex(v)) | |
this.edges.forEach((e) => drawEdge(e)) | |
this.logQ.forEach((l) => { | |
drawMsg(l.from, l.to, l.msg.data, l.reTime) | |
}) | |
} | |
clear() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
this.vertexs = [] | |
this.edges = [] | |
this.logQ = [] | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
this.timer = false | |
this.timeStamp = 0 | |
} | |
resetColor() { | |
this.vertexs.forEach((v) => v.color = 'gray') | |
} | |
} | |
const graphMaker = new GraphMaker() | |
class LogCenter { | |
constructor() { | |
this.db = [] | |
} | |
log(from, to, msg) { | |
this.db.push({ | |
from: from, | |
to: to, | |
msg: msg, | |
reTime: false | |
}) | |
} | |
clear() { | |
this.db = [] | |
} | |
} | |
const logCenter = new LogCenter() | |
class Visualizer { | |
constructor() { | |
this.timer = false | |
} | |
clear() { | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
this.timer = false | |
} | |
replay() { | |
let round = 0 | |
graphMaker.resetColor() | |
graphMaker.logQ = [] | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
let roundState = (ts) => { | |
if(ts < 50) { | |
ts += 1 | |
graphMaker.timeStamp = ts | |
logCenter.db.forEach((log) => { | |
if(log.msg.timeStamp === ts) { | |
if(log.msg.type === 'color') { | |
log.from.color = log.msg.data | |
} | |
let time = new Date() | |
log.reTime = time.getTime() | |
graphMaker.logQ.push(log) | |
} | |
}) | |
this.timer = setTimeout(() => roundState(ts), ReROUNDTIME) | |
} | |
} | |
roundState(round) | |
} | |
} | |
const visualizer = new Visualizer() | |
class Vertex { | |
constructor(id, location) { | |
this.id = id | |
this.location = location | |
this.color = 'gray' | |
this.neighbors = [] | |
this.freeColors = ColorPlatte | |
this.selected = false | |
this.state = 'id' | |
this.mailBox = [] | |
} | |
select() { | |
this.selected = true | |
} | |
free() { | |
this.selected = false | |
} | |
round(ts) { | |
let nMsg | |
let localCompute = () => { | |
let consumeColor = (c) => { | |
this.freeColors = this.freeColors.filter((x) => x !== c) | |
} | |
let usedColors = this.msgOfType('color').map((m) => { | |
return m.msg.data | |
}) | |
usedColors.forEach((c) => consumeColor(c)) | |
let msg = (type, data) => { | |
return Msg(type, data, ts) | |
} | |
switch(this.state) { | |
case 'id': | |
this.state = 'deciding' | |
nMsg = msg('id', this.id) | |
break | |
case 'deciding': | |
if(this.isBiggerUncolored()) { | |
nMsg = msg('plain', 'undecided') | |
} else { | |
this.setColor() | |
this.state = 'colored' | |
nMsg = msg('color', this.color) | |
} | |
break | |
case 'colored': | |
break | |
} | |
this.mailBox = [] | |
if(nMsg) { | |
this.neighbors.forEach((v) => { | |
this.send(v, nMsg) | |
})} | |
} | |
setTimeout(localCompute, ROUNDTIME * 0.5) | |
} | |
send(d, msg) { | |
d.recv(this, msg) | |
logCenter.log(this, d, msg) | |
} | |
recv(from, msg) { | |
this.mailBox.push({from: from, msg: msg}) | |
} | |
setColor() { | |
this.color = this.freeColors[0] | |
} | |
addNeighbor(v) { | |
this.neighbors.push(v) | |
} | |
isBiggerUncolored() { | |
return !!this.mailBox.find((m) => { | |
return (m.from.id > this.id) && (m.msg.type !== 'color') | |
}) | |
} | |
inField(p) { | |
let inCircle = (c, p) => { | |
let d2 = (c.x-p.x)*(c.x-p.x)+(c.y-p.y)*(c.y-p.y) | |
// field is a bit larger than size | |
return d2 < (4*VRadius*VRadius) | |
} | |
return inCircle(this.location, p) | |
} | |
msgOfType(type) { | |
return this.mailBox.filter((m) => { | |
return m.msg.type === type | |
}) | |
} | |
} | |
class UserAgent { | |
constructor() { | |
this.state = 'putVertex' | |
this.currentV = false | |
} | |
handleEvent(event) { | |
let g = graphMaker | |
let p = Point(event) | |
let v = g.inVertex(p) | |
switch (this.state) { | |
case 'putVertex': | |
if(v) { | |
v.select() | |
this.currentV = v | |
this.state = 'chooseOne' | |
} else { | |
g.putVertex(p) | |
} | |
break | |
case 'chooseOne': | |
if(v) { | |
if(v !== this.currentV) { | |
this.currentV.free() | |
g.addEdge(v, this.currentV) | |
} | |
} else { | |
this.currentV.free() | |
} | |
this.state = 'putVertex' | |
break | |
} | |
} | |
clear() { | |
this.state = 'putVertex' | |
} | |
} | |
const userAgent = new UserAgent() | |
canvas.addEventListener('click', event => { | |
userAgent.handleEvent(event) | |
}) | |
// Clear all | |
BClear.addEventListener('click', event => { | |
graphMaker.clear() | |
userAgent.clear() | |
logCenter.clear() | |
visualizer.clear() | |
}) | |
// Start algorithm | |
BStart.addEventListener('click', e => { | |
graphMaker.start() | |
}) | |
BReplay.addEventListener('click', e => { | |
visualizer.replay() | |
}) | |
const draw = () => { | |
graphMaker.render() | |
window.requestAnimationFrame(draw) | |
} | |
window.requestAnimationFrame(draw) | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">// CONSTS | |
const CWidth = 400 | |
const CHeight = 400 | |
const VRadius = 20 | |
const ColorPlatte = ['orange', 'yellow', 'green', 'cyan', 'blue', 'purple'] | |
const canvas = document.getElementById('canvas') | |
const BClear = document.getElementById('clear') | |
const BStart = document.getElementById('start') | |
const BReplay = document.getElementById('replay') | |
const ctx = canvas.getContext('2d') | |
canvas.width = CWidth | |
canvas.height = CHeight | |
const ROUNDTIME = 10 | |
const ReROUNDTIME = 3000 | |
// fake STRUCTS | |
const Point = (event) => { | |
return {x: event.clientX, y: event.clientY} | |
} | |
const Edge = (v1, v2) => { | |
return {v1: v1, v2: v2} | |
} | |
const Msg = (type, data, timeStamp) => { | |
return {type: type, data: data, timeStamp: timeStamp} | |
} | |
// HELPER FUNCTIONS | |
const drawVertex = (v) => { | |
let color = v.selected ? 'black' : v.color | |
let loc = v.location | |
ctx.fillStyle = color | |
ctx.beginPath() | |
ctx.arc(loc.x, loc.y, VRadius, 0, 2 * Math.PI, 0) | |
ctx.fill() | |
} | |
const drawEdge = (e) => { | |
let a = e.v1.location | |
let b = e.v2.location | |
ctx.beginPath() | |
ctx.lineWidth = 3 | |
ctx.moveTo(a.x, a.y) | |
ctx.lineTo(b.x, b.y) | |
ctx.stroke() | |
} | |
const drawRound = (r) => { | |
ctx.fillStyle = 'cyan' | |
ctx.font = '28px serif' | |
ctx.fillText('ROUND: ' + r, 20, 40) | |
} | |
const drawMsgText = (p, text) => { | |
ctx.fillStyle = 'purple' | |
ctx.font = '14px serif' | |
ctx.fillText(text, p.x, p.y) | |
} | |
const drawMsg = (from, to, text, beginTime) => { | |
let time = new Date() | |
let currentTime = time.getTime() | |
let t = currentTime - beginTime | |
if(t < ReROUNDTIME) { | |
let getPosition = (a, b) => { | |
return { | |
x: (b.x - a.x) / ReROUNDTIME * t + a.x, | |
y: (b.y - a.y) / ReROUNDTIME * t + a.y | |
} | |
} | |
let p = getPosition(from.location, to.location) | |
drawMsgText(p, text) | |
} | |
} | |
// ACTORS | |
class GraphMaker { | |
constructor() { | |
this.vertexs = [] | |
this.edges = [] | |
this.timer = false | |
this.timeStamp = 0 | |
this.logQ = [] | |
} | |
start() { | |
this.timer = setInterval(() => { | |
this.round() | |
}, ROUNDTIME) | |
} | |
putVertex(loc) { | |
let id = this.vertexs.length | |
let v = new Vertex(id, loc) | |
this.vertexs.push(v) | |
drawVertex(v) | |
} | |
round() { | |
if(this.timeStamp !== 50) { | |
this.timeStamp += 1 | |
this.vertexs.forEach((c) => { | |
c.round(this.timeStamp) | |
}) | |
} else { | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
} | |
} | |
inVertex(p) { | |
return this.vertexs.find((c) => { | |
return c.inField(p) | |
}) | |
} | |
addEdge(v1, v2) { | |
let e = this.edges.find((e) => { | |
return (e.v1 === v1 && e.v2 === v2) || (e.v1 === v2 && e.v2 === v1) | |
}) | |
if(!e) { | |
let nEdge = Edge(v1, v2) | |
this.edges.push(nEdge) | |
v1.addNeighbor(v2) | |
v2.addNeighbor(v1) | |
drawEdge(nEdge) | |
} | |
} | |
render() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
drawRound(this.timeStamp) | |
this.vertexs.forEach((v) => drawVertex(v)) | |
this.edges.forEach((e) => drawEdge(e)) | |
this.logQ.forEach((l) => { | |
drawMsg(l.from, l.to, l.msg.data, l.reTime) | |
}) | |
} | |
clear() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
this.vertexs = [] | |
this.edges = [] | |
this.logQ = [] | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
this.timer = false | |
this.timeStamp = 0 | |
} | |
resetColor() { | |
this.vertexs.forEach((v) => v.color = 'gray') | |
} | |
} | |
const graphMaker = new GraphMaker() | |
class LogCenter { | |
constructor() { | |
this.db = [] | |
} | |
log(from, to, msg) { | |
this.db.push({ | |
from: from, | |
to: to, | |
msg: msg, | |
reTime: false | |
}) | |
} | |
clear() { | |
this.db = [] | |
} | |
} | |
const logCenter = new LogCenter() | |
class Visualizer { | |
constructor() { | |
this.timer = false | |
} | |
clear() { | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
this.timer = false | |
} | |
replay() { | |
let round = 0 | |
graphMaker.resetColor() | |
graphMaker.logQ = [] | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
let roundState = (ts) => { | |
if(ts < 50) { | |
ts += 1 | |
graphMaker.timeStamp = ts | |
logCenter.db.forEach((log) => { | |
if(log.msg.timeStamp === ts) { | |
if(log.msg.type === 'color') { | |
log.from.color = log.msg.data | |
} | |
let time = new Date() | |
log.reTime = time.getTime() | |
graphMaker.logQ.push(log) | |
} | |
}) | |
this.timer = setTimeout(() => roundState(ts), ReROUNDTIME) | |
} | |
} | |
roundState(round) | |
} | |
} | |
const visualizer = new Visualizer() | |
class Vertex { | |
constructor(id, location) { | |
this.id = id | |
this.location = location | |
this.color = 'gray' | |
this.neighbors = [] | |
this.freeColors = ColorPlatte | |
this.selected = false | |
this.state = 'id' | |
this.mailBox = [] | |
} | |
select() { | |
this.selected = true | |
} | |
free() { | |
this.selected = false | |
} | |
round(ts) { | |
let nMsg | |
let localCompute = () => { | |
let consumeColor = (c) => { | |
this.freeColors = this.freeColors.filter((x) => x !== c) | |
} | |
let usedColors = this.msgOfType('color').map((m) => { | |
return m.msg.data | |
}) | |
usedColors.forEach((c) => consumeColor(c)) | |
let msg = (type, data) => { | |
return Msg(type, data, ts) | |
} | |
switch(this.state) { | |
case 'id': | |
this.state = 'deciding' | |
nMsg = msg('id', this.id) | |
break | |
case 'deciding': | |
if(this.isBiggerUncolored()) { | |
nMsg = msg('plain', 'undecided') | |
} else { | |
this.setColor() | |
this.state = 'colored' | |
nMsg = msg('color', this.color) | |
} | |
break | |
case 'colored': | |
break | |
} | |
this.mailBox = [] | |
if(nMsg) { | |
this.neighbors.forEach((v) => { | |
this.send(v, nMsg) | |
})} | |
} | |
setTimeout(localCompute, ROUNDTIME * 0.5) | |
} | |
send(d, msg) { | |
d.recv(this, msg) | |
logCenter.log(this, d, msg) | |
} | |
recv(from, msg) { | |
this.mailBox.push({from: from, msg: msg}) | |
} | |
setColor() { | |
this.color = this.freeColors[0] | |
} | |
addNeighbor(v) { | |
this.neighbors.push(v) | |
} | |
isBiggerUncolored() { | |
return !!this.mailBox.find((m) => { | |
return (m.from.id > this.id) && (m.msg.type !== 'color') | |
}) | |
} | |
inField(p) { | |
let inCircle = (c, p) => { | |
let d2 = (c.x-p.x)*(c.x-p.x)+(c.y-p.y)*(c.y-p.y) | |
// field is a bit larger than size | |
return d2 < (4*VRadius*VRadius) | |
} | |
return inCircle(this.location, p) | |
} | |
msgOfType(type) { | |
return this.mailBox.filter((m) => { | |
return m.msg.type === type | |
}) | |
} | |
} | |
class UserAgent { | |
constructor() { | |
this.state = 'putVertex' | |
this.currentV = false | |
} | |
handleEvent(event) { | |
let g = graphMaker | |
let p = Point(event) | |
let v = g.inVertex(p) | |
switch (this.state) { | |
case 'putVertex': | |
if(v) { | |
v.select() | |
this.currentV = v | |
this.state = 'chooseOne' | |
} else { | |
g.putVertex(p) | |
} | |
break | |
case 'chooseOne': | |
if(v) { | |
if(v !== this.currentV) { | |
this.currentV.free() | |
g.addEdge(v, this.currentV) | |
} | |
} else { | |
this.currentV.free() | |
} | |
this.state = 'putVertex' | |
break | |
} | |
} | |
clear() { | |
this.state = 'putVertex' | |
} | |
} | |
const userAgent = new UserAgent() | |
canvas.addEventListener('click', event => { | |
userAgent.handleEvent(event) | |
}) | |
// Clear all | |
BClear.addEventListener('click', event => { | |
graphMaker.clear() | |
userAgent.clear() | |
logCenter.clear() | |
visualizer.clear() | |
}) | |
// Start algorithm | |
BStart.addEventListener('click', e => { | |
graphMaker.start() | |
}) | |
BReplay.addEventListener('click', e => { | |
visualizer.replay() | |
}) | |
const draw = () => { | |
graphMaker.render() | |
window.requestAnimationFrame(draw) | |
} | |
window.requestAnimationFrame(draw) | |
</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CONSTS | |
const CWidth = 400 | |
const CHeight = 400 | |
const VRadius = 20 | |
const ColorPlatte = ['orange', 'yellow', 'green', 'cyan', 'blue', 'purple'] | |
const canvas = document.getElementById('canvas') | |
const BClear = document.getElementById('clear') | |
const BStart = document.getElementById('start') | |
const BReplay = document.getElementById('replay') | |
const ctx = canvas.getContext('2d') | |
canvas.width = CWidth | |
canvas.height = CHeight | |
const ROUNDTIME = 10 | |
const ReROUNDTIME = 3000 | |
// fake STRUCTS | |
const Point = (event) => { | |
return {x: event.clientX, y: event.clientY} | |
} | |
const Edge = (v1, v2) => { | |
return {v1: v1, v2: v2} | |
} | |
const Msg = (type, data, timeStamp) => { | |
return {type: type, data: data, timeStamp: timeStamp} | |
} | |
// HELPER FUNCTIONS | |
const drawVertex = (v) => { | |
let color = v.selected ? 'black' : v.color | |
let loc = v.location | |
ctx.fillStyle = color | |
ctx.beginPath() | |
ctx.arc(loc.x, loc.y, VRadius, 0, 2 * Math.PI, 0) | |
ctx.fill() | |
} | |
const drawEdge = (e) => { | |
let a = e.v1.location | |
let b = e.v2.location | |
ctx.beginPath() | |
ctx.lineWidth = 3 | |
ctx.moveTo(a.x, a.y) | |
ctx.lineTo(b.x, b.y) | |
ctx.stroke() | |
} | |
const drawRound = (r) => { | |
ctx.fillStyle = 'cyan' | |
ctx.font = '28px serif' | |
ctx.fillText('ROUND: ' + r, 20, 40) | |
} | |
const drawMsgText = (p, text) => { | |
ctx.fillStyle = 'purple' | |
ctx.font = '14px serif' | |
ctx.fillText(text, p.x, p.y) | |
} | |
const drawMsg = (from, to, text, beginTime) => { | |
let time = new Date() | |
let currentTime = time.getTime() | |
let t = currentTime - beginTime | |
if(t < ReROUNDTIME) { | |
let getPosition = (a, b) => { | |
return { | |
x: (b.x - a.x) / ReROUNDTIME * t + a.x, | |
y: (b.y - a.y) / ReROUNDTIME * t + a.y | |
} | |
} | |
let p = getPosition(from.location, to.location) | |
drawMsgText(p, text) | |
} | |
} | |
// ACTORS | |
class GraphMaker { | |
constructor() { | |
this.vertexs = [] | |
this.edges = [] | |
this.timer = false | |
this.timeStamp = 0 | |
this.logQ = [] | |
} | |
start() { | |
this.timer = setInterval(() => { | |
this.round() | |
}, ROUNDTIME) | |
} | |
putVertex(loc) { | |
let id = this.vertexs.length | |
let v = new Vertex(id, loc) | |
this.vertexs.push(v) | |
drawVertex(v) | |
} | |
round() { | |
if(this.timeStamp !== 50) { | |
this.timeStamp += 1 | |
this.vertexs.forEach((c) => { | |
c.round(this.timeStamp) | |
}) | |
} else { | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
} | |
} | |
inVertex(p) { | |
return this.vertexs.find((c) => { | |
return c.inField(p) | |
}) | |
} | |
addEdge(v1, v2) { | |
let e = this.edges.find((e) => { | |
return (e.v1 === v1 && e.v2 === v2) || (e.v1 === v2 && e.v2 === v1) | |
}) | |
if(!e) { | |
let nEdge = Edge(v1, v2) | |
this.edges.push(nEdge) | |
v1.addNeighbor(v2) | |
v2.addNeighbor(v1) | |
drawEdge(nEdge) | |
} | |
} | |
render() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
drawRound(this.timeStamp) | |
this.vertexs.forEach((v) => drawVertex(v)) | |
this.edges.forEach((e) => drawEdge(e)) | |
this.logQ.forEach((l) => { | |
drawMsg(l.from, l.to, l.msg.data, l.reTime) | |
}) | |
} | |
clear() { | |
ctx.clearRect(0, 0, CWidth, CHeight) | |
this.vertexs = [] | |
this.edges = [] | |
this.logQ = [] | |
if(this.timer) { | |
clearInterval(this.timer) | |
} | |
this.timer = false | |
this.timeStamp = 0 | |
} | |
resetColor() { | |
this.vertexs.forEach((v) => v.color = 'gray') | |
} | |
} | |
const graphMaker = new GraphMaker() | |
class LogCenter { | |
constructor() { | |
this.db = [] | |
} | |
log(from, to, msg) { | |
this.db.push({ | |
from: from, | |
to: to, | |
msg: msg, | |
reTime: false | |
}) | |
} | |
clear() { | |
this.db = [] | |
} | |
} | |
const logCenter = new LogCenter() | |
class Visualizer { | |
constructor() { | |
this.timer = false | |
} | |
clear() { | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
this.timer = false | |
} | |
replay() { | |
let round = 0 | |
graphMaker.resetColor() | |
graphMaker.logQ = [] | |
if(this.timer) { | |
clearTimeout(this.timer) | |
} | |
let roundState = (ts) => { | |
if(ts < 50) { | |
ts += 1 | |
graphMaker.timeStamp = ts | |
logCenter.db.forEach((log) => { | |
if(log.msg.timeStamp === ts) { | |
if(log.msg.type === 'color') { | |
log.from.color = log.msg.data | |
} | |
let time = new Date() | |
log.reTime = time.getTime() | |
graphMaker.logQ.push(log) | |
} | |
}) | |
this.timer = setTimeout(() => roundState(ts), ReROUNDTIME) | |
} | |
} | |
roundState(round) | |
} | |
} | |
const visualizer = new Visualizer() | |
class Vertex { | |
constructor(id, location) { | |
this.id = id | |
this.location = location | |
this.color = 'gray' | |
this.neighbors = [] | |
this.freeColors = ColorPlatte | |
this.selected = false | |
this.state = 'id' | |
this.mailBox = [] | |
} | |
select() { | |
this.selected = true | |
} | |
free() { | |
this.selected = false | |
} | |
round(ts) { | |
let nMsg | |
let localCompute = () => { | |
let consumeColor = (c) => { | |
this.freeColors = this.freeColors.filter((x) => x !== c) | |
} | |
let usedColors = this.msgOfType('color').map((m) => { | |
return m.msg.data | |
}) | |
usedColors.forEach((c) => consumeColor(c)) | |
let msg = (type, data) => { | |
return Msg(type, data, ts) | |
} | |
switch(this.state) { | |
case 'id': | |
this.state = 'deciding' | |
nMsg = msg('id', this.id) | |
break | |
case 'deciding': | |
if(this.isBiggerUncolored()) { | |
nMsg = msg('plain', 'undecided') | |
} else { | |
this.setColor() | |
this.state = 'colored' | |
nMsg = msg('color', this.color) | |
} | |
break | |
case 'colored': | |
break | |
} | |
this.mailBox = [] | |
if(nMsg) { | |
this.neighbors.forEach((v) => { | |
this.send(v, nMsg) | |
})} | |
} | |
setTimeout(localCompute, ROUNDTIME * 0.5) | |
} | |
send(d, msg) { | |
d.recv(this, msg) | |
logCenter.log(this, d, msg) | |
} | |
recv(from, msg) { | |
this.mailBox.push({from: from, msg: msg}) | |
} | |
setColor() { | |
this.color = this.freeColors[0] | |
} | |
addNeighbor(v) { | |
this.neighbors.push(v) | |
} | |
isBiggerUncolored() { | |
return !!this.mailBox.find((m) => { | |
return (m.from.id > this.id) && (m.msg.type !== 'color') | |
}) | |
} | |
inField(p) { | |
let inCircle = (c, p) => { | |
let d2 = (c.x-p.x)*(c.x-p.x)+(c.y-p.y)*(c.y-p.y) | |
// field is a bit larger than size | |
return d2 < (4*VRadius*VRadius) | |
} | |
return inCircle(this.location, p) | |
} | |
msgOfType(type) { | |
return this.mailBox.filter((m) => { | |
return m.msg.type === type | |
}) | |
} | |
} | |
class UserAgent { | |
constructor() { | |
this.state = 'putVertex' | |
this.currentV = false | |
} | |
handleEvent(event) { | |
let g = graphMaker | |
let p = Point(event) | |
let v = g.inVertex(p) | |
switch (this.state) { | |
case 'putVertex': | |
if(v) { | |
v.select() | |
this.currentV = v | |
this.state = 'chooseOne' | |
} else { | |
g.putVertex(p) | |
} | |
break | |
case 'chooseOne': | |
if(v) { | |
if(v !== this.currentV) { | |
this.currentV.free() | |
g.addEdge(v, this.currentV) | |
} | |
} else { | |
this.currentV.free() | |
} | |
this.state = 'putVertex' | |
break | |
} | |
} | |
clear() { | |
this.state = 'putVertex' | |
} | |
} | |
const userAgent = new UserAgent() | |
canvas.addEventListener('click', event => { | |
userAgent.handleEvent(event) | |
}) | |
// Clear all | |
BClear.addEventListener('click', event => { | |
graphMaker.clear() | |
userAgent.clear() | |
logCenter.clear() | |
visualizer.clear() | |
}) | |
// Start algorithm | |
BStart.addEventListener('click', e => { | |
graphMaker.start() | |
}) | |
BReplay.addEventListener('click', e => { | |
visualizer.replay() | |
}) | |
const draw = () => { | |
graphMaker.render() | |
window.requestAnimationFrame(draw) | |
} | |
window.requestAnimationFrame(draw) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment