Browse Source

Framework in place

master
Pete Shadbolt 6 years ago
parent
commit
1b444341f5
20 changed files with 2390 additions and 0 deletions
  1. +32
    -0
      app.py
  2. BIN
      static/img/ball.png
  3. +91
    -0
      static/img/ball.svg
  4. BIN
      static/img/tip.png
  5. +91
    -0
      static/img/tip.svg
  6. +92
    -0
      static/main.css
  7. +1
    -0
      static/scripts/.gitignore
  8. +73
    -0
      static/scripts/anders_briegel.js
  9. +190
    -0
      static/scripts/editor.js
  10. +76
    -0
      static/scripts/graph.js
  11. +80
    -0
      static/scripts/gui.js
  12. +58
    -0
      static/scripts/interaction.js
  13. +10
    -0
      static/scripts/main.js
  14. +38
    -0
      static/scripts/materials.js
  15. +0
    -0
      static/scripts/message.js
  16. +73
    -0
      static/scripts/mouse.js
  17. +412
    -0
      static/scripts/orbitcontrols.js
  18. +990
    -0
      static/scripts/three.js
  19. +36
    -0
      static/scripts/websocket.js
  20. +47
    -0
      templates/index.html

+ 32
- 0
app.py View File

@@ -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")


BIN
static/img/ball.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.8KB

+ 91
- 0
static/img/ball.svg View File

@@ -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>

BIN
static/img/tip.png View File

Before After
Width: 64  |  Height: 64  |  Size: 1.0KB

+ 91
- 0
static/img/tip.svg View File

@@ -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>

+ 92
- 0
static/main.css View File

@@ -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;
}

+ 1
- 0
static/scripts/.gitignore View File

@@ -0,0 +1 @@
qcengine

+ 73
- 0
static/scripts/anders_briegel.js View File

@@ -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;
};



+ 190
- 0
static/scripts/editor.js View File

@@ -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 + ".");
};

+ 76
- 0
static/scripts/graph.js View File

@@ -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();

};


+ 80
- 0
static/scripts/gui.js View File

@@ -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);
};


+ 58
- 0
static/scripts/interaction.js View File

@@ -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);
};

+ 10
- 0
static/scripts/main.js View File

@@ -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();
};

+ 38
- 0
static/scripts/materials.js View File

@@ -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
static/scripts/message.js View File


+ 73
- 0
static/scripts/mouse.js View File

@@ -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();
}
};

+ 412
- 0
static/scripts/orbitcontrols.js View File

@@ -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 );

+ 990
- 0
static/scripts/three.js
File diff suppressed because it is too large
View File


+ 36
- 0
static/scripts/websocket.js View File

@@ -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));
};

+ 47
- 0
templates/index.html View File

@@ -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>

Loading…
Cancel
Save