// ========================================================================== ; // ; // Copyright (1996-1998) Hartmut S. Loos ; // ; // Institut f"ur Neuroinformatik ND 03 ; // Ruhr-Universit"at Bochum ; // 44780 Bochum ; // ; // Tel : +49 234 7007845 ; // Email: Hartmut.Loos@neuroinformatik.ruhr-uni-bochum.de ; // ; // For version information and parameter explanation have a look at ; // the file 'DemoGNG.java'. ; // ; // ========================================================================== ; // ; // Copyright 1996-1998 Hartmut S. Loos ; // ; // This program is free software; you can redistribute it and/or modify ; // it under the terms of the GNU General Public License as published by ; // the Free Software Foundation; either version 1, or (at your option) ; // any later version. ; // ; // This program is distributed in the hope that it will be useful, ; // but WITHOUT ANY WARRANTY; without even the implied warranty of ; // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; // GNU General Public License for more details. ; // ; // You should have received a copy of the GNU General Public License ; // along with this program; if not, write to the Free Software ; // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; // ; // ========================================================================== ; import java.awt.*; /** * A class which implements the algorithms. * This class is an overkill. It implements many functions/algorithms. * The most important method is 'learn()' which implements the different * methods. * */ class ComputeGNG extends Panel implements Runnable { /** * The flag for debugging. */ protected final boolean DEBUG = false; /** * The maximum number of elements to draw/calculate for the distributions. */ protected final int MAX_COMPLEX = 58; /** * The maximum number of nodes. */ protected final int MAX_NODES = 250; /** * The maximum number of edges (3 * maximum number of nodes). */ protected final int MAX_EDGES = 3 * MAX_NODES; /** * The maximum number of Voronoi lines (5 * maximum number of nodes). */ protected final int MAX_V_LINES = 6 * MAX_NODES; /** * The maximum stepsize. */ protected final int MAX_STEPSIZE = 200; /** * The maximum number of discrete signals. */ protected final int MAX_DISCRETE_SIGNALS = 500; /** * The maximum x size of the grid array. */ protected final int MAX_GRID_X = 30; /** * The maximum y size of the grid array. */ protected final int MAX_GRID_Y = 15; /** * The number of errors added for the mean square error. */ protected final int NUM_GLOBAL_GRAPH = 500; /** * The factor for the ring-thickness (distribution). */ protected final float RING_FACTOR = 0.4f; // Factor < 1 /** * The version of the Growing Neural Gas Demo. */ protected final String DGNG_VERSION = "v1.5"; /** * The current maximum number of nodes. */ protected int maxNodes = 100; /** * The current number of runs to insert a new node (GNG). */ protected int NUM_NEW_NODE = 600; /** * The current number of runs. */ protected int numRun = 0; /** * The temporal backup of a run. */ protected int numRunTmp = 0; /** * The x-position of the actual signal. */ protected int SignalX = 0; /** * The y-position of the actual signal. */ protected int SignalY = 0; /** * The initial width of the drawing area. * This value can only be changed by resizing the appletviewer. */ protected int INIT_WIDTH = 550; /** * The initial height of the drawing area. * This value can only be changed by resizing the appletviewer. */ protected int INIT_HEIGHT = 310; protected DemoGNG graph; /** * The actual number of nodes. */ protected int nnodes = 0; /** * The array of the actual used nodes. */ protected NodeGNG nodes[] = new NodeGNG[MAX_NODES]; /** * The sorted array of indices of nodes. * The indices of the nodes are sorted by their distance from the actual * signal. snodes[1] is the index of the nearest node. */ protected int snodes[] = new int[MAX_NODES + 1]; /** * This array of sites is sorted by y-coordinate (2nd y-coordinate). * vsites[1] is the index of the bottom node. */ protected SiteVoronoi vsites[] = new SiteVoronoi[MAX_NODES + 1]; /** * The array of the nodes in the grid. */ protected GridNodeGNG grid[][] = new GridNodeGNG[MAX_GRID_X][MAX_GRID_Y]; /** * The array of the last computed signals (x-coordinate). */ protected float lastSignalsX[] = new float[MAX_STEPSIZE]; /** * The array of the last computed signals (y-coordinate). */ protected float lastSignalsY[] = new float[MAX_STEPSIZE]; /** * The array of the discrete signals (x-coordinate). */ protected int discreteSignalsX[] = new int[MAX_DISCRETE_SIGNALS]; /** * The array of the discrete signals (y-coordinate). */ protected int discreteSignalsY[] = new int[MAX_DISCRETE_SIGNALS]; /** * The array of the best distance (discrete signals). */ protected float discreteSignalsD1[] = new float[MAX_DISCRETE_SIGNALS]; /** * The array of the second best distance (discrete signals). */ protected float discreteSignalsD2[] = new float[MAX_DISCRETE_SIGNALS]; /** * The array of the second best distance (discrete signals). */ protected FPoint Cbest[] = new FPoint[MAX_NODES]; /** * The current number of discrete signals. */ protected int numDiscreteSignals = 500; /** * The actual number of edges. */ protected int nedges = 0; /** * The array of the actual used edges. */ protected EdgeGNG edges[] = new EdgeGNG[MAX_EDGES]; /** * The actual number of Voronoi lines. */ protected int nlines = 0; /** * The array of the actual used lines. */ protected LineGNG lines[] = new LineGNG[MAX_V_LINES]; /** * The array of boolean to distinguish between Voronoi and Delaunay lines. */ protected boolean vd[] = new boolean[MAX_V_LINES]; Thread relaxer; GraphGNG errorGraph; /** * The flag for playing the sound for a new inserted node. */ protected boolean insertedSoundB = false; /** * The flag for a white background. Useful for making hardcopies */ protected boolean whiteB = false; /** * The flag for random init. The nodes will be placed only in the specified * distribution or not. */ protected boolean rndInitB = false; /** * The flag for entering the fine-tuning phase (GG). */ protected boolean fineTuningB = false; /** * The flag for showing the signal. * This variable can be set by the user and shows the last input signals. */ protected boolean signalsB = false; /** * The flag for inserting new nodes. * This variable can be set by the user. If true no new nodes are * inserted (GNG, GG). */ protected boolean noNodesB = false; /** * The flag for stopping the demo. * This variable can be set by the user. If true no calculation is done. */ protected boolean stopB = false; /** * The flag for the sound. * This variable can be set by the user. If false no sound is played. */ protected boolean soundB = false; /** * The flag for the teach-mode. * This variable can be set by the user. If true a legend is displayed * which discribe the new form and color of some nodes. Furthermore * all calculation is very slow. */ protected boolean teachB = false; /** * The flag for variable movement (HCL). * This variable can be set by the user. */ protected boolean variableB = false; /** * The flag for displaying the edges. * This variable can be set by the user. */ protected boolean edgesB = true; /** * The flag for displaying the nodes. * This variable can be set by the user. */ protected boolean nodesB = true; /** * The flag for displaying the error graph. * This variable can be set by the user. */ protected boolean errorGraphB = false; /** * The flag for the global error (more signals). * This variable can be set by the user. */ protected boolean globalGraphB = true; /** * The flag for displaying the Voronoi diagram. * This variable can be set by the user. */ protected boolean voronoiB = false; /** * The flag for displaying the Delaunay triangulation. * This variable can be set by the user. */ protected boolean delaunayB = false; /** * The flag for any moved nodes (to compute the Voronoi diagram/Delaunay * triangulation). */ protected boolean nodesMovedB = true; /** * The flag for using utility (GNG-U). */ protected boolean utilityGNGB = false; /** * The flag for changed number of nodes. */ protected boolean nNodesChangedB = true; /** * The flag for only discrete signals */ protected boolean discreteSigB = true; /** * The flag for LBG-U method */ protected boolean LBG_U_B = false; /** * The flag for end of calculation (LBG) */ protected boolean readyLBG_B = false; /** * The selected distribution. * This variable can be set by the user. */ protected int distribution = 0; /** * The selected algorithm. * This variable can be set by the user. */ protected int algo = 0; /** * The current maximum number to delete an old edge (GNG,NGwCHL). * This variable can be set by the user. */ protected int MAX_EDGE_AGE = 88; /** * The current number of calculations done in one step. * This variable can be set by the user. After stepSize * calculations the result is displayed. */ protected int stepSize = 50; /** * This vaiable determines how long the compute thread sleeps. In this time * the user can interact with the program. Slow machines and/or slow * WWW-browsers need more time than fast machines and/or browsers. * This variable can be set by the user. */ protected int speed = 400; /** * The actual x size of the grid array. */ protected int grid_x = 0; /** * The actual y size of the grid array. */ protected int grid_y = 0; /** * The direction factor for the x axis (-1 or 1) used for the * 'Moving Rectangle' distribution */ protected int bounceX = -1; /** * The direction factor for the y axis (-1 or 1) used for the * 'Moving Rectangle' distribution */ protected int bounceY = -1; /** * The x coordinate for the 'Jumping Rectangle' and 'R.Mouse' distribution */ protected int jumpX = 250; /** * The y coordinate for the 'Jumping Rectangle' and 'R.Mouse' distribution */ protected int jumpY = 250; /** * Stores the old x value of the remainder in order to detect the bounce * (used for the 'Moving Rectangle' distribution) */ protected double bounceX_old = 1; /** * Stores the old y value of the remainder in order to detect the bounce * (used for the 'Moving Rectangle' distribution) */ protected double bounceY_old = 1; /** * The value epsilon for the HCL algorithm. * This variable can be set by the user. */ protected float epsilon = 0.1f; /** * The value epsilon for the GNG algorithm (winner). * This variable can be set by the user. */ protected float epsilonGNG = 0.1f; /** * The value epsilon for the GNG algorithm (second). * This variable can be set by the user. */ protected float epsilonGNG2 = 0.001f; /** * The value alpha for the GNG algorithm. * This variable can be set by the user. */ protected float alphaGNG = 0.5f; /** * The value beta for the GNG algorithm. * This variable can be set by the user. */ protected float betaGNG = 0.0005f; /** * The utility factor for the GNG-U algorithm. * This variable can be set by the user. */ protected float utilityGNG = 3.0f; /** * The decay factor for utility */ protected float forgetFactorUtility = 1.0f - betaGNG; /** * The factor to forget old values. */ protected float forgetFactor = 1.0f - betaGNG; /** * The value lambda initial for the NG,NGwCHL,GG algorithms. * This variable can be set by the user. */ protected float l_i = 30.0f; /** * The value lambda final for the NG,NGwCHL,GG algorithms. * This variable can be set by the user. */ protected float l_f = 0.01f; /** * The value epsiolon(t) for the NG,NGwCHL algorithms. */ protected float e_t = 0.0f; /** * The value epsiolon initial for the NG,NGwCHL,GG algorithms. * This variable can be set by the user. */ protected float e_i = 0.3f; /** * The value epsiolon final for the NG,NGwCHL,GG algorithms. * This variable can be set by the user. */ protected float e_f = 0.05f; /** * The value t_max for the NG,NGwCHL,SOM algorithms. * This variable can be set by the user. */ protected float t_max = 40000.0f; /** * The value delete edge initial for the NGwCHL algorithm. * This variable can be set by the user. */ protected float delEdge_i = 20.0f; /** * The value delete edge final for the NGwCHL algorithm. * This variable can be set by the user. */ protected float delEdge_f = 200.0f; /** * The value sigma for the GG algorithm. * This variable can be set by the user. */ protected float sigma = 0.9f; /** * The value sigma_i for the SOM algorithm. * This variable can be set by the user. */ protected float sigma_i = 5.0f; /** * The value sigma_f for the SOM algorithm. * This variable can be set by the user. */ protected float sigma_f = 5.0f; /** * This value is displayed in the error graph. */ protected float valueGraph = 0.0f; /** * This value contains the best error value for LBG-U up to now. */ protected float errorBestLBG_U = Float.MAX_VALUE; /** * The string shown in the fine-tuning phase of the method GG. */ protected String fineTuningS = ""; /** * The constructor. * * @param graph The drawing area */ ComputeGNG(DemoGNG graph) { this.graph = graph; } /** * Add a node. The new node will be randomly placed within the * given dimension. * * @param d The dimension of the initial drawing area * @return The index of the new node */ protected int addNode(Dimension d) { if ( (nnodes == MAX_NODES) || (nnodes >= maxNodes) ) return -1; NodeGNG n = new NodeGNG(); if (rndInitB) { n.x = (float) (10 + (d.width-20) * Math.random()); n.y = (float) (10 + (d.height-20) * Math.random()); } else { getSignal(distribution); n.x = SignalX; n.y = SignalY; } n.nNeighbor = 0; if (algo == 5) n.moved = true; nodes[nnodes] = n; nNodesChangedB = true; return nnodes++; } /** * Add a node. The new node will be placed at the * given coordinates. * * @param x The x-coordinate of the new node * @param y The y-coordinate of the new node * @return The index of the new node */ protected int addNode(int x, int y) { if ( (nnodes == MAX_NODES) || (nnodes >= maxNodes) ) return -1; NodeGNG n = new NodeGNG(); n.x = x; n.y = y; if (algo == 5) n.moved = true; nodes[nnodes] = n; nNodesChangedB = true; return nnodes++; } /** * Add a node. The new node will be placed between the * given nodes which must be connected. The existing edge is splitted. * The new node gets the average of the interesting values of * the two given nodes. * * @param n1 The index of a node * @param n2 The index of a node * @return The index of the new node */ protected int insertNode(int n1, int n2) { if ( (nnodes == MAX_NODES) || (nnodes >= maxNodes) ) return -1; if ( (n1 < 0) || (n2 < 0) ) return -1; NodeGNG n = new NodeGNG(); float dx = (nodes[n1].x - nodes[n2].x) / 2.0f; float dy = (nodes[n1].y - nodes[n2].y) / 2.0f; nodes[n1].error *= (1.0f - alphaGNG); nodes[n2].error *= (1.0f - alphaGNG); n.error = (nodes[n1].error + nodes[n2].error)/2.0f; n.utility = (nodes[n1].utility + nodes[n2].utility)/2.0f; n.x = nodes[n1].x - dx; n.y = nodes[n1].y - dy; n.inserted = true; nodes[nnodes] = n; deleteEdge(n1, n2); addEdge(n1, nnodes); addEdge(n2, nnodes); nNodesChangedB = true; return nnodes++; } /** * Add a node into the grid. The new node will be randomly placed within the * given dimension. * * @param x The x coordinate of the grid array * @param y The y coordinate of the grid array * @param d The dimension of the initial drawing area * @return The index of the new node */ protected int addGridNode(int x, int y, Dimension d) { if ( (x > MAX_GRID_X) || (y > MAX_GRID_Y) ) return -1; int n = addNode(d); nodes[n].x_grid = x; nodes[n].y_grid = y; grid[x][y] = new GridNodeGNG(n, nodes[n]); return n; } /** * Initialize the grid. The new nodes will be randomly placed within the * given dimension. * * @param x The x size of the grid array * @param y The y size of the grid array * @param d The dimension of the initial drawing area */ protected void initGrid(int x, int y, Dimension d) { if ( (x > MAX_GRID_X) || (y > MAX_GRID_Y) ) return; int i, j; if (algo == 7) maxNodes = x * y; for (i = 0; i < x; i++) for (j = 0; j < y; j++) addGridNode(i, j, d); grid_x = x; grid_y = y; for (i = 0; i < grid_x; i++) { for (j = 0; j < grid_y; j++) { if ( i < (grid_x - 1) ) addEdge(grid[i][j].index, grid[i+1][j].index); if ( j < (grid_y - 1) ) addEdge(grid[i][j].index, grid[i][j+1].index); } } } /** * Prepare to insert a row or column into the grid. * * @return The index of the last inserted node or -1 */ protected int insertGrid() { int tau = 0; int x = 0; int y = 0; int n = -1; float d1 = 0, d2 = 0, d3 = 0, d4 = 0, max = 0; for (int i = 0; i < grid_x; i++) { for (int j = 0; j < grid_y; j++) { // Find maximum resource value tau if ( grid[i][j].tau > tau ) { tau = grid[i][j].tau; x = i; y = j; } grid[i][j].tau = 0; grid[i][j].node.inserted = false; } } // Identify the neighbor with the most different reference vector // left neighbor if (x > 0) d1 = (grid[x-1][y].node.x - grid[x][y].node.x) * (grid[x-1][y].node.x - grid[x][y].node.x) + (grid[x-1][y].node.y - grid[x][y].node.y) * (grid[x-1][y].node.y - grid[x][y].node.y); // upper neighbor if (y > 0) d2 = (grid[x][y-1].node.x - grid[x][y].node.x) * (grid[x][y-1].node.x - grid[x][y].node.x) + (grid[x][y-1].node.y - grid[x][y].node.y) * (grid[x][y-1].node.y - grid[x][y].node.y); // right neighbor if (x < grid_x - 1) d3 = (grid[x+1][y].node.x - grid[x][y].node.x) * (grid[x+1][y].node.x - grid[x][y].node.x) + (grid[x+1][y].node.y - grid[x][y].node.y) * (grid[x+1][y].node.y - grid[x][y].node.y); // lower neighbor if (y < grid_y - 1) d4 = (grid[x][y+1].node.x - grid[x][y].node.x) * (grid[x][y+1].node.x - grid[x][y].node.x) + (grid[x][y+1].node.y - grid[x][y].node.y) * (grid[x][y+1].node.y - grid[x][y].node.y); max = Math.max(d1, Math.max(d2, Math.max(d3, d4))); if (max == d1) n = insertColumn(x - 1); else if (max == d2) n = insertRow(y - 1); else if (max == d3) n = insertColumn(x); else n = insertRow(y); return n; } /** * Add a row into the grid. The new row will be placed after index y. * * @param y The row index * @return The index of the last inserted node or -1 */ protected int insertRow(int y) { if ( (grid_y == MAX_GRID_Y) || (nnodes + grid_x > maxNodes) ) return -1; int n = -1; int i, j; // Insert nodes for the new row for (i = 0; i < grid_x; i++) { n = addNode(0,0); nodes[n].x_grid = i; nodes[n].y_grid = grid_y; grid[i][grid_y] = new GridNodeGNG(n, nodes[n]); // Add Edges if (i != 0) addEdge(grid[i][grid_y].index, grid[i-1][grid_y].index); addEdge(grid[i][grid_y].index, grid[i][grid_y-1].index); } // Now change the parameters (position) for (j = grid_y; j > y+1; j--) { for (i = 0; i < grid_x; i++) { grid[i][j].node.x = grid[i][j-1].node.x; grid[i][j].node.y = grid[i][j-1].node.y; } } for (i = 0; i < grid_x; i++) { grid[i][y+1].node.x = (grid[i][y].node.x + grid[i][y+1].node.x)*0.5f; grid[i][y+1].node.y = (grid[i][y].node.y + grid[i][y+1].node.y)*0.5f; grid[i][y+1].node.inserted = true; } // Make the new row official grid_y++; return n; } /** * Add a column into the grid. The new column will be placed after index x. * * @param x The column index * @return The index of the last inserted node or -1 */ protected int insertColumn(int x) { if ( (grid_x == MAX_GRID_X) || (nnodes + grid_y > maxNodes) ) return -1; int n = -1; int i, j; // Insert nodes for the new Column for (j = 0; j < grid_y; j++) { n = addNode(0,0); nodes[n].x_grid = grid_x; nodes[n].y_grid = j; grid[grid_x][j] = new GridNodeGNG(n, nodes[n]); // Add Edges if (j != 0) addEdge(grid[grid_x][j].index, grid[grid_x][j-1].index); addEdge(grid[grid_x][j].index, grid[grid_x-1][j].index); } // Now change the parameters (position) for (i = grid_x; i > x+1; i--) { for (j = 0; j < grid_y; j++) { grid[i][j].node.x = grid[i-1][j].node.x; grid[i][j].node.y = grid[i-1][j].node.y; } } for (j = 0; j < grid_y; j++) { grid[x+1][j].node.x = (grid[x][j].node.x + grid[x+1][j].node.x)*0.5f; grid[x+1][j].node.y = (grid[x][j].node.y + grid[x+1][j].node.y)*0.5f; grid[x+1][j].node.inserted = true; } // Make the new row official grid_x++; return n; } /** * Sort the array of nodes. The result is in the vsite-array. * The implemented sorting algorithm is heapsort. * * @param n The number of nodes to be sorted * @see ComputeGNG#vsites * @see ComputeGNG#reheapVoronoi */ protected void sortSites(int n) { SiteVoronoi exchange; int i; // Initialize the sorted site array for (i = 1; i <= n; i++) { NodeGNG nd = nodes[i-1]; SiteVoronoi sv = new SiteVoronoi(); sv.coord.x = nd.x; sv.coord.y = nd.y; sv.sitenbr = i-1; sv.refcnt = 0; vsites[i] = sv; } nNodesChangedB = false; // Build a maximum heap for (i = n/2; i > 0; i--) reheapVoronoi(i, n); // Switch elements 1 and i then reheap for (i = n; i > 1; i--) { exchange = vsites[1]; vsites[1] = vsites[i]; vsites[i] = exchange; reheapVoronoi(1, i-1); } } /** * Build a maximum-heap. The result is in the vsite-array. * * @param i The start of the intervall * @param k The end of the intervall * @see ComputeGNG#vsites * @see ComputeGNG#sortSites */ protected void reheapVoronoi(int i, int k) { int j = i; int son; while (2*j <= k) { if (2*j+1 <= k) if ( (vsites[2*j].coord.y > vsites[2*j+1].coord.y) || (vsites[2*j].coord.y == vsites[2*j+1].coord.y && vsites[2*j].coord.x > vsites[2*j+1].coord.x) ) son = 2*j; else son = 2*j + 1; else son = 2*j; if ( (vsites[j].coord.y < vsites[son].coord.y) || (vsites[j].coord.y == vsites[son].coord.y && vsites[j].coord.x < vsites[son].coord.x) ) { SiteVoronoi exchange = vsites[j]; vsites[j] = vsites[son]; vsites[son] = exchange; j = son; } else return; } } /** * Build a minimum-heap. * * @param i The start of the intervall * @param k The end of the intervall */ protected void reheap_min(int i, int k) { int j = i; int son; while (2*j <= k) { if (2*j+1 <= k) if (nodes[snodes[2*j]].dist < nodes[snodes[2*j+1]].dist) son = 2*j; else son = 2*j + 1; else son = 2*j; if (nodes[snodes[j]].dist > nodes[snodes[son]].dist) { int exchange = snodes[j]; snodes[j] = snodes[son]; snodes[son] = exchange; j = son; } else return; } } /** * Delete the given node. * * @param n The index of a node */ protected void deleteNode(int n) { NodeGNG node = nodes[n]; int num = node.numNeighbors(); int i; for (i = 0; i < num; i++) deleteEdge(n, node.neighbor(0)); nNodesChangedB = true; nnodes--; nodes[n] = nodes[nnodes]; nodes[nnodes] = null; // Now rename all occurances of nodes[nnodes] to nodes[n] for (i = 0 ; i < nnodes ; i++) nodes[i].replaceNeighbor(nnodes, n); for (i = 0 ; i < nedges ; i++) edges[i].replace(nnodes, n); return; } /** * Connect two nodes or reset the age of their edge. * * @param from The index of the first node * @param to The index of the second node */ protected void addEdge(int from, int to) { if (nnodes < 2) return; if (nodes[from].isNeighbor(to)) { // Find edge(from,to) and reset age int i = findEdge(from, to); if (i != -1) edges[i].age = 0; return; } if (nedges == MAX_EDGES) return; if ( (nodes[from].moreNeighbors()) && (nodes[to].moreNeighbors()) ) { nodes[to].addNeighbor(from); nodes[from].addNeighbor(to); } else return; // Add new edge EdgeGNG e = new EdgeGNG(); e.from = from; e.to = to; edges[nedges] = e; nedges++; } /** * Disconnect two nodes. * * @param from The index of the first node * @param to The index of the second node */ protected void deleteEdge(int from, int to) { int i = findEdge(from, to); if (i != -1) { nodes[edges[i].from].deleteNeighbor(edges[i].to); nodes[edges[i].to].deleteNeighbor(edges[i].from); nedges--; edges[i] = edges[nedges]; edges[nedges] = null; } return; } /** * Delete an edge. * * @param from The index of the edge */ protected void deleteEdge(int edgeNr) { nodes[edges[edgeNr].from].deleteNeighbor(edges[edgeNr].to); nodes[edges[edgeNr].to].deleteNeighbor(edges[edgeNr].from); nedges--; edges[edgeNr] = edges[nedges]; edges[nedges] = null; } /** * Find an edge. Find the edge between the two given nodes. * * @param from The index of the first node * @param to The index of the second node * @return The index of the found edge or -1 */ protected int findEdge(int from, int to) { for (int i = 0; i < nedges; i++) if (( (edges[i].from == from) && (edges[i].to == to) ) || ( (edges[i].from == to) && (edges[i].to == from) ) ) return i; return -1; } /** * Age an edge. All edges starting from the given node are aged. * Too old edges are deleted. * * @param node The index of a node * @see ComputeGNG#MAX_EDGE_AGE */ protected void ageEdge(int node) { for (int i = nedges - 1; i > -1; i--) { if ( (edges[i].from == node) || (edges[i].to == node) ) edges[i].age++; if (edges[i].age > MAX_EDGE_AGE) deleteEdge(i); } } /** * Find neighbor with the highest error. * * @param master The index of a node * @return The index of a node */ protected int worstErrorNeighbor(int master) { float ws = Float.MIN_VALUE; int wn = -1; int n = -1; int num = nodes[master].numNeighbors(); for (int i = 0; i < num; i++) { n = nodes[master].neighbor(i); if (ws < nodes[n].error) { ws = nodes[n].error; wn = n; } } return wn; } /** * Generate discrete signals for the given distribution. * The result goes into the global arrays discreteSignalsX * and discreteSignalsY . * * @param distrib The specified distribution * @param w The width of the drawing area * @param h The height of the drawing area */ protected void initDiscreteSignals(int distrib) { Dimension d = size(); int w = d.width; int h = d.height; int kx = 1; int ky = 1; int l = 0; int dSX[] = discreteSignalsX; int dSY[] = discreteSignalsY; if (distrib != 7) { for (int i = 0; i < MAX_DISCRETE_SIGNALS; i++) { getSignal(distrib); discreteSignalsX[i] = SignalX; discreteSignalsY[i] = SignalY; } } else { if (w > h) { kx = (int)(w/4); l = h; } else { ky = (int)(h/4); l = w; } dSX[0]=(int)(kx+l*0.13814); dSY[0]=(int)(ky+l*(1.0-0.29675)); dSX[1]=(int)(kx+l*0.19548); dSY[1]=(int)(ky+l*(1.0-0.09674)); dSX[2]=(int)(kx+l*0.73576); dSY[2]=(int)(ky+l*(1.0-0.86994)); dSX[3]=(int)(kx+l*0.73065); dSY[3]=(int)(ky+l*(1.0-0.19024)); dSX[4]=(int)(kx+l*0.83479); dSY[4]=(int)(ky+l*(1.0-0.34258)); dSX[5]=(int)(kx+l*0.13184); dSY[5]=(int)(ky+l*(1.0-0.56509)); dSX[6]=(int)(kx+l*0.15959); dSY[6]=(int)(ky+l*(1.0-0.59065)); dSX[7]=(int)(kx+l*0.21696); dSY[7]=(int)(ky+l*(1.0-0.1402)); dSX[8]=(int)(kx+l*0.61592); dSY[8]=(int)(ky+l*(1.0-0.16657)); dSX[9]=(int)(kx+l*0.10513); dSY[9]=(int)(ky+l*(1.0-0.21708)); dSX[10]=(int)(kx+l*0.1864); dSY[10]=(int)(ky+l*(1.0-0.1454)); dSX[11]=(int)(kx+l*0.36696); dSY[11]=(int)(ky+l*(1.0-0.74924)); dSX[12]=(int)(kx+l*0.18345); dSY[12]=(int)(ky+l*(1.0-0.80946)); dSX[13]=(int)(kx+l*0.8509); dSY[13]=(int)(ky+l*(1.0-0.38268)); dSX[14]=(int)(kx+l*0.19476); dSY[14]=(int)(ky+l*(1.0-0.74262)); dSX[15]=(int)(kx+l*0.49164); dSY[15]=(int)(ky+l*(1.0-0.65776)); dSX[16]=(int)(kx+l*0.86552); dSY[16]=(int)(ky+l*(1.0-0.38373)); dSX[17]=(int)(kx+l*0.73176); dSY[17]=(int)(ky+l*(1.0-0.84414)); dSX[18]=(int)(kx+l*0.71978); dSY[18]=(int)(ky+l*(1.0-0.86979)); dSX[19]=(int)(kx+l*0.83034); dSY[19]=(int)(ky+l*(1.0-0.36613)); dSX[20]=(int)(kx+l*0.39886); dSY[20]=(int)(ky+l*(1.0-0.71479)); dSX[21]=(int)(kx+l*0.09955); dSY[21]=(int)(ky+l*(1.0-0.20342)); dSX[22]=(int)(kx+l*0.07091); dSY[22]=(int)(ky+l*(1.0-0.17197)); dSX[23]=(int)(kx+l*0.21896); dSY[23]=(int)(ky+l*(1.0-0.10398)); dSX[24]=(int)(kx+l*0.72465); dSY[24]=(int)(ky+l*(1.0-0.13984)); dSX[25]=(int)(kx+l*0.71034); dSY[25]=(int)(ky+l*(1.0-0.87981)); dSX[26]=(int)(kx+l*0.83547); dSY[26]=(int)(ky+l*(1.0-0.36065)); dSX[27]=(int)(kx+l*0.13907); dSY[27]=(int)(ky+l*(1.0-0.56451)); dSX[28]=(int)(kx+l*0.62124); dSY[28]=(int)(ky+l*(1.0-0.20175)); dSX[29]=(int)(kx+l*0.65543); dSY[29]=(int)(ky+l*(1.0-0.17331)); dSX[30]=(int)(kx+l*0.72349); dSY[30]=(int)(ky+l*(1.0-0.14375)); dSX[31]=(int)(kx+l*0.82495); dSY[31]=(int)(ky+l*(1.0-0.40116)); dSX[32]=(int)(kx+l*0.76586); dSY[32]=(int)(ky+l*(1.0-0.82376)); dSX[33]=(int)(kx+l*0.24648); dSY[33]=(int)(ky+l*(1.0-0.11987)); dSX[34]=(int)(kx+l*0.14817); dSY[34]=(int)(ky+l*(1.0-0.59985)); dSX[35]=(int)(kx+l*0.82663); dSY[35]=(int)(ky+l*(1.0-0.38964)); dSX[36]=(int)(kx+l*0.37131); dSY[36]=(int)(ky+l*(1.0-0.72726)); dSX[37]=(int)(kx+l*0.12176); dSY[37]=(int)(ky+l*(1.0-0.60139)); dSX[38]=(int)(kx+l*0.73587); dSY[38]=(int)(ky+l*(1.0-0.86952)); dSX[39]=(int)(kx+l*0.59645); dSY[39]=(int)(ky+l*(1.0-0.21302)); dSX[40]=(int)(kx+l*0.39489); dSY[40]=(int)(ky+l*(1.0-0.63452)); dSX[41]=(int)(kx+l*0.234); dSY[41]=(int)(ky+l*(1.0-0.10385)); dSX[42]=(int)(kx+l*0.51314); dSY[42]=(int)(ky+l*(1.0-0.67151)); dSX[43]=(int)(kx+l*0.13499); dSY[43]=(int)(ky+l*(1.0-0.56896)); dSX[44]=(int)(kx+l*0.10815); dSY[44]=(int)(ky+l*(1.0-0.62515)); dSX[45]=(int)(kx+l*0.35487); dSY[45]=(int)(ky+l*(1.0-0.65635)); dSX[46]=(int)(kx+l*0.13939); dSY[46]=(int)(ky+l*(1.0-0.24579)); dSX[47]=(int)(kx+l*0.22087); dSY[47]=(int)(ky+l*(1.0-0.20651)); dSX[48]=(int)(kx+l*0.12274); dSY[48]=(int)(ky+l*(1.0-0.61131)); dSX[49]=(int)(kx+l*0.47888); dSY[49]=(int)(ky+l*(1.0-0.65166)); dSX[50]=(int)(kx+l*0.18836); dSY[50]=(int)(ky+l*(1.0-0.6895)); dSX[51]=(int)(kx+l*0.2511); dSY[51]=(int)(ky+l*(1.0-0.12476)); dSX[52]=(int)(kx+l*0.84242); dSY[52]=(int)(ky+l*(1.0-0.3685)); dSX[53]=(int)(kx+l*0.70824); dSY[53]=(int)(ky+l*(1.0-0.18571)); dSX[54]=(int)(kx+l*0.2548); dSY[54]=(int)(ky+l*(1.0-0.77552)); dSX[55]=(int)(kx+l*0.3659); dSY[55]=(int)(ky+l*(1.0-0.64852)); dSX[56]=(int)(kx+l*0.78094); dSY[56]=(int)(ky+l*(1.0-0.37826)); dSX[57]=(int)(kx+l*0.34205); dSY[57]=(int)(ky+l*(1.0-0.7295)); dSX[58]=(int)(kx+l*0.83349); dSY[58]=(int)(ky+l*(1.0-0.37511)); dSX[59]=(int)(kx+l*0.35477); dSY[59]=(int)(ky+l*(1.0-0.68483)); dSX[60]=(int)(kx+l*0.13761); dSY[60]=(int)(ky+l*(1.0-0.17267)); dSX[61]=(int)(kx+l*0.46041); dSY[61]=(int)(ky+l*(1.0-0.72594)); dSX[62]=(int)(kx+l*0.12945); dSY[62]=(int)(ky+l*(1.0-0.58863)); dSX[63]=(int)(kx+l*0.27379); dSY[63]=(int)(ky+l*(1.0-0.14071)); dSX[64]=(int)(kx+l*0.4097); dSY[64]=(int)(ky+l*(1.0-0.77705)); dSX[65]=(int)(kx+l*0.7175); dSY[65]=(int)(ky+l*(1.0-0.87696)); dSX[66]=(int)(kx+l*0.43969); dSY[66]=(int)(ky+l*(1.0-0.66972)); dSX[67]=(int)(kx+l*0.48588); dSY[67]=(int)(ky+l*(1.0-0.63899)); dSX[68]=(int)(kx+l*0.69263); dSY[68]=(int)(ky+l*(1.0-0.20386)); dSX[69]=(int)(kx+l*0.7374); dSY[69]=(int)(ky+l*(1.0-0.8667)); dSX[70]=(int)(kx+l*0.67306); dSY[70]=(int)(ky+l*(1.0-0.18347)); dSX[71]=(int)(kx+l*0.21203); dSY[71]=(int)(ky+l*(1.0-0.12508)); dSX[72]=(int)(kx+l*0.48821); dSY[72]=(int)(ky+l*(1.0-0.67574)); dSX[73]=(int)(kx+l*0.45742); dSY[73]=(int)(ky+l*(1.0-0.67679)); dSX[74]=(int)(kx+l*0.67982); dSY[74]=(int)(ky+l*(1.0-0.1421)); dSX[75]=(int)(kx+l*0.13429); dSY[75]=(int)(ky+l*(1.0-0.56728)); dSX[76]=(int)(kx+l*0.2402); dSY[76]=(int)(ky+l*(1.0-0.76521)); dSX[77]=(int)(kx+l*0.15482); dSY[77]=(int)(ky+l*(1.0-0.178)); dSX[78]=(int)(kx+l*0.71594); dSY[78]=(int)(ky+l*(1.0-0.15844)); dSX[79]=(int)(kx+l*0.10534); dSY[79]=(int)(ky+l*(1.0-0.59961)); dSX[80]=(int)(kx+l*0.44167); dSY[80]=(int)(ky+l*(1.0-0.69823)); dSX[81]=(int)(kx+l*0.46529); dSY[81]=(int)(ky+l*(1.0-0.70682)); dSX[82]=(int)(kx+l*0.13842); dSY[82]=(int)(ky+l*(1.0-0.56618)); dSX[83]=(int)(kx+l*0.09876); dSY[83]=(int)(ky+l*(1.0-0.5795)); dSX[84]=(int)(kx+l*0.12101); dSY[84]=(int)(ky+l*(1.0-0.57408)); dSX[85]=(int)(kx+l*0.44963); dSY[85]=(int)(ky+l*(1.0-0.74847)); dSX[86]=(int)(kx+l*0.12532); dSY[86]=(int)(ky+l*(1.0-0.56478)); dSX[87]=(int)(kx+l*0.18264); dSY[87]=(int)(ky+l*(1.0-0.77186)); dSX[88]=(int)(kx+l*0.80443); dSY[88]=(int)(ky+l*(1.0-0.35896)); dSX[89]=(int)(kx+l*0.72038); dSY[89]=(int)(ky+l*(1.0-0.90205)); dSX[90]=(int)(kx+l*0.24934); dSY[90]=(int)(ky+l*(1.0-0.77047)); dSX[91]=(int)(kx+l*0.35552); dSY[91]=(int)(ky+l*(1.0-0.70131)); dSX[92]=(int)(kx+l*0.49591); dSY[92]=(int)(ky+l*(1.0-0.71126)); dSX[93]=(int)(kx+l*0.36426); dSY[93]=(int)(ky+l*(1.0-0.72803)); dSX[94]=(int)(kx+l*0.21113); dSY[94]=(int)(ky+l*(1.0-0.08745)); dSX[95]=(int)(kx+l*0.33412); dSY[95]=(int)(ky+l*(1.0-0.68345)); dSX[96]=(int)(kx+l*0.17158); dSY[96]=(int)(ky+l*(1.0-0.226)); dSX[97]=(int)(kx+l*0.69135); dSY[97]=(int)(ky+l*(1.0-0.26172)); dSX[98]=(int)(kx+l*0.80362); dSY[98]=(int)(ky+l*(1.0-0.34908)); dSX[99]=(int)(kx+l*0.49367); dSY[99]=(int)(ky+l*(1.0-0.61372)); dSX[100]=(int)(kx+l*0.67809); dSY[100]=(int)(ky+l*(1.0-0.16071)); dSX[101]=(int)(kx+l*0.42288); dSY[101]=(int)(ky+l*(1.0-0.7547)); dSX[102]=(int)(kx+l*0.21535); dSY[102]=(int)(ky+l*(1.0-0.71766)); dSX[103]=(int)(kx+l*0.26248); dSY[103]=(int)(ky+l*(1.0-0.0794)); dSX[104]=(int)(kx+l*0.65766); dSY[104]=(int)(ky+l*(1.0-0.11433)); dSX[105]=(int)(kx+l*0.81799); dSY[105]=(int)(ky+l*(1.0-0.36416)); dSX[106]=(int)(kx+l*0.80867); dSY[106]=(int)(ky+l*(1.0-0.39382)); dSX[107]=(int)(kx+l*0.2401); dSY[107]=(int)(ky+l*(1.0-0.83207)); dSX[108]=(int)(kx+l*0.83016); dSY[108]=(int)(ky+l*(1.0-0.37551)); dSX[109]=(int)(kx+l*0.30746); dSY[109]=(int)(ky+l*(1.0-0.78597)); dSX[110]=(int)(kx+l*0.22122); dSY[110]=(int)(ky+l*(1.0-0.19961)); dSX[111]=(int)(kx+l*0.81422); dSY[111]=(int)(ky+l*(1.0-0.39008)); dSX[112]=(int)(kx+l*0.28025); dSY[112]=(int)(ky+l*(1.0-0.16485)); dSX[113]=(int)(kx+l*0.42936); dSY[113]=(int)(ky+l*(1.0-0.70449)); dSX[114]=(int)(kx+l*0.20721); dSY[114]=(int)(ky+l*(1.0-0.79412)); dSX[115]=(int)(kx+l*0.1023); dSY[115]=(int)(ky+l*(1.0-0.59687)); dSX[116]=(int)(kx+l*0.49873); dSY[116]=(int)(ky+l*(1.0-0.68088)); dSX[117]=(int)(kx+l*0.44373); dSY[117]=(int)(ky+l*(1.0-0.60472)); dSX[118]=(int)(kx+l*0.12955); dSY[118]=(int)(ky+l*(1.0-0.58045)); dSX[119]=(int)(kx+l*0.40319); dSY[119]=(int)(ky+l*(1.0-0.64087)); dSX[120]=(int)(kx+l*0.39597); dSY[120]=(int)(ky+l*(1.0-0.74223)); dSX[121]=(int)(kx+l*0.37318); dSY[121]=(int)(ky+l*(1.0-0.74561)); dSX[122]=(int)(kx+l*0.48026); dSY[122]=(int)(ky+l*(1.0-0.65002)); dSX[123]=(int)(kx+l*0.09824); dSY[123]=(int)(ky+l*(1.0-0.15969)); dSX[124]=(int)(kx+l*0.68454); dSY[124]=(int)(ky+l*(1.0-0.17986)); dSX[125]=(int)(kx+l*0.11659); dSY[125]=(int)(ky+l*(1.0-0.30008)); dSX[126]=(int)(kx+l*0.73836); dSY[126]=(int)(ky+l*(1.0-0.87819)); dSX[127]=(int)(kx+l*0.37924); dSY[127]=(int)(ky+l*(1.0-0.72885)); dSX[128]=(int)(kx+l*0.07252); dSY[128]=(int)(ky+l*(1.0-0.21803)); dSX[129]=(int)(kx+l*0.22104); dSY[129]=(int)(ky+l*(1.0-0.81961)); dSX[130]=(int)(kx+l*0.23872); dSY[130]=(int)(ky+l*(1.0-0.06629)); dSX[131]=(int)(kx+l*0.27114); dSY[131]=(int)(ky+l*(1.0-0.77851)); dSX[132]=(int)(kx+l*0.84307); dSY[132]=(int)(ky+l*(1.0-0.35729)); dSX[133]=(int)(kx+l*0.83856); dSY[133]=(int)(ky+l*(1.0-0.38892)); dSX[134]=(int)(kx+l*0.84041); dSY[134]=(int)(ky+l*(1.0-0.33806)); dSX[135]=(int)(kx+l*0.72441); dSY[135]=(int)(ky+l*(1.0-0.84423)); dSX[136]=(int)(kx+l*0.45169); dSY[136]=(int)(ky+l*(1.0-0.66888)); dSX[137]=(int)(kx+l*0.7291); dSY[137]=(int)(ky+l*(1.0-0.85748)); dSX[138]=(int)(kx+l*0.38792); dSY[138]=(int)(ky+l*(1.0-0.74045)); dSX[139]=(int)(kx+l*0.69006); dSY[139]=(int)(ky+l*(1.0-0.88995)); dSX[140]=(int)(kx+l*0.09004); dSY[140]=(int)(ky+l*(1.0-0.57847)); dSX[141]=(int)(kx+l*0.20986); dSY[141]=(int)(ky+l*(1.0-0.21552)); dSX[142]=(int)(kx+l*0.22969); dSY[142]=(int)(ky+l*(1.0-0.79372)); dSX[143]=(int)(kx+l*0.2407); dSY[143]=(int)(ky+l*(1.0-0.78147)); dSX[144]=(int)(kx+l*0.83483); dSY[144]=(int)(ky+l*(1.0-0.35725)); dSX[145]=(int)(kx+l*0.74069); dSY[145]=(int)(ky+l*(1.0-0.87034)); dSX[146]=(int)(kx+l*0.53127); dSY[146]=(int)(ky+l*(1.0-0.69099)); dSX[147]=(int)(kx+l*0.73562); dSY[147]=(int)(ky+l*(1.0-0.89203)); dSX[148]=(int)(kx+l*0.22449); dSY[148]=(int)(ky+l*(1.0-0.14296)); dSX[149]=(int)(kx+l*0.74473); dSY[149]=(int)(ky+l*(1.0-0.85085)); dSX[150]=(int)(kx+l*0.80492); dSY[150]=(int)(ky+l*(1.0-0.40119)); dSX[151]=(int)(kx+l*0.66545); dSY[151]=(int)(ky+l*(1.0-0.14658)); dSX[152]=(int)(kx+l*0.74401); dSY[152]=(int)(ky+l*(1.0-0.88545)); dSX[153]=(int)(kx+l*0.16486); dSY[153]=(int)(ky+l*(1.0-0.81768)); dSX[154]=(int)(kx+l*0.10909); dSY[154]=(int)(ky+l*(1.0-0.58963)); dSX[155]=(int)(kx+l*0.36812); dSY[155]=(int)(ky+l*(1.0-0.71451)); dSX[156]=(int)(kx+l*0.77083); dSY[156]=(int)(ky+l*(1.0-0.86754)); dSX[157]=(int)(kx+l*0.19709); dSY[157]=(int)(ky+l*(1.0-0.16813)); dSX[158]=(int)(kx+l*0.08257); dSY[158]=(int)(ky+l*(1.0-0.57901)); dSX[159]=(int)(kx+l*0.81561); dSY[159]=(int)(ky+l*(1.0-0.38789)); dSX[160]=(int)(kx+l*0.11613); dSY[160]=(int)(ky+l*(1.0-0.61403)); dSX[161]=(int)(kx+l*0.16391); dSY[161]=(int)(ky+l*(1.0-0.10041)); dSX[162]=(int)(kx+l*0.36024); dSY[162]=(int)(ky+l*(1.0-0.75178)); dSX[163]=(int)(kx+l*0.73822); dSY[163]=(int)(ky+l*(1.0-0.84884)); dSX[164]=(int)(kx+l*0.22963); dSY[164]=(int)(ky+l*(1.0-0.11442)); dSX[165]=(int)(kx+l*0.01152); dSY[165]=(int)(ky+l*(1.0-0.27939)); dSX[166]=(int)(kx+l*0.74314); dSY[166]=(int)(ky+l*(1.0-0.87522)); dSX[167]=(int)(kx+l*0.22871); dSY[167]=(int)(ky+l*(1.0-0.134)); dSX[168]=(int)(kx+l*0.14996); dSY[168]=(int)(ky+l*(1.0-0.54459)); dSX[169]=(int)(kx+l*0.14354); dSY[169]=(int)(ky+l*(1.0-0.25589)); dSX[170]=(int)(kx+l*0.0779); dSY[170]=(int)(ky+l*(1.0-0.2636)); dSX[171]=(int)(kx+l*0.13208); dSY[171]=(int)(ky+l*(1.0-0.28005)); dSX[172]=(int)(kx+l*0.2498); dSY[172]=(int)(ky+l*(1.0-0.75765)); dSX[173]=(int)(kx+l*0.30859); dSY[173]=(int)(ky+l*(1.0-0.08592)); dSX[174]=(int)(kx+l*0.03277); dSY[174]=(int)(ky+l*(1.0-0.25141)); dSX[175]=(int)(kx+l*0.69026); dSY[175]=(int)(ky+l*(1.0-0.11579)); dSX[176]=(int)(kx+l*0.70569); dSY[176]=(int)(ky+l*(1.0-0.20655)); dSX[177]=(int)(kx+l*0.19796); dSY[177]=(int)(ky+l*(1.0-0.1327)); dSX[178]=(int)(kx+l*0.10402); dSY[178]=(int)(ky+l*(1.0-0.18623)); dSX[179]=(int)(kx+l*0.20623); dSY[179]=(int)(ky+l*(1.0-0.17315)); dSX[180]=(int)(kx+l*0.14383); dSY[180]=(int)(ky+l*(1.0-0.16819)); dSX[181]=(int)(kx+l*0.43416); dSY[181]=(int)(ky+l*(1.0-0.81161)); dSX[182]=(int)(kx+l*0.21801); dSY[182]=(int)(ky+l*(1.0-0.1926)); dSX[183]=(int)(kx+l*0.80582); dSY[183]=(int)(ky+l*(1.0-0.40684)); dSX[184]=(int)(kx+l*0.47273); dSY[184]=(int)(ky+l*(1.0-0.66746)); dSX[185]=(int)(kx+l*0.72923); dSY[185]=(int)(ky+l*(1.0-0.91807)); dSX[186]=(int)(kx+l*0.21609); dSY[186]=(int)(ky+l*(1.0-0.14719)); dSX[187]=(int)(kx+l*0.61592); dSY[187]=(int)(ky+l*(1.0-0.17603)); dSX[188]=(int)(kx+l*0.25956); dSY[188]=(int)(ky+l*(1.0-0.74824)); dSX[189]=(int)(kx+l*0.10157); dSY[189]=(int)(ky+l*(1.0-0.25437)); dSX[190]=(int)(kx+l*0.34822); dSY[190]=(int)(ky+l*(1.0-0.74119)); dSX[191]=(int)(kx+l*0.37535); dSY[191]=(int)(ky+l*(1.0-0.68263)); dSX[192]=(int)(kx+l*0.11609); dSY[192]=(int)(ky+l*(1.0-0.25491)); dSX[193]=(int)(kx+l*0.84751); dSY[193]=(int)(ky+l*(1.0-0.36326)); dSX[194]=(int)(kx+l*0.48434); dSY[194]=(int)(ky+l*(1.0-0.71852)); dSX[195]=(int)(kx+l*0.82894); dSY[195]=(int)(ky+l*(1.0-0.38072)); dSX[196]=(int)(kx+l*0.23618); dSY[196]=(int)(ky+l*(1.0-0.78797)); dSX[197]=(int)(kx+l*0.70894); dSY[197]=(int)(ky+l*(1.0-0.84481)); dSX[198]=(int)(kx+l*0.21377); dSY[198]=(int)(ky+l*(1.0-0.08697)); dSX[199]=(int)(kx+l*0.08777); dSY[199]=(int)(ky+l*(1.0-0.23077)); dSX[200]=(int)(kx+l*0.4627); dSY[200]=(int)(ky+l*(1.0-0.68689)); dSX[201]=(int)(kx+l*0.1064); dSY[201]=(int)(ky+l*(1.0-0.13423)); dSX[202]=(int)(kx+l*0.34044); dSY[202]=(int)(ky+l*(1.0-0.71728)); dSX[203]=(int)(kx+l*0.14377); dSY[203]=(int)(ky+l*(1.0-0.10488)); dSX[204]=(int)(kx+l*0.83586); dSY[204]=(int)(ky+l*(1.0-0.39654)); dSX[205]=(int)(kx+l*0.23719); dSY[205]=(int)(ky+l*(1.0-0.75877)); dSX[206]=(int)(kx+l*0.72909); dSY[206]=(int)(ky+l*(1.0-0.83794)); dSX[207]=(int)(kx+l*0.11163); dSY[207]=(int)(ky+l*(1.0-0.57717)); dSX[208]=(int)(kx+l*0.82082); dSY[208]=(int)(ky+l*(1.0-0.38887)); dSX[209]=(int)(kx+l*0.23973); dSY[209]=(int)(ky+l*(1.0-0.09762)); dSX[210]=(int)(kx+l*0.18049); dSY[210]=(int)(ky+l*(1.0-0.7213)); dSX[211]=(int)(kx+l*0.17251); dSY[211]=(int)(ky+l*(1.0-0.06261)); dSX[212]=(int)(kx+l*0.73943); dSY[212]=(int)(ky+l*(1.0-0.1515)); dSX[213]=(int)(kx+l*0.12257); dSY[213]=(int)(ky+l*(1.0-0.21737)); dSX[214]=(int)(kx+l*0.72598); dSY[214]=(int)(ky+l*(1.0-0.87021)); dSX[215]=(int)(kx+l*0.7244); dSY[215]=(int)(ky+l*(1.0-0.88142)); dSX[216]=(int)(kx+l*0.21058); dSY[216]=(int)(ky+l*(1.0-0.83842)); dSX[217]=(int)(kx+l*0.34401); dSY[217]=(int)(ky+l*(1.0-0.72108)); dSX[218]=(int)(kx+l*0.65233); dSY[218]=(int)(ky+l*(1.0-0.15241)); dSX[219]=(int)(kx+l*0.1184); dSY[219]=(int)(ky+l*(1.0-0.59815)); dSX[220]=(int)(kx+l*0.20673); dSY[220]=(int)(ky+l*(1.0-0.09814)); dSX[221]=(int)(kx+l*0.65673); dSY[221]=(int)(ky+l*(1.0-0.19377)); dSX[222]=(int)(kx+l*0.46674); dSY[222]=(int)(ky+l*(1.0-0.7408)); dSX[223]=(int)(kx+l*0.14444); dSY[223]=(int)(ky+l*(1.0-0.18892)); dSX[224]=(int)(kx+l*0.44631); dSY[224]=(int)(ky+l*(1.0-0.72023)); dSX[225]=(int)(kx+l*0.18501); dSY[225]=(int)(ky+l*(1.0-0.81523)); dSX[226]=(int)(kx+l*0.67013); dSY[226]=(int)(ky+l*(1.0-0.17383)); dSX[227]=(int)(kx+l*0.21007); dSY[227]=(int)(ky+l*(1.0-0.11003)); dSX[228]=(int)(kx+l*0.28895); dSY[228]=(int)(ky+l*(1.0-0.79667)); dSX[229]=(int)(kx+l*0.355); dSY[229]=(int)(ky+l*(1.0-0.77679)); dSX[230]=(int)(kx+l*0.8031); dSY[230]=(int)(ky+l*(1.0-0.40707)); dSX[231]=(int)(kx+l*0.20507); dSY[231]=(int)(ky+l*(1.0-0.23746)); dSX[232]=(int)(kx+l*0.2091); dSY[232]=(int)(ky+l*(1.0-0.1445)); dSX[233]=(int)(kx+l*0.69395); dSY[233]=(int)(ky+l*(1.0-0.87292)); dSX[234]=(int)(kx+l*0.26225); dSY[234]=(int)(ky+l*(1.0-0.83517)); dSX[235]=(int)(kx+l*0.46057); dSY[235]=(int)(ky+l*(1.0-0.66066)); dSX[236]=(int)(kx+l*0.46715); dSY[236]=(int)(ky+l*(1.0-0.62083)); dSX[237]=(int)(kx+l*0.15991); dSY[237]=(int)(ky+l*(1.0-0.16164)); dSX[238]=(int)(kx+l*0.66818); dSY[238]=(int)(ky+l*(1.0-0.18336)); dSX[239]=(int)(kx+l*0.1206); dSY[239]=(int)(ky+l*(1.0-0.20415)); dSX[240]=(int)(kx+l*0.11134); dSY[240]=(int)(ky+l*(1.0-0.17899)); dSX[241]=(int)(kx+l*0.81705); dSY[241]=(int)(ky+l*(1.0-0.3876)); dSX[242]=(int)(kx+l*0.16158); dSY[242]=(int)(ky+l*(1.0-0.58197)); dSX[243]=(int)(kx+l*0.71638); dSY[243]=(int)(ky+l*(1.0-0.92072)); dSX[244]=(int)(kx+l*0.70824); dSY[244]=(int)(ky+l*(1.0-0.86881)); dSX[245]=(int)(kx+l*0.21828); dSY[245]=(int)(ky+l*(1.0-0.07292)); dSX[246]=(int)(kx+l*0.23573); dSY[246]=(int)(ky+l*(1.0-0.05748)); dSX[247]=(int)(kx+l*0.11584); dSY[247]=(int)(ky+l*(1.0-0.70939)); dSX[248]=(int)(kx+l*0.1235); dSY[248]=(int)(ky+l*(1.0-0.23567)); dSX[249]=(int)(kx+l*0.18015); dSY[249]=(int)(ky+l*(1.0-0.73396)); dSX[250]=(int)(kx+l*0.04505); dSY[250]=(int)(ky+l*(1.0-0.22103)); dSX[251]=(int)(kx+l*0.75712); dSY[251]=(int)(ky+l*(1.0-0.89093)); dSX[252]=(int)(kx+l*0.51855); dSY[252]=(int)(ky+l*(1.0-0.68719)); dSX[253]=(int)(kx+l*0.20628); dSY[253]=(int)(ky+l*(1.0-0.78227)); dSX[254]=(int)(kx+l*0.3027); dSY[254]=(int)(ky+l*(1.0-0.74326)); dSX[255]=(int)(kx+l*0.72344); dSY[255]=(int)(ky+l*(1.0-0.86115)); dSX[256]=(int)(kx+l*0.7823); dSY[256]=(int)(ky+l*(1.0-0.40698)); dSX[257]=(int)(kx+l*0.80725); dSY[257]=(int)(ky+l*(1.0-0.36859)); dSX[258]=(int)(kx+l*0.22767); dSY[258]=(int)(ky+l*(1.0-0.72875)); dSX[259]=(int)(kx+l*0.8389); dSY[259]=(int)(ky+l*(1.0-0.36071)); dSX[260]=(int)(kx+l*0.15643); dSY[260]=(int)(ky+l*(1.0-0.1861)); dSX[261]=(int)(kx+l*0.70301); dSY[261]=(int)(ky+l*(1.0-0.13106)); dSX[262]=(int)(kx+l*0.27509); dSY[262]=(int)(ky+l*(1.0-0.07504)); dSX[263]=(int)(kx+l*0.26088); dSY[263]=(int)(ky+l*(1.0-0.7257)); dSX[264]=(int)(kx+l*0.21206); dSY[264]=(int)(ky+l*(1.0-0.75771)); dSX[265]=(int)(kx+l*0.13805); dSY[265]=(int)(ky+l*(1.0-0.58384)); dSX[266]=(int)(kx+l*0.77447); dSY[266]=(int)(ky+l*(1.0-0.88097)); dSX[267]=(int)(kx+l*0.24243); dSY[267]=(int)(ky+l*(1.0-0.76475)); dSX[268]=(int)(kx+l*0.22454); dSY[268]=(int)(ky+l); // -0.01632 dSX[269]=(int)(kx+l*0.46329); dSY[269]=(int)(ky+l*(1.0-0.66386)); dSX[270]=(int)(kx+l*0.42856); dSY[270]=(int)(ky+l*(1.0-0.77059)); dSX[271]=(int)(kx+l*0.09912); dSY[271]=(int)(ky+l*(1.0-0.23061)); dSX[272]=(int)(kx+l*0.40031); dSY[272]=(int)(ky+l*(1.0-0.7123)); dSX[273]=(int)(kx+l*0.41085); dSY[273]=(int)(ky+l*(1.0-0.68234)); dSX[274]=(int)(kx+l*0.16843); dSY[274]=(int)(ky+l*(1.0-0.21484)); dSX[275]=(int)(kx+l*0.19902); dSY[275]=(int)(ky+l*(1.0-0.74001)); dSX[276]=(int)(kx+l*0.23913); dSY[276]=(int)(ky+l*(1.0-0.83891)); dSX[277]=(int)(kx+l*0.52281); dSY[277]=(int)(ky+l*(1.0-0.68538)); dSX[278]=(int)(kx+l*0.77193); dSY[278]=(int)(ky+l*(1.0-0.85292)); dSX[279]=(int)(kx+l*0.62099); dSY[279]=(int)(ky+l*(1.0-0.1954)); dSX[280]=(int)(kx+l*0.76508); dSY[280]=(int)(ky+l*(1.0-0.88685)); dSX[281]=(int)(kx+l*0.17655); dSY[281]=(int)(ky+l*(1.0-0.19809)); dSX[282]=(int)(kx+l*0.25752); dSY[282]=(int)(ky+l*(1.0-0.14649)); dSX[283]=(int)(kx+l*0.41938); dSY[283]=(int)(ky+l*(1.0-0.66025)); dSX[284]=(int)(kx+l*0.64649); dSY[284]=(int)(ky+l*(1.0-0.16984)); dSX[285]=(int)(kx+l*0.83039); dSY[285]=(int)(ky+l*(1.0-0.36848)); dSX[286]=(int)(kx+l*0.47482); dSY[286]=(int)(ky+l*(1.0-0.75725)); dSX[287]=(int)(kx+l*0.36224); dSY[287]=(int)(ky+l*(1.0-0.72407)); dSX[288]=(int)(kx+l*0.41691); dSY[288]=(int)(ky+l*(1.0-0.76211)); dSX[289]=(int)(kx+l*0.71197); dSY[289]=(int)(ky+l*(1.0-0.88301)); dSX[290]=(int)(kx+l*0.38875); dSY[290]=(int)(ky+l*(1.0-0.66923)); dSX[291]=(int)(kx+l*0.65122); dSY[291]=(int)(ky+l*(1.0-0.2006)); dSX[292]=(int)(kx+l*0.14963); dSY[292]=(int)(ky+l*(1.0-0.58176)); dSX[293]=(int)(kx+l*0.12702); dSY[293]=(int)(ky+l*(1.0-0.20527)); dSX[294]=(int)(kx+l*0.21471); dSY[294]=(int)(ky+l*(1.0-0.80336)); dSX[295]=(int)(kx+l*0.1117); dSY[295]=(int)(ky+l*(1.0-0.58565)); dSX[296]=(int)(kx+l*0.21964); dSY[296]=(int)(ky+l*(1.0-0.05787)); dSX[297]=(int)(kx+l*0.52603); dSY[297]=(int)(ky+l*(1.0-0.68563)); dSX[298]=(int)(kx+l*0.2513); dSY[298]=(int)(ky+l*(1.0-0.11038)); dSX[299]=(int)(kx+l*0.23048); dSY[299]=(int)(ky+l*(1.0-0.81628)); dSX[300]=(int)(kx+l*0.07918); dSY[300]=(int)(ky+l*(1.0-0.59745)); dSX[301]=(int)(kx+l*0.39107); dSY[301]=(int)(ky+l*(1.0-0.70391)); dSX[302]=(int)(kx+l*0.1921); dSY[302]=(int)(ky+l*(1.0-0.75531)); dSX[303]=(int)(kx+l*0.69606); dSY[303]=(int)(ky+l*(1.0-0.21785)); dSX[304]=(int)(kx+l*0.14619); dSY[304]=(int)(ky+l*(1.0-0.57905)); dSX[305]=(int)(kx+l*0.79628); dSY[305]=(int)(ky+l*(1.0-0.3461)); dSX[306]=(int)(kx+l*0.26276); dSY[306]=(int)(ky+l*(1.0-0.17608)); dSX[307]=(int)(kx+l*0.32383); dSY[307]=(int)(ky+l*(1.0-0.74517)); dSX[308]=(int)(kx+l*0.71259); dSY[308]=(int)(ky+l*(1.0-0.85462)); dSX[309]=(int)(kx+l*0.11917); dSY[309]=(int)(ky+l*(1.0-0.25115)); dSX[310]=(int)(kx+l*0.15771); dSY[310]=(int)(ky+l*(1.0-0.5723)); dSX[311]=(int)(kx+l*0.74207); dSY[311]=(int)(ky+l*(1.0-0.86498)); dSX[312]=(int)(kx+l*0.30246); dSY[312]=(int)(ky+l*(1.0-0.66994)); dSX[313]=(int)(kx+l*0.20864); dSY[313]=(int)(ky+l*(1.0-0.16323)); dSX[314]=(int)(kx+l*0.1412); dSY[314]=(int)(ky+l*(1.0-0.56028)); dSX[315]=(int)(kx+l*0.82053); dSY[315]=(int)(ky+l*(1.0-0.35693)); dSX[316]=(int)(kx+l*0.22989); dSY[316]=(int)(ky+l*(1.0-0.81021)); dSX[317]=(int)(kx+l*0.10676); dSY[317]=(int)(ky+l*(1.0-0.20945)); dSX[318]=(int)(kx+l*0.24867); dSY[318]=(int)(ky+l*(1.0-0.13241)); dSX[319]=(int)(kx+l*0.18081); dSY[319]=(int)(ky+l*(1.0-0.77961)); dSX[320]=(int)(kx+l*0.8051); dSY[320]=(int)(ky+l*(1.0-0.37642)); dSX[321]=(int)(kx+l*0.71303); dSY[321]=(int)(ky+l*(1.0-0.87868)); dSX[322]=(int)(kx+l*0.20502); dSY[322]=(int)(ky+l*(1.0-0.20587)); dSX[323]=(int)(kx+l*0.47605); dSY[323]=(int)(ky+l*(1.0-0.68292)); dSX[324]=(int)(kx+l*0.20975); dSY[324]=(int)(ky+l*(1.0-0.13444)); dSX[325]=(int)(kx+l*0.7098); dSY[325]=(int)(ky+l*(1.0-0.85967)); dSX[326]=(int)(kx+l*0.19912); dSY[326]=(int)(ky+l*(1.0-0.11887)); dSX[327]=(int)(kx+l*0.21338); dSY[327]=(int)(ky+l*(1.0-0.15242)); dSX[328]=(int)(kx+l*0.0816); dSY[328]=(int)(ky+l*(1.0-0.20505)); dSX[329]=(int)(kx+l*0.81617); dSY[329]=(int)(ky+l*(1.0-0.37632)); dSX[330]=(int)(kx+l*0.11072); dSY[330]=(int)(ky+l*(1.0-0.1742)); dSX[331]=(int)(kx+l*0.44663); dSY[331]=(int)(ky+l*(1.0-0.7283)); dSX[332]=(int)(kx+l*0.43758); dSY[332]=(int)(ky+l*(1.0-0.7116)); dSX[333]=(int)(kx+l*0.11169); dSY[333]=(int)(ky+l*(1.0-0.58286)); dSX[334]=(int)(kx+l*0.21739); dSY[334]=(int)(ky+l*(1.0-0.808)); dSX[335]=(int)(kx+l*0.11504); dSY[335]=(int)(ky+l*(1.0-0.58542)); dSX[336]=(int)(kx+l*0.22232); dSY[336]=(int)(ky+l*(1.0-0.10244)); dSX[337]=(int)(kx+l*0.13277); dSY[337]=(int)(ky+l*(1.0-0.5679)); dSX[338]=(int)(kx+l*0.41598); dSY[338]=(int)(ky+l*(1.0-0.73469)); dSX[339]=(int)(kx+l*0.23372); dSY[339]=(int)(ky+l*(1.0-0.76431)); dSX[340]=(int)(kx+l*0.32057); dSY[340]=(int)(ky+l*(1.0-0.75133)); dSX[341]=(int)(kx+l*0.82525); dSY[341]=(int)(ky+l*(1.0-0.39564)); dSX[342]=(int)(kx+l*0.15967); dSY[342]=(int)(ky+l*(1.0-0.17686)); dSX[343]=(int)(kx+l*0.65594); dSY[343]=(int)(ky+l*(1.0-0.90155)); dSX[344]=(int)(kx+l*0.71754); dSY[344]=(int)(ky+l*(1.0-0.87787)); dSX[345]=(int)(kx+l*0.11191); dSY[345]=(int)(ky+l*(1.0-0.59932)); dSX[346]=(int)(kx+l*0.2125); dSY[346]=(int)(ky+l*(1.0-0.05011)); dSX[347]=(int)(kx+l*0.21381); dSY[347]=(int)(ky+l*(1.0-0.13874)); dSX[348]=(int)(kx+l*0.32597); dSY[348]=(int)(ky+l*(1.0-0.702)); dSX[349]=(int)(kx+l*0.84447); dSY[349]=(int)(ky+l*(1.0-0.377)); dSX[350]=(int)(kx+l*0.23257); dSY[350]=(int)(ky+l*(1.0-0.0836)); dSX[351]=(int)(kx+l*0.09849); dSY[351]=(int)(ky+l*(1.0-0.15117)); dSX[352]=(int)(kx+l*0.25526); dSY[352]=(int)(ky+l*(1.0-0.156)); dSX[353]=(int)(kx+l*0.46334); dSY[353]=(int)(ky+l*(1.0-0.69123)); dSX[354]=(int)(kx+l*0.48943); dSY[354]=(int)(ky+l*(1.0-0.75123)); dSX[355]=(int)(kx+l*0.7088); dSY[355]=(int)(ky+l*(1.0-0.8525)); dSX[356]=(int)(kx+l*0.29138); dSY[356]=(int)(ky+l*(1.0-0.73165)); dSX[357]=(int)(kx+l*0.15562); dSY[357]=(int)(ky+l*(1.0-0.80957)); dSX[358]=(int)(kx+l*0.45633); dSY[358]=(int)(ky+l*(1.0-0.62115)); dSX[359]=(int)(kx+l*0.22247); dSY[359]=(int)(ky+l*(1.0-0.73574)); dSX[360]=(int)(kx+l*0.20278); dSY[360]=(int)(ky+l*(1.0-0.02718)); dSX[361]=(int)(kx+l*0.1757); dSY[361]=(int)(ky+l*(1.0-0.77329)); dSX[362]=(int)(kx+l*0.81154); dSY[362]=(int)(ky+l*(1.0-0.34851)); dSX[363]=(int)(kx+l*0.63127); dSY[363]=(int)(ky+l*(1.0-0.19212)); dSX[364]=(int)(kx+l*0.80712); dSY[364]=(int)(ky+l*(1.0-0.3727)); dSX[365]=(int)(kx+l*0.79678); dSY[365]=(int)(ky+l*(1.0-0.37069)); dSX[366]=(int)(kx+l*0.65493); dSY[366]=(int)(ky+l*(1.0-0.17201)); dSX[367]=(int)(kx+l*0.11119); dSY[367]=(int)(ky+l*(1.0-0.55032)); dSX[368]=(int)(kx+l*0.35914); dSY[368]=(int)(ky+l*(1.0-0.69928)); dSX[369]=(int)(kx+l*0.84783); dSY[369]=(int)(ky+l*(1.0-0.38467)); dSX[370]=(int)(kx+l*0.25637); dSY[370]=(int)(ky+l*(1.0-0.16449)); dSX[371]=(int)(kx+l*0.4251); dSY[371]=(int)(ky+l*(1.0-0.75901)); dSX[372]=(int)(kx+l*0.19824); dSY[372]=(int)(ky+l*(1.0-0.85476)); dSX[373]=(int)(kx+l*0.49887); dSY[373]=(int)(ky+l*(1.0-0.69768)); dSX[374]=(int)(kx+l*0.86102); dSY[374]=(int)(ky+l*(1.0-0.37142)); dSX[375]=(int)(kx+l*0.19372); dSY[375]=(int)(ky+l*(1.0-0.80485)); dSX[376]=(int)(kx+l*0.11601); dSY[376]=(int)(ky+l*(1.0-0.55327)); dSX[377]=(int)(kx+l*0.72774); dSY[377]=(int)(ky+l*(1.0-0.87631)); dSX[378]=(int)(kx+l*0.24923); dSY[378]=(int)(ky+l*(1.0-0.79912)); dSX[379]=(int)(kx+l*0.4765); dSY[379]=(int)(ky+l*(1.0-0.68893)); dSX[380]=(int)(kx+l*0.82476); dSY[380]=(int)(ky+l*(1.0-0.35662)); dSX[381]=(int)(kx+l*0.73111); dSY[381]=(int)(ky+l*(1.0-0.17849)); dSX[382]=(int)(kx+l*0.23645); dSY[382]=(int)(ky+l*(1.0-0.8192)); dSX[383]=(int)(kx+l*0.24282); dSY[383]=(int)(ky+l*(1.0-0.79375)); dSX[384]=(int)(kx+l*0.32193); dSY[384]=(int)(ky+l*(1.0-0.73014)); dSX[385]=(int)(kx+l*0.18991); dSY[385]=(int)(ky+l*(1.0-0.76666)); dSX[386]=(int)(kx+l*0.4943); dSY[386]=(int)(ky+l*(1.0-0.64545)); dSX[387]=(int)(kx+l*0.45752); dSY[387]=(int)(ky+l*(1.0-0.68871)); dSX[388]=(int)(kx+l*0.27258); dSY[388]=(int)(ky+l*(1.0-0.75787)); dSX[389]=(int)(kx+l*0.48832); dSY[389]=(int)(ky+l*(1.0-0.66738)); dSX[390]=(int)(kx+l*0.70802); dSY[390]=(int)(ky+l*(1.0-0.84396)); dSX[391]=(int)(kx+l*0.36794); dSY[391]=(int)(ky+l*(1.0-0.63548)); dSX[392]=(int)(kx+l*0.37738); dSY[392]=(int)(ky+l*(1.0-0.73531)); dSX[393]=(int)(kx+l*0.23611); dSY[393]=(int)(ky+l*(1.0-0.10068)); dSX[394]=(int)(kx+l*0.2211); dSY[394]=(int)(ky+l*(1.0-0.77825)); dSX[395]=(int)(kx+l*0.21163); dSY[395]=(int)(ky+l*(1.0-0.12681)); dSX[396]=(int)(kx+l*0.18089); dSY[396]=(int)(ky+l*(1.0-0.73562)); dSX[397]=(int)(kx+l*0.66299); dSY[397]=(int)(ky+l*(1.0-0.22315)); dSX[398]=(int)(kx+l*0.21315); dSY[398]=(int)(ky+l*(1.0-0.13477)); dSX[399]=(int)(kx+l*0.71559); dSY[399]=(int)(ky+l*(1.0-0.89859)); dSX[400]=(int)(kx+l*0.43222); dSY[400]=(int)(ky+l*(1.0-0.77103)); dSX[401]=(int)(kx+l*0.87618); dSY[401]=(int)(ky+l*(1.0-0.37878)); dSX[402]=(int)(kx+l*0.26296); dSY[402]=(int)(ky+l*(1.0-0.15748)); dSX[403]=(int)(kx+l*0.1686); dSY[403]=(int)(ky+l*(1.0-0.20094)); dSX[404]=(int)(kx+l*0.6815); dSY[404]=(int)(ky+l*(1.0-0.10764)); dSX[405]=(int)(kx+l*0.37811); dSY[405]=(int)(ky+l*(1.0-0.70541)); dSX[406]=(int)(kx+l*0.36094); dSY[406]=(int)(ky+l*(1.0-0.67579)); dSX[407]=(int)(kx+l*0.82511); dSY[407]=(int)(ky+l*(1.0-0.41853)); dSX[408]=(int)(kx+l*0.14138); dSY[408]=(int)(ky+l*(1.0-0.23299)); dSX[409]=(int)(kx+l*0.67249); dSY[409]=(int)(ky+l*(1.0-0.20002)); dSX[410]=(int)(kx+l*0.23894); dSY[410]=(int)(ky+l*(1.0-0.17142)); dSX[411]=(int)(kx+l*0.75744); dSY[411]=(int)(ky+l*(1.0-0.14058)); dSX[412]=(int)(kx+l*0.17161); dSY[412]=(int)(ky+l*(1.0-0.10035)); dSX[413]=(int)(kx+l*0.48828); dSY[413]=(int)(ky+l*(1.0-0.66026)); dSX[414]=(int)(kx+l*0.09221); dSY[414]=(int)(ky+l*(1.0-0.24637)); dSX[415]=(int)(kx+l*0.16063); dSY[415]=(int)(ky+l*(1.0-0.59428)); dSX[416]=(int)(kx+l*0.12893); dSY[416]=(int)(ky+l*(1.0-0.59674)); dSX[417]=(int)(kx+l*0.35694); dSY[417]=(int)(ky+l*(1.0-0.78796)); dSX[418]=(int)(kx+l*0.41546); dSY[418]=(int)(ky+l*(1.0-0.76092)); dSX[419]=(int)(kx+l*0.16968); dSY[419]=(int)(ky+l*(1.0-0.83991)); dSX[420]=(int)(kx+l*0.10334); dSY[420]=(int)(ky+l*(1.0-0.13985)); dSX[421]=(int)(kx+l*0.16873); dSY[421]=(int)(ky+l*(1.0-0.03174)); dSX[422]=(int)(kx+l*0.09976); dSY[422]=(int)(ky+l*(1.0-0.57833)); dSX[423]=(int)(kx+l*0.73443); dSY[423]=(int)(ky+l*(1.0-0.86841)); dSX[424]=(int)(kx+l*0.2138); dSY[424]=(int)(ky+l*(1.0-0.14457)); dSX[425]=(int)(kx+l*0.18475); dSY[425]=(int)(ky+l*(1.0-0.73202)); dSX[426]=(int)(kx+l*0.48298); dSY[426]=(int)(ky+l*(1.0-0.70441)); dSX[427]=(int)(kx+l*0.18751); dSY[427]=(int)(ky+l*(1.0-0.17179)); dSX[428]=(int)(kx+l*0.15242); dSY[428]=(int)(ky+l*(1.0-0.56863)); dSX[429]=(int)(kx+l*0.47199); dSY[429]=(int)(ky+l*(1.0-0.60514)); dSX[430]=(int)(kx+l*0.08912); dSY[430]=(int)(ky+l*(1.0-0.59353)); dSX[431]=(int)(kx+l*0.14872); dSY[431]=(int)(ky+l*(1.0-0.63872)); dSX[432]=(int)(kx+l*0.79864); dSY[432]=(int)(ky+l*(1.0-0.35493)); dSX[433]=(int)(kx+l*0.35112); dSY[433]=(int)(ky+l*(1.0-0.78383)); dSX[434]=(int)(kx+l*0.69891); dSY[434]=(int)(ky+l*(1.0-0.84894)); dSX[435]=(int)(kx+l*0.80731); dSY[435]=(int)(ky+l*(1.0-0.39325)); dSX[436]=(int)(kx+l*0.82968); dSY[436]=(int)(ky+l*(1.0-0.3552)); dSX[437]=(int)(kx+l*0.72571); dSY[437]=(int)(ky+l*(1.0-0.19687)); dSX[438]=(int)(kx+l*0.69843); dSY[438]=(int)(ky+l*(1.0-0.84846)); dSX[439]=(int)(kx+l*0.84693); dSY[439]=(int)(ky+l*(1.0-0.40964)); dSX[440]=(int)(kx+l*0.20669); dSY[440]=(int)(ky+l*(1.0-0.77071)); dSX[441]=(int)(kx+l*0.12141); dSY[441]=(int)(ky+l*(1.0-0.58855)); dSX[442]=(int)(kx+l*0.2279); dSY[442]=(int)(ky+l*(1.0-0.12276)); dSX[443]=(int)(kx+l*0.83297); dSY[443]=(int)(ky+l*(1.0-0.39735)); dSX[444]=(int)(kx+l*0.14542); dSY[444]=(int)(ky+l*(1.0-0.56013)); dSX[445]=(int)(kx+l*0.12433); dSY[445]=(int)(ky+l*(1.0-0.20911)); dSX[446]=(int)(kx+l*0.72573); dSY[446]=(int)(ky+l*(1.0-0.8408)); dSX[447]=(int)(kx+l*0.09379); dSY[447]=(int)(ky+l*(1.0-0.55713)); dSX[448]=(int)(kx+l*0.14829); dSY[448]=(int)(ky+l*(1.0-0.23154)); dSX[449]=(int)(kx+l*0.4523); dSY[449]=(int)(ky+l*(1.0-0.67249)); dSX[450]=(int)(kx+l*0.11726); dSY[450]=(int)(ky+l*(1.0-0.19693)); dSX[451]=(int)(kx+l*0.11815); dSY[451]=(int)(ky+l*(1.0-0.25814)); dSX[452]=(int)(kx+l*0.67506); dSY[452]=(int)(ky+l*(1.0-0.17122)); dSX[453]=(int)(kx+l*0.83483); dSY[453]=(int)(ky+l*(1.0-0.40775)); dSX[454]=(int)(kx+l*0.07239); dSY[454]=(int)(ky+l*(1.0-0.18731)); dSX[455]=(int)(kx+l*0.3272); dSY[455]=(int)(ky+l*(1.0-0.7225)); dSX[456]=(int)(kx+l*0.16136); dSY[456]=(int)(ky+l*(1.0-0.61121)); dSX[457]=(int)(kx+l*0.21065); dSY[457]=(int)(ky+l*(1.0-0.13483)); dSX[458]=(int)(kx+l*0.71889); dSY[458]=(int)(ky+l*(1.0-0.20099)); dSX[459]=(int)(kx+l*0.36902); dSY[459]=(int)(ky+l*(1.0-0.7864)); dSX[460]=(int)(kx+l*0.84165); dSY[460]=(int)(ky+l*(1.0-0.36644)); dSX[461]=(int)(kx+l*0.68612); dSY[461]=(int)(ky+l*(1.0-0.12094)); dSX[462]=(int)(kx+l*0.22926); dSY[462]=(int)(ky+l*(1.0-0.16182)); dSX[463]=(int)(kx+l*0.18717); dSY[463]=(int)(ky+l*(1.0-0.11579)); dSX[464]=(int)(kx+l*0.80286); dSY[464]=(int)(ky+l*(1.0-0.32103)); dSX[465]=(int)(kx+l*0.25034); dSY[465]=(int)(ky+l*(1.0-0.04969)); dSX[466]=(int)(kx+l*0.25102); dSY[466]=(int)(ky+l*(1.0-0.81178)); dSX[467]=(int)(kx+l*0.40104); dSY[467]=(int)(ky+l*(1.0-0.70706)); dSX[468]=(int)(kx+l*0.47589); dSY[468]=(int)(ky+l*(1.0-0.62965)); dSX[469]=(int)(kx+l*0.4878); dSY[469]=(int)(ky+l*(1.0-0.77431)); dSX[470]=(int)(kx+l*0.44168); dSY[470]=(int)(ky+l*(1.0-0.69073)); dSX[471]=(int)(kx+l*0.10281); dSY[471]=(int)(ky+l*(1.0-0.2403)); dSX[472]=(int)(kx+l*0.82296); dSY[472]=(int)(ky+l*(1.0-0.3797)); dSX[473]=(int)(kx+l*0.48731); dSY[473]=(int)(ky+l*(1.0-0.69098)); dSX[474]=(int)(kx+l*0.63263); dSY[474]=(int)(ky+l*(1.0-0.22947)); dSX[475]=(int)(kx+l*0.67528); dSY[475]=(int)(ky+l*(1.0-0.20604)); dSX[476]=(int)(kx+l*0.19472); dSY[476]=(int)(ky+l*(1.0-0.11076)); dSX[477]=(int)(kx+l*0.84223); dSY[477]=(int)(ky+l*(1.0-0.34943)); dSX[478]=(int)(kx+l*0.66502); dSY[478]=(int)(ky+l*(1.0-0.2078)); dSX[479]=(int)(kx+l*0.5253); dSY[479]=(int)(ky+l*(1.0-0.70939)); dSX[480]=(int)(kx+l*0.26947); dSY[480]=(int)(ky+l*(1.0-0.10746)); dSX[481]=(int)(kx+l*0.22708); dSY[481]=(int)(ky+l*(1.0-0.7998)); dSX[482]=(int)(kx+l*0.39279); dSY[482]=(int)(ky+l*(1.0-0.6481)); dSX[483]=(int)(kx+l*0.36533); dSY[483]=(int)(ky+l*(1.0-0.74434)); dSX[484]=(int)(kx+l*0.21924); dSY[484]=(int)(ky+l*(1.0-0.76278)); dSX[485]=(int)(kx+l*0.17686); dSY[485]=(int)(ky+l*(1.0-0.18335)); dSX[486]=(int)(kx+l*0.51587); dSY[486]=(int)(ky+l*(1.0-0.5951)); dSX[487]=(int)(kx+l*0.8566); dSY[487]=(int)(ky+l*(1.0-0.40405)); dSX[488]=(int)(kx+l*0.12652); dSY[488]=(int)(ky+l*(1.0-0.57607)); dSX[489]=(int)(kx+l*0.22685); dSY[489]=(int)(ky+l*(1.0-0.79786)); dSX[490]=(int)(kx+l*0.68578); dSY[490]=(int)(ky+l*(1.0-0.8548)); dSX[491]=(int)(kx+l*0.38968); dSY[491]=(int)(ky+l*(1.0-0.73713)); dSX[492]=(int)(kx+l*0.70811); dSY[492]=(int)(ky+l*(1.0-0.19062)); dSX[493]=(int)(kx+l*0.46795); dSY[493]=(int)(ky+l*(1.0-0.68742)); dSX[494]=(int)(kx+l*0.70386); dSY[494]=(int)(ky+l*(1.0-0.13081)); dSX[495]=(int)(kx+l*0.12347); dSY[495]=(int)(ky+l*(1.0-0.57067)); dSX[496]=(int)(kx+l*0.07499); dSY[496]=(int)(ky+l*(1.0-0.58753)); dSX[497]=(int)(kx+l*0.26596); dSY[497]=(int)(ky+l*(1.0-0.75632)); dSX[498]=(int)(kx+l*0.2196); dSY[498]=(int)(ky+l*(1.0-0.81844)); dSX[499]=(int)(kx+l*0.22364); dSY[499]=(int)(ky+l*(1.0-0.11876)); } // if (distrib != 7) {...} else } /** * Generate a signal for the given distribution. * The result goes into the global variables SignalX * and SignalY . * * @param distrib The specified distribution */ protected void getSignal(int distrib) { int wi = INIT_WIDTH; int hi = INIT_HEIGHT; int r1 = (int) wi/4; int l1 = (int) hi/4; int r2 = (int) wi/2; int l2 = (int) hi/2; int cx = (int) (wi/2); int cy = (int) (hi/2); int xA[] = new int[MAX_COMPLEX]; int yA[] = new int[MAX_COMPLEX]; int ringRadius; int w; int h; int z; double remainderX = 0; double remainderY = 0; float rdist; switch (distrib) { case 0: // Rectangle r1 = (int) wi/4; l1 = (int) hi/4; r2 = (int) wi/2; l2 = (int) hi/2; SignalX = (int) (r1 + (r2 * Math.random())); SignalY = (int) (l1 + (l2 * Math.random())); break; case 1: // Circle l2 = (int) ((cx < cy) ? cx : cy); // Diameter r1 = (int) wi/2 -l2/2; l1 = (int) hi/2 -l2/2; do { SignalX = (int) (r1 + (l2 * Math.random())); SignalY = (int) (l1 + (l2 * Math.random())); rdist = (float) Math.sqrt(((cx - SignalX) * (cx - SignalX) + (cy - SignalY) * (cy - SignalY))); } while ( rdist > l2/2 ); break; case 2: // Ring l2 = (int) ((cx < cy) ? cx : cy); // Diameter r1 = (int) cx - l2; l1 = (int) cy - l2; ringRadius = (int) (l2 * RING_FACTOR); do { SignalX = (int) (r1 + (2*l2 * Math.random())); SignalY = (int) (l1 + (2*l2 * Math.random())); rdist = (float) Math.sqrt(((cx - SignalX) * (cx - SignalX) + (cy - SignalY) * (cy - SignalY))); } while ( (rdist > l2) || (rdist < (l2 - ringRadius)) ); break; case 3: // Complex (1) w = (int) wi/9; h = (int) hi/5; xA[0] = w; yA[0] = h; xA[1] = w; yA[1] = 2*h; xA[2] = w; yA[2] = 3*h; xA[3] = 2*w; yA[3] = 3*h; xA[4] = 3*w; yA[4] = 3*h; xA[5] = 3*w; yA[5] = 2*h; xA[6] = 3*w; yA[6] = h; xA[7] = 4*w; yA[7] = h; xA[8] = 5*w; yA[8] = h; xA[9] = 5*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 3*h; xA[11] = 7*w; yA[11] = h; xA[12] = 7*w; yA[12] = 2*h; xA[13] = 7*w; yA[13] = 3*h; z = (int) (14 * Math.random()); SignalX = (int) (xA[z] + (w * Math.random())); SignalY = (int) (yA[z] + (h * Math.random())); break; case 4: // Complex (2) w = (int) wi/9; h = (int) hi/7; xA[0] = w; yA[0] = 5*h; xA[1] = w; yA[1] = 4*h; xA[2] = w; yA[2] = 3*h; xA[3] = w; yA[3] = 2*h; xA[4] = 1*w; yA[4] = h; xA[5] = 2*w; yA[5] = h; xA[6] = 3*w; yA[6] = h; xA[7] = 4*w; yA[7] = h; xA[8] = 5*w; yA[8] = 1*h; xA[9] = 5*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 3*h; xA[11] = 3*w; yA[11] = 3*h; xA[12] = 3*w; yA[12] = 4*h; xA[13] = 3*w; yA[13] = 5*h; xA[14] = 4*w; yA[14] = 5*h; xA[15] = 5*w; yA[15] = 5*h; xA[16] = 6*w; yA[16] = 5*h; xA[17] = 7*w; yA[17] = 5*h; xA[18] = 7*w; yA[18] = 4*h; xA[19] = 7*w; yA[19] = 3*h; xA[20] = 7*w; yA[20] = 2*h; xA[21] = 7*w; yA[21] = 1*h; z = (int) (22 * Math.random()); SignalX = (int) (xA[z] + (w * Math.random())); SignalY = (int) (yA[z] + (h * Math.random())); break; case 5: // Complex (3) w = (int) wi/13; h = (int) hi/11; xA[0] = w; yA[0] = h; xA[1] = w; yA[1] = 2*h; xA[2] = w; yA[2] = 3*h; xA[3] = w; yA[3] = 4*h; xA[4] = 1*w; yA[4] = 5*h; xA[5] = 1*w; yA[5] = 6*h; xA[6] = 1*w; yA[6] = 7*h; xA[7] = 1*w; yA[7] = 8*h; xA[8] = 1*w; yA[8] = 9*h; xA[9] = 2*w; yA[9] = 1*h; xA[10] = 3*w; yA[10] = 1*h; xA[11] = 4*w; yA[11] = 1*h; xA[12] = 5*w; yA[12] = 1*h; xA[13] = 6*w; yA[13] = 1*h; xA[14] = 7*w; yA[14] = 1*h; xA[15] = 8*w; yA[15] = 1*h; xA[16] = 9*w; yA[16] = 1*h; xA[17] = 9*w; yA[17] = 2*h; xA[18] = 9*w; yA[18] = 3*h; xA[19] = 9*w; yA[19] = 4*h; xA[20] = 9*w; yA[20] = 5*h; xA[21] = 9*w; yA[21] = 6*h; xA[22] = 9*w; yA[22] = 7*h; xA[23] = 8*w; yA[23] = 7*h; xA[24] = 7*w; yA[24] = 7*h; xA[25] = 6*w; yA[25] = 7*h; xA[26] = 5*w; yA[26] = 7*h; xA[27] = 5*w; yA[27] = 6*h; xA[28] = 5*w; yA[28] = 5*h; xA[29] = 3*w; yA[29] = 3*h; xA[30] = 3*w; yA[30] = 4*h; xA[31] = 3*w; yA[31] = 5*h; xA[32] = 3*w; yA[32] = 6*h; xA[33] = 3*w; yA[33] = 7*h; xA[34] = 3*w; yA[34] = 8*h; xA[35] = 3*w; yA[35] = 9*h; xA[36] = 4*w; yA[36] = 3*h; xA[37] = 5*w; yA[37] = 3*h; xA[38] = 6*w; yA[38] = 3*h; xA[39] = 7*w; yA[39] = 3*h; xA[40] = 7*w; yA[40] = 4*h; xA[41] = 7*w; yA[41] = 5*h; xA[42] = 4*w; yA[42] = 9*h; xA[43] = 5*w; yA[43] = 9*h; xA[44] = 6*w; yA[44] = 9*h; xA[45] = 7*w; yA[45] = 9*h; xA[46] = 8*w; yA[46] = 9*h; xA[47] = 9*w; yA[47] = 9*h; xA[48] =10*w; yA[48] = 9*h; xA[49] =11*w; yA[49] = 9*h; xA[50] =11*w; yA[50] = 8*h; xA[51] =11*w; yA[51] = 7*h; xA[52] =11*w; yA[52] = 6*h; xA[53] =11*w; yA[53] = 5*h; xA[54] =11*w; yA[54] = 4*h; xA[55] =11*w; yA[55] = 3*h; xA[56] =11*w; yA[56] = 2*h; xA[57] =11*w; yA[57] = 1*h; z = (int) (58 * Math.random()); SignalX = (int) (xA[z] + (w * Math.random())); SignalY = (int) (yA[z] + (h * Math.random())); break; case 6: // HiLo-Density w = (int) wi/10; h = (int) hi/10; xA[0] = 2 * w; yA[0] = 4 * h; xA[1] = 5 * w; yA[1] = 1 * h; z = (int) (2 * Math.random()); if (z == 0) { SignalX = (int) (xA[z] + (w * Math.random())); SignalY = (int) (yA[z] + (h * Math.random())); } else { SignalX = (int) (xA[z] + (4 * w * Math.random())); SignalY = (int) (yA[z] + (8 * h * Math.random())); } break; case 7: // Discrete z = (int) (MAX_DISCRETE_SIGNALS * Math.random()); SignalX = discreteSignalsX[z]; SignalY = discreteSignalsY[z]; break; case 8: // Complex (4) w = (int) wi/17; h = (int) hi/8; xA[0] = w; yA[0] = 2*h; xA[1] = w; yA[1] = 3*h; xA[2] = w; yA[2] = 4*h; xA[3] = w; yA[3] = 5*h; xA[4] = 2*w; yA[4] = 5*h; xA[5] = 3*w; yA[5] = 5*h; xA[6] = 3*w; yA[6] = 4*h; xA[7] = 3*w; yA[7] = 3*h; xA[8] = 3*w; yA[8] = 2*h; xA[9] = 4*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 2*h; xA[11] = 6*w; yA[11] = 2*h; xA[12] = 7*w; yA[12] = 2*h; xA[13] = 7*w; yA[13] = 3*h; xA[14] = 7*w; yA[14] = 4*h; xA[15] = 7*w; yA[15] = 5*h; xA[16] = 8*w; yA[16] = 5*h; xA[17] = 9*w; yA[17] = 5*h; xA[18] = 10*w; yA[18] = 5*h; xA[19] = 11*w; yA[19] = 5*h; xA[20] = 11*w; yA[20] = 4*h; xA[21] = 11*w; yA[21] = 3*h; xA[22] = 11*w; yA[22] = 2*h; xA[23] = 14*w; yA[23] = 2*h; xA[24] = 15*w; yA[24] = 2*h; xA[25] = 15*w; yA[25] = 3*h; xA[26] = 15*w; yA[26] = 4*h; xA[27] = 15*w; yA[27] = 5*h; z = (int) (28 * Math.random()); SignalX = (int) (xA[z] + (w * Math.random())); SignalY = (int) (yA[z] + (h * Math.random())); break; case 9: // Moving and Jumping Rectangle r2 = (int) wi/4; l2 = (int) hi/4; r1 = (int) (0.75 * (wi/2 + Math.IEEEremainder((double)0.2 * numRun,(double)(wi)))); l1 = (int) (0.75 * (hi/2 + Math.IEEEremainder((double)0.2 * numRun,(double)(hi)))); if (DEBUG) System.out.println("signal x = " + r1); SignalX = (int) (r1 + (r2 * Math.random())); SignalY = (int) (l1 + (l2 * Math.random())); break; case 10: // Moving Rectangle r2 = (int) wi/4; l2 = (int) hi/4; remainderX = Math.IEEEremainder((double)0.2 * numRun,(double)(wi)); remainderY = Math.IEEEremainder((double)0.2 * numRun,(double)(hi)); if ( (bounceX_old > 0) && (remainderX < 0) ) bounceX = bounceX * (-1); if ( (bounceY_old > 0) && (remainderY < 0) ) bounceY = bounceY * (-1); r1 = (int) (0.75 * (wi/2 + bounceX * remainderX)); l1 = (int) (0.75 * (hi/2 + bounceY * remainderY)); bounceX_old = remainderX; bounceY_old = remainderY; if (DEBUG) System.out.println("signal x = " + r1); SignalX = (int) (r1 + (r2 * Math.random())); SignalY = (int) (l1 + (l2 * Math.random())); break; case 11: // Jumping Rectangle r2 = (int) wi/4; l2 = (int) hi/4; if (Math.ceil(Math.IEEEremainder((double)numRun, 1000.0)) == 0) { jumpX = (int) ((wi - r2) * Math.random()); jumpY = (int) ((hi - l2) * Math.random()); } SignalX = (int) (jumpX + (r2 * Math.random())); SignalY = (int) (jumpY + (l2 * Math.random())); break; case 12: // R.Mouse Rectangle r2 = (int) wi/4; l2 = (int) hi/4; SignalX = (int) (jumpX + (r2 * Math.random())); SignalY = (int) (jumpY + (l2 * Math.random())); break; } } /** * Do resize calculations, start the learning method. * */ public void run() { int i; while (true) { // Relativate Positions Dimension d = size(); if ( (d.width != INIT_WIDTH) || (d.height != INIT_HEIGHT) ) { NodeGNG n; for (i = 0 ; i < nnodes ; i++) { n = nodes[i]; n.x = n.x * d.width / INIT_WIDTH; n.y = n.y * d.height / INIT_HEIGHT; } INIT_WIDTH = d.width; INIT_HEIGHT = d.height; initDiscreteSignals(distribution); if ( ( nnodes == 0) && (algo == 5) ) { // Gernerate some nodes int z = (int) (numDiscreteSignals * Math.random()); int mod = 0; for (i = 0; i < maxNodes; i++) { mod = (z+i)%numDiscreteSignals; addNode(discreteSignalsX[mod], discreteSignalsY[mod]); } } } // Calculate the new positions if (!stopB) { learn(); nodesMovedB = true; } if ((delaunayB || voronoiB) && nodesMovedB) { nlines = 0; nodesMovedB = computeVoronoi(); } if (errorGraphB && !stopB) errorGraph.graph.add(valueGraph); repaint(); if (teachB) { try { Thread.sleep(2000); } catch (InterruptedException e) { break; } } else { try { Thread.sleep(speed); } catch (InterruptedException e) { break; } } } } /** * Do the learning. An input signal is generated for the given distribution * and forwarded to the switched algorithm. * Available Algorithms (abbrev, case): * Growing Neural Gas (GNG, 0), * Hard Competitive Learning (HCL, 1), * Neural Gas (NG, 2), * Neural Gas with Competitive Hebbian Learning (NGwCHL, 3) and * Competitive Hebbian Learning (CHL, 4). * LBG (LBG, 5: but not in the switch). * Growing Grid (GG, 6). * Self-Organizing Map (SOM, 7). * */ protected synchronized void learn() { Dimension d = size(); int nr1, nr2; int i, j, k, l, m; int x, y; int numError, lowUtilityNode; int numNb; int toDelete; float dx, dy; float dstSgmExp; float bestDist, nextBestDist; float h_l = 0.0f; float l_t = 0.0f; float worstError, lowUtility; NodeGNG pick, pick2, n, node; SignalX = (int) d.width/2; SignalY = (int) d.height/2; valueGraph = 0.0f; for (k = 0; k < stepSize; k++) { numRun++; if (algo != 5) { pick = nodes[0]; pick2 = nodes[0]; nr1 = 0; nr2 = 0; numError = 0; lowUtilityNode = 0; worstError = 0.0f; lowUtility = Float.MAX_VALUE; bestDist = Float.MAX_VALUE; nextBestDist = Float.MAX_VALUE; toDelete = -1; // Get a random point out of the selected distribution getSignal(distribution); // Save the signals lastSignalsX[k] = SignalX; lastSignalsY[k] = SignalY; // Locate the nearest node and prepare for the second for (i = 0 ; i < nnodes ; i++) { n = nodes[i]; n.winner = n.second = n.moved = false; if ( ((numRun % NUM_NEW_NODE) == 0) && (!noNodesB) && (algo == 0) ) n.inserted = false; // Mark Node without neighbors (one each run is enough) if (n.numNeighbors() == 0) toDelete = i; // Calculate distance n.dist = (n.x - SignalX) * (n.x - SignalX) + (n.y - SignalY) * (n.y - SignalY); // Reduce error and utility n.error *= forgetFactor; n.utility *= forgetFactorUtility; // Calculate node with best distance if (n.dist < bestDist) { pick2 = pick; nr2 = nr1; pick = n; nr1 = i; nextBestDist = bestDist; bestDist = n.dist; } // Calculate node with worst Error if (n.error > worstError) { worstError = n.error; numError = i; } // Calculate most useless node (GNG-U) if (n.utility < lowUtility) { lowUtility = n.utility; lowUtilityNode = i; } } valueGraph += bestDist; // Mark winner for teach-mode pick.winner = true; pick.x_old = pick.x; pick.y_old = pick.y; switch (algo) { // // Growing Neural Gas // case 0: // Find second node (continued) if (nr1 == nr2) { nr2++; nextBestDist = Float.MAX_VALUE; pick2 = nodes[nr2]; } for (i = nr1 + 1 ; i < nnodes ; i++) { n = nodes[i]; if (n.dist < nextBestDist) { pick2 = n; nr2 = i; nextBestDist = n.dist; } } // Mark second for teach-mode pick2.second = true; pick2.x_old = pick2.x; pick2.y_old = pick2.y; // Adapt picked nodes // Winner: pick.x += epsilonGNG * (SignalX - pick.x); pick.y += epsilonGNG * (SignalY - pick.y); numNb = pick.numNeighbors(); // Neighbors: int nn; for (i = 0; i < numNb; i++) { nn = pick.neighbor(i); nodes[nn].moved = true; pick2.x_old = pick2.x; pick2.y_old = pick2.y; nodes[nn].x += epsilonGNG2 * (SignalX - nodes[nn].x); nodes[nn].y += epsilonGNG2 * (SignalY - nodes[nn].y); } // Calculate error pick.error += bestDist; // Calculate utility for GNG-U pick.utility += (nextBestDist - bestDist); // Connect the nodes addEdge(nr1, nr2); // Calculate the age of the connected edges and delete too old edges ageEdge(nr1); // Prove inserting node and insert if neccessary if ( (numRun % NUM_NEW_NODE) == 0 ) if (!noNodesB) insertedSoundB = ( -1 != insertNode(numError, worstErrorNeighbor(numError)) ); // Delete Node without Neighbors (not GNG-U) if ((toDelete != -1) && (nnodes > 2) && !utilityGNGB ) deleteNode(toDelete); // Delete Node with very low utility else { if ( worstError > lowUtility * utilityGNG) { if (utilityGNGB && (nnodes > 2)) { deleteNode(lowUtilityNode); } if (DEBUG) System.out.println("Utlity-delete (" + lowUtilityNode + "): " + worstError + " > " + lowUtility + " * " + utilityGNG); //utilityNode = lowUtility; } else if ( utilityGNGB && (nnodes > 2) && (nnodes > maxNodes) ) { deleteNode(lowUtilityNode); } } break; // // Hard Competitive Learning // case 1: if ((numRun >= t_max) && variableB) break; // Adapt picked node if (variableB) { e_t = (float)(e_i * Math.pow(e_f/e_i, numRun/t_max)); pick.x += e_t * (SignalX - pick.x); pick.y += e_t * (SignalY - pick.y); } else { pick.x += epsilon * (SignalX - pick.x); pick.y += epsilon * (SignalY - pick.y); } break; // // Neural Gas // case 2: if (numRun >= t_max) break; // Initialize the sorted node array, if necessary if (nNodesChangedB) { for (i = 1; i <= nnodes; i++) snodes[i] = i-1; nNodesChangedB = false; } l_t = (float)(l_i * Math.pow(l_f/l_i, numRun/t_max)); e_t = (float)(e_i * Math.pow(e_f/e_i, numRun/t_max)); // Build a minimum heap for (i = nnodes/2; i > 0; i--) reheap_min(i, nnodes); { int decrNnodes = nnodes - 1; int minimum; // Fetch minimum, calculate new position and reheap for (i = nnodes; i > 0; i--) { minimum = snodes[1]; snodes[1] = snodes[i]; snodes[i] = minimum; n = nodes[minimum]; // Mark second for teach-mode if (i == decrNnodes) nodes[minimum].second = true; h_l = (float)(Math.exp(-(nnodes - i)/l_t)); // Adapt nodes dx = e_t * h_l * (SignalX - n.x); dy = e_t * h_l * (SignalY - n.y); n.x += dx; n.y += dy; if ( (Math.abs(dx) < 1.0) && (Math.abs(dy) < 1.0) && (i < decrNnodes) ) break; reheap_min(1, i-1); } } break; // // Neural Gas with Competitive Hebbian Learning // case 3: if (numRun >= t_max) break; // Initialize the sorted node array, if necessary if (nNodesChangedB) { for (i = 1; i <= nnodes; i++) snodes[i] = i-1; nNodesChangedB = false; } l_t = (float)(l_i * Math.pow(l_f/l_i, numRun/t_max)); e_t = (float)(e_i * Math.pow(e_f/e_i, numRun/t_max)); // Calculate the new edge-deleting term MAX_EDGE_AGE = (int) (delEdge_i * Math.pow(delEdge_f/delEdge_i, numRun/t_max)); // Build a minimum heap for (i = nnodes/2; i > 0; i--) reheap_min(i, nnodes); { int decrNnodes = nnodes - 1; int minimum; // Fetch minimum, calculate new position and reheap for (i = nnodes; i > 0; i--) { minimum = snodes[1]; snodes[1] = snodes[i]; snodes[i] = minimum; n = nodes[minimum]; // Mark second for teach-mode if (i == decrNnodes) { nodes[minimum].second = true; // This is the only difference between NG and NGwCHL: // - Connect the first and the second node addEdge(snodes[nnodes],snodes[nnodes - 1]); // - Calculate the age of the connected edges and // delete too old edges ageEdge(snodes[nnodes]); } h_l = (float)(Math.exp(-(nnodes - i)/l_t)); // Adapt nodes dx = e_t * h_l * (SignalX - n.x); dy = e_t * h_l * (SignalY - n.y); n.x += dx; n.y += dy; if ( (Math.abs(dx) < 1.0) && (Math.abs(dy) < 1.0) && (i < decrNnodes) ) break; reheap_min(1, i-1); } } break; // // Competitive Hebbian Learning // case 4: // Find second node (continued) if (nr1 == nr2) { nr2++; nextBestDist = Float.MAX_VALUE; pick2 = nodes[nr2]; } for (i = nr1 + 1 ; i < nnodes ; i++) { n = nodes[i]; if (n.dist < nextBestDist) { pick2 = n; nr2 = i; nextBestDist = n.dist; } } // Mark second for teach-mode pick2.second = true; pick2.x_old = pick2.x; pick2.y_old = pick2.y; // Connect the nodes addEdge(nr1, nr2); break; // // Growing Grid // case 6: // Adapt nodes x = pick.x_grid; y = pick.y_grid; grid[x][y].tau++; if (fineTuningB) { int percent; float tmax = grid_x * grid_y * l_f; e_t = (float)(e_i * Math.pow(e_f/e_i, (numRun-numRunTmp)/tmax)); percent = (int) (((numRun-numRunTmp)*100)/tmax); if (percent > 100) { fineTuningS = "Fine-tuning (100%)"; break; } fineTuningS = "Fine-tuning (" + String.valueOf(percent) + "%)"; } else e_t = e_i; int dst; for (i = 0; i < grid_x; i++) { for (j = 0; j < grid_y; j++) { // Distance dst = Math.abs(x - i) + Math.abs(y - j); node = grid[i][j].node; dstSgmExp = (float) (Math.exp(-(dst*dst)/(2.0 * sigma * sigma))); node.x += e_t * dstSgmExp * (SignalX - node.x); node.y += e_t * dstSgmExp * (SignalY - node.y); if ( dstSgmExp > 0.5f ) node.second = true; } } // Prove inserting nodes and insert if neccessary if ( (numRun % (grid_x * grid_y * l_i) == 0) && (!fineTuningB) ) { if (!noNodesB) { insertedSoundB = ( -1 != insertGrid() ); fineTuningB = !insertedSoundB; numRunTmp = numRun; } } break; // // Self-Organizing Map // case 7: if (numRun >= t_max) break; // Adapt nodes x = pick.x_grid; y = pick.y_grid; e_t = (float)(e_i * Math.pow(e_f/e_i, numRun/t_max)); sigma = (float)(sigma_i * Math.pow(sigma_f, numRun/t_max)); for (i = 0; i < grid_x; i++) { for (j = 0; j < grid_y; j++) { // Distance dst = Math.abs(x - i) + Math.abs(y - j); node = grid[i][j].node; dstSgmExp = (float) (Math.exp(-(dst*dst)/(2.0 * sigma * sigma))); node.x += e_t * dstSgmExp * (SignalX - node.x); node.y += e_t * dstSgmExp * (SignalY - node.y); if ( dstSgmExp > 0.5f ) node.second = true; } } break; } // switch } // if (algo != 5) else { // // LBG // readyLBG_B = true; int sumSignal, sig; int wa = 0, wb = 0; int pickNo = 0, pick2No = 0; NodeGNG pickLBG, pick2LBG; float bestDistLBG, nextBestDistLBG; float utility = 0.0f; float minUtility = Float.MAX_VALUE; float error = 0.0f; float maxError = 0.0f; float errorAct = 0.0f; FPoint tmpFP; for (j = 0; j < numDiscreteSignals; j++) { pickLBG = nodes[0]; pick2LBG = nodes[0]; bestDistLBG = Float.MAX_VALUE; nextBestDistLBG = Float.MAX_VALUE; // Locate the nearest node and prepare for the second for (i = 0; i < nnodes; i++) { n = nodes[i]; // Calculate distance n.dist = (n.x - discreteSignalsX[j]) * (n.x - discreteSignalsX[j]) + (n.y - discreteSignalsY[j]) * (n.y - discreteSignalsY[j]); // Calculate node with best distance and prepare for second if (n.dist < bestDistLBG) { pick2No = pickNo; pick2LBG = pickLBG; pickNo = i; pickLBG = n; nextBestDistLBG = bestDistLBG; bestDistLBG = n.dist; } } // Store distance to the nearest codebook vector (LBG-U) discreteSignalsD1[j] = bestDistLBG; errorAct += bestDistLBG; // Add signal index to the winning codebook vector pickLBG.addSignal(j); // Find node with second best distance if (pickLBG == pick2LBG) nextBestDistLBG = Float.MAX_VALUE; for (i = pickNo + 1; i < nnodes; i++) { n = nodes[i]; if (n.dist < nextBestDistLBG) { pick2No = i; pick2LBG = n; nextBestDistLBG = n.dist; } } // Store distance to the second nearest codebook vector (LBG-U) discreteSignalsD2[j] = nextBestDistLBG; valueGraph += bestDistLBG; } minUtility = Float.MAX_VALUE; maxError = 0.0f; // Adapt selected nodes for (l = 0; l < nnodes; l++) { n = nodes[l]; tmpFP = new FPoint(n.x, n.y); utility = 0.0f; error = 0.0f; sumSignal = n.numSignals(); if (sumSignal > 0) { for (m = 0; m < sumSignal; m++) { sig = n.removeSignal(); n.x += discreteSignalsX[sig]; n.y += discreteSignalsY[sig]; // calculate utility utility += (discreteSignalsD2[sig] - discreteSignalsD1[sig]); // calculate error error += discreteSignalsD1[sig]; } n.x /= (sumSignal + 1.0f); n.y /= (sumSignal + 1.0f); // nodes moved? if (!tmpFP.equal(n.x, n.y)) { n.moved = true; readyLBG_B = false; } else { n.moved = false; } // determine minimum utility if (utility < minUtility) { wa = l; minUtility = utility; } // determine maximum error if (error > maxError) { wb = l; maxError = error; } } } if (LBG_U_B) { if (DEBUG) System.out.println("Act: " + errorAct + ", Best: " + errorBestLBG_U); if (readyLBG_B && (errorAct < errorBestLBG_U) ) { // Save old positions for (i = 0; i < nnodes; i++) { Cbest[i] = new FPoint(nodes[i].x, nodes[i].y); } readyLBG_B = false; errorBestLBG_U = errorAct; // move node from wa to wb with a small offset (LBG-U) // IMPORTANT: This works only for image data space!!! nodes[wa].x = nodes[wb].x; nodes[wa].y = nodes[wb].y + 1; } else if (readyLBG_B && (errorAct > errorBestLBG_U) ) { // Restore old positions for (i = 0; i < nnodes; i++) { nodes[i].x = Cbest[i].x; nodes[i].y = Cbest[i].y; } } } } // if (algo != 5) } // for if (errorGraphB) { // Calculate mean square error if (algo == 5) valueGraph = (float) valueGraph / numDiscreteSignals; valueGraph = (float) Math.sqrt( valueGraph / ((float) stepSize) ); } } // Vars for Voronoi diagram (start). int xmin, ymin, xmax, ymax; int deltax, deltay; int siteidx, nsites, sqrt_nsites; int nvertices, nvedges; int PQcount, PQmin; SiteVoronoi bottomsite; final int LE = 0; final int RE = 1; final int DELETED = -2; ListGNG list, pq; boolean debug = true; HalfEdgeVoronoi ELleftend, ELrightend; float pxmin, pymin, pxmax, pymax; // Vars for Voronoi diagram (end). /** * Compute Voronoi diagram. * A sweepline algorithm is implemented (Steven Fortune, 1987). * It computes the Voronoi diagram/Delaunay triangulation of n sites * in time O(n log n) and space usage O(n). * Input: nodes[], Output: lines[] (global). * */ public boolean computeVoronoi() { Dimension d = size(); int i; xmin = 0; ymin = 0; xmax = deltax = d.width; ymax = deltay = d.height; siteidx = 0; nsites = nnodes; nvertices = 0; nvedges = 0; sqrt_nsites = (int) Math.sqrt(nsites + 4); PQcount = 0; PQmin = 0; // Copy nodes[] to vsites[] and sort them sortSites(nnodes); if ( (nnodes == 0) || ( (nnodes != maxNodes) && (algo != 0) && (algo != 6) ) ) return true; xmin = (int) vsites[1].coord.x; xmax = (int) vsites[1].coord.x; for(i = 2; i <= nsites; i++) { if (vsites[i].coord.x < xmin) xmin = (int) vsites[i].coord.x; if (vsites[i].coord.x > xmax) xmax = (int) vsites[i].coord.x; } ymin = (int) vsites[1].coord.y; ymax = (int) vsites[nsites].coord.y; voronoi(); return false; } /** * Compute Voronoi diagram (2). * A sweepline algorithm is implemented (Steven Fortune, 1987). * Input: nodes[], Output: lines[] (global). * * @see ComputeGNG#computeVoronoi */ public void voronoi() { SiteVoronoi newsite, bot, top, temp, p, v; FPoint newintstar = new FPoint(); int pm; HalfEdgeVoronoi lbnd, rbnd, llbnd, rrbnd, bisector; EdgeVoronoi e; pq = new ListGNG(); bottomsite = nextsite(); ELinitialize(); newsite = nextsite(); while(true) { if (!pq.empty()) newintstar = pq.PQ_min(); if ( (newsite != null) && ( pq.empty() || (newsite.coord.y < newintstar.y) || ( (newsite.coord.y == newintstar.y) && (newsite.coord.x < newintstar.x) ) ) ) { lbnd = ELleftbnd(newsite.coord); rbnd = lbnd.ELright; bot = rightreg(lbnd); e = bisect(bot, newsite); bisector = new HalfEdgeVoronoi(e, LE); ELinsert(lbnd, bisector); if ( (p = intersect(lbnd, bisector)) != null ) { PQdelete(lbnd); PQinsert(lbnd, p, dist(p,newsite)); } lbnd = bisector; bisector = new HalfEdgeVoronoi(e, RE); ELinsert(lbnd, bisector); if ( (p = intersect(bisector, rbnd)) != null ) { PQinsert(bisector, p, dist(p,newsite)); } newsite = nextsite(); } else if ( !pq.empty() ) { // intersection is smallest PQcount--; lbnd = pq.PQextractmin(); llbnd = lbnd.ELleft; rbnd = lbnd.ELright; rrbnd = rbnd.ELright; bot = leftreg(lbnd); top = rightreg(rbnd); v = lbnd.vertex; makevertex(v); endpoint(lbnd.ELedge, lbnd.ELpm, v); endpoint(rbnd.ELedge, rbnd.ELpm, v); ELdelete(lbnd); PQdelete(rbnd); ELdelete(rbnd); pm = LE; if (bot.coord.y > top.coord.y) { temp = bot; bot = top; top = temp; pm = RE; } e = bisect(bot, top); bisector = new HalfEdgeVoronoi(e, pm); ELinsert(llbnd, bisector); endpoint(e, RE-pm, v); deref(v); if ( (p = intersect(llbnd, bisector)) != null ) { PQdelete(llbnd); PQinsert(llbnd, p, dist(p, bot) ); } if ( (p = intersect(bisector, rrbnd)) != null ) PQinsert(bisector, p, dist(p, bot) ); } else break; } if (voronoiB) { for(lbnd = ELleftend.ELright; lbnd != ELrightend; lbnd = lbnd.ELright) { e = lbnd.ELedge; out_ep(e); } } } public void out_bisector(EdgeVoronoi e) { line(e.reg[0].coord.x, e.reg[0].coord.y, e.reg[1].coord.x, e.reg[1].coord.y, false); } public void out_ep(EdgeVoronoi e) { SiteVoronoi s1, s2; float x1, x2, y1, y2; float dx,dy,d; Dimension dim = size(); pxmin = 0.0f; pymin = 0.0f; pxmax = dim.width; pymax = dim.height; if ( (e.a == 1.0f) && (e.b >= 0.0f) ) { s1 = e.ep[1]; s2 = e.ep[0]; } else { s1 = e.ep[0]; s2 = e.ep[1]; } if (e.a == 1.0) { y1 = pymin; if ( (s1 != null) && (s1.coord.y > pymin) ) y1 = s1.coord.y; if (y1 > pymax) return; x1 = e.c - e.b * y1; y2 = pymax; if ( (s2 != null) && (s2.coord.y < pymax) ) y2 = s2.coord.y; if (y2 < pymin) return; x2 = e.c - e.b * y2; if ( (x1 > pxmax & x2 > pxmax) | (x1 < pxmin & x2 < pxmin) ) return; if (x1 > pxmax) { x1 = pxmax; y1 = (e.c - x1)/e.b; } if (x1 < pxmin) { x1 = pxmin; y1 = (e.c - x1)/e.b; } if (x2 > pxmax) { x2 = pxmax; y2 = (e.c - x2)/e.b; } if (x2 < pxmin) { x2 = pxmin; y2 = (e.c - x2)/e.b; } } else { x1 = pxmin; if ( (s1 != null) && (s1.coord.x > pxmin) ) x1 = s1.coord.x; if (x1 > pxmax) return; y1 = e.c - e.a * x1; x2 = pxmax; if ( (s2 != null) && (s2.coord.x < pxmax) ) x2 = s2.coord.x; if (x2 < pxmin) return; y2 = e.c - e.a * x2; if ( (y1 > pymax & y2 > pymax) | ( y1 < pymin & y2 < pymin) ) return; if (y1 > pymax) { y1 = pymax; x1 = (e.c - y1)/e.a; } if(y1 < pymin) { y1 = pymin; x1 = (e.c - y1)/e.a; } if(y2 > pymax) { y2 = pymax; x2 = (e.c - y2)/e.a; } if(y2 < pymin) { y2 = pymin; x2 = (e.c - y2)/e.a; } } line(x1, y1, x2, y2, true); } public void line(float x1, float y1, float x2, float y2, boolean vdB) { LineGNG l = new LineGNG((int) x1, (int) y1, (int) x2, (int) y2); lines[nlines] = l; vd[nlines] = vdB; nlines++; } public void PQinsert(HalfEdgeVoronoi he, SiteVoronoi v, float offset) { HalfEdgeVoronoi last, next; he.vertex = v; v.refcnt++; he.ystar = v.coord.y + offset; pq.PQinsert(he); PQcount++; } public void PQdelete(HalfEdgeVoronoi he) { HalfEdgeVoronoi last; if(he.vertex != null) { pq.PQdelete(he); PQcount--; deref(he.vertex); he.vertex = null; } } public float dist(SiteVoronoi s, SiteVoronoi t) { float dx,dy; dx = s.coord.x - t.coord.x; dy = s.coord.y - t.coord.y; return( (float) Math.sqrt(dx*dx + dy*dy) ); } public SiteVoronoi intersect(HalfEdgeVoronoi el1, HalfEdgeVoronoi el2) { EdgeVoronoi e1, e2, e; HalfEdgeVoronoi el; float d, xint, yint; boolean right_of_site; SiteVoronoi v; e1 = el1.ELedge; e2 = el2.ELedge; if ( (e1 == null) || (e2 == null) ) return(null); if (e1.reg[1] == e2.reg[1]) return(null); d = e1.a * e2.b - e1.b * e2.a; if ( (-1.0e-10 < d) && (d < 1.0e-10) ) return(null); xint = (e1.c * e2.b - e2.c * e1.b)/d; yint = (e2.c * e1.a - e1.c * e2.a)/d; if ( (e1.reg[1].coord.y < e2.reg[1].coord.y) || ( (e1.reg[1].coord.y == e2.reg[1].coord.y) && (e1.reg[1].coord.x < e2.reg[1].coord.x) ) ) { el = el1; e = e1; } else { el = el2; e = e2; } right_of_site = (xint >= e.reg[1].coord.x); if ( (right_of_site && el.ELpm == LE) || (!right_of_site && el.ELpm == RE) ) return(null); v = new SiteVoronoi(); v.refcnt = 0; v.coord.x = xint; v.coord.y = yint; return(v); } public void ELinsert(HalfEdgeVoronoi lb, HalfEdgeVoronoi henew) { henew.ELleft = lb; henew.ELright = lb.ELright; (lb.ELright).ELleft = henew; lb.ELright = henew; } public void deref(SiteVoronoi v) { v.refcnt--; if (v.refcnt == 0 ) v = null; } public EdgeVoronoi bisect(SiteVoronoi s1, SiteVoronoi s2) { float dx, dy, adx, ady; EdgeVoronoi newedge; newedge = new EdgeVoronoi(); newedge.reg[0] = s1; newedge.reg[1] = s2; s1.refcnt++; s2.refcnt++; newedge.ep[0] = null; newedge.ep[1] = null; dx = s2.coord.x - s1.coord.x; dy = s2.coord.y - s1.coord.y; adx = (dx > 0) ? dx : -dx; ady = (dy > 0) ? dy : -dy; newedge.c = s1.coord.x * dx + s1.coord.y * dy + (dx*dx + dy*dy) * 0.5f; if (adx > ady) { newedge.a = 1.0f; newedge.b = dy/dx; newedge.c /= dx; } else { newedge.b = 1.0f; newedge.a = dx/dy; newedge.c /= dy; } newedge.edgenbr = nvedges; if (delaunayB) out_bisector(newedge); nvedges++; return(newedge); } public void endpoint(EdgeVoronoi e, int lr, SiteVoronoi s) { e.ep[lr] = s; s.refcnt++;; if (e.ep[RE-lr] == null) return; if (voronoiB) out_ep(e); deref(e.reg[LE]); deref(e.reg[RE]); e = null; } public void makevertex(SiteVoronoi v) { v.sitenbr = nvertices; nvertices++; } public void ELdelete(HalfEdgeVoronoi he) { (he.ELleft).ELright = he.ELright; (he.ELright).ELleft = he.ELleft; he.ELedge = null; } public SiteVoronoi rightreg(HalfEdgeVoronoi he) { if(he.ELedge == null) return(bottomsite); return( he.ELpm == LE ? he.ELedge.reg[RE] : he.ELedge.reg[LE] ); } public SiteVoronoi leftreg(HalfEdgeVoronoi he) { if (he.ELedge == null) return(bottomsite); return( he.ELpm == LE ? he.ELedge.reg[LE] : he.ELedge.reg[RE] ); } public void ELinitialize() { list = new ListGNG(); ELleftend = new HalfEdgeVoronoi(null, 0); ELrightend = new HalfEdgeVoronoi(null, 0); ELleftend.ELleft = null; ELleftend.ELright = ELrightend; ELrightend.ELleft = ELleftend; ELrightend.ELright = null; list.insert(ELleftend, list.head); list.insert(ELrightend, list.last()); } public HalfEdgeVoronoi ELleftbnd(FPoint p) { HalfEdgeVoronoi he; he = (list.first()).elem; // Now search linear list of halfedges for the corect one if ( he == ELleftend || (he != ELrightend && right_of(he,p)) ) { do { he = he.ELright; } while ( (he != ELrightend) && right_of(he,p) ); he = he.ELleft; } else { do { he = he.ELleft; } while ( he != ELleftend && !right_of(he,p) ); } return(he); } // returns true if p is to right of halfedge e public boolean right_of(HalfEdgeVoronoi el, FPoint p) { EdgeVoronoi e; SiteVoronoi topsite; boolean right_of_site, above, fast; float dxp, dyp, dxs, t1, t2, t3, yl; e = el.ELedge; topsite = e.reg[1]; right_of_site = p.x > topsite.coord.x; if(right_of_site && (el.ELpm == LE) ) return(true); if(!right_of_site && (el.ELpm == RE) ) return (false); if (e.a == 1.0) { dyp = p.y - topsite.coord.y; dxp = p.x - topsite.coord.x; fast = false; if ( (!right_of_site & e.b < 0.0) | (right_of_site & e.b >= 0.0) ) { above = (dyp >= e.b * dxp); fast = above; } else { above = (p.x + p.y * e.b) > e.c; if(e.b < 0.0) above = !above; if (!above) fast = true; } if (!fast) { dxs = topsite.coord.x - (e.reg[0]).coord.x; above = (e.b * (dxp*dxp - dyp*dyp)) < (dxs * dyp * (1.0 + 2.0 * dxp/dxs + e.b * e.b)); if(e.b < 0.0) above = !above; } } else { // e.b == 1.0 yl = e.c - e.a * p.x; t1 = p.y - yl; t2 = p.x - topsite.coord.x; t3 = yl - topsite.coord.y; above = t1*t1 > t2*t2 + t3*t3; } return (el.ELpm == LE ? above : !above); } public SiteVoronoi nextsite() { siteidx++; if (siteidx > nsites) return(null); return(vsites[siteidx]); } /** * The mouse-selected node. */ protected NodeGNG pick; /** * The flag for mouse-selected node. */ protected boolean pickfixed; Image offscreen; Dimension offscreensize; Graphics offgraphics; /** * The color of the winner node (teach-mode). */ protected final Color winnerColor = Color.red; /** * The color of the second node (teach-mode). */ protected final Color secondColor = Color.orange; /** * The color of the last moved nodes (teach-mode). */ protected final Color movedColor = Color.yellow; /** * The color of the last inserted node (teach-mode). */ protected final Color insertedColor = Color.blue; /** * The color of the shown signal (teach-mode). */ protected final Color signalColor = Color.black; /** * The color of the mouse-selected node. */ protected final Color selectColor = Color.pink; /** * The color of the edges. */ protected final Color edgeColor = Color.black; /** * The color of the Voronoi diagram. */ protected final Color voronoiColor = Color.red; /** * The color of the Delaunay diagram. */ protected final Color delaunayColor = Color.orange; /** * The color of the nodes. */ protected final Color nodeColor = Color.green; /** * The color for debug information. */ protected final Color debugColor = Color.gray; /** * The color of the distribution. */ protected final Color distribColor = new Color(203, 205, 252); /** * The color of the low density distribution. */ protected final Color lowDistribColor = new Color(203, 205, 252); /** * The color of the high density distribution. */ protected final Color highDistribColor = new Color(152, 161, 250); /** * Paint a node. * * @param g The graphic context * @param n The node */ public void paintNode(Graphics g, NodeGNG n) { int RADIUS = 10; Color col = nodeColor; if (teachB && (algo != 5) ) { if (n.winner) { RADIUS += 5; col = winnerColor; } else if (n.second) { RADIUS += 4; col = secondColor; } else if (n.moved) { RADIUS += 2; col = movedColor; } } if (n.inserted) { RADIUS += 2; col = insertedColor; } if ( (algo == 5) && (!n.moved) ) { RADIUS += 4; col = movedColor; } g.setColor(col); g.fillOval((int)n.x - (RADIUS/2), (int)n.y - (RADIUS/2), RADIUS, RADIUS); g.setColor(Color.black); g.drawOval((int)n.x - (RADIUS/2), (int)n.y - (RADIUS/2), RADIUS, RADIUS); } /** * Update the drawing area. * * @param g The graphic context */ public synchronized void update(Graphics g) { Dimension d = size(); int r1 = (int) d.width/4; int l1 = (int) d.height/4; int r2 = (int) d.width/2; int l2 = (int) d.height/2; int cx = (int) (d.width/2); int cy = (int) (d.height/2); int xA[] = new int[MAX_COMPLEX]; int yA[] = new int[MAX_COMPLEX]; int w; int h; int ringRadius; int i, x, y; if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) { offscreen = createImage(d.width, d.height); offscreensize = d; offgraphics = offscreen.getGraphics(); offgraphics.setFont(getFont()); } if (whiteB) offgraphics.setColor(Color.white); else offgraphics.setColor(getBackground()); offgraphics.fillRect(0, 0, d.width, d.height); // Set color for distribution if (algo != 5) offgraphics.setColor(distribColor); switch (distribution) { case 0: // Rectangle r1 = (int) d.width/4; l1 = (int) d.height/4; r2 = (int) d.width/2; l2 = (int) d.height/2; offgraphics.fillRect(r1, l1, r2, l2); break; case 1: // Circle l2 = (int) ((cx < cy) ? cx : cy); // Diameter r1 = (int) d.width/2 -l2/2; l1 = (int) d.height/2 -l2/2; offgraphics.fillOval(r1, l1, l2, l2); break; case 2: // Ring l2 = (int) ((cx < cy) ? cx : cy); // Diameter r1 = (int) cx - l2; l1 = (int) cy - l2; ringRadius = (int) (l2 * RING_FACTOR); offgraphics.fillOval(r1, l1, 2*l2, 2*l2); if (whiteB) offgraphics.setColor(Color.white); else offgraphics.setColor(getBackground()); offgraphics.fillOval(r1 + ringRadius, l1+ringRadius, 2*l2-2*ringRadius, 2*l2-2*ringRadius); break; case 3: // Complex (1) w = (int) d.width/9; h = (int) d.height/5; xA[0] = w; yA[0] = h; xA[1] = w; yA[1] = 2*h; xA[2] = w; yA[2] = 3*h; xA[3] = 2*w; yA[3] = 3*h; xA[4] = 3*w; yA[4] = 3*h; xA[5] = 3*w; yA[5] = 2*h; xA[6] = 3*w; yA[6] = h; xA[7] = 4*w; yA[7] = h; xA[8] = 5*w; yA[8] = h; xA[9] = 5*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 3*h; xA[11] = 7*w; yA[11] = h; xA[12] = 7*w; yA[12] = 2*h; xA[13] = 7*w; yA[13] = 3*h; for (i = 0; i < 14; i++) offgraphics.fillRect(xA[i], yA[i], w, h); break; case 4: // Complex (2) w = (int) d.width/9; h = (int) d.height/7; xA[0] = w; yA[0] = 5*h; xA[1] = w; yA[1] = 4*h; xA[2] = w; yA[2] = 3*h; xA[3] = w; yA[3] = 2*h; xA[4] = 1*w; yA[4] = h; xA[5] = 2*w; yA[5] = h; xA[6] = 3*w; yA[6] = h; xA[7] = 4*w; yA[7] = h; xA[8] = 5*w; yA[8] = 1*h; xA[9] = 5*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 3*h; xA[11] = 3*w; yA[11] = 3*h; xA[12] = 3*w; yA[12] = 4*h; xA[13] = 3*w; yA[13] = 5*h; xA[14] = 4*w; yA[14] = 5*h; xA[15] = 5*w; yA[15] = 5*h; xA[16] = 6*w; yA[16] = 5*h; xA[17] = 7*w; yA[17] = 5*h; xA[18] = 7*w; yA[18] = 4*h; xA[19] = 7*w; yA[19] = 3*h; xA[20] = 7*w; yA[20] = 2*h; xA[21] = 7*w; yA[21] = 1*h; for (i = 0; i < 22; i++) offgraphics.fillRect(xA[i], yA[i], w, h); break; case 5: // Complex (3) w = (int) d.width/13; h = (int) d.height/11; xA[0] = w; yA[0] = h; xA[1] = w; yA[1] = 2*h; xA[2] = w; yA[2] = 3*h; xA[3] = w; yA[3] = 4*h; xA[4] = 1*w; yA[4] = 5*h; xA[5] = 1*w; yA[5] = 6*h; xA[6] = 1*w; yA[6] = 7*h; xA[7] = 1*w; yA[7] = 8*h; xA[8] = 1*w; yA[8] = 9*h; xA[9] = 2*w; yA[9] = 1*h; xA[10] = 3*w; yA[10] = 1*h; xA[11] = 4*w; yA[11] = 1*h; xA[12] = 5*w; yA[12] = 1*h; xA[13] = 6*w; yA[13] = 1*h; xA[14] = 7*w; yA[14] = 1*h; xA[15] = 8*w; yA[15] = 1*h; xA[16] = 9*w; yA[16] = 1*h; xA[17] = 9*w; yA[17] = 2*h; xA[18] = 9*w; yA[18] = 3*h; xA[19] = 9*w; yA[19] = 4*h; xA[20] = 9*w; yA[20] = 5*h; xA[21] = 9*w; yA[21] = 6*h; xA[22] = 9*w; yA[22] = 7*h; xA[23] = 8*w; yA[23] = 7*h; xA[24] = 7*w; yA[24] = 7*h; xA[25] = 6*w; yA[25] = 7*h; xA[26] = 5*w; yA[26] = 7*h; xA[27] = 5*w; yA[27] = 6*h; xA[28] = 5*w; yA[28] = 5*h; xA[29] = 3*w; yA[29] = 3*h; xA[30] = 3*w; yA[30] = 4*h; xA[31] = 3*w; yA[31] = 5*h; xA[32] = 3*w; yA[32] = 6*h; xA[33] = 3*w; yA[33] = 7*h; xA[34] = 3*w; yA[34] = 8*h; xA[35] = 3*w; yA[35] = 9*h; xA[36] = 4*w; yA[36] = 3*h; xA[37] = 5*w; yA[37] = 3*h; xA[38] = 6*w; yA[38] = 3*h; xA[39] = 7*w; yA[39] = 3*h; xA[40] = 7*w; yA[40] = 4*h; xA[41] = 7*w; yA[41] = 5*h; xA[42] = 4*w; yA[42] = 9*h; xA[43] = 5*w; yA[43] = 9*h; xA[44] = 6*w; yA[44] = 9*h; xA[45] = 7*w; yA[45] = 9*h; xA[46] = 8*w; yA[46] = 9*h; xA[47] = 9*w; yA[47] = 9*h; xA[48] =10*w; yA[48] = 9*h; xA[49] =11*w; yA[49] = 9*h; xA[50] =11*w; yA[50] = 8*h; xA[51] =11*w; yA[51] = 7*h; xA[52] =11*w; yA[52] = 6*h; xA[53] =11*w; yA[53] = 5*h; xA[54] =11*w; yA[54] = 4*h; xA[55] =11*w; yA[55] = 3*h; xA[56] =11*w; yA[56] = 2*h; xA[57] =11*w; yA[57] = 1*h; for (i = 0; i < 58; i++) offgraphics.fillRect(xA[i], yA[i], w, h); break; case 6: // HiLo-Density w = (int) d.width/10; h = (int) d.height/10; xA[0] = 2 * w; yA[0] = 4 * h; xA[1] = 5 * w; yA[1] = 1 * h; if (algo != 5) offgraphics.setColor(highDistribColor); offgraphics.fillRect(xA[0], yA[0], w, h); if (algo != 5) offgraphics.setColor(lowDistribColor); offgraphics.fillRect(xA[1], yA[1], 4 * w, 8 * h); break; case 7: // discrete int RADIUS = 2; for (i = 0; i < numDiscreteSignals; i++) { x = (int) (discreteSignalsX[i]); y = (int) (discreteSignalsY[i]); offgraphics.setColor(distribColor); offgraphics.fillOval(x - 1, y - 1, 2, 2); offgraphics.setColor(Color.black); offgraphics.drawOval(x - 1, y - 1, 2, 2); } break; case 8: // Complex (4) w = (int) d.width/17; h = (int) d.height/8; xA[0] = w; yA[0] = 2*h; xA[1] = w; yA[1] = 3*h; xA[2] = w; yA[2] = 4*h; xA[3] = w; yA[3] = 5*h; xA[4] = 2*w; yA[4] = 5*h; xA[5] = 3*w; yA[5] = 5*h; xA[6] = 3*w; yA[6] = 4*h; xA[7] = 3*w; yA[7] = 3*h; xA[8] = 3*w; yA[8] = 2*h; xA[9] = 4*w; yA[9] = 2*h; xA[10] = 5*w; yA[10] = 2*h; xA[11] = 6*w; yA[11] = 2*h; xA[12] = 7*w; yA[12] = 2*h; xA[13] = 7*w; yA[13] = 3*h; xA[14] = 7*w; yA[14] = 4*h; xA[15] = 7*w; yA[15] = 5*h; xA[16] = 8*w; yA[16] = 5*h; xA[17] = 9*w; yA[17] = 5*h; xA[18] = 10*w; yA[18] = 5*h; xA[19] = 11*w; yA[19] = 5*h; xA[20] = 11*w; yA[20] = 4*h; xA[21] = 11*w; yA[21] = 3*h; xA[22] = 11*w; yA[22] = 2*h; xA[23] = 14*w; yA[23] = 2*h; xA[24] = 15*w; yA[24] = 2*h; xA[25] = 15*w; yA[25] = 3*h; xA[26] = 15*w; yA[26] = 4*h; xA[27] = 15*w; yA[27] = 5*h; for (i = 0; i < 28; i++) offgraphics.fillRect(xA[i], yA[i], w, h); break; case 9: // Moving and Jumping Rectangle r2 = (int) d.width/4; l2 = (int) d.height/4; r1 = (int) (0.75 * (d.width/2 + Math.IEEEremainder((double)0.2 * numRun,(double)(d.width)))); l1 = (int) (0.75 * (d.height/2 + Math.IEEEremainder((double)0.2 * numRun,(double)(d.height)))); if (DEBUG) System.out.println("Rectangle x = " + r1); offgraphics.fillRect(r1, l1, r2, l2); break; case 10: // Moving Rectangle r2 = (int) d.width/4; l2 = (int) d.height/4; r1 = (int) (0.75 * (d.width/2 + bounceX * Math.IEEEremainder((double)0.2 * numRun, (double)(d.width)))); l1 = (int) (0.75 * (d.height/2 + bounceY * Math.IEEEremainder((double)0.2 * numRun, (double)(d.height)))); if (DEBUG) System.out.println("Rectangle x = " + r1 + "\nremainder x = " + Math.IEEEremainder((double)0.2 * numRun, (double)(d.width)) + "\nremainder y = " + Math.IEEEremainder((double)0.2 * numRun, (double)(d.height))); offgraphics.fillRect(r1, l1, r2, l2); break; case 11: // Jumping Rectangle r2 = (int) d.width/4; l2 = (int) d.height/4; offgraphics.fillRect(jumpX, jumpY, r2, l2); break; case 12: // R.Mouse Rectangle r2 = (int) d.width/4; l2 = (int) d.height/4; offgraphics.fillRect(jumpX, jumpY, r2, l2); break; } // Draw the edges if (edgesB) { int x1, y1, x2, y2; EdgeGNG e; for (i = 0 ; i < nedges ; i++) { e = edges[i]; x1 = (int)nodes[e.from].x; y1 = (int)nodes[e.from].y; x2 = (int)nodes[e.to].x; y2 = (int)nodes[e.to].y; offgraphics.setColor(edgeColor); offgraphics.drawLine(x1, y1, x2, y2); } } // Draw the Voronoi diagram if (voronoiB || delaunayB) { LineGNG l; for (i = 0; i < nlines; i++) { l = lines[i]; if (vd[i]) offgraphics.setColor(voronoiColor); else offgraphics.setColor(delaunayColor); offgraphics.drawLine(l.x1, l.y1, l.x2, l.y2); } } // Draw the nodes if (nodesB) for (i = 0; i < nnodes; i++) paintNode(offgraphics, nodes[i]); if ( teachB ) { int r = 6; int offset_x = 12; int offset2_x = offset_x + 5; int offset_y = (int) d.height/4; if (algo == 5) { // Draw legend offgraphics.setColor(Color.black); offgraphics.drawString("Legend:", 2, offset_y); offset_y += 15; offgraphics.setColor(movedColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Not moved", offset2_x, offset_y); offset_y += 15; } else { offgraphics.setColor(signalColor); offgraphics.fillOval(SignalX - r/2, SignalY - r/2, r, r); // Draw legend offgraphics.setColor(Color.black); offgraphics.drawString("Legend:", 2, offset_y); offset_y += 15; offgraphics.setColor(winnerColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Winner", offset2_x, offset_y); offset_y += 15; if (algo != 1) { offgraphics.setColor(secondColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Second", offset2_x, offset_y); offset_y += 15; } if (algo == 0) { offgraphics.setColor(movedColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Neighbors", offset2_x, offset_y); offset_y += 15; offgraphics.setColor(insertedColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Last inserted", offset2_x, offset_y); offset_y += 15; } offgraphics.setColor(signalColor); offgraphics.fillOval(offset_x - r, offset_y - r, r, r); offgraphics.setColor(Color.black); offgraphics.drawString("Signal", offset2_x, offset_y); offset_y += 15; } } offgraphics.setColor(Color.black); offgraphics.drawString(String.valueOf(numRun), 10, 10); if (maxNodes == 1) offgraphics.drawString(String.valueOf(nnodes) + " Node", 10, d.height - 10); else offgraphics.drawString(String.valueOf(nnodes) + " Nodes", 10, d.height - 10); offgraphics.drawString(DGNG_VERSION, d.width - 40, 10); if ( readyLBG_B && (algo == 5) ) offgraphics.drawString("READY!", d.width-50, d.height-10); if ( fineTuningB && (algo == 6) ) offgraphics.drawString(fineTuningS, d.width-130, d.height-10); if ( signalsB && (algo != 5) ) { for (i = 0; i < stepSize; i++) { x = (int) (lastSignalsX[i]); y = (int) (lastSignalsY[i]); offgraphics.setColor(Color.green); offgraphics.fillOval(x - 1, y - 1, 2, 2); offgraphics.setColor(Color.black); offgraphics.drawOval(x - 1, y - 1, 2, 2); } } if ( insertedSoundB && soundB ) { graph.play(graph.getCodeBase(), "audio/drip.au"); insertedSoundB = false; } if (algo == 5) { for (i = 0; i < numDiscreteSignals; i++) { x = (int) (discreteSignalsX[i]); y = (int) (discreteSignalsY[i]); offgraphics.setColor(distribColor); offgraphics.fillOval(x - 1, y - 1, 2, 2); offgraphics.setColor(Color.black); offgraphics.drawOval(x - 1, y - 1, 2, 2); } } // Show error graph or not if (errorGraph != null) { if (errorGraphB) errorGraph.show(); else errorGraph.hide(); } g.drawImage(offscreen, 0, 0, null); } public synchronized boolean mouseDown(Event evt, int x, int y) { if (evt.metaDown() && (distribution == 12) ) { Dimension d = size(); //System.out.println("RIGHT Mousebutton pressed!"); jumpX = x; jumpY = y; // Draw distribution only inside the visible region if (jumpX > (0.75 * d.width)) jumpX = (int) (0.75 * d.width); if (jumpY > (0.75 * d.height)) jumpY = (int) (0.75 * d.height); repaint(); return true; } float bestDist = Float.MAX_VALUE; NodeGNG n; float dist; // if (evt.modifiers == Event.ALT_MASK) // System.out.println("MIDDLE Mousebutton pressed!"); for (int i = 0 ; i < nnodes ; i++) { n = nodes[i]; dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y); if (dist < bestDist) { pick = n; bestDist = dist; } } pickfixed = pick.fixed; pick.fixed = true; pick.x = x; pick.y = y; if (algo == 5) pick.moved = true; nodesMovedB = true; repaint(); return true; } public synchronized boolean mouseDrag(Event evt, int x, int y) { Dimension d = size(); if (evt.metaDown() && (distribution == 12) ) { //System.out.println("RIGHT Mousebutton dragged!"); jumpX = x; jumpY = y; // Draw distribution only inside the visible region if (jumpX < 0) jumpX = 0; else if (jumpX > (0.75 * d.width)) jumpX = (int) (0.75 * d.width); if (jumpY < 0) jumpY = 0; else if (jumpY > (0.75 * d.height)) jumpY = (int) (0.75 * d.height); repaint(); return true; } pick.x = x; pick.y = y; // Draw nodes only inside the visible region if (pick.x < 0) pick.x = 0; else if (pick.x > d.width) pick.x = d.width; if (pick.y < 0) pick.y = 0; else if (pick.y > d.height) pick.y = d.height; nodesMovedB = true; repaint(); return true; } public synchronized boolean mouseUp(Event evt, int x, int y) { Dimension d = size(); if (evt.metaDown() && (distribution == 12) ) { //System.out.println("RIGHT Mousebutton released!"); jumpX = x; jumpY = y; // Draw distribution only inside the visible region if (jumpX < 0) jumpX = 0; else if (jumpX > (0.75 * d.width)) jumpX = (int) (0.75 * d.width); if (jumpY < 0) jumpY = 0; else if (jumpY > (0.75 * d.height)) jumpY = (int) (0.75 * d.height); repaint(); return true; } pick.x = x; pick.y = y; pick.fixed = pickfixed; // Draw nodes only inside the visible region if (pick.x < 0) pick.x = 0; else if (pick.x > d.width) pick.x = d.width; if (pick.y < 0) pick.y = 0; else if (pick.y > d.height) pick.y = d.height; pick = null; nodesMovedB = true; repaint(); return true; } public void start() { relaxer = new Thread(this); relaxer.start(); if ( errorGraphB && (errorGraph != null) ) errorGraph.show(); } public void stop() { relaxer.stop(); if ( errorGraphB && (errorGraph != null) ) errorGraph.hide(); } public void destroy() { if ( errorGraphB && (errorGraph != null) ) { errorGraph.dispose(); errorGraph = null; } } public void graphClose() { if ( errorGraphB && (errorGraph != null) ) { errorGraph.dispose(); errorGraph = null; } } }