Skip to content

Commit

Permalink
Add possibility to export the autoleveled gcode (#2438)
Browse files Browse the repository at this point in the history
  • Loading branch information
breiler committed Jan 23, 2024
1 parent 262e17c commit 061670e
Show file tree
Hide file tree
Showing 12 changed files with 473 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.utils;

import com.willwinder.universalgcodesender.types.GcodeCommand;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

/**
* Writes the preprocessed gcode to the given file.
*
* @author Joacim Breiler
*/
public class SimpleGcodeStreamWriter implements IGcodeWriter {
private final PrintWriter fileWriter;

public SimpleGcodeStreamWriter(File f) throws FileNotFoundException {
try {
fileWriter = new PrintWriter(f, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new FileNotFoundException("Could not use UTF-8 to output gcode stream file");
}
}

@Override
public String getCanonicalPath() {
return "";
}

@Override
public void addLine(GcodeCommand command) {
addLine(command.getOriginalCommandString(), command.getCommandString(), command.getComment(), command.getCommandNumber());
}

@Override
public void addLine(String original, String processed, String comment, int commandNumber) {
fileWriter.append(processed);
fileWriter.append("\n");
}

@Override
public void close() throws IOException {
fileWriter.close();
}
}
2 changes: 2 additions & 0 deletions ugs-core/src/resources/MessagesBundle_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ autoleveler.option.offset-y = Probe Y offset
autoleveler.option.offset-z = Probe Z offset
autoleveler.probe-failed = Probe failed
autoleveler.panel.clear = Clear scan
autoleveler.panel.export = Export gcode
autoleveler.panel.export.tooltip = Exports the autoleveled gcode
experimental.feature = This is an experimental feature. Please use caution and report any bugs you find on GitHub.
# Window title for platform GUI
platform-title = Universal Gcode Sender
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2023 Will Winder
Copyright 2023-2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
Expand All @@ -18,11 +18,8 @@ This file is part of Universal Gcode Sender (UGS).
*/
package com.willwinder.ugs.platform.surfacescanner;

import com.willwinder.universalgcodesender.gcode.GcodePreprocessorUtils;
import com.willwinder.universalgcodesender.gcode.processors.ArcExpander;
import static com.willwinder.ugs.platform.surfacescanner.Utils.createCommandProcessor;
import com.willwinder.universalgcodesender.gcode.processors.CommandProcessorList;
import com.willwinder.universalgcodesender.gcode.processors.LineSplitter;
import com.willwinder.universalgcodesender.gcode.processors.MeshLeveler;
import com.willwinder.universalgcodesender.model.BackendAPI;
import com.willwinder.universalgcodesender.utils.AutoLevelSettings;
import com.willwinder.universalgcodesender.utils.GUIHelpers;
Expand Down Expand Up @@ -51,19 +48,7 @@ public void update() {
return;
}

commandProcessorList = new CommandProcessorList();

// Step 1: Convert arcs to line segments.
commandProcessorList.add(new ArcExpander(true, autoLevelSettings.getAutoLevelArcSliceLength(), GcodePreprocessorUtils.getDecimalFormatter()));

// Step 2: Line splitter. No line should be longer than some fraction of "resolution"
commandProcessorList.add(new LineSplitter(autoLevelSettings.getStepResolution() / 4));

// Step 3: Adjust Z heights codes based on mesh offsets.
commandProcessorList.add(
new MeshLeveler(autoLevelSettings.getZSurface(),
surfaceScanner.getProbePositionGrid()));

commandProcessorList = createCommandProcessor(autoLevelSettings, surfaceScanner);
backend.applyCommandProcessor(commandProcessorList);
} catch (Exception ex) {
GUIHelpers.displayErrorDialog(ex.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ This file is part of Universal Gcode Sender (UGS).
*/
package com.willwinder.ugs.platform.surfacescanner;

import com.willwinder.universalgcodesender.gcode.GcodePreprocessorUtils;
import com.willwinder.universalgcodesender.gcode.processors.ArcExpander;
import com.willwinder.universalgcodesender.gcode.processors.CommandProcessorList;
import com.willwinder.universalgcodesender.gcode.processors.LineSplitter;
import com.willwinder.universalgcodesender.gcode.processors.MeshLeveler;
import com.willwinder.universalgcodesender.i18n.Localization;
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UnitUtils;
import com.willwinder.universalgcodesender.utils.AutoLevelSettings;

import javax.swing.*;
import java.awt.*;
Expand Down Expand Up @@ -77,4 +83,20 @@ public static Position getRoundPosition(Position position) {
double z = Math.round(position.getZ() * 1000d) / 1000d;
return new Position(x, y, z, position.getUnits());
}

public static CommandProcessorList createCommandProcessor(AutoLevelSettings autoLevelSettings, SurfaceScanner surfaceScanner) {
CommandProcessorList result = new CommandProcessorList();

// Step 1: Convert arcs to line segments.
result.add(new ArcExpander(true, autoLevelSettings.getAutoLevelArcSliceLength(), GcodePreprocessorUtils.getDecimalFormatter()));

// Step 2: Line splitter. No line should be longer than some fraction of "resolution"
result.add(new LineSplitter(autoLevelSettings.getStepResolution() / 4));

// Step 3: Adjust Z heights codes based on mesh offsets.
result.add(
new MeshLeveler(autoLevelSettings.getZSurface(),
surfaceScanner.getProbePositionGrid()));
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.ugs.platform.surfacescanner.actions;

import com.willwinder.ugs.nbp.lib.lookup.CentralLookup;
import com.willwinder.ugs.platform.surfacescanner.SurfaceScanner;
import static com.willwinder.ugs.platform.surfacescanner.Utils.createCommandProcessor;
import static com.willwinder.ugs.platform.surfacescanner.Utils.fileChooser;
import com.willwinder.universalgcodesender.gcode.GcodeParser;
import com.willwinder.universalgcodesender.gcode.processors.CommandProcessorList;
import com.willwinder.universalgcodesender.gcode.util.GcodeParserException;
import com.willwinder.universalgcodesender.gcode.util.GcodeParserUtils;
import com.willwinder.universalgcodesender.i18n.Localization;
import com.willwinder.universalgcodesender.listeners.UGSEventListener;
import com.willwinder.universalgcodesender.model.BackendAPI;
import com.willwinder.universalgcodesender.model.UGSEvent;
import com.willwinder.universalgcodesender.model.events.FileStateEvent;
import com.willwinder.universalgcodesender.uielements.components.GcodeFileTypeFilter;
import com.willwinder.universalgcodesender.utils.GUIHelpers;
import com.willwinder.universalgcodesender.utils.SimpleGcodeStreamWriter;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.ImageUtilities;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* An action that will export the currently loaded gcode with the applied surface scan
*
* @author Joacim Breiler
*/
public class ExportGcodeAction extends AbstractAction implements UGSEventListener {
public static final String ICON_BASE = "com/willwinder/ugs/platform/surfacescanner/icons/export.svg";
private static final Logger LOGGER = Logger.getLogger(ExportGcodeAction.class.getSimpleName());
private final transient SurfaceScanner surfaceScanner;
private final transient BackendAPI backend;

public ExportGcodeAction(SurfaceScanner surfaceScanner) {
this.surfaceScanner = surfaceScanner;
backend = CentralLookup.getDefault().lookup(BackendAPI.class);
backend.addUGSEventListener(this);

String title = Localization.getString("autoleveler.panel.export");
putValue(NAME, title);
putValue("menuText", title);
putValue(Action.SHORT_DESCRIPTION, Localization.getString("autoleveler.panel.export.tooltip"));
putValue("iconBase", ICON_BASE);
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(ICON_BASE, false));

setEnabled(isEnabled());
this.surfaceScanner.addListener(() -> setEnabled(isEnabled()));
}

private static Optional<File> chooseSaveFile(File file) {
fileChooser.setFileFilter(new GcodeFileTypeFilter());
fileChooser.setSelectedFile(file);
int result = fileChooser.showSaveDialog(null);
if (result != JFileChooser.APPROVE_OPTION) {
return Optional.empty();
}
File selectedFile = fileChooser.getSelectedFile();
if (!selectedFile.getName().endsWith(".gcode")) {
selectedFile = new File(selectedFile.getAbsolutePath() + ".gcode");
}
return Optional.of(selectedFile);
}

private Optional<File> getExportFile() {
File loadedFile = backend.getGcodeFile();
File file = new File(loadedFile.getPath() + File.separator + FilenameUtils.removeExtension(loadedFile.getName()) + "-autoleveled.gcode");
return chooseSaveFile(file);
}

@Override
public boolean isEnabled() {
return surfaceScanner.isValid() && backend.getGcodeFile() != null;
}

@Override
public void actionPerformed(ActionEvent e) {
Optional<File> selectedFile = getExportFile();
if (selectedFile.isEmpty()) {
return;
}

CommandProcessorList commandProcessor = createCommandProcessor(backend.getSettings().getAutoLevelSettings(), surfaceScanner);
File gcodeFile = backend.getGcodeFile();
GcodeParser gcp = new GcodeParser();
gcp.addCommandProcessor(commandProcessor);

try (SimpleGcodeStreamWriter gcw = new SimpleGcodeStreamWriter(selectedFile.get())) {
GcodeParserUtils.processAndExport(gcp, gcodeFile, gcw);
} catch (IOException | GcodeParserException ex) {
LOGGER.log(Level.SEVERE, "Could not export gcode", ex);
GUIHelpers.displayErrorDialog("Could not export the loaded gcode: " + ex.getMessage());
}
}

@Override
public void UGSEvent(UGSEvent evt) {
if (evt instanceof FileStateEvent) {
setEnabled(isEnabled());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public AutoLevelerToolbar(SurfaceScanner surfaceScanner) {
add(new JButton(new SaveScannedSurfaceAction(surfaceScanner)));
add(new JButton(new ClearScannedSurfaceAction(surfaceScanner)));
add(Box.createGlue());
add(new JButton(new ExportGcodeAction(surfaceScanner)));
add(new JButton(new GenerateTestDataAction(surfaceScanner)));
add(new JButton(new OpenSettingsAction()));
}
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 061670e

Please sign in to comment.