PageRenderTime 7ms CodeModel.GetById 61ms app.highlight 480ms RepoModel.GetById 15ms app.codeStats 2ms

/batik/src/main/java/org/apache/batik/ext/awt/geom/RectListManager.java

https://github.com/rmuller/xmlgraphics-mavenized
Java | 1017 lines | 673 code | 117 blank | 227 comment | 236 complexity | 0c25ea0295f16a5c7d137a7214e17d60 MD5 | raw file
   1/*
   2
   3   Licensed to the Apache Software Foundation (ASF) under one or more
   4   contributor license agreements.  See the NOTICE file distributed with
   5   this work for additional information regarding copyright ownership.
   6   The ASF licenses this file to You under the Apache License, Version 2.0
   7   (the "License"); you may not use this file except in compliance with
   8   the License.  You may obtain a copy of the License at
   9
  10       http://www.apache.org/licenses/LICENSE-2.0
  11
  12   Unless required by applicable law or agreed to in writing, software
  13   distributed under the License is distributed on an "AS IS" BASIS,
  14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15   See the License for the specific language governing permissions and
  16   limitations under the License.
  17
  18 */
  19package org.apache.batik.ext.awt.geom;
  20
  21import java.awt.Rectangle;
  22import java.io.Serializable;
  23import java.util.Arrays;
  24import java.util.Collection;
  25import java.util.Comparator;
  26import java.util.Iterator;
  27import java.util.ListIterator;
  28import java.util.NoSuchElementException;
  29
  30/**
  31 * RectListManager is a class to manage a list of rectangular regions.
  32 * This class contains methods to add new rectangles to the List, to
  33 * merge rectangles in the list (based on a cost function), and
  34 * functions to subract one RectListManager from another.  The main
  35 * purpose of this class is to manage dirty regions on a display (for
  36 * this reason it uses Rectangle not Rectangle2D).
  37 *
  38 * @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
  39 * @version $Id: RectListManager.java 1372129 2012-08-12 15:31:50Z helder $
  40 */
  41public class RectListManager implements Collection {
  42    Rectangle [] rects = null;
  43    int size = 0;
  44
  45    Rectangle bounds = null;
  46
  47    public void dump() {
  48        System.err.println("RLM: " + this + " Sz: " + size);
  49        System.err.println("Bounds: " + getBounds());
  50        for (int i=0; i<size; i++) {
  51            Rectangle r = rects[i];
  52            System.err.println("  [" + r.x + ", " + r.y + ", " +
  53                               r.width + ", " + r.height + ']' );
  54        }
  55    }
  56
  57    /**
  58     * The comparator used to sort the elements of this List.
  59     * Sorts on x value of Rectangle.
  60     */
  61    public static Comparator comparator = new RectXComparator();
  62
  63    /**
  64     * Construct a <code>RectListManager</code> from a Collection of Rectangles
  65     * @param rects Collection that must only contain rectangles.
  66     */
  67    public RectListManager(Collection rects) {
  68        this.rects = new Rectangle[rects.size()];
  69        Iterator i = rects.iterator();
  70        int j=0;
  71        while (i.hasNext())          // todo can be replaced by rects.toArray()
  72            this.rects[j++] = (Rectangle)i.next();
  73        this.size  = this.rects.length;
  74
  75
  76        Arrays.sort(this.rects, comparator);
  77    }
  78
  79    /**
  80     * Construct a <code>RectListManager</code> from an Array of
  81     * <code>Rectangles</code>
  82     * @param rects Array of <code>Rectangles</code>, must not contain
  83     *              any null entries.
  84     */
  85    public RectListManager(Rectangle [] rects) {
  86        this(rects, 0, rects.length);
  87    }
  88
  89    /**
  90     * Construct a <code>RectListManager</code> from an Array of
  91     * <code>Rectangles</code>
  92     * @param rects Array of <code>Rectangles</code>, must not contain
  93     *              any null entries in the range [off, off+sz-1].
  94     * @param off   The offset to start copying from in rects.
  95     * @param sz    The number of entries to copy from rects.
  96     */
  97    public RectListManager(Rectangle [] rects, int off, int sz) {
  98        this.size  = sz;
  99        this.rects = new Rectangle[sz];
 100        System.arraycopy(rects, off, this.rects, 0, sz);
 101        Arrays.sort(this.rects, comparator);
 102    }
 103
 104    /**
 105     * Construct a <code>RectListManager</code> from another
 106     * <code>RectListManager</code> (data is copied).
 107     * @param rlm RectListManager to copy.
 108     */
 109    public RectListManager(RectListManager rlm) {
 110        this(rlm.rects);
 111    }
 112
 113    /**
 114     * Construct a <code>RectListManager</code> with one rectangle
 115     * @param rect The rectangle to put in this rlm.
 116     */
 117    public RectListManager(Rectangle rect) {
 118        this();
 119        add(rect);
 120    }
 121
 122
 123    /**
 124     * Construct an initially empty <code>RectListManager</code>.
 125     */
 126    public RectListManager() {
 127        this.rects = new Rectangle[10];
 128        size = 0;
 129    }
 130
 131    /**
 132     * Construct an initially empty <code>RectListManager</code>,
 133     * with initial <code>capacity</code>.
 134     * @param capacity The inital capacity for the list.  Setting
 135     *                 this appropriately can save reallocations.
 136     */
 137    public RectListManager(int capacity) {
 138        this.rects = new Rectangle[capacity];
 139    }
 140
 141    public Rectangle getBounds() {
 142        if (bounds != null )
 143            return bounds;
 144        if (size == 0) return null;
 145        bounds = new Rectangle(rects[0]);
 146        for (int i=1; i< size; i++) {
 147            Rectangle r = rects[i];
 148            if (r.x < bounds.x) {
 149                bounds.width = bounds.x+bounds.width-r.x;
 150                bounds.x = r.x;
 151            }
 152            if (r.y < bounds.y) {
 153                bounds.height = bounds.y+bounds.height-r.y;
 154                bounds.y = r.y;
 155            }
 156            if (r.x+r.width > bounds.x+bounds.width)
 157                bounds.width = r.x+r.width-bounds.x;
 158            if (r.y+r.height > bounds.y+bounds.height)
 159                bounds.height = r.y+r.height-bounds.y;
 160        }
 161        return bounds;
 162    }
 163
 164    /**
 165     * Standard <code>Object</code> clone method.
 166     */
 167    public Object clone() throws CloneNotSupportedException {
 168        return copy();
 169    }
 170
 171    /**
 172     * Similar to clone only strongly typed
 173     * TODO Java 5: The use of covariant return types on clone() can eliminate
 174     * this method.
 175     */
 176    public RectListManager copy() {
 177        return new RectListManager(rects);
 178    }
 179
 180    /**
 181     * Returns the number of elements currently stored in this collection.
 182     */
 183    public int size() { return size; }
 184
 185
 186    /**
 187     * Returns true if this collection contains no elements.
 188     */
 189    public boolean isEmpty() { return (size==0); }
 190
 191    public void clear() {
 192        Arrays.fill( rects, null );
 193        size=0;
 194        bounds = null;
 195    }
 196
 197    /**
 198     * Returns an iterator over the elements in this collection
 199     */
 200    public Iterator iterator() {
 201        return new RLMIterator();
 202    }
 203
 204    /**
 205     * Returns a list iterator of the elements in this list
 206     * (in proper sequence).
 207     */
 208    public ListIterator listIterator() {
 209        return new RLMIterator();
 210    }
 211
 212    public Object [] toArray() {
 213        Object [] ret = new Rectangle[size];
 214        System.arraycopy(rects, 0, ret, 0, size);
 215        return ret;
 216    }
 217
 218    /**
 219     * fill the given array a with values from my internal <code>rects</code>.
 220     * when a is not large enough, a new array is allocated, filled and returned.
 221     * the method works only, when a is a Object[] or a Rectange[].
 222     * When this is not the case, the a[] is just cleared.
 223     *
 224     * @param a array to fill (must not be null!)
 225     * @return the content of rects, either in a[] or a fresh array.
 226     */
 227    public Object [] toArray(Object[] a) {
 228        Class t = a.getClass().getComponentType();
 229        if ((t != Object.class) &&
 230            (t != Rectangle.class)) {
 231            // Nothing here for it...
 232            Arrays.fill( a, null );
 233            return a;
 234        }
 235
 236        if (a.length < size)
 237            a = new Rectangle[size];
 238        System.arraycopy(rects, 0, a, 0, size);
 239        Arrays.fill( a, size, a.length, null );
 240
 241        return a;
 242    }
 243
 244    public boolean add(Object o) {
 245        add((Rectangle)o);
 246        return true;
 247    }
 248
 249    /**
 250     * Ensures that this collection contains the specified element
 251     * @param rect The rectangle to add
 252     */
 253    public void add(Rectangle rect) {
 254        add(rect, 0, size-1);
 255    }
 256
 257    /**
 258     * Ensures that this collection contains the specified element
 259     * l is the lower bound index for insertion r is upper
 260     * bound index for insertion.
 261     * @param rect The rectangle to add
 262     * @param l the lowest possible index for a rect with
 263     *          greater 'x' coord.
 264     * @param r the highest possible index for a rect with
 265     *          greater 'x' coord.
 266     */
 267    protected void add(Rectangle rect, int l, int r) {
 268        ensureCapacity(size+1);
 269        int idx=l;
 270        while (l <= r) {
 271            idx = (l+r)/2;
 272            while ((rects[idx] == null) && (idx <r)) idx++;
 273            if (rects[idx] == null) {
 274                // All 'null' from center to r so skip them
 275                r = (l+r)/2;
 276                idx = (l+r)/2;
 277                if (l>r)
 278                    idx=l;
 279                while ((rects[idx] == null) && (idx > l)) idx--;
 280                if (rects[idx] == null) {
 281                    rects[idx] = rect;
 282                    return;
 283                }
 284            }
 285            if (rect.x == rects[idx].x) break;
 286            if (rect.x <  rects[idx].x) {
 287                if (idx == 0) break;
 288                if ((rects[idx-1] != null) &&
 289                    (rect.x >= rects[idx-1].x)) break;
 290                r = idx-1;
 291            } else {
 292                if (idx == size-1)  {idx++; break; }
 293                if ((rects[idx+1] != null) &&
 294                    (rect.x <= rects[idx+1].x)) { idx++; break;}
 295                l = idx+1;
 296            }
 297        }
 298
 299        if (idx < size) {
 300            System.arraycopy(rects, idx,
 301                             rects, idx+1, size-idx);
 302        }
 303
 304        // if (idx!=0) System.out.print(rects[idx-1].x);
 305        // else System.out.print("[First]");
 306        // System.out.print(" " + rect.x + " ");
 307        // if (idx<size) System.out.print(rects[idx+1].x);
 308        // else System.out.print("[last]");
 309        // System.out.println("");
 310
 311        rects[idx] = rect;
 312        size++;
 313        bounds=null;
 314    }
 315
 316    public boolean addAll(Collection c) {
 317        if (c instanceof RectListManager) {
 318            add((RectListManager)c);
 319        } else {
 320            add(new RectListManager(c));
 321        }
 322
 323        return (c.size() != 0);
 324    }
 325
 326    public boolean contains(Object o) {
 327        Rectangle rect = (Rectangle)o;
 328        int l=0, r=size-1, idx=0;
 329        while (l <= r) {
 330            idx = (l+r) >>> 1;
 331            if (rect.x == rects[idx].x) break;
 332            if (rect.x <  rects[idx].x) {
 333                if (idx == 0) break;
 334                if (rect.x >= rects[idx-1].x) break;
 335                r = idx-1;
 336            } else {
 337                if (idx == size-1)  {idx++; break; }
 338                if (rect.x <= rects[idx+1].x) { idx++; break;}
 339                l = idx+1;
 340            }
 341        }
 342        // Didn't find any rect with the same x value.
 343        if (rects[idx].x != rect.x) return false;
 344
 345        // Search towards 0 from idx for rect that matches
 346        for (int i=idx; i>=0; i--){
 347            if (rects[idx].equals(rect)) return true;
 348            if (rects[idx].x != rect.x)  break;
 349        }
 350
 351        // Search towards size from idx for rect that matches
 352        for (int i=idx+1; i<size; i++) {
 353            if (rects[idx].equals(rect)) return true;
 354            if (rects[idx].x != rect.x)  break;
 355        }
 356
 357        // No match...
 358        return false;
 359    }
 360
 361    /**
 362     * Returns true if this collection contains all of the elements in
 363     * the specified collection.
 364     */
 365    public boolean containsAll(Collection c) {
 366        if (c instanceof RectListManager)
 367            return containsAll((RectListManager)c);
 368        return containsAll(new RectListManager(c));
 369    }
 370
 371    public boolean containsAll(RectListManager rlm) {
 372        int x, xChange = 0;
 373        for (int j=0, i=0; j<rlm.size; j++) {
 374            i=xChange;
 375            while(rects[i].x < rlm.rects[j].x) {
 376                i++;
 377                if (i == size) return false;
 378            }
 379            xChange = i;
 380            x = rects[i].x;
 381            while (!rlm.rects[j].equals(rects[i])) {
 382                i++;
 383                if (i == size) return false; // out of rects
 384                if (x != rects[i].x)
 385                    return false; // out of the zone.
 386            }
 387        }
 388        return true;
 389    }
 390
 391    /**
 392     * Removes a single instance of the specified element from this
 393     * collection, if it is present.
 394     * @param o Object to remove an matching instance of.
 395     */
 396    public boolean remove(Object o) {
 397        return remove((Rectangle)o);
 398    }
 399
 400    /**
 401     * Removes a single instance of the specified Rectangle from this
 402     * collection, if it is present.
 403     * @param rect Rectangle to remove an matching instance of.
 404     */
 405    public boolean remove(Rectangle rect) {
 406        int l=0, r=size-1, idx=0;
 407        while (l <= r) {
 408            idx = (l+r) >>> 1;
 409            if (rect.x == rects[idx].x) break;
 410            if (rect.x <  rects[idx].x) {
 411                if (idx == 0) break;
 412                if (rect.x >= rects[idx-1].x) break;
 413                r = idx-1;
 414            } else {
 415                if (idx == size-1)  {idx++; break; }
 416                if (rect.x <= rects[idx+1].x) { idx++; break;}
 417                l = idx+1;
 418            }
 419        }
 420        // Didn't find any rect with the same x value.
 421        if (rects[idx].x != rect.x) return false;
 422
 423        // Search towards 0 from idx for rect that matches
 424        for (int i=idx; i>=0; i--){
 425            if (rects[idx].equals(rect)) {
 426                System.arraycopy(rects, idx+1, rects, idx, size-idx);
 427                size--;
 428                bounds = null;
 429                return true;
 430            }
 431            if (rects[idx].x != rect.x)  break;
 432        }
 433
 434        // Search towards size from idx for rect that matches
 435        for (int i=idx+1; i<size; i++) {
 436            if (rects[idx].equals(rect)) {
 437                System.arraycopy(rects, idx+1, rects, idx, size-idx);
 438                size--;
 439                bounds = null;
 440                return true;
 441            }
 442            if (rects[idx].x != rect.x)  break;
 443        }
 444
 445        // No match...
 446        return false;
 447    }
 448
 449    public boolean removeAll(Collection c) {
 450        if (c instanceof RectListManager)
 451            return removeAll((RectListManager)c);
 452        return removeAll(new RectListManager(c));
 453    }
 454
 455    public boolean removeAll(RectListManager rlm) {
 456        int x, xChange = 0;
 457        boolean ret = false;
 458        for (int j=0, i=0; j<rlm.size; j++) {
 459            i=xChange;
 460            while ((rects[i] == null) ||
 461                   (rects[i].x < rlm.rects[j].x)) {
 462                i++;
 463                if (i == size) break;
 464            }
 465
 466            if (i == size) break;
 467
 468            xChange = i;
 469            x = rects[i].x;
 470            while (true) {
 471                if (rects[i] == null) {
 472                    i++;
 473                    if (i == size) break; // out of rects
 474                    continue;
 475                }
 476                if (rlm.rects[j].equals(rects[i])) {
 477                    rects[i] = null;
 478                    ret = true;
 479                }
 480                i++;
 481                if (i == size)       break; // out of rects
 482                if (x != rects[i].x) break; // out of the zone.
 483            }
 484        }
 485
 486        // Now we will go through collapsing the nulled entries.
 487        if (ret) {
 488            int j=0, i=0;
 489            while (i<size) {
 490                if (rects[i] != null)
 491                    rects[j++] = rects[i];
 492                i++;
 493            }
 494            size = j;
 495            bounds = null;
 496        }
 497        return ret;
 498    }
 499
 500    public boolean retainAll(Collection c) {
 501        if (c instanceof RectListManager)
 502            return retainAll((RectListManager)c);
 503        return retainAll(new RectListManager(c));
 504    }
 505    public boolean retainAll(RectListManager rlm) {
 506        int x, xChange = 0;
 507        boolean ret = false;
 508
 509        for (int j=0, i=0; j<size; j++) {
 510            i=xChange;
 511            while (rlm.rects[i].x < rects[j].x) {
 512                i++;
 513                if (i == rlm.size) break;
 514            }
 515            if (i == rlm.size) {
 516                ret = true;
 517                // No more rects will match anything from rlm
 518                // so remove them from this RLM.
 519                for (int k=j; k<size; k++)
 520                    rects[k] = null;
 521                size = j;
 522                break;
 523            }
 524
 525            xChange = i;
 526            x = rlm.rects[i].x;
 527            while (true) {
 528                if (rects[j].equals(rlm.rects[i])) break;
 529                i++;
 530                if ((i == rlm.size) ||
 531                    (x != rlm.rects[i].x)) {
 532                    // Out of zone or rects
 533                    rects[j] = null;
 534                    ret = true;
 535                    break;
 536                }
 537            }
 538        }
 539
 540        // Now we will go through collapsing the nulled entries.
 541        if (ret) {
 542            int j=0, i=0;
 543            while (i<size) {
 544                if (rects[i] != null)
 545                    rects[j++] = rects[i];
 546                i++;
 547            }
 548            size = j;
 549            bounds = null;
 550        }
 551        return ret;
 552    }
 553
 554    /**
 555     * Adds the contents of <code>rlm</code> to this RectListManager.  No
 556     * collapsing of rectangles is done here the contents are simply
 557     * added (you should generally call 'mergeRects' some time after
 558     * this operation before using the contents of this
 559     * RectListManager.
 560     * @param rlm The RectListManager to add the contents of.  */
 561    public void add(RectListManager rlm) {
 562        if (rlm.size == 0)
 563            return;
 564
 565        Rectangle [] dst = rects;
 566        if (rects.length < (size+rlm.size)) {
 567            dst = new Rectangle[size+rlm.size];
 568        }
 569
 570        if (size == 0) {
 571            System.arraycopy(rlm.rects, 0, dst, size, rlm.size);
 572            size = rlm.size;
 573            bounds = null;
 574            return;
 575        }
 576
 577        Rectangle [] src1   = rlm.rects;
 578        int          src1Sz = rlm.size;
 579        int          src1I  = src1Sz-1;
 580
 581        Rectangle [] src2   = rects;
 582        int          src2Sz = size;
 583        int          src2I  = src2Sz-1;
 584
 585        int dstI = size+rlm.size-1;
 586        int x1 = src1[src1I].x;
 587        int x2 = src2[src2I].x;
 588
 589        while (dstI >= 0) {
 590            if (x1 <= x2) {
 591                dst[dstI] = src2[src2I];
 592                if (src2I == 0) {
 593                    System.arraycopy(src1, 0, dst, 0, src1I+1);
 594                    break;
 595                }
 596                src2I--;
 597                x2 = src2[src2I].x;
 598            } else {
 599                dst[dstI] = src1[src1I];
 600                if (src1I == 0) {
 601                    System.arraycopy(src2, 0, dst, 0, src2I+1);
 602                    break;
 603                }
 604                src1I--;
 605                x1 = src1[src1I].x;
 606            }
 607            dstI--;
 608        }
 609        rects = dst;
 610        size += rlm.size;
 611        bounds = null;
 612    }
 613
 614    public void mergeRects(int overhead, int lineOverhead) {
 615        if (size == 0) return;
 616        Rectangle r, cr;
 617        int cost1, cost2, cost3;
 618        Rectangle []splits = new Rectangle[4];
 619        for (int j, i=0; i<size; i++) {
 620            r = rects[i];
 621            if (r == null) continue;
 622            cost1 = (overhead                 +
 623                     (r.height*lineOverhead) +
 624                     (r.height*r.width));
 625            do {
 626                int maxX = r.x+r.width+overhead/r.height;
 627                for (j=i+1; j<size; j++) {
 628                    cr = rects[j];
 629                    if ((cr == null) || (cr == r)) continue;
 630                    if (cr.x >= maxX) {
 631                        // No more merges can happen.
 632                        j = size;
 633                        break;
 634                    }
 635                    cost2 = (overhead                 +
 636                             (cr.height*lineOverhead) +
 637                             (cr.height*cr.width));
 638
 639                    Rectangle mr = r.union(cr);
 640                    cost3 = (overhead                 +
 641                             (mr.height*lineOverhead) +
 642                             (mr.height*mr.width));
 643                    if (cost3 <= cost1+cost2) {
 644                        r = rects[i] = mr;
 645                        rects[j] = null;
 646                        cost1 = cost3;
 647                        j=-1;
 648                        break;
 649                    }
 650
 651                    if (!r.intersects(cr)) continue;
 652
 653                    splitRect(cr, r, splits);
 654                    int splitCost=0;
 655                    int l=0;
 656                    for (int k=0; k<4; k++) {
 657                        if (splits[k] != null) {
 658                            Rectangle sr = splits[k];
 659                            // Collapse null entries in first three
 660                            // (That share common 'x').
 661                            if (k<3) splits[l++] = sr;
 662                            splitCost += (overhead                 +
 663                                          (sr.height*lineOverhead) +
 664                                          (sr.height*sr.width));
 665                        }
 666                    }
 667                    if (splitCost >= cost2) continue;
 668
 669                    // Insert the splits.
 670                    if (l == 0) {
 671                        // only third split may be left (no common 'x').
 672                        rects[j] = null;
 673                        if (splits[3] != null)
 674                            add(splits[3], j, size-1);
 675                        continue;
 676                    }
 677
 678                    rects[j] = splits[0];
 679                    if (l > 1)
 680                        insertRects(splits, 1, j+1, l-1);
 681                    if (splits[3] != null)
 682                        add(splits[3], j, size-1);
 683                }
 684
 685                // if we merged it with another rect then
 686                // we need to check all the rects up to i again,
 687                // against the merged rect.
 688            } while (j != size);
 689        }
 690
 691        // Now we will go through collapsing the nulled entries.
 692        int j=0, i=0;
 693        float area=0;
 694        while (i<size) {
 695            if (rects[i] != null) {
 696                r = rects[i];
 697                rects[j++] = r;
 698                area += overhead + (r.height*lineOverhead) +
 699                    (r.height*r.width);
 700            }
 701            i++;
 702        }
 703        size = j;
 704        bounds=null;
 705        r = getBounds();
 706        if (r == null) return;
 707        if (overhead + (r.height*lineOverhead) + (r.height*r.width) < area) {
 708            rects[0] = r;
 709            size=1;
 710        }
 711    }
 712
 713    public void subtract(RectListManager rlm, int overhead, int lineOverhead) {
 714        Rectangle r, sr;
 715        int cost;
 716        int jMin=0;
 717        Rectangle [] splits = new Rectangle[4];
 718
 719        for(int i=0; i<size; i++) {
 720            r = rects[i]; // Canidate rect...
 721            cost = (overhead                +
 722                    (r.height*lineOverhead) +
 723                    (r.height*r.width));
 724            for (int j=jMin; j<rlm.size; j++) {
 725                sr = rlm.rects[j]; // subtraction rect.
 726
 727                // Check if the canidate rect starts after
 728                // the end of this rect in 'x' if so
 729                // go to the next one.
 730                if (sr.x+sr.width < r.x) {
 731                    // If this was jMin then increment jMin (no
 732                    // future canidate rect will intersect this rect).
 733                    if (j == jMin) jMin++;
 734                    continue;
 735                }
 736
 737                // Check if the rest of the rects from rlm are past
 738                // the end of the canidate rect.  If so we are
 739                // done with this canidate rect.
 740                if (sr.x > r.x+r.width)
 741                    break;
 742
 743                // If they don't insersect then go to next sub rect.
 744                if (!r.intersects(sr))
 745                    continue;
 746
 747                // Now we know they intersect one another lets
 748                // figure out how...
 749
 750                splitRect(r, sr, splits);
 751
 752                int splitCost=0;
 753                Rectangle tmpR;
 754                for (int k=0; k<4; k++) {
 755                    tmpR = splits[k];
 756                    if (tmpR != null)
 757                        splitCost += (overhead                   +
 758                                      (tmpR.height*lineOverhead) +
 759                                      (tmpR.height*tmpR.width));
 760                }
 761
 762                if (splitCost >= cost)
 763                    // This isn't ideal as depending on the order
 764                    // Stuff is done in we might later kill some of
 765                    // these rectangles (hence lowering the cost).
 766                    // For this reason it is probably best of the
 767                    // subtract list has been merged as this will help
 768                    // reduce the instances where this will happen.
 769                    continue;
 770
 771                // Collapse null entries in first three elements
 772                // split 0, 1, 2 (entries that share a common 'x').
 773                int l = 0;
 774                for (int k=0; k<3; k++) {
 775                    if (splits[k] != null)
 776                        splits[l++] = splits[k];
 777                }
 778
 779                // Fully covered (or only split 3 survived which we
 780                // will visit later) this canidate rect goes away.
 781                if (l==0) {
 782                    rects[i].width = 0;
 783                    // Insert the third split (if any) at the
 784                    // proper place in rects list.
 785                    if (splits[3] != null)
 786                        add(splits[3], i, size-1);
 787                    break;
 788                }
 789
 790                // Otherwise replace the canidate with the top of
 791                // the split, since it only shrunk it didn't grow,
 792                // we know that the previous subtract rects don't
 793                // intersect it.
 794                r        = splits[0];
 795                rects[i] = r;
 796                cost = (overhead                +
 797                        (r.height*lineOverhead) +
 798                        (r.height*r.width));
 799
 800                // Add the remainder of the rects that
 801                // share 'r.x' (if any).  Possible
 802                // are split 1, and split 2.
 803                if (l > 1)
 804                    insertRects(splits, 1, i+1, l-1);
 805
 806                // Insert the third split (if any) at the
 807                // proper place in rects list.
 808                if (splits[3] != null)
 809                    add(splits[3], i+l, size-1);
 810            }
 811        }
 812
 813        // Now we will go through collapsing the nulled entries.
 814        int j=0, i=0;
 815        while (i<size) {
 816            if (rects[i].width == 0)
 817                rects[i] = null;
 818            else
 819                rects[j++] = rects[i];
 820            i++;
 821        }
 822        size = j;
 823        bounds = null;
 824    }
 825
 826    protected void splitRect(Rectangle r, Rectangle sr,
 827                             Rectangle []splits) {
 828        // We split the canidate rectrect into four parts.  In
 829        // many cases one or more of these will be empty.
 830        //
 831        //    +-------------------------------------+ ry0
 832        //    |                                     |
 833        //    |                                     |
 834        //    |          Split 0                    |
 835        //    |                                     |
 836        //    |                                     |
 837        // ------------+-----------------+--------------- sry0
 838        //    |        |                 |          |
 839        //    | Split2 |   subtracted    | Split 3  |
 840        //    |        |   rect          |          |
 841        //    |        |                 |          |
 842        // ------------+-----------------+--------------- sry1
 843        //    |       srx0              srx1        |
 844        //    |                                     |
 845        //    |          Split 1                    |
 846        //    |                                     |
 847        //    +-------------------------------------+ ry1
 848        //   rx0                                   rx1
 849
 850        int rx0 = r.x;
 851        int rx1 = rx0+r.width-1;
 852        int ry0 = r.y;
 853        int ry1 = ry0+r.height-1;
 854
 855        int srx0 = sr.x;
 856        int srx1 = srx0+sr.width-1;
 857        int sry0 = sr.y;
 858        int sry1 = sry0+sr.height-1;
 859
 860        if ((ry0 < sry0) && (ry1 >= sry0)) {
 861            splits[0] = new Rectangle(rx0, ry0, r.width, sry0-ry0);
 862            ry0 = sry0;
 863        } else {
 864            splits[0] = null;
 865        }
 866
 867        if ((ry0 <= sry1) && (ry1 > sry1)) {
 868            splits[1] = new Rectangle(rx0, sry1+1, r.width, ry1-sry1);
 869            ry1 = sry1;
 870        } else {
 871            splits[1] = null;
 872        }
 873
 874        if ((rx0 < srx0) && (rx1 >= srx0)) {
 875            splits[2] = new Rectangle(rx0, ry0, srx0-rx0, ry1-ry0+1);
 876        } else {
 877            splits[2] = null;
 878        }
 879
 880        if ((rx0 <= srx1) && (rx1 > srx1)) {
 881            splits[3]= new Rectangle(srx1+1, ry0, rx1-srx1, ry1-ry0+1);
 882        } else {
 883            splits[3] = null;
 884        }
 885    }
 886
 887    protected void insertRects(Rectangle[] rects, int srcPos,
 888                               int dstPos, int len) {
 889        if (len == 0) return;
 890
 891        // Make sure we have room.
 892        ensureCapacity(size+len);
 893
 894        // Move everything after pos up...
 895        for (int i=size-1; i>=dstPos; i--)
 896            this.rects[i+len] = this.rects[i];
 897
 898        // Put the new rects in.
 899        System.arraycopy( rects, srcPos, this.rects, dstPos, len );
 900
 901        size += len;
 902    }
 903
 904    public void ensureCapacity(int sz) {
 905        if (sz <= rects.length)
 906            return;
 907        int nSz = rects.length + (rects.length>>1) + 1;
 908        while (nSz < sz)
 909            nSz+=(nSz>>1)+1;
 910
 911        Rectangle [] nRects = new Rectangle[nSz];
 912        System.arraycopy(rects, 0, nRects, 0, size);
 913
 914        rects = nRects;
 915    }
 916
 917    /**
 918     * Comparator for ordering rects in X.
 919     *
 920     * Note: this comparator imposes orderings that are inconsistent
 921     *       with equals.
 922     */
 923    private static class RectXComparator implements Comparator, Serializable {
 924
 925        RectXComparator() { }
 926
 927        public final int compare(Object o1, Object o2) {
 928            return ((Rectangle)o1).x-((Rectangle)o2).x;
 929        }
 930    }
 931
 932
 933    private class RLMIterator implements ListIterator {
 934        int idx = 0;
 935        boolean removeOk = false;
 936        boolean forward  = true;
 937        RLMIterator() { }
 938
 939        public boolean hasNext() { return idx < size; }
 940        public int nextIndex() { return idx; }
 941        public Object next() {
 942            if (idx >= size)
 943                throw new NoSuchElementException("No Next Element");
 944            forward = true;
 945            removeOk = true;
 946            return rects[idx++];
 947        }
 948
 949        public boolean hasPrevious() { return idx > 0; }
 950        public int previousIndex() { return idx-1; }
 951        public Object previous() {
 952            if (idx <= 0)
 953                throw new NoSuchElementException("No Previous Element");
 954            forward = false;
 955            removeOk = true;
 956            return rects[--idx];
 957        }
 958
 959        public void remove() {
 960            if (!removeOk)
 961                throw new IllegalStateException
 962                    ("remove can only be called directly after next/previous");
 963
 964            if (forward) idx--;
 965            if (idx != size-1)
 966                System.arraycopy(rects, idx+1, rects, idx, size-(idx+1));
 967            size--;
 968            rects[size] = null;
 969            removeOk = false;
 970        }
 971
 972
 973        public void set(Object o) {
 974            Rectangle r = (Rectangle)o;
 975
 976            if (!removeOk)
 977                throw new IllegalStateException
 978                    ("set can only be called directly after next/previous");
 979
 980            if (forward) idx--;
 981
 982            if (idx+1<size) {
 983                if (rects[idx+1].x < r.x)
 984                    throw new UnsupportedOperationException
 985                        ("RectListManager entries must be sorted");
 986            }
 987            if (idx>=0) {
 988                if (rects[idx-1].x > r.x)
 989                    throw new UnsupportedOperationException
 990                        ("RectListManager entries must be sorted");
 991            }
 992
 993            rects[idx] = r;
 994            removeOk = false;
 995        }
 996
 997        public void add(Object o) {
 998            Rectangle r = (Rectangle)o;
 999            if (idx<size) {
1000                if (rects[idx].x < r.x)
1001                    throw new UnsupportedOperationException
1002                        ("RectListManager entries must be sorted");
1003            }
1004            if (idx!=0) {
1005                if (rects[idx-1].x > r.x)
1006                    throw new UnsupportedOperationException
1007                        ("RectListManager entries must be sorted");
1008            }
1009            ensureCapacity(size+1);
1010            if (idx != size)
1011                System.arraycopy(rects, idx, rects, idx+1, size-idx);
1012            rects[idx] = r;
1013            idx++;
1014            removeOk = false;
1015        }
1016    }
1017}