/** Polargraph controller Copyright Sandy Noble 2018. This file is part of Polargraph Controller. Polargraph Controller 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. Polargraph Controller 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 Polargraph Controller. If not, see . Requires the excellent ControlP5 GUI library available from http://www.sojamo.de/libraries/controlP5/. Requires the excellent Geomerative library available from http://www.ricardmarxer.com/geomerative/. This is an application for controlling a polargraph machine, communicating using ASCII command language over a serial link. sandy.noble@gmail.com http://www.polargraph.co.uk https://github.com/euphy/polargraphcontroller */ //import processing.video.*; import diewald_CV_kit.libraryinfo.*; import diewald_CV_kit.utility.*; import diewald_CV_kit.blobdetection.*; import geomerative.*; import java.util.zip.CRC32; // for OSX import java.text.*; import java.util.*; import java.io.*; import java.util.logging.*; import javax.swing.*; import processing.serial.*; import controlP5.*; import java.awt.event.KeyEvent; import java.awt.event.*; import java.awt.Frame; import java.awt.BorderLayout; import java.lang.reflect.Method; int majorVersionNo = 2; int minorVersionNo = 6; int buildNo = 0; String programTitle = "Polargraph Controller v" + majorVersionNo + "." + minorVersionNo + " build " + buildNo; ControlP5 cp5; Map cp5s = new HashMap(); boolean drawbotReady = false; boolean drawbotConnected = false; static final int HARDWARE_VER_UNO = 1; static final int HARDWARE_VER_MEGA = 100; static final int HARDWARE_VER_MEGA_POLARSHIELD = 200; static final int HARDWARE_VER_POLARPRO = 300; int currentHardware = HARDWARE_VER_MEGA_POLARSHIELD; final int HARDWARE_ATMEGA328_SRAM = 2048; final int HARDWARE_ATMEGA1280_SRAM = 8096; int currentSram = HARDWARE_ATMEGA328_SRAM; String newMachineName = "PGXXABCD"; PVector machinePosition = new PVector(130.0, 50.0); float machineScaling = 1.0; DisplayMachine displayMachine = null; int homeALengthMM = 400; int homeBLengthMM = 400; // preset sizes - these can be referred to in the properties file // and will be automatically converted to numbers when loaded. final String PRESET_A3_SHORT = "A3SHORT"; final String PRESET_A3_LONG = "A3LONG"; final String PRESET_A2_SHORT = "A2SHORT"; final String PRESET_A2_LONG = "A2LONG"; final String PRESET_A2_IMP_SHORT = "A2+SHORT"; final String PRESET_A2_IMP_LONG = "A2+LONG"; final String PRESET_A1_SHORT = "A1SHORT"; final String PRESET_A1_LONG = "A1LONG"; final int A3_SHORT = 297; final int A3_LONG = 420; final int A2_SHORT = 418; final int A2_LONG = 594; final int A2_IMP_SHORT = 450; final int A2_IMP_LONG = 640; final int A1_SHORT = 594; final int A1_LONG = 841; int leftEdgeOfQueue = 800; int rightEdgeOfQueue = 1100; int topEdgeOfQueue = 0; int bottomEdgeOfQueue = 0; int queueRowHeight = 15; int baudRate = 57600; Serial myPort; // The serial port int[] serialInArray = new int[1]; // Where we'll put what we receive int serialCount = 0; // A count of how many bytes we receive boolean[] keys = new boolean[526]; final JFileChooser chooser = new JFileChooser(); SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy hh:mm:ss"); String commandStatus = "Waiting for a click."; float sampleArea = 10; float gridSize = 75.0; float currentPenWidth = 0.8; float penIncrement = 0.05; int penLiftDownPosition = 90; int penLiftUpPosition = 180; // this variable controls how big the pixels are scaled when drawn. // 1.0 represents full size, 2.0 would be twice as big as the grid size, // 0.5 would be half the grid size. float pixelScalingOverGridSize = 1.0; float currentMachineMaxSpeed = 600.0; float currentMachineAccel = 400.0; float MACHINE_ACCEL_INCREMENT = 25.0; float MACHINE_MAXSPEED_INCREMENT = 25.0; List commandQueue = new ArrayList(); List realtimeCommandQueue = new ArrayList(); List commandHistory = new ArrayList(); List machineMessageLog = new ArrayList(); List previewCommandList = new ArrayList(); long lastCommandQueueHash = 0L; File lastImageDirectory = null; File lastPropertiesDirectory = null; String lastCommand = ""; String lastDrawingCommand = ""; Boolean commandQueueRunning = false; static final int DRAW_DIR_NE = 1; static final int DRAW_DIR_SE = 2; static final int DRAW_DIR_SW = 3; static final int DRAW_DIR_NW = 4; static final int DRAW_DIR_N = 5; static final int DRAW_DIR_E = 6; static final int DRAW_DIR_S = 7; static final int DRAW_DIR_W = 8; static final int DRAW_DIR_MODE_AUTO = 1; static final int DRAW_DIR_MODE_PRESET = 2; static final int DRAW_DIR_MODE_RANDOM = 3; static int pixelDirectionMode = DRAW_DIR_MODE_PRESET; static final int PIXEL_STYLE_SQ_FREQ = 0; static final int PIXEL_STYLE_SQ_SIZE = 1; static final int PIXEL_STYLE_SQ_SOLID = 2; static final int PIXEL_STYLE_SCRIBBLE = 3; static final int PIXEL_STYLE_CIRCLE = 4; static final int PIXEL_STYLE_SAW = 5; PVector currentMachinePos = new PVector(); PVector currentCartesianMachinePos = new PVector(); int machineAvailMem = 0; int machineUsedMem = 0; int machineMinAvailMem = 2048; //String testPenWidthCommand = "TESTPENWIDTHSCRIBBLE,"; String testPenWidthCommand = CMD_TESTPENWIDTHSQUARE; float testPenWidthStartSize = 0.5; float testPenWidthEndSize = 2.0; float testPenWidthIncrementSize = 0.5; int machineStepMultiplier = 8; int maxSegmentLength = 2; static final String MODE_BEGIN = "button_mode_begin"; static final String MODE_DRAW_OUTLINE_BOX = "button_mode_drawOutlineBox"; static final String MODE_DRAW_OUTLINE_BOX_ROWS = "button_mode_drawOutlineBoxRows"; static final String MODE_DRAW_SHADE_BOX_ROWS_PIXELS = "button_mode_drawShadeBoxRowsPixels"; static final String MODE_RENDER_SQUARE_PIXELS = "button_mode_renderSquarePixel"; static final String MODE_RENDER_SAW_PIXELS = "button_mode_renderSawPixel"; static final String MODE_RENDER_CIRCLE_PIXELS = "button_mode_renderCirclePixel"; static final String MODE_RENDER_PIXEL_DIALOG = "button_mode_drawPixelsDialog"; static final String MODE_INPUT_ROW_START = "button_mode_inputRowStart"; static final String MODE_INPUT_ROW_END = "button_mode_inputRowEnd"; static final String MODE_DRAW_TESTPATTERN = "button_mode_drawTestPattern"; static final String MODE_DRAW_GRID = "button_mode_drawGrid"; static final String MODE_PLACE_IMAGE = "button_mode_placeImage"; static final String MODE_LOAD_IMAGE = "button_mode_loadImage"; static final String MODE_PAUSE_QUEUE = "button_mode_pauseQueue"; static final String MODE_RUN_QUEUE = "button_mode_runQueue"; static final String MODE_SET_POSITION_HOME = "button_mode_setPositionHome"; static final String MODE_RETURN_TO_HOME = "button_mode_returnToHome"; static final String MODE_INPUT_SINGLE_PIXEL = "button_mode_inputSinglePixel"; static final String MODE_DRAW_TEST_PENWIDTH = "button_mode_drawTestPenWidth"; static final String MODE_RENDER_SCALED_SQUARE_PIXELS = "button_mode_renderScaledSquarePixels"; static final String MODE_RENDER_SOLID_SQUARE_PIXELS = "button_mode_renderSolidSquarePixels"; static final String MODE_RENDER_SCRIBBLE_PIXELS = "button_mode_renderScribblePixels"; static final String MODE_CHANGE_MACHINE_SPEC = "button_mode_changeMachineSpec"; static final String MODE_REQUEST_MACHINE_SIZE = "button_mode_requestMachineSize"; static final String MODE_RESET_MACHINE = "button_mode_resetMachine"; static final String MODE_SAVE_PROPERTIES = "button_mode_saveProperties"; static final String MODE_SAVE_AS_PROPERTIES = "button_mode_saveAsProperties"; static final String MODE_LOAD_PROPERTIES = "button_mode_loadProperties"; static final String MODE_INC_SAMPLE_AREA = "button_mode_incSampleArea"; static final String MODE_DEC_SAMPLE_AREA = "button_mode_decSampleArea"; static final String MODE_INPUT_IMAGE = "button_mode_inputImage"; static final String MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD = "numberbox_mode_pixelBrightThreshold"; static final String MODE_IMAGE_PIXEL_DARK_THRESHOLD = "numberbox_mode_pixelDarkThreshold"; static final String MODE_CONVERT_BOX_TO_PICTUREFRAME = "button_mode_convertBoxToPictureframe"; static final String MODE_SELECT_PICTUREFRAME = "button_mode_selectPictureframe"; static final String MODE_EXPORT_QUEUE = "button_mode_exportQueue"; static final String MODE_IMPORT_QUEUE = "button_mode_importQueue"; static final String MODE_CLEAR_QUEUE = "button_mode_clearQueue"; static final String MODE_FIT_IMAGE_TO_BOX = "button_mode_fitImageToBox"; static final String MODE_RESIZE_IMAGE = "numberbox_mode_resizeImage"; static final String MODE_RENDER_COMMAND_QUEUE = "button_mode_renderCommandQueue"; static final String MODE_MOVE_IMAGE = "toggle_mode_moveImage"; static final String MODE_SET_POSITION = "toggle_mode_setPosition"; static final String MODE_INPUT_BOX_TOP_LEFT = "toggle_mode_inputBoxTopLeft"; static final String MODE_INPUT_BOX_BOT_RIGHT = "toggle_mode_inputBoxBotRight"; static final String MODE_DRAW_TO_POSITION = "toggle_mode_drawToPosition"; static final String MODE_DRAW_DIRECT = "toggle_mode_drawDirect"; static final String MODE_CHANGE_SAMPLE_AREA = "numberbox_mode_changeSampleArea"; static final String MODE_CHANGE_GRID_SIZE = "numberbox_mode_changeGridSize"; static final String MODE_SHOW_DENSITY_PREVIEW = "minitoggle_mode_showDensityPreview"; static final String MODE_SHOW_IMAGE = "minitoggle_mode_showImage"; static final String MODE_SHOW_QUEUE_PREVIEW = "minitoggle_mode_showQueuePreview"; static final String MODE_SHOW_VECTOR = "minitoggle_mode_showVector"; static final String MODE_SHOW_GUIDES = "minitoggle_mode_showGuides"; static final String MODE_CHANGE_MACHINE_WIDTH = "numberbox_mode_changeMachineWidth"; static final String MODE_CHANGE_MACHINE_HEIGHT = "numberbox_mode_changeMachineHeight"; static final String MODE_CHANGE_MM_PER_REV = "numberbox_mode_changeMMPerRev"; static final String MODE_CHANGE_STEPS_PER_REV = "numberbox_mode_changeStepsPerRev"; static final String MODE_CHANGE_STEP_MULTIPLIER = "numberbox_mode_changeStepMultiplier"; static final String MODE_CHANGE_PAGE_WIDTH = "numberbox_mode_changePageWidth"; static final String MODE_CHANGE_PAGE_HEIGHT = "numberbox_mode_changePageHeight"; static final String MODE_CHANGE_PAGE_OFFSET_X = "numberbox_mode_changePageOffsetX"; static final String MODE_CHANGE_PAGE_OFFSET_Y = "numberbox_mode_changePageOffsetY"; static final String MODE_CHANGE_PAGE_OFFSET_X_CENTRE = "button_mode_changePageOffsetXCentre"; static final String MODE_CHANGE_HOMEPOINT_X = "numberbox_mode_changeHomePointX"; static final String MODE_CHANGE_HOMEPOINT_Y = "numberbox_mode_changeHomePointY"; static final String MODE_CHANGE_HOMEPOINT_X_CENTRE = "button_mode_changeHomePointXCentre"; static final String MODE_CHANGE_PEN_WIDTH = "numberbox_mode_changePenWidth"; static final String MODE_SEND_PEN_WIDTH = "button_mode_sendPenWidth"; static final String MODE_CHANGE_PEN_TEST_START_WIDTH = "numberbox_mode_changePenTestStartWidth"; static final String MODE_CHANGE_PEN_TEST_END_WIDTH = "numberbox_mode_changePenTestEndWidth"; static final String MODE_CHANGE_PEN_TEST_INCREMENT_SIZE = "numberbox_mode_changePenTestIncrementSize"; static final String MODE_CHANGE_MACHINE_MAX_SPEED = "numberbox_mode_changeMachineMaxSpeed"; static final String MODE_CHANGE_MACHINE_ACCELERATION = "numberbox_mode_changeMachineAcceleration"; static final String MODE_SEND_MACHINE_SPEED = "button_mode_sendMachineSpeed"; static final String MODE_SEND_MACHINE_SPEED_PERSIST = "button_mode_sendMachineSpeedPersist"; static final String MODE_RENDER_VECTORS = "button_mode_renderVectors"; static final String MODE_LOAD_VECTOR_FILE = "button_mode_loadVectorFile"; static final String MODE_CHANGE_MIN_VECTOR_LINE_LENGTH = "numberbox_mode_changeMinVectorLineLength"; static final String MODE_CHANGE_SERIAL_PORT = "button_mode_serialPortDialog"; static final String MODE_SEND_MACHINE_STORE_MODE = "button_mode_machineStoreDialog"; static final String MODE_SEND_MACHINE_LIVE_MODE = "button_mode_sendMachineLiveMode"; static final String MODE_SEND_MACHINE_EXEC_MODE = "button_mode_machineExecDialog"; static final String MODE_RESIZE_VECTOR = "numberbox_mode_resizeVector"; static final String MODE_MOVE_VECTOR = "toggle_mode_moveVector"; static final String MODE_CHOOSE_CHROMA_KEY_COLOUR = "toggle_mode_chooseChromaKeyColour"; static final String MODE_CHANGE_PIXEL_SCALING = "numberbox_mode_changePixelScaling"; static final String MODE_PEN_LIFT_UP = "button_mode_penUp"; static final String MODE_PEN_LIFT_DOWN = "button_mode_penDown"; static final String MODE_PEN_LIFT_POS_UP = "numberbox_mode_penUpPos"; static final String MODE_PEN_LIFT_POS_DOWN = "numberbox_mode_penDownPos"; static final String MODE_SEND_PEN_LIFT_RANGE = "button_mode_sendPenliftRange"; static final String MODE_SEND_PEN_LIFT_RANGE_PERSIST = "button_mode_sendPenliftRangePersist"; static final String MODE_SEND_ROVE_AREA = "button_mode_sendRoveArea"; static final String MODE_SELECT_ROVE_IMAGE_SOURCE = "button_mode_selectRoveImageSource"; static final String MODE_SEND_START_TEXT = "toggle_mode_sendStartText"; // controls to do with text start static final String MODE_CHANGE_TEXT_ROW_SIZE = "numberbox_mode_changeTextRowSize"; static final String MODE_CHANGE_TEXT_ROW_SPACING = "numberbox_mode_changeTextRowSize"; static final String MODE_SHOW_WRITING_DIALOG = "button_mode_drawWritingDialog"; static final String MODE_START_SWIRLING = "button_mode_startSwirling"; static final String MODE_STOP_SWIRLING = "button_mode_stopSwirling"; static final String MODE_START_MARKING = "button_mode_startMarking"; static final String MODE_STOP_MARKING = "button_mode_stopMarking"; static final String MODE_START_SPRITE = "button_mode_drawSpriteDialog"; static final String MODE_START_RANDOM_SPRITES = "button_mode_startRandomSprite"; static final String MODE_STOP_RANDOM_SPRITES = "button_mode_stopRandomSprites"; static final String MODE_DRAW_NORWEGIAN_DIALOG = "button_mode_drawNorwegianDialog"; static final String MODE_LIVE_BLUR_VALUE = "numberbox_mode_liveBlurValue"; static final String MODE_LIVE_SIMPLIFICATION_VALUE = "numberbox_mode_liveSimplificationValue"; static final String MODE_LIVE_POSTERISE_VALUE = "numberbox_mode_livePosteriseValue"; static final String MODE_LIVE_CAPTURE_FROM_LIVE = "button_mode_liveCaptureFromLive"; static final String MODE_LIVE_CANCEL_CAPTURE = "button_mode_liveClearCapture"; static final String MODE_LIVE_ADD_CAPTION = "button_mode_liveAddCaption"; static final String MODE_LIVE_CONFIRM_DRAW = "button_mode_liveConfirmDraw"; static final String MODE_VECTOR_PATH_LENGTH_HIGHPASS_CUTOFF = "numberbox_mode_vectorPathLengthHighPassCutoff"; static final String MODE_SHOW_WEBCAM_RAW_VIDEO = "toggle_mode_showWebcamRawVideo"; static final String MODE_FLIP_WEBCAM_INPUT = "toggle_mode_flipWebcam"; static final String MODE_ROTATE_WEBCAM_INPUT = "toggle_mode_rotateWebcam"; static final String MODE_SEND_BUTTON_ACTIVATE = "button_mode_sendButtonActivate"; static final String MODE_SEND_BUTTON_DEACTIVATE = "button_mode_sendButtonDeactivate"; static final String MODE_ADJUST_PREVIEW_CORD_OFFSET = "numberbox_mode_previewCordOffsetValue"; static final String MODE_CYCLE_DENSITY_PREVIEW_STYLE = "dropdown_mode_cycleDensityPreviewStyle"; static final String MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE = "numberbox_mode_changeDensityPreviewPosterize"; static final String MODE_PREVIEW_PIXEL_DENSITY_RANGE = "minitoggle_mode_previewPixelDensityRange"; static final String MODE_CHANGE_POLYGONIZER = "dropdown_mode_changePolygonizer"; static final String MODE_CHANGE_POLYGONIZER_LENGTH = "numberbox_mode_changePolygonizerLength"; static final String MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE = "numberbox_mode_changePolygonizerAdaptativeAngle"; static final String MODE_CHANGE_INVERT_MASK = "dropdown_mode_changeMaskInvert"; List polygonizerStyles = Arrays.asList("ADAPTATIVE", "UNIFORMLENGTH"); List invertMaskModes = Arrays.asList("Mask off", "Mask hides", "Mask shows"); static final int MASK_MODES_COUNT = 3; static final int MASK_IS_UNUSED = 0; static final int MASKED_COLOURS_ARE_HIDDEN = 1; static final int MASKED_COLOURS_ARE_SHOWN = 2; PVector statusTextPosition = new PVector(300.0, 12.0); static String currentMode = MODE_BEGIN; static String lastMode = MODE_BEGIN; static PVector boxVector1 = null; static PVector boxVector2 = null; static PVector rowsVector1 = null; static PVector rowsVector2 = null; static final float MASKED_PIXEL_BRIGHTNESS = -1.0; static int pixelExtractBrightThreshold = 255; static int pixelExtractDarkThreshold = 0; static boolean liftPenOnMaskedPixels = true; int numberOfPixelsTotal = 0; int numberOfPixelsCompleted = 0; Date timerStart = null; Date timeLastPixelStarted = null; boolean pixelTimerRunning = false; boolean displayingSelectedCentres = false; boolean displayingRowGridlines = false; boolean displayingInfoTextOnInputPage = false; boolean displayingGridSpots = true; boolean displayingImage = true; boolean displayingVector = true; boolean displayingQueuePreview = true; boolean displayingDensityPreview = false; boolean displayingGuides = true; List densityPreviewStyles = Arrays.asList("Round", "Diamond", "Native Simple", "Native Arc", "Round size", "Native size"); static final int DENSITY_PREVIEW_STYLE_COUNT = 6; static final int DENSITY_PREVIEW_ROUND = 0; static final int DENSITY_PREVIEW_DIAMOND = 1; static final int DENSITY_PREVIEW_NATIVE = 2; static final int DENSITY_PREVIEW_NATIVE_ARC = 3; static final int DENSITY_PREVIEW_ROUND_SIZE = 4; static final int DENSITY_PREVIEW_NATIVE_SIZE = 5; static final int DEFAULT_DENSITY_PREVIEW_STYLE = DENSITY_PREVIEW_NATIVE; int densityPreviewStyle = DEFAULT_DENSITY_PREVIEW_STYLE; int densityPreviewPosterize = 255; boolean previewPixelDensityRange = true; static final byte COORD_MODE_NATIVE_STEPS = 0; static final byte COORD_MODE_NATIVE_MM = 1; static final byte COORD_MODE_CARTESIAN_MM_ABS = 2; static final byte COORD_MODE_CARTESIAN_MM_SCALED = 3; boolean useSerialPortConnection = false; static final char BITMAP_BACKGROUND_COLOUR = 0x0F; PVector homePointCartesian = null; public color chromaKeyColour = color(0,255,0); public int invertMaskMode = MASK_IS_UNUSED; // used in the preview page public color pageColour = color(220); public color frameColour = color(200,0,0); public color machineColour = color(150); public color guideColour = color(255); public color backgroundColour = color(100); public color densityPreviewColour = color(0); public Integer previewCordOffset = 0; public boolean debugPanels = false; public boolean showingSummaryOverlay = true; public boolean showingDialogBox = false; public Integer windowWidth = 650; public Integer windowHeight = 400; public static Integer serialPortNumber = -1; public Textarea consoleArea = null; public Println console = null; public PrintStream savedOut = null; Properties props = null; public static String propertiesFilename = "default.properties.txt"; public static String newPropertiesFilename = null; public static final String TAB_NAME_INPUT= "default"; public static final String TAB_LABEL_INPUT = "input"; public static final String TAB_NAME_ROVING = "tab_roving"; public static final String TAB_LABEL_ROVING = "Roving"; public static final String TAB_NAME_DETAILS = "tab_details"; public static final String TAB_LABEL_DETAILS = "Setup"; public static final String TAB_NAME_QUEUE = "tab_queue"; public static final String TAB_LABEL_QUEUE = "Queue"; public static final String TAB_NAME_TRACE = "tab_trace"; public static final String TAB_LABEL_TRACE = "Trace"; // Page states public String currentTab = TAB_NAME_INPUT; public static final String PANEL_NAME_INPUT = "panel_input"; public static final String PANEL_NAME_ROVING = "panel_roving"; public static final String PANEL_NAME_DETAILS = "panel_details"; public static final String PANEL_NAME_QUEUE = "panel_queue"; public static final String PANEL_NAME_TRACE = "panel_trace"; public static final String PANEL_NAME_GENERAL = "panel_general"; public final PVector DEFAULT_CONTROL_SIZE = new PVector(100.0, 20.0); public final PVector CONTROL_SPACING = new PVector(4.0, 4.0); public PVector mainPanelPosition = new PVector(10.0, 85.0); public final Integer PANEL_MIN_HEIGHT = 400; public Set panelNames = null; public List tabNames = null; public Set controlNames = null; public Map> controlsForPanels = null; public Map allControls = null; public Map controlLabels = null; public Set controlsToLockIfBoxNotSpecified = null; public Set controlsToLockIfImageNotLoaded = null; public Map> panelsForTabs = null; public Map panels = null; // machine moving PVector machineDragOffset = new PVector (0.0, 0.0); PVector lastMachineDragPosition = new PVector (0.0, 0.0); public final float MIN_SCALING = 0.01; public final float MAX_SCALING = 30.0; RShape vectorShape = null; String vectorFilename = null; float vectorScaling = 100; PVector vectorPosition = new PVector(0.0,0.0); int minimumVectorLineLength = 2; public static final int VECTOR_FILTER_LOW_PASS = 0; public Set controlsToLockIfVectorNotLoaded = null; String storeFilename = "comm.txt"; boolean overwriteExistingStoreFile = true; static boolean drawingTraceShape = true; static boolean retraceShape = true; static boolean flipWebcamImage = false; static boolean rotateWebcamImage = false; static boolean confirmedDraw = false; static PImage liveImage = null; static PImage processedLiveImage = null; static PImage capturedImage = null; static PImage processedCapturedImage = null; static final Integer LIVE_SIMPLIFICATION_MIN = 1; static final Integer LIVE_SIMPLIFICATION_MAX = 32; static int pathLengthHighPassCutoff = 0; static final Integer PATH_LENGTH_HIGHPASS_CUTOFF_MAX = 10000; static final Integer PATH_LENGTH_HIGHPASS_CUTOFF_MIN = 0; BlobDetector blob_detector; int liveSimplification = 5; int blurValue = 1; int posterizeValue = 5; int sepKeyColour = color(0, 0, 255); Map colourSeparations = null; RShape traceShape = null; RShape captureShape = null; String shapeSavePath = "../../savedcaptures/"; String shapeSavePrefix = "shape-"; String shapeSaveExtension = ".svg"; static Float gcodeZAxisDrawingHeight = 1.0; //-0.125000; String filePath = null; static PApplet parentPapplet = null; boolean rescaleDisplayMachine = true; // Polygonization. It's a geomerative thing. int polygonizer = 0; float polygonizerLength = 0.0; float polygonizerAdaptativeAngle = 0.0F; void setup() { println("Running polargraph controller"); frame.setResizable(true); initLogging(); parentPapplet = this; try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } RG.init(this); loadFromPropertiesFile(); size(200, 200); size(windowWidth, windowHeight); this.cp5 = new ControlP5(this); initTabs(); String[] serialPorts = Serial.list(); println("Serial ports available on your machine:"); println(serialPorts); if (getSerialPortNumber() >= 0) { println("About to connect to serial port in slot " + getSerialPortNumber()); // Print a list of the serial ports, for debugging purposes: if (serialPorts.length > 0) { String portName = null; try { println("Get serial port no: "+getSerialPortNumber()); portName = serialPorts[getSerialPortNumber()]; myPort = new Serial(this, portName, getBaudRate()); //read bytes into a buffer until you get a linefeed (ASCII 10): myPort.bufferUntil('\n'); useSerialPortConnection = true; println("Successfully connected to port " + portName); } catch (Exception e) { println("Attempting to connect to serial port " + portName + " in slot " + getSerialPortNumber() + " caused an exception: " + e.getMessage()); } } else { println("No serial ports found."); useSerialPortConnection = false; } } else { useSerialPortConnection = false; } currentMode = MODE_BEGIN; preLoadCommandQueue(); changeTab(TAB_NAME_INPUT, TAB_NAME_INPUT); addEventListeners(); frameRate(8); noLoop(); } void fitDisplayMachineToWindow() { Rectangle gr = panels.get(PANEL_NAME_GENERAL).getOutline(); println(gr); Rectangle ir = panels.get(PANEL_NAME_INPUT).getOutline(); println(ir); float targetHeight = ir.getBottom() - gr.getTop() - CONTROL_SPACING.y; println("Target height is " + targetHeight + " pixels"); float machineHeight = getDisplayMachine().getOutline().getHeight(); println(machineHeight); machineScaling = (targetHeight / machineHeight); println(machineScaling); if (machineScaling < 0) { machineScaling = 1.0; } getDisplayMachine().getOffset().x = ((gr.getRight() > ir.getRight()) ? gr.getRight() : ir.getRight()) + CONTROL_SPACING.x; getDisplayMachine().getOffset().y = gr.getTop(); } void addEventListeners() { frame.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent event) { windowResized(); if (event.getSource()==frame) { windowResized(); } } } ); addMouseWheelListener(new java.awt.event.MouseWheelListener() { public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { mouseWheel(evt.getWheelRotation()); } } ); } void preLoadCommandQueue() { addToCommandQueue(CMD_SETPENWIDTH+currentPenWidth+",END"); addToCommandQueue(CMD_SETMOTORSPEED+currentMachineMaxSpeed+",END"); addToCommandQueue(CMD_SETMOTORACCEL+currentMachineAccel+",END"); } void windowResized() { noLoop(); windowWidth = frame.getWidth(); windowHeight = frame.getHeight(); println("New window size: " + windowWidth + " x " + windowHeight); if (frame.getExtendedState() == Frame.MAXIMIZED_BOTH) { println("Max"); frame.setExtendedState(0); frame.setSize(windowWidth, windowHeight); } for (String key : getPanels().keySet()) { Panel p = getPanels().get(key); p.setSizeByHeight(windowHeight - p.getOutline().getTop() - (DEFAULT_CONTROL_SIZE.y*2)); if (debugPanels) { println("Resize " + key + " to be " + p.getOutline().getWidth() + "px across, " + p.getOutline().getHeight() + "px tall"); } } // Required to tell CP5 to be able to use the new sized window // How does this work? cp5.setGraphics(this,0,0); loop(); } void draw() { if (getCurrentTab() == TAB_NAME_INPUT) { drawImagePage(); } else if (getCurrentTab() == TAB_NAME_QUEUE) { drawCommandQueuePage(); } else if (getCurrentTab() == TAB_NAME_DETAILS) { drawDetailsPage(); } else if (getCurrentTab() == TAB_NAME_ROVING) { drawRovingPage(); } else if (getCurrentTab() == TAB_NAME_TRACE) { drawTracePage(); } else { drawDetailsPage(); } if (isShowingSummaryOverlay()) { drawSummaryOverlay(); } if (isShowingDialogBox()) { drawDialogBox(); } if (drawbotReady) { dispatchCommandQueue(); } } String getCurrentTab() { return this.currentTab; } boolean isShowingSummaryOverlay() { return this.showingSummaryOverlay; } void drawSummaryOverlay() { } boolean isShowingDialogBox() { return false; } void drawDialogBox() { } String getVectorFilename() { return this.vectorFilename; } void setVectorFilename(String filename) { this.vectorFilename = filename; } RShape getVectorShape() { return this.vectorShape; } void setVectorShape(RShape shape) { this.vectorShape = shape; } color getPageColour() { return this.pageColour; } color getMachineColour() { return this.machineColour; } color getBackgroundColour() { return this.backgroundColour; } color getGuideColour() { return this.guideColour; } color getFrameColour() { return this.frameColour; } Panel getPanel(String panelName) { return getPanels().get(panelName); } void drawImagePage() { noLoop(); strokeWeight(1); background(getBackgroundColour()); noFill(); stroke(255, 150, 255, 100); strokeWeight(3); stroke(150); noFill(); if (rescaleDisplayMachine) { fitDisplayMachineToWindow(); rescaleDisplayMachine = false; } getDisplayMachine().draw(); drawMoveImageOutline(); stroke(255, 0, 0); for (Panel panel : getPanelsForTab(TAB_NAME_INPUT)) { panel.draw(); } stroke(200,200); text(propertiesFilename, getPanel(PANEL_NAME_GENERAL).getOutline().getLeft(), getPanel(PANEL_NAME_GENERAL).getOutline().getTop()-7); showGroupBox(); showCurrentMachinePosition(); try { if (displayingQueuePreview) previewQueue(); } catch (ConcurrentModificationException cme) { // not doing anything with this exception - I don't mind if it's wrong on the screen for a second or two. println("Caught the pesky ConcurrentModificationException: " + cme.getMessage()); } if (displayingInfoTextOnInputPage) showText(250,45); drawStatusText((int)statusTextPosition.x, (int)statusTextPosition.y); showCommandQueue((int) getDisplayMachine().getOutline().getRight()+6, 20); } void drawMachineOutline() { rect(machinePosition.x,machinePosition.y, machinePosition.x+getDisplayMachine().getWidth(), machinePosition.y+getDisplayMachine().getHeight()); } void drawDetailsPage() { strokeWeight(1); background(100); noFill(); stroke(255, 150, 255, 100); strokeWeight(3); stroke(150); noFill(); getDisplayMachine().drawForSetup(); stroke(255, 0, 0); for (Panel panel : getPanelsForTab(TAB_NAME_DETAILS)) { panel.draw(); } text(propertiesFilename, getPanel(PANEL_NAME_GENERAL).getOutline().getLeft(), getPanel(PANEL_NAME_GENERAL).getOutline().getTop()-7); // showCurrentMachinePosition(); if (displayingInfoTextOnInputPage) showText(250,45); drawStatusText((int)statusTextPosition.x, (int)statusTextPosition.y); showCommandQueue((int) getDisplayMachine().getOutline().getRight()+6, 20); } void drawRovingPage() { strokeWeight(1); background(100); noFill(); stroke(255, 150, 255, 100); strokeWeight(3); stroke(150); noFill(); getDisplayMachine().drawForSetup(); stroke(255, 0, 0); for (Panel panel : getPanelsForTab(TAB_NAME_ROVING)) { panel.draw(); } text(propertiesFilename, getPanel(PANEL_NAME_GENERAL).getOutline().getLeft(), getPanel(PANEL_NAME_GENERAL).getOutline().getTop()-7); // showCurrentMachinePosition(); showGroupBox(); showCurrentMachinePosition(); if (displayingInfoTextOnInputPage) showText(250,45); drawStatusText((int)statusTextPosition.x, (int)statusTextPosition.y); showCommandQueue((int) getDisplayMachine().getOutline().getRight()+6, 20); } void drawTracePage() { strokeWeight(1); background(100); noFill(); stroke(255, 150, 255, 100); strokeWeight(3); stroke(150); noFill(); getDisplayMachine().drawForTrace(); if (displayingImage && getDisplayMachine().imageIsReady() && retraceShape) { processedLiveImage = trace_processImageForTrace(getDisplayMachine().getImage()); colourSeparations = trace_buildSeps(processedLiveImage, sepKeyColour); traceShape = trace_traceImage(colourSeparations); drawingTraceShape = true; } stroke(255, 0, 0); for (Panel panel : getPanelsForTab(TAB_NAME_TRACE)) { panel.draw(); } text(propertiesFilename, getPanel(PANEL_NAME_GENERAL).getOutline().getLeft(), getPanel(PANEL_NAME_GENERAL).getOutline().getTop()-7); if (displayingInfoTextOnInputPage) showText(250,45); drawStatusText((int)statusTextPosition.x, (int)statusTextPosition.y); showCommandQueue((int) width-200, 20); // processGamepadInput(); // // if (displayGamepadOverlay) // displayGamepadOverlay(); } void drawCommandQueuePage() { cursor(ARROW); background(100); // machine outline fill(100); drawMachineOutline(); showingSummaryOverlay = false; int right = 0; for (Panel panel : getPanelsForTab(TAB_NAME_QUEUE)) { panel.draw(); float r = panel.getOutline().getRight(); if (r > right) right = (int) r; } text(propertiesFilename, getPanel(PANEL_NAME_GENERAL).getOutline().getLeft(), getPanel(PANEL_NAME_GENERAL).getOutline().getTop()-7); showCommandQueue(right, (int)mainPanelPosition.y); drawStatusText((int)statusTextPosition.x, (int)statusTextPosition.y); } void drawImageLoadPage() { drawImagePage(); } void drawMoveImageOutline() { if (MODE_MOVE_IMAGE == currentMode && getDisplayMachine().getImage() != null) { // get scaled size of the image PVector imageSize = getDisplayMachine().inMM(getDisplayMachine().getImageFrame().getSize()); PVector imageSizeOnScreen = getDisplayMachine().scaleToScreen(imageSize); imageSizeOnScreen.sub(getDisplayMachine().getOutline().getTopLeft()); PVector offset = new PVector(imageSizeOnScreen.x/2.0, imageSizeOnScreen.y/2.0); PVector mVect = getMouseVector(); PVector imagePos = new PVector(mVect.x-offset.x, mVect.y-offset.y); fill(80,50); noStroke(); rect(imagePos.x+imageSizeOnScreen.x, imagePos.y+4, 4, imageSizeOnScreen.y); rect(imagePos.x+4, imageSizeOnScreen.y+imagePos.y, imageSizeOnScreen.x-4, 4); tint(255,180); image(getDisplayMachine().getImage(), imagePos.x, imagePos.y, imageSizeOnScreen.x, imageSizeOnScreen.y); noTint(); // decorate image noFill(); } else if (MODE_MOVE_VECTOR == currentMode && getVectorShape() != null) { RPoint[][] pointPaths = getVectorShape().getPointsInPaths(); RG.ignoreStyles(); stroke(100); strokeWeight(1); // offset mouse vector so it grabs the centre of the shape PVector centroid = new PVector(getVectorShape().width/2, getVectorShape().height/2); centroid = PVector.mult(centroid, (vectorScaling/100)); PVector offsetMouseVector = PVector.sub(getDisplayMachine().scaleToDisplayMachine(getMouseVector()), centroid); if (pointPaths != null) { int increment = round((pointPaths.length/10.0)+0.5); println(increment); for (int i = 0; i mouseOverControls() { Set set = new HashSet(1); for (String key : getAllControls().keySet()) { if (getAllControls().get(key).isInside()) { set.add(getAllControls().get(key)); } } return set; } boolean isMachineClickable() { if (getCurrentTab() == TAB_NAME_INPUT) { return true; } else if (getCurrentTab() == TAB_NAME_ROVING) { return true; } else if (getCurrentTab() == TAB_NAME_QUEUE) { return false; } else if (getCurrentTab() == TAB_NAME_DETAILS) { return false; } else { return false; } } boolean isPanelClickable() { return true; } boolean isQueueClickable() { return true; } boolean mouseOverPanel() { boolean result = false; for (Panel panel : getPanelsForTab(currentTab)) { if (panel.getOutline().surrounds(getMouseVector())) { // println("Outline: " + panel.getOutline().toString()); // println("OVER PANEL!" + panel.getName()); result = true; break; } } return result; } boolean mouseOverQueue() { boolean result = true; if (mouseX < leftEdgeOfQueue || mouseX > rightEdgeOfQueue || mouseY < topEdgeOfQueue || mouseY > bottomEdgeOfQueue) result = false; return result; } void changeMachineScaling(int delta) { boolean scalingChanged = true; machineScaling += (delta * (machineScaling * 0.1)); if (machineScaling < MIN_SCALING) { machineScaling = MIN_SCALING; scalingChanged = false; } else if (machineScaling > MAX_SCALING) { machineScaling = MAX_SCALING; scalingChanged = false; } } boolean checkKey(int k) { if (keys.length >= k) { return keys[k]; } return false; } void keyReleased() { keys[keyCode] = false; } void keyPressed() { keys[keyCode] = true; //println("key: " + KeyEvent.getKeyText(keyCode)); //println("Keys: " + keys); //println("Keycode: " + keyCode); if (checkKey(CONTROL) && checkKey(KeyEvent.VK_PAGE_UP)) changeMachineScaling(1); else if (checkKey(CONTROL) && checkKey(KeyEvent.VK_PAGE_DOWN)) changeMachineScaling(-1); else if (checkKey(CONTROL) && checkKey(DOWN)) getDisplayMachine().getOffset().y = getDisplayMachine().getOffset().y + 10; else if (checkKey(CONTROL) && checkKey(UP)) getDisplayMachine().getOffset().y = getDisplayMachine().getOffset().y - 10; else if (checkKey(CONTROL) && checkKey(RIGHT)) getDisplayMachine().getOffset().x = getDisplayMachine().getOffset().x + 10; else if (checkKey(CONTROL) && checkKey(LEFT)) getDisplayMachine().getOffset().x = getDisplayMachine().getOffset().x - 10; else if (checkKey(KeyEvent.VK_ESCAPE)) key = 0; else if (checkKey(CONTROL) && checkKey(KeyEvent.VK_G)) { Toggle t = (Toggle) getAllControls().get(MODE_SHOW_GUIDES); if (displayingGuides) { minitoggle_mode_showGuides(false); t.setValue(0); } else { minitoggle_mode_showGuides(true); t.setValue(1); } t.update(); } else if (checkKey(CONTROL) && checkKey(KeyEvent.VK_C)) { toggleShowConsole(); } else if (checkKey(CONTROL) && checkKey(KeyEvent.VK_S)) { if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) displayingSelectedCentres = (displayingSelectedCentres) ? false : true; } else if (checkKey(CONTROL) && checkKey(KeyEvent.VK_I)) { displayingInfoTextOnInputPage = (displayingInfoTextOnInputPage) ? false : true; } else if (key == '#' ) { addToRealtimeCommandQueue(CMD_PENUP+"END"); } else if (key == '~') { addToRealtimeCommandQueue(CMD_PENDOWN+"END"); } else if (key == '<') { if (this.maxSegmentLength > 1) this.maxSegmentLength--; } else if (key == '>') { this.maxSegmentLength++; } } void mouseDragged() { loop(); if (mouseOverControls().isEmpty()) { if (mouseButton == CENTER) { machineDragged(); } else if (mouseButton == LEFT) { if (currentMode.equals(MODE_INPUT_BOX_TOP_LEFT)) { // dragging a selection area PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); setBoxVector2(pos); } } } } void mouseMoved() { loop(); } void mouseClicked() { if (mouseOverPanel()) { // changing mode } else { if (currentMode.equals(MODE_MOVE_IMAGE)) { PVector imageSize = getDisplayMachine().inMM(getDisplayMachine().getImageFrame().getSize()); PVector mVect = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); PVector offset = new PVector(imageSize.x/2.0, imageSize.y/2.0); PVector imagePos = new PVector(mVect.x-offset.x, mVect.y-offset.y); imagePos = getDisplayMachine().inSteps(imagePos); getDisplayMachine().getImageFrame().setPosition(imagePos.x, imagePos.y); if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); } else if (currentMode.equals(MODE_MOVE_VECTOR) && vectorShape != null) { // offset mouse vector so it grabs the centre of the shape PVector centroid = new PVector(getVectorShape().width/2, getVectorShape().height/2); centroid = PVector.mult(centroid, (vectorScaling/100)); PVector offsetMouseVector = PVector.sub(getDisplayMachine().scaleToDisplayMachine(getMouseVector()), centroid); vectorPosition = offsetMouseVector; } else if (mouseOverQueue()) { // stopping or starting println("queue clicked."); queueClicked(); } else if (mouseOverMachine()) { // picking coords machineClicked(); } } } void machineDragged() { if (mouseButton == CENTER) { PVector currentPos = getMouseVector(); PVector change = PVector.sub(currentPos, lastMachineDragPosition); lastMachineDragPosition = new PVector(currentPos.x, currentPos.y); PVector currentPosition = getDisplayMachine().getOutline().getPosition(); getDisplayMachine().getOffset().add(change); cursor(MOVE); } } void machineClicked() { if (mouseButton == LEFT) { leftButtonMachineClick(); } } void mousePressed() { // println("mouse pressed"); // println("mouse button: "+mouseButton); // println("Current mode: " +currentMode); if (mouseButton == CENTER) { middleButtonMachinePress(); lastMachineDragPosition = getMouseVector(); } else if (mouseButton == LEFT) { if (MODE_INPUT_BOX_TOP_LEFT.equals(currentMode) && mouseOverMachine()) { minitoggle_mode_showImage(true); minitoggle_mode_showDensityPreview(false); PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); setBoxVector1(pos); if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) { getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); // minitoggle_mode_showImage(false); // minitoggle_mode_showDensityPreview(true); } } else { // println("Do nothing."); } } } void mouseReleased() { if (mouseButton == LEFT) { if (MODE_INPUT_BOX_TOP_LEFT.equals(currentMode) && mouseOverMachine()) { PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); setBoxVector2(pos); if (isBoxSpecified()) { if (getBoxVector1().x > getBoxVector2().x) { float temp = getBoxVector1().x; getBoxVector1().x = getBoxVector2().x; getBoxVector2().x = temp; } if (getBoxVector1().y > getBoxVector2().y) { float temp = getBoxVector1().y; getBoxVector1().y = getBoxVector2().y; getBoxVector2().y = temp; } if (getDisplayMachine().pixelsCanBeExtracted()) { getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); minitoggle_mode_showImage(false); minitoggle_mode_showDensityPreview(true); getAllControls().get(MODE_SHOW_IMAGE).setValue(0); getAllControls().get(MODE_SHOW_DENSITY_PREVIEW).setValue(1); } } } } } void middleButtonMachinePress() { PVector machineDragOffset = PVector.sub(getMouseVector(), getDisplayMachine().getOutline().getPosition()); this.machineDragOffset = machineDragOffset; } void leftButtonMachineClick() { if (currentMode.equals(MODE_BEGIN)) currentMode = MODE_INPUT_BOX_TOP_LEFT; else if (currentMode.equals(MODE_SET_POSITION)) sendSetPosition(); else if (currentMode.equals(MODE_DRAW_DIRECT)) sendMoveToPosition(true); else if (currentMode.equals(MODE_DRAW_TO_POSITION)) sendMoveToPosition(false); else if (currentMode.equals(MODE_CHOOSE_CHROMA_KEY_COLOUR)) setChromaKey(getMouseVector()); else if (currentMode.equals(MODE_SEND_START_TEXT)) sendStartTextAtPoint(); } void mouseWheel(int delta) { noLoop(); if (mouseOverMachine()) { // get the mouse position on the machine, before changing the machine scaling PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); changeMachineScaling(delta); // now work out what the machine position needs to be to line the pos up with mousevector again PVector scaledPos = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); PVector change = PVector.sub(scaledPos, pos); // and adjust for the new scaling factor change.mult(machineScaling); // finally update the machine offset (position) getDisplayMachine().getOffset().add(change); } loop(); } void setChromaKey(PVector p) { color col = getDisplayMachine().getPixelAtScreenCoords(p); chromaKeyColour = col; rebuildPixels(); } void rebuildPixels() { if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) { getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); } } boolean isPreviewable(String command) { if (command.startsWith(CMD_CHANGELENGTHDIRECT) || command.startsWith(CMD_CHANGELENGTH) || command.startsWith(CMD_DRAWPIXEL)) { return true; } else { return false; } } boolean toggleShowConsole() { if (console == null) { savedOut = System.out; console = cp5.addConsole(consoleArea); consoleArea.setVisible(true); console.play(); } else { console.pause(); consoleArea.setVisible(false); cp5.remove(console); console = null; System.setOut(savedOut); } // println("Ow"); return console == null; } /** This will comb the command queue and attempt to draw a picture of what it contains. Coordinates here are in pixels. */ void previewQueue() { previewQueue(false); } void previewQueue(boolean forceRebuild) { PVector startPoint = null; if (forceRebuild || (commandQueue.hashCode() != lastCommandQueueHash)) { println("regenerating preview queue."); previewCommandList.clear(); for (String command : commandQueue) { if (command.startsWith(CMD_CHANGELENGTHDIRECT) || command.startsWith(CMD_CHANGELENGTH) || command.startsWith(CMD_DRAWPIXEL)) { String[] splitted = split(command, ","); PreviewVector pv = new PreviewVector(); pv.command = splitted[0]; String aLenStr = splitted[1]; String bLenStr = splitted[2]; PVector endPoint = new PVector(Integer.parseInt(aLenStr)+previewCordOffset, Integer.parseInt(bLenStr)+previewCordOffset); endPoint = getDisplayMachine().asCartesianCoords(endPoint); endPoint = getDisplayMachine().inMM(endPoint); pv.x = endPoint.x; pv.y = endPoint.y; pv.z = -1.0; if (command.startsWith(CMD_DRAWPIXEL)) { String densStr = splitted[4]; pv.z = Integer.parseInt(densStr); } previewCommandList.add(pv); } } lastCommandQueueHash = commandQueue.hashCode(); } for (PreviewVector pv : previewCommandList) { PVector p = (PVector) pv; p = getDisplayMachine().scaleToScreen(p); if (startPoint == null) { noStroke(); fill(255,0,255,150); startPoint = getDisplayMachine().scaleToScreen(currentMachinePos); ellipse(p.x, p.y, 20, 20); noFill(); } if (pv.command.equals(CMD_CHANGELENGTHDIRECT)) stroke(0); else stroke(200,0,0); line(startPoint.x, startPoint.y, p.x, p.y); startPoint = p; if (pv.z >= 0.0) { noStroke(); fill(255,pv.z,pv.z); ellipse(p.x, p.y, 5,5); noFill(); } // ellipse(p.x, p.y, 5,5); // Circle at each node } if (startPoint != null) { noStroke(); fill(200,0,0,128); ellipse(startPoint.x, startPoint.y, 15,15); noFill(); } } boolean isHiddenPixel(PVector p) { if ((p.z == MASKED_PIXEL_BRIGHTNESS) || (p.z > pixelExtractBrightThreshold) || (p.z < pixelExtractDarkThreshold)) return true; else return false; } void sizeImageToFitBox() { PVector boxSize = getDisplayMachine().inSteps(getBoxSize()); PVector boxPos = getDisplayMachine().inSteps(getBoxVector1()); println("image: " + boxSize); Rectangle r = new Rectangle(boxPos, boxSize); getDisplayMachine().setImageFrame(r); } void exportQueueToFile() { if (!commandQueue.isEmpty() || !realtimeCommandQueue.isEmpty()) { selectOutput("Enter a filename to save to:", "exportQueueToFile"); // Opens file chooser } } void exportQueueToFile(File selection) { if (selection != null) { filePath = selection.getAbsolutePath(); println("User selected " + filePath); // If a file was selected, print path to folder println("Output file: " + filePath); List allCommands = new ArrayList(realtimeCommandQueue); allCommands.addAll(commandQueue); String[] list = (String[]) allCommands.toArray(new String[0]); saveStrings(filePath, list); println("Completed queue export, " + list.length + " commands exported."); } } void fileSelected(File selection) { if (selection == null) { println("Window was closed or the user hit cancel."); filePath = null; } else { filePath = selection.getAbsolutePath(); println("User selected " + filePath); } } void importQueueFromFile() { commandQueue.clear(); selectInput("Select file to import queue from", "fileSelected"); if (filePath == null) { // nothing selected println("No input file was selected."); } else { println("Input file: " + filePath); String commands[] = loadStrings(filePath); commandQueue.addAll(Arrays.asList(commands)); println("Completed queue import, " + commandQueue.size() + " commands found."); } } void queueClicked() { int relativeCoord = (mouseY-topEdgeOfQueue); int rowClicked = relativeCoord / queueRowHeight; int totalCommands = commandQueue.size()+realtimeCommandQueue.size(); if (rowClicked < 1) // its the header - start or stop queue { if (commandQueueRunning) commandQueueRunning = false; else commandQueueRunning = true; } else if (rowClicked > 2 && rowClicked < totalCommands+3) // it's a command from the queue { int cmdNumber = rowClicked-2; if (commandQueueRunning) { // if its running, then clicking on a command will mark it as a pause point } else { // if it's not running, then clicking on a command row will remove it if (!realtimeCommandQueue.isEmpty()) { if (cmdNumber <= realtimeCommandQueue.size()) realtimeCommandQueue.remove(cmdNumber-1); else { cmdNumber-=(realtimeCommandQueue.size()+1); commandQueue.remove(cmdNumber); } } else { commandQueue.remove(cmdNumber-1); } } } } boolean isRowsSpecified() { if (rowsVector1 != null && rowsVector2 != null) return true; else return false; } boolean isBoxSpecified() { if (boxVector1 != null && boxVector2 != null) { return true; } else return false; } void setBoxVector1(PVector vec) { boxVector1 = vec; } void setBoxVector2(PVector vec) { boxVector2 = vec; } PVector getBoxVector1() { return this.boxVector1; } PVector getBoxVector2() { return this.boxVector2; } PVector getBoxVectorSize() { return PVector.sub(getBoxVector2(),getBoxVector1()); } float getSampleArea() { return this.sampleArea; } void resetQueue() { currentMode = MODE_BEGIN; commandQueue.clear(); realtimeCommandQueue.clear(); } void showText(int xPosOrigin, int yPosOrigin) { noStroke(); fill(0, 0, 0, 80); rect(xPosOrigin, yPosOrigin, 220, 550); textSize(12); fill(255); int tRow = 15; int textPositionX = xPosOrigin+4; int textPositionY = yPosOrigin+4; int tRowNo = 1; PVector screenCoordsCart = getMouseVector(); text(programTitle, textPositionX, textPositionY+(tRow*tRowNo++)); tRowNo++; text("Cursor position: " + mouseX + ", " + mouseY, textPositionX, textPositionY+(tRow*tRowNo++)); text("MM Per Step: " + getDisplayMachine().getMMPerStep(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Steps Per MM: " + getDisplayMachine().getStepsPerMM() ,textPositionX, textPositionY+(tRow*tRowNo++)); if (getDisplayMachine().getOutline().surrounds(screenCoordsCart)) { PVector posOnMachineCartesianInMM = getDisplayMachine().scaleToDisplayMachine(screenCoordsCart); text("Machine x/y mm: " + posOnMachineCartesianInMM.x+","+posOnMachineCartesianInMM.y, textPositionX, textPositionY+(tRow*tRowNo++)); PVector posOnMachineNativeInMM = getDisplayMachine().convertToNative(posOnMachineCartesianInMM); text("Machine a/b mm: " + posOnMachineNativeInMM.x+","+posOnMachineNativeInMM.y, textPositionX, textPositionY+(tRow*tRowNo++)); PVector posOnMachineNativeInSteps = getDisplayMachine().inSteps(posOnMachineNativeInMM); text("Machine a/b steps: " + posOnMachineNativeInSteps.x+","+posOnMachineNativeInSteps.y, textPositionX, textPositionY+(tRow*tRowNo++)); } else { text("Machine x/y mm: --,--", textPositionX, textPositionY+(tRow*tRowNo++)); text("Machine a/b mm: --,--", textPositionX, textPositionY+(tRow*tRowNo++)); text("Machine a/b steps: --,--", textPositionX, textPositionY+(tRow*tRowNo++)); } drawStatusText(textPositionX, textPositionY+(tRow*tRowNo++)); text(commandStatus, textPositionX, textPositionY+(tRow*tRowNo++)); text("Mode: " + currentMode, textPositionX, textPositionY+(tRow*tRowNo++)); // middle side text("Grid size: " + getGridSize(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Box width: " + getBoxWidth(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Box height: " + getBoxHeight(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Box offset left: " + getBoxPosition().x, textPositionX, textPositionY+(tRow*tRowNo++)); text("Box offset top: " + getBoxPosition().y, textPositionX, textPositionY+(tRow*tRowNo++)); text("Available memory: " + machineAvailMem + " (min: " + machineMinAvailMem +", used: "+ machineUsedMem+")", textPositionX, textPositionY+(tRow*tRowNo++)); text("Time cmd: " + getCurrentPixelTime() + ", total: " + getTimeSoFar(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Average time per cmd: " + getAveragePixelTime(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Time to go: " + getTimeRemainingMins() + " mins (" + getTimeRemainingSecs() + " secs)", textPositionX, textPositionY+(tRow*tRowNo++)); text("Commands sent: " + getPixelsCompleted() + ", remaining: " + getPixelsRemaining(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Estimated complete: " + getEstimatedCompletionTime(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Pixel sample area: " + sampleArea, textPositionX, textPositionY+(tRow*tRowNo++)); text("Pixel drawing scale: " + getPixelScalingOverGridSize(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Max line segment length: " + getMaxSegmentLength(), textPositionX, textPositionY+(tRow*tRowNo++)); text("Ignore vector lines shorter than: " + minimumVectorLineLength, textPositionX, textPositionY+(tRow*tRowNo++)); text("Zoom: " + machineScaling, textPositionX, textPositionY+(tRow*tRowNo++)); tRowNo++; text("Machine settings:", textPositionX, textPositionY+(tRow*tRowNo++)); text("Last sent pen width: " + currentPenWidth, textPositionX, textPositionY+(tRow*tRowNo++)); text("Last sent speed: " + currentMachineMaxSpeed, textPositionX, textPositionY+(tRow*tRowNo++)); text("Last sent accel: " + currentMachineAccel, textPositionX, textPositionY+(tRow*tRowNo++)); tRowNo++; text("Chroma key colour: ", textPositionX, textPositionY+(tRow*tRowNo)); fill(chromaKeyColour); stroke(255); strokeWeight(1); rect(textPositionX+120, textPositionY+(tRow*tRowNo)-15, 25, 15); noFill(); noStroke(); tRowNo++; } void drawStatusText(int x, int y) { String drawbotStatus = null; if (useSerialPortConnection) { if (isDrawbotConnected()) { if (drawbotReady) { fill(0, 200, 0); if (currentHardware >= HARDWARE_VER_MEGA_POLARSHIELD) drawbotStatus = "Polargraph READY! (PolargraphSD)"; else if (currentHardware >= HARDWARE_VER_MEGA) drawbotStatus = "Polargraph READY! (Mega)"; else if (currentHardware >= HARDWARE_VER_POLARPRO) drawbotStatus = "Polargraph READY! (PRO)"; else drawbotStatus = "Polargraph READY! (Uno)"; } else { fill(200, 200, 0); String busyDoing = lastCommand; if ("".equals(busyDoing)) busyDoing = commandHistory.get(commandHistory.size()-1); drawbotStatus = "BUSY: " + busyDoing; } } else { fill(255, 0, 0); drawbotStatus = "Polargraph is not connected."; } } else { fill(255, 0, 0); drawbotStatus = "No serial connection."; } text(drawbotStatus, x, y); fill(255); } void setCommandQueueFont() { textSize(12); fill(255); } void showCommandQueue(int xPos, int yPos) { setCommandQueueFont(); int tRow = 15; int textPositionX = xPos; int textPositionY = yPos; int tRowNo = 1; int commandQueuePos = textPositionY+(tRow*tRowNo++); topEdgeOfQueue = commandQueuePos-queueRowHeight; leftEdgeOfQueue = textPositionX; rightEdgeOfQueue = textPositionX+300; bottomEdgeOfQueue = height; drawCommandQueueStatus(textPositionX, commandQueuePos, 14); commandQueuePos+=queueRowHeight; text("Last command: " + ((commandHistory.isEmpty()) ? "-" : commandHistory.get(commandHistory.size()-1)), textPositionX, commandQueuePos); commandQueuePos+=queueRowHeight; text("Current command: " + lastCommand, textPositionX, commandQueuePos); commandQueuePos+=queueRowHeight; fill(128,255,255); int queueNumber = commandQueue.size()+realtimeCommandQueue.size(); for (String s : realtimeCommandQueue) { text((queueNumber--)+". "+ s, textPositionX, commandQueuePos); commandQueuePos+=queueRowHeight; } fill(255); try { // Write out the commands into the window, stop when you fall off the bottom of the window // Or run out of commands int commandNo = 0; while (commandQueuePos <= height && commandNo < commandQueue.size()) { String s = commandQueue.get(commandNo); text((queueNumber--)+". "+ s, textPositionX, commandQueuePos); commandQueuePos+=queueRowHeight; commandNo++; } } catch (ConcurrentModificationException cme) { // not doing anything with this exception - I don't mind if it's wrong on the screen for a second or two. println("Caught the pesky ConcurrentModificationException: " + cme.getMessage()); } showmachineMessageLog(rightEdgeOfQueue, 20); } void drawCommandQueueStatus(int x, int y, int tSize) { String queueStatus = null; textSize(tSize); if (commandQueueRunning) { queueStatus = "QUEUE RUNNING - click to pause"; fill(0, 200, 0); } else { queueStatus = "QUEUE PAUSED - click to start"; fill(255, 0, 0); } text("CommandQueue: " + queueStatus, x, y); setCommandQueueFont(); } void showmachineMessageLog(int xPos, int yPos) { setCommandQueueFont(); int tRow = 15; int textPositionX = xPos; int textPositionY = yPos; int tRowNo = 1; int pos = textPositionY+(tRow*tRowNo++); pos+=queueRowHeight; fill(255); // Write out the commands into the window, stop when you fall off the bottom of the window // Or run out of commands int entryNo = machineMessageLog.size()-1; while (pos <= height && entryNo >= 0) { String s = machineMessageLog.get(entryNo); String type = s.substring(0,1); if ("E".equals(type)) fill(255,128,128); else if ("D".equals(type)) fill(50,50,50); else if ("I".equals(type)) fill(255); text(s, textPositionX, pos); pos+=queueRowHeight; entryNo--; } } long getCurrentPixelTime() { if (pixelTimerRunning) return new Date().getTime() - timeLastPixelStarted.getTime(); else return 0L; } long getAveragePixelTime() { if (pixelTimerRunning) { long msElapsed = timeLastPixelStarted.getTime() - timerStart.getTime(); int pixelsCompleted = getPixelsCompleted(); if (pixelsCompleted > 0) return msElapsed / pixelsCompleted; else return 0L; } else return 0L; } long getTimeSoFar() { if (pixelTimerRunning) return new Date().getTime() - timerStart.getTime(); else return 0L; } long getTimeRemaining() { if (pixelTimerRunning) return getTotalEstimatedTime() - getTimeSoFar(); else return 0L; } long getTotalEstimatedTime() { if (pixelTimerRunning) return (getAveragePixelTime() * numberOfPixelsTotal); else return 0L; } long getTimeRemainingSecs() { if (pixelTimerRunning) return getTimeRemaining() / 1000L; else return 0L; } long getTimeRemainingMins() { if (pixelTimerRunning) return getTimeRemainingSecs()/60L; else return 0L; } String getEstimatedCompletionTime() { if (pixelTimerRunning) { long totalTime = getTotalEstimatedTime()+timerStart.getTime(); return sdf.format(totalTime); } else return "TIMER NOT RUNNING"; } int getPixelsCompleted() { if (pixelTimerRunning) return numberOfPixelsCompleted-1; else return 0; } int getPixelsRemaining() { if (pixelTimerRunning) return numberOfPixelsTotal - getPixelsCompleted(); else return 0; } float getBoxWidth() { if (boxVector1 != null && boxVector2 != null) return (boxVector2.x-boxVector1.x); else return 0; } float getBoxHeight() { if (boxVector1 != null && boxVector2 != null) return (boxVector2.y-boxVector1.y); else return 0; } PVector getBoxSize() { PVector p = PVector.sub(getBoxVector2(), getBoxVector1()); return p; } PVector getBoxPosition() { if (boxVector1 != null) return boxVector1; else return new PVector(); } void clearBoxVectors() { setBoxVector1(null); setBoxVector2(null); getDisplayMachine().setExtractedPixels(null); } public PVector getHomePoint() { return this.homePointCartesian; } //public Machine getMachine() //{ // return this.machine; //} public DisplayMachine getDisplayMachine() { if (displayMachine == null) displayMachine = new DisplayMachine(new Machine(5000, 5000, 200.0, 95.0), machinePosition, machineScaling); displayMachine.setOffset(machinePosition); displayMachine.setScale(machineScaling); return displayMachine; } Integer getHardwareVersion() { return this.currentHardware; } void changeHardwareVersionTo(int newVer) { this.currentHardware = newVer; this.panelNames = null; this.tabNames = null; this.controlNames = null; this.controlsForPanels = null; this.panelsForTabs = null; this.panels = null; switch (newVer) { case HARDWARE_VER_MEGA : currentSram = HARDWARE_ATMEGA1280_SRAM; default : currentSram = HARDWARE_ATMEGA328_SRAM; } // windowResized(); } void setHardwareVersionFromIncoming(String readyString) { int newHardwareVersion = HARDWARE_VER_UNO; if ("READY".equals(readyString)) { newHardwareVersion = HARDWARE_VER_UNO; } else { String ver = readyString.substring(6); int verInt = HARDWARE_VER_UNO; try { verInt = Integer.parseInt(ver); } catch (NumberFormatException nfe) { println("Bad format for hardware version - defaulting to ATMEGA328 (Uno)"); verInt = HARDWARE_VER_UNO; } if (HARDWARE_VER_MEGA == verInt || HARDWARE_VER_MEGA_POLARSHIELD == verInt) newHardwareVersion = verInt; else newHardwareVersion = HARDWARE_VER_UNO; } // now see if it's different to last time. if (newHardwareVersion != currentHardware) { // and make the controller reflect the new hardware. changeHardwareVersionTo(newHardwareVersion); } } void serialEvent(Serial myPort) { // read the serial buffer: String incoming = myPort.readStringUntil('\n'); myPort.clear(); // if you got any bytes other than the linefeed: incoming = trim(incoming); println("incoming: " + incoming); if (incoming.startsWith("READY")) { drawbotReady = true; setHardwareVersionFromIncoming(incoming); } else if (incoming.startsWith("MSG")) readMachineMessage(incoming); else if (incoming.startsWith("SYNC")) readMachinePosition(incoming); else if (incoming.startsWith("CARTESIAN")) readCartesianMachinePosition(incoming); else if (incoming.startsWith("PGNAME")) readMachineName(incoming); else if (incoming.startsWith("PGSIZE")) readMachineSize(incoming); else if (incoming.startsWith("PGMMPERREV")) readMmPerRev(incoming); else if (incoming.startsWith("PGSTEPSPERREV")) readStepsPerRev(incoming); else if (incoming.startsWith("PGSTEPMULTIPLIER")) readStepMultiplier(incoming); else if (incoming.startsWith("PGLIFT")) readPenLiftRange(incoming); else if (incoming.startsWith("PGSPEED")) readMachineSpeed(incoming); else if ("RESEND".equals(incoming)) resendLastCommand(); else if ("DRAWING".equals(incoming)) drawbotReady = false; else if (incoming.startsWith("MEMORY")) extractMemoryUsage(incoming); else if (incoming.startsWith("BUTTON")) handleMachineButton(incoming); if (drawbotReady) drawbotConnected = true; } void handleMachineButton(String msg) { machineMessageLog.add(msg); } void extractMemoryUsage(String mem) { String[] splitted = split(mem, ","); if (splitted.length == 3) { machineAvailMem = Integer.parseInt(splitted[1]); machineUsedMem = currentSram - machineAvailMem; if (machineAvailMem < machineMinAvailMem) machineMinAvailMem = machineAvailMem; } } void readMachineMessage(String msg) { msg = msg.substring(4, msg.length()); String type = msg.substring(0,1); msg = msg.substring(2, msg.length()); String timestamp = new SimpleDateFormat("HH:mm:ss").format(new Date()); msg = type + timestamp + " " + msg; machineMessageLog.add(msg); if (machineMessageLog.size() > 200) { machineMessageLog.remove(0); } } void readMachinePosition(String sync) { String[] splitted = split(sync, ","); int aPosIndex = 0; int bPosIndex = 0; if (splitted.length == 4) { aPosIndex = 1; bPosIndex = 2; } else if (splitted.length == 5) { aPosIndex = 2; bPosIndex = 3; } if (aPosIndex != 0) { String currentAPos = splitted[aPosIndex]; String currentBPos = splitted[bPosIndex]; Float a = Float.valueOf(currentAPos).floatValue(); Float b = Float.valueOf(currentBPos).floatValue(); currentMachinePos.x = a; currentMachinePos.y = b; currentMachinePos = getDisplayMachine().inMM(getDisplayMachine().asCartesianCoords(currentMachinePos)); } else if (splitted.length == 5) { String currentAPos = splitted[2]; String currentBPos = splitted[3]; Float a = Float.valueOf(currentAPos).floatValue(); Float b = Float.valueOf(currentBPos).floatValue(); currentMachinePos.x = a; currentMachinePos.y = b; currentMachinePos = getDisplayMachine().inMM(getDisplayMachine().asCartesianCoords(currentMachinePos)); } } void readCartesianMachinePosition(String sync) { String[] splitted = split(sync, ","); if (splitted.length == 4) { String currentAPos = splitted[1]; String currentBPos = splitted[2]; Float a = Float.valueOf(currentAPos).floatValue(); Float b = Float.valueOf(currentBPos).floatValue(); currentCartesianMachinePos.x = a; currentCartesianMachinePos.y = b; } } void readMmPerRev(String in) { String[] splitted = split(in, ","); if (splitted.length == 3) { String mmStr = splitted[1]; float mmPerRev = Float.parseFloat(mmStr); getDisplayMachine().setMMPerRev(mmPerRev); updateNumberboxValues(); } } void readStepsPerRev(String in) { String[] splitted = split(in, ","); if (splitted.length == 3) { String stepsStr = splitted[1]; Float stepsPerRev = Float.parseFloat(stepsStr); getDisplayMachine().setStepsPerRev(stepsPerRev); updateNumberboxValues(); } } void readStepMultiplier(String in) { String[] splitted = split(in, ","); if (splitted.length == 3) { String stepsStr = splitted[1]; machineStepMultiplier = Integer.parseInt(stepsStr); updateNumberboxValues(); } } void readMachineSize(String in) { String[] splitted = split(in, ","); if (splitted.length == 4) { String mWidth = splitted[1]; String mHeight = splitted[2]; Integer intWidth = Integer.parseInt(mWidth); Integer intHeight = Integer.parseInt(mHeight); float fWidth = getDisplayMachine().inSteps(intWidth); float fHeight = getDisplayMachine().inSteps(intHeight); getDisplayMachine().setSize(int(fWidth+0.5), int(fHeight+0.5)); updateNumberboxValues(); } } void readMachineName(String sync) { String[] splitted = split(sync, ","); if (splitted.length == 3) { String name = splitted[1]; } } void readMachineSpeed(String in) { String[] splitted = split(in, ","); if (splitted.length == 4) { String speed = splitted[1]; String accel = splitted[2]; currentMachineMaxSpeed = Float.parseFloat(speed); currentMachineAccel = Float.parseFloat(accel); updateNumberboxValues(); } } void readPenLiftRange(String in) { String[] splitted = split(in, ","); if (splitted.length == 4) { String downPos = splitted[1]; String upPos = splitted[2]; penLiftDownPosition = Integer.parseInt(downPos); penLiftUpPosition = Integer.parseInt(upPos); updateNumberboxValues(); } } void resendLastCommand() { println("Re-sending command: " + lastCommand); myPort.write(lastCommand); drawbotReady = false; } void dispatchCommandQueue() { if (isDrawbotReady() && (!commandQueue.isEmpty() || !realtimeCommandQueue.isEmpty()) && commandQueueRunning) { if (pixelTimerRunning) { timeLastPixelStarted = new Date(); numberOfPixelsCompleted++; } if (!realtimeCommandQueue.isEmpty()) { String command = realtimeCommandQueue.get(0); lastCommand = command; realtimeCommandQueue.remove(0); println("Dispatching PRIORITY command: " + command); } else { String command = commandQueue.get(0); lastCommand = command; commandQueue.remove(0); println("Dispatching command: " + command); } // Checksum crc = new CRC32(); // crc.update(lastCommand.getBytes(), 0, lastCommand.length()); // lastCommand = lastCommand+":"+crc.getValue(); println("Last command:" + lastCommand); myPort.write(lastCommand); myPort.write(10); // OH *$%! of COURSE you should terminate it. drawbotReady = false; } else if (commandQueue.isEmpty()) { stopPixelTimer(); } } void addToCommandQueue(String command) { synchronized (commandQueue) { commandQueue.add(command); } } synchronized void addToRealtimeCommandQueue(String command) { synchronized (realtimeCommandQueue) { realtimeCommandQueue.add(command); } } void startPixelTimer() { timerStart = new Date(); timeLastPixelStarted = timerStart; pixelTimerRunning = true; } void stopPixelTimer() { pixelTimerRunning = false; } boolean isDrawbotReady() { return drawbotReady; } boolean isDrawbotConnected() { return drawbotConnected; } Properties getProperties() { if (props == null) { FileInputStream propertiesFileStream = null; try { props = new Properties(); String fileToLoad = sketchPath(propertiesFilename); File propertiesFile = new File(fileToLoad); if (!propertiesFile.exists()) { println("saving."); savePropertiesFile(); println("saved."); } propertiesFileStream = new FileInputStream(propertiesFile); props.load(propertiesFileStream); println("Successfully loaded properties file " + fileToLoad); } catch (IOException e) { println("Couldn't read the properties file - will attempt to create one."); println(e.getMessage()); } finally { try { propertiesFileStream.close(); } catch (Exception e) { println("Exception: "+e.getMessage()); }; } } return props; } void loadFromPropertiesFile() { getDisplayMachine().loadDefinitionFromProperties(getProperties()); this.pageColour = getColourProperty("controller.page.colour", color(220)); this.frameColour = getColourProperty("controller.frame.colour", color(200,0,0)); this.machineColour = getColourProperty("controller.machine.colour", color(150)); this.guideColour = getColourProperty("controller.guide.colour", color(255)); this.backgroundColour = getColourProperty("controller.background.colour", color(100)); this.densityPreviewColour = getColourProperty("controller.densitypreview.colour", color(0)); this.chromaKeyColour = getColourProperty("controller.pixel.mask.color", color(0,255,0)); // pen size this.currentPenWidth = getFloatProperty("machine.pen.size", 0.8); // motor settings this.currentMachineMaxSpeed = getFloatProperty("machine.motors.maxSpeed", 2000.0); this.currentMachineAccel = getFloatProperty("machine.motors.accel", 2000.0); this.machineStepMultiplier = getIntProperty("machine.step.multiplier", 8); // serial port this.serialPortNumber = getIntProperty("controller.machine.serialport", 0); this.baudRate = getIntProperty("controller.machine.baudrate", 57600); // row size this.gridSize = getFloatProperty("controller.grid.size", 100.0); this.sampleArea = getIntProperty("controller.pixel.samplearea", 2); this.pixelScalingOverGridSize = getFloatProperty("controller.pixel.scaling", 1.0); // pixel renderer this.densityPreviewStyle = getIntProperty("controller.density.preview.style", 1); // initial screen size this.windowWidth = getIntProperty("controller.window.width", 650); this.windowHeight = getIntProperty("controller.window.height", 400); println("windowHeight:" + this.windowHeight); this.testPenWidthStartSize = getFloatProperty("controller.testPenWidth.startSize", 0.5); this.testPenWidthEndSize = getFloatProperty("controller.testPenWidth.endSize", 2.0); this.testPenWidthIncrementSize = getFloatProperty("controller.testPenWidth.incrementSize", 0.5); this.maxSegmentLength = getIntProperty("controller.maxSegmentLength", 2); float homePointX = getFloatProperty("controller.homepoint.x", 0.0); float homePointY = getFloatProperty("controller.homepoint.y", 0.0); if (homePointX == 0.0) { float defaultX = getDisplayMachine().getWidth() / 2.0; // in steps float defaultY = getDisplayMachine().getPage().getTop(); // in steps // homePointX = getDisplayMachine().inMM(defaultX); // homePointY = getDisplayMachine().inMM(defaultY); println("Loading default homepoint."); } this.homePointCartesian = new PVector(getDisplayMachine().inSteps(homePointX), getDisplayMachine().inSteps(homePointY)); // println("home point loaded: " + homePointCartesian + ", " + getHomePoint()); // Geomerative stuff println("RG.ADAPTATIVE is " + RG.ADAPTATIVE); println("RG.UNIFORMLENGTH is " + RG.UNIFORMLENGTH); println("RG.UNIFORMSTEP is " + RG.UNIFORMSTEP); polygonizer = getIntProperty("controller.geomerative.polygonizer", RG.ADAPTATIVE); polygonizerLength = getFloatProperty("controller.geomerative.polygonizerLength", 1.0); setupPolygonizer(); setVectorFilename(getStringProperty("controller.vector.filename", null)); if (getVectorFilename() != null) { RShape shape = null; // test if file exists File f = new File(getVectorFilename()); if (f.isFile()) { shape = RG.loadShape(getVectorFilename()); } else { println("Tried to load vector file (" + getVectorFilename() + ") but I couldn't find it."); } if (shape != null) { setVectorShape(shape); } else { println("File not found (" + getVectorFilename() + ")"); } } vectorScaling = getFloatProperty("controller.vector.scaling", 100.0); getVectorPosition().x = getFloatProperty("controller.vector.position.x", 0.0); getVectorPosition().y = getFloatProperty("controller.vector.position.y", 0.0); this.minimumVectorLineLength = getIntProperty("controller.vector.minLineLength", 0); println("Finished loading configuration from properties file."); } void savePropertiesFile() { Properties props = new Properties(); props = getDisplayMachine().loadDefinitionIntoProperties(props); NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); DecimalFormat df = (DecimalFormat)nf; df.applyPattern("###.##"); props.setProperty("controller.page.colour", hex(this.pageColour, 6)); props.setProperty("controller.frame.colour", hex(this.frameColour,6)); props.setProperty("controller.machine.colour", hex(this.machineColour,6)); props.setProperty("controller.guide.colour", hex(this.guideColour,6)); props.setProperty("controller.background.colour", hex(this.backgroundColour,6)); props.setProperty("controller.densitypreview.colour", hex(this.densityPreviewColour,6)); // pen size props.setProperty("machine.pen.size", df.format(currentPenWidth)); // serial port props.setProperty("controller.machine.serialport", getSerialPortNumber().toString()); props.setProperty("controller.machine.baudrate", getBaudRate().toString()); // row size props.setProperty("controller.grid.size", new Float(gridSize).toString()); props.setProperty("controller.pixel.samplearea", df.format(sampleArea)); props.setProperty("controller.pixel.scaling", df.format(pixelScalingOverGridSize)); // density preview style props.setProperty("controller.density.preview.style", new Integer(getDensityPreviewStyle()).toString()); // initial screen size props.setProperty("controller.window.width", new Integer((windowWidth < 100) ? 100 : windowWidth-16).toString()); props.setProperty("controller.window.height", new Integer((windowWidth < 100) ? 100 : windowHeight-38).toString()); props.setProperty("controller.testPenWidth.startSize", df.format(testPenWidthStartSize)); props.setProperty("controller.testPenWidth.endSize", df.format(testPenWidthEndSize)); props.setProperty("controller.testPenWidth.incrementSize", df.format(testPenWidthIncrementSize)); props.setProperty("controller.maxSegmentLength", new Integer(getMaxSegmentLength()).toString()); props.setProperty("machine.motors.maxSpeed", df.format(currentMachineMaxSpeed)); props.setProperty("machine.motors.accel", df.format(currentMachineAccel)); props.setProperty("machine.step.multiplier", new Integer(machineStepMultiplier).toString()); props.setProperty("controller.pixel.mask.color", hex(this.chromaKeyColour, 6)); PVector hp = null; if (getHomePoint() != null) { hp = getHomePoint(); } else hp = new PVector(2000.0, 1000.0); hp = getDisplayMachine().inMM(hp); props.setProperty("controller.homepoint.x", df.format(hp.x)); props.setProperty("controller.homepoint.y", df.format(hp.y)); if (getVectorFilename() != null) props.setProperty("controller.vector.filename", getVectorFilename()); props.setProperty("controller.vector.scaling", df.format(vectorScaling)); props.setProperty("controller.vector.position.x", df.format(getVectorPosition().x)); props.setProperty("controller.vector.position.y", df.format(getVectorPosition().y)); props.setProperty("controller.vector.minLineLength", new Integer(this.minimumVectorLineLength).toString()); props.setProperty("controller.geomerative.polygonizer", new Integer(polygonizer).toString()); props.setProperty("controller.geomerative.polygonizerLength", df.format(polygonizerLength)); FileOutputStream propertiesOutput = null; try { //save the properties to a file File propertiesFile = new File(sketchPath(propertiesFilename)); if (propertiesFile.exists()) { propertiesOutput = new FileOutputStream(propertiesFile); Properties oldProps = new Properties(); FileInputStream propertiesFileStream = new FileInputStream(propertiesFile); oldProps.load(propertiesFileStream); oldProps.putAll(props); oldProps.store(propertiesOutput," *** Polargraph properties file *** "); println("Saved settings."); } else { // create it propertiesFile.createNewFile(); propertiesOutput = new FileOutputStream(propertiesFile); props.store(propertiesOutput," *** Polargraph properties file *** "); println("Created file."); } } catch (Exception e) { println("Exception occurred while creating new properties file: " + e.getMessage()); } finally { if (propertiesOutput != null) { try { propertiesOutput.close(); } catch (Exception e2) {println("what now!"+e2.getMessage());} } } } boolean getBooleanProperty(String id, boolean defState) { return boolean(getProperties().getProperty(id,""+defState)); } int getIntProperty(String id, int defVal) { return int(getProperties().getProperty(id,""+defVal)); } float getFloatProperty(String id, float defVal) { return float(getProperties().getProperty(id,""+defVal)); } String getStringProperty(String id, String defVal) { return getProperties().getProperty(id, defVal); } color getColourProperty(String id, color defVal) { color col = color(180); String colStr = getProperties().getProperty(id, ""); if ("".equals(colStr)) { col = defVal; } if (colStr.length() == 1) { // single value grey colStr = colStr+colStr; col = color(unhex(colStr)); } else if (colStr.length() == 3) { // 3 digit rgb String d1 = colStr.substring(0,1); String d2 = colStr.substring(1,2); String d3 = colStr.substring(2,3); d1 = d1+d1; d2 = d2+d2; d3 = d3+d3; col = color(unhex(d1), unhex(d2), unhex(d3)); } else if (colStr.length() == 6) { // 6 digit rgb String d1 = colStr.substring(0,2); String d2 = colStr.substring(2,4); String d3 = colStr.substring(4,6); col = color(unhex(d1), unhex(d2), unhex(d3)); } return col; } Integer getSerialPortNumber() { return this.serialPortNumber; } String getStoreFilename() { return this.storeFilename; } void setStoreFilename(String filename) { this.storeFilename = filename; } boolean getOverwriteExistingStoreFile() { return this.overwriteExistingStoreFile; } void setOverwriteExistingStoreFile(boolean over) { this.overwriteExistingStoreFile = over; } void initProperties() { getProperties(); } PVector getVectorPosition() { return vectorPosition; } float getPixelScalingOverGridSize() { return pixelScalingOverGridSize; } void setPixelScalingOverGridSize(float scaling) { pixelScalingOverGridSize = scaling; } int getDensityPreviewStyle() { return densityPreviewStyle; } Integer getBaudRate() { return baudRate; } void setupPolygonizer() { RG.setPolygonizer(polygonizer); // http://www.polargraph.co.uk/forum/polargraphs-group2/troubleshooting-forum5/svg-differences-between-polargraphcontroller-2-1-1-2-3-0-thread523.0 switch(polygonizer) { // case 0: // RG.setPolygonizerAngle(polygonizerAdaptativeAngle); // break; case 1: RG.setPolygonizerLength(polygonizerLength); break; } } void initLogging() { try { // logger = Logger.getLogger("uk.co.polargraph.controller"); // FileHandler fileHandler = new FileHandler("mylog.txt"); // fileHandler.setFormatter(new SimpleFormatter()); // logger.addHandler(fileHandler); // logger.setLevel(Level.INFO); // logger.info("Hello"); // if (isUseWindowedConsole()) // { // console = new Console(); // } // else // { // console.close(); // console = null; // } } catch(Exception e) { println("Exception setting up logger: " + e.getMessage()); } } PImage makeColourImage(int w, int h, int colour) { PImage img = createImage(w,h,RGB); for(int i=0; i < img.pixels.length; i++) { img.pixels[i] = colour; } return img; }