/**
  * Computes and plots correlation functions
  *
  * @param tauMax is the maximum time for correlation functions
  */
 public void computeCorrelation(int tauMax) {
   plotFrame.clearData();
   double energyAccumulator = 0, magnetizationAccumulator = 0;
   double energySquaredAccumulator = 0, magnetizationSquaredAccumulator = 0;
   for (int t = 0; t < numberOfPoints; t++) {
     energyAccumulator += energy[t];
     magnetizationAccumulator += magnetization[t];
     energySquaredAccumulator += energy[t] * energy[t];
     magnetizationSquaredAccumulator += magnetization[t] * magnetization[t];
   }
   double averageEnergySquared = Math.pow(energyAccumulator / numberOfPoints, 2);
   double averageMagnetizationSquared = Math.pow(magnetizationAccumulator / numberOfPoints, 2);
   // compute normalization factors
   double normE = (energySquaredAccumulator / numberOfPoints) - averageEnergySquared;
   double normM = (magnetizationSquaredAccumulator / numberOfPoints) - averageMagnetizationSquared;
   for (int tau = 1; tau <= tauMax; tau++) {
     double c_MAccumulator = 0;
     double c_EAccumulator = 0;
     int counter = 0;
     for (int t = 0; t < numberOfPoints - tau; t++) {
       c_MAccumulator += magnetization[t] * magnetization[t + tau];
       c_EAccumulator += energy[t] * energy[t + tau];
       counter++;
     }
     // correlation function defined so that c(0) = 1 and c(infinity) -> 0
     plotFrame.append(0, tau, ((c_MAccumulator / counter) - averageMagnetizationSquared) / normM);
     plotFrame.append(1, tau, ((c_EAccumulator / counter) - averageEnergySquared) / normE);
   }
   plotFrame.setVisible(true);
 }
예제 #2
0
 /** initializes site lattice with single particle clusters randomly placed on the lattice */
 public void initialize() {
   site = new int[L][L];
   for (int i = 0; i < L; i++) {
     for (int j = 0; j < L; j++) {
       site[i][j] = -1; // site not occupied
     }
   }
   x = new int[numberOfParticles];
   y = new int[numberOfParticles];
   firstParticle = new int[numberOfParticles];
   nextParticle = new int[numberOfParticles];
   lastParticle = new int[numberOfParticles];
   mass = new int[numberOfParticles + 1];
   numberOfClusters = 0;
   for (int i = 0; i < numberOfParticles; i++) {
     do {
       x[i] = (int) (Math.random() * L);
       y[i] = (int) (Math.random() * L);
     } while (site[x[i]][y[i]] != -1);
     site[x[i]][y[i]] = numberOfClusters;
     firstParticle[numberOfClusters] = i;
     mass[numberOfClusters] = 1;
     nextParticle[i] = -1; // no more particles in cluster
     lastParticle[numberOfClusters] = i;
     numberOfClusters++;
     checkNeighbors(x[i], y[i]);
   }
 }
예제 #3
0
 /**
  * Constructs OscillatorsMode with the given mode and number of particles.
  *
  * <p>The particle separation is one in this model.
  *
  * @param mode int
  * @param N int
  */
 OscillatorsMode(int mode, int N) {
   amplitude = Math.sqrt(2.0 / (N + 1));
   omega = 2 * Math.sqrt(OMEGA_SQUARED) * Math.abs(Math.sin(mode * Math.PI / N / 2));
   wavenumber = Math.PI * mode / (N + 1);
   functionDrawer = new FunctionDrawer(this);
   functionDrawer.initialize(0, N + 1, 300, false); // draws the initial displacement
   functionDrawer.color = Color.LIGHT_GRAY;
 }
예제 #4
0
 /** Draws clusters */
 public void draw(DrawingPanel panel, Graphics g) {
   if (site == null) {
     return;
   }
   int sizeX = Math.abs(panel.xToPix(1.0) - panel.xToPix(0));
   int sizeY = Math.abs(panel.yToPix(1.0) - panel.yToPix(0));
   for (int i = 0; i < numberOfParticles; i++) {
     int xpix = panel.xToPix(x[i]) - sizeX;
     int ypix = panel.yToPix(y[i]) - sizeY;
     g.fillRect(xpix + sizeX / 2, ypix + sizeY / 2, sizeX, sizeY);
   }
 }
예제 #5
0
 /**
  * Overrides TPoint showCoordinates method.
  *
  * @param vidPanel the video panel
  */
 public void showCoordinates(VideoPanel vidPanel) {
   // put values into pointmass x and y fields
   Point2D p = getWorldPosition(vidPanel);
   track.xField.setValue(p.getX());
   track.yField.setValue(p.getY());
   track.magField.setValue(p.distance(0, 0));
   double theta = Math.atan2(p.getY(), p.getX());
   track.angleField.setValue(theta);
   super.showCoordinates(vidPanel);
 }
예제 #6
0
 /**
  * Gets the dimension of the lattice in pixel units.
  *
  * @param panel
  * @return the dimension
  */
 public Dimension getInterior(DrawingPanel panel) {
   float availableWidth = panel.getWidth() - panel.getLeftGutter() - panel.getRightGutter() - 1;
   float availableHeight = panel.getHeight() - panel.getTopGutter() - panel.getBottomGutter() - 1;
   scaleFactor = Math.min(availableWidth / dimension.width, availableHeight / dimension.height);
   if (scaleFactor > 1) {
     scaleFactor = 1;
     return dimension;
   }
   return new Dimension((int) (scaleFactor * ncol), (int) (scaleFactor * nrow));
 }
 public ScientificRenderer(int sigfigs) {
   sigfigs = Math.min(sigfigs, 6);
   if (format instanceof DecimalFormat) {
     String pattern = "0.0"; // $NON-NLS-1$
     for (int i = 0; i < sigfigs - 1; i++) {
       pattern += "0"; // $NON-NLS-1$
     }
     pattern += "E0"; // $NON-NLS-1$
     ((DecimalFormat) format).applyPattern(pattern);
   }
 }
 /**
  * Calculates statistical values for a data array.
  *
  * @param data the data array
  * @return the max, min, mean, SD, SE and non-NaN data count
  */
 private double[] getStatistics(double[] data) {
   double max = -Double.MAX_VALUE;
   double min = Double.MAX_VALUE;
   double sum = 0.0;
   double squareSum = 0.0;
   int count = 0;
   for (int i = 0; i < data.length; i++) {
     if (Double.isNaN(data[i])) {
       continue;
     }
     count++;
     max = Math.max(max, data[i]);
     min = Math.min(min, data[i]);
     sum += data[i];
     squareSum += data[i] * data[i];
   }
   double mean = sum / count;
   double sd = count < 2 ? Double.NaN : Math.sqrt((squareSum - count * mean * mean) / (count - 1));
   if (max == -Double.MAX_VALUE) max = Double.NaN;
   if (min == Double.MAX_VALUE) min = Double.NaN;
   return new double[] {max, min, mean, sd, sd / Math.sqrt(count), count};
 }
예제 #9
0
 /** Moves a cluster chosen at random in a random direction */
 public void step() {
   int cluster = (int) (Math.random() * numberOfClusters);
   int direction = (int) (Math.random() * 4);
   int dx = nnx[direction];
   int dy = nny[direction];
   int particle = firstParticle[cluster];
   do {
     site[x[particle]][y[particle]] = -1;
     x[particle] = PBC.position(x[particle] + dx, L);
     y[particle] = PBC.position(y[particle] + dy, L);
     particle = nextParticle[particle];
   } while (particle != -1);
   particle = firstParticle[cluster];
   do {
     site[x[particle]][y[particle]] = cluster; // labels new sites occupied by cluster
     particle = nextParticle[particle];
   } while (particle != -1);
   particle = firstParticle[cluster];
   do {
     checkNeighbors(x[particle], y[particle]); // checks for merger
     particle = nextParticle[particle];
   } while (particle != -1);
 }
예제 #10
0
 /**
  * Evaluates the displacement for an oscillator at postion x
  *
  * @param x postion along chain
  * @return the displacement
  */
 public double evaluate(double x) {
   return amplitude * Math.sin(x * wavenumber);
 }
예제 #11
0
 /**
  * Sets the default point index. This defines the index of the points array used to get the point
  * initially selected when the step is created.
  *
  * @param index the index
  */
 public void setDefaultPointIndex(int index) {
   index = Math.min(index, points.length - 1);
   defaultIndex = Math.max(0, index);
 }
 /**
  * Gets a list of Point2D objects that lie within pixels in a rectangle and along a line.
  *
  * @param searchRect the rectangle
  * @param x0 the x-component of a point on the line
  * @param y0 the y-component of a point on the line
  * @param slope the slope of the line
  * @return a list of Point2D
  */
 public ArrayList<Point2D> getSearchPoints(
     Rectangle searchRect, double x0, double y0, double theta) {
   double slope = -Math.tan(theta);
   // create line to search along
   Line2D line = new Line2D.Double();
   if (slope > LARGE_NUMBER) {
     line.setLine(x0, y0, x0, y0 + 1);
   } else if (slope < 1 / LARGE_NUMBER) {
     line.setLine(x0, y0, x0 + 1, y0);
   } else {
     line.setLine(x0, y0, x0 + 1, y0 + slope);
   }
   // create intersection points (to set line ends)
   Point2D p1 = new Point2D.Double();
   Point2D p2 = new Point2D.Double(Double.NaN, Double.NaN);
   Point2D p = p1;
   boolean foundBoth = false;
   double d = searchRect.x;
   Object[] data = getDistanceAndPointAtX(line, d);
   if (data != null) {
     p.setLocation((Point2D) data[1]);
     if (p.getY() >= searchRect.y && p.getY() <= searchRect.y + searchRect.height) {
       // line end is left edge
       p = p2;
     }
   }
   d += searchRect.width;
   data = getDistanceAndPointAtX(line, d);
   if (data != null) {
     p.setLocation((Point2D) data[1]);
     if (p.getY() >= searchRect.y && p.getY() <= searchRect.y + searchRect.height) {
       // line end is right edge
       if (p == p1) p = p2;
       else foundBoth = true;
     }
   }
   if (!foundBoth) {
     d = searchRect.y;
     data = getDistanceAndPointAtY(line, d);
     if (data != null) {
       p.setLocation((Point2D) data[1]);
       if (p.getX() >= searchRect.x && p.getX() <= searchRect.x + searchRect.width) {
         // line end is top edge
         if (p == p1) p = p2;
         else if (!p1.equals(p2)) foundBoth = true;
       }
     }
   }
   if (!foundBoth) {
     d += searchRect.height;
     data = getDistanceAndPointAtY(line, d);
     if (data != null) {
       p.setLocation((Point2D) data[1]);
       if (p.getX() >= searchRect.x && p.getX() <= searchRect.x + searchRect.width) {
         // line end is bottom edge
         if (p == p2 && !p1.equals(p2)) foundBoth = true;
       }
     }
   }
   // if both line ends have been found, use line to find pixels to search
   if (foundBoth) {
     // set line ends to intersections
     line.setLine(p1, p2);
     if (p1.getX() > p2.getX()) {
       line.setLine(p2, p1);
     }
     // find pixel intersections that fall along the line
     int xMin = (int) Math.ceil(Math.min(p1.getX(), p2.getX()));
     int xMax = (int) Math.floor(Math.max(p1.getX(), p2.getX()));
     int yMin = (int) Math.ceil(Math.min(p1.getY(), p2.getY()));
     int yMax = (int) Math.floor(Math.max(p1.getY(), p2.getY()));
     // collect intersections in TreeMap sorted by position along line
     TreeMap<Double, Point2D> intersections = new TreeMap<Double, Point2D>();
     for (int x = xMin; x <= xMax; x++) {
       Object[] next = getDistanceAndPointAtX(line, x);
       intersections.put((Double) next[0], (Point2D) next[1]);
     }
     for (int y = yMin; y <= yMax; y++) {
       Object[] next = getDistanceAndPointAtY(line, y);
       intersections.put((Double) next[0], (Point2D) next[1]);
     }
     p = null;
     // create array of search points that are midway between intersections
     ArrayList<Point2D> searchPts = new ArrayList<Point2D>();
     for (Double key : intersections.keySet()) {
       Point2D next = intersections.get(key);
       if (p != null) {
         double x = (p.getX() + next.getX()) / 2 - searchRect.x;
         double y = (p.getY() + next.getY()) / 2 - searchRect.y;
         p.setLocation(x, y);
         searchPts.add(p);
       }
       p = next;
     }
     return searchPts;
   }
   return null;
 }
  /**
   * Gets the template location at which the best match occurs in a rectangle and along a line. May
   * return null.
   *
   * @param target the image to search
   * @param searchRect the rectangle to search within the target image
   * @param x0 the x-component of a point on the line
   * @param y0 the y-component of a point on the line
   * @param slope the slope of the line
   * @param spread the spread of the line (line width = 1+2*spread)
   * @return the optimized template location of the best match, if any
   */
  public TPoint getMatchLocation(
      BufferedImage target, Rectangle searchRect, double x0, double y0, double theta, int spread) {
    wTarget = target.getWidth();
    hTarget = target.getHeight();
    // determine insets needed to accommodate template
    int left = wTemplate / 2, right = left;
    if (wTemplate % 2 > 0) right++;
    int top = hTemplate / 2, bottom = top;
    if (hTemplate % 2 > 0) bottom++;

    // trim search rectangle if necessary
    searchRect.x = Math.max(left, Math.min(wTarget - right, searchRect.x));
    searchRect.y = Math.max(top, Math.min(hTarget - bottom, searchRect.y));
    searchRect.width = Math.min(wTarget - searchRect.x - right, searchRect.width);
    searchRect.height = Math.min(hTarget - searchRect.y - bottom, searchRect.height);
    if (searchRect.width <= 0 || searchRect.height <= 0) {
      peakHeight = Double.NaN;
      peakWidth = Double.NaN;
      return null;
    }
    // set up test pixels to search (rectangle plus template)
    int xMin = Math.max(0, searchRect.x - left);
    int xMax = Math.min(wTarget, searchRect.x + searchRect.width + right);
    int yMin = Math.max(0, searchRect.y - top);
    int yMax = Math.min(hTarget, searchRect.y + searchRect.height + bottom);
    wTest = xMax - xMin;
    hTest = yMax - yMin;
    if (target.getType() != BufferedImage.TYPE_INT_RGB) {
      BufferedImage image = new BufferedImage(wTarget, hTarget, BufferedImage.TYPE_INT_RGB);
      image.createGraphics().drawImage(target, 0, 0, null);
      target = image;
    }
    targetPixels = new int[wTest * hTest];
    target.getRaster().getDataElements(xMin, yMin, wTest, hTest, targetPixels);
    // get the points to search along the line
    ArrayList<Point2D> searchPts = getSearchPoints(searchRect, x0, y0, theta);
    if (searchPts == null) {
      peakHeight = Double.NaN;
      peakWidth = Double.NaN;
      return null;
    }
    // collect differences in a map as they are measured
    HashMap<Point2D, Double> diffs = new HashMap<Point2D, Double>();
    // find the point with the minimum difference from template
    double matchDiff = largeNumber; // larger than typical differences
    int xMatch = 0, yMatch = 0;
    double avgDiff = 0;
    Point2D matchPt = null;
    for (Point2D pt : searchPts) {
      int x = (int) pt.getX();
      int y = (int) pt.getY();
      double diff = getDifferenceAtTestPoint(x, y);
      diffs.put(pt, diff);
      avgDiff += diff;
      if (diff < matchDiff) {
        matchDiff = diff;
        xMatch = x;
        yMatch = y;
        matchPt = pt;
      }
    }
    avgDiff /= searchPts.size();
    peakHeight = avgDiff / matchDiff - 1;
    peakWidth = Double.NaN;
    double dl = 0;
    int matchIndex = searchPts.indexOf(matchPt);

    // if match is not exact, fit a Gaussian and find peak
    if (!Double.isInfinite(peakHeight) && matchIndex > 0 && matchIndex < searchPts.size() - 1) {
      // fill data arrays
      Point2D pt = searchPts.get(matchIndex - 1);
      double diff = diffs.get(pt);
      xValues[0] = -pt.distance(matchPt);
      yValues[0] = avgDiff / diff - 1;
      xValues[1] = 0;
      yValues[1] = peakHeight;
      pt = searchPts.get(matchIndex + 1);
      diff = diffs.get(pt);
      xValues[2] = pt.distance(matchPt);
      yValues[2] = avgDiff / diff - 1;

      // determine approximate offset (dl) and width (w) values
      double pull = -xValues[0] / (yValues[1] - yValues[0]);
      double push = xValues[2] / (yValues[1] - yValues[2]);
      if (Double.isNaN(pull)) pull = LARGE_NUMBER;
      if (Double.isNaN(push)) push = LARGE_NUMBER;
      dl = 0.3 * (xValues[2] - xValues[0]) * (push - pull) / (push + pull);
      double ratio = dl > 0 ? peakHeight / yValues[0] : peakHeight / yValues[2];
      double w = dl > 0 ? dl - xValues[0] : dl - xValues[2];
      w = w * w / Math.log(ratio);

      // set parameters and fit to x data
      dataset.clear();
      dataset.append(xValues, yValues);
      double rmsDev = 1;
      for (int k = 0; k < 3; k++) {
        double c = k == 0 ? w : k == 1 ? w / 3 : w * 3;
        f.setParameterValue(0, peakHeight);
        f.setParameterValue(1, dl);
        f.setParameterValue(2, c);
        rmsDev = fitter.fit(f);
        if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact)	
          dl = f.getParameterValue(1);
          peakWidth = f.getParameterValue(2);
          break;
        }
      }
    }
    double dx = dl * Math.cos(theta);
    double dy = dl * Math.sin(theta);
    double xImage = xMatch + searchRect.x - left - trimLeft + dx;
    double yImage = yMatch + searchRect.y - top - trimTop + dy;
    return new TPoint(xImage, yImage);
  }
  /**
   * Gets the template location at which the best match occurs in a rectangle. May return null.
   *
   * @param target the image to search
   * @param searchRect the rectangle to search within the target image
   * @return the optimized template location at which the best match, if any, is found
   */
  public TPoint getMatchLocation(BufferedImage target, Rectangle searchRect) {
    wTarget = target.getWidth();
    hTarget = target.getHeight();
    // determine insets needed to accommodate template
    int left = wTemplate / 2, right = left;
    if (wTemplate % 2 > 0) right++;
    int top = hTemplate / 2, bottom = top;
    if (hTemplate % 2 > 0) bottom++;
    // trim search rectangle if necessary
    searchRect.x = Math.max(left, Math.min(wTarget - right, searchRect.x));
    searchRect.y = Math.max(top, Math.min(hTarget - bottom, searchRect.y));
    searchRect.width = Math.min(wTarget - searchRect.x - right, searchRect.width);
    searchRect.height = Math.min(hTarget - searchRect.y - bottom, searchRect.height);
    if (searchRect.width <= 0 || searchRect.height <= 0) {
      peakHeight = Double.NaN;
      peakWidth = Double.NaN;
      return null;
    }
    // set up test pixels to search (rectangle plus template)
    int xMin = Math.max(0, searchRect.x - left);
    int xMax = Math.min(wTarget, searchRect.x + searchRect.width + right);
    int yMin = Math.max(0, searchRect.y - top);
    int yMax = Math.min(hTarget, searchRect.y + searchRect.height + bottom);
    wTest = xMax - xMin;
    hTest = yMax - yMin;
    if (target.getType() != BufferedImage.TYPE_INT_RGB) {
      BufferedImage image = new BufferedImage(wTarget, hTarget, BufferedImage.TYPE_INT_RGB);
      image.createGraphics().drawImage(target, 0, 0, null);
      target = image;
    }
    targetPixels = new int[wTest * hTest];
    target.getRaster().getDataElements(xMin, yMin, wTest, hTest, targetPixels);
    // find the rectangle point with the minimum difference
    double matchDiff = largeNumber; // larger than typical differences
    int xMatch = 0, yMatch = 0;
    double avgDiff = 0;
    for (int x = 0; x <= searchRect.width; x++) {
      for (int y = 0; y <= searchRect.height; y++) {
        double diff = getDifferenceAtTestPoint(x, y);
        avgDiff += diff;
        if (diff < matchDiff) {
          matchDiff = diff;
          xMatch = x;
          yMatch = y;
        }
      }
    }
    avgDiff /= (searchRect.width * searchRect.height);
    peakHeight = avgDiff / matchDiff - 1;
    peakWidth = Double.NaN;
    double dx = 0, dy = 0;
    // if match is not exact, fit a Gaussian and find peak
    if (!Double.isInfinite(peakHeight)) {
      // fill data arrays
      xValues[1] = yValues[1] = peakHeight;
      for (int i = -1; i < 2; i++) {
        if (i == 0) continue;
        double diff = getDifferenceAtTestPoint(xMatch + i, yMatch);
        xValues[i + 1] = avgDiff / diff - 1;
        diff = getDifferenceAtTestPoint(xMatch, yMatch + i);
        yValues[i + 1] = avgDiff / diff - 1;
      }
      // estimate peakHeight = peak of gaussian
      // estimate offset dx of gaussian
      double pull = 1 / (xValues[1] - xValues[0]);
      double push = 1 / (xValues[1] - xValues[2]);
      if (Double.isNaN(pull)) pull = LARGE_NUMBER;
      if (Double.isNaN(push)) push = LARGE_NUMBER;
      dx = 0.6 * (push - pull) / (push + pull);
      // estimate width wx of gaussian
      double ratio = dx > 0 ? peakHeight / xValues[0] : peakHeight / xValues[2];
      double wx = dx > 0 ? dx + 1 : dx - 1;
      wx = wx * wx / Math.log(ratio);
      // estimate offset dy of gaussian
      pull = 1 / (yValues[1] - yValues[0]);
      push = 1 / (yValues[1] - yValues[2]);
      if (Double.isNaN(pull)) pull = LARGE_NUMBER;
      if (Double.isNaN(push)) push = LARGE_NUMBER;
      dy = 0.6 * (push - pull) / (push + pull);
      // estimate width wy of gaussian
      ratio = dy > 0 ? peakHeight / yValues[0] : peakHeight / yValues[2];
      double wy = dy > 0 ? dy + 1 : dy - 1;
      wy = wy * wy / Math.log(ratio);

      // set x parameters and fit to x data
      dataset.clear();
      dataset.append(pixelOffsets, xValues);
      double rmsDev = 1;
      for (int k = 0; k < 3; k++) {
        double c = k == 0 ? wx : k == 1 ? wx / 3 : wx * 3;
        f.setParameterValue(0, peakHeight);
        f.setParameterValue(1, dx);
        f.setParameterValue(2, c);
        rmsDev = fitter.fit(f);
        if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact)	
          dx = f.getParameterValue(1);
          peakWidth = f.getParameterValue(2);
          break;
        }
      }
      if (!Double.isNaN(peakWidth)) {
        // set y parameters and fit to y data
        dataset.clear();
        dataset.append(pixelOffsets, yValues);
        for (int k = 0; k < 3; k++) {
          double c = k == 0 ? wy : k == 1 ? wy / 3 : wy * 3;
          f.setParameterValue(0, peakHeight);
          f.setParameterValue(1, dx);
          f.setParameterValue(2, c);
          rmsDev = fitter.fit(f);
          if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact)	
            dy = f.getParameterValue(1);
            peakWidth = (peakWidth + f.getParameterValue(2)) / 2;
            break;
          }
        }
        if (rmsDev > 0.01) peakWidth = Double.NaN;
      }
    }
    double xImage = xMatch + searchRect.x - left - trimLeft + dx;
    double yImage = yMatch + searchRect.y - top - trimTop + dy;
    return new TPoint(xImage, yImage);
  }
 /**
  * Rebuilds the template from an input image. The input image dimensions must match the original.
  * The input and original are overlaid onto the existing template, if any. Pixels that fall
  * outside the mask are ignored in the final template.
  *
  * @param image the input image
  * @param alphaInput the opacity with which the input image is overlaid
  * @param alphaOriginal the opacity with which the original image is overlaid
  */
 public void rebuildTemplate(BufferedImage image, int alphaInput, int alphaOriginal) {
   int w = image.getWidth();
   int h = image.getHeight();
   // return if image dimensions do not match original image
   if (original.getWidth() != w || original.getHeight() != h) return;
   // return if both alphas are zero
   if (alphaInput == 0 && alphaOriginal == 0) return;
   // draw image onto argb input
   BufferedImage input = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
   input.createGraphics().drawImage(image, 0, 0, null);
   // create working image if needed
   if (working == null) {
     working = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
   }
   // reset template dimensions and create new template if needed
   if (template == null || w != wTemplate || h != hTemplate) {
     wTemplate = w;
     hTemplate = h;
     int len = w * h;
     template = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
     pixels = new int[len];
     templateR = new int[len];
     templateG = new int[len];
     templateB = new int[len];
     isPixelTransparent = new boolean[len];
   }
   // set alpha of input and draw onto working
   Graphics2D gWorking = working.createGraphics();
   alphaInput = Math.max(0, Math.min(255, alphaInput));
   if (alphaInput > 0) { // overlay only if not transparent
     gWorking.setComposite(getComposite(alphaInput));
     input.getRaster().getDataElements(0, 0, w, h, pixels);
     gWorking.drawImage(input, 0, 0, null);
   }
   // set alpha of original and draw onto working
   alphaOriginal = Math.max(0, Math.min(255, alphaOriginal));
   if (alphaOriginal > 0) { // overlay only if not transparent
     gWorking.setComposite(getComposite(alphaOriginal));
     original.getRaster().getDataElements(0, 0, w, h, pixels);
     gWorking.drawImage(original, 0, 0, null);
   }
   // read pixels from working raster
   working.getRaster().getDataElements(0, 0, wTemplate, hTemplate, pixels);
   if (mask != null) {
     // set pixels outside mask to transparent
     for (int i = 0; i < pixels.length; i++) {
       boolean inside = true;
       // pixel is inside only if all corners are inside
       int x = i % wTemplate, y = i / wTemplate;
       for (int j = 0; j < 2; j++) {
         for (int k = 0; k < 2; k++) {
           p.setLocation(x + j, y + k);
           inside = inside && mask.contains(p);
         }
       }
       if (!inside) pixels[i] = pixels[i] & (0 << 24); // set alpha to zero (transparent)
     }
   }
   // write pixels to template raster
   template.getRaster().setDataElements(0, 0, wTemplate, hTemplate, pixels);
   // trim transparent edges from template
   int trimRight = 0, trimBottom = 0;
   trimLeft = trimTop = 0;
   // left edge
   boolean transparentEdge = true;
   while (transparentEdge && trimLeft < wTemplate) {
     for (int line = 0; line < hTemplate; line++) {
       int i = line * wTemplate + trimLeft;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimLeft++;
   }
   // right edge
   transparentEdge = true;
   while (transparentEdge && (trimLeft + trimRight) < wTemplate) {
     for (int line = 0; line < hTemplate; line++) {
       int i = (line + 1) * wTemplate - 1 - trimRight;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimRight++;
   }
   // top edge
   transparentEdge = true;
   while (transparentEdge && trimTop < hTemplate) {
     for (int col = 0; col < wTemplate; col++) {
       int i = trimTop * wTemplate + col;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimTop++;
   }
   // bottom edge
   transparentEdge = true;
   while (transparentEdge && (trimTop + trimBottom) < hTemplate) {
     for (int col = 0; col < wTemplate; col++) {
       int i = (hTemplate - 1 - trimBottom) * wTemplate + col;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimBottom++;
   }
   // reduce size of template if needed
   if (trimLeft + trimRight + trimTop + trimBottom > 0) {
     wTemplate -= (trimLeft + trimRight);
     hTemplate -= (trimTop + trimBottom);
     pixels = new int[wTemplate * hTemplate];
     templateR = new int[wTemplate * hTemplate];
     templateG = new int[wTemplate * hTemplate];
     templateB = new int[wTemplate * hTemplate];
     isPixelTransparent = new boolean[wTemplate * hTemplate];
     BufferedImage bi = new BufferedImage(wTemplate, hTemplate, BufferedImage.TYPE_INT_ARGB);
     bi.createGraphics().drawImage(template, -trimLeft, -trimTop, null);
     template = bi;
     template.getRaster().getDataElements(0, 0, wTemplate, hTemplate, pixels);
   }
   // set up rgb and transparency arrays for faster matching
   for (int i = 0; i < pixels.length; i++) {
     int val = pixels[i];
     templateR[i] = getRed(val); // red
     templateG[i] = getGreen(val); // green
     templateB[i] = getBlue(val); // blue
     isPixelTransparent[i] = getAlpha(val) == 0; // alpha
   }
 }