commit a2fa25cfaafbffebe3d1b94764446b88e5deb660 Author: Sandy Noble Date: Sun Feb 17 22:52:07 2013 +0000 Initial import This is the controller application. It's a Processing app. 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; +} +