// (C) 1997-1998, revised June 2014 by Frank Nielsen (Frank.Nie...@acm.org 5793b870) // // Compile with: javac StabbingPiercingBoxes.java (if javac complains use option -source 1.4 -Xlint:deprecation -Xlint:unchecked) // Run with: appletviewer run.html // where run.html contains this code: // // Left-click drag for adding a box or select from menu for adding many boxes // originally, those files were called boxes.java, IntervalSet.java, etc. /* Divide and conquer paradigm for approximation: Fast stabbing of boxes in high dimensions. Frank Nielsen Theoretical Computer Science 246(1-2): 53-72 (2000) http://www.sciencedirect.com/science/article/pii/S0304397598003363 @Article{n-fsbhd-2000, author = {Frank Nielsen}, title = {Fast stabbing of boxes in high dimensions}, journal = {Theoretical Computer Science}, volume = {246}, number = {1/2}, year = {2000}, issn = {0304-3975}, pages = {53--75}, publisher = {Elsevier Science Publishers Ltd.}, doi= {10.1016/S0304-3975(98)00336-3} } On point covers of c-oriented polygons. Frank Nielsen Theoretical Computer Science 263(1-2): 17-29 (2001) see CCCG 1996 and also Maintenance of a Piercing Set for Intervals with Applications. Matthew J. Katz, Frank Nielsen, Michael Segal Algorithmica 36(1): 59-73 (2003) The heuristics compute in output-sensitive time an approximation of : - a maximum independent set of rectangles (axis-parallel also called isothetic d-dimensional boxes, independent set into k pairwise non-connected vertices) - a minimum clique cover of rectangles (vertices of a graph can be partitioned into k cliques) without computing the intersection graph (that would require quadratic time) */ import java.applet.Applet; import java.awt.*; import java.io.PrintStream; import java.util.Random; import java.util.Vector; public class StabbingPiercingBoxes extends Applet { public StabbingPiercingBoxes() { ABOUT = 0; } public void Animation(Graphics g) { clear(g); g.drawString("Piercing Isothetic Boxes / Giving an Independent Set", 200, 100); g.drawString("Nielsen Frank, Frank.Nielsen@acm.org", 260, 115); g.drawString("An output-sensitive precision-sensitive algorithm", 200, 140); g.drawString("DOES NOT COMPUTE THE INCIDENCE/INTERSECTION GRAPH", 200, 160); g.drawString("See http://www.sonycsl.co.jp", 200, 180); ABOUT++; } public boolean action(Event event, Object obj) { if(event.target == reset) { Graphics g = getGraphics(); Rectangle rectangle = getBounds(); g.setColor(getBackground()); g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); nbboxes = 0; VRect.setSize(0); return true; } if(event.target == pierce) { if(nbboxes != 0) { Pierce pierce1 = new Pierce(VRect); if(salg == 1) pierce1.Alg1(); if(salg == 2) pierce1.Alg2(); System.out.println("#Piercing points :" + pierce1.VPoints.size() + " on " + pierce1.size() + " Rectangles.(" + salg + ")"); System.out.println(" Independent set of size:" + pierce1.VIRectangles.size()); VPPoints = pierce1.VPoints; VIRect = pierce1.VIRectangles; Graphics g1 = getGraphics(); Rectangle rectangle1 = getBounds(); g1.setColor(getBackground()); g1.fillRect(rectangle1.x, rectangle1.y, rectangle1.width, rectangle1.height); g1.setColor(Color.black); paint(g1); } } else if(event.target == about) Animation(getGraphics()); else if(event.target == random) { Random random1 = new Random(); char c = '\n'; if(obj.equals("20 rectangles")) c = '\024'; if(obj.equals("50 rectangles")) c = '2'; if(obj.equals("100 rectangles")) c = 'd'; if(obj.equals("200 rectangles")) c = '\310'; if(obj.equals("500 rectangles")) c = '\u01F4'; if(obj.equals("1000 rectangles")) c = '\u03E8'; if(obj.equals("10000 rectangles")) c = '\u2710'; Vector vector = new Vector(c); VRect.setSize(0); for(int i = 0; i < c; i++) { int j = random1.nextInt() % WIDTH; int k = random1.nextInt() % HEIGHT; int l = random1.nextInt() % WIDTH; int i1 = random1.nextInt() % HEIGHT; if(j < 0) j = -j; if(k < 0) k = -k; if(l < 0) l = -l; if(i1 < 0) i1 = -i1; if(j > l) { int j1 = j; j = l; l = j1; } if(k > i1) { int k1 = k; k = i1; i1 = k1; } Rectangle rectangle3 = new Rectangle(j, k, l - j, i1 - k); VRect.addElement(rectangle3); vector.addElement(rectangle3); } Pierce pierce3 = new Pierce(vector); if(salg == 1) pierce3.Alg1(); if(salg == 2) pierce3.Alg2(); VPPoints = pierce3.VPoints; VIRect = pierce3.VIRectangles; System.out.println("#Piercing points :" + pierce3.VPoints.size() + " on " + pierce3.size() + " Rectangles.(" + salg + ")"); System.out.println(" Independent set of size:" + VIRect.size()); clear(getGraphics()); paint(getGraphics()); } else if(event.target == selectalg) { if(obj.equals("Heuristic 1, 1996")) salg = 1; if(obj.equals("Heuristic 2, 1997")) salg = 2; Pierce pierce2 = new Pierce(VRect); if(salg == 1) pierce2.Alg1(); if(salg == 2) pierce2.Alg2(); VPPoints = pierce2.VPoints; VIRect = pierce2.VIRectangles; System.out.println("#Piercing points :" + pierce2.VPoints.size() + " on " + pierce2.size() + " Rectangles.(" + salg + ")"); System.out.println(" Independent set of size:" + VIRect.size()); Graphics g2 = getGraphics(); Rectangle rectangle2 = getBounds(); g2.setColor(getBackground()); g2.fillRect(rectangle2.x, rectangle2.y, rectangle2.width, rectangle2.height); g2.setColor(Color.black); paint(g2); } else { return super.action(event, obj); } return true; } public void clear(Graphics g) { Rectangle rectangle = getBounds(); g.setColor(getBackground()); g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); g.setColor(Color.black); } public void init() { System.out.println("Welcome to the piercing java program.\n(C) Frank Nielsen 1997 1998 v1.1\nSony Computer Science Laboratories and Ecole Polytechnique"); nbboxes = 0; WIDTH = 800; HEIGHT = 500; VRect = new Vector(20); VPPoints = new Vector(20); VIRect = new Vector(20); setBackground(Color.white); setForeground(Color.black); reset = new Button("Reset"); pierce = new Button("Pierce"); about = new Button("About"); add(reset); add(pierce); add(about); random = new Choice(); random.addItem("20 rectangles"); random.addItem("50 rectangles"); random.addItem("100 rectangles"); random.addItem("200 rectangles"); random.addItem("500 rectangles"); random.addItem("1000 rectangles"); random.addItem("10000 rectangles"); add(new Label("Random:")); add(random); salg = 1; selectalg = new Choice(); selectalg.addItem("Heuristic 1, 1996"); selectalg.addItem("Heuristic 2, 1997"); add(new Label("Heuristics:")); add(selectalg); } public boolean mouseDown(Event event, int i, int j) { x1 = i; y1 = j; Xp = x1; Yp = y1; DXp = 0; DYp = 0; return true; } public boolean mouseDrag(Event event, int i, int j) { x2 = i; y2 = j; int k = x1; int l = y1; if(x1 > x2) { int i2 = x1; k = x2; x2 = i2; } if(y1 > y2) { int j2 = y1; l = y2; y2 = j2; } int i1 = k; int j1 = l; int k1 = x2 - k; int l1 = y2 - l; Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.blue); g.drawRect(Xp, Yp, DXp, DYp); Xp = i1; Yp = j1; DXp = k1; DYp = l1; g.setColor(Color.blue); g.drawRect(i1, j1, k1, l1); return true; } public boolean mouseEnter(Event event, int i, int j) { showStatus("[" + i + "," + j + "]"); return true; } public boolean mouseMove(Event event, int i, int j) { showStatus("[" + i + "," + j + "]"); return true; } public boolean mouseUp(Event event, int i, int j) { int k = x1; int l = y1; x2 = i; y2 = j; nbboxes++; if(x1 > x2) { int i1 = x1; k = x2; x2 = i1; } if(y1 > y2) { int j1 = y1; l = y2; y2 = j1; } Xp = k; Yp = l; DXp = x2 - k; DYp = y2 - l; Graphics g = getGraphics(); g.drawRect(Xp, Yp, DXp, DYp); Rectangle rectangle = new Rectangle(Xp, Yp, DXp, DYp); VRect.addElement(rectangle); return true; } public void paint(Graphics g) { if(ABOUT == 1) { Animation(getGraphics()); return; } g.setColor(Color.green); int l = VIRect.size(); for(int i = 0; i < l; i++) { Rectangle rectangle = (Rectangle)VIRect.elementAt(i); g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); } g.setColor(Color.black); l = VRect.size(); byte byte0 = 5; for(int j = 0; j < l; j++) { Rectangle rectangle1 = (Rectangle)VRect.elementAt(j); g.drawRect(rectangle1.x, rectangle1.y, rectangle1.width, rectangle1.height); } l = VPPoints.size(); g.setColor(Color.red); for(int k = 0; k < l; k++) { Point point = (Point)VPPoints.elementAt(k); g.fillRect(point.x - 3, point.y - 3, 6, 6); } g.setColor(Color.black); } private Button reset; private Button pierce; private Button about; private Choice random; private Choice selectalg; private int x1; private int y1; private int x2; private int y2; private int Xp; private int Yp; private int DXp; private int DYp; private int nbboxes; private Vector VRect; private Vector VPPoints; private Vector VIRect; private int WIDTH; private int HEIGHT; private int salg; private int ABOUT; } class About extends Dialog { public About(Frame frame, String s, String s1) { super(frame, s, false); setLayout(new BorderLayout(15, 15)); label = new Label(s1); add("Center", label); ok = new Button("Close"); Panel panel = new Panel(); panel.setLayout(new FlowLayout(1, 15, 15)); panel.add(ok); add("South", panel); pack(); } public boolean action(Event event, Object obj) { if(event.target == ok) { hide(); dispose(); return true; } else { return false; } } protected Button ok; protected Label label; } class IntervalSet { public IntervalSet(int i, int ai[], int ai1[], int j) { n = i; x = new int[n]; y = new int[n]; for(int k = 0; k < i; k++) { x[k] = ai[k]; y[k] = ai1[k]; } VPoints = new Vector(0); VIindex = new Vector(0); Xmed = j; } public IntervalSet(int i, Rectangle arectangle[], int j) { n = i; x = new int[n]; y = new int[n]; for(int k = 0; k < i; k++) { x[k] = arectangle[k].x; y[k] = arectangle[k].x + arectangle[k].height; } VPoints = new Vector(0); VIindex = new Vector(0); Xmed = j; } public void Pierce() { int ai[] = new int[n]; int l = 0; for(int i = 0; i < n; i++) ai[i] = 0; while(l < n) { int i1 = 0; int j1 = 0; for(int j = 0; j < n; j++) if(x[j] >= i1 && ai[j] == 0) { i1 = x[j]; j1 = j; } Point point = new Point(Xmed, i1); Point point1 = new Point(j1, j1); VPoints.addElement(point); VIindex.addElement(point1); for(int k = 0; k < n; k++) if(ai[k] == 0 && x[k] <= i1 && y[k] >= i1) { l++; ai[k] = 1; } } } private int n; private int Xmed; private int x[]; private int y[]; public Vector VPoints; public Vector VIindex; } class Pierce { public Pierce(Vector vector) { n = vector.size(); x = new int[2 * n]; y = new int[2 * n]; for(int i = 0; i < n; i++) { Rectangle rectangle = (Rectangle)vector.elementAt(i); x[i] = rectangle.x; y[i] = rectangle.y; x[i + n] = x[i] + rectangle.width; y[n + i] = y[i] + rectangle.height; } VPoints = new Vector(5); VIRectangles = new Vector(5); } public void Alg1() { Statistic statistic = new Statistic(2 * n, x); int i2 = statistic.Median(n); int k1; int l1; int j1 = k1 = l1 = 0; for(int i = 0; i < n; i++) if(x[i] <= i2 && x[n + i] >= i2) l1++; else if(x[i] < i2) j1++; else k1++; int ai[] = new int[l1]; int ai1[] = new int[l1]; Vector vector = new Vector(j1); Vector vector1 = new Vector(k1); l1 = 0; for(int j = 0; j < n; j++) if(x[j] <= i2 && x[n + j] >= i2) { ai[l1] = y[j]; ai1[l1++] = y[n + j]; } else { Rectangle rectangle = new Rectangle(x[j], y[j], x[j + n] - x[j], y[j + n] - y[j]); if(x[j] < i2) vector.addElement(rectangle); else vector1.addElement(rectangle); } if(l1 > 0) { IntervalSet intervalset = new IntervalSet(l1, ai, ai1, i2); intervalset.Pierce(); for(int k = 0; k < intervalset.VPoints.size(); k++) VPoints.addElement(intervalset.VPoints.elementAt(k)); } if(j1 > 0) { Pierce pierce = new Pierce(vector); pierce.Alg1(); for(int l = 0; l < pierce.VPoints.size(); l++) VPoints.addElement(pierce.VPoints.elementAt(l)); } if(k1 > 0) { Pierce pierce1 = new Pierce(vector1); pierce1.Alg1(); for(int i1 = 0; i1 < pierce1.VPoints.size(); i1++) VPoints.addElement(pierce1.VPoints.elementAt(i1)); } } public void Alg2() { int ai[] = new int[n]; int ai1[] = new int[n]; for(int i = 0; i < n; i++) { ai[i] = x[i]; ai1[i] = x[i + n]; } IntervalSet intervalset = new IntervalSet(n, ai, ai1, 0); intervalset.Pierce(); int l3 = ((Point)intervalset.VPoints.elementAt(intervalset.VPoints.size() / 2)).y; int k2; int l2; int j2 = k2 = l2 = 0; int j3; int k3; int i3 = j3 = k3 = 0; for(int j = 0; j < n; j++) if(x[j] <= l3 && x[n + j] >= l3) l2++; else if(x[j] < l3) j2++; else k2++; Rectangle arectangle[] = new Rectangle[l2]; Vector vector = new Vector(j2); Vector vector1 = new Vector(k2); l2 = 0; for(int k = 0; k < n; k++) if(x[k] <= l3 && x[n + k] >= l3) { arectangle[l2] = new Rectangle(x[k], y[k], x[k + n] - x[k], y[k + n] - y[k]); l2++; } else { Rectangle rectangle = new Rectangle(x[k], y[k], x[k + n] - x[k], y[k + n] - y[k]); if(x[k] < l3) vector.addElement(rectangle); else vector1.addElement(rectangle); } Rectangle1D rectangle1d = new Rectangle1D(l2, arectangle, l3); if(l2 > 0) { rectangle1d.Pierce(); for(int l = 0; l < rectangle1d.VPoints.size(); l++) VPoints.addElement(rectangle1d.VPoints.elementAt(l)); k3 = rectangle1d.VIRectangles.size(); } Pierce pierce = new Pierce(vector); if(j2 > 0) { pierce.Alg2(); i3 = pierce.VIRectangles.size(); for(int i1 = 0; i1 < pierce.VPoints.size(); i1++) VPoints.addElement(pierce.VPoints.elementAt(i1)); } Pierce pierce1 = new Pierce(vector1); if(k2 > 0) { pierce1.Alg2(); j3 = pierce1.VIRectangles.size(); for(int j1 = 0; j1 < pierce1.VPoints.size(); j1++) VPoints.addElement(pierce1.VPoints.elementAt(j1)); } if(i3 + j3 <= k3) { for(int k1 = 0; k1 < k3; k1++) VIRectangles.addElement(rectangle1d.VIRectangles.elementAt(k1)); } else { for(int l1 = 0; l1 < i3; l1++) VIRectangles.addElement(pierce.VIRectangles.elementAt(l1)); for(int i2 = 0; i2 < j3; i2++) VIRectangles.addElement(pierce1.VIRectangles.elementAt(i2)); } } public int size() { return n; } private int n; private int x[]; private int y[]; public Vector VPoints; public Vector VIRectangles; } class Rectangle1D { public Rectangle1D(int i, Rectangle arectangle[], int j) { n = i; R = new Rectangle[i]; for(int k = 0; k < i; k++) R[k] = arectangle[k]; VPoints = new Vector(0); VIRectangles = new Vector(0); Xmed = j; } public void Pierce() { int j1 = -1; int ai[] = new int[n]; int l = 0; for(int i = 0; i < n; i++) ai[i] = 0; while(l < n) { int i1 = 0; for(int j = 0; j < n; j++) if(R[j].y >= i1 && ai[j] == 0) { i1 = R[j].y; j1 = j; } Point point = new Point(Xmed, i1); Rectangle rectangle = new Rectangle(R[j1].x, R[j1].y, R[j1].width, R[j1].height); VPoints.addElement(point); VIRectangles.addElement(rectangle); for(int k = 0; k < n; k++) if(ai[k] == 0 && R[k].y <= i1 && R[k].y + R[k].height >= i1) { l++; ai[k] = 1; } } } private int n; private int Xmed; private Rectangle R[]; public Vector VPoints; public Vector VIRectangles; } class Statistic { public Statistic(int i, int ai[]) { n = i; tab = new int[n]; for(int j = 0; j < n; j++) tab[j] = ai[j]; } public int Median(int i) { int j = 0; if(n == 1) { j = tab[0]; } else { int k = tab[0]; int k1 = 0; int l1 = 0; for(int l = 0; l < n; l++) { if(tab[l] < k) k1++; if(tab[l] > k) l1++; } if(i <= k1) { int ai[] = new int[k1]; k1 = 0; for(int i1 = 0; i1 < n; i1++) if(tab[i1] < k) ai[k1++] = tab[i1]; Statistic statistic = new Statistic(k1, ai); j = statistic.Median(i); } if(i > n - l1) { int ai1[] = new int[l1]; l1 = 0; for(int j1 = 0; j1 < n; j1++) if(tab[j1] > k) ai1[l1++] = tab[j1]; Statistic statistic1 = new Statistic(l1, ai1); j = statistic1.Median((i - n) + l1); } if(i > k1 && i <= n - l1) j = k; } return j; } private int tab[]; private int n; }