Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
528 lines (480 sloc) 16.2 KB
/* -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
////////////////////////////////////////////////////////////////////////////////
// stuff.pjs
////////////////////////////////////////////////////////////////////////////////
// constants
int centreRadius = 25;
int rate = 30;
// current beats per minute, doesn't allow for multiple bpms
int curbpm = 0;
// universal tick count
int ticks = 0;
////////////////////////////////////////////////////////////////////////////////
// circle menu UIs
function displayUI(ui) {
if(!ui.display) {
return;
}
noFill();
strokeCap(SQUARE);
// draw the circle background
var backgroundColor = 64;
strokeWeight(ui.thickness+2*ui.margin);
stroke(backgroundColor);
circle(ui.x, ui.y, ui.r);
if(ui.sections) {
// it's a circle menu
// add up all the weights
var totalWeight = 0;
for(var i=0; i < ui.sections.length; i++) {
if(ui.sections[i].weight)
totalWeight += ui.sections[i].weight;
else
totalWeight += 1;
}
ui.totalWeight = totalWeight;
float bAngle = 0, eAngle = 0, angle = 0;
int ix, iy, ox, oy;
noFill();
// and then partition the segments
for(var i=0; i < ui.sections.length; i++) {
if(ui.sections[i].weight)
eAngle = bAngle + 2*PI*ui.sections[i].weight/totalWeight;
else
eAngle = bAngle + 2*PI/totalWeight;
// draw the arcs
strokeWeight(ui.thickness);
if(i == ui.focus)
stroke(220);
else
stroke(255);
arc(ui.x, ui.y, 2*ui.r, 2*ui.r,
bAngle+ui.angleOffset, eAngle+ui.angleOffset);
var angle = (bAngle+eAngle)/2;
var tx = Math.cos(angle+ui.angleOffset)*(ui.r);
var ty = Math.sin(angle+ui.angleOffset)*(ui.r);
if(ui.sections[i].draw) {
ui.sections[i].draw(tx+ui.x,ty+ui.y);
}
strokeWeight(ui.margin);
stroke(backgroundColor);
var m = ui.thickness/2 + ui.margin/2;
ix = Math.cos(bAngle+ui.angleOffset)*(ui.r-m);
iy = Math.sin(bAngle+ui.angleOffset)*(ui.r-m);
ox = Math.cos(bAngle+ui.angleOffset)*(ui.r+m);
oy = Math.sin(bAngle+ui.angleOffset)*(ui.r+m);
line(ix+ui.x,iy+ui.y, ox+ui.x,oy+ui.y);
ix = Math.cos(eAngle+ui.angleOffset)*(ui.r-m);
iy = Math.sin(eAngle+ui.angleOffset)*(ui.r-m);
ox = Math.cos(eAngle+ui.angleOffset)*(ui.r+m);
oy = Math.sin(eAngle+ui.angleOffset)*(ui.r+m);
line(ix+ui.x,iy+ui.y, ox+ui.x,oy+ui.y);
bAngle = eAngle;
}
} else if(ui.value != undefined) {
// it's a slider value
noFill();
stroke(255);
strokeWeight(ui.thickness);
circle(ui.x,ui.y, ui.r);
stroke(200);
var angle = (2*PI*ui.value/(ui.range[1]-ui.range[0])+ui.angleOffset) % (2*PI);
arc(ui.x,ui.y, 2*ui.r,2*ui.r, ui.angleOffset,angle);
strokeWeight(ui.thickness+ui.margin);
strokeCap(ROUND);
stroke(150);
// target 20px width
var dt = 20/(2*PI*ui.r);
arc(ui.x,ui.y, 2*ui.r,2*ui.r, angle-dt,angle+dt);
}
// Fill the middle?
// fill(color(0,0,0,255));
// circle(ui.x,ui.y, ui.r-ui.thickness/2-ui.margin/2);
strokeWeight(1);
}
function pointOnUI(ui, x,y) {
if(!ui.display)
return false;
var d = dist(x,y, ui.x,ui.y);
return d<ui.r+ui.thickness/2 && d>ui.r-ui.thickness/2;
}
function changeUIState(ui, x,y) {
var d = dist(x,y, ui.x,ui.y);
var angle = (atan2(ui.y-y,ui.x-x) + ui.angleOffset+2*PI) % (2*PI);
if(ui.sections) {
// it's a circle menu
if(d<ui.r+ui.thickness/2 && d>ui.r-ui.thickness/2) {
// check where in stuff
var frac = angle/(2*PI);
var accWeight = 0, i = 0;
while(accWeight/ui.totalWeight < frac) {
var w = ui.sections[i].weight;
accWeight += w ? w : 1;
i++;
}
// just so we can focus some more
ui.focus = i-1;
} else {
ui.focus = -1;
}
} else if(ui.value != undefined) {
ui.value = angle/(2*PI)*(ui.range[1]-ui.range[0])+ui.range[0];
if(ui.snap){
var minDiff = ui.snap[0];
var j = 0;
for(var i in ui.snap) {
if(Math.abs(ui.value-ui.snap[i]) < minDiff) {
minDiff = Math.abs(ui.value-ui.snap[i]);
j = i;
}
}
ui.value = ui.snap[j];
}
}
}
// supposed to be a UI logo element
void drawSine(x,y) {
stroke(0);
strokeWeight(2);
var xscale = 4;
var yscale = 8;
var incr = 0.1;
for(var i=-PI; i < PI; i+= incr) {
line(x+xscale*i,y+yscale*Math.sin(i),
x+xscale*(i+incr),y+yscale*Math.sin(i+incr));
}
}
void drawSquare(x,y) {
stroke(0);
strokeWeight(2);
var xscale = 12;
var yscale = 8;
line(x-xscale,y, x-xscale,y-yscale);
line(x-xscale,y-yscale, x,y-yscale);
line(x,y-yscale, x,y+yscale);
line(x,y+yscale, x+xscale,y+yscale);
line(x+xscale,y+yscale, x+xscale,y);
}
void drawSaw(x,y) {
stroke(0);
strokeWeight(2);
var xscale = 12;
var yscale = 8;
line(x-xscale,y-yscale, x-xscale,y+yscale);
line(x-xscale,y+yscale, x,y-yscale);
line(x,y-yscale, x,y+yscale);
line(x,y+yscale, x+xscale,y-yscale);
line(x+xscale,y-yscale, x+xscale,y+yscale);
}
void drawTri(x,y) {
stroke(0);
strokeWeight(2);
var xscale = 12;
var yscale = 8;
line(x-xscale,y, x-xscale/2,y-yscale);
line(x-xscale/2,y-yscale, x,y);
line(x+xscale/2,y+yscale, x,y);
line(x+xscale,y, x+xscale/2,y+yscale);
}
void drawGate(x,y) {
stroke(0);
strokeWeight(2);
line(x-20,y,x-10,y);
line(x-10,y-10,x-10,y+10);
line(x-5,y-10,x-5,y+10);
line(x-5,y-10,x+5,y-10);
line(x+5,y-10,x+5,y-15);
line(x-5,y+10,x+5,y+10);
line(x+5,y+10,x+5,y+15);
}
void drawClose(x,y) {
stroke(0);
strokeWeight(8);
var size = 14;
line(x+size,y+size, x-size,y-size);
line(x+size,y-size, x-size,y+size);
}
// Menus
var addUI = {x:0, y:0, r:40,
thickness:50,
display:false,
margin:10, angleOffset:-PI/2,
sections:[{weight:1, draw:drawSine},
{weight:1, draw:drawSquare},
{weight:1, draw:drawSaw},
{weight:1, draw:drawTri},
{weight:1, draw:drawGate},
]};
var editUI = {x:0, y:0, r:30,
thickness:50,
display:false,
margin:10, angleOffset:-PI/2,
sections:[{weight:1, draw:drawClose},
]};
var volumeUI = {x:0, y:0, r:80,
thickness:20,
display:false,
margin:10, angleOffset:-PI/2,
value:0.1, range:[0,1.0]};
var frequencyUI = {x:0, y:0, r:120,
thickness:20,
display:false,
margin:10, angleOffset:-PI/2,
value:440, range:[0,1000],
snap:[ 16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87,
/* 1 */ 32.7, 34.65, 36.71, 38.89, 41.2, 43.65, 46.25, 49, 51.91, 55, 58.27, 61.74,
/* 2 */ 65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 98, 103.83, 110, 116.54, 123.47,
/* 3 */ 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185, 196, 207.65, 220, 233.08, 246.94,
/* 4 */ 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16, 493.88,
/* 5 */ 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880, 932.33, 987.77,
/* 6 */ 1046.5, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.53,
/* 7 */ 2093, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31, 3951.07,
/* 8 */ 4186.01, 4434.92, 4698.64, 4978 ]};
////////////////////////////////////////////////////////////////////////////////
// required by processing.js
void setup() {
size(screen.width, screen.height);
frameRate(rate);
// plops down the center
int cx = width / 2 - centreRadius;
int cy = height / 2 - centreRadius;
HomeNode.x = cx;
HomeNode.y = cy;
HomeNode.size = centreRadius;
HomeNode.influence = 200;
}
// teeny utility fun
function circle(x,y,radius) {
ellipse(x,y,2*radius,2*radius);
}
// required by processing.js
void draw() {
// drawing background
background(125);
fill(255);
if (curbpm != bpm) {
curbpm = bpm;
ticks = curbpm / 60;
}
//// Draw Layers
// Draw the influence circles
for (int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if(node.mouseOver) {
// display an influence circle, opacity
fill(color(255, 255, 255, 50));
stroke(color(0,0,0,100));
circle(node.x, node.y, node.influence);
} else {
fill(color(255, 255, 255, 20));
stroke(color(0,0,0,20));
circle(node.x, node.y, node.influence);
}
}
// reset the stroke
stroke(color(0,0,0,255));
// Draw relationships
for (int i=0; i < nodeList.length; i++) {
var node = nodeList[i];
stroke(0);
fill(0);
for(int j=0; j < node.inputNodes.length; j++) {
// input into this node
var parent = node.inputNodes[j];
// replace with a triangle, to show direction
int dx = parent.x - node.x;
int dy = parent.y - node.y;
int norm = dist(0,0, dx,dy);
int base = 1.5 + 5.0 * parent.playing;
triangle(parent.x + base*dy/norm, parent.y - base*dx/norm,
parent.x - base*dy/norm, parent.y + base*dx/norm,
node.x, node.y);
}
}
// Draw all nodes proper
for (int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
fill(node.color);
switch (node.shape) {
case Circle:
circle(node.x, node.y, node.size);
break;
case Square:
rect(node.x - node.size, node.y - node.size, node.size * 2, node.size * 2);
break;
}
}
// draw UIs, if needed
displayUI(addUI);
displayUI(editUI);
displayUI(volumeUI);
displayUI(frequencyUI);
}
////////////////////////////////////////////////////////////////////////////////
// Mouse events
void mousePressed() {
var x=mouseX,y=mouseY;
// check node pressed
for (int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node.pointInShape.call(node, mouseX, mouseY)) {
node.click = true;
} else {
node.click = false;
}
}
if(pointOnUI(volumeUI, x,y)) {
volumeUI.click = true;
} else {
volumeUI.click = false;
}
if(pointOnUI(frequencyUI, x,y)) {
frequencyUI.click = true;
} else {
frequencyUI.click = false;
}
}
var underScrutiny = null;
void mouseClicked() {
int x = mouseX;
int y = mouseY;
// check if the click falls into an object
for (int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node.pointInShape.call(node, mouseX, mouseY)) {
editUI.x = node.x;
volumeUI.x = node.x;
frequencyUI.x = node.x;
editUI.y = node.y;
volumeUI.y = node.y;
frequencyUI.y = node.y;
editUI.display = true;
volumeUI.display = true;
frequencyUI.display = true;
addUI.display = false;
editUI.node = node;
volumeUI.node = node;
frequencyUI.node = node;
if(node.amp != undefined) {
volumeUI.value = node.amp;
}
if(node.freq != undefined) {
frequencyUI.value = node.freq;
}
underScrutiny = node;
return;
}
}
if(pointOnUI(editUI, x,y)) {
changeUIState(editUI, x,y);
if(editUI.focus == 0) {
removeNode(nodeList.indexOf(editUI.node));
editUI.node.disconnect.call(editUI.node);
editUI.display = false;
editUI.node = undefined;
volumeUI.display = false;
volumeUI.node = undefined;
frequencyUI.display = false;
frequencyUI.node = undefined;
return;
}
} else if(pointOnUI(volumeUI, x,y)) {
changeUIState(volumeUI, x,y);
underScrutiny.update.call(underScrutiny, {amplitude: volumeUI.value});
return;
} else if(pointOnUI(frequencyUI, x,y)) {
changeUIState(frequencyUI, x,y);
underScrutiny.update.call(underScrutiny, {frequency: frequencyUI.value});
return;
} else {
if(editUI.display) { // proxy for whether edit mode
editUI.display = false;
volumeUI.display = false;
frequencyUI.display = false;
underScrutiny = null;
return; // don't lay down addUI
}
}
// otherwise, we're placing an object
if(addUI.display) {
if(pointOnUI(addUI, x,y)) {
// if it's on the UI, pass on
changeUIState(addUI,x,y);
if (mouseButton == LEFT) {
//newNode = make_random_osc(x, y);
var cx = addUI.x, cy = addUI.y;
switch(addUI.focus) {
case 0:
newNode = makeSineOsc(cx,cy, 440, 2);
break;
case 1:
newNode = makeSquareOsc(cx,cy, 440, 2);
break;
case 2:
newNode = makeSawOsc(cx,cy, 440, 2);
break;
case 3:
newNode = makeTriangleOsc(cx,cy, 440, 2);
break;
case 4:
newNode = makeGate(x, y, 50);
}
newNode.update.call(newNode, {amplitude: volumeUI.value});
}
// add us as children if inside influence
intersecting = generateIntersections(newNode);
addNode(newNode);
if (intersecting.length != 0) {
reconnectNode(intersecting, newNode);
}
}
addUI.display = false;
} else {
addUI.x = x;
addUI.y = y;
addUI.display = true;
}
}
void mouseDragged() {
var x = mouseX, y = mouseY;
for(int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
// do elemental dragging
if(node.click) {
node.x = x;
node.y = y;
intersecting = generateIntersections(node);
reconnectNode(intersecting, node);
}
}
if(volumeUI.click) {
changeUIState(volumeUI, x,y);
underScrutiny.update.call(underScrutiny, {amplitude: volumeUI.value});
}
if(frequencyUI.click) {
changeUIState(frequencyUI, x,y);
underScrutiny.update.call(underScrutiny, {frequency: frequencyUI.value});
}
}
void mouseReleased() {
for(int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
node.click = false;
}
volumeUI.click = false;
frequencyUI.click = false;
}
// analogous to onmousemove
void mouseMoved() {
var x = mouseX, y = mouseY;
for(int i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node.pointInShape.call(node, x, y)) {
node.mouseOver = true;
} else {
node.mouseOver = false;
}
}
changeUIState(addUI, x,y);
changeUIState(editUI, x,y);
}