map;
double[] seg;
double lastX = 0;
double lastY = 0;
Path2D scaled;
Line2D newLine;
Color color;
PathIterator iter;
seg = new double[] { 0, 0, 0, 0, 0, 0 };
rnd = new Random();
// Compute the bounding box based on the points
getBoundingBox(vertices);
// Compute the transform to convert points to pixels
computeTransform();
// Do all drawing in a synchronized block
synchronized (this.offscreen) {
grx = (Graphics2D) this.offscreen.getGraphics();
grx.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Clear the window
grx.setColor(Color.BLACK);
grx.fillRect(0, 0, IMGWIDTH, IMGHEIGHT);
// Draw Voronoi regions
map = sub.vorMap;
if (map != null) {
for (Path2D path : map.values()) {
color = this.gradient.getColor(rnd.nextInt(100));
color = new Color(color.getRed() / 2, color.getGreen() / 2, // NOPMD SRB
color.getBlue() / 2);
grx.setColor(color);
scaled = new Path2D.Double(); // NOPMD SRB
iter = path.getPathIterator(null);
while (!iter.isDone()) {
switch (iter.currentSegment(seg)) {
case PathIterator.SEG_MOVETO:
lastX = seg[0];
lastY = seg[1];
break;
case PathIterator.SEG_LINETO:
newLine = new Line2D.Double(scaleX(lastX), scaleY(lastY), // NOPMD SRB
scaleX(seg[0]), scaleY(seg[1]));
scaled.append(newLine, true);
lastX = seg[0];
lastY = seg[1];
break;
default:
break;
}
iter.next();
}
grx.fill(scaled);
}
}
// Draw all the edges in green
grx.setColor(LINES);
for (Edge edge : sub.delEdges) {
pixX1 = scaleX(edge.org().xPos);
pixY1 = scaleY(edge.org().yPos);
pixX2 = scaleX(edge.dest().xPos);
pixY2 = scaleY(edge.dest().yPos);
grx.drawLine(pixX1, pixY1, pixX2, pixY2);
}
// Draw all the voronoi boundaries in dark gray
grx.setColor(Color.RED);
for (GraphEdge edge : sub.vorEdges) {
pixX1 = scaleX(edge.xPos1);
pixY1 = scaleY(edge.yPos1);
pixX2 = scaleX(edge.xPos2);
pixY2 = scaleY(edge.yPos2);
grx.drawLine(pixX1, pixY1, pixX2, pixY2);
}
// Plot the points
grx.setColor(Color.YELLOW);
for (Edge edge : sub.delEdges) {
if (edge.data != null) {
pixX1 = scaleX(edge.data.xPos);
pixY1 = scaleY(edge.data.yPos);
grx.fillOval(pixX1 - 1, pixY1 - 1, 3, 3);
}
if (edge.sym().data != null) {
pixX1 = scaleX(edge.sym().data.xPos);
pixY1 = scaleY(edge.sym().data.yPos);
grx.fillOval(pixX1 - 1, pixY1 - 1, 3, 3);
}
}
}
repaint();
}
/**
* Paints the panel.
*
* @param grx the Graphics
to which to draw
*/
@Override public void paintComponent(final Graphics grx) {
super.paintComponent(grx);
synchronized (this.offscreen) {
grx.drawImage(this.offscreen, 0, 0, getWidth(), getHeight(), null);
}
}
/**
* Computes the bounding box of a set of points. The box will be just large enough to contain
* the points, which could possibly mean having zero width or height if points are colinear.
* The bounding rectangle is stored in the member variable bounds
.
*
* @param points the set of points
*/
private void getBoundingBox(final Vertex[] points) {
Vertex vert;
vert = points[0];
this.minX = vert.xPos;
this.maxX = this.minX;
this.minY = vert.yPos;
this.maxY = this.minY;
for (int i = 1; i < points.length; i++) {
vert = points[i];
if (vert.xPos < this.minX) {
this.minX = vert.xPos;
}
if (vert.xPos > this.maxX) {
this.maxX = vert.xPos;
}
if (vert.yPos < this.minY) {
this.minY = vert.yPos;
}
if (vert.yPos > this.maxY) {
this.maxY = vert.yPos;
}
}
// Ensure that no edge is zero, to make matrix computation easier
if (Math.abs(this.minX) < 0.0000001) {
this.minX = -0.0000001;
}
if (Math.abs(this.maxX) < 0.0000001) {
this.maxX = 0.0000001;
}
if (Math.abs(this.minY) < 0.0000001) {
this.minY = -0.0000001;
}
if (Math.abs(this.maxY) < 0.0000001) {
this.maxY = 0.0000001;
}
}
/**
* Computes the transformation matrix to map a rectangle to an area in the panel covering 80%
* of the panel in each dimension (that is, the panel will have a 10% margin on each edge).
*
*
* [ . . ] [x] = [X]
* [ . . ] [y] [Y]
*
*
* where (x,y) is the real-valued point space, (X, Y) is the pixel, and (minX,minY) maps to
* (w/10,9h/10) and (maxX,maxY) maps to (9w/10,h/10).
*/
private void computeTransform() {
// Compute scale factors for X and Y axis (flipping Y axis)
if (this.maxX == this.minX) {
this.xScale = 1;
} else {
this.xScale = (1 - (2 * MARGIN)) * IMGWIDTH / (this.maxX - this.minX);
}
if (this.maxY == this.minY) {
this.yScale = -1;
} else {
this.yScale = -(1 - (2 * MARGIN)) * IMGHEIGHT / (this.maxY - this.minY);
}
if (-this.yScale > this.xScale) {
this.yScale = -this.xScale;
} else if (this.xScale > -this.yScale) {
this.xScale = -this.yScale;
}
// Now set offset so (minX, minY) maps to (0.1W, 0.9H)
this.xOffset = (MARGIN * IMGWIDTH) - (this.xScale * this.minX);
this.yOffset = ((1 - MARGIN) * IMGHEIGHT) - (this.yScale * this.minY);
}
/**
* Generates an X pixel from a real X coordinate in point space.
*
* @param coord the X coordinate
* @return the X pixel value
*/
private int scaleX(final double coord) {
return (int) ((this.xScale * coord) + this.xOffset + 0.5);
}
/**
* Generates a Y pixel from a real Y coordinate in point space.
*
* @param coord the Y coordinate
* @return the Y pixel value
*/
private int scaleY(final double coord) {
return (int) ((this.yScale * coord) + this.yOffset + 0.5);
}
}