From a2fa25cfaafbffebe3d1b94764446b88e5deb660 Mon Sep 17 00:00:00 2001 From: Sandy Noble Date: Sun, 17 Feb 2013 22:52:07 +0000 Subject: [PATCH] Initial import This is the controller application. It's a Processing app. --- DisplayMachine.pde | 652 +++++++++ Machine.pde | 690 +++++++++ Misc.pde | 253 ++++ Panel.pde | 235 ++++ Rectangle.pde | 121 ++ controlsActions.pde | 1107 +++++++++++++++ controlsSetup.pde | 957 +++++++++++++ drawing.pde | 770 ++++++++++ gpl.txt | 674 +++++++++ polargraphcontroller.pde | 2865 ++++++++++++++++++++++++++++++++++++++ tabSetup.pde | 107 ++ 11 files changed, 8431 insertions(+) create mode 100644 DisplayMachine.pde create mode 100644 Machine.pde create mode 100644 Misc.pde create mode 100644 Panel.pde create mode 100644 Rectangle.pde create mode 100644 controlsActions.pde create mode 100644 controlsSetup.pde create mode 100644 drawing.pde create mode 100644 gpl.txt create mode 100644 polargraphcontroller.pde create mode 100644 tabSetup.pde diff --git a/DisplayMachine.pde b/DisplayMachine.pde new file mode 100644 index 0000000..af2974e --- /dev/null +++ b/DisplayMachine.pde @@ -0,0 +1,652 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ + */ + +class DisplayMachine extends Machine +{ + private Rectangle outline = null; + private float scaling = 1.0; + private Scaler scaler = null; + private PVector offset = null; + private float imageTransparency = 1.0; + + private Set extractedPixels = new HashSet(0); + + PImage scaledImage = null; + + private PVector currentPixel = null; + + public DisplayMachine(Machine m, PVector offset, float scaling) + { + // construct + super(m.getWidth(), m.getHeight(), m.getMMPerRev(), m.getStepsPerRev()); + + super.machineSize = m.machineSize; + + super.page = m.page; + super.imageFrame = m.imageFrame; + super.pictureFrame = m.pictureFrame; + + super.imageBitmap = m.imageBitmap; + super.imageFilename = m.imageFilename; + + super.stepsPerRev = m.stepsPerRev; + super.mmPerRev = m.mmPerRev; + + super.mmPerStep = m.mmPerStep; + super.stepsPerMM = m.stepsPerMM; + super.maxLength = m.maxLength; + super.gridSize = m.gridSize; + + this.offset = offset; + this.scaling = scaling; + this.scaler = new Scaler(scaling, 100.0); + + this.outline = null; + } + + public Rectangle getOutline() + { + outline = new Rectangle(offset, new PVector(sc(super.getWidth()), sc(super.getHeight()))); + return this.outline; + } + + private Scaler getScaler() + { + if (scaler == null) + this.scaler = new Scaler(getScaling(), getMMPerStep()); + return scaler; + } + + public void setScale(float scale) + { + this.scaling = scale; + this.scaler = new Scaler(scale, getMMPerStep()); + } + public float getScaling() + { + return this.scaling; + } + public float sc(float val) + { + return getScaler().scale(val); + } + public void setOffset(PVector offset) + { + this.offset = offset; + } + public PVector getOffset() + { + return this.offset; + } + public void setImageTransparency(float trans) + { + this.imageTransparency = trans; + } + public int getImageTransparency() + { + float f = 255.0 * this.imageTransparency; + f += 0.5; + int result = (int) f; + return result; + } + + public PVector getCurrentPixel() + { + return this.currentPixel; + } + public void setCurrentPixel(PVector p) + { + this.currentPixel = p; + } + + public void loadNewImageFromFilename(String filename) + { + super.loadImageFromFilename(filename); + super.sizeImageFrameToImageAspectRatio(); + this.setExtractedPixels(new HashSet(0)); + } + + public final int DROP_SHADOW_DISTANCE = 4; + public String getZoomText() + { + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###"); + String zoom = df.format(scaling * 100) + "% zoom"; + return zoom; + } + + public String getDimensionsAsText(Rectangle r) + { + return getDimensionsAsText(r.getSize()); + } + public String getDimensionsAsText(PVector p) + { + String dim = inMM(p.x) + " x " + inMM(p.y) + "mm"; + return dim; + } + + public void drawForSetup() + { + // work out the scaling factor. + noStroke(); + // draw machine outline + + // drop shadow + fill(80); + rect(getOutline().getLeft()+DROP_SHADOW_DISTANCE, getOutline().getTop()+DROP_SHADOW_DISTANCE, getOutline().getWidth(), getOutline().getHeight()); + + fill(getMachineColour()); + rect(getOutline().getLeft(), getOutline().getTop(), getOutline().getWidth(), getOutline().getHeight()); + text("machine " + getDimensionsAsText(getSize()) + " " + getZoomText(), getOutline().getLeft(), getOutline().getTop()); + + if (displayingGuides) + { + // draw some guides + stroke(getGuideColour()); + strokeWeight(1); + // centre line + line(getOutline().getLeft()+(getOutline().getWidth()/2), getOutline().getTop(), + getOutline().getLeft()+(getOutline().getWidth()/2), getOutline().getBottom()); + + // page top line + line(getOutline().getLeft(), getOutline().getTop()+sc(getHomePoint().y), + getOutline().getRight(), getOutline().getTop()+sc(getHomePoint().y)); + } + + // draw page + fill(getPageColour()); + rect(getOutline().getLeft()+sc(getPage().getLeft()), + getOutline().getTop()+sc(getPage().getTop()), + sc(getPage().getWidth()), + sc(getPage().getHeight())); + text("page " + getDimensionsAsText(getPage()), getOutline().getLeft()+sc(getPage().getLeft()), + getOutline().getTop()+sc(getPage().getTop())); + fill(0); + text("offset " + getDimensionsAsText(getPage().getPosition()), + getOutline().getLeft()+sc(getPage().getLeft()), + getOutline().getTop()+sc(getPage().getTop())+10); + noFill(); + + // draw home point + noFill(); + strokeWeight(5); + stroke(0, 128); + PVector onScreen = scaleToScreen(inMM(getHomePoint())); + ellipse(onScreen.x, onScreen.y, 15, 15); + strokeWeight(2); + stroke(255); + ellipse(onScreen.x, onScreen.y, 15, 15); + + text("Home point", onScreen.x+ 15, onScreen.y-5); + text(int(inMM(getHomePoint().x)+0.5) + ", " + int(inMM(getHomePoint().y)+0.5), onScreen.x+ 15, onScreen.y+15); + + + if (displayingGuides + && getOutline().surrounds(getMouseVector()) + && currentMode != MODE_MOVE_IMAGE + && mouseOverControls().isEmpty() + ) + { + drawHangingStrings(); + drawLineLengthTexts(); + cursor(CROSS); + } + else + { + cursor(ARROW); + } + } + + public void drawLineLengthTexts() + { + PVector actual = inMM(asNativeCoords(inSteps(scaleToDisplayMachine(getMouseVector())))); + PVector cart = scaleToDisplayMachine(getMouseVector()); + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.#"); + + text("Line 1: " + df.format(actual.x) + "mm", getDisplayMachine().getOutline().getLeft()+10, getDisplayMachine().getOutline().getTop()+18); + text("Line 2: " + df.format(actual.y) + "mm", getDisplayMachine().getOutline().getLeft()+10, getDisplayMachine().getOutline().getTop()+28); + + text("X Position: " + df.format(cart.x) + "mm", getDisplayMachine().getOutline().getLeft()+10, getDisplayMachine().getOutline().getTop()+42); + text("Y Position: " + df.format(cart.y) + "mm", getDisplayMachine().getOutline().getLeft()+10, getDisplayMachine().getOutline().getTop()+52); + } + + public void draw() + { + // work out the scaling factor. + noStroke(); + // draw machine outline + +// fill(80); +// rect(getOutline().getLeft()+DROP_SHADOW_DISTANCE, getOutline().getTop()+DROP_SHADOW_DISTANCE, getOutline().getWidth(), getOutline().getHeight()); + + fill(getMachineColour()); + rect(getOutline().getLeft(), getOutline().getTop(), getOutline().getWidth(), getOutline().getHeight()); + + + + if (displayingGuides) + { + // draw some guides + stroke(getGuideColour()); + strokeWeight(1); + // centre line + line(getOutline().getLeft()+(getOutline().getWidth()/2), getOutline().getTop(), + getOutline().getLeft()+(getOutline().getWidth()/2), getOutline().getBottom()); + + // page top line + line(getOutline().getLeft(), getOutline().getTop()+sc(getHomePoint().y), + getOutline().getRight(), getOutline().getTop()+sc(getHomePoint().y)); + } + + // draw page + fill(getPageColour()); + rect(getOutline().getLeft()+sc(getPage().getLeft()), + getOutline().getTop()+sc(getPage().getTop()), + sc(getPage().getWidth()), + sc(getPage().getHeight())); + text("page " + getDimensionsAsText(getPage()), getOutline().getLeft()+sc(getPage().getLeft()), + getOutline().getTop()+sc(getPage().getTop())-3); + noFill(); + + + + // draw actual image + if (displayingImage && imageIsReady()) + { + float ox = getOutline().getLeft()+sc(getImageFrame().getLeft()); + float oy = getOutline().getTop()+sc(getImageFrame().getTop()); + float w = sc(getImageFrame().getWidth()); + float h = sc(getImageFrame().getHeight()); + tint(255, getImageTransparency()); + image(getImage(), ox, oy, w, h); + noTint(); + strokeWeight(1); + stroke(150, 150, 150, 40); + rect(ox, oy, w-1, h-1); + fill(150, 150, 150, 40); + text("image", ox, oy-3); + noFill(); + } + + stroke(getBackgroundColour(),150); + strokeWeight(3); + noFill(); + rect(getOutline().getLeft()-2, getOutline().getTop()-2, getOutline().getWidth()+3, getOutline().getHeight()+3); + + stroke(getMachineColour(),150); + strokeWeight(3); + noFill(); + rect(getOutline().getLeft()+sc(getPage().getLeft())-2, + getOutline().getTop()+sc(getPage().getTop())-2, + sc(getPage().getWidth())+4, + sc(getPage().getHeight())+4); + + + + if (displayingSelectedCentres) + { + drawExtractedPixelCentres(); + } + if (displayingDensityPreview) + { + drawExtractedPixelDensities(); + } + if (displayingGuides) + { + drawPictureFrame(); + } + + if (displayingVector && getVectorShape() != null) + { + displayVectorImage(); + } + + if (displayingGuides + && getOutline().surrounds(getMouseVector()) + && currentMode != MODE_MOVE_IMAGE + && mouseOverControls().isEmpty() + ) + { + drawHangingStrings(); + drawRows(); + cursor(CROSS); + } + else + { + cursor(ARROW); + } + } + + public void displayVectorImage() + { + RPoint[][] pointPaths = getVectorShape().getPointsInPaths(); + RG.ignoreStyles(); + stroke(1); + if (pointPaths != null) + { + for(int i = 0; i= pixelExtractDarkThreshold)) + { + // scale em, danno. + PVector scaledPos = scaleToScreen(cartesianPos); + noStroke(); + fill(cartesianPos.z); + switch (getDensityPreviewStyle()) + { + case DENSITY_PREVIEW_ROUND: + previewRoundPixel(scaledPos, pixelSize, pixelSize); + break; + case DENSITY_PREVIEW_DIAMOND: + previewDiamondPixel(scaledPos, pixelSize, pixelSize, cartesianPos.z); + break; + default: + previewRoundPixel(scaledPos, pixelSize, pixelSize); + break; + } + } + } + } + noFill(); + } + + void previewDiamondPixel(PVector pos, float wide, float high, float brightness) + { + wide*=1.4; + high*=1.4; + // shall I try and draw a diamond here instead? OK! I'll do it! Ha! + float halfWidth = wide / 2.0; + float halfHeight = high / 2.0; + fill(0,0,0, 255-brightness); + quad(pos.x, pos.y-halfHeight, pos.x+halfWidth, pos.y, pos.x, pos.y+halfHeight, pos.x-halfWidth, pos.y); + + } + void previewNativePixel(PVector pos, float wide, float high) + { + // shall I try and draw a diamond here instead? OK! I'll do it! Ha! + float halfWidth = wide / 2.0; + float halfHeight = high / 2.0; + quad(pos.x, pos.y-halfHeight, pos.x+halfWidth, pos.y, pos.x, pos.y+halfHeight, pos.x-halfWidth, pos.y); + + } + void previewRoundPixel(PVector pos, float wide, float high) + { + ellipse(pos.x, pos.y, wide*1.1, high*1.1); + } + + color getPixelAtScreenCoords(PVector pos) + { + pos = scaleToDisplayMachine(pos); + pos = inSteps(pos); + float scalingFactor = getImage().width / getImageFrame().getWidth(); + color col = super.getPixelAtMachineCoords(pos, scalingFactor); + return col; + } + + Set getExtractedPixels() + { + return this.extractedPixels; + } + void setExtractedPixels(Set p) + { + this.extractedPixels = p; + } + + /* This will return a list of pixels that are included in the area in the + parameter. All coordinates are for the screen. + */ + Set getPixelsPositionsFromArea(PVector p, PVector s, float rowSize) + { + extractPixelsFromArea(p, s, rowSize, 0.0); + return getExtractedPixels(); + } + + public void extractPixelsFromArea(PVector p, PVector s, float rowSize, float sampleSize) + { + // get the native positions from the superclass + Set nativePositions = super.getPixelsPositionsFromArea(inSteps(p), inSteps(s), rowSize, sampleSize); + + // work out the cartesian positions + Set cartesianPositions = new HashSet(nativePositions.size()); + for (PVector nativePos : nativePositions) + { + // convert to cartesian + PVector displayPos = super.asCartesianCoords(nativePos); + displayPos = inMM(displayPos); + displayPos.z = nativePos.z; + cartesianPositions.add(displayPos); + } + setExtractedPixels(cartesianPositions); + } + + + public Set extractNativePixelsFromArea(PVector p, PVector s, float rowSize, float sampleSize) + { + // get the native positions from the superclass + Set nativePositions = super.getPixelsPositionsFromArea(inSteps(p), inSteps(s), rowSize, sampleSize); + return nativePositions; + } + + protected PVector snapToGrid(PVector loose, float rowSize) + { + PVector snapped = inSteps(loose); + snapped = super.snapToGrid(snapped, rowSize); + snapped = inMM(snapped); + return snapped; + } + + public boolean pixelsCanBeExtracted() + { + if (super.getImage() == null) + return false; + else + return true; + } +} + diff --git a/Machine.pde b/Machine.pde new file mode 100644 index 0000000..1788b7f --- /dev/null +++ b/Machine.pde @@ -0,0 +1,690 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ +/** +* +* +* +*/ +class Machine +{ + protected PVector machineSize = new PVector(4000,6000); + + protected Rectangle page = new Rectangle(1000,1000,2000,3000); + protected Rectangle imageFrame = new Rectangle(1500,1500,1000,1000); + protected Rectangle pictureFrame = new Rectangle(1600,1600,800,800); + + protected Float stepsPerRev = 800.0; + protected Float mmPerRev = 95.0; + + protected Float mmPerStep = null; + protected Float stepsPerMM = null; + protected Float maxLength = null; + protected Float gridSize = 100.0; + protected List gridLinePositions = null; + + protected PImage imageBitmap = null; + protected String imageFilename = null; + + + public Machine(Integer width, Integer height, Float stepsPerRev, Float mmPerRev) + { + this.setSize(width, height); + this.setStepsPerRev(stepsPerRev); + this.setMMPerRev(mmPerRev); + } + + public void setSize(Integer width, Integer height) + { + PVector s = new PVector(width, height); + this.machineSize = s; + maxLength = null; + } + public PVector getSize() + { + return this.machineSize; + } + public Float getMaxLength() + { + if (maxLength == null) + { + maxLength = dist(0,0, getWidth(), getHeight()); + } + return maxLength; + } + + public void setPage(Rectangle r) + { + this.page = r; + } + public Rectangle getPage() + { + return this.page; + } + public float getPageCentrePosition(float pageWidth) + { + return (getWidth()- pageWidth/2)/2; + } + + public void setImageFrame(Rectangle r) + { + this.imageFrame = r; + } + + public Rectangle getImageFrame() + { + return this.imageFrame; + } + + public void setPictureFrame(Rectangle r) + { + this.pictureFrame = r; + } + public Rectangle getPictureFrame() + { + return this.pictureFrame; + } + + public Integer getWidth() + { + return int(this.machineSize.x); + } + public Integer getHeight() + { + return int(this.machineSize.y); + } + + public void setStepsPerRev(Float s) + { + this.stepsPerRev = s; + } + public Float getStepsPerRev() + { + mmPerStep = null; + stepsPerMM = null; + return this.stepsPerRev; + } + public void setMMPerRev(Float d) + { + mmPerStep = null; + stepsPerMM = null; + this.mmPerRev = d; + } + public Float getMMPerRev() + { + return this.mmPerRev; + } + public Float getMMPerStep() + { + if (mmPerStep == null) + { + mmPerStep = mmPerRev / stepsPerRev; + } + return mmPerStep; + } + public Float getStepsPerMM() + { + if (stepsPerMM == null) + { + stepsPerMM = stepsPerRev / mmPerRev; + } + return stepsPerMM; + } + + public int inSteps(int inMM) + { + double steps = inMM * getStepsPerMM(); + steps += 0.5; + int stepsInt = (int) steps; + return stepsInt; + } + + public int inSteps(float inMM) + { + double steps = inMM * getStepsPerMM(); + steps += 0.5; + int stepsInt = (int) steps; + return stepsInt; + } + + public PVector inSteps(PVector mm) + { + PVector steps = new PVector(inSteps(mm.x), inSteps(mm.y)); + return steps; + } + + public int inMM(float steps) + { + double mm = steps / getStepsPerMM(); + mm += 0.5; + int mmInt = (int) mm; + return mmInt; + } + + public PVector inMM (PVector steps) + { + PVector mm = new PVector(inMM(steps.x), inMM(steps.y)); + return mm; + } + + float getPixelBrightness(PVector pos, float dim, float scalingFactor) + { + float averageBrightness = 255.0; + + if (getImageFrame().surrounds(pos)) + { + // offset it by image position to get position over image + PVector offsetPos = PVector.sub(pos, getImageFrame().getPosition()); + int originX = (int) offsetPos.x; + int originY = (int) offsetPos.y; + + PImage extractedPixels = null; + + extractedPixels = getImage().get(int(originX*scalingFactor), int(originY*scalingFactor), 1, 1); + extractedPixels.loadPixels(); + + if (dim >= 2) + { + int halfDim = (int)dim / (int)2.0; + + // restrict the sample area from going off the top/left edge of the image + float startX = originX - halfDim; + float startY = originY - halfDim; + + if (startX < 0) + startX = 0; + + if (startY < 0) + startY = 0; + + // and do the same for the bottom / right edges + float endX = originX+halfDim; + float endY = originY+halfDim; + + if (endX > getImageFrame().getWidth()) + endX = getImageFrame().getWidth(); + + if (endY > getImageFrame().getHeight()) + endY = getImageFrame().getHeight(); + + // now convert end coordinates to width/height + float dimWidth = (endX - startX)*scalingFactor; + float dimHeight = (endY - startY)*scalingFactor; + + dimWidth = (dimWidth < 1.0) ? 1.0 : dimWidth; + dimHeight = (dimHeight < 1.0) ? 1.0 : dimHeight; + startX = int(startX*scalingFactor); + startY = int(startY*scalingFactor); + + // get the block of pixels + extractedPixels = getImage().get(int(startX), int(startY), int(dimWidth+0.5), int(dimHeight+0.5)); + extractedPixels.loadPixels(); + } + + // going to go through them and total the brightnesses + int numberOfPixels = extractedPixels.pixels.length; + float totalPixelBrightness = 0; + for (int i = 0; i < numberOfPixels; i++) + { + color p = extractedPixels.pixels[i]; + float r = brightness(p); + totalPixelBrightness += r; + } + + // and get an average brightness for all of these pixels. + averageBrightness = totalPixelBrightness / numberOfPixels; + } + + return averageBrightness; + } + + color getPixelAtMachineCoords(PVector pos, float scalingFactor) + { + if (getImageFrame().surrounds(pos)) + { + // offset it by image position to get position over image + PVector offsetPos = PVector.sub(pos, getImageFrame().getPosition()); + int originX = (int) offsetPos.x; + int originY = (int) offsetPos.y; + + PImage centrePixel = null; + + centrePixel = getImage().get(int(originX*scalingFactor), int(originY*scalingFactor), 1, 1); + centrePixel.loadPixels(); + + color col = centrePixel.pixels[0]; + return col; + } + else + { + return 0; + } + } + + boolean isChromaKey(PVector pos, float scalingFactor) + { + if (getImageFrame().surrounds(pos)) + { + color col = getPixelAtMachineCoords(pos, scalingFactor); + + // get pixels from the vector coords + if (col == chromaKeyColour) + { +// println("is chroma key " + red(col) + ", "+green(col)+","+blue(col)); + return true; + } + else + { +// println("isn't chroma key " + red(col) + ", "+green(col)+","+blue(col)); + return false; + } + } + else return false; + } + + public PVector asNativeCoords(PVector cartCoords) + { + return asNativeCoords(cartCoords.x, cartCoords.y); + } + public PVector asNativeCoords(float cartX, float cartY) + { + float distA = dist(0,0,cartX, cartY); + float distB = dist(getWidth(),0,cartX, cartY); + PVector pgCoords = new PVector(distA, distB); + return pgCoords; + } + + + public PVector asCartesianCoords(PVector pgCoords) + { + float calcX = int((pow(getWidth(), 2) - pow(pgCoords.y, 2) + pow(pgCoords.x, 2)) / (getWidth()*2)); + float calcY = int(sqrt(pow(pgCoords.x,2)-pow(calcX,2))); + PVector vect = new PVector(calcX, calcY); + return vect; + } + + public Integer convertSizePreset(String preset) + { + Integer result = A3_SHORT; + if (preset.equalsIgnoreCase(PRESET_A3_SHORT)) + result = A3_SHORT; + else if (preset.equalsIgnoreCase(PRESET_A3_LONG)) + result = A3_LONG; + else if (preset.equalsIgnoreCase(PRESET_A2_SHORT)) + result = A2_SHORT; + else if (preset.equalsIgnoreCase(PRESET_A2_LONG)) + result = A2_LONG; + else if (preset.equalsIgnoreCase(PRESET_A2_IMP_SHORT)) + result = A2_IMP_SHORT; + else if (preset.equalsIgnoreCase(PRESET_A2_IMP_LONG)) + result = A2_IMP_LONG; + else if (preset.equalsIgnoreCase(PRESET_A1_SHORT)) + result = A1_SHORT; + else if (preset.equalsIgnoreCase(PRESET_A1_LONG)) + result = A1_LONG; + else + { + try + { + result = Integer.parseInt(preset); + } + catch (NumberFormatException nfe) + { + result = A3_SHORT; + } + } + return result; + } + + public void loadDefinitionFromProperties(Properties props) + { + // get these first because they are important to convert the rest of them + setStepsPerRev(getFloatProperty("machine.motors.stepsPerRev", 800.0)); + setMMPerRev(getFloatProperty("machine.motors.mmPerRev", 95.0)); + + // now stepsPerMM and mmPerStep should have been calculated. It's safe to get the rest. + + // machine size + setSize(inSteps(getIntProperty("machine.width", 600)), inSteps(getIntProperty("machine.height", 800))); + + // page size + String pageWidth = getStringProperty("controller.page.width", PRESET_A3_SHORT); + float pw = convertSizePreset(pageWidth); + String pageHeight = getStringProperty("controller.page.height", PRESET_A3_LONG); + float ph = convertSizePreset(pageHeight); + PVector pageSize = new PVector(pw, ph); + + // page position + String pos = getStringProperty("controller.page.position.x", "CENTRE"); + float px = 0.0; + println("machine size: " + getSize().x + ", " + inSteps(pageSize.x)); + if (pos.equalsIgnoreCase("CENTRE")) + { + px = inMM((getSize().x - pageSize.x) / 2.0); + } + else + px = getFloatProperty("controller.page.position.x", (int) getDisplayMachine().getPageCentrePosition(pageSize.x)); + + float py = getFloatProperty("controller.page.position.y", 120); + + PVector pagePos = new PVector(px, py); + Rectangle page = new Rectangle(inSteps(pagePos), inSteps(pageSize)); + setPage(page); + + // bitmap + setImageFilename(getStringProperty("controller.image.filename", "")); + loadImageFromFilename(imageFilename); + + // image position + Float offsetX = getFloatProperty("controller.image.position.x", 0.0); + Float offsetY = getFloatProperty("controller.image.position.y", 0.0); + PVector imagePos = new PVector(offsetX, offsetY); +// println("image pos: " + imagePos); + + // image size + Float imageWidth = getFloatProperty("controller.image.width", 500); + Float imageHeight = getFloatProperty("controller.image.height", 0); + if (imageHeight == 0) // default was set + { + println("Image height not supplied - creating default."); + if (getImage() != null) + { + float scaling = imageWidth / getImage().width; + imageHeight = getImage().height * scaling; + } + else + imageHeight = 500.0; + } + PVector imageSize = new PVector(imageWidth, imageHeight); + + Rectangle imageFrame = new Rectangle(inSteps(imagePos), inSteps(imageSize)); + setImageFrame(imageFrame); + + // picture frame size + PVector frameSize = new PVector(getIntProperty("controller.pictureframe.width", 200), getIntProperty("controller.pictureframe.height", 200)); + PVector framePos = new PVector(getIntProperty("controller.pictureframe.position.x", 200), getIntProperty("controller.pictureframe.position.y", 200)); + Rectangle frame = new Rectangle(inSteps(framePos), inSteps(frameSize)); + setPictureFrame(frame); + } + + + public Properties loadDefinitionIntoProperties(Properties props) + { + // Put keys into properties file: + props.setProperty("machine.motors.stepsPerRev", getStepsPerRev().toString()); + props.setProperty("machine.motors.mmPerRev", getMMPerRev().toString()); + + // machine width + props.setProperty("machine.width", Integer.toString((int) inMM(getWidth()))); + // machine.height + props.setProperty("machine.height", Integer.toString((int) inMM(getHeight()))); + + // image filename + props.setProperty("controller.image.filename", (getImageFilename() == null) ? "" : getImageFilename()); + + // image position + float imagePosX = 0.0; + float imagePosY = 0.0; + float imageWidth = 0.0; + float imageHeight = 0.0; + if (getImageFrame() != null) + { + imagePosX = getImageFrame().getLeft(); + imagePosY = getImageFrame().getTop(); + imageWidth = getImageFrame().getWidth(); + imageHeight = getImageFrame().getHeight(); + } + props.setProperty("controller.image.position.x", Integer.toString((int) inMM(imagePosX))); + props.setProperty("controller.image.position.y", Integer.toString((int) inMM(imagePosY))); + + // image size + props.setProperty("controller.image.width", Integer.toString((int) inMM(imageWidth))); + props.setProperty("controller.image.height", Integer.toString((int) inMM(imageHeight))); + + // page size + // page position + float pageSizeX = 0.0; + float pageSizeY = 0.0; + float pagePosX = 0.0; + float pagePosY = 0.0; + if (getPage() != null) + { + pageSizeX = getPage().getWidth(); + pageSizeY = getPage().getHeight(); + pagePosX = getPage().getLeft(); + pagePosY = getPage().getTop(); + } + props.setProperty("controller.page.width", Integer.toString((int) inMM(pageSizeX))); + props.setProperty("controller.page.height", Integer.toString((int) inMM(pageSizeY))); + props.setProperty("controller.page.position.x", Integer.toString((int) inMM(pagePosX))); + props.setProperty("controller.page.position.y", Integer.toString((int) inMM(pagePosY))); + + // picture frame size + float frameSizeX = 0.0; + float frameSizeY = 0.0; + float framePosX = 0.0; + float framePosY = 0.0; + if (getPictureFrame() != null) + { + frameSizeX = getPictureFrame().getWidth(); + frameSizeY = getPictureFrame().getHeight(); + framePosX = getPictureFrame().getLeft(); + framePosY = getPictureFrame().getTop(); + } + props.setProperty("controller.pictureframe.width", Integer.toString((int) inMM(frameSizeX))); + props.setProperty("controller.pictureframe.height", Integer.toString((int) inMM(frameSizeY))); + + // picture frame position + props.setProperty("controller.pictureframe.position.x", Integer.toString((int) inMM(framePosX))); + props.setProperty("controller.pictureframe.position.y", Integer.toString((int) inMM(framePosY))); + +// println("framesize: " + inMM(frameSizeX)); + + return props; + } + + protected void loadImageFromFilename(String filename) + { + if (filename != null && !"".equals(filename)) + { + // check for format etc here + println("loading from filename: " + filename); + this.imageBitmap = loadImage(filename); + this.imageFilename = filename; + } + else + { + this.imageBitmap = null; + this.imageFilename = null; + } + } + + public void sizeImageFrameToImageAspectRatio() + { + float scaling = getImageFrame().getWidth() / getImage().width; + float frameHeight = getImage().height * scaling; + getImageFrame().getSize().y = frameHeight; + } + + public void setImage(PImage b) + { + this.imageBitmap = b; + } + public void setImageFilename(String filename) + { + this.loadImageFromFilename(filename); + } + public String getImageFilename() + { + return this.imageFilename; + } + public PImage getImage() + { + return this.imageBitmap; + } + + public boolean imageIsReady() + { + if (imageBitmapIsLoaded()) + return true; + else + return false; + } + + public boolean imageBitmapIsLoaded() + { + if (getImage() != null) + return true; + else + return false; + } + + + protected void setGridSize(float gridSize) + { + this.gridSize = gridSize; + this.gridLinePositions = generateGridLinePositions(gridSize); + } + + /** + This takes in an area defined in cartesian steps, + and returns a set of pixels that are included + in that area. Coordinates are specified + in cartesian steps. The pixels are worked out + based on the gridsize parameter. d*/ + Set getPixelsPositionsFromArea(PVector p, PVector s, float gridSize, float sampleSize) + { + + // work out the grid + setGridSize(gridSize); + float maxLength = getMaxLength(); + float numberOfGridlines = maxLength / gridSize; + float gridIncrement = maxLength / numberOfGridlines; + List gridLinePositions = getGridLinePositions(gridSize); + + Rectangle selectedArea = new Rectangle (p.x,p.y, s.x,s.y); + + // now work out the scaling factor that'll be needed to work out + // the positions of the pixels on the bitmap. + float scalingFactor = getImage().width / getImageFrame().getWidth(); + + // now go through all the combinations of the two values. + Set nativeCoords = new HashSet(); + for (Float a : gridLinePositions) + { + for (Float b : gridLinePositions) + { + PVector nativeCoord = new PVector(a, b); + PVector cartesianCoord = asCartesianCoords(nativeCoord); + if (selectedArea.surrounds(cartesianCoord)) + { + if (isChromaKey(cartesianCoord, scalingFactor)) + { + nativeCoord.z = MASKED_PIXEL_BRIGHTNESS; // magic number + nativeCoords.add(nativeCoord); + } + else + { + if (sampleSize >= 1.0) + { + float brightness = getPixelBrightness(cartesianCoord, sampleSize, scalingFactor); + nativeCoord.z = brightness; + } + nativeCoords.add(nativeCoord); + } + } + } + } + + return nativeCoords; + } + + protected PVector snapToGrid(PVector loose, float gridSize) + { + List pos = getGridLinePositions(gridSize); + boolean higherupperFound = false; + boolean lowerFound = false; + + float halfGrid = gridSize / 2.0; + float x = loose.x; + float y = loose.y; + + Float snappedX = null; + Float snappedY = null; + + int i = 0; + while ((snappedX == null || snappedY == null) && i < pos.size()) + { + float upperBound = pos.get(i)+halfGrid; + float lowerBound = pos.get(i)-halfGrid; +// println("pos:" +pos.get(i) + "half: "+halfGrid+ ", upper: "+ upperBound + ", lower: " + lowerBound); + if (snappedX == null + && x > lowerBound + && x <= upperBound) + { + snappedX = pos.get(i); +// println("snappedX:" + snappedX); + } + + if (snappedY == null + && y > lowerBound + && y <= upperBound) + { + snappedY = pos.get(i); +// println("snappedY:" + snappedY); + } + + i++; + } + + PVector snapped = new PVector((snappedX == null) ? 0.0 : snappedX, (snappedY == null) ? 0.0 : snappedY); +// println("loose:" + loose); +// println("snapped:" + snapped); + return snapped; + } + + protected List getGridLinePositions(float gridSize) + { + setGridSize(gridSize); + return this.gridLinePositions; + } + + private List generateGridLinePositions(float gridSize) + { + List glp = new ArrayList(); + float maxLength = getMaxLength(); + for (float i = gridSize; i <= maxLength; i+=gridSize) + { + glp.add(i); + } + return glp; + } + + + +} diff --git a/Misc.pde b/Misc.pde new file mode 100644 index 0000000..c415e60 --- /dev/null +++ b/Misc.pde @@ -0,0 +1,253 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ + +class Scaler +{ + public float scale = 1.0; + public float mmPerStep = 1.0; + + public Scaler(float scale, float mmPerStep) + { + this.scale = scale; + this.mmPerStep = mmPerStep; + } + public void setScale(float scale) + { + this.scale = scale; + } + + public float scale(float in) + { + return in * mmPerStep * scale; + } +} + +class PreviewVector extends PVector +{ + public String command; +} + + +import java.awt.Toolkit; +import java.awt.BorderLayout; +import java.awt.GraphicsEnvironment; + +public class Console extends WindowAdapter implements WindowListener, ActionListener, Runnable +{ + private JFrame frame; + private JTextArea textArea; + private Thread reader; + private Thread reader2; + private boolean quit; + + private final PipedInputStream pin=new PipedInputStream(); + private final PipedInputStream pin2=new PipedInputStream(); + + private PrintStream cOut = System.out; + private PrintStream cErr = System.err; + + Thread errorThrower; // just for testing (Throws an Exception at this Console + + public Console() + { + // create all components and add them + frame=new JFrame("Java Console"); + Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize=new Dimension((int)(screenSize.width/2),(int)(screenSize.height/2)); + int x=(int)(frameSize.width/2); + int y=(int)(frameSize.height/2); + frame.setBounds(x,y,frameSize.width,frameSize.height); + + textArea=new JTextArea(); + textArea.setEditable(false); + JButton button=new JButton("clear"); + + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(new JScrollPane(textArea),BorderLayout.CENTER); + frame.getContentPane().add(button,BorderLayout.SOUTH); + frame.setVisible(true); + + frame.addWindowListener(this); + button.addActionListener(this); + + try + { + this.cOut = System.out; + PipedOutputStream pout=new PipedOutputStream(this.pin); + System.setOut(new PrintStream(pout,true)); + } + catch (java.io.IOException io) + { + textArea.append("Couldn't redirect STDOUT to this console\n"+io.getMessage()); + } + catch (SecurityException se) + { + textArea.append("Couldn't redirect STDOUT to this console\n"+se.getMessage()); + } + + try + { + this.cErr = System.err; + PipedOutputStream pout2=new PipedOutputStream(this.pin2); + System.setErr(new PrintStream(pout2,true)); + } + catch (java.io.IOException io) + { + textArea.append("Couldn't redirect STDERR to this console\n"+io.getMessage()); + } + catch (SecurityException se) + { + textArea.append("Couldn't redirect STDERR to this console\n"+se.getMessage()); + } + + quit=false; // signals the Threads that they should exit + + // Starting two seperate threads to read from the PipedInputStreams + // + reader=new Thread(this); + reader.setDaemon(true); + reader.start(); + // + reader2=new Thread(this); + reader2.setDaemon(true); + reader2.start(); + +// // testing part +// // you may omit this part for your application +// // +// System.out.println("Hello World 2"); +// System.out.println("All fonts available to Graphic2D:\n"); +// GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); +// String[] fontNames=ge.getAvailableFontFamilyNames(); +// for(int n=0;n. + + 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/ + http://code.google.com/p/polargraph/ +*/ +class Panel +{ + private Rectangle outline = null; + private String name = null; + private List controls = null; + private Map controlPositions = null; + private Map controlSizes = null; + private boolean resizable = true; + private float minimumHeight = DEFAULT_CONTROL_SIZE.y+4; + private color outlineColour = color(255); + + public final color CONTROL_COL_BG_DEFAULT = color(0,54,82); + public final color CONTROL_COL_BG_DISABLED = color(20,44,62); + public final color CONTROL_COL_LABEL_DEFAULT = color(255); + public final color CONTROL_COL_LABEL_DISABLED = color(200); + + public Panel(String name, Rectangle outline) + { + this.name = name; + this.outline = outline; + } + + public Rectangle getOutline() + { + return this.outline; + } + public void setOutline(Rectangle r) + { + this.outline = r; + } + + public String getName() + { + return this.name; + } + public void setName(String name) + { + this.name = name; + } + + public List getControls() + { + if (this.controls == null) + this.controls = new ArrayList(0); + return this.controls; + } + public void setControls(List c) + { + this.controls = c; + } + + public Map getControlPositions() + { + return this.controlPositions; + } + public void setControlPositions(Map cp) + { + this.controlPositions = cp; + } + + public Map getControlSizes() + { + return this.controlSizes; + } + public void setControlSizes(Map cs) + { + this.controlSizes = cs; + } + + void setOutlineColour(color c) + { + this.outlineColour = c; + } + + void setResizable(boolean r) + { + this.resizable = r; + } + boolean isResizable() + { + return this.resizable; + } + + void setMinimumHeight(float h) + { + this.minimumHeight = h; + } + float getMinimumHeight() + { + return this.minimumHeight; + } + + public void draw() + { +// stroke(outlineColour); +// strokeWeight(2); +// rect(getOutline().getLeft(), getOutline().getTop(), getOutline().getWidth(), getOutline().getHeight()); + + drawControls(); + } + + public void drawControls() + { + for (Controller c : this.getControls()) + { + PVector pos = getControlPositions().get(c.name()); + float x = pos.x+getOutline().getLeft(); + float y = pos.y+getOutline().getTop(); + c.setPosition(x, y); + + PVector cSize = getControlSizes().get(c.name()); + c.setSize((int)cSize.x, (int)cSize.y); + + boolean locked = false; + + // theres a few cases here where the controls are locked (disabled) + + // any drawing / extracting controls are disabled if there is no selec + // box specified. + if (getControlsToLockIfBoxNotSpecified().contains(c.name()) && !isBoxSpecified()) + { + locked = true; + } + + // if there is no vector shape loaded then lock the "draw vector" + // control. + if (c.name().equals(MODE_RENDER_VECTORS) && getVectorShape() == null) + { + locked = true; + } + + // if there's no image loaded, then hide resizing/moving + if (getControlsToLockIfImageNotLoaded().contains(c.name()) && getDisplayMachine().getImage() == null) + { + locked = true; + } + + if (c.name().equals(MODE_LOAD_VECTOR_FILE)) + { + if (getVectorShape() != null) + c.setLabel("Clear vector"); + else + c.setLabel("Load vector"); + } + else if (c.name().equals(MODE_LOAD_IMAGE)) + { + if (getDisplayMachine().getImage() != null) + c.setLabel("Clear image"); + else + c.setLabel("Load image file"); + } + + + int col = c.getColor().getBackground(); + setLock(c, locked); + } + } + + void setLock(Controller c, boolean locked) + { + c.setLock(locked); + if (locked) + { + c.setColorBackground(CONTROL_COL_BG_DISABLED); + c.setColorLabel(CONTROL_COL_LABEL_DISABLED); + } + else + { + c.setColorBackground(CONTROL_COL_BG_DEFAULT); + c.setColorLabel(CONTROL_COL_LABEL_DEFAULT); + } + } + + void setHeight(float h) + { + if (this.isResizable()) + { + if (h <= getMinimumHeight()) + this.getOutline().setHeight(getMinimumHeight()); + else + this.getOutline().setHeight(h); + setControlPositions(buildControlPositionsForPanel(this)); + + float left = 0.0; + String controlName = ""; + for (String key : getControlPositions().keySet()) + { + PVector pos = getControlPositions().get(key); + if (pos.x >= left) + { + left = pos.x; + controlName = key; + } + } + + Map map = getControlSizes(); + +// PVector size = getControlSizes().get(controlName); +// println("size: " + size); + float right = left + DEFAULT_CONTROL_SIZE.x; + + this.getOutline().setWidth(right); + } + } + + +} diff --git a/Rectangle.pde b/Rectangle.pde new file mode 100644 index 0000000..830ed6a --- /dev/null +++ b/Rectangle.pde @@ -0,0 +1,121 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ +class Rectangle +{ + public PVector position = null; + public PVector size = null; + + public Rectangle(float px, float py, float width, float height) + { + this.position = new PVector(px, py); + this.size = new PVector(width, height); + } + public Rectangle(PVector position, PVector size) + { + this.position = position; + this.size = size; + } + public Rectangle(Rectangle r) + { + this.position = new PVector(r.getPosition().x, r.getPosition().y); + this.size = new PVector(r.getSize().x, r.getSize().y); + } + + public float getWidth() + { + return this.size.x; + } + public void setWidth(float w) + { + this.size.x = w; + } + public float getHeight() + { + return this.size.y; + } + public void setHeight(float h) + { + this.size.y = h; + } + public PVector getPosition() + { + return this.position; + } + public PVector getSize() + { + return this.size; + } + public PVector getTopLeft() + { + return getPosition(); + } + public PVector getBotRight() + { + return PVector.add(this.position, this.size); + } + public float getLeft() + { + return getPosition().x; + } + public float getRight() + { + return getPosition().x + getSize().x; + } + public float getTop() + { + return getPosition().y; + } + public float getBottom() + { + return getPosition().y + getSize().y; + } + + public void setPosition(float x, float y) + { + if (this.position == null) + this.position = new PVector(x, y); + else + { + this.position.x = x; + this.position.y = y; + } + } + + public Boolean surrounds(PVector p) + { + if (p.x >= this.getLeft() + && p.x < this.getRight() + && p.y >= this.getTop() + && p.y < this.getBottom()-1) + return true; + else + return false; + } + +} diff --git a/controlsActions.pde b/controlsActions.pde new file mode 100644 index 0000000..9bc6e3e --- /dev/null +++ b/controlsActions.pde @@ -0,0 +1,1107 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ +void button_mode_begin() +{ + button_mode_clearQueue(); +} +void numberbox_mode_changeGridSize(float value) +{ + setGridSize(value); + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + } +} +void numberbox_mode_changeSampleArea(float value) +{ + setSampleArea(value); + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + } +} +void numberbox_mode_changePixelScaling(float value) +{ + setPixelScalingOverGridSize(value); +} +void minitoggle_mode_showImage(boolean flag) +{ + this.displayingImage = flag; +} +void minitoggle_mode_showVector(boolean flag) +{ + this.displayingVector = flag; +} +void minitoggle_mode_showDensityPreview(boolean flag) +{ + this.displayingDensityPreview = flag; +} +void minitoggle_mode_showQueuePreview(boolean flag) +{ + this.displayingQueuePreview = flag; +} +void minitoggle_mode_showGuides(boolean flag) +{ + this.displayingGuides = flag; +} +void unsetOtherToggles(String except) +{ + for (String name : getAllControls().keySet()) + { + if (name.startsWith("toggle_")) + { + if (name.equals(except)) + { +// println("not resetting this one."); + } + else + { + getAllControls().get(name).setValue(0); + } + } + } +} +void button_mode_penUp() +{ + addToCommandQueue(CMD_PENUP + "END"); +} +void button_mode_penDown() +{ + addToCommandQueue(CMD_PENDOWN + "END"); +} +void toggle_mode_inputBoxTopLeft(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_INPUT_BOX_TOP_LEFT); + setMode(MODE_INPUT_BOX_TOP_LEFT); + } + else + currentMode = ""; +} +void toggle_mode_inputBoxBotRight(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_INPUT_BOX_BOT_RIGHT); + setMode(MODE_INPUT_BOX_BOT_RIGHT); + // unset topleft + } + else + currentMode = ""; +} +void button_mode_drawOutlineBox() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + sendOutlineOfBox(); +} +void button_mode_drawOutlineBoxRows() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendOutlineOfRows(pixels, DRAW_DIR_SE); + } +} +void button_mode_drawShadeBoxRowsPixels() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendOutlineOfPixels(pixels); + } +} +void toggle_mode_drawToPosition(boolean flag) +{ + // unset other toggles + if (flag) + { + unsetOtherToggles(MODE_DRAW_TO_POSITION); + setMode(MODE_DRAW_TO_POSITION); + } +} +void button_mode_renderSquarePixel() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendSquarePixels(pixels); + } +} +void button_mode_renderSawPixel() +{ +// if (pixelCentresForMachine != null && !pixelCentresForMachine.isEmpty()) +// sendSawtoothPixels(); +} +void button_mode_renderCirclePixel() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendCircularPixels(pixels); + } +} +void button_mode_renderVectors() +{ + // turn off vector view and turn queue preview on + minitoggle_mode_showVector(false); + minitoggle_mode_showQueuePreview(true); + sendVectorShapes(); +} + +void toggle_mode_setPosition(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_SET_POSITION); + setMode(MODE_SET_POSITION); + } +} + +void button_mode_returnToHome() +{ + // lift pen + button_mode_penUp(); + PVector pgCoords = getDisplayMachine().asNativeCoords(getHomePoint()); + sendMoveToNativePosition(false, pgCoords); +} + +void button_mode_drawTestPattern() +{ + sendTestPattern(); +} + +void button_mode_drawGrid() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendGridOfBox(pixels); + } +} +void button_mode_loadImage() +{ + if (getDisplayMachine().getImage() == null) + { + loadImageWithFileChooser(); + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + } + } + else + { + getDisplayMachine().setImage(null); + getDisplayMachine().setImageFilename(null); + } +} +void button_mode_loadVectorFile() +{ + if (getVectorShape() == null) + { + loadVectorWithFileChooser(); + minitoggle_mode_showVector(true); + } + else + { + vectorShape = null; + vectorFilename = null; + } +} +void numberbox_mode_pixelBrightThreshold(float value) +{ + pixelExtractBrightThreshold = int(value+0.5); +} +void numberbox_mode_pixelDarkThreshold(float value) +{ + pixelExtractDarkThreshold = int(value+0.5); +} + +void button_mode_pauseQueue() +{ +} +void button_mode_runQueue() +{ +} +void button_mode_clearQueue() +{ + resetQueue(); +} +void button_mode_setPositionHome() +{ + sendSetHomePosition(); +} +void button_mode_drawTestPenWidth() +{ + sendTestPenWidth(); +} +void button_mode_renderScaledSquarePixels() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendScaledSquarePixels(pixels); + } +} +void button_mode_renderSolidSquarePixels() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendSolidSquarePixels(pixels); + } +} +void button_mode_renderScribblePixels() +{ + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + // get the pixels + Set pixels = getDisplayMachine().extractNativePixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + sendScribblePixels(pixels); + } +} +void button_mode_changeMachineSpec() +{ + sendMachineSpec(); +} +void button_mode_requestMachineSize() +{ + sendRequestMachineSize(); +} +void button_mode_resetMachine() +{ + sendResetMachine(); +} +void button_mode_saveProperties() +{ + savePropertiesFile(); + // clear old properties. + props = null; + loadFromPropertiesFile(); +} +void button_mode_saveAsProperties() +{ + saveNewPropertiesFileWithFileChooser(); +} +void button_mode_loadProperties() +{ + loadNewPropertiesFilenameWithFileChooser(); +} +void toggle_mode_moveImage(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_MOVE_IMAGE); + setMode(MODE_MOVE_IMAGE); + } + else + { + setMode(""); + } +} + +void toggle_mode_chooseChromaKeyColour(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_CHOOSE_CHROMA_KEY_COLOUR); + setMode(MODE_CHOOSE_CHROMA_KEY_COLOUR); + } + else + setMode(""); +} + +void button_mode_convertBoxToPictureframe() +{ + setPictureFrameDimensionsToBox(); +} +void button_mode_selectPictureframe() +{ + setBoxToPictureframeDimensions(); +} +void button_mode_exportQueue() +{ + exportQueueToFile(); +} +void button_mode_importQueue() +{ + importQueueFromFile(); +} +void toggle_mode_drawDirect(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_DRAW_DIRECT); + setMode(MODE_DRAW_DIRECT); + } +} + +void numberbox_mode_resizeImage(float value) +{ + float steps = getDisplayMachine().inSteps(value); + Rectangle r = getDisplayMachine().getImageFrame(); + float ratio = r.getHeight() / r.getWidth(); + + float oldSize = r.getSize().x; + + r.getSize().x = steps; + r.getSize().y = steps * ratio; + + float difference = (r.getSize().x / 2.0)-(oldSize/2.0); + r.getPosition().x -= difference; + r.getPosition().y -= difference * ratio; + + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), getSampleArea()); +} + +void numberbox_mode_resizeVector(float value) +{ + vectorScaling = value; +} +void toggle_mode_moveVector(boolean flag) +{ + // unset other toggles + if (flag) + { + unsetOtherToggles(MODE_MOVE_VECTOR); + setMode(MODE_MOVE_VECTOR); + } + else + { + setMode(""); + } +} + +void numberbox_mode_changeMachineWidth(float value) +{ + clearBoxVectors(); + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getSize().x = steps; +} +void numberbox_mode_changeMachineHeight(float value) +{ + clearBoxVectors(); + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getSize().y = steps; +} +void numberbox_mode_changeMMPerRev(float value) +{ + clearBoxVectors(); + getDisplayMachine().setMMPerRev(value); +} +void numberbox_mode_changeStepsPerRev(float value) +{ + clearBoxVectors(); + getDisplayMachine().setStepsPerRev(value); +} +void numberbox_mode_changeStepMultiplier(float value) +{ + machineStepMultiplier = (int) value; +} +void numberbox_mode_changeMinVectorLineLength(float value) +{ + minimumVectorLineLength = (int) value; +} +void numberbox_mode_changePageWidth(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getPage().setWidth(steps); +} +void numberbox_mode_changePageHeight(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getPage().setHeight(steps); +} +void numberbox_mode_changePageOffsetX(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getPage().getTopLeft().x = steps; +} +void numberbox_mode_changePageOffsetY(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getDisplayMachine().getPage().getTopLeft().y = steps; +} +void button_mode_changePageOffsetXCentre() +{ + float pageWidth = getDisplayMachine().getPage().getWidth(); + float machineWidth = getDisplayMachine().getSize().x; + float diff = (machineWidth - pageWidth) / 2.0; + getDisplayMachine().getPage().getTopLeft().x = diff; + initialiseNumberboxValues(getAllControls()); +} + +void numberbox_mode_changeHomePointX(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getHomePoint().x = steps; +} +void numberbox_mode_changeHomePointY(float value) +{ + float steps = getDisplayMachine().inSteps(value); + getHomePoint().y = steps; +} +void button_mode_changeHomePointXCentre() +{ + float halfWay = getDisplayMachine().getSize().x / 2.0; + getHomePoint().x = halfWay; + getHomePoint().y = getDisplayMachine().getPage().getTop(); + initialiseNumberboxValues(getAllControls()); +} + + +void numberbox_mode_changePenWidth(float value) +{ + currentPenWidth = Math.round(value*100.0)/100.0; +} +void button_mode_sendPenWidth() +{ + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_CHANGEPENWIDTH+df.format(currentPenWidth)+",END"); +} + +void numberbox_mode_changePenTestStartWidth(float value) +{ + testPenWidthStartSize = Math.round(value*100.0)/100.0; +} +void numberbox_mode_changePenTestEndWidth(float value) +{ + testPenWidthEndSize = Math.round(value*100.0)/100.0; +} +void numberbox_mode_changePenTestIncrementSize(float value) +{ + testPenWidthIncrementSize = Math.round(value*100.0)/100.0; +} + +void numberbox_mode_changeMachineMaxSpeed(float value) +{ + currentMachineMaxSpeed = Math.round(value*100.0)/100.0; +} +void numberbox_mode_changeMachineAcceleration(float value) +{ + currentMachineAccel = Math.round(value*100.0)/100.0; +} +void button_mode_sendMachineSpeed() +{ + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORSPEED+df.format(currentMachineMaxSpeed)+",END"); + + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORACCEL+df.format(currentMachineAccel)+",END"); +} + +void button_mode_sendRoveArea() +{ + if (isBoxSpecified()) + { + addToCommandQueue(CMD_SET_ROVE_AREA+(long)boxVector1.x+","+(long)boxVector1.y+"," + +(long)(boxVector2.x-boxVector1.x)+","+(long)(boxVector2.y-boxVector1.y)+",END"); + } +} + +void toggle_mode_sendStartText(boolean flag) +{ + if (flag) + { + unsetOtherToggles(MODE_SEND_START_TEXT); + setMode(MODE_SEND_START_TEXT); + } + else + { + setMode(""); + } +} + +void button_mode_startSwirling() +{ + addToCommandQueue(CMD_SWIRLING+"1,END"); +} +void button_mode_stopSwirling() +{ + addToCommandQueue(CMD_SWIRLING+"0,END"); +} +void setMode(String m) +{ + lastMode = currentMode; + currentMode = m; +} +void revertToLastMode() +{ + currentMode = lastMode; +} + +/*------------------------------------------------------------------------ + Details about the "serial port" subwindow +------------------------------------------------------------------------*/ + +void button_mode_serialPortDialog() +{ + ControlWindow serialPortWindow = cp5.addControlWindow("changeSerialPortWindow",100,100,150,150); + serialPortWindow.hideCoordinates(); + + serialPortWindow.setBackground(getBackgroundColour()); + Radio r = cp5.addRadio("radio_serialPort",10,10); + r.setWindow(serialPortWindow); + + String[] ports = Serial.list(); + if (getSerialPortNumber() >= 0 && getSerialPortNumber() < ports.length) + r.setValue(getSerialPortNumber()); + + r.add("setup", -2); + r.add("No serial connection", -1); + + for (int i = 0; i < ports.length; i++) + { + r.add(ports[i], i); + } + + int portNo = getSerialPortNumber(); + if (portNo > -1 && portNo < ports.length) + r.activate(ports[portNo]); + else + r.activate("No serial connection"); + + r.removeItem("setup"); +} + +void radio_serialPort(int newSerialPort) +{ + if (newSerialPort == -2) + { + } + else if (newSerialPort == -1) + { + println("Disconnecting serial port."); + useSerialPortConnection = false; + if (myPort != null) + { + myPort.stop(); + myPort = null; + } + drawbotReady = false; + drawbotConnected = false; + serialPortNumber = newSerialPort; + } + else if (newSerialPort != getSerialPortNumber()) + { + println("About to connect to serial port in slot " + newSerialPort); + // Print a list of the serial ports, for debugging purposes: + if (newSerialPort < Serial.list().length) + { + try + { + drawbotReady = false; + drawbotConnected = false; + if (myPort != null) + { + myPort.stop(); + myPort = null; + } + if (getSerialPortNumber() >= 0) + println("closing " + Serial.list()[getSerialPortNumber()]); + + serialPortNumber = newSerialPort; + String portName = Serial.list()[serialPortNumber]; + + 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 in slot " + getSerialPortNumber() + + " caused an exception: " + e.getMessage()); + } + } + else + { + println("No serial ports found."); + useSerialPortConnection = false; + } + } + else + { + println("no serial port change."); + } +} + + +/*------------------------------------------------------------------------ + Details about the "machine store" subwindow +------------------------------------------------------------------------*/ + +ControlWindow dialogWindow = null; + +void button_mode_machineStoreDialog() +{ + this.dialogWindow = cp5.addControlWindow("chooseStoreFilenameWindow",100,100,450,150); + dialogWindow.hideCoordinates(); + + dialogWindow.setBackground(getBackgroundColour()); + + Textfield filenameField = cp5.addTextfield("storeFilename",20,20,150,20); + filenameField.setText(getStoreFilename()); + filenameField.setLabel("Filename to store to"); + filenameField.setWindow(dialogWindow); + + Button submitButton = cp5.addButton("submitStoreFilenameWindow",0,180,20,60,20); + submitButton.setLabel("Submit"); + submitButton.setWindow(dialogWindow); + + Toggle overwriteToggle = cp5.addToggle("toggleAppendToFile",true,180,50,20,20); + overwriteToggle.setCaptionLabel("Overwrite existing file"); + overwriteToggle.setWindow(dialogWindow); + + filenameField.setFocus(true); + +} + +void storeFilename(String filename) +{ + println("Filename event: "+ filename); + if (filename != null && filename.length() <= 12) + { + setStoreFilename(filename); + sendMachineStoreMode(); + } +} + +void toggleAppendToFile(boolean theFlag) +{ + setOverwriteExistingStoreFile(theFlag); +} + +void submitStoreFilenameWindow(int theValue) +{ + Textfield tf = (Textfield) cp5.controller("storeFilename"); + tf.submit(); +} + +void button_mode_machineExecDialog() +{ + this.dialogWindow = cp5.addControlWindow("chooseExecFilenameWindow",100,100,450,150); + dialogWindow.hideCoordinates(); + + dialogWindow.setBackground(getBackgroundColour()); + + Textfield filenameField = cp5.addTextfield("execFilename",20,20,150,20); + filenameField.setText(getStoreFilename()); + filenameField.setLabel("Filename to execute from"); + filenameField.setWindow(dialogWindow); + + Button submitButton = cp5.addButton("submitExecFilenameWindow",0,180,20,60,20); + submitButton.setLabel("Submit"); + submitButton.setWindow(dialogWindow); + + filenameField.setFocus(true); + +} + +void execFilename(String filename) +{ + println("Filename event: "+ filename); + if (filename != null && filename.length() <= 12) + { + setStoreFilename(filename); + sendMachineExecMode(); + } +} +void submitExecFilenameWindow(int theValue) +{ + Textfield tf = (Textfield) cp5.controller("execFilename"); + tf.submit(); +} + +void button_mode_sendMachineLiveMode() +{ + sendMachineLiveMode(); +} + + + + + +/*------------------------------------------------------------------------ + Details about the "drawing" subwindow +------------------------------------------------------------------------*/ +void button_mode_drawPixelsDialog() +{ + this.dialogWindow = cp5.addControlWindow("drawPixelsWindow",100,100,450,150); + dialogWindow.hideCoordinates(); + + dialogWindow.setBackground(getBackgroundColour()); + + Radio rPos = cp5.addRadio("radio_startPosition",10,10); + rPos.add("Top-right", DRAW_DIR_NE); + rPos.add("Bottom-right", DRAW_DIR_SE); + rPos.add("Bottom-left", DRAW_DIR_SW); + rPos.add("Top-left", DRAW_DIR_NW); + rPos.setWindow(dialogWindow); + + Radio rSkip = cp5.addRadio("radio_pixelSkipStyle",10,100); + rSkip.add("Lift pen over masked pixels", 1); + rSkip.add("Draw masked pixels as blanks", 2); + rSkip.setWindow(dialogWindow); + +// Radio rDir = cp5.addRadio("radio_rowStartDirection",100,10); +// rDir.add("Upwards", 0); +// rDir.add("Downwards", 1); +// rDir.setWindow(dialogWindow); + + Radio rStyle = cp5.addRadio("radio_pixelStyle",100,10); + rStyle.add("Variable frequency square wave", PIXEL_STYLE_SQ_FREQ); + rStyle.add("Variable size square wave", PIXEL_STYLE_SQ_SIZE); + rStyle.add("Solid square wave", PIXEL_STYLE_SQ_SOLID); + rStyle.add("Scribble", PIXEL_STYLE_SCRIBBLE); + if (currentHardware >= HARDWARE_VER_MEGA) + { + rStyle.add("Circles", PIXEL_STYLE_CIRCLE); + rStyle.add("Sawtooth", PIXEL_STYLE_SAW); + } + rStyle.setWindow(dialogWindow); + + Button submitButton = cp5.addButton("submitDrawWindow",0,280,10,120,20); + submitButton.setLabel("Generate commands"); + submitButton.setWindow(dialogWindow); + + +} + +public Integer renderStartPosition = DRAW_DIR_NE; // default top right hand corner for start +public Integer renderStartDirection = DRAW_DIR_SE; // default start drawing in SE direction (DOWN) +public Integer renderStyle = PIXEL_STYLE_SQ_FREQ; // default pixel style square wave +void radio_startPosition(int pos) +{ + this.renderStartPosition = pos; + radio_rowStartDirection(1); +} +void radio_rowStartDirection(int dir) +{ + if (renderStartPosition == DRAW_DIR_NE || renderStartPosition == DRAW_DIR_SW) + renderStartDirection = (dir == 0) ? DRAW_DIR_NW : DRAW_DIR_SE; + else if (renderStartPosition == DRAW_DIR_SE || renderStartPosition == DRAW_DIR_NW) + renderStartDirection = (dir == 0) ? DRAW_DIR_NE : DRAW_DIR_SW; +} +void radio_pixelStyle(int style) +{ + renderStyle = style; +} +void radio_pixelSkipStyle(int style) +{ + if (style == 1) + liftPenOnMaskedPixels = true; + else if (style == 2) + liftPenOnMaskedPixels = false; +} +void submitDrawWindow(int theValue) +{ + println("draw."); + println("Style: " + renderStyle); + println("Start pos: " + renderStartPosition); + println("Start dir: " + renderStartDirection); + + switch (renderStyle) + { + case PIXEL_STYLE_SQ_FREQ: button_mode_renderSquarePixel(); break; + case PIXEL_STYLE_SQ_SIZE: button_mode_renderScaledSquarePixels(); break; + case PIXEL_STYLE_SQ_SOLID: button_mode_renderSolidSquarePixels(); break; + case PIXEL_STYLE_SCRIBBLE: button_mode_renderScribblePixels(); break; + case PIXEL_STYLE_CIRCLE: button_mode_renderCirclePixel(); break; + case PIXEL_STYLE_SAW: button_mode_renderSawPixel(); break; + } + + +} + +/*------------------------------------------------------------------------ + Details about the "writing" subwindow +------------------------------------------------------------------------*/ +String textToWrite = ""; +String spriteFilePrefix = "sprite/let"; +String spriteFileSuffix = ".txt"; + +void button_mode_drawWritingDialog() +{ + this.dialogWindow = cp5.addControlWindow("drawWritingWindow",100,100,450,200); + dialogWindow.hideCoordinates(); + + dialogWindow.setBackground(getBackgroundColour()); + + Textfield spriteFileField = cp5.addTextfield("spriteFilePrefixField",20,20,150,20); + spriteFileField.setText(getSpriteFilePrefix()); + spriteFileField.setLabel("File prefix"); + spriteFileField.setWindow(dialogWindow); + + Textfield writingField = cp5.addTextfield("textToWriteField",20,60,400,20); + writingField.setText(getTextToWrite()); + writingField.setLabel("Text to write"); + writingField.setWindow(dialogWindow); + + Button importTextButton = cp5.addButton("importTextButton",0,20,100,120,20); + importTextButton.setLabel("Load text from file"); + importTextButton.setWindow(dialogWindow); + + Radio rPos = cp5.addRadio("radio_drawWritingDirection",20,140); +// rPos.add("North-east", DRAW_DIR_NE); + rPos.add("South-east", DRAW_DIR_SE); +// rPos.add("South-west", DRAW_DIR_SW); +// rPos.add("North-west", DRAW_DIR_NW); + rPos.setWindow(dialogWindow); + + + + Button submitButton = cp5.addButton("submitWritingWindow",0,300,100,120,20); + submitButton.setLabel("Generate commands"); + submitButton.setWindow(dialogWindow); +} + +void spriteFilePrefixField(String value) +{ + spriteFilePrefix = value; +} +void textToWriteField(String value) +{ + textToWrite = value; +} + +String getTextToWrite() +{ + return textToWrite; +} +String getSpriteFilePrefix() +{ + return spriteFilePrefix; +} +String getSpriteFileSuffix() +{ + return spriteFileSuffix; +} + +void importTextButton() +{ + textToWrite = importTextToWriteFromFile(); + Textfield tf = (Textfield) cp5.controller("textToWriteField"); + tf.setText(getTextToWrite()); + tf.submit(); +} + + +void submitWritingWindow(int theValue) +{ + println("Write."); + + Textfield tf = (Textfield) cp5.controller("spriteFilePrefixField"); + tf.submit(); + tf.setText(getSpriteFilePrefix()); + tf = (Textfield) cp5.controller("textToWriteField"); + tf.submit(); + tf.setText(getTextToWrite()); + + println("Start dir: " + renderStartDirection); + println("Sprite file prefix: " + spriteFilePrefix); + println("Text: " + textToWrite); + + for (int i=0; i. + + 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/ + http://code.google.com/p/polargraph/ + */ +Set getPanelNames() +{ + if (this.panelNames == null) + this.panelNames = buildPanelNames(); + return this.panelNames; +} +List getTabNames() +{ + if (this.tabNames == null) + this.tabNames = buildTabNames(); + return this.tabNames; +} +Set getControlNames() +{ + if (this.controlNames == null) + this.controlNames = buildControlNames(); + return this.controlNames; +} +Map> getControlsForPanels() +{ + if (this.controlsForPanels == null) + this.controlsForPanels = buildControlsForPanels(); + return this.controlsForPanels; +} +Map getAllControls() +{ + if (this.allControls == null) + this.allControls = buildAllControls(); + return this.allControls; +} +Map getControlLabels() +{ + if (this.controlLabels == null) + this.controlLabels = buildControlLabels(); + return this.controlLabels; +} +Map> getPanelsForTabs() +{ + if (this.panelsForTabs == null) + this.panelsForTabs = buildPanelsForTabs(); + return this.panelsForTabs; +} +Map getPanels() +{ + if (this.panels == null) + this.panels = buildPanels(); + return this.panels; +} + +Set getControlsToLockIfBoxNotSpecified() +{ + if (this.controlsToLockIfBoxNotSpecified == null) + { + this.controlsToLockIfBoxNotSpecified = buildControlsToLockIfBoxNotSpecified(); + } + return this.controlsToLockIfBoxNotSpecified; +} + +Set getControlsToLockIfImageNotLoaded() +{ + if (this.controlsToLockIfImageNotLoaded == null) + { + this.controlsToLockIfImageNotLoaded = buildControlsToLockIfImageNotLoaded(); + } + return this.controlsToLockIfImageNotLoaded; +} + + +void hideAllControls() +{ + for (String key : allControls.keySet()) + { + Controller c = allControls.get(key); + c.hide(); + } +} + +Map buildPanels() +{ + Map panels = new HashMap(); + + float panelHeight = frame.getHeight() - getMainPanelPosition().y - (DEFAULT_CONTROL_SIZE.y*3); + Rectangle panelOutline = new Rectangle(getMainPanelPosition(), + new PVector((DEFAULT_CONTROL_SIZE.x+CONTROL_SPACING.x)*2, panelHeight)); + Panel inputPanel = new Panel(PANEL_NAME_INPUT, panelOutline); + inputPanel.setResizable(true); + inputPanel.setOutlineColour(color(200, 200, 200)); + // get controls + inputPanel.setControls(getControlsForPanels().get(PANEL_NAME_INPUT)); + // get control positions + inputPanel.setControlPositions(buildControlPositionsForPanel(inputPanel)); + inputPanel.setControlSizes(buildControlSizesForPanel(inputPanel)); + panels.put(PANEL_NAME_INPUT, inputPanel); + + Panel rovingPanel = new Panel(PANEL_NAME_ROVING, panelOutline); + rovingPanel.setOutlineColour(color(200,200,200)); + // get controls + rovingPanel.setResizable(true); + rovingPanel.setControls(getControlsForPanels().get(PANEL_NAME_ROVING)); + // get control positions + rovingPanel.setControlPositions(buildControlPositionsForPanel(rovingPanel)); + rovingPanel.setControlSizes(buildControlSizesForPanel(rovingPanel)); + panels.put(PANEL_NAME_ROVING, rovingPanel); + + Panel detailsPanel = new Panel(PANEL_NAME_DETAILS, panelOutline); + detailsPanel.setOutlineColour(color(200, 200, 200)); + // get controls + detailsPanel.setResizable(true); + detailsPanel.setControls(getControlsForPanels().get(PANEL_NAME_DETAILS)); + // get control positions + detailsPanel.setControlPositions(buildControlPositionsForPanel(detailsPanel)); + detailsPanel.setControlSizes(buildControlSizesForPanel(detailsPanel)); + panels.put(PANEL_NAME_DETAILS, detailsPanel); + + Panel queuePanel = new Panel(PANEL_NAME_QUEUE, panelOutline); + queuePanel.setOutlineColour(color(200, 200, 200)); + // get controls + queuePanel.setResizable(true); + queuePanel.setControls(getControlsForPanels().get(PANEL_NAME_QUEUE)); + // get control positions + queuePanel.setControlPositions(buildControlPositionsForPanel(queuePanel)); + queuePanel.setControlSizes(buildControlSizesForPanel(queuePanel)); + panels.put(PANEL_NAME_QUEUE, queuePanel); + + panelOutline = new Rectangle( + new PVector(getMainPanelPosition().x, getMainPanelPosition().y-((DEFAULT_CONTROL_SIZE.y+CONTROL_SPACING.y)*2)), + new PVector((DEFAULT_CONTROL_SIZE.x+CONTROL_SPACING.x)*2, (DEFAULT_CONTROL_SIZE.y+CONTROL_SPACING.y)*2)); + Panel generalPanel = new Panel(PANEL_NAME_GENERAL, panelOutline); + generalPanel.setResizable(false); + generalPanel.setOutlineColour(color(200, 200, 200)); + // get controls + generalPanel.setControls(getControlsForPanels().get(PANEL_NAME_GENERAL)); + // get control positions + generalPanel.setControlPositions(buildControlPositionsForPanel(generalPanel)); + generalPanel.setControlSizes(buildControlSizesForPanel(generalPanel)); + panels.put(PANEL_NAME_GENERAL, generalPanel); + + return panels; +} + +PVector getMainPanelPosition() +{ + return this.mainPanelPosition; +} + +void updateNumberboxValues() +{ + initialiseNumberboxValues(getAllControls()); +} + +Set buildControlsToLockIfBoxNotSpecified() +{ + Set result = new HashSet(); + result.add(MODE_DRAW_OUTLINE_BOX); + result.add(MODE_DRAW_OUTLINE_BOX_ROWS); + result.add(MODE_DRAW_SHADE_BOX_ROWS_PIXELS); + result.add(MODE_RENDER_SQUARE_PIXELS); + result.add(MODE_RENDER_SCALED_SQUARE_PIXELS); + result.add(MODE_RENDER_SAW_PIXELS); + result.add(MODE_RENDER_CIRCLE_PIXELS); + result.add(MODE_RENDER_PIXEL_DIALOG); + result.add(MODE_DRAW_GRID); + result.add(MODE_DRAW_TESTPATTERN); + result.add(MODE_RENDER_SOLID_SQUARE_PIXELS); + result.add(MODE_RENDER_SCRIBBLE_PIXELS); + result.add(MODE_CONVERT_BOX_TO_PICTUREFRAME); + result.add(MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD); + result.add(MODE_IMAGE_PIXEL_DARK_THRESHOLD); + + return result; +} + +Set buildControlsToLockIfImageNotLoaded() +{ + Set result = new HashSet(); + result.add(MODE_MOVE_IMAGE); + result.add(MODE_RESIZE_IMAGE); +// result.add(MODE_INPUT_BOX_TOP_LEFT); + result.add(MODE_CHANGE_GRID_SIZE); + result.add(MODE_CHANGE_SAMPLE_AREA); + result.add(MODE_SELECT_PICTUREFRAME); + + return result; +} + +Map buildAllControls() +{ + + Map map = new HashMap(); + + for (String controlName : getControlNames()) + { + if (controlName.startsWith("button_")) + { + Button b = cp5.addButton(controlName, 0, 100, 100, 100, 100); + b.setLabel(getControlLabels().get(controlName)); + b.hide(); + map.put(controlName, b); + // println("Added button " + controlName); + } + else if (controlName.startsWith("toggle_")) + { + Toggle t = cp5.addToggle(controlName, false, 100, 100, 100, 100); + t.setLabel(getControlLabels().get(controlName)); + t.hide(); + controlP5.Label l = t.captionLabel(); + l.style().marginTop = -17; //move upwards (relative to button size) + l.style().marginLeft = 4; //move to the right + map.put(controlName, t); + // println("Added toggle " + controlName); + } + else if (controlName.startsWith("minitoggle_")) + { + Toggle t = cp5.addToggle(controlName, false, 100, 100, 100, 100); + t.setLabel(getControlLabels().get(controlName)); + t.hide(); + controlP5.Label l = t.captionLabel(); + l.style().marginTop = -17; //move upwards (relative to button size) + l.style().marginLeft = 4; //move to the right + map.put(controlName, t); + // println("Added minitoggle " + controlName); + } + else if (controlName.startsWith("numberbox_")) + { + Numberbox n = cp5.addNumberbox(controlName, 100, 100, 100, 100, 20); + n.setLabel(getControlLabels().get(controlName)); + n.hide(); + n.setDecimalPrecision(0); + controlP5.Label l = n.captionLabel(); + l.style().marginTop = -17; //move upwards (relative to button size) + l.style().marginLeft = 40; //move to the right + // change the control direction to left/right + n.setDirection(Controller.VERTICAL); + map.put(controlName, n); + // println("Added numberbox " + controlName); + } + } + + initialiseMiniToggleValues(map); + initialiseNumberboxValues(map); + return map; +} + +Map initialiseNumberboxValues(Map map) +{ + for (String key : map.keySet()) + { + if (key.startsWith("numberbox_")) + { + Numberbox n = (Numberbox) map.get(key); + + if (MODE_CHANGE_SAMPLE_AREA.equals(key)) + { + n.setValue(getSampleArea()); + n.setMin(1); + n.setMultiplier(1); + } + else if (MODE_CHANGE_GRID_SIZE.equals(key)) + { + n.setValue(getGridSize()); + n.setMin(20); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_MACHINE_WIDTH.equals(key)) + { + println("key:"+key+", width: " + getDisplayMachine().getWidth() + ", multiplied:"+getDisplayMachine().inMM(getDisplayMachine().getWidth())); + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getWidth())); + n.setMin(20); + n.setMultiplier(0.5); + } + else if (MODE_RESIZE_IMAGE.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getImageFrame().getWidth())); + n.setMin(20); + n.setMultiplier(1); + } + else if (MODE_CHANGE_MACHINE_HEIGHT.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getHeight())); + n.setMin(20); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_MM_PER_REV.equals(key)) + { + n.setValue(getDisplayMachine().getMMPerRev()); + n.setMin(20); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_STEPS_PER_REV.equals(key)) + { + n.setValue(getDisplayMachine().getStepsPerRev()); + n.setMin(20); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_STEP_MULTIPLIER.equals(key)) + { + n.setValue(machineStepMultiplier); + n.setMin(1); + n.setMax(16); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_PAGE_WIDTH.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getPage().getWidth())); + n.setMin(10); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_PAGE_HEIGHT.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getPage().getHeight())); + n.setMin(10); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_PAGE_OFFSET_X.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getPage().getLeft())); + n.setMin(0); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_PAGE_OFFSET_Y.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getDisplayMachine().getPage().getTop())); + n.setMin(0); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_HOMEPOINT_X.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getHomePoint().x)); + n.setMin(0); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_HOMEPOINT_Y.equals(key)) + { + n.setValue(getDisplayMachine().inMM(getHomePoint().y)); + n.setMin(0); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_PEN_WIDTH.equals(key)) + { + n.setDecimalPrecision(2); + n.setValue(currentPenWidth); + n.setMin(0.01); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_PEN_TEST_START_WIDTH.equals(key)) + { + n.setDecimalPrecision(2); + n.setValue(testPenWidthStartSize); + n.setMin(0.01); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_PEN_TEST_END_WIDTH.equals(key)) + { + n.setDecimalPrecision(2); + n.setValue(testPenWidthEndSize); + n.setMin(0.01); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_PEN_TEST_INCREMENT_SIZE.equals(key)) + { + n.setDecimalPrecision(2); + n.setValue(testPenWidthIncrementSize); + n.setMin(0.01); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_MACHINE_MAX_SPEED.equals(key)) + { + n.setDecimalPrecision(0); + n.setValue(currentMachineMaxSpeed); + n.setMin(1); + n.setMultiplier(1); + } + else if (MODE_CHANGE_MACHINE_ACCELERATION.equals(key)) + { + n.setDecimalPrecision(0); + n.setValue(currentMachineAccel); + n.setMin(1); + n.setMultiplier(1); + } + else if (MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD.equals(key)) + { + n.setDecimalPrecision(0); + n.setValue(pixelExtractBrightThreshold); + n.setMin(0); + n.setMax(255); + n.setMultiplier(0.5); + } + else if (MODE_IMAGE_PIXEL_DARK_THRESHOLD.equals(key)) + { + n.setDecimalPrecision(0); + n.setValue(pixelExtractDarkThreshold); + n.setMin(0); + n.setMax(255); + n.setMultiplier(0.5); + } + else if (MODE_RESIZE_VECTOR.equals(key)) + { + n.setDecimalPrecision(0); + n.setValue(vectorScaling); + n.setMin(1); + n.setMax(1000); + n.setMultiplier(0.5); + } + else if (MODE_CHANGE_PIXEL_SCALING.equals(key)) + { + n.setDecimalPrecision(2); + n.setValue(pixelScalingOverGridSize); + n.setMin(0.1); + n.setMax(4.0); + n.setMultiplier(0.01); + } + else if (MODE_CHANGE_MIN_VECTOR_LINE_LENGTH.equals(key)) + { + n.setValue(minimumVectorLineLength); + n.setMin(0); + n.setMultiplier(0.1); + } + } + } + return map; +} + + +Map initialiseMiniToggleValues(Map map) +{ + for (String key : map.keySet()) + { + if (MODE_SHOW_DENSITY_PREVIEW.equals(key)) + { + Toggle t = (Toggle) map.get(key); + t.setValue((displayingDensityPreview) ? 1 : 0); + } + if (MODE_SHOW_QUEUE_PREVIEW.equals(key)) + { + Toggle t = (Toggle) map.get(key); + t.setValue((displayingQueuePreview) ? 1 : 0); + } + if (MODE_SHOW_IMAGE.equals(key)) + { + Toggle t = (Toggle) map.get(key); + t.setValue((displayingImage) ? 1 : 0); + } + if (MODE_SHOW_VECTOR.equals(key)) + { + Toggle t = (Toggle) map.get(key); + t.setValue((displayingVector) ? 1 : 0); + } + if (MODE_SHOW_GUIDES.equals(key)) + { + Toggle t = (Toggle) map.get(key); + t.setValue((displayingGuides) ? 1 : 0); + } + } + return map; +} + + + + +String getControlLabel(String butName) +{ + if (controlLabels.containsKey(butName)) + return controlLabels.get(butName); + else + return ""; +} + +Map buildControlPositionsForPanel(Panel panel) +{ + Map map = new HashMap(); + String panelName = panel.getName(); + int col = 0; + int row = 0; + for (Controller controller : panel.getControls()) + { + if (controller.name().startsWith("minitoggle_")) + { + PVector p = new PVector(col*(DEFAULT_CONTROL_SIZE.x+CONTROL_SPACING.x), row*(DEFAULT_CONTROL_SIZE.y+CONTROL_SPACING.y)); + map.put(controller.name(), p); + row++; + if (p.y + (DEFAULT_CONTROL_SIZE.y*2) >= panel.getOutline().getHeight()) + { + row = 0; + col++; + } + } + else + { + PVector p = new PVector(col*(DEFAULT_CONTROL_SIZE.x+CONTROL_SPACING.x), row*(DEFAULT_CONTROL_SIZE.y+CONTROL_SPACING.y)); + map.put(controller.name(), p); + row++; + if (p.y + (DEFAULT_CONTROL_SIZE.y*2) >= panel.getOutline().getHeight()) + { + row = 0; + col++; + } + } + } + + return map; +} +Map buildControlSizesForPanel(Panel panel) +{ + Map map = new HashMap(); + String panelName = panel.getName(); + int col = 0; + int row = 0; + for (Controller controller : panel.getControls()) + { + if (controller.name().startsWith("minitoggle_")) + { + PVector s = new PVector(DEFAULT_CONTROL_SIZE.y, DEFAULT_CONTROL_SIZE.y); + map.put(controller.name(), s); + } + else + { + PVector s = new PVector(DEFAULT_CONTROL_SIZE.x, DEFAULT_CONTROL_SIZE.y); + map.put(controller.name(), s); + } + } + + return map; +} + + +Map> buildControlsForPanels() +{ +// println("build controls for panels."); + Map> map = new HashMap>(); + map.put(PANEL_NAME_INPUT, getControllersForControllerNames(getControlNamesForInputPanel())); + map.put(PANEL_NAME_ROVING, getControllersForControllerNames(getControlNamesForRovingPanel())); + map.put(PANEL_NAME_DETAILS, getControllersForControllerNames(getControlNamesForDetailPanel())); + map.put(PANEL_NAME_QUEUE, getControllersForControllerNames(getControlNamesForQueuePanel())); + map.put(PANEL_NAME_GENERAL, getControllersForControllerNames(getControlNamesForGeneralPanel())); + return map; +} + +List getControllersForControllerNames(List names) +{ + List list = new ArrayList(); + for (String name : names) + { + Controller c = getAllControls().get(name); + if (c != null) + list.add(c); + } + return list; +} + +/* This creates a list of control names for the input panel. */ +List getControlNamesForInputPanel() +{ + List controlNames = new ArrayList(); + controlNames.add(MODE_CLEAR_QUEUE); + controlNames.add(MODE_SET_POSITION_HOME); + controlNames.add(MODE_SET_POSITION); + controlNames.add(MODE_DRAW_TO_POSITION); + controlNames.add(MODE_DRAW_DIRECT); + controlNames.add(MODE_RETURN_TO_HOME); + controlNames.add(MODE_PEN_LIFT_UP); + controlNames.add(MODE_PEN_LIFT_DOWN); + controlNames.add(MODE_INPUT_BOX_TOP_LEFT); + controlNames.add(MODE_CONVERT_BOX_TO_PICTUREFRAME); + controlNames.add(MODE_SELECT_PICTUREFRAME); + controlNames.add(MODE_LOAD_IMAGE); + controlNames.add(MODE_MOVE_IMAGE); + controlNames.add(MODE_RESIZE_IMAGE); + controlNames.add(MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD); + controlNames.add(MODE_IMAGE_PIXEL_DARK_THRESHOLD); + controlNames.add(MODE_CHANGE_GRID_SIZE); + controlNames.add(MODE_CHANGE_SAMPLE_AREA); + controlNames.add(MODE_CHOOSE_CHROMA_KEY_COLOUR); + controlNames.add(MODE_CHANGE_PIXEL_SCALING); + + controlNames.add(MODE_RENDER_PIXEL_DIALOG); + controlNames.add(MODE_DRAW_GRID); + controlNames.add(MODE_DRAW_OUTLINE_BOX); + controlNames.add(MODE_DRAW_OUTLINE_BOX_ROWS); + controlNames.add(MODE_DRAW_SHADE_BOX_ROWS_PIXELS); + + controlNames.add(MODE_LOAD_VECTOR_FILE); + controlNames.add(MODE_RESIZE_VECTOR); + controlNames.add(MODE_MOVE_VECTOR); + controlNames.add(MODE_CHANGE_MIN_VECTOR_LINE_LENGTH); + controlNames.add(MODE_RENDER_VECTORS); + + controlNames.add(MODE_SHOW_IMAGE); + controlNames.add(MODE_SHOW_VECTOR); + controlNames.add(MODE_SHOW_QUEUE_PREVIEW); + controlNames.add(MODE_SHOW_DENSITY_PREVIEW); + controlNames.add(MODE_SHOW_GUIDES); + + + return controlNames; +} + +List getControlNamesForRovingPanel() +{ + List controlNames = new ArrayList(); + controlNames.add(MODE_CLEAR_QUEUE); + controlNames.add(MODE_INPUT_BOX_TOP_LEFT); + controlNames.add(MODE_CONVERT_BOX_TO_PICTUREFRAME); + controlNames.add(MODE_SELECT_PICTUREFRAME); + controlNames.add(MODE_SEND_ROVE_AREA); + controlNames.add(MODE_SEND_START_TEXT); + controlNames.add(MODE_CHANGE_GRID_SIZE); + controlNames.add(MODE_SHOW_WRITING_DIALOG); +// controlNames.add(MODE_START_SWIRLING); +// controlNames.add(MODE_STOP_SWIRLING); +// controlNames.add(MODE_START_SPRITE); +// controlNames.add(MODE_START_RANDOM_SPRITES); +// controlNames.add(MODE_STOP_RANDOM_SPRITES); + controlNames.add(MODE_DRAW_NORWEGIAN_DIALOG); + + return controlNames; +} + +List getControlNamesForDetailPanel() +{ + List controlNames = new ArrayList(); + controlNames.add(MODE_CHANGE_MACHINE_SPEC); + controlNames.add(MODE_REQUEST_MACHINE_SIZE); + controlNames.add(MODE_RESET_MACHINE); + + controlNames.add(MODE_CHANGE_MM_PER_REV); + controlNames.add(MODE_CHANGE_STEPS_PER_REV); + controlNames.add(MODE_CHANGE_STEP_MULTIPLIER); + controlNames.add(MODE_CHANGE_MACHINE_WIDTH); + controlNames.add(MODE_CHANGE_MACHINE_HEIGHT); + controlNames.add(MODE_CHANGE_PAGE_WIDTH); + controlNames.add(MODE_CHANGE_PAGE_HEIGHT); + controlNames.add(MODE_CHANGE_PAGE_OFFSET_X); + controlNames.add(MODE_CHANGE_PAGE_OFFSET_Y); + controlNames.add(MODE_CHANGE_PAGE_OFFSET_X_CENTRE); + + controlNames.add(MODE_CHANGE_HOMEPOINT_X); + controlNames.add(MODE_CHANGE_HOMEPOINT_Y); + controlNames.add(MODE_CHANGE_HOMEPOINT_X_CENTRE); + + controlNames.add(MODE_CHANGE_PEN_WIDTH); + controlNames.add(MODE_SEND_PEN_WIDTH); + + controlNames.add(MODE_CHANGE_PEN_TEST_START_WIDTH); + controlNames.add(MODE_CHANGE_PEN_TEST_END_WIDTH); + controlNames.add(MODE_CHANGE_PEN_TEST_INCREMENT_SIZE); + controlNames.add(MODE_DRAW_TEST_PENWIDTH); + + controlNames.add(MODE_CHANGE_MACHINE_MAX_SPEED); + controlNames.add(MODE_CHANGE_MACHINE_ACCELERATION); + controlNames.add(MODE_SEND_MACHINE_SPEED); + + controlNames.add(MODE_CHANGE_SERIAL_PORT); + + return controlNames; +} + +List getControlNamesForQueuePanel() +{ + List controlNames = new ArrayList(); + controlNames.add(MODE_CLEAR_QUEUE); + controlNames.add(MODE_EXPORT_QUEUE); + controlNames.add(MODE_IMPORT_QUEUE); + + if (getHardwareVersion() >= HARDWARE_VER_MEGA) + { + controlNames.add(MODE_SEND_MACHINE_STORE_MODE); + controlNames.add(MODE_SEND_MACHINE_LIVE_MODE); + controlNames.add(MODE_SEND_MACHINE_EXEC_MODE); + } + + return controlNames; +} + +List getControlNamesForGeneralPanel() +{ + List controlNames = new ArrayList(); + controlNames.add(MODE_SAVE_PROPERTIES); + controlNames.add(MODE_SAVE_AS_PROPERTIES); + controlNames.add(MODE_LOAD_PROPERTIES); + return controlNames; +} + + + +Map buildControlLabels() +{ + Map result = new HashMap(); + + result.put(MODE_BEGIN, "Reset queue"); + result.put(MODE_INPUT_BOX_TOP_LEFT, "Select Area"); + result.put(MODE_INPUT_BOX_BOT_RIGHT, "Select BotRight"); + result.put(MODE_DRAW_OUTLINE_BOX, "Draw Outline box"); + result.put(MODE_DRAW_OUTLINE_BOX_ROWS, "Draw Outline rows"); + result.put(MODE_DRAW_SHADE_BOX_ROWS_PIXELS, "Draw Outline pixels"); + result.put(MODE_DRAW_TO_POSITION, "Move pen to point"); + result.put(MODE_DRAW_DIRECT, "Move direct"); + result.put(MODE_RENDER_SQUARE_PIXELS, "Shade Squarewave"); + result.put(MODE_RENDER_SCALED_SQUARE_PIXELS, "Shade Scaled Square"); + result.put(MODE_RENDER_SAW_PIXELS, "Shade sawtooth"); + result.put(MODE_RENDER_CIRCLE_PIXELS, "Shade circular"); + result.put(MODE_INPUT_ROW_START, "Select Row start"); + result.put(MODE_INPUT_ROW_END, "Select Row end"); + result.put(MODE_SET_POSITION, "Set pen position"); + result.put(MODE_DRAW_GRID, "Draw grid of box"); + result.put(MODE_DRAW_TESTPATTERN, "test pattern"); + result.put(MODE_PLACE_IMAGE, "place image"); + result.put(MODE_LOAD_IMAGE, "Load image file"); + result.put(MODE_INC_ROW_SIZE, "Rowsize up"); + result.put(MODE_DEC_ROW_SIZE, "Rowsize down"); + result.put(MODE_SET_POSITION_HOME, "Set home"); + result.put(MODE_RETURN_TO_HOME, "Return to home"); + result.put(MODE_INPUT_SINGLE_PIXEL, "Choose pixel"); + result.put(MODE_DRAW_TEST_PENWIDTH, "Test pen widths"); + result.put(MODE_RENDER_SOLID_SQUARE_PIXELS, "Shade solid"); + result.put(MODE_RENDER_SCRIBBLE_PIXELS, "Shade scribble"); + + result.put(MODE_CHANGE_MACHINE_SPEC, "Upload machine spec"); + result.put(MODE_REQUEST_MACHINE_SIZE, "Download size spec"); + result.put(MODE_RESET_MACHINE, "Reset machine to factory"); + result.put(MODE_SAVE_PROPERTIES, "Save"); + result.put(MODE_SAVE_AS_PROPERTIES, "Save as"); + result.put(MODE_LOAD_PROPERTIES, "Load config"); + + result.put(MODE_INC_SAMPLE_AREA, "Inc sample size"); + result.put(MODE_DEC_SAMPLE_AREA, "Dec sample size"); + + result.put(MODE_MOVE_IMAGE, "Move image"); + result.put(MODE_CONVERT_BOX_TO_PICTUREFRAME, "Set frame to area"); + result.put(MODE_SELECT_PICTUREFRAME, "Select frame"); + + result.put(MODE_CLEAR_QUEUE, "Clear queue"); + result.put(MODE_EXPORT_QUEUE, "Export queue"); + result.put(MODE_IMPORT_QUEUE, "Import queue"); + result.put(MODE_RESIZE_IMAGE, "Resize image"); + + result.put(MODE_RENDER_COMMAND_QUEUE, "Preview queue"); + + result.put(MODE_CHANGE_GRID_SIZE, "Grid size"); + result.put(MODE_CHANGE_SAMPLE_AREA, "Sample area"); + + result.put(MODE_SHOW_IMAGE, "Show image"); + result.put(MODE_SHOW_DENSITY_PREVIEW, "Show density preview"); + result.put(MODE_SHOW_QUEUE_PREVIEW, "Show Queue preview"); + result.put(MODE_SHOW_VECTOR, "Show Vector"); + result.put(MODE_SHOW_GUIDES, "Show Guides"); + + result.put(MODE_CHANGE_MACHINE_WIDTH, "Machine Width"); + result.put(MODE_CHANGE_MACHINE_HEIGHT, "Machine Height"); + result.put(MODE_CHANGE_MM_PER_REV, "MM Per Rev"); + result.put(MODE_CHANGE_STEPS_PER_REV, "Steps Per Rev"); + result.put(MODE_CHANGE_STEP_MULTIPLIER, "Step multiplier"); + result.put(MODE_CHANGE_PAGE_WIDTH, "Page Width"); + result.put(MODE_CHANGE_PAGE_HEIGHT, "Page Height"); + result.put(MODE_CHANGE_PAGE_OFFSET_X, "Page Pos X"); + result.put(MODE_CHANGE_PAGE_OFFSET_Y, "Page Pos Y"); + result.put(MODE_CHANGE_PAGE_OFFSET_X_CENTRE, "Centre page"); + + result.put(MODE_CHANGE_HOMEPOINT_X, "Home Pos X"); + result.put(MODE_CHANGE_HOMEPOINT_Y, "Home Pos Y"); + result.put(MODE_CHANGE_HOMEPOINT_X_CENTRE, "Centre Homepoint"); + + result.put(MODE_CHANGE_PEN_WIDTH, "Pen tip size"); + result.put(MODE_SEND_PEN_WIDTH, "Send Pen tip size"); + + result.put(MODE_CHANGE_PEN_TEST_START_WIDTH, "Pen test start tip"); + result.put(MODE_CHANGE_PEN_TEST_END_WIDTH, "Pen test end tip"); + result.put(MODE_CHANGE_PEN_TEST_INCREMENT_SIZE, "Pen test inc size"); + + result.put(MODE_CHANGE_MACHINE_MAX_SPEED, "Motor max speed"); + result.put(MODE_CHANGE_MACHINE_ACCELERATION, "Motor acceleration"); + result.put(MODE_SEND_MACHINE_SPEED, "Send speed"); + result.put(MODE_RENDER_VECTORS, "Draw vectors"); + result.put(MODE_LOAD_VECTOR_FILE, "Load vector"); + result.put(MODE_CHANGE_MIN_VECTOR_LINE_LENGTH, "Shortest vector"); + + result.put(MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD, "Bright pixel"); + result.put(MODE_IMAGE_PIXEL_DARK_THRESHOLD, "Dark pixel"); + + result.put(MODE_CHANGE_SERIAL_PORT, "Serial port..."); + + result.put(MODE_SEND_MACHINE_STORE_MODE, "Signal store..."); + result.put(MODE_SEND_MACHINE_LIVE_MODE, "Signal play"); + result.put(MODE_SEND_MACHINE_EXEC_MODE, "Exec from store..."); + + result.put(MODE_RESIZE_VECTOR, "Resize vector"); + result.put(MODE_MOVE_VECTOR, "Move vector"); + result.put(MODE_RENDER_PIXEL_DIALOG, "Render pixels..."); + result.put(MODE_CHOOSE_CHROMA_KEY_COLOUR, "Choose mask colour"); + result.put(MODE_CHANGE_PIXEL_SCALING, "Pixel scaling"); + + result.put(MODE_PEN_LIFT_UP, "Pen lift"); + result.put(MODE_PEN_LIFT_DOWN, "Pen drop"); + + result.put(MODE_SEND_ROVE_AREA, "Send Roving Area"); + result.put(MODE_SEND_START_TEXT, "Start text at point"); + result.put(MODE_SHOW_WRITING_DIALOG, "Render writing..."); + + result.put(MODE_START_SWIRLING, "Swirl"); + result.put(MODE_STOP_SWIRLING, "Stop swirl"); + result.put(MODE_START_SPRITE, "Choose sprite..."); + result.put(MODE_START_RANDOM_SPRITES, "Random sprites"); + result.put(MODE_STOP_RANDOM_SPRITES, "Stop sprites"); + result.put(MODE_DRAW_NORWEGIAN_DIALOG, "Draw norwegian..."); + + return result; +} + +Set buildControlNames() +{ + Set result = new HashSet(); + result.add(MODE_BEGIN); + result.add(MODE_INPUT_BOX_TOP_LEFT); + result.add(MODE_INPUT_BOX_BOT_RIGHT); + result.add(MODE_DRAW_OUTLINE_BOX); + result.add(MODE_DRAW_OUTLINE_BOX_ROWS); + result.add(MODE_DRAW_SHADE_BOX_ROWS_PIXELS); + result.add(MODE_DRAW_TO_POSITION); + result.add(MODE_DRAW_DIRECT); + result.add(MODE_RENDER_SQUARE_PIXELS); + result.add(MODE_RENDER_SCALED_SQUARE_PIXELS); + result.add(MODE_RENDER_SAW_PIXELS); + result.add(MODE_RENDER_CIRCLE_PIXELS); + + result.add(MODE_RENDER_PIXEL_DIALOG); + + result.add(MODE_INPUT_ROW_START); + result.add(MODE_INPUT_ROW_END); + result.add(MODE_SET_POSITION); + result.add(MODE_DRAW_GRID); + result.add(MODE_DRAW_TESTPATTERN); + result.add(MODE_PLACE_IMAGE); + result.add(MODE_LOAD_IMAGE); + result.add(MODE_INC_ROW_SIZE); + result.add(MODE_DEC_ROW_SIZE); + result.add(MODE_SET_POSITION_HOME); + result.add(MODE_RETURN_TO_HOME); + result.add(MODE_INPUT_SINGLE_PIXEL); + result.add(MODE_DRAW_TEST_PENWIDTH); + result.add(MODE_RENDER_SOLID_SQUARE_PIXELS); + result.add(MODE_RENDER_SCRIBBLE_PIXELS); + result.add(MODE_CHANGE_MACHINE_SPEC); + result.add(MODE_REQUEST_MACHINE_SIZE); + result.add(MODE_RESET_MACHINE); + + result.add(MODE_SAVE_PROPERTIES); + result.add(MODE_SAVE_AS_PROPERTIES); + result.add(MODE_LOAD_PROPERTIES); + + result.add(MODE_INC_SAMPLE_AREA); + result.add(MODE_DEC_SAMPLE_AREA); + result.add(MODE_MOVE_IMAGE); + result.add(MODE_CONVERT_BOX_TO_PICTUREFRAME); + result.add(MODE_SELECT_PICTUREFRAME); + result.add(MODE_CLEAR_QUEUE); + result.add(MODE_EXPORT_QUEUE); + result.add(MODE_IMPORT_QUEUE); + result.add(MODE_FIT_IMAGE_TO_BOX); + result.add(MODE_RESIZE_IMAGE); + result.add(MODE_RENDER_COMMAND_QUEUE); + + result.add(MODE_CHANGE_GRID_SIZE); + result.add(MODE_CHANGE_SAMPLE_AREA); + + result.add(MODE_SHOW_IMAGE); + result.add(MODE_SHOW_DENSITY_PREVIEW); + result.add(MODE_SHOW_VECTOR); + result.add(MODE_SHOW_QUEUE_PREVIEW); + result.add(MODE_SHOW_GUIDES); + + result.add(MODE_CHANGE_MACHINE_WIDTH); + result.add(MODE_CHANGE_MACHINE_HEIGHT); + result.add(MODE_CHANGE_MM_PER_REV); + result.add(MODE_CHANGE_STEPS_PER_REV); + result.add(MODE_CHANGE_STEP_MULTIPLIER); + result.add(MODE_CHANGE_PAGE_WIDTH); + result.add(MODE_CHANGE_PAGE_HEIGHT); + result.add(MODE_CHANGE_PAGE_OFFSET_X); + result.add(MODE_CHANGE_PAGE_OFFSET_Y); + result.add(MODE_CHANGE_PAGE_OFFSET_X_CENTRE); + + result.add(MODE_CHANGE_HOMEPOINT_X); + result.add(MODE_CHANGE_HOMEPOINT_Y); + result.add(MODE_CHANGE_HOMEPOINT_X_CENTRE); + + result.add(MODE_CHANGE_PEN_WIDTH); + + result.add(MODE_CHANGE_PEN_TEST_START_WIDTH); + result.add(MODE_CHANGE_PEN_TEST_END_WIDTH); + result.add(MODE_CHANGE_PEN_TEST_INCREMENT_SIZE); + + result.add(MODE_SEND_PEN_WIDTH); + + result.add(MODE_CHANGE_MACHINE_MAX_SPEED); + result.add(MODE_CHANGE_MACHINE_ACCELERATION); + result.add(MODE_SEND_MACHINE_SPEED); + + result.add(MODE_RENDER_VECTORS); + result.add(MODE_LOAD_VECTOR_FILE); + result.add(MODE_IMAGE_PIXEL_BRIGHT_THRESHOLD); + result.add(MODE_IMAGE_PIXEL_DARK_THRESHOLD); + result.add(MODE_CHANGE_SERIAL_PORT); + + result.add(MODE_SEND_MACHINE_STORE_MODE); + result.add(MODE_SEND_MACHINE_LIVE_MODE); + result.add(MODE_SEND_MACHINE_EXEC_MODE); + + result.add(MODE_RESIZE_VECTOR); + result.add(MODE_MOVE_VECTOR); + result.add(MODE_CHANGE_MIN_VECTOR_LINE_LENGTH); + + result.add(MODE_CHOOSE_CHROMA_KEY_COLOUR); + result.add(MODE_CHANGE_PIXEL_SCALING); + result.add(MODE_PEN_LIFT_UP); + result.add(MODE_PEN_LIFT_DOWN); + + result.add(MODE_SEND_ROVE_AREA); + result.add(MODE_SEND_START_TEXT); + result.add(MODE_SHOW_WRITING_DIALOG); + result.add(MODE_START_SWIRLING); + result.add(MODE_STOP_SWIRLING); + result.add(MODE_START_SPRITE); + result.add(MODE_START_RANDOM_SPRITES); + result.add(MODE_STOP_RANDOM_SPRITES); + result.add(MODE_DRAW_NORWEGIAN_DIALOG); + + return result; +} + + diff --git a/drawing.pde b/drawing.pde new file mode 100644 index 0000000..9143766 --- /dev/null +++ b/drawing.pde @@ -0,0 +1,770 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ +static final String CMD_CHANGELENGTH = "C01,"; +static final String CMD_CHANGEPENWIDTH = "C02,"; +static final String CMD_CHANGEMOTORSPEED = "C03,"; +static final String CMD_CHANGEMOTORACCEL = "C04,"; +static final String CMD_DRAWPIXEL = "C05,"; +static final String CMD_DRAWSCRIBBLEPIXEL = "C06,"; +static final String CMD_DRAWRECT = "C07,"; +static final String CMD_CHANGEDRAWINGDIRECTION = "C08,"; +static final String CMD_SETPOSITION = "C09,"; +static final String CMD_TESTPATTERN = "C10,"; +static final String CMD_TESTPENWIDTHSQUARE = "C11,"; +static final String CMD_TESTPENWIDTHSCRIBBLE = "C12,"; +static final String CMD_PENDOWN = "C13,"; +static final String CMD_PENUP = "C14,"; +static final String CMD_DRAWSAWPIXEL = "C15,"; +static final String CMD_DRAWROUNDPIXEL = "C16,"; +static final String CMD_CHANGELENGTHDIRECT = "C17,"; +static final String CMD_TXIMAGEBLOCK = "C18,"; +static final String CMD_STARTROVE = "C19,"; +static final String CMD_STOPROVE = "C20,"; +static final String CMD_SET_ROVE_AREA = "C21,"; +static final String CMD_LOADMAGEFILE = "C23,"; +static final String CMD_CHANGEMACHINESIZE = "C24,"; +static final String CMD_CHANGEMACHINENAME = "C25,"; +static final String CMD_REQUESTMACHINESIZE = "C26,"; +static final String CMD_RESETMACHINE = "C27,"; +static final String CMD_DRAWDIRECTIONTEST = "C28,"; +static final String CMD_CHANGEMACHINEMMPERREV = "C29,"; +static final String CMD_CHANGEMACHINESTEPSPERREV = "C30,"; +static final String CMD_SETMOTORSPEED = "C31,"; +static final String CMD_SETMOTORACCEL = "C32,"; +static final String CMD_MACHINE_MODE_STORE_COMMANDS = "C33,"; +static final String CMD_MACHINE_MODE_EXEC_FROM_STORE = "C34,"; +static final String CMD_MACHINE_MODE_LIVE = "C35,"; +static final String CMD_RANDOM_DRAW = "C36,"; +static final String CMD_SETMACHINESTEPMULTIPLIER = "C37,"; +static final String CMD_START_TEXT = "C38,"; +static final String CMD_DRAW_SPRITE = "C39,"; +static final String CMD_CHANGELENGTH_RELATIVE = "C40,"; +static final String CMD_SWIRLING = "C41,"; +static final String CMD_DRAW_RANDOM_SPRITE = "C42,"; +static final String CMD_DRAW_NORWEGIAN = "C43,"; +static final String CMD_DRAW_NORWEGIAN_OUTLINE = "C44,"; + +private PVector mouseVector = new PVector(0,0); + +Comparator xAscending = new Comparator() +{ + public int compare(Object p1, Object p2) + { + PVector a = (PVector) p1; + PVector b = (PVector) p2; + + int xValue = new Float(a.x).compareTo(b.x); + return xValue; + } +}; + +Comparator yAscending = new Comparator() +{ + public int compare(Object p1, Object p2) + { + PVector a = (PVector) p1; + PVector b = (PVector) p2; + + int yValue = new Float(a.y).compareTo(b.y); + return yValue; + } +}; + +void sendResetMachine() +{ + String command = CMD_RESETMACHINE + "END"; + addToCommandQueue(command); +} +void sendRequestMachineSize() +{ + String command = CMD_REQUESTMACHINESIZE + "END"; + addToCommandQueue(command); +} +void sendMachineSpec() +{ + // ask for input to get the new machine size + String command = CMD_CHANGEMACHINENAME+newMachineName+",END"; + addToCommandQueue(command); + command = CMD_CHANGEMACHINESIZE+getDisplayMachine().inMM(getDisplayMachine().getWidth())+","+getDisplayMachine().inMM(getDisplayMachine().getHeight())+",END"; + addToCommandQueue(command); + command = CMD_CHANGEMACHINEMMPERREV+int(getDisplayMachine().getMMPerRev())+",END"; + addToCommandQueue(command); + command = CMD_CHANGEMACHINESTEPSPERREV+int(getDisplayMachine().getStepsPerRev())+",END"; + addToCommandQueue(command); + command = CMD_SETMACHINESTEPMULTIPLIER+machineStepMultiplier+",END"; + addToCommandQueue(command); +} + +public PVector getMouseVector() +{ + if (mouseVector == null) + { + mouseVector = new PVector(0,0); + } + + mouseVector.x = mouseX; + mouseVector.y = mouseY; + return mouseVector; +} + +// Uses the mouse position unless one is specified +void sendMoveToPosition(boolean direct) +{ + sendMoveToPosition(direct, getMouseVector()); +} + +void sendMoveToPosition(boolean direct, PVector position) +{ + String command = null; + PVector p = getDisplayMachine().scaleToDisplayMachine(position); + p = getDisplayMachine().inSteps(p); + p = getDisplayMachine().asNativeCoords(p); + sendMoveToNativePosition(direct, p); +} + +void sendMoveToNativePosition(boolean direct, PVector p) +{ + String command = null; + if (direct) + command = CMD_CHANGELENGTHDIRECT+int(p.x+0.5)+","+int(p.y+0.5)+","+getMaxSegmentLength()+",END"; + else + command = CMD_CHANGELENGTH+(int)p.x+","+(int)p.y+",END"; + + addToCommandQueue(command); +} + + +int getMaxSegmentLength() +{ + return this.maxSegmentLength; +} + +void sendTestPattern() +{ + String command = CMD_DRAWDIRECTIONTEST+int(gridSize)+",6,END"; + addToCommandQueue(command); +} + +void sendTestPenWidth() +{ + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("##0.##"); + StringBuilder sb = new StringBuilder(); + sb.append(testPenWidthCommand) + .append(int(gridSize)) + .append(",") + .append(df.format(testPenWidthStartSize)) + .append(",") + .append(df.format(testPenWidthEndSize)) + .append(",") + .append(df.format(testPenWidthIncrementSize)) + .append(",END"); + addToCommandQueue(sb.toString()); +} + +void sendSetPosition() +{ + PVector p = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); + p = getDisplayMachine().convertToNative(p); + p = getDisplayMachine().inSteps(p); + + String command = CMD_SETPOSITION+int(p.x+0.5)+","+int(p.y+0.5)+",END"; + addToCommandQueue(command); +} + +void sendStartTextAtPoint() +{ + PVector p = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); + p = getDisplayMachine().convertToNative(p); + p = getDisplayMachine().inSteps(p); + + String command = CMD_START_TEXT+(int)p.x+","+(int)p.y+","+gridSize+",2,END"; + addToCommandQueue(command); +} + +void sendSetHomePosition() +{ + PVector pgCoords = getDisplayMachine().asNativeCoords(getHomePoint()); + + String command = CMD_SETPOSITION+int(pgCoords.x+0.5)+","+int(pgCoords.y+0.5)+",END"; + addToCommandQueue(command); +} + +int scaleDensity(int inDens, int inMax, int outMax) +{ + float reducedDens = (float(inDens) / float(inMax)) * float(outMax); + reducedDens = outMax-reducedDens; +// println("inDens:"+inDens+", inMax:"+inMax+", outMax:"+outMax+", reduced:"+reducedDens); + + // round up if bigger than .5 + int result = int(reducedDens); + if (reducedDens - (result) > 0.5) + result ++; + + //result = outMax - result; + return result; +} + +SortedMap> divideIntoRows(Set pixels, int direction) +{ + SortedMap> inRows = new TreeMap>(); + + for (PVector p : pixels) + { + Float row = p.x; + if (direction == DRAW_DIR_SE || direction == DRAW_DIR_NW) + row = p.y; + + if (!inRows.containsKey(row)) + { + inRows.put(row, new ArrayList()); + } + inRows.get(row).add(p); + } + return inRows; +} + +PVector sortPixelsInRowsAlternating(SortedMap> inRows, int initialDirection, float maxPixelSize) +{ + PVector startPoint = null; + Comparator comp = null; + boolean rowIsAlongXAxis = true; + + if (initialDirection == DRAW_DIR_SE || initialDirection == DRAW_DIR_NW) + { + rowIsAlongXAxis = true; + comp = xAscending; + } + else + { + rowIsAlongXAxis = false; + comp = yAscending; + } + + // now sort each row, reversing the direction after each row + boolean reverse = false; + for (Float rowCoord : inRows.keySet()) + { + println("row: " + rowCoord); + List row = inRows.get(rowCoord); + + if (reverse) + { + // reverse it (descending) + Collections.sort(row, comp); + Collections.reverse(row); +// if (startPoint == null) +// { +// if (rowIsAlongXAxis) +// startPoint = new PVector(row.get(0).x+(maxPixelSize/2.0), row.get(0).y); +// else +// startPoint = new PVector(row.get(0).x, row.get(0).y-(maxPixelSize/2.0)); +// } + reverse = false; + } + else + { + // sort row ascending + Collections.sort(row, comp); +// if (startPoint == null) +// { +// if (rowIsAlongXAxis) +// startPoint = new PVector(row.get(0).x-(maxPixelSize/2.0), row.get(0).y); +// else +// startPoint = new PVector(row.get(0).x, row.get(0).y+(maxPixelSize/2.0)); +// } + reverse = true; + } + } + return startPoint; +} + +void sortPixelsInRows(SortedMap> inRows, int initialDirection) +{ + PVector startPoint = null; + Comparator comp = null; + boolean rowIsAlongXAxis = true; + + if (initialDirection == DRAW_DIR_SE || initialDirection == DRAW_DIR_NW) + { + rowIsAlongXAxis = true; + comp = xAscending; + } + else + { + rowIsAlongXAxis = false; + comp = yAscending; + } + + // now sort each row, reversing the direction after each row + for (Float rowCoord : inRows.keySet()) + { + println("row: " + rowCoord); + List row = inRows.get(rowCoord); + // sort row ascending + Collections.sort(row, comp); + + if (initialDirection == DRAW_DIR_NW || initialDirection == DRAW_DIR_NE) + Collections.reverse(row); + } +} + + + +void sendPixels(Set pixels, String pixelCommand, int initialDirection, int startCorner, float maxPixelSize, boolean scaleSizeToDensity) +{ + + // sort it into a map of rows, keyed by y coordinate value + SortedMap> inRows = divideIntoRows(pixels, initialDirection); + + sortPixelsInRowsAlternating(inRows, initialDirection, maxPixelSize); + + // that was easy. + // load the queue + // add a preamble + + // set the first direction + int drawDirection = initialDirection; + String changeDir = CMD_CHANGEDRAWINGDIRECTION+getPixelDirectionMode()+"," + drawDirection +",END"; + addToCommandQueue(changeDir); + + // reverse the row sequence if the draw is starting from the bottom + // and reverse the pixel sequence if it needs to be done (odd number of rows) + boolean reversePixelSequence = false; + List rowKeys = new ArrayList(); + rowKeys.addAll(inRows.keySet()); + Collections.sort(rowKeys); + if (startCorner == DRAW_DIR_SE || startCorner == DRAW_DIR_SW) + { + Collections.reverse(rowKeys); + if (rowKeys.size() % 2 == 0) + reversePixelSequence = true; + } + + // and move the pen to just next to the first pixel + List firstRow = inRows.get(rowKeys.get(0)); + + PVector startPoint = firstRow.get(0); + int startPointX = int(startPoint.x); + int startPointY = int(startPoint.y); + int halfSize = int(maxPixelSize/2.0); + + print("Dir:"); + if (initialDirection == DRAW_DIR_SE) + { + startPointX-=halfSize; + println("SE"); + } + else if (initialDirection == DRAW_DIR_SW) + { + startPointY-=halfSize; + println("SW"); + } + else if (initialDirection == DRAW_DIR_NW) + { + startPointX-=halfSize; + println("NW"); + } + else if (initialDirection == DRAW_DIR_NE) + { + startPointY-=halfSize; + println("NE"); + } + + if (startPoint != null) + { + String touchdown = CMD_CHANGELENGTH+int(startPointX)+","+int(startPointY)+",END"; + addToCommandQueue(touchdown); + addToCommandQueue(CMD_PENDOWN+"END"); + } + + boolean penLifted = false; + + // so for each row + for (Float key : rowKeys) + { + List row = inRows.get(key); + if (reversePixelSequence) + Collections.reverse(row); + + for (PVector v : row) + { + if (isHiddenPixel(v)) // check for masked pixels, + { + //println("It's outside the bright/dark threshold."); + if (liftPenOnMaskedPixels) + { + if (!penLifted) // if the pen isn't already up + { + String raisePen = CMD_PENUP + "END"; + addToCommandQueue(raisePen); + penLifted = true; + } + else + { + // println("Pen is already lifted."); + } + // now convert to ints + int inX = int(v.x); + int inY = int(v.y); + int pixelSize = int(maxPixelSize); + // render a fully bright (255) pixel. + String command = pixelCommand+inX+","+inY+","+int(pixelSize+0.5)+",255,END"; + addToCommandQueue(command); + } + else + { + //println("liftPenOnMaskedPixels is not selected."); + } + // so this pixel doesn't get added to the queue. + } + else // pixel wasn't masked - render it up + { + // now convert to ints + int inX = int(v.x); + int inY = int(v.y); + Integer density = int(v.z); + int pixelSize = int(maxPixelSize); + if (scaleSizeToDensity) + { + pixelSize = scaleDensity(density, 255, int(maxPixelSize)); + density = 0; + } + int scaledPixelSize = int((pixelSize*getPixelScalingOverGridSize())+0.5); + String command = pixelCommand+inX+","+inY+","+scaledPixelSize+","+density+",END"; + + // put the pen down if lifting over masked pixels is on + if (liftPenOnMaskedPixels && penLifted) + { +// println("Pen down."); + String lowerPen = CMD_PENDOWN + "END"; + addToCommandQueue(lowerPen); + penLifted = false; + } + addToCommandQueue(command); + } + } + + drawDirection = flipDrawDirection(drawDirection); + String command = CMD_CHANGEDRAWINGDIRECTION+getPixelDirectionMode()+"," + drawDirection +",END"; + addToCommandQueue(command); + } + + addToCommandQueue(CMD_PENUP+"END"); + numberOfPixelsTotal = commandQueue.size(); + startPixelTimer(); +} + + +int flipDrawDirection(int curr) +{ + if (curr == DRAW_DIR_SE) + return DRAW_DIR_NW; + else if (curr == DRAW_DIR_NW) + return DRAW_DIR_SE; + else if (curr == DRAW_DIR_NE) + return DRAW_DIR_SW; + else if (curr == DRAW_DIR_SW) + return DRAW_DIR_NE; + else return DRAW_DIR_SE; +} + + +int getPixelDirectionMode() +{ + return pixelDirectionMode; +} + + +void sendSawtoothPixels(Set pixels) +{ + sendPixels(pixels, CMD_DRAWSAWPIXEL, renderStartDirection, renderStartPosition, getGridSize(), false); +} +void sendCircularPixels(Set pixels) +{ + sendPixels(pixels, CMD_DRAWROUNDPIXEL, renderStartDirection, renderStartPosition, getGridSize(), false); +} + +void sendScaledSquarePixels(Set pixels) +{ + sendPixels(pixels, CMD_DRAWPIXEL, renderStartDirection, renderStartPosition, getGridSize(), true); +} + +void sendSolidSquarePixels(Set pixels) +{ + for (PVector p : pixels) + { + if (p.z != MASKED_PIXEL_BRIGHTNESS) + p.z = 0.0; + } + sendPixels(pixels, CMD_DRAWPIXEL, renderStartDirection, renderStartPosition, getGridSize(), false); +} + +void sendSquarePixels(Set pixels) +{ + sendPixels(pixels, CMD_DRAWPIXEL, renderStartDirection, renderStartPosition, getGridSize(), false); +} + +void sendScribblePixels(Set pixels) +{ + sendPixels(pixels, CMD_DRAWSCRIBBLEPIXEL, renderStartDirection, renderStartPosition, getGridSize(), false); +} + + +void sendOutlineOfPixels(Set pixels) +{ + // sort it into a map of rows, keyed by y coordinate value + SortedMap> inRows = divideIntoRows(pixels, DRAW_DIR_SE); + + sortPixelsInRowsAlternating(inRows, DRAW_DIR_SE, getGridSize()); + + float halfGrid = getGridSize() / 2.0; + for (Float key : inRows.keySet()) + { + for (PVector p : inRows.get(key)) + { + PVector startPoint = new PVector(p.x-halfGrid, p.y-halfGrid); + PVector endPoint = new PVector(p.x+halfGrid, p.y+halfGrid); + String command = CMD_DRAWRECT + int(startPoint.x)+","+int(startPoint.y)+","+int(endPoint.x)+","+int(endPoint.y)+",END"; + addToCommandQueue(command); + } + } +} + +void sendOutlineOfRows(Set pixels, int drawDirection) +{ + // sort it into a map of rows, keyed by y coordinate value + SortedMap> inRows = divideIntoRows(pixels, drawDirection); + + sortPixelsInRows(inRows, drawDirection); + + PVector offset = new PVector(getGridSize() / 2.0, getGridSize() / 2.0); + for (Float key : inRows.keySet()) + { + PVector startPoint = inRows.get(key).get(0); + PVector endPoint = inRows.get(key).get(inRows.get(key).size()-1); + + if (drawDirection == DRAW_DIR_SE) + { + startPoint.sub(offset); + endPoint.add(offset); + } + else if (drawDirection == DRAW_DIR_NW) + { + startPoint.add(offset); + endPoint.sub(offset); + } + else if (drawDirection == DRAW_DIR_SW) + { + startPoint.add(offset); + endPoint.sub(offset); + } + else if (drawDirection == DRAW_DIR_NW) + { + startPoint.add(offset); + endPoint.sub(offset); + } + + String command = CMD_DRAWRECT + int(startPoint.x)+","+int(startPoint.y)+","+int(endPoint.x)+","+int(endPoint.y)+",END"; + addToCommandQueue(command); + } +} + +void sendGridOfBox(Set pixels) +{ + sendOutlineOfRows(pixels, DRAW_DIR_SE); + sendOutlineOfRows(pixels, DRAW_DIR_SW); +} + + +void sendOutlineOfBox() +{ + // convert cartesian to native format + PVector tl = getDisplayMachine().inSteps(getBoxVector1()); + PVector br = getDisplayMachine().inSteps(getBoxVector2()); + + PVector tr = new PVector(br.x, tl.y); + PVector bl = new PVector(tl.x, br.y); + + tl = getDisplayMachine().asNativeCoords(tl); + tr = getDisplayMachine().asNativeCoords(tr); + bl = getDisplayMachine().asNativeCoords(bl); + br = getDisplayMachine().asNativeCoords(br); + + String command = CMD_CHANGELENGTHDIRECT+(int)tl.x+","+(int)tl.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + + command = CMD_CHANGELENGTHDIRECT+(int)tr.x+","+(int)tr.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + + command = CMD_CHANGELENGTHDIRECT+(int)br.x+","+(int)br.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + + command = CMD_CHANGELENGTHDIRECT+(int)bl.x+","+(int)bl.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + + command = CMD_CHANGELENGTHDIRECT+(int)tl.x+","+(int)tl.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + +} + + +void sendVectorShapes() +{ + RPoint[][] pointPaths = getVectorShape().getPointsInPaths(); + + String command = ""; + + // go through and get each path + for (int i = 0; i filteredPoints = filterPoints(pointPaths[i], VECTOR_FILTER_LOW_PASS, minimumVectorLineLength); + //println(filteredPoints); + if (!filteredPoints.isEmpty()) + { + // draw the first one with a pen up and down to get to it + PVector p = filteredPoints.get(0); + // pen UP! + addToCommandQueue(CMD_PENUP+"END"); + // move to this point and put the pen down + command = CMD_CHANGELENGTHDIRECT+(int)p.x+","+(int)p.y+","+getMaxSegmentLength()+",END"; + addToCommandQueue(command); + addToCommandQueue(CMD_PENDOWN+"END"); + + // then just iterate through the rest + for (int j=1; j filterPoints(RPoint[] points, int filterToUse, long filterParam) +{ + return filterPointsLowPass(points, filterParam); +} + +List filterPointsLowPass(RPoint[] points, long filterParam) +{ + List result = new ArrayList(); + + // scale and convert all the points first + List scaled = new ArrayList(points.length); + for (int j = 0; j 1) + { + PVector p = scaled.get(0); + result.add(p); + + for (int j = 1; j filterParam || abs(diffy) > filterParam) + { + println("Adding point " + p + ", last: " + result.get(result.size()-1)); + result.add(p); + } + } + } + + if (result.size() < 2) + result.clear(); + + println("finished filter."); + return result; +} + + + + +void sendMachineStoreMode() +{ + String overwrite = ",R"; + if (!getOverwriteExistingStoreFile()) + overwrite = ",A"; + + addToCommandQueue(CMD_MACHINE_MODE_STORE_COMMANDS + getStoreFilename()+overwrite+",END"); +} +void sendMachineLiveMode() +{ + addToCommandQueue(CMD_MACHINE_MODE_LIVE+"END"); +} +void sendMachineExecMode() +{ + sendMachineLiveMode(); + if (storeFilename != null && !"".equals(storeFilename)) + addToCommandQueue(CMD_MACHINE_MODE_EXEC_FROM_STORE + getStoreFilename() + ",END"); +} +void sendRandomDraw() +{ + addToCommandQueue(CMD_RANDOM_DRAW+"END"); +} +void sendStartSwirling() +{ + addToCommandQueue(CMD_SWIRLING+"1,END"); +} +void sendStopSwirling() +{ + addToCommandQueue(CMD_SWIRLING+"0,END"); +} +void sendDrawRandomSprite(String spriteFilename) +{ + addToCommandQueue(CMD_DRAW_RANDOM_SPRITE+","+spriteFilename+",100,500,END"); +} + + + diff --git a/gpl.txt b/gpl.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/gpl.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/polargraphcontroller.pde b/polargraphcontroller.pde new file mode 100644 index 0000000..8cdc2f1 --- /dev/null +++ b/polargraphcontroller.pde @@ -0,0 +1,2865 @@ +import geomerative.*; +import org.apache.batik.svggen.font.table.*; +import org.apache.batik.svggen.font.*; +import java.util.zip.CRC32; + +// for OSX +import java.text.*; +import java.util.*; +import java.io.*; + +import java.util.logging.*; +//import java.util.concurrent.CopyOnWriteArrayList; +//import processing.opengl.PGraphicsOpenGL; +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ + +*/ +import javax.swing.*; +import processing.serial.*; +import controlP5.*; +import java.awt.event.*; + +int majorVersionNo = 1; +int minorVersionNo = 2; +int buildNo = 5; + +String programTitle = "Polargraph Controller v" + majorVersionNo + "." + minorVersionNo + " build " + buildNo; +ControlP5 cp5; + +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; +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 + +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; + +// 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 previewCommandList = new ArrayList(); +long lastCommandQueueHash = 0L; + + +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 = 1; + +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_INC_ROW_SIZE = "button_mode_incRowSize"; +static final String MODE_DEC_ROW_SIZE = "button_mode_decRowSize"; +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_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_SEND_ROVE_AREA = "button_mode_sendRoveArea"; +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_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"; + +PVector statusTextPosition = new PVector(240.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 displayingImage = true; +boolean displayingVector = true; +boolean displayingQueuePreview = true; +boolean displayingDensityPreview = false; + +boolean displayingGuides = true; + +static final int DENSITY_PREVIEW_ROUND = 0; +static final int DENSITY_PREVIEW_DIAMOND = 1; +static final int DEFAULT_DENSITY_PREVIEW_STYLE = DENSITY_PREVIEW_DIAMOND; +int densityPreviewStyle = DEFAULT_DENSITY_PREVIEW_STYLE; + +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); + +// 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 boolean showingSummaryOverlay = true; +public boolean showingDialogBox = false; + +public Integer windowWidth = 650; +public Integer windowHeight = 400; + +public static Integer serialPortNumber = -1; + + +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"; + +// 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_GENERAL = "panel_general"; + +public final PVector DEFAULT_CONTROL_SIZE = new PVector(100.0, 20.0); +public final PVector CONTROL_SPACING = new PVector(2.0, 2.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.1; +public final float MAX_SCALING = 5.0; + +RShape vectorShape = null; +String vectorFilename = null; +float vectorScaling = 100; +PVector vectorPosition = new PVector(0.0,0.0); +int minimumVectorLineLength = 0; +public static final int VECTOR_FILTER_LOW_PASS = 0; + + +String storeFilename = "comm.txt"; +boolean overwriteExistingStoreFile = true; +//private static Logger logger; +public static Console console; +public boolean useWindowedConsole = false; + +void setup() +{ + println("Running polargraph controller"); + frame.setResizable(true); + initLogging(); + + RG.init(this); + RG.setPolygonizer(RG.ADAPTATIVE); + + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) + { + e.printStackTrace(); + } + loadFromPropertiesFile(); + + this.cp5 = new ControlP5(this); + initTabs(); + + String[] serialPorts = Serial.list(); + println("Serial ports available on your machine:"); + println(serialPorts); + +// println("getSerialPortNumber()"+getSerialPortNumber()); + 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(); + size(windowWidth, windowHeight, JAVA2D ); + changeTab(TAB_NAME_INPUT, TAB_NAME_INPUT); + + addEventListeners(); + +} +void addEventListeners() +{ + frame.addComponentListener(new ComponentAdapter() + { + public void componentResized(ComponentEvent event) + { + 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_CHANGEPENWIDTH+currentPenWidth+",END"); + addToCommandQueue(CMD_SETMOTORSPEED+currentMachineMaxSpeed+",END"); + addToCommandQueue(CMD_SETMOTORACCEL+currentMachineAccel+",END"); + +} + +void windowResized() +{ + windowWidth = frame.getWidth(); + windowHeight = frame.getHeight(); + for (String key : getPanels().keySet()) + { + Panel p = getPanels().get(key); + p.setHeight(frame.getHeight() - p.getOutline().getTop() - (DEFAULT_CONTROL_SIZE.y*2)); + } + +} +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 + { + 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() +{ + strokeWeight(1); + background(getBackgroundColour()); + noFill(); + stroke(255, 150, 255, 100); + strokeWeight(3); + stroke(150); + noFill(); + 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(); + if (displayingQueuePreview) + previewQueue(); + 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 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(1); + strokeWeight(1); + if (pointPaths != null) + { + 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())) + result = true; + } + 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 * 0.1); + if (machineScaling < MIN_SCALING) + { + machineScaling = MIN_SCALING; + scalingChanged = false; + } + else if (machineScaling > MAX_SCALING) + { + machineScaling = MAX_SCALING; + scalingChanged = false; + } +} + +void keyPressed() +{ + if (key == CODED) + { + // set key to zero (or something besides the ESC). + if (keyCode == java.awt.event.KeyEvent.VK_PAGE_UP) + { + changeMachineScaling(1); + } + else if (keyCode == java.awt.event.KeyEvent.VK_PAGE_DOWN) + { + changeMachineScaling(-1); + } + else if (keyCode == DOWN) + { + getDisplayMachine().getOffset().y = getDisplayMachine().getOffset().y + 10; + } + else if (keyCode == UP) + { + getDisplayMachine().getOffset().y = getDisplayMachine().getOffset().y - 10; + } + else if (keyCode == RIGHT) + { + getDisplayMachine().getOffset().x = getDisplayMachine().getOffset().x + 10; + } + else if (keyCode == LEFT) + { + getDisplayMachine().getOffset().x = getDisplayMachine().getOffset().x - 10; + } + } + else if (key == java.awt.event.KeyEvent.VK_ESCAPE) + { + key = 0; + } + else if (key == 'g' || key == '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 (key == 'c' || key == 'C') + { + if (isUseWindowedConsole()) + setUseWindowedConsole(false); + else + setUseWindowedConsole(true); + + initLogging(); + } + else if (key == 's' || key == 'S') + { + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + displayingSelectedCentres = (displayingSelectedCentres) ? false : true; + } + else if (key == 'i' || key == 'I') + { + println("I pressed!."); + displayingInfoTextOnInputPage = (displayingInfoTextOnInputPage) ? false : true; + } + else if (key == '+') + { + currentMachineMaxSpeed = currentMachineMaxSpeed+MACHINE_MAXSPEED_INCREMENT; + currentMachineMaxSpeed = Math.round(currentMachineMaxSpeed*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORSPEED+df.format(currentMachineMaxSpeed)+",END"); + } + else if (key == '-') + { + currentMachineMaxSpeed = currentMachineMaxSpeed+(0.0 - MACHINE_MAXSPEED_INCREMENT); + currentMachineMaxSpeed = Math.round(currentMachineMaxSpeed*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORSPEED+df.format(currentMachineMaxSpeed)+",END"); + } + else if (key == '*') + { + currentMachineAccel = currentMachineAccel+MACHINE_ACCEL_INCREMENT; + currentMachineAccel = Math.round(currentMachineAccel*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORACCEL+df.format(currentMachineAccel)+",END"); + } + else if (key == '/') + { + currentMachineAccel = currentMachineAccel+(0.0 - MACHINE_ACCEL_INCREMENT); + currentMachineAccel = Math.round(currentMachineAccel*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_SETMOTORACCEL+df.format(currentMachineAccel)+",END"); + } + else if (key == ']') + { + currentPenWidth = currentPenWidth+penIncrement; + currentPenWidth = Math.round(currentPenWidth*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_CHANGEPENWIDTH+df.format(currentPenWidth)+",END"); + } + else if (key == '[') + { + currentPenWidth = currentPenWidth-penIncrement; + currentPenWidth = Math.round(currentPenWidth*100.0)/100.0; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.UK); + DecimalFormat df = (DecimalFormat)nf; + df.applyPattern("###.##"); + addToRealtimeCommandQueue(CMD_CHANGEPENWIDTH+df.format(currentPenWidth)+",END"); + } + 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++; + } + else if (key == ',') + { + if (this.minimumVectorLineLength > 0) + this.minimumVectorLineLength--; + } + else if (key == '.') + { + this.minimumVectorLineLength++; + } +} +void mouseDragged() +{ + 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 mouseClicked() +{ + if (mouseOverPanel()) + { // changing mode +// panelClicked(); + } + 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)) + { + PVector mVect = getDisplayMachine().scaleToDisplayMachine(getMouseVector()); + vectorPosition = mVect; + } + 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); + } +} + +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) +{ + changeMachineScaling(delta); +} + +void setChromaKey(PVector p) +{ + color col = getDisplayMachine().getPixelAtScreenCoords(p); + chromaKeyColour = col; + if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified()) + { + getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea); + } +} + +boolean isPreviewable(String command) +{ + if ((command.startsWith(CMD_CHANGELENGTHDIRECT) + || command.startsWith(CMD_CHANGELENGTH))) + { + return true; + } + else + { + return false; + } +} + +/** + This will comb the command queue and attempt to draw a picture of what it contains. + Coordinates here are in pixels. +*/ +void previewQueue() +{ + PVector startPoint = null; + +// if (!commandHistory.isEmpty()) +// { +// Integer commandPosition = commandHistory.size()-1; +// String lastCommand = ""; +// while (commandPosition>=0) +// { +// lastCommand = commandHistory.get(commandPosition); +// if (isPreviewable(lastCommand)) +// { +// String[] splitted = split(lastCommand, ","); +// fullList.add(splitted); +//// fullList.add(lastCommand); +// break; +// } +// commandPosition--; +// } +// } + + if (commandQueue.hashCode() != lastCommandQueueHash) + { + println("regenerating preview queue."); + previewCommandList.clear(); + for (String command : commandQueue) + { + if ((command.startsWith(CMD_CHANGELENGTHDIRECT) || command.startsWith(CMD_CHANGELENGTH))) + { + 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), Integer.parseInt(bLenStr)); + endPoint = getDisplayMachine().asCartesianCoords(endPoint); + endPoint = getDisplayMachine().inMM(endPoint); + + pv.x = endPoint.x; + pv.y = endPoint.y; + + 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 (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 mmBoxSize = getDisplayMachine().inSteps(getBoxSize()); +// PVector mmBoxPos = getDisplayMachine().inSteps(getBoxVector1()); +// println("mm box: " + mmBoxSize); + + 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()) + { + String savePath = selectOutput(); // Opens file chooser + if (savePath == null) + { + // If a file was not selected + println("No output file was selected..."); + } + else + { + // If a file was selected, print path to folder + println("Output file: " + savePath); + List allCommands = new ArrayList(realtimeCommandQueue); + allCommands.addAll(commandQueue); + + String[] list = (String[]) allCommands.toArray(new String[0]); + saveStrings(savePath, list); + println("Completed queue export, " + list.length + " commands exported."); + } + } +} +void importQueueFromFile() +{ + commandQueue.clear(); + String loadPath = selectInput(); + if (loadPath == null) + { + // nothing selected + println("No input file was selected."); + } + else + { + println("Input file: " + loadPath); + String commands[] = loadStrings(loadPath); +// List list = Arrays + commandQueue.addAll(Arrays.asList(commands)); + println("Completed queue import, " + commandQueue.size() + " commands found."); + } +} + +String importTextToWriteFromFile() +{ + String loadPath = selectInput(); + String result = ""; + if (loadPath == null) + { + // nothing selected + println("No input file was selected."); + } + else + { + println("Input file: " + loadPath); + List rows = java.util.Arrays.asList(loadStrings(loadPath)); + StringBuilder sb = new StringBuilder(200); + for (String row : rows) + { + sb.append(row); + } + result = sb.toString(); + + println("Completed text import, " + result.length() + " characters found."); + } + return result; +} + + + +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 + 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()); + } +} + +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(); +} + +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, 800.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("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 ("RESEND".equals(incoming)) + resendLastCommand(); + else if ("DRAWING".equals(incoming)) + drawbotReady = false; + else if (incoming.startsWith("MEMORY")) + extractMemoryUsage(incoming); + + if (drawbotReady) + drawbotConnected = true; +} + +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 readMachinePosition(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(); + 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 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); + 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", 600.0); + this.currentMachineAccel = getFloatProperty("machine.motors.accel", 400.0); + this.machineStepMultiplier = getIntProperty("machine.step.multiplier", 1); + + // 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()); + + setVectorFilename(getStringProperty("controller.vector.filename", null)); + if (getVectorFilename() != null) + { + RShape shape = RG.loadShape(getVectorFilename()); + 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); + + 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", new Float(currentPenWidth).toString()); + // 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", new Float(sampleArea).toString()); + props.setProperty("controller.pixel.scaling", new Float(pixelScalingOverGridSize).toString()); + + // density preview style + props.setProperty("controller.density.preview.style", new Integer(getDensityPreviewStyle()).toString()); + + // initial screen size + props.setProperty("controller.window.width", new Integer((windowWidth < 50) ? 50 : windowWidth-16).toString()); + props.setProperty("controller.window.height", new Integer((windowWidth < 50) ? 50 : windowHeight-38).toString()); + + props.setProperty("controller.testPenWidth.startSize", new Float(testPenWidthStartSize).toString()); + props.setProperty("controller.testPenWidth.endSize", new Float(testPenWidthEndSize).toString()); + props.setProperty("controller.testPenWidth.incrementSize", new Float(testPenWidthIncrementSize).toString()); + + props.setProperty("controller.maxSegmentLength", new Integer(getMaxSegmentLength()).toString()); + + props.setProperty("machine.motors.maxSpeed", new Float(currentMachineMaxSpeed).toString()); + props.setProperty("machine.motors.accel", new Float(currentMachineAccel).toString()); + 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", new Float(hp.x).toString()); + props.setProperty("controller.homepoint.y", new Float(hp.y).toString()); + + if (getVectorFilename() != null) + props.setProperty("controller.vector.filename", getVectorFilename()); + + props.setProperty("controller.vector.scaling", new Float(vectorScaling).toString()); + props.setProperty("controller.vector.position.x", new Float(getVectorPosition().x).toString()); + props.setProperty("controller.vector.position.y", new Float(getVectorPosition().y).toString()); + props.setProperty("controller.vector.minLineLength", new Integer(this.minimumVectorLineLength).toString()); + + + 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; +} +boolean isUseWindowedConsole() +{ + return this.useWindowedConsole; +} +void setUseWindowedConsole(boolean use) +{ + this.useWindowedConsole = use; +} +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()); + } +} + diff --git a/tabSetup.pde b/tabSetup.pde new file mode 100644 index 0000000..4dd450d --- /dev/null +++ b/tabSetup.pde @@ -0,0 +1,107 @@ +/** + Polargraph controller + Copyright Sandy Noble 2012. + + 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/ + http://code.google.com/p/polargraph/ +*/ + +Set getPanelsForTab(String tabName) +{ + if (getPanelsForTabs().containsKey(tabName)) + { + return getPanelsForTabs().get(tabName); + } + else + return new HashSet(0); +} + +Map> buildPanelsForTabs() +{ + Map> map = new HashMap>(); + + Set inputPanels = new HashSet(2); + inputPanels.add(getPanel(PANEL_NAME_INPUT)); + inputPanels.add(getPanel(PANEL_NAME_GENERAL)); + + Set rovingPanels = new HashSet(2); + rovingPanels.add(getPanel(PANEL_NAME_ROVING)); + rovingPanels.add(getPanel(PANEL_NAME_GENERAL)); + + Set detailsPanels = new HashSet(2); + detailsPanels.add(getPanel(PANEL_NAME_DETAILS)); + detailsPanels.add(getPanel(PANEL_NAME_GENERAL)); + + Set queuePanels = new HashSet(2); + queuePanels.add(getPanel(PANEL_NAME_QUEUE)); + queuePanels.add(getPanel(PANEL_NAME_GENERAL)); + + map.put(TAB_NAME_INPUT, inputPanels); + map.put(TAB_NAME_ROVING, rovingPanels); + map.put(TAB_NAME_DETAILS, detailsPanels); + map.put(TAB_NAME_QUEUE, queuePanels); + + return map; +} + +List buildTabNames() +{ + List list = new ArrayList(4); + list.add(TAB_NAME_INPUT); + list.add(TAB_NAME_ROVING); + list.add(TAB_NAME_DETAILS); + list.add(TAB_NAME_QUEUE); + return list; +} + +void initTabs() +{ + cp5.tab(TAB_NAME_INPUT).setLabel(TAB_LABEL_INPUT); + cp5.tab(TAB_NAME_INPUT).activateEvent(true); + cp5.tab(TAB_NAME_INPUT).setId(1); + + cp5.tab(TAB_NAME_DETAILS).setLabel(TAB_LABEL_DETAILS); + cp5.tab(TAB_NAME_DETAILS).activateEvent(true); + cp5.tab(TAB_NAME_DETAILS).setId(2); + + cp5.tab(TAB_NAME_ROVING).setLabel(TAB_LABEL_ROVING); + cp5.tab(TAB_NAME_ROVING).activateEvent(true); + cp5.tab(TAB_NAME_ROVING).setId(3); + + cp5.tab(TAB_NAME_QUEUE).setLabel(TAB_LABEL_QUEUE); + cp5.tab(TAB_NAME_QUEUE).activateEvent(true); + cp5.tab(TAB_NAME_QUEUE).setId(4); +} + +public Set buildPanelNames() +{ + Set set = new HashSet(5); + set.add(PANEL_NAME_INPUT); + set.add(PANEL_NAME_ROVING); + set.add(PANEL_NAME_DETAILS); + set.add(PANEL_NAME_QUEUE); + set.add(PANEL_NAME_GENERAL); + return set; +} +