v2.5.2 - UI changes to make cycling er, work properly. Changed the .toMM() method of the Machine class to be floaty. This helps rendering a bit.

This commit is contained in:
Sandy Noble 2018-05-27 23:01:08 +01:00
parent f2b1df7405
commit 5e7e010a79
8 changed files with 658 additions and 485 deletions

436
FileLoading.pde Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<String, Float> 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<points.length; i++) {
if (points[i] != null) {
for (int j = 0; j<points[i].length; j++) {
totalPoints++;
}
}
}
}
String conclusionMessage = "Imported " + totalPoints + " points from " + countLines + " lines of code in the file.";
println(conclusionMessage);
javax.swing.JOptionPane.showMessageDialog(null, conclusionMessage);
loop();
return parent;
}
Boolean isGCodeZAxisForDrawing(float z) {
return gcodeZAxisDrawingHeight.compareTo(z) == 0;
}
Map<String, Float> unpackGCodeInstruction(String line) throws Exception {
Map<String, Float> instruction = new HashMap<String, Float>(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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -83,6 +83,14 @@ Set<String> getControlsToLockIfImageNotLoaded() {
return this.controlsToLockIfImageNotLoaded;
}
Set<String> getControlsToLockIfVectorNotLoaded() {
if (this.controlsToLockIfVectorNotLoaded == null)
{
this.controlsToLockIfVectorNotLoaded = buildControlsToLockIfVectorNotLoaded();
}
return this.controlsToLockIfVectorNotLoaded;
}
void hideAllControls() {
for (String key : allControls.keySet())
{
@ -227,9 +235,37 @@ Set<String> 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<String> buildControlsToLockIfVectorNotLoaded()
{
Set<String> result = new HashSet<String>();
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<String, Controller> buildAllControls()
{
@ -243,6 +279,8 @@ Map<String, Controller> 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<String, Controller> 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<String, Controller> initialiseButtonValues(Map<String, Controller> 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<String, Controller> initialiseNumberboxValues(Map<String, Controller> 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<String, Controller> initialiseNumberboxValues(Map<String, Controller> 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<String, Controller> initialiseToggleValues(Map<String, Controller> map)
return map;
}
Map<String, Controller> initialiseDropdownContents(Map<String, Controller> 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<String, Controller> initialiseDropdownValues(Map<String, Controller> 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<String, PVector> 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<String, PVector> 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<String> 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<String> 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<String, String> 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<String> buildControlNames()
result.add(MODE_CHANGE_POLYGONIZER_LENGTH);
result.add(MODE_CHANGE_POLYGONIZER);
result.add(MODE_CHANGE_POLYGONIZER_ADAPTATIVE_ANGLE);
return result;
}

View File

@ -912,12 +912,12 @@ List<PVector> 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");
}
}

View File

@ -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<String> polygonizerStyles = Arrays.asList("ADAPTATIVE", "UNIFORMLENGTH");
PVector statusTextPosition = new PVector(300.0, 12.0);
@ -386,6 +383,8 @@ boolean displayingDensityPreview = false;
boolean displayingGuides = true;
List<String> 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<String> 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<String, Float> 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<points.length; i++) {
if (points[i] != null) {
for (int j = 0; j<points[i].length; j++) {
totalPoints++;
}
}
}
}
String conclusionMessage = "Imported " + totalPoints + " points from " + countLines + " lines of code in the file.";
println(conclusionMessage);
javax.swing.JOptionPane.showMessageDialog(null, conclusionMessage);
loop();
return parent;
}
Boolean isGCodeZAxisForDrawing(float z) {
return gcodeZAxisDrawingHeight.compareTo(z) == 0;
}
Map<String, Float> unpackGCodeInstruction(String line) throws Exception {
Map<String, Float> instruction = new HashMap<String, Float>(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

7
queue.pde Normal file
View File

@ -0,0 +1,7 @@
/**
Tools for dealing with a full command queue.
Optimise queue.
*/