@@ -0,0 +1,32 @@ | |||
from flask import Flask, request, redirect, url_for, make_response, render_template, Markup, send_from_directory, send_file | |||
from flask_redis import FlaskRedis | |||
import json, abp | |||
app = Flask(__name__) | |||
redis = FlaskRedis(app) | |||
@app.route("/") | |||
def index(): | |||
return render_template("index.html") | |||
@app.route("/graph", methods=["GET", "POST"]) | |||
def graph(page): | |||
if request.method == 'POST': | |||
# Convert the data to a graph state | |||
g = abp.GraphState() | |||
g.from_json(json.loads(data)) | |||
# Convert it back to JSON | |||
data = json.dumps(g.to_json(stringify=True)) | |||
# Insert into the database | |||
redis.execute_command("SET", "graph", data) | |||
# Return success | |||
return "OK" | |||
else: | |||
# Get from the database | |||
return redis.get("graph") | |||
@@ -0,0 +1,91 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:xlink="http://www.w3.org/1999/xlink" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="64" | |||
height="64" | |||
id="svg2" | |||
version="1.1" | |||
inkscape:version="0.48.4 r9939" | |||
sodipodi:docname="New document 1"> | |||
<defs | |||
id="defs4"> | |||
<linearGradient | |||
id="linearGradient3763"> | |||
<stop | |||
style="stop-color:#ffffff;stop-opacity:1;" | |||
offset="0" | |||
id="stop3765" /> | |||
<stop | |||
style="stop-color:#000000;stop-opacity:1;" | |||
offset="1" | |||
id="stop3767" /> | |||
</linearGradient> | |||
<radialGradient | |||
inkscape:collect="always" | |||
xlink:href="#linearGradient3763" | |||
id="radialGradient3771" | |||
cx="38.780914" | |||
cy="23.33404" | |||
fx="38.780914" | |||
fy="23.33404" | |||
r="32.661938" | |||
gradientUnits="userSpaceOnUse" | |||
gradientTransform="matrix(1.1215552,0.13870084,-0.14202472,1.1484324,1.1779947,-9.189628)" /> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="3.959798" | |||
inkscape:cx="-12.465613" | |||
inkscape:cy="8.3918647" | |||
inkscape:document-units="px" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
inkscape:snap-page="false" | |||
inkscape:window-width="1366" | |||
inkscape:window-height="721" | |||
inkscape:window-x="0" | |||
inkscape:window-y="0" | |||
inkscape:window-maximized="1" /> | |||
<metadata | |||
id="metadata7"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(0,-988.36218)"> | |||
<path | |||
sodipodi:type="arc" | |||
style="fill:url(#radialGradient3771);fill-opacity:1;stroke:#000000;stroke-width:1.32387710000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" | |||
id="path2993" | |||
sodipodi:cx="32" | |||
sodipodi:cy="32" | |||
sodipodi:rx="32" | |||
sodipodi:ry="32" | |||
d="M 64,32 A 32,32 0 1 1 0,32 32,32 0 1 1 64,32 z" | |||
transform="matrix(0.94419643,0,0,0.94419643,1.7857143,990.14789)" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,91 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="64" | |||
height="64" | |||
id="svg2" | |||
version="1.1" | |||
inkscape:version="0.48.4 r9939" | |||
sodipodi:docname="tip.svg"> | |||
<defs | |||
id="defs4"> | |||
<linearGradient | |||
id="linearGradient3763"> | |||
<stop | |||
style="stop-color:#ffffff;stop-opacity:1;" | |||
offset="0" | |||
id="stop3765" /> | |||
<stop | |||
style="stop-color:#000000;stop-opacity:1;" | |||
offset="1" | |||
id="stop3767" /> | |||
</linearGradient> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="3.959798" | |||
inkscape:cx="5.4453989" | |||
inkscape:cy="4.9448826" | |||
inkscape:document-units="px" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
inkscape:snap-page="true" | |||
inkscape:window-width="1366" | |||
inkscape:window-height="721" | |||
inkscape:window-x="0" | |||
inkscape:window-y="0" | |||
inkscape:window-maximized="1" | |||
inkscape:snap-center="true" | |||
inkscape:object-nodes="true" /> | |||
<metadata | |||
id="metadata7"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(0,-988.36218)"> | |||
<path | |||
sodipodi:type="arc" | |||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.32387710000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.29302326" | |||
id="path2993" | |||
sodipodi:cx="32" | |||
sodipodi:cy="32" | |||
sodipodi:rx="32" | |||
sodipodi:ry="32" | |||
d="M 64,32 A 32,32 0 1 1 0,32 32,32 0 1 1 64,32 z" | |||
transform="matrix(0.94419643,0,0,0.94419643,1.7857143,990.14789)" /> | |||
<path | |||
style="color:#000000;fill:none;stroke:#000000;stroke-width:9.65;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | |||
d="m 32,996.21932 0,48.28568" | |||
id="path3811" | |||
inkscape:connector-curvature="0" /> | |||
<path | |||
style="color:#000000;fill:none;stroke:#000000;stroke-width:9.65;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | |||
d="m 7.8571427,1020.3622 48.2857143,0" | |||
id="path3813" | |||
inkscape:connector-curvature="0" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,92 @@ | |||
html, body { margin: 0; padding: 0; overflow: hidden; font-size: 10pt; font-family: monospace; } | |||
#node_info { | |||
background: rgba(0, 0, 0, .8); | |||
color:white; | |||
padding: 5px; | |||
margin:0px; | |||
position: absolute; | |||
top:5px; | |||
left:5px; | |||
font-family: monospace; | |||
text-align: center; | |||
font-size:9pt; | |||
/*height:15px;*/ | |||
border-radius:3px; | |||
pointer-events: none; | |||
} | |||
#server_info { | |||
background-color: black; | |||
color:white; | |||
padding: 10px; | |||
font-family: monospace; | |||
position: absolute; | |||
top: 10px; | |||
right: 10px; | |||
font-size: 9pt; | |||
} | |||
#node_name { | |||
font-size: 12pt; | |||
} | |||
#node_data { | |||
background-color: black; | |||
color:white; | |||
padding: 10px; | |||
font-family: monospace; | |||
position: absolute; | |||
top: 10px; | |||
left: 10px; | |||
font-size: 9pt; | |||
} | |||
#version { | |||
color:black; | |||
padding: 10px; | |||
font-family: monospace; | |||
position: absolute; | |||
bottom: 10px; | |||
left: 10px; | |||
font-size: 9pt; | |||
} | |||
ul { | |||
list-style-type: none; | |||
padding: 0px; | |||
margin: 0px; | |||
} | |||
li{ | |||
padding:3px; | |||
padding-left: 0em; | |||
} | |||
.visible { | |||
visibility: visible; | |||
opacity: 1; | |||
transform: scale(1); | |||
transition: opacity .08s linear, transform .08s linear; | |||
} | |||
.hidden { | |||
visibility: hidden; | |||
opacity: 0; | |||
transform: scale(.5); | |||
transition: visibility .08s, opacity .08s linear, transform .08s linear; | |||
} | |||
a { | |||
color: yellow; | |||
} | |||
h3 { | |||
padding-top: 0px; | |||
padding-bottom: 0px; | |||
margin-top: 2px; | |||
margin-bottom: 2px; | |||
} |
@@ -0,0 +1 @@ | |||
qcengine |
@@ -0,0 +1,73 @@ | |||
var abj = {}; | |||
abj.node = {}; | |||
abj.adj = {}; | |||
abj.add_node = function(node, meta) { | |||
if (meta === undefined){meta = {};} | |||
abj.adj[node] = {}; | |||
abj.node[node] = {}; | |||
abj.node[node].vop = tables.clifford.hadamard; | |||
Object.assign(abj.node[node], meta); | |||
}; | |||
abj.add_nodes = function(nodes) { | |||
nodes.forEach(add_node); | |||
}; | |||
abj.del_node = function(node) { | |||
for (var i in abj.adj[node]) { | |||
abj.del_edge(node, i); | |||
} | |||
delete abj.node[node]; | |||
}; | |||
abj.add_edge = function(a, b) { | |||
abj.adj[a][b] = {}; | |||
abj.adj[b][a] = {}; | |||
}; | |||
abj.add_edges = function(edges) { | |||
edges.forEach(function(e) { | |||
add_edge(e[0], e[1]); | |||
}); | |||
}; | |||
abj.del_edge = function(a, b) { | |||
delete abj.adj[a][b]; | |||
delete abj.adj[b][a]; | |||
}; | |||
abj.has_edge = function(a, b) { | |||
return Object.prototype.hasOwnProperty.call(abj.adj[a], b); | |||
}; | |||
abj.is_sole_member = function(group, node) { | |||
// TODO: this is slow as heck | |||
var keys = Object.keys(group); | |||
return keys.length == 1 && keys[0] == node; | |||
}; | |||
abj.update = function(newState) { | |||
abj.node = newState.node; | |||
abj.adj = newState.adj; | |||
}; | |||
abj.order = function(){ | |||
return Object.keys(abj.node).length; | |||
}; | |||
abj.edgelist = function() { | |||
var seen = {}; | |||
var output = []; | |||
for (var i in abj.adj) { | |||
for (var j in abj.adj[i]) { | |||
if (!Object.prototype.hasOwnProperty.call(seen, j)) { | |||
output.push([i, j]); | |||
} | |||
} | |||
seen[i] = true; | |||
} | |||
return output; | |||
}; | |||
@@ -0,0 +1,190 @@ | |||
var editor = {}; | |||
var pi2 = Math.PI / 2; | |||
editor.selection = undefined; | |||
editor.mouseOver = undefined; | |||
editor.orientations = [ | |||
new THREE.Euler(pi2, 0, 0), | |||
new THREE.Euler(0, 0, 0), | |||
new THREE.Euler(pi2, 0, pi2), | |||
]; | |||
editor.onFreeMove = function() { | |||
var found = editor.findNodeOnRay(mouse.ray); | |||
if (editor.mouseOver !== found) { | |||
editor.mouseOver = found; | |||
if (found) { | |||
var n = abj.node[found]; | |||
var s = "Node " + found + "<br/> "; | |||
for (var i in n) { | |||
if (i!="position"){ | |||
s += i + ":" + n[i] + " "; | |||
} | |||
} | |||
s += ""; | |||
gui.nodeMessage(s); | |||
} else { | |||
gui.hideNodeMessage(); | |||
} | |||
} | |||
}; | |||
editor.focus = function(node) { | |||
gui.hideNodeMessage(); | |||
editor.selection = node; | |||
//gui.serverMessage("Selected node " + node + "."); | |||
}; | |||
editor.addQubitAtPoint = function(point) { | |||
if (point === null) { | |||
return; | |||
} | |||
point.round(); | |||
var new_node = Math.floor(point.x) + "," + Math.floor(point.y) + "," + Math.floor(point.z); | |||
if (Object.prototype.hasOwnProperty.call(abj.node, new_node)) { | |||
gui.serverMessage("Node " + new_node +" already exists."); | |||
return; | |||
} | |||
websocket.edit({action:"create", name:new_node, position: point}); | |||
editor.focus(new_node); | |||
gui.serverMessage("Created node " + new_node +"."); | |||
}; | |||
editor.onClick = function() { | |||
var found = editor.findNodeOnRay(mouse.ray); | |||
if (found) { | |||
editor.focus(found); | |||
var node=found; | |||
editor.grid.position.copy(abj.node[node].position); | |||
gui.controls.target.copy(abj.node[node].position); | |||
node_name.innerHTML = "Node " + node; | |||
node_data.className = "visible"; | |||
node_vop.innerHTML = "VOP: " + abj.node[node].vop; | |||
} else { | |||
var intersection = mouse.ray.intersectPlane(editor.plane); | |||
if (intersection !== null) { | |||
editor.addQubitAtPoint(intersection); | |||
} | |||
} | |||
}; | |||
editor.onShiftClick = function() { | |||
var found = editor.findNodeOnRay(mouse.ray); | |||
if (found === undefined){ return; } | |||
if (editor.selection === undefined){ return; } | |||
if (found === editor.selection){ return; } | |||
//abj.act_cz(found, editor.selection); | |||
websocket.edit({action:"cz", start:found, end:editor.selection}); | |||
gui.serverMessage("Acted CZ between " + found + " & " + editor.selection + "."); | |||
editor.focus(found); | |||
}; | |||
editor.onCtrlClick = function() { | |||
var found = editor.findNodeOnRay(mouse.ray); | |||
if (found === undefined){ return; } | |||
if (editor.selection === undefined){ return; } | |||
editor.focus(found); | |||
websocket.edit({action:"hadamard", node:found}); | |||
gui.serverMessage("Acted H on node " + found + "."); | |||
}; | |||
editor.prepare = function() { | |||
mouse.onFreeMove = editor.onFreeMove; | |||
mouse.onClick = editor.onClick; | |||
mouse.onShiftClick = editor.onShiftClick; | |||
mouse.onCtrlClick = editor.onCtrlClick; | |||
document.addEventListener("keydown", editor.onKey, false); | |||
editor.makeGrid(); | |||
}; | |||
editor.onKey = function(evt) { | |||
if (evt.keyCode === 32) { | |||
editor.setOrientation((editor.orientation + 1) % 3); | |||
} | |||
if (evt.keyCode === 46 || evt.keyCode === 68) { | |||
editor.deleteNode(); | |||
} | |||
}; | |||
editor.setOrientation = function(orientation) { | |||
editor.orientation = orientation; | |||
var rotation = editor.orientations[orientation]; | |||
var normal = new THREE.Vector3(0, 1, 0); | |||
normal.applyEuler(rotation); | |||
editor.grid.rotation.copy(rotation); | |||
editor.plane = new THREE.Plane(); | |||
editor.plane.setFromNormalAndCoplanarPoint(normal, editor.grid.position); | |||
gui.render(); | |||
}; | |||
editor.makeGrid = function() { | |||
editor.grid = new THREE.GridHelper(10, 1); | |||
editor.grid.setColors(0xbbbbbb, 0xeeeeee); | |||
editor.grid.renderOrder = 1000; | |||
editor.setOrientation(0); | |||
gui.scene.add(editor.grid); | |||
gui.scene.children[0].renderOrder = -3000; | |||
}; | |||
editor.update = function() {}; | |||
// Gets a reference to the node nearest to the mouse cursor | |||
editor.findNodeOnRay = function(ray) { | |||
for (var n in abj.node) { | |||
if (ray.distanceSqToPoint(abj.node[n].position) < 0.012) { | |||
return n; | |||
} | |||
} | |||
return undefined; | |||
}; | |||
editor.deleteNode = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"delete", node:editor.selection}); | |||
gui.serverMessage("Deleted node " + editor.selection + "."); | |||
editor.selection = undefined; | |||
node_data.className = "hidden"; | |||
}; | |||
//TODO: loadsa space for DRY here | |||
editor.hadamard = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"hadamard", node:editor.selection}); | |||
gui.serverMessage("Acted Hadamard on node " + editor.selection + "."); | |||
}; | |||
editor.phase = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"phase", node:editor.selection}); | |||
gui.serverMessage("Acted phase on node " + editor.selection + "."); | |||
}; | |||
editor.measureX = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"measure", node:editor.selection, basis:"x"}); | |||
gui.serverMessage("Measured node " + editor.selection + " in X."); | |||
}; | |||
editor.measureY = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"measure", node:editor.selection, basis:"y"}); | |||
gui.serverMessage("Measured node " + editor.selection + " in Y."); | |||
}; | |||
editor.measureZ = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"measure", node:editor.selection, basis:"z"}); | |||
gui.serverMessage("Measured node " + editor.selection + " in z."); | |||
}; | |||
editor.localComplementation = function() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"localcomplementation", node:editor.selection}); | |||
abj.local_complementation(editor.selection); | |||
gui.serverMessage("Inverted neighbourhood of " + editor.selection + "."); | |||
}; |
@@ -0,0 +1,76 @@ | |||
var graph = {}; | |||
graph.colors = ["red", "green"]; | |||
graph.prepare = function() { | |||
materials.prepare(); | |||
websocket.connect(graph.update); | |||
}; | |||
graph.center = function() { | |||
var middle = new THREE.Vector3(0, 0, 0); | |||
for (var i in abj.node) { | |||
middle = middle.add(abj.node[i].position); | |||
} | |||
middle = middle.multiplyScalar(1.0/abj.order()); | |||
return middle; | |||
}; | |||
graph.update = function(newState) { | |||
if (newState){abj.update(newState);} | |||
var gs = gui.scene.getObjectByName("graphstate"); | |||
if (gs){ gui.scene.remove(gs); } | |||
var geometry = new THREE.Geometry(); | |||
geometry.colors = []; | |||
for (var i in abj.node) { | |||
var color = graph.colors[abj.node[i].vop % graph.colors.length]; | |||
if (abj.node[i].color !== undefined){ | |||
color = abj.node[i].color; | |||
} | |||
geometry.vertices.push(abj.node[i].position); | |||
geometry.colors.push(new THREE.Color(color)); | |||
} | |||
graph.center(); | |||
gui.controls.target.copy(graph.center()); | |||
var edges = new THREE.Object3D(); | |||
var my_edges = abj.edgelist(); | |||
for (i = 0; i < my_edges.length; ++i) { | |||
var edge = my_edges[i]; | |||
var start = abj.node[edge[0]].position; | |||
var startpos = new THREE.Vector3(start.x, start.y, start.z); | |||
var end = abj.node[edge[1]].position; | |||
var endpos = new THREE.Vector3(end.x, end.y, end.z); | |||
var newEdge = materials.makeCurve(startpos, endpos); | |||
edges.add(newEdge); | |||
} | |||
if (editor.selection) { | |||
console.log(editor.selection); | |||
var node = editor.selection; | |||
if (Object.prototype.hasOwnProperty.call(abj.node, node)) { | |||
editor.grid.position.copy(abj.node[node].position); | |||
gui.controls.target.copy(abj.node[node].position); | |||
node_name.innerHTML = "Node " + node; | |||
node_data.className = "visible"; | |||
node_vop.innerHTML = "VOP: " + abj.node[node].vop; | |||
} else { | |||
editor.selection = undefined; | |||
node_data.className = "hidden"; | |||
} | |||
} else { | |||
node_data.className = "hidden"; | |||
} | |||
var particles = new THREE.Points(geometry, materials.qubit); | |||
var object = new THREE.Object3D(); | |||
object.name = "graphstate"; | |||
object.add(particles); | |||
object.add(edges); | |||
gui.scene.add(object); | |||
gui.render(); | |||
}; | |||
@@ -0,0 +1,80 @@ | |||
var gui = {}; | |||
gui.prepare = function() { | |||
gui.renderer = new THREE.WebGLRenderer({ | |||
"antialias": true | |||
}); | |||
gui.renderer.setSize(window.innerWidth, window.innerHeight); | |||
gui.renderer.setClearColor(0xffffff, 1); | |||
document.querySelector("body").appendChild(gui.renderer.domElement); | |||
window.addEventListener("resize", gui.onWindowResize, false); | |||
gui.makeScene(); | |||
gui.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.3, 1000); | |||
gui.controls = new THREE.OrbitControls(gui.camera); | |||
gui.controls.addEventListener("change", gui.render); | |||
gui.controls.center.set(0, 0, 0); | |||
gui.controls.target.set(0, 0, 0); | |||
gui.controls.rotateSpeed = 0.2; | |||
gui.controls.userPanSpeed = 0.1; | |||
gui.camera.position.set(0, 0, 10); | |||
gui.controls.autoRotate = true; | |||
gui.controls.autoRotateSpeed = 0.1; | |||
}; | |||
// Someone resized the window | |||
gui.onWindowResize = function(evt) { | |||
gui.camera.aspect = window.innerWidth / window.innerHeight; | |||
gui.camera.updateProjectionMatrix(); | |||
gui.renderer.setSize(window.innerWidth, window.innerHeight); | |||
gui.render(); | |||
}; | |||
// Render the current frame to the screen | |||
gui.render = function() { | |||
requestAnimationFrame(function() { | |||
gui.renderer.render(gui.scene, gui.camera); | |||
}); | |||
}; | |||
// Make the extra bits of gui | |||
gui.makeScene = function() { | |||
gui.scene = new THREE.Scene(); | |||
}; | |||
// Put an HTML message to the screen | |||
// TODO: write a generic messaging class? | |||
gui.serverMessage = function(msgtext, persist) { | |||
if (persist === undefined) {persist = false;} | |||
server_info.innerHTML = msgtext; | |||
server_info.className = "visible"; | |||
clearInterval(gui.ki); | |||
if (!persist){ | |||
gui.ki = setInterval(function(){server_info.className="hidden";}, 3000); | |||
} | |||
}; | |||
gui.nodeMessage = function(msgtext) { | |||
node_info.innerHTML = msgtext; | |||
node_info.className = "visible"; | |||
}; | |||
gui.hideNodeMessage = function(){ | |||
node_info.className = "hidden"; | |||
}; | |||
// Set the position of the info popup | |||
gui.setInfoPosition = function(position){ | |||
w = node_info.offsetWidth; | |||
h = node_info.offsetHeight; | |||
node_info.style.left = position.x - w/2 + "px"; | |||
node_info.style.top = position.y - h -10 + "px"; | |||
}; | |||
// The main loop | |||
gui.loop = function() { | |||
gui.controls.update(); | |||
editor.update(); | |||
requestAnimationFrame(gui.loop); | |||
}; | |||
@@ -0,0 +1,58 @@ | |||
var mouse = {}; | |||
var interaction = {}; | |||
interaction.raycaster = new THREE.Raycaster(); | |||
interaction.xyplane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); | |||
// Gets a reference to the node nearest to the mouse cursor | |||
interaction.nearestNode = function() { | |||
this.raycaster.setFromCamera(mouse, camera); | |||
for (var i = 0; i < nodeGeometry.vertices.length; ++i) { | |||
var v = nodeGeometry.vertices[i]; | |||
if (this.raycaster.ray.distanceSqToPoint(v) < 0.01) { | |||
return i; | |||
} | |||
} | |||
return undefined; | |||
}; | |||
// Find out: what is the mouse pointing at? | |||
interaction.checkIntersections = function() { | |||
var new_selection = nearestNode(); | |||
if (new_selection != this.selection) { | |||
this.selection = new_selection; | |||
info.className = this.selection ? "visible" : "hidden"; | |||
info.innerHTML = this.selection ? nodeGeometry.labels[new_selection] : info.innerHTML; | |||
render(); | |||
} | |||
}; | |||
// Update the mouse position tracker | |||
interaction.onMouseMove = function(event) { | |||
mouse.wasClick = false; | |||
mouse.absx = event.clientX; | |||
mouse.absy = event.clientY; | |||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |||
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; | |||
//w = 200; //h = 15; //info.style.top = mouse.absy - h - 40 + "px"; //info.style.left = mouse.absx - w / 2 + "px"; //checkIntersections(); | |||
}; | |||
// Try to add a qubit at the current mouse position | |||
interaction.addQubitAtMouse = function(event) { | |||
this.raycaster.setFromCamera(mouse, camera); | |||
var intersection = this.raycaster.ray.intersectPlane(this.plane); | |||
intersection.x = Math.round(intersection.x); | |||
intersection.y = Math.round(intersection.y); | |||
abj.add_node(Object.keys(vops).length, { | |||
"position": intersection | |||
}); | |||
updateScene(); | |||
} | |||
interaction.bind = function() { | |||
var el = renderer.domElement; | |||
el.addEventListener("mousedown", this.onMouseDown); | |||
el.addEventListener("mouseup", this.onMouseDown); | |||
el.addEventListener("mousemove", this.onMouseMove); | |||
}; |
@@ -0,0 +1,10 @@ | |||
console.log("abp v0.4.27"); | |||
window.onload = function() { | |||
graph.prepare(); | |||
materials.prepare(); | |||
gui.prepare(); | |||
mouse.prepare(); | |||
editor.prepare(); | |||
gui.loop(); | |||
}; |
@@ -0,0 +1,38 @@ | |||
var materials = {}; | |||
var curveProperties = { | |||
splineDensity: 10, | |||
curvature: 20 | |||
}; | |||
// Is called on boot | |||
materials.prepare = function() { | |||
var ballSprite = new THREE.Texture(document.getElementById("ball")); | |||
ballSprite.needsUpdate = true; | |||
materials.edge = new THREE.LineBasicMaterial({ | |||
color: "gray", | |||
transparent: false, | |||
linewidth: 3 | |||
}); | |||
materials.edge.depthTest = false; | |||
materials.qubit = new THREE.PointsMaterial({ | |||
size: 0.6, | |||
map: ballSprite, | |||
alphaTest: 0.5, | |||
transparent: true, | |||
vertexColors: THREE.VertexColors | |||
}); | |||
}; | |||
materials.makeCurve = function(a, b) { | |||
var length = new THREE.Vector3().subVectors(a, b).length(); | |||
var bend = new THREE.Vector3(length / curveProperties.curvature, length / curveProperties.curvature, 0); | |||
var mid = new THREE.Vector3().add(a).add(b).multiplyScalar(0.5).add(bend); | |||
var spline = new THREE.CatmullRomCurve3([a, mid, b]); | |||
var geometry = new THREE.Geometry(); | |||
var splinePoints = spline.getPoints(curveProperties.splineDensity); | |||
Array.prototype.push.apply(geometry.vertices, splinePoints); | |||
return new THREE.Line(geometry, materials.edge); | |||
}; | |||
@@ -0,0 +1,73 @@ | |||
var mouse = {}; | |||
mouse.wasClick = true; | |||
mouse.pressed = false; | |||
mouse.leniency = 4; | |||
mouse.raycaster = new THREE.Raycaster(); | |||
mouse.onFreeMove = function() { | |||
console.log("Free move"); | |||
}; | |||
mouse.onDrag = function() { | |||
//console.log("Drag"); | |||
}; | |||
mouse.onClick = function() { | |||
console.log("Click"); | |||
}; | |||
mouse.onCtrlClick = function() { | |||
console.log("Ctrl-click"); | |||
}; | |||
mouse.onShiftClick = function() { | |||
console.log("Shift-click"); | |||
}; | |||
mouse.prepare = function() { | |||
var el = gui.renderer.domElement; | |||
el.addEventListener("mousedown", mouse.onDown); | |||
el.addEventListener("mouseup", mouse.onUp); | |||
el.addEventListener("mousemove", mouse.onMove); | |||
}; | |||
mouse.onDown = function(event) { | |||
mouse.wasClick = true; | |||
mouse.pressed = true; | |||
mouse.startX = event.clientX; | |||
mouse.startY = event.clientY; | |||
}; | |||
mouse.onUp = function(event) { | |||
mouse.pressed = false; | |||
if (!mouse.wasClick) { | |||
return; | |||
} | |||
if (event.ctrlKey) { | |||
mouse.onCtrlClick(); | |||
} else if (event.shiftKey) { | |||
mouse.onShiftClick(); | |||
} else { | |||
mouse.onClick(); | |||
} | |||
}; | |||
mouse.onMove = function(event) { | |||
// TODO: wasclick sux | |||
if (Math.abs(event.clientX - mouse.startX)>mouse.leniency || Math.abs(event.clientY - mouse.startY)>mouse.leniency){ | |||
mouse.wasClick = false; | |||
} | |||
mouse.position_absolute = { | |||
x: event.clientX, | |||
y: event.clientY | |||
}; | |||
mouse.position_relative = { | |||
x: (event.clientX / window.innerWidth) * 2 - 1, | |||
y: -(event.clientY / window.innerHeight) * 2 + 1 | |||
}; | |||
gui.setInfoPosition(mouse.position_absolute); | |||
mouse.raycaster.setFromCamera(mouse.position_relative, gui.camera); | |||
mouse.ray = mouse.raycaster.ray; | |||
if (mouse.pressed) { | |||
mouse.onDrag(); | |||
} else { | |||
mouse.onFreeMove(); | |||
} | |||
}; |
@@ -0,0 +1,412 @@ | |||
// three.js - http://github.com/mrdoob/three.js | |||
/** | |||
* @author qiao / https://github.com/qiao | |||
* @author mrdoob / http://mrdoob.com | |||
* @author alteredq / http://alteredqualia.com/ | |||
* @author WestLangley / http://github.com/WestLangley | |||
*/ | |||
THREE.OrbitControls = function ( object, domElement ) { | |||
this.object = object; | |||
this.domElement = ( domElement !== undefined ) ? domElement : document; | |||
// API | |||
this.enabled = true; | |||
this.center = new THREE.Vector3(); | |||
this.target = new THREE.Vector3(); | |||
this.userZoom = true; | |||
this.userZoomSpeed = 1.0; | |||
this.userRotate = true; | |||
this.userRotateSpeed = 1.0; | |||
this.userPan = true; | |||
this.userPanSpeed = 0.1; | |||
this.autoRotate = false; | |||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 | |||
this.minPolarAngle = 0; // radians | |||
this.maxPolarAngle = Math.PI; // radians | |||
this.minDistance = 0; | |||
this.maxDistance = Infinity; | |||
// 65 /*A*/, 83 /*S*/, 68 /*D*/ | |||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 }; | |||
// internals | |||
var scope = this; | |||
var EPS = 0.000001; | |||
var PIXELS_PER_ROUND = 1800; | |||
var rotateStart = new THREE.Vector2(); | |||
var rotateEnd = new THREE.Vector2(); | |||
var rotateDelta = new THREE.Vector2(); | |||
var zoomStart = new THREE.Vector2(); | |||
var zoomEnd = new THREE.Vector2(); | |||
var zoomDelta = new THREE.Vector2(); | |||
var phiDelta = 0; | |||
var thetaDelta = 0; | |||
var scale = 1; | |||
var lastPosition = new THREE.Vector3(); | |||
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 }; | |||
var state = STATE.NONE; | |||
// events | |||
var changeEvent = { type: 'change' }; | |||
this.rotateLeft = function ( angle ) { | |||
if ( angle === undefined ) { | |||
angle = getAutoRotationAngle(); | |||
} | |||
thetaDelta -= angle; | |||
}; | |||
this.rotateRight = function ( angle ) { | |||
if ( angle === undefined ) { | |||
angle = getAutoRotationAngle(); | |||
} | |||
thetaDelta += angle; | |||
}; | |||
this.rotateUp = function ( angle ) { | |||
if ( angle === undefined ) { | |||
angle = getAutoRotationAngle(); | |||
} | |||
phiDelta -= angle; | |||
}; | |||
this.rotateDown = function ( angle ) { | |||
if ( angle === undefined ) { | |||
angle = getAutoRotationAngle(); | |||
} | |||
phiDelta += angle; | |||
}; | |||
this.zoomIn = function ( zoomScale ) { | |||
if ( zoomScale === undefined ) { | |||
zoomScale = getZoomScale(); | |||
} | |||
scale /= zoomScale; | |||
}; | |||
this.zoomOut = function ( zoomScale ) { | |||
if ( zoomScale === undefined ) { | |||
zoomScale = getZoomScale(); | |||
} | |||
scale *= zoomScale; | |||
}; | |||
this.pan = function ( distance ) { | |||
distance.transformDirection( this.object.matrix ); | |||
distance.multiplyScalar( scope.userPanSpeed ); | |||
this.object.position.add( distance ); | |||
this.center.add( distance ); | |||
this.target.add( distance ); | |||
}; | |||
this.update = function () { | |||
var position = this.object.position; | |||
var offset = position.clone().sub( this.center ); | |||
var diff = this.center.clone().sub( this.target ).multiplyScalar(0.2); | |||
this.center.sub(diff); | |||
// angle from z-axis around y-axis | |||
var theta = Math.atan2( offset.x, offset.z ); | |||
// angle from y-axis | |||
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); | |||
if ( this.autoRotate ) { | |||
this.rotateLeft( getAutoRotationAngle() ); | |||
} | |||
theta += thetaDelta; | |||
phi += phiDelta; | |||
// restrict phi to be between desired limits | |||
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); | |||
// restrict phi to be betwee EPS and PI-EPS | |||
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); | |||
var radius = offset.length() * scale; | |||
// restrict radius to be between desired limits | |||
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); | |||
offset.x = radius * Math.sin( phi ) * Math.sin( theta ); | |||
offset.y = radius * Math.cos( phi ); | |||
offset.z = radius * Math.sin( phi ) * Math.cos( theta ); | |||
position.copy( this.center ).add( offset ); | |||
this.object.lookAt( this.center ); | |||
thetaDelta /= 1.5; | |||
phiDelta /= 1.5; | |||
scale = 1; | |||
if ( lastPosition.distanceTo( this.object.position ) > 0.01 ) { | |||
this.dispatchEvent( changeEvent ); | |||
lastPosition.copy( this.object.position ); | |||
} | |||
}; | |||
function getAutoRotationAngle() { | |||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; | |||
} | |||
function getZoomScale() { | |||
return Math.pow( 0.95, scope.userZoomSpeed ); | |||
} | |||
function onMouseDown( event ) { | |||
if ( scope.enabled === false ) return; | |||
if ( scope.userRotate === false ) return; | |||
event.preventDefault(); | |||
if ( state === STATE.NONE ) | |||
{ | |||
if ( event.button === 0 ) | |||
state = STATE.ROTATE; | |||
if ( event.button === 1 ) | |||
state = STATE.ZOOM; | |||
if ( event.button === 2 ) | |||
state = STATE.PAN; | |||
} | |||
if ( state === STATE.ROTATE ) { | |||
//state = STATE.ROTATE; | |||
rotateStart.set( event.clientX, event.clientY ); | |||
} else if ( state === STATE.ZOOM ) { | |||
//state = STATE.ZOOM; | |||
zoomStart.set( event.clientX, event.clientY ); | |||
} else if ( state === STATE.PAN ) { | |||
//state = STATE.PAN; | |||
} | |||
document.addEventListener( 'mousemove', onMouseMove, false ); | |||
document.addEventListener( 'mouseup', onMouseUp, false ); | |||
} | |||
function onMouseMove( event ) { | |||
if ( scope.enabled === false ) return; | |||
event.preventDefault(); | |||
if ( state === STATE.ROTATE ) { | |||
rotateEnd.set( event.clientX, event.clientY ); | |||
rotateDelta.subVectors( rotateEnd, rotateStart ); | |||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed ); | |||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed ); | |||
rotateStart.copy( rotateEnd ); | |||
} else if ( state === STATE.ZOOM ) { | |||
zoomEnd.set( event.clientX, event.clientY ); | |||
zoomDelta.subVectors( zoomEnd, zoomStart ); | |||
if ( zoomDelta.y > 0 ) { | |||
scope.zoomIn(); | |||
} else { | |||
scope.zoomOut(); | |||
} | |||
zoomStart.copy( zoomEnd ); | |||
} else if ( state === STATE.PAN ) { | |||
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; | |||
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; | |||
scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) ); | |||
} | |||
} | |||
function onMouseUp( event ) { | |||
if ( scope.enabled === false ) return; | |||
if ( scope.userRotate === false ) return; | |||
document.removeEventListener( 'mousemove', onMouseMove, false ); | |||
document.removeEventListener( 'mouseup', onMouseUp, false ); | |||
state = STATE.NONE; | |||
} | |||
function onMouseWheel( event ) { | |||
if ( scope.enabled === false ) return; | |||
if ( scope.userZoom === false ) return; | |||
var delta = 0; | |||
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 | |||
delta = event.wheelDelta; | |||
} else if ( event.detail ) { // Firefox | |||
delta = - event.detail; | |||
} | |||
if ( delta > 0 ) { | |||
scope.zoomOut(); | |||
} else { | |||
scope.zoomIn(); | |||
} | |||
} | |||
function onKeyDown( event ) { | |||
if ( scope.enabled === false ) return; | |||
if ( scope.userPan === false ) return; | |||
switch ( event.keyCode ) { | |||
/*case scope.keys.UP: | |||
scope.pan( new THREE.Vector3( 0, 1, 0 ) ); | |||
break; | |||
case scope.keys.BOTTOM: | |||
scope.pan( new THREE.Vector3( 0, - 1, 0 ) ); | |||
break; | |||
case scope.keys.LEFT: | |||
scope.pan( new THREE.Vector3( - 1, 0, 0 ) ); | |||
break; | |||
case scope.keys.RIGHT: | |||
scope.pan( new THREE.Vector3( 1, 0, 0 ) ); | |||
break; | |||
*/ | |||
case scope.keys.ROTATE: | |||
state = STATE.ROTATE; | |||
break; | |||
case scope.keys.ZOOM: | |||
state = STATE.ZOOM; | |||
break; | |||
case scope.keys.PAN: | |||
state = STATE.PAN; | |||
break; | |||
} | |||
} | |||
function onKeyUp( event ) { | |||
switch ( event.keyCode ) { | |||
case scope.keys.ROTATE: | |||
case scope.keys.ZOOM: | |||
case scope.keys.PAN: | |||
state = STATE.NONE; | |||
break; | |||
} | |||
} | |||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); | |||
this.domElement.addEventListener( 'mousedown', onMouseDown, false ); | |||
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); | |||
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox | |||
window.addEventListener( 'keydown', onKeyDown, false ); | |||
window.addEventListener( 'keyup', onKeyUp, false ); | |||
}; | |||
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); |
@@ -0,0 +1,36 @@ | |||
var websocket = {}; | |||
websocket.update = undefined; | |||
websocket.connect = function(update) { | |||
websocket.ws = new WebSocket("ws://localhost:5000"); | |||
if (update){ | |||
websocket.update = update; | |||
} | |||
websocket.ws.onopen = function(evt) { | |||
gui.serverMessage("Connected to server."); | |||
}; | |||
websocket.ws.onerror = function(err) { | |||
gui.serverMessage("Could not connect to server."); | |||
}; | |||
websocket.ws.onmessage = function(evt) { | |||
json = JSON.parse(evt.data); | |||
for (var i in json.node) { | |||
var pos = json.node[i].position; | |||
json.node[i].position = new THREE.Vector3(pos.x, pos.y, pos.z); | |||
if (json.node[i].vop === undefined){ | |||
json.node[i].vop = 0; | |||
} | |||
} | |||
websocket.update(json); | |||
}; | |||
websocket.ws.onclose = function(evt) { | |||
gui.serverMessage("No connection to server. <a href='#' onclick='javascript:websocket.connect()'>Reconnect</a>.", true); | |||
}; | |||
}; | |||
websocket.edit = function (data) { | |||
websocket.ws.send("edit:"+JSON.stringify(data)); | |||
}; |
@@ -0,0 +1,47 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<title>abp</title> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1" /> | |||
<link rel="stylesheet" href="{{ url_for("static", filename="main.css") }}"> | |||
<script src="{{ url_for("static", filename="scripts/three.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/orbitcontrols.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/anders_briegel.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/mouse.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/materials.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/graph.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/gui.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/editor.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/websocket.js") }}"></script> | |||
<script src="{{ url_for("static", filename="scripts/main.js") }}"></script> | |||
</head> | |||
<body> | |||
<img id=ball src="img/ball.png" style=display:none;> | |||
<img id=tip src="img/tip.png" style=display:none;> | |||
<div id=node_info class=hidden> nothing </div> | |||
<div id=server_info class=hidden> </div> | |||
<div id=version>Version 0.4.27</div> | |||
<div id=node_data class=hidden> | |||
<div id=node_name></div> | |||
<ul> | |||
<li id=node_vop></li> | |||
<li>Measure in | |||
<a href="#" onclick="editor.measureX()">X</a> / | |||
<a href="#" onclick="editor.measureY()">Y</a> / | |||
<a href="#" onclick="editor.measureZ()">Z</a></li> | |||
<li>Act | |||
<a href="#" onclick="editor.hadamard()">Hadamard</a> / | |||
<a href="#" onclick="editor.phase()">Phase</a></li> | |||
<li><a href="#" onclick="editor.localComplementation()">Invert neighbourhood</a></li> | |||
<li><a href="#" onclick="editor.deleteNode()">Delete</a></li> | |||
</ul> | |||
</div> | |||
</body> | |||
</html> |