Package ketUI

Source Code of ketUI.MouseGrid$IdentityCounter

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ketUI;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import geom.*;
import ket.display.box.Box;
import ketUI.panel.KetPanel;
import ketUI.responder.Responder;

import ket.*;
import ket.math.*;
import ketUI.modes.*;
import ket.math.EquationList;

/**
* Record the path of the mouse and identify loops and their associated attributes.
*/
public class MouseGrid {

  // TODO: Move to a separate class.
  public class IdentityCounter {
    IdentityHashMap<Argument,Double> map;
    public IdentityCounter() {
      map = new IdentityHashMap<Argument,Double>();
    }
    public double get(Argument key) {
      Double d = map.get(key);
      return d!=null ? d : 0.0;
    }
    public void put(Argument key, double value) {
      map.put(key, value);
    }
    public void add(Argument key, double value) {
      put(key, get(key)+value);
    }
    public void scale(Argument key, double value) {
      put(key, get(key)*value);
    }
    public void increment(Argument key) {
      add(key, 1);
    }
    public Set<Argument> keySet() {
      return map.keySet();
    }
    public java.util.List<Argument> keyList() {
      return new Vector<Argument>(map.keySet());
    }
    public void remove(Argument a) {
      map.remove(a);
    }
    public void clear() {
      map.clear();
    }
    public Argument calcMostFrequentArgument() {
      Argument maxKey = null;
      Double maxValue = null;
      for (Argument key : map.keySet()) {
        Double value = map.get(key);
        if (maxKey==null || value>maxValue) {
          maxKey = key;
          maxValue = value;
        }
      }
      return maxKey;
    }
    public void decay(double dt) {
      for (Argument a : keyList()) {
        scale(a, Math.pow(BASE, -dt/DECAY_SCALE));
      }
    }
    // Remove elements less than a given value.
    public void threshold(double min) {
      for (Argument a : keyList()) {
        double value = get(a);
        if (value<min) {
          remove(a);
        } else {
          put(a, value);
        }
      }

    }
  }

  static double DECAY_SCALE            = 500.0; // ms
  static double BASE                   = 10.0;
  static double BACKGROUND_NOISE_LEVEL = 0.05;
  static final int MINIMUM_SAMPLE_SIZE = 6;     // pixels
  static final int GRID_SIZE           = 10;    // pixels
  static final boolean SHOW_GRID       = true;

  IdentityHashMap<Argument,Box> boxMap;
  IdentityCounter argumentFrequency;
  Vector<Position> grid; //D
  double time;

  public MouseGrid() {
    argumentFrequency = new IdentityCounter();
    boxMap = new IdentityHashMap<Argument,Box>();
    grid = new Vector<Position>();
    time = System.currentTimeMillis();
  }

  public void paint(Graphics2D g2D) {
    if (!SHOW_GRID || grid==null) return;
    g2D.setColor(new Color(255, 0, 255));
    for (Position p : grid) {
      g2D.drawLine(1+(int) p.x, 1+(int) p.y, -1+(int) p.x, -1+(int) p.y);
      g2D.drawLine(1+(int) p.x, -1+(int) p.y, -1+(int) p.x, +1+(int) p.y);
    }
  }

  public void clear() {
    grid.clear();
    argumentFrequency.clear();
    boxMap.clear();
  }

  /**
   * Identify the selection based on what the loop circled.
   */
  public Argument getMostFrequentArgument(Vector<Position> loop, boolean clockwise, KetPanel ketPanel) {
    grid.clear();
    removeInvalids();
    argumentFrequency.decay(timeSinceLastCall());
    argumentFrequency.threshold(BACKGROUND_NOISE_LEVEL);
    calcArgumentFrequency(loop, clockwise, ketPanel);
    return argumentFrequency.calcMostFrequentArgument();
  }

  private void removeInvalids() {
    for (Argument a : argumentFrequency.keyList()) {
      if (a.getEquationList()==null) {
        argumentFrequency.remove(a);
        boxMap.remove(a);
      }
    }
  }

  private double timeSinceLastCall() {
    double t2 = System.currentTimeMillis();
    double dt = t2 - time;
    time = t2;
    return dt;
  }

  /*
   * Evenly sample points within the loop for the associated argument.
   * TODO: This may made more efficient by finding the x_min and
   * x_max for each y value and iterating at the original step
   * size between these points.
   */
  private void calcArgumentFrequency(Vector<Position> loop, boolean clockwise, KetPanel ketPanel) {
    Position centre = Position.average(loop.firstElement(), loop.lastElement());
    Offset step = getSpread(loop, centre);
    step.scale(2.0/GRID_SIZE);
    step.upperLimit(MINIMUM_SAMPLE_SIZE, MINIMUM_SAMPLE_SIZE);
    IdentityCounter counter = new IdentityCounter();
    double areaCircled = 0.0;
    for (double x=centre.x-step.width; x<=centre.x+step.width; x+=step.width) {
      for (double y=centre.y-step.height; y<=centre.y+step.height; y+=step.height) {
        Position point = new Position(x, y);
        if (!isWithinLoop(point, loop, clockwise, centre)) continue;
        areaCircled += step.area();
        grid.add(point);
        Box box = ketPanel.findDeepestBox(point);
        if (box==null) continue;
        Argument argument = box.getArgument();
        if (argument==null) continue;
        counter.increment(argument);
        if ( ! boxMap.containsKey(argument) ) {
          boxMap.put(argument, box);
        }
        for (Branch a : argument.getAncestors()) {
          counter.increment(a);
        }
      }
    }
    // Remove all keys associated with null boxes.
    for (Argument argument : counter.keyList()) {
      if (boxMap.get(argument)==null) {
        //- System.out.println("[remove " + argument + "]");
        counter.remove(argument);
      }
    }
    // Scale grids by the size of each box so larger boxes require
    // more area of the loop to be selected.
    double norm = 0.0;
    for (Argument argument : counter.keySet()) { // (box, argument, count)
      Box box = boxMap.get(argument);
      if (box==null) continue;
      double count = counter.get(argument);
      double selectedBoxArea = count*step.area();
      double fracCircled = selectedBoxArea/areaCircled;
      double fracBox = selectedBoxArea/box.getArea();
      double weight = fracCircled * fracBox * selectedBoxArea;
      /*-
      double boxFrac = selectedBoxArea / box.getArea(); // Frac area of box.
      //? double weight = boxFrac * selectedArea;
      double weight = boxFrac * selectedBoxArea;
      */
      counter.put(argument, weight);
      norm += weight;
    }
    for (Argument argument : counter.keySet()) {
      double value = counter.get(argument) / norm;
      argumentFrequency.add(argument, value);
    }
  }

  private Offset getSpread(Vector<Position> loop, Position centre) {
    Offset spread = new Offset();
    for (Position p : loop) {
      double deltaX = Math.abs(centre.x-p.x);
      double deltaY = Math.abs(centre.y-p.y);
      if (spread.width<deltaX) {
        spread.width = deltaX;
      }
      if (spread.height<deltaY) {
        spread.height = deltaY;
      }
    }
    return spread;
  }

  /**
   * Determine if the given point Q lies within the loop by
   * checking if its on the 'inside' of each line segment, AB, of the
   * given loop.
   */
  private boolean isWithinLoop(Position Q, Vector<Position> loop, boolean clockwise, Position centre) {
    Position A = null;
    for (Position B : loop) {
      if (A==null) { // Skip the first iteration.
        A = B;
        continue;
      }
      if (Position.isLeftOfLine(Q, A, B)^clockwise) {
        return false;
      }
      // Rotate line segment AB 180 degrees around centre and compare.
      double dAx = A.x - centre.x;
      double dAy = A.y - centre.y;
      double dBx = B.x - centre.x;
      double dBy = B.y - centre.y;
      Position Arot = new Position(centre.x-dAx, centre.y-dAy);
      Position Brot = new Position(centre.x-dBx, centre.y-dBy);
      if (Position.isLeftOfLine(Q, Arot, Brot)^clockwise) {
        return false;
      }
      A = B;
    }
    return true;
  }

}
TOP

Related Classes of ketUI.MouseGrid$IdentityCounter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.