class NetworkView extends View { Dot[] dots = new Dot[0]; Arrow[] arrows = new Arrow[0]; Statevar[] vars = new Statevar[0]; Flux[] fluxes = new Flux[0]; float varScale, fluxScale; String lastVar = ""; int currentCell = 0; // in a more-than-one-cell Environment, change this to change what cell the network diagram shows boolean selectable = false; NetworkView() { shortname = "network"; } NetworkView(float varScale, float fluxScale) { shortname = "network"; this.varScale = varScale; this.fluxScale = fluxScale; } Dot addDot(Statevar V, float xcen, float ycen, color col) { Dot D = new Dot(V.shortname, xcen, ycen, col, varScale, this); D.selectable = selectable; D.units = V.units; dots = (Dot[])append(dots, D); vars = (Statevar[])append(vars, V); return D; } Dot addDot_polar(Statevar V, float R, float theta, color col) { float x = R*cos(theta*PI/180); float y = R*sin(theta*PI/180); return addDot(V, x, y, col); } Arrow addArrow(Flux F, color col, float angle) { Arrow A = new Arrow(F.shortname, findDot(F.from.shortname), findDot(F.to.shortname), col, angle, fluxScale, this); arrows = (Arrow[])append(arrows, A); fluxes = (Flux[])append(fluxes, F); return A; } Arrow addArrow(Flux F, float dx, float dy, color col, float angle) { // use this one if one endpoint of the flux is null Arrow A; if (F.from != null) { A = new Arrow(F.shortname, findDot(F.from.shortname), dx, dy, col, angle, fluxScale, this); } else { A = new Arrow(F.shortname, dx, dy, findDot(F.to.shortname), col, angle, fluxScale, this); } arrows = (Arrow[])append(arrows, A); fluxes = (Flux[])append(fluxes, F); return A; } Dot findDot(String S) { // searches the internalNames first, then the (display) names for (int i=0; i= styles.smallFontSize) { styles.setFont(min(1.2*radius, styles.maxFontSize)); textAlign(CENTER); text(name, x0, y0 + textAscent()/2); } if (over()) { annotate(name + ": " + round(value*100)/100. + " " + units); } popStyle(); } boolean over() { return dist(mouseX,mouseY,x0,y0) < max(radius, 5); } void annotate(String S) { pushStyle(); styles.setFont(styles.normalFontSize); fill(styles.labelColor); noStroke(); textAlign(LEFT); // text(" " + S, mouseX, mouseY); text(" " + S, x0+radius, y0); popStyle(); } } // ------------------------------------------------------------------------------------------------------------------ class Arrow { float angle, dataScale; Dot from,to; float xfrom, yfrom, xto, yto; // normalized units float x0,y0,x1,y1,weight; // screen units int dir; float[] displayRect; color col; String name; float arrowheadPos = 0.5; Axes ax; Arrow(String name, color col, float angle, float dataScale, View view) { this.name = name; this.col = col; this.angle = angle; this.dataScale = dataScale; ax = view.ax; } Arrow(String name, Dot from, Dot to, color col, float angle, float dataScale, View view) { this(name, col, angle, dataScale, view); this.from = from; this.to = to; } Arrow(String name, float xfrom, float yfrom, Dot to, color col, float angle, float dataScale, View view) { this(name, col, angle, dataScale, view); setFrom(xfrom, yfrom); this.to = to; } Arrow(String name, Dot from, float xto, float yto, color col, float angle, float dataScale, View view) { this(name, col, angle, dataScale, view); setTo(xto, yto); this.from = from; } void setFrom(float x, float y) { from = null; xfrom = x; yfrom = y; } void setTo(float x, float y) { to = null; xto = x; yto = y; } void update(float val) { weight = constrain(abs(val) / dataScale * 12, 0.01, 12); if (val > 0) {dir = 1;} else {dir = -1;} if (from != null) { x0 = from.x0; y0 = from.y0; } else { x0 = ax.x2scr(xfrom); y0 = ax.y2scr(yfrom); } if (to != null) { x1 = to.x0; y1 = to.y0; } else { x1 = ax.x2scr(xto); y1 = ax.y2scr(yto); } } void draw() { pushStyle(); if (isnan(x0+y0+x1+y1)) return; strokeWeight(weight); stroke(col); noFill(); float[][] vertices = fluxArc(x0, y0, x1, y1, angle*PI/180); fill(col); noStroke(); arrowhead(vertices, arrowheadPos, weight*5, dir); popStyle(); } boolean over() { return false; } void annotate(String S) { } } // arc and arrowhead --------------------------------------------------------------------------------------------------------------- float[][] fluxArc(float x0, float y0, float x1, float y1, float ang) { return fluxArc(x0,y0,x1,y1,ang,60); } float[][] fluxArc(float x0, float y0, float x1, float y1, float ang, int resolution) { // draws an arc from (x0,y0) to (x1,y1) that spans _ang_ degrees of a circle. float small = 1e-6; float dx,dy,D,m,xc,yc,xm,ym,angthing,th0m,th1m,dangm; float R,thStart,thEnd,mindist,th; int j,i; float[][] th0,th1,vertices; ang=-ang; // because y axis is inverted angthing = sqrt(1+cos(ang))/sqrt(1-cos(ang)); if (abs(ang) (1/small)) { // endpoints on vertical line xc = -0.5 * angthing * D; yc = 0; } else if (abs(m) < small) { // endpoints on horizontal line xc = 0; yc = 0.5 * angthing * D; } else { xc = -0.5 * m / sqrt(m*m+1) * angthing * D; yc = (-1/m) * xc; } // now pick one of the candidate centers such that (x1,y1) is a smaller angle off it than (x0,y0) // (if ang > 0). There is probably a better way to this. th0m = atan2(y0-ym+yc,x0-xm+xc); // angle from (x0,y0) to (xm-xc,ym-yc) th1m = atan2(y1-ym+yc,x1-xm+xc); if (th0m<0) {th0m = th0m + 2*PI;} if (th1m<0) {th1m = th1m + 2*PI;} dangm = th0m-th1m; if (dangm>PI) {dangm = dangm - 2*PI;} if (dangm<-PI) {dangm = dangm + 2*PI;} if ((dangm>0 && ang>0) || (dangm<0 && ang<0)) {xc = -xc; yc = -yc;} // polar coords of points along the arc R = sqrt((y1-ym-yc)*(y1-ym-yc)+(x1-xm-xc)*(x1-xm-xc)); // find the shortest way around the circle from th0 to th1: start with all options // (this is very elegant in Matlab) thStart = atan2(y0-ym-yc,x0-xm-xc); thEnd = atan2(y1-ym-yc,x1-xm-xc); th0 = new float[3][3]; th1 = new float[3][3]; for (i=0; i<3; i++) { th0[0][i] = thStart - 2*PI; th0[1][i] = thStart; th0[2][i] = thStart + 2*PI; th1[i][0] = thEnd - 2*PI; th1[i][1] = thEnd; th1[i][2] = thEnd + 2*PI; } mindist = (1./small); for (j=0; j<3; j++) { for (i=0; i<3; i++) { if (abs(th1[j][i] - th0[j][i]) < mindist) { mindist = abs(th1[j][i] - th0[j][i]); thStart = th0[j][i]; thEnd = th1[j][i]; } } } // now assemble line from (R,th) vertices = new float[resolution][2]; noFill(); beginShape(); for (i=0; i (1./small)) { xa1 = -(W/2)+x0; xa2 = (W/2)+x0; ya1 = L * sign_y + y0; ya2 = ya1; xb = x0; yb = y0 + (1-baseindent) * L * sign_y; } else if (abs(m) < small) { xa1 = L * sign_x + x0; xa2 = xa1; ya1 = -(W/2)+y0; ya2 = (W/2)+y0; xb = x0 + (1-baseindent) * L * sign_x; yb = y0; } else { float Ls = L / sqrt(m*m+1); xl1 = Ls + x0; xl2 = -Ls + x0; yl1 = m * (xl1-x0) + y0; yl2 = m * (xl2-x0) + y0; Dsq1 = (xl1-xp)*(xl1-xp) + (yl1-yp)*(yl1-yp); Dsq2 = (xl2-xp)*(xl2-xp) + (yl2-yp)*(yl2-yp); if (Dsq1