/* a client of the aglo library */ import java.awt.*; import java.applet.Applet; import java.lang.*; import java.io.*; import java.util.*; public class Aglo extends Applet implements Runnable { Thread testThread; long start_time; int num; boolean do_monitor = true; boolean do_end_sleep = false; boolean enable[] = {true, true, true, true, true, true, true, true}; double w[] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; AgloCanvas c; AgloGraph graph; AgloParams parameters = new AgloParams(); AgloMonitor aMonitor = new AgloMonitor(); Label javaglolabel = new Label("JAVAGLO",Label.CENTER); Panel mousePanel = new Panel(); Panel mouse1 = new Panel(); Panel mouse2 = new Panel(); Panel mouse3 = new Panel(); Panel paramPanel = new Panel(); Panel aesthPanel = new Panel(); AgloPanel agloPanel[] = new AgloPanel[12]; String fn; String apNames[] = { "beginning temperature", "ending temperature", "iterations", "monitor delay", "node repulsion", "min edge_length", "centripetal", "node edge repulsion", "min edge intersect", "min edge intersect2", "parent to left", "min level variance" }; String apInitVals[] = { "100.0", "0.001", "1000", "2", "0", "0", "0", "0", "0", "0", "0", "0" }; double apDegVals[] = { 5.0, 5.0, 25.0, 0.25, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; boolean apBVals[] = { false, false, true, true, false, false, false, false, false, false, false, false }; CheckboxGroup mouseGroup = new CheckboxGroup(); Checkbox chkbox1 = new Checkbox("add point", mouseGroup, true); TextField tf1 = new TextField("x", 3); TextField tf2 = new TextField("y", 3); Checkbox chkbox2 = new Checkbox("add edge", mouseGroup, false); TextField tf3 = new TextField("pt1", 3); TextField tf4 = new TextField("pt2", 3); Checkbox chkbox3 = new Checkbox("move point", mouseGroup, false); TextField tf5 = new TextField("x", 3); TextField tf6 = new TextField("y", 3); // struct font_desc font_desc[] = { // { "nr", "node repulsion" }, // { "mel", "minimize edge length" }, // { "cp", "centripetal (repulsion from centroid)" }, // { "ner", "node/edge repulsion" }, // { "mei", "minimize edge intersections" }, // { "mei2", "minimize edge intersections v2" }, // { "pl", "place parent to left of children" }, // { "mlv", "minimize intralevel variance" }, // { 0 } // }; public void init() { int i; // initialize a canvas, put stuffs on it, // draw the windows, wait for input. start_time = System.currentTimeMillis(); graph = new AgloGraph(aMonitor, 0, 0, true, enable, w) ; c = new AgloCanvas(graph); setLayout(new BorderLayout()); add("North", javaglolabel); add("Center", c); add("West", mousePanel); add("East", paramPanel); add("South", aesthPanel); // add components to the panels // mousePanel is an aggregation of 4 flow layout panels mousePanel.setLayout(new GridLayout(0,1)); mousePanel.add(mouse1); mousePanel.add(mouse2); mousePanel.add(mouse3); mouse1.setLayout(new FlowLayout(FlowLayout.LEFT)); mouse1.add(chkbox1); mouse1.add(tf1); mouse1.add(tf2); mouse2.setLayout(new FlowLayout(FlowLayout.LEFT)); mouse2.add(chkbox2); mouse2.add(tf3); mouse2.add(tf4); mouse3.setLayout(new FlowLayout(FlowLayout.LEFT)); mouse3.add(chkbox3); mouse3.add(tf5); mouse3.add(tf6); paramPanel.setLayout(new GridLayout(0,2)); for (i=0; i<4; i++) { paramPanel.add(agloPanel[i] = new AgloPanel(apNames[i], apInitVals[i], apDegVals[i], apBVals[i])); } aesthPanel.setLayout(new GridLayout(0,4)); for (i=4; i<12; i++) { aesthPanel.add(agloPanel[i] = new AgloPanel(apNames[i], apInitVals[i], apDegVals[i], apBVals[i])); } } public void start () { for (int i=0; i<12; i++) { agloPanel[i].start(); } testThread = new Thread(this); testThread.start(); graph.start(); // spin off another thread for the graph layout... c.start(); } public void run () { int i; while (true) { repaint(); if (!aMonitor.val()) { // when layout_running == 0... do the following: // check agloPanel[i] to see if there are error, illegal inputs... if ((agloPanel[0].getValue() < 0) || (agloPanel[0].getValue() > agloPanel[1].getValue())) { // if there are errors, revert to what was before, update agloPanels... agloPanel[0].forceSetValue(parameters.k_begin_temp); agloPanel[1].forceSetValue(parameters.k_end_temp); } else { parameters.k_begin_temp = agloPanel[0].getValue(); parameters.k_end_temp = agloPanel[1].getValue(); } parameters.k_iterations = (int) agloPanel[2].getValue(); parameters.mon_delay = (int) agloPanel[3].getValue(); // if not, copy agloPanel[i] into AgloParam and fonts, weights structures for (i=4; i<12; i++) { agloPanel[i].repaint(); w[i-4] = agloPanel[i].getValue(); enable[i-4] = (w[i-4] != 0); } // read file if necessary... // if (fn != null) read_graph(fn,num); // send the graph to layout again, after adjusting parameters,etc.... graph.aglo_update(parameters, enable, w); aMonitor.set(); } try { testThread.sleep(100); } catch (InterruptedException e) { } } } public synchronized void update(Graphics g) { c.repaint(); } public synchronized boolean handleEvent(Event e) { int pt, x, y; double x1, y1; int width1 = c.size().width; int height1 = c.size().height; if ((e.target instanceof Checkbox) && (e.id == Event.ACTION_EVENT)) { if (e.target == chkbox1) c.setMode(0); if (e.target == chkbox2) c.setMode(1); if (e.target == chkbox3) c.setMode(2); } if (e.target instanceof AgloCanvas) { switch (c.getMode()) { case 0: tf1.setText(String.valueOf(c.getX())); tf2.setText(String.valueOf(c.getY())); break; case 1: tf3.setText(String.valueOf(c.getPoint1())); tf4.setText(String.valueOf(c.getPoint2())); break; case 2: tf5.setText(String.valueOf(c.getX())); tf6.setText(String.valueOf(c.getY())); break; } } if ((e.target instanceof TextField) && (e.id == Event.ACTION_EVENT)) { switch (c.getMode()) { case 0: // add the vertex with the x,y values x = (int) (getTFValue(tf1)); y = (int) (getTFValue(tf2)); x1 = (double) x / (double) width1; y1 = (double) y / (double) height1; pt = graph.vertices; if (!graph.g_register_vertex(pt+1)) return false; graph.StateVector[pt].putValue(0, x1); graph.StateVector[pt].putValue(1, y1); break; case 1: // add an edge with the two vertices; // check that they are within bound x = (int) (getTFValue(tf3)); y = (int) (getTFValue(tf4)); if ((x0) find_layout(); try { layoutThread.sleep(100); } catch (InterruptedException e) { } } } public void stop() { layoutThread.stop(); } public void state_frame_points(AgloPoint min_frame, AgloPoint max_frame) { // assert(vertices > 0); min_frame.assign(StateVector[0]); max_frame.assign(StateVector[0]); for (int i=0;i dy1*dx2) return +1; if (dx1*dy2 < dy1*dx2) return -1; if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) return +1; return 0; } public boolean intersect(double x11, double y11, double x12, double y12, /* line 1 */ double x21, double y21, double x22, double y22) /* line 2 */ { return ((ccw(x11, y11, x12, y12, x21, y21) *ccw(x11, y11, x12, y12, x22, y22)) <= 0) && ((ccw(x21, y21, x22, y22, x11, y11) *ccw(x21, y21, x22, y22, x12, y12)) <= 0); } public void ae_intersection1(AgloPoint state[], AgloPoint gradient[], int p11, int p12, int p21, int p22) { if (intersect(state[p11].getValue(0), state[p11].getValue(1), state[p12].getValue(0), state[p12].getValue(1), state[p21].getValue(0), state[p21].getValue(1), state[p22].getValue(0), state[p22].getValue(1))) { AgloPoint mid1 = new AgloPoint(); AgloPoint mid2 = new AgloPoint(); AgloPoint delta = new AgloPoint(); double mag; AgloPoint force_delta = new AgloPoint(); mid1.midpoint(state[p11], state[p12]); mid2.midpoint(state[p21], state[p22]); delta.sub(mid2, mid1); mag = delta.mag(); mag = Math.max(mag, 1e-8); /* avoid div by 0 */ force_delta.scalar_mult(1/mag, delta); gradient[p11].dec(force_delta); gradient[p12].dec(force_delta); gradient[p21].inc(force_delta); gradient[p22].inc(force_delta); } } public void ae_min_edge_intersect1(AgloPoint state[], AgloPoint gradient[]) { int i, j, k, l; AgloEdgeRecord p, q; /* this doesn't make any sense except in 2D */ // assert(AGLO_SPACE_ARITY == 2); /* all 4 endpoints are distinct */ /* i p */ /* i j q */ for (i=0;i0;l++) { for (i=0;i0;l++) for (i=0;i 0.0) { /* check this */ double force_delta = mag * mag * delta; gradient[at_level_nodes[l][i]].putValue(AXIS, gradient[at_level_nodes[l][i]].getValue(AXIS) + force_delta); } } } /* aesthetic 'node edge repulsion' - makes node and edges spread apart */ public void ae_setup_node_edge_repulsion() { return; } public void ae_line_line_intersection(double a1, double b1, double c1, double a2, double b2, double c2, AgloPoint i) { /* this is really hideous, but good enough for now, I hope */ // assert(a1 != 0.0 || b1 != 0.0); // assert(a2 != 0.0 || b2 != 0.0); if (a1 == 0.0) i.putValue(1, -c1 / b1); else if (a2 == 0.0) i.putValue(1, -c2 / b2); else { double piv = -a2 / a1; double b = piv * b1 + b2; double c = piv * c1 + c2; i.putValue(1, -c / b); } if (b1 == 0.0) i.putValue(0, -c1 / a1); else if (b2 == 0.0) i.putValue(0, -c2 / a2); else if (a1 == 0.0) { // assert(a2 != 0.0); i.putValue(0, -(b2 * i.getValue(1) + c2) / a2); } else { i.putValue(0, -(b1 * i.getValue(1) + c1) / a1); } } public void ae_line_point_vector(AgloPoint e1, AgloPoint e2, AgloPoint p, AgloPoint v) { double a, b, c, a1, b1, c1; AgloPoint e1e2 = new AgloPoint(); AgloPoint i = new AgloPoint(); /* TBF 3D not implemented */ // assert(AGLO_SPACE_ARITY == 2); a = e2.getValue(1) - e1.getValue(1); b = e1.getValue(0) - e2.getValue(0); c = e1.getValue(1) * e2.getValue(0) - e2.getValue(1) * e1.getValue(0); e1e2.sub(e2, e1); a1 = e1e2.getValue(0); b1 = e1e2.getValue(1); c1 = -(e1e2.getValue(1) * p.getValue(1) + e1e2.getValue(0) * p.getValue(0)); ae_line_line_intersection(a, b, c, a1, b1, c1, i); v.sub(p, i); } public void ae_point_linesegment_distance_2(AgloPoint state[], AgloPoint gradient[], int pn, int e1n, int e2n) { AgloPoint e1p = new AgloPoint(); AgloPoint e2p = new AgloPoint(); AgloPoint e1e2 = new AgloPoint(); AgloPoint e2e1 = new AgloPoint(); double dp_e2e1p, dp_e1e2p; e1p.sub(state[pn], state[e1n]); e2p.sub(state[pn], state[e2n]); e1e2.sub(state[e2n], state[e1n]); e2e1.scalar_mult(-1, e1e2); dp_e2e1p = e1p.dot_product(e1e2); dp_e1e2p = e2p.dot_product(e2e1); if (dp_e2e1p >= 0) /* acute */ if (dp_e1e2p >= 0) /* acute */ { AgloPoint delta = new AgloPoint(); double mag2; AgloPoint force_delta = new AgloPoint(); AgloPoint half_force_delta = new AgloPoint(); ae_line_point_vector(state[e1n], state[e2n], state[pn], delta); mag2 = delta.mag2(); mag2 = Math.max(mag2, 1e-8); /* avoid div by 0 */ force_delta.scalar_mult(1/mag2, delta); half_force_delta.scalar_mult(0.5, force_delta); gradient[pn].inc(force_delta); gradient[e1n].dec(half_force_delta); gradient[e2n].dec(half_force_delta); } /* really should handle the case when not both acute TBF */ } public void ae_node_edge_repulsion(AgloPoint state[], AgloPoint gradient[]) { int i, j, k; AgloEdgeRecord p; /* i < p */ /* j != i; j != p */ for (j=0;j= (b = state[p.getTail()].getValue(AXIS))) { double mag = (a-b) * (a - b); gradient[i].putValue(AXIS, gradient[i].getValue(AXIS) + (-mag)); gradient[p.getTail()].putValue(AXIS, gradient[p.getTail()].getValue(AXIS) + mag); } } } /* attribute 'centroid' - centroid of current state */ public AgloPoint at_centroid(AgloPoint state[]) { if ( (at_state_sequence < aglo_state_sequence || aglo_state_sequence == 0) ) { at_state_sequence = aglo_state_sequence; int i; cached_centroid.zero(); for (i=0;i0;) { int i; AgloEdgeRecord p; change=0; for (i=0;i t) { mag = t / mag; for (i=0;i 20) random_placement(1.0); // assert(p.k_begin_temp > p.k_end_temp); // assert(p.k_end_temp > 0); // assert(p.k_iterations > 0); k_lambda = Math.pow(params.k_begin_temp / params.k_end_temp, 1.0 / params.k_iterations); t = params.k_begin_temp; do_aesth_setup(); if (do_monitor) { start_time = System.currentTimeMillis(); next_time = start_time + 20*num_aesths*params.mon_delay; update_monitor(); } for (i=1;i<=params.k_iterations;i++) { if (layoutAbort) break; aglo_state_sequence++; calculate_aesth_forces(gradient); limit_displacement(t, gradient, vertices); make_move(StateVector, gradient, vertices); jitter(StateVector, vertices); t = t / k_lambda; if (do_monitor) if (System.currentTimeMillis() >= next_time) { next_time += 20*num_aesths*params.mon_delay; update_monitor(); } if (layoutAbort) break; } if (do_monitor) update_monitor(); } void normalize_state(AgloPoint norm_state[], AgloPoint state[]) { /* possibly norm_state == state */ AgloPoint min_frame = new AgloPoint(); AgloPoint max_frame = new AgloPoint(); AgloPoint min_iso_frame = new AgloPoint(); AgloPoint max_iso_frame = new AgloPoint(); double iso_frame_size; int i; state_frame_points(min_frame, max_frame); min_iso_frame.iso_frame(min_iso_frame, max_iso_frame, min_frame, max_frame); iso_frame_size = max_iso_frame.diffx(min_iso_frame); for (i=0;i 0) norm_state[i].scalar_mult(1/iso_frame_size, norm_state[i]); } } /* read graphs and write states, etc. */ public boolean g_register_vertex(int v) { if (v == 0) { // System.err.println("0 is invalid vertex"); return false; } vertices = Math.max(vertices, v); if (vertices > AGLO_MAX_GRAPH_SIZE) { // System.err.println("vertex allocation exceeded"); return false; } return true; } public boolean g_add_edge(int v1, int v2, boolean forward) { if (v1 == v2) return true; else if (v1 < v2) { AgloEdgeRecord gp = new AgloEdgeRecord(--v2,forward); EdgeTable[--v1].addEdge(gp); return true; } else { AgloEdgeRecord gp = new AgloEdgeRecord(--v1,forward); EdgeTable[--v2].addEdge(gp); return true; } } } class AgloMonitor { private boolean layout_running = false; public synchronized boolean val() { return layout_running; } public synchronized boolean set() { while (layout_running) { try { wait(); } catch (InterruptedException e) { } } layout_running = true; notify(); return true; } public synchronized boolean reset() { while (!layout_running) { try { wait(); } catch (InterruptedException e) { } } layout_running = false; notify(); return true; } } class AgloPanel extends Panel implements Runnable { AgloPanelCanvas aCanvas; TextField aTextField; double currentValue; Label aLabel; double degValue; // this is how much each degree on the knob represents boolean intConstraint; Thread panelThread; Panel aPanel = new Panel(); Panel aPanel2 = new Panel(); AgloPanel(String somelabel, String somestring, double degVal, boolean aVal) { degValue = degVal; intConstraint = aVal; // if this is set, the value has to be unsigned int this.setLayout(new FlowLayout()); this.add(aPanel2); aPanel2.setLayout(new GridLayout(0,1)); currentValue = Double.valueOf(somestring).doubleValue(); aCanvas = new AgloPanelCanvas(63,currentValue/degVal); aPanel2.add(aCanvas); aPanel2.add(aPanel); aPanel.setLayout(new GridLayout(0,1)); aPanel.add(aLabel = new Label(somelabel,Label.CENTER)); aPanel.add(aTextField = new TextField(somestring,18)); } public void start() { panelThread = new Thread(this); panelThread.start(); } public void stop() { panelThread.stop(); } public double getValue() { return (currentValue); } public void forceSetValue(double aVal) { currentValue = aVal; } public void run () { while (true) { aCanvas.repaint(); try { panelThread.sleep(100); } catch(InterruptedException e) { } } } public synchronized boolean handleEvent(Event e) { if (e.target instanceof Canvas) { currentValue = (double) (aCanvas.getAngle() * degValue); if (!intConstraint) aTextField.setText(String.valueOf(currentValue)); else { if (currentValue < 0) currentValue = 0.0; aCanvas.setAngle(currentValue/degValue); aTextField.setText(String.valueOf((int) currentValue)); } aCanvas.repaint(); } else if ((e.target instanceof TextField) && (e.id == Event.ACTION_EVENT)) { currentValue = getTFValue(); if (!intConstraint) aTextField.setText(String.valueOf(currentValue)); else { if (currentValue < 0) currentValue = 0.0; aTextField.setText(String.valueOf((int) currentValue)); } aCanvas.setAngle(currentValue/degValue); aCanvas.repaint(); } return false; } double getTFValue() { double f; try { f = Double.valueOf(aTextField.getText()).doubleValue(); } catch (java.lang.NumberFormatException e) { f = 0.0; } return f; } } class AgloPanelCanvas extends Canvas { double angle; int center; int diameter; double size; int xinit, yinit; boolean onknob = false; final Color indicatorColor = new Color(23, 208, 243); final Color notchColor = new Color(205, 105, 201); Color arcColors[] = new Color[3]; public AgloPanelCanvas(int side, double initAngle) { this.resize((side+5), side); diameter = side; size = side*side/4.0; center = side/2; angle = initAngle; arcColors[0] = new Color(0, 103, 194); arcColors[1] = new Color(12, 73, 200); arcColors[2] = new Color(2, 0, 135); } public void setAngle(double aValue) { angle = aValue; } public double getAngle() { return angle; } public void update(Graphics g) { // draw the notch for 0 position... g.setColor(notchColor); g.drawLine(2*center,center,2*center+4,center); // draw the circle... for (int i=0; i<12; i++) { g.setColor(arcColors[i%3]); g.fillArc(0,0,diameter,diameter,((int) angle)+30*i,30); } //draw the dot (the indicator)... g.setColor(indicatorColor); g.fillOval(-4 + center + (int) (23.0*Math.cos(angle * Math.PI / 180.0)), -4 + center + (int) (-23.0*Math.sin(angle * Math.PI / 180.0)), 9, 9); } public void paint(Graphics g) { g.setColor(arcColors[2]); g.fillOval(0, 0, diameter, diameter); update(g); } public synchronized boolean mouseDown(Event evt, int x, int y) { if (((x-center)*(x-center) + (y-center)*(y-center)) < size) { xinit = x-center; yinit = center-y; onknob = true; } else onknob = false; return false; } public synchronized boolean mouseDrag(Event evt, int x, int y) { double theta1, theta2; double deltatheta; int x1, y1; x1 = x-center; y1 = center-y; if (onknob) { theta1 = Math.atan((double)yinit/(double)xinit); if (xinit < 0) theta1 += Math.PI; theta2 = Math.atan((double)y1/(double)x1); if (x1 < 0) theta2 += Math.PI; if ((theta1 > Math.PI) && (theta2 < 0)) theta1 -= 2*Math.PI; if ((theta2 > Math.PI) && (theta1 < 0)) theta1 += 2*Math.PI; deltatheta = theta2 - theta1; angle += deltatheta * 180.0 / Math.PI; xinit = x-center; yinit = center-y; } repaint(); return false; } public synchronized boolean mouseUp(Event evt, int x, int y) { onknob = false; repaint(); return false; } } class AgloParams { double k_begin_temp; double k_end_temp; int k_iterations; int mon_delay; public AgloParams() { k_begin_temp = 100.0; k_end_temp = 0.001; k_iterations = 1000; mon_delay = 2; } } /* point manipulation routines */ class AgloPoint { static final int AGLO_SPACE_ARITY = 2; double point[] = new double[2]; public AgloPoint () { point[0] = 0.0; point[1] = 0.0; } public AgloPoint (double x, double y) { point[0] = x; point[1] = y; } public void add(AgloPoint arg1, AgloPoint arg2) { for(int i=0;i