Package com.tinkerlog.kritzler

Source Code of com.tinkerlog.kritzler.Plotter$PathComparator

package com.tinkerlog.kritzler;


import geomerative.RG;
import geomerative.RPath;
import geomerative.RPoint;
import geomerative.RShape;

import controlP5.*;

import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.tinkerlog.kritzler.FillSvg.MyPoint;

import processing.core.PApplet;
import processing.core.PGraphics;
import processing.serial.Serial;

@SuppressWarnings("serial")
public class Plotter extends PApplet {
 
  private static final String BUFFER_ACC_PATH = "buffer_acc/";
  private static final String BUFFER_DONE_PATH = "buffer_done/";
  private static final String BUFFER_DENIED_PATH = "buffer_denied/";
   
  private static final int MAX_PLOTTER_X = 7000;
  private static final int MAX_PLOTTER_Y = 8000;
 
  //private static final int MAX_SCREEN_X = 800;
  private static final int MAX_SCREEN_Y = 700;
  private static final int MENU_X = 190;
 
  private static final int SCREEN_PADDING = 20;
 
  private static final int START_X = 4000;
  private static final int START_Y = 4000;
  private static final int HOME_X = 7500;
  private static final int HOME_Y = 7500;
 
  private static final int STATE_START = 1;
  private static final int STATE_WAITING = 2;
  private static final int STATE_PLOTTING_SCREEN = 3;
  private static final int STATE_WAITING_INPUT = 4;
  private static final int STATE_SETUP_PLOTTER = 5;
  private static final int STATE_PLOTTING = 6;
  private static final int STATE_PAUSED = 7;
  private static final int STATE_RESUME = 8;
  private static final int STATE_ABORTING = 9;
  private static final int STATE_FINISHING = 10;
  private int state = STATE_START;
  private int oldState = STATE_START;
  private static final String[] STATES = {
    "NONE", "START", "WAITING", "PLOTTING_SCREEN", "WAITING_INPUT", "SETUP_PLOTTER", "PLOTTING", "PAUSED", "RESUME",
    "ABORTING", "FINISHING"
  };
 
  private static final int BACKGROUND_STD = 0xFFA0A0A0;
  private static final int BACKGROUND_PAUSED = 0xFFA07070;
  private static final int CANVAS_BACKGROUND_BLACK = 0xFF202020;
  private static final int CANVAS_BACKGROUND_WHITE = 0xFFFFFFFF;
  private static final int SHAPE_STROKE_BLACK = 0xFF000000;
  private static final int SHAPE_STROKE_WHITE = 0xFFFFFFFF;
  private static final int PLOTTER_STD = 0x642186F8;
  private static final int PLOTTER_PAUSED = 0x64FE1A1A;
  private static final int PLOTTER_FINISHED = 0x641AFE1A;
  // private static final float STROKE_WEIGHT_GRID = 10.0F;
  private static final float STROKE_WEIGHT_GRID = 1.0F;
  private static final int STROKE_GRID = 0xFFD0D0D0
  // private static final float STROKE_WEIGHT_SHAPE = 10.0F;
  private static final float STROKE_WEIGHT_SHAPE = 1.0F;
  private static final int STROKE_SHAPE = 0xFF808080;
  private static final float DELTA_STEP = 20.0F;
  private static final float MAX_COMPARE_DELTA = 70.0F;
 
  float screenScale = 0.0F;
  float plotterScale = 1.0F;
  float svgScale = 5.0F;

  float dx, dy = 0.0F;
 
  private ControlP5 cp5;
  private ControlP5 cp5files;
  private Button startButton;
  private Button pauseButton;
  private Button stopButton;
  private Button leftButton;
  private DropdownList portsList;
  private DropdownList filesList;
  private Textfield statusField;
  private PGraphics graphics;
  private RShape shape;
  private Serial port;
  private Kritzler plotter;
  private List<Instruction> currentInstructions;
  private String[] ports;
  private String[] fileNames;
  private String currentFileName;

  private long nextUpdate = 0
 
  private boolean buttonsEnabled = false;
  private boolean plotting = false;
  private boolean useCache = false;

  private boolean drawGrid = true;
  private boolean drawBoundingBox = true;
  private boolean blackOnWhite = true;
  private int canvasBackground = CANVAS_BACKGROUND_WHITE;
  private int shapeStroke = SHAPE_STROKE_BLACK;

  /**
   * Initial sketch set up
   *
   * @see processing.core.PApplet#setup()
   */
  public void setup() {

    // Set up the Geomerative library
    RG.init(this);

    // Determine the screen scale and window size
    screenScale = (MAX_SCREEN_Y - 2F * SCREEN_PADDING) / MAX_PLOTTER_Y;
    int xsize = (int)(MAX_PLOTTER_X * screenScale) + 2 * SCREEN_PADDING;
    int ysize = (int)(MAX_PLOTTER_Y * screenScale) + 2 * SCREEN_PADDING;
    System.out.println(screenScale + " " + xsize + " " + ysize);
    // screenScale *= 10;
   
    smooth();
    size(xsize + MENU_X, ysize);
   
    graphics = createGraphics((int)(MAX_PLOTTER_X * screenScale), (int)(MAX_PLOTTER_Y * screenScale), P2D);
    // graphics.smooth();  // do this after beginDraw()
   
    background(BACKGROUND_STD);

    cp5 = new ControlP5(this);
    cp5files = new ControlP5(this);

    // move buttons
    Button upButton = cp5.addButton("up")
      .setLabel("up")
      .setWidth(150)
      .setPosition(xsize + 10, 20);     
    upButton.getCaptionLabel().align(CENTER, CENTER);
    leftButton = cp5.addButton("left")
      .setLabel("left")
      .setWidth(74)
      .setPosition(xsize + 10, 40)
    cp5.addButton("right")
      .setLabel("right")
      .setWidth(75)
      .setPosition(xsize + 85, 40);     
    Button downButton = cp5.addButton("down")
      .setLabel("down")
      .setWidth(150)
      .setPosition(xsize + 10, 60);
    downButton.getCaptionLabel().align(CENTER, CENTER);   
    cp5.addTextlabel("moveLabel")
    .setText("MOVE")
    .setPosition(xsize + 8, 82)
    ;    
   
    // scale buttons
    cp5.addButton("scaleUp")
      .setLabel("up")
      .setWidth(74)
      .setPosition(xsize + 10, 100)
    cp5.addButton("scaleDown")
      .setLabel("Down")
      .setWidth(75)
      .setPosition(xsize + 85, 100);
    cp5.addTextlabel("label")
      .setText("SCALE")
      .setPosition(xsize + 8, 122)
      ;
   
    // flip/mirror switches
    int y = 140;
    cp5.addToggle("toggleHorizontal")
      .setLabel("flip horizontal")
      .setPosition(xsize + 10, y)
      .setSize(25,10)
      .setValue(false)
      .setMode(ControlP5.SWITCH)
      ;
    cp5.addToggle("toggleVertical")
      .setLabel("flip vertical")
      .setPosition(xsize + 10, y + 30)
      .setSize(25,10)
      .setValue(false)
      .setMode(ControlP5.SWITCH)
      ;      
    cp5.addToggle("toggleBW")
      .setLabel("toggle B/W")
      .setPosition(xsize + 10, y + 60)
      .setSize(25,10)
      .setValue(false)
      .setMode(ControlP5.SWITCH)
      ;      

    // setup serial ports
    y = 250;
    cp5.addTextlabel("serlabel")
      .setText("SERIAL")
      .setPosition(xsize + 8, y + 2)
      ;
    portsList = cp5.addDropdownList("ports")
      .setPosition(xsize + 10, y)
      .setItemHeight(15)
      .setHeight(60)
      .setWidth(150)
      .setBarHeight(15)
      .setIndex(0)
    ;
    portsList.getCaptionLabel().getStyle().marginTop = 3;
    portsList.getCaptionLabel().getStyle().marginLeft = 3;
    portsList.getValueLabel().getStyle().marginTop = 3;

    ports = Serial.list();
    for (int i = 0; i < ports.length; i++) {
      portsList.addItem(ports[i], i);
    }
    startSerial(ports[0]); // default
   
    // setup files
    y = 310;
    cp5.addTextlabel("filelabel")
      .setText("SELECT FILE")
      .setPosition(xsize + 8, y + 2)
      ;
    filesList = cp5.addDropdownList("files")
      .setPosition(xsize + 10, y)
      .setBarHeight(15)
      .setItemHeight(15)
      .setHeight(60)
      .setWidth(150)
      .setIndex(0)
      ;   
    filesList.getCaptionLabel().getStyle().marginTop = 3;
    filesList.getCaptionLabel().getStyle().marginLeft = 3;
    filesList.getValueLabel().getStyle().marginTop = 3;
    updateFileList();
    if (fileNames != null) {
      currentFileName = fileNames[0];
    }
   
    // buttons
    y = 370;
    cp5.addTextlabel("runlabel")
      .setText("RUN")
      .setPosition(xsize + 8, y + 22)
      ;
    startButton = cp5.addButton("start")
      .setPosition(xsize + 10, y)
      .setWidth(49)
      .setValue(0);   
    pauseButton = cp5.addButton("pause")
      .setPosition(xsize + 60, y)
      .setWidth(49)
      .setValue(0);
    stopButton = cp5.addButton("stop")
      .setPosition(xsize + 110, y)
      .setWidth(49)
      .setValue(0);

    statusField = cp5.addTextfield("state")
      .setPosition(xsize + 10, 410)
      .setWidth(150)
      .setText("hello")
      .setLock(true);
   
    buttonsEnabled = true;
    println("plotter ready");
  }
 
  public void controlEvent(ControlEvent e) {
    if (e.isGroup()) {
      // check if the Event was triggered from a ControlGroup
      if (e.getGroup().getName().equals("ports")) {
        int i = (int)e.getGroup().getValue();
        startSerial(ports[i]);
        background(BACKGROUND_STD);
        state = STATE_PLOTTING_SCREEN;  // trigger screen redraw to get rid of opened drop down       
      }
      else if (e.getGroup().getName().equals("files")) {
        int i = (int)e.getGroup().getValue();
        currentFileName = fileNames[i];
        shape = null;
        background(BACKGROUND_STD);
        state = STATE_WAITING;       
      }
    }
  }
 
  public void status(String s) {
    statusField.setText(s);
  }
 
  public void updateFileList() {
    fileNames = getFiles();
    for (int i = 0; i < fileNames.length; i++) {
      filesList.addItem(fileNames[i], i);
    }   
  }
 
  public void startSerial(String portName) {
    if (port != null) {
      port.clear();
      port.stop();
    }
    port = new Serial(this, portName, 57600);   
  }

  public void start(int value) {
    if (buttonsEnabled) {
      actOnKey('p');
    }
  }
 
  public void pause(int value) {
    if (buttonsEnabled) {
      actOnKey(' ');
    }
  }

  public void stop(int value) {
    if (buttonsEnabled) {
      actOnKey('a');
    }
  }
 
  public void up(int value) {
    actOnKey('u');
  }

  public void down(int value) {
    actOnKey('d');
  }

  public void left(int value) {
    actOnKey('l');
  }
 
  public void right(int value) {
    actOnKey('r');
  }

  public void toggleHorizontal(boolean b) {
    actOnKey('m');
 

  public void toggleVertical(boolean b) {
    actOnKey('M');
 
 
  public void toggleBW(boolean b) {
    if (buttonsEnabled) {
      actOnKey('w');
    }
  }
 
  public void scaleUp(int value) {
    actOnKey('+');
  }
 
  public void scaleDown(int value) {
    actOnKey('-');
  }
 
  /**
   * Main program loop
   *
   * @see processing.core.PApplet#draw()
   */
  public void draw() {
    // clears the background of the menu (controlp5 area)
    noStroke();
    fill(BACKGROUND_STD);
    rect(width - MENU_X, 0, MENU_X, height);
   
    pushMatrix();   
    oldState = state;

    switch (state) {

    // Initial sketch set up
    case STATE_START:
      dx = 0;
      dy = 0;
      shape = null;
      translate(SCREEN_PADDING, SCREEN_PADDING);
      scale(screenScale);
      rect(0, 0, MAX_PLOTTER_X, MAX_PLOTTER_Y);
      state = STATE_WAITING;
      break;

    // Load SVG file contents, wait to load
    case STATE_WAITING:
      status("waiting ...");
      if (shape == null && System.currentTimeMillis() > nextUpdate) {
        nextUpdate = System.currentTimeMillis() + 3000;
        shape = loadNewShape(currentFileName);
        if (shape != null) {
          state = STATE_PLOTTING_SCREEN;
        }
      }
      break;

    // Draw the canvas to the screen
    case STATE_PLOTTING_SCREEN:
      drawCanvas();
      state = STATE_WAITING_INPUT;
      break;

    // Waiting for user to begin plotting by pressing 'p'
    case STATE_WAITING_INPUT:
      status("waiting input ...");
      if (plotting) {
        state = STATE_SETUP_PLOTTER;
        plotting = false;
      }
      break;

    // Generate Instructions from the SVG file, then set up the Kritzler
    // object
    case STATE_SETUP_PLOTTER:
      currentInstructions = new ArrayList<Instruction>();
      List<RPath> paths = new ArrayList<RPath>();
      getAllPaths(paths, shape);
      paths = sortPaths(paths);
      convertToInstructions(currentInstructions, paths);
      setupPlotter(currentInstructions);
      state = STATE_PLOTTING;
      break;

    case STATE_PAUSED:
      status("paused");
      drawCanvas();
      break;
     
    case STATE_RESUME:
      drawCanvas();
      state = STATE_PLOTTING;
      break;
     
    // Actively plotting
    case STATE_PLOTTING:
      status("plotting ...");
      plotter.checkSerial();
      // When finished drawing, return to the coordinates specified by
      // HOME_X and HOME_Y
      if (plotter.isFinished()) {
        status("finished");
        println("finished");
        state = STATE_FINISHING;
        useCache = false;
      }
      else {
        drawCanvas();
      }
      break;
     
    case STATE_FINISHING:
      status("finished");
      drawCanvas();
      state = STATE_WAITING_INPUT;
      break;
     
    case STATE_ABORTING:
      status("aborted");
      plotting = false;
      useCache = false;
      shape = null;
      drawCanvas();
      state = STATE_WAITING_INPUT;
      break;
   
   
    if (oldState != state) {
      println("switching: " + STATES[oldState] + " --> " + STATES[state]);
      oldState = state;
    }
   
    popMatrix();
  }
 
  /**
   * Set up the Kritzler object
   *
   * @param instructions
   *            Instruction set to use
   */
  public void setupPlotter(List<Instruction> instructions) {
    plotter = new Kritzler(this, port);
    plotter.setInstructions(instructions);
    plotter.translate(START_X + dx, START_Y + dy);
    plotter.setScale(plotterScale);
  }

  /**
   * Draw the canvas to the screen
   */
  public void drawCanvas() {

    if (useCache) {
      switch(state) {
      case STATE_PAUSED:
        background(BACKGROUND_PAUSED);
        break;
      default:
        background(BACKGROUND_STD);
      }
      image(graphics, SCREEN_PADDING, SCREEN_PADDING);
      translate(SCREEN_PADDING, SCREEN_PADDING);
      scale(screenScale * plotterScale);     
      translate(dx, dy);
      drawBot();
      return;
    }
   
    graphics.beginDraw();
    graphics.smooth();
    
    switch(state) {
    case STATE_PAUSED:
      graphics.background(BACKGROUND_PAUSED);
      break;
    default:
      graphics.background(BACKGROUND_STD);
    }
    
    // Draw the canvas rectangle
    // graphics.translate(SCREEN_PADDING, SCREEN_PADDING);
    graphics.scale(screenScale * plotterScale);
    graphics.fill(canvasBackground);
    graphics.rect(0, 0, MAX_PLOTTER_X, MAX_PLOTTER_Y);

    // Draw the grid
    if (drawGrid) {
      graphics.stroke(STROKE_GRID);
      graphics.strokeWeight(STROKE_WEIGHT_GRID);

      int cols = MAX_PLOTTER_X / 100;
      int rows = MAX_PLOTTER_Y / 100;
      for(int i=0; i<cols; i++) {
        graphics.line(i*100, 0, i*100, MAX_PLOTTER_Y);
      }
      for(int i=0; i<rows; i++) {
        graphics.line(0, i*100, MAX_PLOTTER_X, i*100);
      }
    }

    // Draw the homing crosshairs
    graphics.strokeWeight((float)(STROKE_WEIGHT_GRID * 2));
    graphics.line(MAX_PLOTTER_X/2, 0, MAX_PLOTTER_X/2, MAX_PLOTTER_Y);
    graphics.line(0, MAX_PLOTTER_Y/2, MAX_PLOTTER_X, MAX_PLOTTER_Y/2);

    graphics.translate(dx, dy);       

    // Draw the bounding box of the current shape
    if (drawBoundingBox && shape != null) {
      // Bounding box
      RPoint bounds[] = shape.getBoundsPoints();
      graphics.strokeWeight(STROKE_WEIGHT_GRID);
      graphics.stroke(255,0,0);
      graphics.line( bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y );
      graphics.line( bounds[1].x, bounds[1].y, bounds[2].x, bounds[2].y );
      graphics.line( bounds[2].x, bounds[2].y, bounds[3].x, bounds[3].y );
      graphics.line( bounds[3].x, bounds[3].y, bounds[0].x, bounds[0].y );

      // Center cross hairs
      RPoint center = shape.getCenter();
      graphics.line( center.x, bounds[0].y, center.x, bounds[0].y - 200 );
      graphics.line( center.x, bounds[3].y, center.x, bounds[3].y + 200 );
      graphics.line( bounds[0].x, center.y, bounds[0].x - 200, center.y );
      graphics.line( bounds[1].x, center.y, bounds[1].x + 200, center.y );
    }
   
    // Draw the SVG content
    graphics.strokeWeight(STROKE_WEIGHT_SHAPE);
    graphics.stroke(shapeStroke);
    if (shape != null) {
      drawShape(graphics, shape);
    }
    graphics.endDraw();
    image(graphics, SCREEN_PADDING, SCREEN_PADDING);
  }
 
  public void drawBot() {
    if (plotter == null) {
      return;
    }
    noStroke();
    switch (state) {
    case STATE_PLOTTING:
      fill(PLOTTER_STD);
      break;
    case STATE_PAUSED:
      fill(PLOTTER_PAUSED);
      break;
    case STATE_FINISHING:
      fill(PLOTTER_FINISHED);
      break;
    }
    Instruction i = currentInstructions.get(plotter.getCurrentInstructionIndex());
    ellipseMode(CENTER);   
    ellipse(i.x, i.y, 200, 200);
 

  /**
   * Draw the SVG file contents to the screen
   *
   * @param shape
   *            Shape to draw
   */
  public void drawShape(PGraphics g, RShape shape) {
    // Recurse through any children of this shape
    for (int i = 0; i < shape.countChildren(); i++) {
      RShape s = shape.children[i];
      drawShape(g, s);
    }

    // Iterate through each path of the shape, drawing them to the screen
    for (int i = 0; i < shape.countPaths(); i++) {
      // Get the points of the current path
      RPath p = shape.paths[i];
      RPoint[] points = p.getPoints();

      // Connect each of the points using lines
      for (int k = 0; k < points.length - 1; k++) {
        g.line(points[k].x, points[k].y, points[k + 1].x, points[k + 1].y);
      }
    }
  }

  /**
   * Convert SVG file contents into Instruction objects
   *
   * @param instructions
   *            Resulting List of Instruction objects
   * @param shape
   *            RShape to proces
   */
  public void convertToInstructions(List<Instruction> instructions, RShape shape) {
    // Recurse through any children of current shape
    for (int i = 0; i < shape.countChildren(); i++) {
      RShape s = shape.children[i];
      convertToInstructions(instructions, s);
    }

    // Generate Instruction objects for every path of shape
    for (int i = 0; i < shape.countPaths(); i++) {
      // Get the first point of this path
      RPath p = shape.paths[i];
      RPoint[] points = p.getPoints();
      RPoint p1 = points[0];

      // Move to that point
      instructions.add(new Instruction(Instruction.MOVE_ABS, p1.x, p1.y));

      // Draw lines to any subsequent points
      for (int k = 0; k < points.length - 1; k++) {
        RPoint p2 = points[k];
        instructions.add(new Instruction(Instruction.LINE_ABS, p2.x, p2.y));
      }
    }   
  }

  public void convertToInstructions(List<Instruction> instructions, List<RPath> paths) {
    for (RPath p : paths) {
      RPoint[] points = p.getPoints();
      RPoint p1 = points[0];

      // Move to that point
      instructions.add(new Instruction(Instruction.MOVE_ABS, p1.x, p1.y));

      // Draw lines to any subsequent points
      for (int k = 0; k < points.length - 1; k++) {
        RPoint p2 = points[k];
        instructions.add(new Instruction(Instruction.LINE_ABS, p2.x, p2.y));
      }     
    }
  }
 
  public List<RPath> sortPaths(List<RPath> paths) {
    println("sorting paths ...");
    List<RPath> resultPath = new ArrayList<RPath>();
    PathComparator comparator = new PathComparator();
    RPoint tl = shape.getTopLeft();
    RPoint br = shape.getBottomRight();
   
    float y = tl.y;
    while (y < br.y) {
      List<RPath> sortedRow = new ArrayList<RPath>();
      for (int i = 0; i < paths.size(); i++) {
        RPath path = paths.get(i);
        RPoint p = path.getPoints()[0];
        if (p.y < y + MAX_COMPARE_DELTA && p.y >= y) {
          sortedRow.add(path);
        }
      }
      Collections.sort(sortedRow, comparator);
      resultPath.addAll(sortedRow);
      y += MAX_COMPARE_DELTA;
    }
    return resultPath;
  }
 
  public void getAllPaths(List<RPath> paths, RShape shape) {
    for (int i = 0; i < shape.countChildren(); i++) {
      RShape s = shape.children[i];
      getAllPaths(paths, s);
    }
    for (int i = 0; i < shape.countPaths(); i++) {
      paths.add(shape.paths[i]);
    }
  }
 
  /**
   * Process keyboard input
   *
   * @see processing.core.PApplet#keyPressed()
   */
  public void keyPressed() {
    actOnKey(key);
 
   
  public void actOnKey(int key) {
   
    switch (key) {

    // a = abort
    case 'a':
      state = STATE_ABORTING;
      break;
   
    // p = begin plotting
    case 'p':
      plotting = true;
      useCache = true;
      break;

    // [space] = pause plotting
    case ' ':
      if (currentInstructions != null && state != STATE_PAUSED) {
        state = STATE_PAUSED;
      }
      else if (state == STATE_PAUSED) {
        state = STATE_RESUME;
      }
      break;

    // Arrow keys = translate shape around canvas
    case CODED:
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      // Move SVG shape around based on arrow keys
      switch (keyCode) {
      case UP:
        dy -= DELTA_STEP;
        state = STATE_PLOTTING_SCREEN;
        break;
      case DOWN:
        dy += DELTA_STEP;
        state = STATE_PLOTTING_SCREEN;
        break;
      case LEFT:
        dx -= DELTA_STEP;
        state = STATE_PLOTTING_SCREEN;
        break;
      case RIGHT:
        dx += DELTA_STEP;
        state = STATE_PLOTTING_SCREEN;
        break;
      }
      break;
     
    case 'u':
      dy -= DELTA_STEP;
      state = STATE_PLOTTING_SCREEN;
      break;
    case 'd':
      dy += DELTA_STEP;
      state = STATE_PLOTTING_SCREEN;
      break;
    case 'l':
      dx -= DELTA_STEP;
      state = STATE_PLOTTING_SCREEN;
      break;
    case 'r':
      dx += DELTA_STEP;
      state = STATE_PLOTTING_SCREEN;
      break;
     
    case 'w':
      blackOnWhite = !blackOnWhite;
      shapeStroke = (blackOnWhite) ? SHAPE_STROKE_BLACK : SHAPE_STROKE_WHITE;
      canvasBackground = (blackOnWhite) ? CANVAS_BACKGROUND_WHITE : CANVAS_BACKGROUND_BLACK;
      state = STATE_PLOTTING_SCREEN;
      break;

    // l = scale shape down
    case '-':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      shape.scale(0.95F);
      print(shape, "-: ");
      state = STATE_PLOTTING_SCREEN;
      break;

    // L = scale shape up
    case '+':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      shape.scale(1.05F);
      print(shape, "+: ");
      state = STATE_PLOTTING_SCREEN;
      break;

    // m = flip shape horizontally
    case 'm':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      float width = shape.getBottomRight().x;
      shape.scale(-1.0F, 1.0F);
      shape.translate(width, 0);
      state = STATE_PLOTTING_SCREEN;
      break;

    // M = flip shape vertically
    case 'M':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      shape.scale(1.0F, -1.0F);
      state = STATE_PLOTTING_SCREEN;
      break;

    // s = deny shape (?)
    case 's':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      moveShapeDenied();
      state = STATE_START;
      break;

    // g = toggle grid
    case 'g':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      drawGrid = !drawGrid;
      state = STATE_PLOTTING_SCREEN;
      break;

    // b = toggle bounding box
    case 'b':
      if (shape == null || state == STATE_PLOTTING) {
        return;
      }
      drawBoundingBox = !drawBoundingBox;
      state = STATE_PLOTTING_SCREEN;
      break;
    }
  }

  /**
   * Print a message to the console, along with the coordinates of the top
   * left and bottom right extends of the SVG shape
   *
   * @param p
   *            RShape to acquire extents from
   * @param msg
   *            Message to print to console
   */
  private void print(RShape p, String msg) {
    RPoint p1 = p.getTopLeft();
    RPoint p2 = p.getBottomRight();
    System.out.println(msg + " (" + p1.x + ", " + p1.y + "), (" + p2.x + ", " + p2.y + ")");
  }

  /**
   * Move current SVG file to the folder specified by BUFFER_DONE_PATH
   */
  private void moveShapeDone() {
    System.out.println("moving file to " + BUFFER_DONE_PATH + currentFileName);
    File file = new File(BUFFER_ACC_PATH + currentFileName);
    File newFile = new File(BUFFER_DONE_PATH + currentFileName);
    file.renameTo(newFile);
  }

  /**
   * Move current SVG file to the folder specified by BUFFER_DENIED_PATH
   */
  private void moveShapeDenied() {
    System.out.println("moving file to " + BUFFER_DENIED_PATH + currentFileName);
    File file = new File(BUFFER_ACC_PATH + currentFileName);
    File newFile = new File(BUFFER_DENIED_PATH + currentFileName);
    file.renameTo(newFile);
  }

  /**
   * Loads the first SVG file from the BUFFER_ACC_PATH folder
   *
   * @return RShape The SVG file contents, if it exists. Otherwise null.
   */
  private RShape loadNewShape(String filename) {
    System.out.println("loading " + filename);
    RShape shape = RG.loadShape(BUFFER_ACC_PATH + filename);
    shape.scale(svgScale, shape.getCenter());
    print(shape, "loaded: ");
    return shape;
  }
 
  private String[] getFiles() {
    // Get a list of all SVG files in the BUFFER_ACC_PATH folder
    File dir = new File(BUFFER_ACC_PATH);
    String[] listing = dir.list(new FilenameFilter() {
      public boolean accept(File file, String filename) {
        return filename.endsWith("svg");
      }
    });
    return listing;
  }

  /**
   * Run the PApplet when this file is run as an application
   *
   * @param args
   */
  public static void main(String args[]) {
    PApplet.main(new String[] { "com.tinkerlog.kritzler.Plotter" });
 
 
  private class PathComparator implements Comparator<RPath> {

    @Override
    public int compare(RPath path1, RPath path2) {
      RPoint p1 = path1.getPoints()[0];
      RPoint p2 = path2.getPoints()[0];
      float dx = p1.x - p2.x;
      float dy = p1.y - p2.y;
      if (abs(dy) < MAX_COMPARE_DELTA) {
        return p1.x > p2.x ? 1 : p1.x < p2.x ? -1 : 0;
      }
      return (dy > 0) ? 1 : -1;
    }
  }
 
}
TOP

Related Classes of com.tinkerlog.kritzler.Plotter$PathComparator

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.