diff --git a/FileLoading.pde b/FileLoading.pde
new file mode 100644
index 0000000..b8217a2
--- /dev/null
+++ b/FileLoading.pde
@@ -0,0 +1,436 @@
+/**
+ Polargraph controller
+ Copyright Sandy Noble 2018.
+
+ This file is part of Polargraph Controller.
+
+ Polargraph Controller is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Polargraph Controller is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Polargraph Controller. If not, see .
+
+ Requires the excellent ControlP5 GUI library available from http://www.sojamo.de/libraries/controlP5/.
+ Requires the excellent Geomerative library available from http://www.ricardmarxer.com/geomerative/.
+
+ This is an application for controlling a polargraph machine, communicating using ASCII command language over a serial link.
+
+ sandy.noble@gmail.com
+ http://www.polargraph.co.uk/
+ https://github.com/euphy/polargraphcontroller
+*/
+
+
+
+void loadImageWithFileChooser()
+{
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run() {
+ JFileChooser fc = new JFileChooser();
+ if (lastImageDirectory != null) fc.setCurrentDirectory(lastImageDirectory);
+ fc.setFileFilter(new ImageFileFilter());
+ fc.setDialogTitle("Choose an image file...");
+
+ int returned = fc.showOpenDialog(frame);
+
+ lastImageDirectory = fc.getCurrentDirectory();
+
+ if (returned == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ // see if it's an image
+ PImage img = loadImage(file.getPath());
+ if (img != null)
+ {
+ img = null;
+ getDisplayMachine().loadNewImageFromFilename(file.getPath());
+ if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified())
+ {
+ getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea);
+ }
+ }
+ }
+ }
+ });
+}
+
+class ImageFileFilter extends javax.swing.filechooser.FileFilter
+{
+ public boolean accept(File file) {
+ String filename = file.getName();
+ filename.toLowerCase();
+ if (file.isDirectory() || filename.endsWith(".png") || filename.endsWith(".jpg") || filename.endsWith(".jpeg"))
+ return true;
+ else
+ return false;
+ }
+ public String getDescription() {
+ return "Image files (PNG or JPG)";
+ }
+}
+
+void loadVectorWithFileChooser()
+{
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run() {
+ JFileChooser fc = new JFileChooser();
+ if (lastImageDirectory != null)
+ {
+ fc.setCurrentDirectory(lastImageDirectory);
+ }
+
+ fc.setFileFilter(new VectorFileFilter());
+ fc.setDialogTitle("Choose a vector file...");
+ int returned = fc.showOpenDialog(frame);
+ lastImageDirectory = fc.getCurrentDirectory();
+
+ if (returned == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ if (file.exists())
+ {
+ RShape shape = loadShapeFromFile(file.getPath());
+ if (shape != null)
+ {
+ setVectorFilename(file.getPath());
+ setVectorShape(shape);
+ }
+ else
+ {
+ println("File not found (" + file.getPath() + ")");
+ }
+ }
+ }
+ }
+ }
+ );
+}
+
+class VectorFileFilter extends javax.swing.filechooser.FileFilter
+{
+ public boolean accept(File file) {
+ String filename = file.getName();
+ filename.toLowerCase();
+ if (file.isDirectory() || filename.endsWith(".svg") || isGCodeExtension(filename))
+ return true;
+ else
+ return false;
+ }
+ public String getDescription() {
+ return "Vector graphic files (SVG, GCode)";
+ }
+}
+
+void loadNewPropertiesFilenameWithFileChooser()
+{
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ JFileChooser fc = new JFileChooser();
+ if (lastPropertiesDirectory != null) fc.setCurrentDirectory(lastPropertiesDirectory);
+ fc.setFileFilter(new PropertiesFileFilter());
+
+ fc.setDialogTitle("Choose a config file...");
+
+ int returned = fc.showOpenDialog(frame);
+
+ lastPropertiesDirectory = fc.getCurrentDirectory();
+
+ if (returned == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ if (file.exists())
+ {
+ println("New properties file exists.");
+ newPropertiesFilename = file.toString();
+ println("new propertiesFilename: "+ newPropertiesFilename);
+ propertiesFilename = newPropertiesFilename;
+ // clear old properties.
+ props = null;
+ loadFromPropertiesFile();
+
+ // set values of number spinners etc
+ updateNumberboxValues();
+ }
+ }
+ }
+ });
+}
+
+class PropertiesFileFilter extends javax.swing.filechooser.FileFilter
+{
+ public boolean accept(File file) {
+ String filename = file.getName();
+ filename.toLowerCase();
+ if (file.isDirectory() || filename.endsWith(".properties.txt"))
+ return true;
+ else
+ return false;
+ }
+ public String getDescription() {
+ return "Properties files (*.properties.txt)";
+ }
+}
+
+void saveNewPropertiesFileWithFileChooser()
+{
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ JFileChooser fc = new JFileChooser();
+ if (lastPropertiesDirectory != null) fc.setCurrentDirectory(lastPropertiesDirectory);
+ fc.setFileFilter(new PropertiesFileFilter());
+
+ fc.setDialogTitle("Enter a config file name...");
+
+ int returned = fc.showSaveDialog(frame);
+ if (returned == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ newPropertiesFilename = file.toString();
+ newPropertiesFilename.toLowerCase();
+ if (!newPropertiesFilename.endsWith(".properties.txt"))
+ newPropertiesFilename+=".properties.txt";
+
+ println("new propertiesFilename: "+ newPropertiesFilename);
+ propertiesFilename = newPropertiesFilename;
+ savePropertiesFile();
+ // clear old properties.
+ props = null;
+ loadFromPropertiesFile();
+ }
+ }
+ });
+}
+
+
+
+RShape loadShapeFromFile(String filename) {
+ RShape sh = null;
+ if (filename.toLowerCase().endsWith(".svg")) {
+ sh = RG.loadShape(filename);
+ }
+ else if (isGCodeExtension(filename)) {
+ sh = loadShapeFromGCodeFile(filename);
+ }
+ return sh;
+}
+
+
+boolean isGCodeExtension(String filename) {
+ return (filename.toLowerCase().endsWith(".gcode") || filename.toLowerCase().endsWith(".g") || filename.toLowerCase().endsWith(".ngc") || filename.toLowerCase().endsWith(".txt"));
+}
+
+
+int countLines(String filename) throws IOException {
+ InputStream is = new BufferedInputStream(new FileInputStream(filename));
+ try {
+ byte[] c = new byte[1024];
+ int count = 0;
+ int readChars = 0;
+ boolean empty = true;
+ while ((readChars = is.read(c)) != -1) {
+ empty = false;
+ for (int i = 0; i < readChars; ++i) {
+ if (c[i] == '\n') {
+ ++count;
+ }
+ }
+ }
+ return (count == 0 && !empty) ? 1 : count+1;
+ } finally {
+ is.close();
+ }
+}
+
+RShape loadShapeFromGCodeFile(String filename) {
+ noLoop();
+ RShape parent = null;
+ BufferedReader reader = null;
+ long totalPoints = 0;
+ long time = millis();
+ long countLines = 0;
+
+ try {
+ countLines = countLines(filename);
+ println("" + countLines + " lines found.");
+ if (countLines < 1) {
+ throw new IOException("No lines found in GCode file.");
+ }
+ reader = createReader(filename);
+ parent = new RShape();
+ String line;
+ boolean drawLine = false;
+ int gCodeZAxisChanges = 0;
+
+ long lineNo = 0;
+ float lastPercent = 0.0f;
+ boolean reportStatus = true;
+ while ((line = reader.readLine ()) != null) {
+ lineNo++;
+// println("Line: " + line);
+
+ if (reportStatus) {
+ float percent = ((float)lineNo / (float)countLines) * 100.0;
+ println("----" + percent + "% of the way through.");
+ lastPercent = percent;
+ }
+
+ if (line.toUpperCase().startsWith("G")) {
+ if (reportStatus) {
+ println(new StringBuilder().append(lineNo).append(" of ").append(countLines).append(": ").append(line).append(". Points: ").append(totalPoints).toString());
+ long free = Runtime.getRuntime().freeMemory();
+ long maximum = Runtime.getRuntime().maxMemory();
+ println(new StringBuilder().append("Free: ").append(free).append(", max: ").append(maximum).toString());
+ }
+
+ Map ins = null;
+ try {
+ ins = unpackGCodeInstruction(line);
+ }
+ catch (Exception e) {
+ println(e.toString());
+ continue;
+ }
+// println("Ins: " + ins);
+ Integer code = Math.round(ins.get("G"));
+
+ Float z = ins.get("Z");
+ if (z != null) {
+ gCodeZAxisChanges++;
+ if (gCodeZAxisChanges == 2) {
+ println("Assume second z axis change is to drop the pen to start drawing " + z);
+ gcodeZAxisDrawingHeight = z;
+ drawLine = true;
+ }
+ else if (gCodeZAxisChanges > 2) {
+ drawLine = isGCodeZAxisForDrawing(z);
+ }
+ else {
+ println("Assume first z axis change is to RAISE the pen " + z);
+ drawLine = false;
+ }
+ }
+
+ Float x = ins.get("X");
+ Float y = ins.get("Y");
+ if (x != null && y == null) {
+ // move x axis only, use y of last
+ RPoint[][] points = parent.getPointsInPaths();
+ RPoint rp = points[points.length-1][points[points.length-1].length-1];
+ y = rp.y;
+ }
+ else if (x == null && y != null) {
+ // move y axis only, use x of last
+ RPoint[][] points = parent.getPointsInPaths();
+ RPoint rp = points[points.length-1][points[points.length-1].length-1];
+ x = rp.x;
+ }
+
+ if (x != null && y != null) {
+ // move both x and y axis
+ if (drawLine) {
+ parent.addLineTo(x, y);
+ }
+ else {
+ parent.addMoveTo(x, y);
+ }
+ }
+ }
+ else {
+
+ }
+
+ if ((millis() - time) > 500) {
+ time = millis();
+ reportStatus = true;
+ }
+ else {
+ reportStatus = false;
+ }
+
+ if (lineNo == (countLines-1)) {
+ reportStatus = true;
+ }
+
+ }
+ }
+ catch (IOException e) {
+ println("IOExecption reading lines from the gcode file " + filename);
+ e.printStackTrace();
+ }
+ finally {
+ try {
+ reader.close();
+ }
+ catch (IOException e) {
+ println("IOException closing the gcode file " + filename);
+ e.printStackTrace();
+ }
+ }
+
+ RPoint[][] points = parent.getPointsInPaths();
+ totalPoints = 0;
+ if (points != null) {
+ for (int i = 0; i unpackGCodeInstruction(String line) throws Exception {
+ Map instruction = new HashMap(4);
+ try {
+ String[] splitted = line.trim().split(" ");
+ for (int i = 0; i < splitted.length; i++) {
+ // remove ; character
+ splitted[i] = splitted[i].replace(";", "");
+ String axis = splitted[i].substring(0, 1);
+ String sanitisedValue = splitted[i].substring(1);
+ sanitisedValue = sanitisedValue.replace(",", ".");
+ Float value = Float.parseFloat(sanitisedValue);
+ if ("X".equalsIgnoreCase(axis) || "Y".equalsIgnoreCase(axis) || "Z".equalsIgnoreCase(axis) || "G".equalsIgnoreCase(axis)) {
+ instruction.put(axis.toUpperCase(), value);
+ }
+ }
+// println("instruction: " + instruction);
+ if (instruction.isEmpty()) {
+ throw new Exception("Empty instruction");
+ }
+ }
+ catch (NumberFormatException nfe) {
+ println("Number format exception: " + nfe.getMessage());
+ }
+ catch (Exception e) {
+ println("e: " + e);
+ throw new Exception("Exception while reading the lines from a gcode file: " + line + ", " + e.getMessage());
+ }
+
+ return instruction;
+}
diff --git a/Machine.pde b/Machine.pde
index f5d6c37..df86936 100644
--- a/Machine.pde
+++ b/Machine.pde
@@ -186,9 +186,15 @@ class Machine
return mmInt;
}
+ public float inMMFloat(float steps)
+ {
+ double mm = steps / getStepsPerMM();
+ return (float) mm;
+ }
+
public PVector inMM (PVector steps)
{
- PVector mm = new PVector(inMM(steps.x), inMM(steps.y));
+ PVector mm = new PVector(inMMFloat(steps.x), inMMFloat(steps.y));
return mm;
}
@@ -322,8 +328,8 @@ class Machine
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)));
+ float calcX = (pow(getWidth(), 2.0) - pow(pgCoords.y, 2.0) + pow(pgCoords.x, 2.0)) / (getWidth()*2.0);
+ float calcY = sqrt(pow(pgCoords.x,2.0)-pow(calcX,2.0));
PVector vect = new PVector(calcX, calcY);
return vect;
}
diff --git a/Panel.pde b/Panel.pde
index bd3e281..d75408c 100644
--- a/Panel.pde
+++ b/Panel.pde
@@ -133,7 +133,6 @@ class Panel
{
for (Controller c : this.getControls())
{
-// println("Control: " + c.getName());
PVector pos = getControlPositions().get(c.getName());
float x = pos.x+getOutline().getLeft();
float y = pos.y+getOutline().getTop();
@@ -165,6 +164,13 @@ class Panel
{
locked = true;
}
+
+ // if there's no vector loaded, then hide vector controls
+ if (getControlsToLockIfVectorNotLoaded().contains(c.getName()) && vectorFilename == null)
+ {
+ locked = true;
+ }
+
if (c.getName().equals(MODE_LOAD_VECTOR_FILE))
{
@@ -211,6 +217,7 @@ class Panel
this.getOutline().setHeight(getMinimumHeight());
else
this.getOutline().setHeight(h);
+
setControlPositions(buildControlPositionsForPanel(this));
float left = 0.0;
diff --git a/controlsActions.pde b/controlsActions.pde
index 409c737..9be876f 100644
--- a/controlsActions.pde
+++ b/controlsActions.pde
@@ -323,6 +323,7 @@ void button_mode_loadImage()
getDisplayMachine().setImageFilename(null);
}
}
+
void button_mode_loadVectorFile()
{
if (getVectorShape() == null)
@@ -336,6 +337,7 @@ void button_mode_loadVectorFile()
vectorFilename = null;
}
}
+
void numberbox_mode_pixelBrightThreshold(float value)
{
pixelExtractBrightThreshold = (int) value;
@@ -730,17 +732,11 @@ void numberbox_mode_previewCordOffsetValue(int value)
previewQueue(true);
}
-void button_mode_cycleDensityPreviewStyle()
+void dropdown_mode_cycleDensityPreviewStyle(int index)
{
- Controller c = cp5.getController(MODE_CYCLE_DENSITY_PREVIEW_STYLE);
- c.setLabel(this.controlLabels.get(MODE_CYCLE_DENSITY_PREVIEW_STYLE) + ": " + densityPreviewStyle);
-
- if (densityPreviewStyle == DENSITY_PREVIEW_STYLE_COUNT) {
- densityPreviewStyle = 0;
- }
- else {
- densityPreviewStyle++;
- }
+ println("In dropdown_mode_cycleDensityPreviewStyle");
+ densityPreviewStyle = index;
+ println("Style: " + densityPreviewStyle);
}
void numberbox_mode_changeDensityPreviewPosterize(int value) {
@@ -760,20 +756,17 @@ void numberbox_mode_changePolygonizerLength(float value) {
setupPolygonizer();
}
-
-void button_mode_cyclePolygonizer()
-{
-
- // this is a bit silly for only two choices
- if (polygonizer == 1) {
- polygonizer = 0;
- }
- else {
- polygonizer++;
- }
+void numberbox_mode_changePolygonizerAdaptativeAngle(float value) {
+ println("numberbox_mode_changePolygonizerAdaptativeAngle");
+ polygonizerAdaptativeAngle = value;
+ setupPolygonizer();
+}
+
+
+void dropdown_mode_changePolygonizer(int value)
+{
+ polygonizer = value;
setupPolygonizer();
- Controller c = cp5.getController(MODE_CHANGE_POLYGONIZER);
- c.setLabel(this.controlLabels.get(MODE_CHANGE_POLYGONIZER) + ": " + polygonizer);
}
diff --git a/controlsSetup.pde b/controlsSetup.pde
index ec7e22a..163a633 100644
--- a/controlsSetup.pde
+++ b/controlsSetup.pde
@@ -83,6 +83,14 @@ Set getControlsToLockIfImageNotLoaded() {
return this.controlsToLockIfImageNotLoaded;
}
+Set getControlsToLockIfVectorNotLoaded() {
+ if (this.controlsToLockIfVectorNotLoaded == null)
+ {
+ this.controlsToLockIfVectorNotLoaded = buildControlsToLockIfVectorNotLoaded();
+ }
+ return this.controlsToLockIfVectorNotLoaded;
+}
+
void hideAllControls() {
for (String key : allControls.keySet())
{
@@ -227,9 +235,37 @@ Set buildControlsToLockIfImageNotLoaded()
result.add(MODE_CHANGE_SAMPLE_AREA);
result.add(MODE_SELECT_PICTUREFRAME);
+ result.add(MODE_CHANGE_PIXEL_SCALING);
+ result.add(MODE_CHOOSE_CHROMA_KEY_COLOUR);
+
return result;
}
+Set buildControlsToLockIfVectorNotLoaded()
+{
+ Set result = new HashSet();
+ result.add(MODE_CHANGE_MIN_VECTOR_LINE_LENGTH);
+ result.add(MODE_RESIZE_VECTOR);
+ result.add(MODE_MOVE_VECTOR);
+ result.add(MODE_CHANGE_POLYGONIZER_LENGTH);
+ result.add(MODE_CHANGE_POLYGONIZER);
+
+ return result;
+}
+
+CallbackListener toFront = new CallbackListener() {
+ public void controlEvent(CallbackEvent theEvent) {
+ theEvent.getController().bringToFront();
+ ((ScrollableList)theEvent.getController()).open();
+ }
+};
+
+CallbackListener close = new CallbackListener() {
+ public void controlEvent(CallbackEvent theEvent) {
+ ((ScrollableList)theEvent.getController()).close();
+ }
+};
+
Map buildAllControls()
{
@@ -243,6 +279,8 @@ Map buildAllControls()
{
Button b = cp5.addButton(controlName, 0, 100, 100, 100, 100);
b.setLabel(getControlLabels().get(controlName));
+ controlP5.Label l = b.getCaptionLabel();
+ l.getStyle().marginLeft = 4; //move to the right
b.hide();
map.put(controlName, b);
// println("Added button " + controlName);
@@ -283,11 +321,29 @@ Map buildAllControls()
map.put(controlName, n);
// println("Added numberbox " + controlName);
}
+ else if (controlName.startsWith("dropdown_"))
+ {
+ ScrollableList sl = cp5.addScrollableList(controlName, 100, 100, 100, 100);
+ sl.setBarHeight(20);
+ sl.setItemHeight(20);
+ sl.setLabel(getControlLabels().get(controlName));
+ sl.setType(ScrollableList.DROPDOWN);
+ sl.onEnter(toFront);
+ sl.onLeave(close);
+ sl.setHeight(100);
+ sl.hide();
+
+ controlP5.Label l = sl.getCaptionLabel();
+ map.put(controlName, sl);
+ println("Added dropdown " + controlName);
+ }
}
initialiseButtonValues(map);
initialiseToggleValues(map);
initialiseNumberboxValues(map);
+ initialiseDropdownContents(map);
+ initialiseDropdownValues(map);
return map;
}
@@ -298,12 +354,7 @@ Map initialiseButtonValues(Map map)
if (key.startsWith("button_"))
{
Button n = (Button) map.get(key);
-
- if (MODE_CYCLE_DENSITY_PREVIEW_STYLE.equals(key)) {
- n.setValue(densityPreviewStyle);
- n.setLabel(this.controlLabels.get(MODE_CYCLE_DENSITY_PREVIEW_STYLE) + ": " + densityPreviewStyle);
- }
- else if (MODE_CHANGE_POLYGONIZER.equals(key)) {
+ if (MODE_CHANGE_POLYGONIZER.equals(key)) {
n.setValue(polygonizer);
n.setLabel(this.controlLabels.get(MODE_CHANGE_POLYGONIZER) + ": " + polygonizer);
}
@@ -536,8 +587,8 @@ Map initialiseNumberboxValues(Map map)
}
else if (MODE_ADJUST_PREVIEW_CORD_OFFSET.equals(key))
{
- n.setDecimalPrecision(0);
- n.setValue(0);
+ n.setDecimalPrecision(2);
+ n.setValue(0.0);
n.setMultiplier(0.5);
}
else if (MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE.equals(key))
@@ -553,7 +604,13 @@ Map initialiseNumberboxValues(Map map)
n.setMin(1.0);
n.setDecimalPrecision(1);
n.setMultiplier(0.1);
-
+ }
+ else if (MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE.equals(key)) {
+ n.setValue(polygonizerAdaptativeAngle);
+ n.setMin(0.0);
+ n.setMax(1.57079632679);
+ n.setDecimalPrecision(2);
+ n.setMultiplier(0.01);
}
}
}
@@ -614,6 +671,51 @@ Map initialiseToggleValues(Map map)
return map;
}
+Map initialiseDropdownContents(Map map)
+{
+ println("Init dropdown contents");
+ for (String key : map.keySet())
+ {
+ if (MODE_CYCLE_DENSITY_PREVIEW_STYLE.equals(key))
+ {
+ println("Adding " + key);
+ ScrollableList sl = (ScrollableList) map.get(key);
+ sl.setItems(densityPreviewStyles);
+ }
+ if (MODE_CHANGE_POLYGONIZER.equals(key))
+ {
+ println("Adding " + key);
+ ScrollableList sl = (ScrollableList) map.get(key);
+ sl.setItems(polygonizerStyles);
+ }
+ }
+ return map;
+}
+
+Map initialiseDropdownValues(Map map)
+{
+ println("Init dropdown values");
+ for (String key : map.keySet())
+ {
+ if (MODE_CYCLE_DENSITY_PREVIEW_STYLE.equals(key))
+ {
+ println("Adding " + key);
+ ScrollableList sl = (ScrollableList) map.get(key);
+ sl.setValue(densityPreviewStyle);
+ sl.close();
+ }
+ else if (MODE_CHANGE_POLYGONIZER.equals(key))
+ {
+ println("Adding " + key);
+ ScrollableList sl = (ScrollableList) map.get(key);
+ sl.setValue(polygonizer);
+ sl.close();
+ }
+ }
+ return map;
+}
+
+
String getControlLabel(String butName)
{
if (controlLabels.containsKey(butName))
@@ -642,6 +744,18 @@ Map buildControlPositionsForPanel(Panel panel)
col++;
}
}
+ else if (controller.getName().startsWith("dropdown_"))
+ {
+ PVector p = new PVector(col*(DEFAULT_CONTROL_SIZE.x+CONTROL_SPACING.x), row*(DEFAULT_CONTROL_SIZE.y+CONTROL_SPACING.y));
+ println(controller);
+ map.put(controller.getName(), 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));
@@ -671,6 +785,11 @@ Map buildControlSizesForPanel(Panel panel)
PVector s = new PVector(DEFAULT_CONTROL_SIZE.y, DEFAULT_CONTROL_SIZE.y);
map.put(controller.getName(), s);
}
+ else if (controller.getName().startsWith("dropdown_"))
+ {
+ PVector s = new PVector(DEFAULT_CONTROL_SIZE.x, DEFAULT_CONTROL_SIZE.y * 4);
+ map.put(controller.getName(), s);
+ }
else
{
PVector s = new PVector(DEFAULT_CONTROL_SIZE.x, DEFAULT_CONTROL_SIZE.y);
@@ -732,8 +851,6 @@ List getControlNamesForInputPanel()
controlNames.add(MODE_CHANGE_SAMPLE_AREA);
controlNames.add(MODE_CHOOSE_CHROMA_KEY_COLOUR);
controlNames.add(MODE_CHANGE_PIXEL_SCALING);
- controlNames.add(MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE);
- controlNames.add(MODE_CYCLE_DENSITY_PREVIEW_STYLE);
controlNames.add(MODE_RENDER_PIXEL_DIALOG);
// controlNames.add(MODE_DRAW_GRID);
@@ -744,20 +861,23 @@ List getControlNamesForInputPanel()
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_VECTOR_PATH_LENGTH_HIGHPASS_CUTOFF);
- controlNames.add(MODE_RENDER_VECTORS);
-
controlNames.add(MODE_ADJUST_PREVIEW_CORD_OFFSET);
+
+ controlNames.add(MODE_RENDER_VECTORS);
+ controlNames.add(MODE_CHANGE_MIN_VECTOR_LINE_LENGTH);
controlNames.add(MODE_CHANGE_POLYGONIZER);
controlNames.add(MODE_CHANGE_POLYGONIZER_LENGTH);
+// controlNames.add(MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE);
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);
+
+ controlNames.add(MODE_SHOW_DENSITY_PREVIEW);
controlNames.add(MODE_PREVIEW_PIXEL_DENSITY_RANGE);
+ controlNames.add(MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE);
+ controlNames.add(MODE_CYCLE_DENSITY_PREVIEW_STYLE);
return controlNames;
@@ -1025,13 +1145,14 @@ Map buildControlLabels()
result.put(MODE_SEND_BUTTON_DEACTIVATE, "Deactivate button");
result.put(MODE_ADJUST_PREVIEW_CORD_OFFSET, "Cord offset");
- result.put(MODE_CYCLE_DENSITY_PREVIEW_STYLE, "Cycle preview style");
+ result.put(MODE_CYCLE_DENSITY_PREVIEW_STYLE, "Density preview style");
result.put(MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE, "Pixel posterize");
result.put(MODE_PREVIEW_PIXEL_DENSITY_RANGE, "Show density range");
- result.put(MODE_CHANGE_POLYGONIZER, "Cycle polygonizer");
+ result.put(MODE_CHANGE_POLYGONIZER, "Polygonizer style");
result.put(MODE_CHANGE_POLYGONIZER_LENGTH, "Polygonizer length");
+ result.put(MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE, "Polygonizer angle");
return result;
@@ -1184,6 +1305,7 @@ Set buildControlNames()
result.add(MODE_CHANGE_POLYGONIZER_LENGTH);
result.add(MODE_CHANGE_POLYGONIZER);
+ result.add(MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE);
return result;
}
diff --git a/drawing.pde b/drawing.pde
index e7b7059..07197a3 100644
--- a/drawing.pde
+++ b/drawing.pde
@@ -912,12 +912,12 @@ List filterPointsLowPass(RPoint[] points, long filterParam, float scali
{
p = scaled.get(j);
// and even then, only bother drawing if it's a move of over "x" steps
- int diffx = int(p.x) - int(result.get(result.size()-1).x);
- int diffy = int(p.y) - int(result.get(result.size()-1).y);
+ int diffx = abs(int(p.x) - int(result.get(result.size()-1).x));
+ int diffy = abs(int(p.y) - int(result.get(result.size()-1).y));
- if (abs(diffx) > filterParam || abs(diffy) > filterParam)
+ if (diffx > filterParam || diffy > filterParam)
{
- println(j + ". Adding point " + p + ", last: " + result.get(result.size()-1));
+ println(j + ". Adding point " + p + " because diffx (" + diffx + ") or diffy (" + diffy + ") is > " + filterParam + ", last: " + result.get(result.size()-1));
result.add(p);
}
}
@@ -967,4 +967,4 @@ void sendStopSwirling()
void sendDrawRandomSprite(String spriteFilename)
{
addToCommandQueue(CMD_DRAW_RANDOM_SPRITE+","+spriteFilename+",100,500,END");
-}
\ No newline at end of file
+}
diff --git a/polargraphcontroller.pde b/polargraphcontroller.pde
index 7c07e84..cc75a80 100644
--- a/polargraphcontroller.pde
+++ b/polargraphcontroller.pde
@@ -1,7 +1,7 @@
/**
Polargraph controller
- Copyright Sandy Noble 2015.
-
+ Copyright Sandy Noble 2018.
+
This file is part of Polargraph Controller.
Polargraph Controller is free software: you can redistribute it and/or modify
@@ -35,11 +35,8 @@ import diewald_CV_kit.utility.*;
import diewald_CV_kit.blobdetection.*;
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.*;
@@ -58,7 +55,7 @@ import java.lang.reflect.Method;
int majorVersionNo = 2;
int minorVersionNo = 5;
-int buildNo = 1;
+int buildNo = 2;
String programTitle = "Polargraph Controller v" + majorVersionNo + "." + minorVersionNo + " build " + buildNo;
ControlP5 cp5;
@@ -340,16 +337,16 @@ static final String MODE_SEND_BUTTON_DEACTIVATE = "button_mode_sendButtonDeactiv
static final String MODE_ADJUST_PREVIEW_CORD_OFFSET = "numberbox_mode_previewCordOffsetValue";
-static final String MODE_CYCLE_DENSITY_PREVIEW_STYLE = "button_mode_cycleDensityPreviewStyle";
+static final String MODE_CYCLE_DENSITY_PREVIEW_STYLE = "dropdown_mode_cycleDensityPreviewStyle";
static final String MODE_CHANGE_DENSITY_PREVIEW_POSTERIZE = "numberbox_mode_changeDensityPreviewPosterize";
static final String MODE_PREVIEW_PIXEL_DENSITY_RANGE = "minitoggle_mode_previewPixelDensityRange";
-static final String MODE_CHANGE_POLYGONIZER = "button_mode_cyclePolygonizer";
+static final String MODE_CHANGE_POLYGONIZER = "dropdown_mode_changePolygonizer";
static final String MODE_CHANGE_POLYGONIZER_LENGTH = "numberbox_mode_changePolygonizerLength";
+static final String MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE = "numberbox_mode_changePolygonizerAdaptativeAngle";
-
-
+List polygonizerStyles = Arrays.asList("ADAPTATIVE", "UNIFORMLENGTH");
PVector statusTextPosition = new PVector(300.0, 12.0);
@@ -386,6 +383,8 @@ boolean displayingDensityPreview = false;
boolean displayingGuides = true;
+
+List densityPreviewStyles = Arrays.asList("Round", "Diamond", "Native Simple", "Native Arc", "Round size", "Native size");
static final int DENSITY_PREVIEW_STYLE_COUNT = 6;
static final int DENSITY_PREVIEW_ROUND = 0;
@@ -496,6 +495,7 @@ float vectorScaling = 100;
PVector vectorPosition = new PVector(0.0,0.0);
int minimumVectorLineLength = 2;
public static final int VECTOR_FILTER_LOW_PASS = 0;
+public Set controlsToLockIfVectorNotLoaded = null;
String storeFilename = "comm.txt";
@@ -544,6 +544,7 @@ boolean rescaleDisplayMachine = true;
// Polygonization. It's a geomerative thing.
int polygonizer = 0;
float polygonizerLength = 0.0;
+float polygonizerAdaptativeAngle = 0.0F;
void setup()
{
@@ -1081,411 +1082,6 @@ void showGroupBox()
}
-void loadImageWithFileChooser()
-{
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run() {
- JFileChooser fc = new JFileChooser();
- if (lastImageDirectory != null) fc.setCurrentDirectory(lastImageDirectory);
- fc.setFileFilter(new ImageFileFilter());
- fc.setDialogTitle("Choose an image file...");
-
- int returned = fc.showOpenDialog(frame);
-
- lastImageDirectory = fc.getCurrentDirectory();
-
- if (returned == JFileChooser.APPROVE_OPTION)
- {
- File file = fc.getSelectedFile();
- // see if it's an image
- PImage img = loadImage(file.getPath());
- if (img != null)
- {
- img = null;
- getDisplayMachine().loadNewImageFromFilename(file.getPath());
- if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified())
- {
- getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea);
- }
- }
- }
- }
- });
-}
-
-class ImageFileFilter extends javax.swing.filechooser.FileFilter
-{
- public boolean accept(File file) {
- String filename = file.getName();
- filename.toLowerCase();
- if (file.isDirectory() || filename.endsWith(".png") || filename.endsWith(".jpg") || filename.endsWith(".jpeg"))
- return true;
- else
- return false;
- }
- public String getDescription() {
- return "Image files (PNG or JPG)";
- }
-}
-
-void loadVectorWithFileChooser()
-{
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run() {
- JFileChooser fc = new JFileChooser();
- if (lastImageDirectory != null)
- {
- fc.setCurrentDirectory(lastImageDirectory);
- }
-
- fc.setFileFilter(new VectorFileFilter());
- fc.setDialogTitle("Choose a vector file...");
- int returned = fc.showOpenDialog(frame);
- lastImageDirectory = fc.getCurrentDirectory();
-
- if (returned == JFileChooser.APPROVE_OPTION)
- {
- File file = fc.getSelectedFile();
- if (file.exists())
- {
- RShape shape = loadShapeFromFile(file.getPath());
- if (shape != null)
- {
- setVectorFilename(file.getPath());
- setVectorShape(shape);
- }
- else
- {
- println("File not found (" + file.getPath() + ")");
- }
- }
- }
- }
- }
- );
-}
-
-class VectorFileFilter extends javax.swing.filechooser.FileFilter
-{
- public boolean accept(File file) {
- String filename = file.getName();
- filename.toLowerCase();
- if (file.isDirectory() || filename.endsWith(".svg") || isGCodeExtension(filename))
- return true;
- else
- return false;
- }
- public String getDescription() {
- return "Vector graphic files (SVG, GCode)";
- }
-}
-
-void loadNewPropertiesFilenameWithFileChooser()
-{
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- JFileChooser fc = new JFileChooser();
- if (lastPropertiesDirectory != null) fc.setCurrentDirectory(lastPropertiesDirectory);
- fc.setFileFilter(new PropertiesFileFilter());
-
- fc.setDialogTitle("Choose a config file...");
-
- int returned = fc.showOpenDialog(frame);
-
- lastPropertiesDirectory = fc.getCurrentDirectory();
-
- if (returned == JFileChooser.APPROVE_OPTION)
- {
- File file = fc.getSelectedFile();
- if (file.exists())
- {
- println("New properties file exists.");
- newPropertiesFilename = file.toString();
- println("new propertiesFilename: "+ newPropertiesFilename);
- propertiesFilename = newPropertiesFilename;
- // clear old properties.
- props = null;
- loadFromPropertiesFile();
-
- // set values of number spinners etc
- updateNumberboxValues();
- }
- }
- }
- });
-}
-
-class PropertiesFileFilter extends javax.swing.filechooser.FileFilter
-{
- public boolean accept(File file) {
- String filename = file.getName();
- filename.toLowerCase();
- if (file.isDirectory() || filename.endsWith(".properties.txt"))
- return true;
- else
- return false;
- }
- public String getDescription() {
- return "Properties files (*.properties.txt)";
- }
-}
-
-void saveNewPropertiesFileWithFileChooser()
-{
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- JFileChooser fc = new JFileChooser();
- if (lastPropertiesDirectory != null) fc.setCurrentDirectory(lastPropertiesDirectory);
- fc.setFileFilter(new PropertiesFileFilter());
-
- fc.setDialogTitle("Enter a config file name...");
-
- int returned = fc.showSaveDialog(frame);
- if (returned == JFileChooser.APPROVE_OPTION)
- {
- File file = fc.getSelectedFile();
- newPropertiesFilename = file.toString();
- newPropertiesFilename.toLowerCase();
- if (!newPropertiesFilename.endsWith(".properties.txt"))
- newPropertiesFilename+=".properties.txt";
-
- println("new propertiesFilename: "+ newPropertiesFilename);
- propertiesFilename = newPropertiesFilename;
- savePropertiesFile();
- // clear old properties.
- props = null;
- loadFromPropertiesFile();
- }
- }
- });
-}
-
-
-
-RShape loadShapeFromFile(String filename) {
- RShape sh = null;
- if (filename.toLowerCase().endsWith(".svg")) {
- sh = RG.loadShape(filename);
- }
- else if (isGCodeExtension(filename)) {
- sh = loadShapeFromGCodeFile(filename);
- }
- return sh;
-}
-
-
-boolean isGCodeExtension(String filename) {
- return (filename.toLowerCase().endsWith(".gcode") || filename.toLowerCase().endsWith(".g") || filename.toLowerCase().endsWith(".ngc") || filename.toLowerCase().endsWith(".txt"));
-}
-
-
-int countLines(String filename) throws IOException {
- InputStream is = new BufferedInputStream(new FileInputStream(filename));
- try {
- byte[] c = new byte[1024];
- int count = 0;
- int readChars = 0;
- boolean empty = true;
- while ((readChars = is.read(c)) != -1) {
- empty = false;
- for (int i = 0; i < readChars; ++i) {
- if (c[i] == '\n') {
- ++count;
- }
- }
- }
- return (count == 0 && !empty) ? 1 : count+1;
- } finally {
- is.close();
- }
-}
-
-RShape loadShapeFromGCodeFile(String filename) {
- noLoop();
- RShape parent = null;
- BufferedReader reader = null;
- long totalPoints = 0;
- long time = millis();
- long countLines = 0;
-
- try {
- countLines = countLines(filename);
- println("" + countLines + " lines found.");
- if (countLines < 1) {
- throw new IOException("No lines found in GCode file.");
- }
- reader = createReader(filename);
- parent = new RShape();
- String line;
- boolean drawLine = false;
- int gCodeZAxisChanges = 0;
-
- long lineNo = 0;
- float lastPercent = 0.0f;
- boolean reportStatus = true;
- while ((line = reader.readLine ()) != null) {
- lineNo++;
-// println("Line: " + line);
-
- if (reportStatus) {
- float percent = ((float)lineNo / (float)countLines) * 100.0;
- println("----" + percent + "% of the way through.");
- lastPercent = percent;
- }
-
- if (line.toUpperCase().startsWith("G")) {
- if (reportStatus) {
- println(new StringBuilder().append(lineNo).append(" of ").append(countLines).append(": ").append(line).append(". Points: ").append(totalPoints).toString());
- long free = Runtime.getRuntime().freeMemory();
- long maximum = Runtime.getRuntime().maxMemory();
- println(new StringBuilder().append("Free: ").append(free).append(", max: ").append(maximum).toString());
- }
-
- Map ins = null;
- try {
- ins = unpackGCodeInstruction(line);
- }
- catch (Exception e) {
- println(e.toString());
- continue;
- }
-// println("Ins: " + ins);
- Integer code = Math.round(ins.get("G"));
-
- Float z = ins.get("Z");
- if (z != null) {
- gCodeZAxisChanges++;
- if (gCodeZAxisChanges == 2) {
- println("Assume second z axis change is to drop the pen to start drawing " + z);
- gcodeZAxisDrawingHeight = z;
- drawLine = true;
- }
- else if (gCodeZAxisChanges > 2) {
- drawLine = isGCodeZAxisForDrawing(z);
- }
- else {
- println("Assume first z axis change is to RAISE the pen " + z);
- drawLine = false;
- }
- }
-
- Float x = ins.get("X");
- Float y = ins.get("Y");
- if (x != null && y == null) {
- // move x axis only, use y of last
- RPoint[][] points = parent.getPointsInPaths();
- RPoint rp = points[points.length-1][points[points.length-1].length-1];
- y = rp.y;
- }
- else if (x == null && y != null) {
- // move y axis only, use x of last
- RPoint[][] points = parent.getPointsInPaths();
- RPoint rp = points[points.length-1][points[points.length-1].length-1];
- x = rp.x;
- }
-
- if (x != null && y != null) {
- // move both x and y axis
- if (drawLine) {
- parent.addLineTo(x, y);
- }
- else {
- parent.addMoveTo(x, y);
- }
- }
- }
- else {
-
- }
-
- if ((millis() - time) > 500) {
- time = millis();
- reportStatus = true;
- }
- else {
- reportStatus = false;
- }
-
- if (lineNo == (countLines-1)) {
- reportStatus = true;
- }
-
- }
- }
- catch (IOException e) {
- println("IOExecption reading lines from the gcode file " + filename);
- e.printStackTrace();
- }
- finally {
- try {
- reader.close();
- }
- catch (IOException e) {
- println("IOException closing the gcode file " + filename);
- e.printStackTrace();
- }
- }
-
- RPoint[][] points = parent.getPointsInPaths();
- totalPoints = 0;
- if (points != null) {
- for (int i = 0; i unpackGCodeInstruction(String line) throws Exception {
- Map instruction = new HashMap(4);
- try {
- String[] splitted = line.trim().split(" ");
- for (int i = 0; i < splitted.length; i++) {
- // remove ; character
- splitted[i] = splitted[i].replace(";", "");
- String axis = splitted[i].substring(0, 1);
- String sanitisedValue = splitted[i].substring(1);
- sanitisedValue = sanitisedValue.replace(",", ".");
- Float value = Float.parseFloat(sanitisedValue);
- if ("X".equalsIgnoreCase(axis) || "Y".equalsIgnoreCase(axis) || "Z".equalsIgnoreCase(axis) || "G".equalsIgnoreCase(axis)) {
- instruction.put(axis.toUpperCase(), value);
- }
- }
-// println("instruction: " + instruction);
- if (instruction.isEmpty()) {
- throw new Exception("Empty instruction");
- }
- }
- catch (NumberFormatException nfe) {
- println("Number format exception: " + nfe.getMessage());
- }
- catch (Exception e) {
- println("e: " + e);
- throw new Exception("Exception while reading the lines from a gcode file: " + line + ", " + e.getMessage());
- }
-
- return instruction;
-}
void setPictureFrameDimensionsToBox()
@@ -1799,7 +1395,7 @@ void mouseClicked()
if (getDisplayMachine().pixelsCanBeExtracted() && isBoxSpecified())
getDisplayMachine().extractPixelsFromArea(getBoxVector1(), getBoxVectorSize(), getGridSize(), sampleArea);
}
- else if (currentMode.equals(MODE_MOVE_VECTOR))
+ else if (currentMode.equals(MODE_MOVE_VECTOR) && vectorShape != null)
{
// offset mouse vector so it grabs the centre of the shape
PVector centroid = new PVector(getVectorShape().width/2, getVectorShape().height/2);
@@ -1934,23 +1530,23 @@ void leftButtonMachineClick()
void mouseWheel(int delta)
{
noLoop();
- // get the mouse position on the machine, before changing the machine scaling
- PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector());
- changeMachineScaling(delta);
- // now work out what the machine position needs to be to line the pos up with mousevector again
- PVector scaledPos = getDisplayMachine().scaleToDisplayMachine(getMouseVector());
-// println("original pos: " + pos);
-// println("scaled pos: " + scaledPos);
+ if (mouseOverMachine()) {
+ // get the mouse position on the machine, before changing the machine scaling
+ PVector pos = getDisplayMachine().scaleToDisplayMachine(getMouseVector());
+ changeMachineScaling(delta);
+
+ // now work out what the machine position needs to be to line the pos up with mousevector again
+ PVector scaledPos = getDisplayMachine().scaleToDisplayMachine(getMouseVector());
+ PVector change = PVector.sub(scaledPos, pos);
- PVector change = PVector.sub(scaledPos, pos);
-// println("change: " + change);
-
- // and adjust for the new scaling factor
- change.mult(machineScaling);
+ // and adjust for the new scaling factor
+ change.mult(machineScaling);
+
+ // finally update the machine offset (position)
+ getDisplayMachine().getOffset().add(change);
+ }
- // finally update the machine offset (position)
- getDisplayMachine().getOffset().add(change);
loop();
}
@@ -3082,6 +2678,9 @@ void loadFromPropertiesFile()
// println("home point loaded: " + homePointCartesian + ", " + getHomePoint());
// Geomerative stuff
+ println("RG.ADAPTATIVE is " + RG.ADAPTATIVE);
+ println("RG.UNIFORMLENGTH is " + RG.UNIFORMLENGTH);
+ println("RG.UNIFORMSTEP is " + RG.UNIFORMSTEP);
polygonizer = getIntProperty("controller.geomerative.polygonizer", RG.ADAPTATIVE);
polygonizerLength = getFloatProperty("controller.geomerative.polygonizerLength", 1.0);
setupPolygonizer();
@@ -3342,12 +2941,15 @@ Integer getBaudRate()
void setupPolygonizer() {
RG.setPolygonizer(polygonizer); // http://www.polargraph.co.uk/forum/polargraphs-group2/troubleshooting-forum5/svg-differences-between-polargraphcontroller-2-1-1-2-3-0-thread523.0
switch(polygonizer) {
- case 1: RG.setPolygonizerLength(polygonizerLength); break;
- }
- println("Polygonizer: " + polygonizer);
- println("PolygonizerLength: " + polygonizerLength);
-
+// case 0:
+// RG.setPolygonizerAngle(polygonizerAdaptativeAngle);
+// break;
+ case 1:
+ RG.setPolygonizerLength(polygonizerLength);
+ break;
+ }
}
+
void initLogging()
{
try
diff --git a/queue.pde b/queue.pde
new file mode 100644
index 0000000..9bfcf6b
--- /dev/null
+++ b/queue.pde
@@ -0,0 +1,7 @@
+/**
+Tools for dealing with a full command queue.
+
+Optimise queue.
+*/
+
+