Generated on Thu Nov 2 14:49:35 2006 for Gecode/J by doxygen 1.5.0

ShapeList.java

Go to the documentation of this file.
00001 /* -*- indent-tabs-mode: nil -*- */
00002 /*
00003  *  Main authors:
00004  *     Marco Kuhlmann <kuhlmann@ps.uni-sb.de>
00005  *
00006  *  Copyright:
00007  *     Marco Kuhlmann, 2005
00008  *
00009  *  Last modified:
00010  *     $Date: 2006-10-26 11:31:58 +0200 (Thu, 26 Oct 2006) $ by $Author: tack $
00011  *     $Revision: 3796 $
00012  *
00013  *  This file is part of Gecode, the generic constraint
00014  *  development environment:
00015  *     http://www.gecode.org
00016  *
00017  *  See the file "LICENSE" for information on usage and
00018  *  redistribution of this file, and for a
00019  *     DISCLAIMER OF ALL WARRANTIES.
00020  *
00021  */
00022 
00023 package org.gecode.gist;
00024 
00025 import java.util.*;
00026 
00027 
00028 public class ShapeList {
00029     
00030     // list of the individual shapes
00031     private ArrayList shapes;
00032     
00033     // minimal separation between the rightmost extent of a left shape and the
00034     // leftmost extent of a right shape
00035     private int minimalSeparation;
00036     
00037     // whether a merge operation needs to be performed
00038     private boolean needsMerge;
00039     
00040     // the merged shape
00041     private Shape mergedShape;
00042     
00043     // list of the offsets, relative to the axis of the merged shape
00044     private ArrayList offsetList;
00045     
00046     // constructor
00047     public ShapeList(int theMinimalSeparation) {
00048         this.minimalSeparation = theMinimalSeparation;
00049         this.shapes = new ArrayList();
00050         this.needsMerge = false;
00051     }
00052     
00053     public ShapeList(Collection theShapes, int theMinimalSeparation) {
00054         this.minimalSeparation = theMinimalSeparation;
00055         this.shapes = new ArrayList();
00056         Iterator theShapesIterator = theShapes.iterator();
00057         while (theShapesIterator.hasNext()) {
00058             Shape nextShape = (Shape) theShapesIterator.next();
00059             shapes.add(nextShape);
00060         }
00061         this.needsMerge = true;
00062     }
00063     
00064     // add a new shape to the list of shapes
00065     public void add(Shape theShape) {
00066         shapes.add(theShape);
00067         needsMerge = true;
00068     }
00069     
00070     // -------------------------------------------------------------------------
00071     // methods
00072     // -------------------------------------------------------------------------
00073     
00074     // Return the minimal distance between the axes of shape1 and shape2 that
00075     // ensures that the rightmost extent of shape1 and the leftmost extent of
00076     // shape2 obey the minimal separation.
00077     private int getAlpha(Shape shape1, Shape shape2) {
00078         int alpha = minimalSeparation;
00079         int extentR = 0;
00080         int extentL = 0;
00081         Iterator extentIterator1 = shape1.iterator();
00082         Iterator extentIterator2 = shape2.iterator();
00083         while (extentIterator1.hasNext() && extentIterator2.hasNext()) {
00084             extentR += ((Extent) extentIterator1.next()).extentR;
00085             extentL += ((Extent) extentIterator2.next()).extentL;
00086             alpha = Math.max(alpha, extentR - extentL + minimalSeparation);
00087         }
00088         return alpha;
00089     }
00090     
00091     // Merge shape1 and shape2 into a new shape.  The axes of the two shapes
00092     // are assumed to be alpha units apart.  The merged shape will have the
00093     // same axis as shape1; this fact makes this operation asymmetric.
00094     private static Shape merge(Shape shape1, Shape shape2, int alpha) {
00095         if (shape1.depth() == 0) {
00096             return shape2;
00097         } else if (shape2.depth() == 0) {
00098             return shape1;
00099         } else {
00100             Shape result = new Shape();
00101             Iterator extentIterator1 = shape1.iterator();
00102             Iterator extentIterator2 = shape2.iterator();
00103             Extent currentExtent1 = (Extent) extentIterator1.next();
00104             Extent currentExtent2 = (Extent) extentIterator2.next();
00105             
00106             // Extend the topmost right extent by alpha.  This, in effect,
00107             // moves the second shape to the right by alpha units.
00108             int topmostL = currentExtent1.extentL;
00109             int topmostR = currentExtent2.extentR;
00110             Extent topmostExtent = new Extent(topmostL, topmostR);
00111             topmostExtent.extend(0, alpha);
00112             result.add(topmostExtent);
00113             
00114             // Now, since extents are given in relative units, in order to
00115             // compute the extents of the merged shape, we can just collect the
00116             // extents of shape1 and shape2, until one of the shapes ends.  If
00117             // this happens, we need to "back-off" to the axis of the deeper
00118             // shape in order to properly determine the remaining extents.
00119             int backoffTo1 =
00120                 currentExtent1.extentR - alpha - currentExtent2.extentR;
00121             int backoffTo2 =
00122                 currentExtent2.extentL + alpha - currentExtent1.extentL;
00123             while (extentIterator1.hasNext() && extentIterator2.hasNext()) {
00124                 currentExtent1 = (Extent) extentIterator1.next();
00125                 currentExtent2 = (Extent) extentIterator2.next();
00126                 int newExtentL = currentExtent1.extentL;
00127                 int newExtentR = currentExtent2.extentR;
00128                 Extent newExtent = new Extent(newExtentL, newExtentR);
00129                 result.add(newExtent);
00130                 backoffTo1 += currentExtent1.extentR - currentExtent2.extentR;
00131                 backoffTo2 += currentExtent2.extentL - currentExtent1.extentL;
00132             }
00133             
00134             // If shape1 is deeper than shape2, back off to the axis of shape1,
00135             // and process the remaining extents of shape1.
00136             if (extentIterator1.hasNext()) {
00137                 currentExtent1 = (Extent) extentIterator1.next();
00138                 int newExtentL = currentExtent1.extentL;
00139                 int newExtentR = currentExtent1.extentR;
00140                 Extent newExtent = new Extent(newExtentL, newExtentR);
00141                 newExtent.extend(0, backoffTo1);
00142                 result.add(newExtent);
00143                 while (extentIterator1.hasNext()) {
00144                     currentExtent1 = (Extent) extentIterator1.next();
00145                     result.add(currentExtent1);
00146                 }
00147             }
00148             
00149             // Vice versa, if shape2 is deeper than shape1, back off to the
00150             // axis of shape2, and process the remaining extents of shape2.
00151             if (extentIterator2.hasNext()) {
00152                 currentExtent2 = (Extent) extentIterator2.next();
00153                 int newExtentL = currentExtent2.extentL;
00154                 int newExtentR = currentExtent2.extentR;
00155                 Extent newExtent = new Extent(newExtentL, newExtentR);
00156                 newExtent.extend(backoffTo2, 0);
00157                 result.add(newExtent);
00158                 while (extentIterator2.hasNext()) {
00159                     currentExtent2 = (Extent) extentIterator2.next();
00160                     result.add(currentExtent2);
00161                 }
00162             }
00163             
00164             return result;
00165         }
00166     }
00167     
00168     // Compute the shape that results from merging all the shapes in the shape
00169     // list and centering its axis between the axis of the leftmost and the
00170     // axis of the rightmost shape.  This method also computes the offset list,
00171     // which determines the amount of space that each shape in the list needs
00172     // to be shifted in order to be properly aligned with respect to the axis
00173     // of the merged shape, where properly is defined as in Kennedy's paper.
00174     private void merge() {
00175         int numberOfShapes = shapes.size();
00176         if (numberOfShapes == 1) {
00177             mergedShape = (Shape) shapes.get(0);
00178             offsetList = new ArrayList();
00179             offsetList.add(new Integer(0));
00180         } else {
00181             // alphaL[] and alphaR[] store the necessary distances between the
00182             // axes of the shapes in the list: alphaL[i] gives the distance
00183             // between shape[i] and shape[i-1], when shape[i-1] and shape[i]
00184             // are merged left-to-right; alphaR[i] gives the distance between
00185             // shape[i] and shape[i+1], when shape[i] and shape[i+1] are merged
00186             // right-to-left.
00187             int[] alphaL = new int[numberOfShapes];
00188             int[] alphaR = new int[numberOfShapes];
00189             
00190             // distance between the leftmost and the rightmost axis in the list
00191             int width = 0;
00192             
00193             Shape currentShapeL = (Shape) shapes.get(0);
00194             Shape currentShapeR = (Shape) shapes.get(numberOfShapes - 1);
00195             for (int i = 1; i < numberOfShapes; i++) {
00196                 // Merge left-to-right.  Note that due to the asymmetry of the
00197                 // merge operation, nextAlphaL is the distance between the
00198                 // *leftmost* axis in the shape list, and the axis of
00199                 // nextShapeL; what we are really interested in is the distance
00200                 // between the *previous* axis and the axis of nextShapeL.
00201                 // This explains the correction.
00202                 Shape nextShapeL = (Shape) shapes.get(i);
00203                 int nextAlphaL = getAlpha(currentShapeL, nextShapeL);
00204                 currentShapeL = merge(currentShapeL, nextShapeL, nextAlphaL);
00205                 alphaL[i] = nextAlphaL - width;
00206                 width = nextAlphaL;
00207                 
00208                 // Merge right-to-left.  Here, a correction of nextAlphaR is
00209                 // not required.
00210                 Shape nextShapeR = (Shape) shapes.get(numberOfShapes - 1 - i);
00211                 int nextAlphaR = getAlpha(nextShapeR, currentShapeR);
00212                 currentShapeR = merge(nextShapeR, currentShapeR, nextAlphaR);
00213                 alphaR[numberOfShapes - i] = nextAlphaR;
00214             }
00215             
00216             // The merged shape for the shape list is the last shape from any
00217             // of the merge directions; here, we pick currentShapeR.
00218             mergedShape = currentShapeR;
00219             
00220             // After the loop, the merged shape has the same axis as the
00221             // leftmost shape in the list.  What we want is to move the axis
00222             // such that it is the center of the axis of the leftmost shape in
00223             // the list and the axis of the rightmost shape.
00224             int halfWidth = width / 2;
00225             mergedShape.move(- halfWidth);
00226             
00227             // Finally, for the offset lists.  Now that the axis of the merged
00228             // shape is at the center of the two extreme axes, the first shape
00229             // needs to be offset by -halfWidth units with respect to the new
00230             // axis.  As for the offsets for the other shapes, we take the
00231             // median of the alphaL and alphaR values, as suggested in
00232             // Kennedy's paper.
00233             int offset = - halfWidth;
00234             offsetList = new ArrayList(numberOfShapes);
00235             offsetList.add(new Integer(offset));
00236             for (int i = 1; i < numberOfShapes; i++) {
00237                 offset += (alphaL[i] + alphaR[i]) / 2;
00238                 offsetList.add(new Integer(offset));
00239             }
00240         }
00241         needsMerge = false;
00242     }
00243     
00244     // accessor for the merged shape
00245     public Shape getMergedShape() {
00246         if (needsMerge) {
00247             merge();
00248         }
00249         return mergedShape;
00250     }
00251     
00252     // return an iterator for the offset list
00253     public Iterator offsetIterator() {
00254         if (needsMerge) {
00255             merge();
00256         }
00257         return offsetList.iterator();
00258     }
00259     
00260 }