From e435f2e98859a331e4447154ac822535e9fd6af5 Mon Sep 17 00:00:00 2001 From: Juergen Weigert Date: Mon, 8 Apr 2024 10:16:31 +0200 Subject: [PATCH 01/27] Fix Visicut calling inkscape in an AppImage. In Ubuntu 20.04, this worked flawlessly. But only by chance. We use libraries from the host system, together with the binary inside the mounted appimage. in Ubintu 22.04, this crashes: /tmp/.mount_inkscaYGPL8G/usr/bin/inkscape --version /tmp/.mount_inkscaYGPL8G/usr/bin/inkscape: error while loading shared libraries: libboost_filesystem.so.1.71.0: cannot open shared object file: No such file or directory The fix is to not call the binary directly, but the AppRun scrit, which nicely prepares the environment. AppRun also prints a message for the user to stdout, (instead of stderr). We now need to filter this, when parsing output from e.g. --version: /tmp/.mount_inkscaYGPL8G/AppRun --version 2>/dev/null You should not use AppImage in production, but you can speedup the AppImage by following this guide: https://inkscape.org/learn/appimage/ Inkscape 1.3.2 (091e20e, 2023-11-25) --- tools/inkscape_extension/visicut_export.py | 29 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index 78c6d952..59bf7704 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -141,12 +141,19 @@ def is_exe(fpath): def inkscape_version(): """Return Inkscape version number as float, e.g. version "0.92.4" --> return: float 0.92""" version = subprocess.check_output([INKSCAPEBIN, "--version"], stderr=DEVNULL).decode('ASCII', 'ignore') + if not version.startswith("Inkscape "): + ## When inkscape lives in an appimage, AppRun may pollute stdout with extra information. + # Go through all the lines, and find the one that starts with Inkscape + lines = version.splitlines() + for version in lines: + if version.startswith("Inkscape "): + break assert version.startswith("Inkscape ") match = re.match("Inkscape ([0-9]+\.[0-9]+).*", version) assert match is not None version_float = float(match.group(1)) return version_float - + # Strip SVG to only contain selected elements, convert objects to paths, unlink clones @@ -157,7 +164,7 @@ def inkscape_version(): # The idea is similar to http://bazaar.launchpad.net/~nikitakit/inkscape/svg2sif/view/head:/share/extensions/synfig_prepare.py#L181 , but more primitive - there is no need for more complicated preprocessing here def stripSVG_inkscape(src, dest, elements): version = inkscape_version() - + # create temporary file for opening with inkscape. # delete this file later so that it will disappear from the "recently opened" list. tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix='temp-visicut-', suffix='.svg') @@ -199,8 +206,8 @@ def stripSVG_inkscape(src, dest, elements): verbs += ["UnhideAllInAllLayers", "EditInvertInAllLayers", "EditDelete", "EditSelectAllInAllLayers", "EditUnlinkClone", "ObjectToPath", "FileSave"] # --verb=action1;action2;... command += ["--verb=" + ";".join(verbs)] - - + + DEBUG = False if DEBUG: # Inkscape sometimes silently ignores wrong verbs, so we need to double-check that everything's right @@ -248,7 +255,7 @@ def stripSVG_inkscape(src, dest, elements): actions += ["export-area-page"] command = [INKSCAPEBIN, tmpfile, "--export-overwrite", "--actions=" + ";".join(actions)] - + try: #sys.stderr.write(" ".join(command)) # run inkscape, buffer output @@ -326,6 +333,17 @@ def get_original_filename(filename): VISICUTBIN = which("VisiCut.Linux", [VISICUTDIR, "/usr/share/visicut"]) INKSCAPEBIN = which("inkscape", [INKSCAPEDIR]) +## Test if this inkscape is in an appimage. +# We detect this by checking for an AppRun file, in one of the parent folders of our INKSCAPEBIN. +# If so, replace INKSCAPEBIN with AppRun, as this is the only safe way to call inkscape. +# (a direct call mixes libraries from the host system with the appimage, may or may not work.) +dir = os.path.split(INKSCAPEBIN)[0] +while dir != '/': + if os.path.exists(os.path.join(dir, "AppRun")): + INKSCAPEBIN = os.path.join(dir, "AppRun") + break + dir = os.path.split(dir)[0] + tmpdir = tempfile.mkdtemp(prefix='temp-visicut-') dest_filename = os.path.join(tmpdir, get_original_filename(filename)) @@ -384,3 +402,4 @@ def get_original_filename(filename): sys.exit(1) # TODO (complicated, probably WONTFIX): cleanup temporary directories -- this is really difficult because we need to make sure that visicut no longer needs the file, even for reloading! +# - maybe add the PID od the running visicut, then we can detect orphaned temp direcories. From ab42570ce77b924619145972854732e03fdfea51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Weigert?= Date: Mon, 8 Apr 2024 11:25:24 +0200 Subject: [PATCH 02/27] Update tools/inkscape_extension/visicut_export.py dirname() should be fine to. It is more readable. Thanks! (not sure if introducing a new apprun_path variable contributes to readability though ...) But I don't mind. Co-authored-by: TheAssassin --- tools/inkscape_extension/visicut_export.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index 59bf7704..60056830 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -337,13 +337,13 @@ def get_original_filename(filename): # We detect this by checking for an AppRun file, in one of the parent folders of our INKSCAPEBIN. # If so, replace INKSCAPEBIN with AppRun, as this is the only safe way to call inkscape. # (a direct call mixes libraries from the host system with the appimage, may or may not work.) -dir = os.path.split(INKSCAPEBIN)[0] +dir = os.path.dirname(INKSCAPEBIN) while dir != '/': - if os.path.exists(os.path.join(dir, "AppRun")): - INKSCAPEBIN = os.path.join(dir, "AppRun") + apprun_path = os.path.join(dir, "AppRun") + if os.path.exists(apprun_path): + INKSCAPEBIN = apprun_path break - dir = os.path.split(dir)[0] - + dir = os.path.dirname(dir) tmpdir = tempfile.mkdtemp(prefix='temp-visicut-') dest_filename = os.path.join(tmpdir, get_original_filename(filename)) From 64f5e4f024f7ef96b4c7f2ea16aab6c91538f55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Weigert?= Date: Mon, 8 Apr 2024 12:45:14 +0200 Subject: [PATCH 03/27] Update tools/inkscape_extension/visicut_export.py --- tools/inkscape_extension/visicut_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index 60056830..12a61b04 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -402,4 +402,4 @@ def get_original_filename(filename): sys.exit(1) # TODO (complicated, probably WONTFIX): cleanup temporary directories -- this is really difficult because we need to make sure that visicut no longer needs the file, even for reloading! -# - maybe add the PID od the running visicut, then we can detect orphaned temp direcories. +# - Maybe add the PID od the running visicut, then we can detect orphaned temp direcories. From 5bfbeef46eed1bf8936de601f7ecc20368558258 Mon Sep 17 00:00:00 2001 From: Juergen Weigert Date: Mon, 8 Apr 2024 22:40:39 +0200 Subject: [PATCH 04/27] rewritten as suggested --- tools/inkscape_extension/visicut_export.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index 12a61b04..f414f68c 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -140,17 +140,14 @@ def is_exe(fpath): def inkscape_version(): """Return Inkscape version number as float, e.g. version "0.92.4" --> return: float 0.92""" - version = subprocess.check_output([INKSCAPEBIN, "--version"], stderr=DEVNULL).decode('ASCII', 'ignore') - if not version.startswith("Inkscape "): - ## When inkscape lives in an appimage, AppRun may pollute stdout with extra information. - # Go through all the lines, and find the one that starts with Inkscape - lines = version.splitlines() - for version in lines: - if version.startswith("Inkscape "): - break - assert version.startswith("Inkscape ") - match = re.match("Inkscape ([0-9]+\.[0-9]+).*", version) - assert match is not None + version_raw = subprocess.check_output([INKSCAPEBIN, "--version"], stderr=DEVNULL).decode('ASCII', 'ignore') + ## When inkscape lives in an appimage, AppRun may pollute stdout with extra information. + # Go through all the lines, and find the one that starts with Inkscape + lines = version_raw.splitlines() + version = [line for line in lines if line.startswith("Inkscape ")] + assert len(version) == 1, "inkscape --version did not return a version number: " + version_raw + match = re.match("Inkscape ([0-9]+\.[0-9]+).*", version[0]) + assert match is not None, "failed to parse version number from " + version[0] version_float = float(match.group(1)) return version_float From 4a551e2fb31dc125ce0e9e7298a2642b67a2f29c Mon Sep 17 00:00:00 2001 From: Juergen Weigert Date: Wed, 10 Apr 2024 01:29:29 +0200 Subject: [PATCH 05/27] Ported the appimage patch to pathlib. Much nicer code and windows compatible. --- tools/inkscape_extension/visicut_export.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index f414f68c..97de806f 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -33,6 +33,7 @@ import random import string import socket +from pathlib import Path try: from os import fsencode @@ -334,13 +335,12 @@ def get_original_filename(filename): # We detect this by checking for an AppRun file, in one of the parent folders of our INKSCAPEBIN. # If so, replace INKSCAPEBIN with AppRun, as this is the only safe way to call inkscape. # (a direct call mixes libraries from the host system with the appimage, may or may not work.) -dir = os.path.dirname(INKSCAPEBIN) -while dir != '/': - apprun_path = os.path.join(dir, "AppRun") - if os.path.exists(apprun_path): - INKSCAPEBIN = apprun_path +for parent in Path(INKSCAPEBIN).parents: + apprun = parent / "AppRun" + if apprun.is_file() and os.access(apprun, os.X_OK): + INKSCAPEBIN = apprun break - dir = os.path.dirname(dir) + tmpdir = tempfile.mkdtemp(prefix='temp-visicut-') dest_filename = os.path.join(tmpdir, get_original_filename(filename)) From fc2ceae470bd3e92d36a8965ee1fd03099dd5bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Weigert?= Date: Mon, 10 Nov 2025 11:15:38 +0100 Subject: [PATCH 06/27] Fix regex pattern for Inkscape version parsing The SyntaxWarning for invalid escape sequences (such as '\.') started as a DeprecationWarning in Python 3.6 and was made into a SyntaxWarning in Python 3.12. --- tools/inkscape_extension/visicut_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py index 97de806f..c89b5853 100755 --- a/tools/inkscape_extension/visicut_export.py +++ b/tools/inkscape_extension/visicut_export.py @@ -147,7 +147,7 @@ def inkscape_version(): lines = version_raw.splitlines() version = [line for line in lines if line.startswith("Inkscape ")] assert len(version) == 1, "inkscape --version did not return a version number: " + version_raw - match = re.match("Inkscape ([0-9]+\.[0-9]+).*", version[0]) + match = re.match(r"Inkscape ([0-9]+\.[0-9]+).*", version[0]) assert match is not None, "failed to parse version number from " + version[0] version_float = float(match.group(1)) return version_float From f0d62e51b8ae385107277ee95dddc1b7a017a302 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Mon, 9 Feb 2026 00:33:26 +0100 Subject: [PATCH 07/27] update LibLaserCut --- LibLaserCut | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibLaserCut b/LibLaserCut index ebe72ea3..b2035be2 160000 --- a/LibLaserCut +++ b/LibLaserCut @@ -1 +1 @@ -Subproject commit ebe72ea3af3b2ab52d797d8100c635f68722100e +Subproject commit b2035be2a5db6f7026273d847b336ec5d02b067e From e2bada4385232904134b3c98d60c7516f9f41dcf Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Mon, 9 Feb 2026 00:37:53 +0100 Subject: [PATCH 08/27] fix moving because of floating point errors --- .../de/thomas_oster/visicut/VisicutModel.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/thomas_oster/visicut/VisicutModel.java b/src/main/java/de/thomas_oster/visicut/VisicutModel.java index b58c6615..54e0010c 100644 --- a/src/main/java/de/thomas_oster/visicut/VisicutModel.java +++ b/src/main/java/de/thomas_oster/visicut/VisicutModel.java @@ -86,6 +86,7 @@ public class VisicutModel extends Component // FIXME: "extends Component" isn't private PlfPart selectedPart = null; public static final String PROP_SELECTEDPART = "selectedPart"; + public static final float ZERO_TOLERANCE = 0.0001f; private PlfFile plfFile = new PlfFile(); @@ -866,16 +867,30 @@ public Modification fitObjectsIntoBed() for(PlfPart p : this.plfFile) { - boolean modified = false; Rectangle2D bb = p.getGraphicObjects().getBoundingBox(); - + result.oldHeight = bb.getHeight(); result.oldWidth = bb.getWidth(); - - + AffineTransform trans = p.getGraphicObjects().getTransform(); - //first try moving to origin, if not in range + + // automatically move if there are floating point errors + if (bb.getX() < 0 && bb.getX() > -ZERO_TOLERANCE) + { + // adding ZERO_TOLERANCE to show 0 instead of -0 mm as reference point x/y + trans.preConcatenate(AffineTransform.getTranslateInstance(-bb.getX() + ZERO_TOLERANCE, 0)); + p.getGraphicObjects().setTransform(trans); + bb = p.getGraphicObjects().getBoundingBox(); + } + if (bb.getY() < 0 && bb.getY() > -ZERO_TOLERANCE) + { + trans.preConcatenate(AffineTransform.getTranslateInstance(0, -bb.getY() + ZERO_TOLERANCE)); + p.getGraphicObjects().setTransform(trans); + bb = p.getGraphicObjects().getBoundingBox(); + } + + // if outside of bed, try moving to origin if (bb.getX() < 0 || bb.getX() + bb.getWidth() > bw) { trans.preConcatenate(AffineTransform.getTranslateInstance(-bb.getX(), 0)); @@ -886,7 +901,7 @@ public Modification fitObjectsIntoBed() result.newHeight = bb.getHeight(); result.newWidth = bb.getWidth(); result.newX = bb.getX(); - result.newY = bb.getY(); + result.newY = bb.getY(); } if (bb.getY() < 0 || bb.getY() + bb.getHeight() > bh) { @@ -898,8 +913,9 @@ public Modification fitObjectsIntoBed() result.newHeight = bb.getHeight(); result.newWidth = bb.getWidth(); result.newX = bb.getX(); - result.newY = bb.getY(); + result.newY = bb.getY(); } + //if still too big (we're in origin now) check if rotation is useful if (bb.getX() + bb.getWidth() > bw || bb.getY() + bb.getHeight() > bh) { From ebab5dcf0feb588f9a39099839c34aff18c7e346 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Mon, 9 Feb 2026 00:39:49 +0100 Subject: [PATCH 09/27] rename Inkscape extension subfolder to VisiCut --- tools/inkscape_extension/visicut_export.inx | 2 +- tools/inkscape_extension/visicut_export_replace.inx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/inkscape_extension/visicut_export.inx b/tools/inkscape_extension/visicut_export.inx index 50962b94..3b88d76b 100644 --- a/tools/inkscape_extension/visicut_export.inx +++ b/tools/inkscape_extension/visicut_export.inx @@ -7,7 +7,7 @@ path - + diff --git a/tools/inkscape_extension/visicut_export_replace.inx b/tools/inkscape_extension/visicut_export_replace.inx deleted file mode 100644 index 4d836438..00000000 --- a/tools/inkscape_extension/visicut_export_replace.inx +++ /dev/null @@ -1,16 +0,0 @@ - - - <_name>Open in VisiCut - visicut.export_replace - visicut_export.py - false - - path - - - - - - From ca7c9855a09a7e5b7ef69beca2d1a2d77f861ca0 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Sat, 28 Mar 2026 18:40:06 +0100 Subject: [PATCH 21/27] Makefile: add appimage shortcut --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Makefile b/Makefile index 1bd92341..f8651f20 100644 --- a/Makefile +++ b/Makefile @@ -12,29 +12,39 @@ help: @echo " 'make run': run the compiled VisiCut" @echo " 'make dist': build setup files (in distribute/ subdirectory)" @echo " 'make clean': remove all compiled files" + splash: ./generatesplash.sh + jar: splash libLaserCut # Write version into properties file (used by Help-About screen). ./versionnumber.sh echo "Version = $(shell ./versionnumber.sh)" > src/main/resources/de/thomas_oster/visicut/gui/resources/VisicutAppVersion.properties mvn initialize mvn package + dist: ./distribute/distribute.sh zip echo "Successfully built the Platform independent ZIP file. For other build variants, please run ./distribute/distribute.sh" + +appimage: + ./distribute/distribute.sh linux-appimage + run: @echo "Running the compiled JAR. If you'd like to recompile, run 'make jar'." @echo java -Xmx2048m -Xms256m -jar target/visicut*full.jar + libLaserCut: @test -f LibLaserCut/pom.xml || { echo "Error: the LibLaserCut submodule is missing. Try running 'git submodule update --init'."; false; } cd LibLaserCut && mvn install cd .. + clean: rm -f src/main/resources/de/thomas_oster/visicut/gui/resources/splash.png rm -f src/main/resources/de/thomas_oster/visicut/gui/resources/VisicutAppVersion.properties mvn clean + install: mkdir -p $(DESTDIR)$(PREFIX)/share/visicut cp target/visicut*full.jar $(DESTDIR)$(PREFIX)/share/visicut/Visicut.jar From 486aecdee3abbdfeee4559931303b3c36c48e407 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Sat, 28 Mar 2026 22:19:29 +0100 Subject: [PATCH 22/27] Revert "generatesplash: move from rsvg-convert to magick" This reverts commit bf794a722762ba5b5139a94c5782155e6dbae3b3. didn't make the splash look better. --- generatesplash.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/generatesplash.sh b/generatesplash.sh index e805e64b..b69f567f 100755 --- a/generatesplash.sh +++ b/generatesplash.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash -echo "Checking for magick..." -if command -v magick >/dev/null 2>&1 +echo "Checking for rsvg..." +if command -v rsvg-convert >/dev/null 2>&1 then - echo "found magick." + echo "found rsvg-convert." else - echo "no magick found. skipping generation of splash" >&2 + echo "no rsvg-convert found. skipping generation of splash" >&2 cp src/main/resources/de/thomas_oster/visicut/gui/resources/splash{-fallback,}.png rm -f src/main/resources/de/thomas_oster/visicut/gui/resources/splash@{2,3}x.png exit @@ -15,10 +15,10 @@ echo "Version is: \"$VERSION\" (override with VERSION environment variable)" echo "Generating SVG" cat splashsource.svg|sed s#insert#$VERSION#g# > splash.svg echo "Converting to png" -magick -background none splash.svg -resize 514x444 src/main/resources/de/thomas_oster/visicut/gui/resources/splash.png +rsvg-convert -w 514 -h 444 splash.svg > src/main/resources/de/thomas_oster/visicut/gui/resources/splash.png # high-dpi variants (see https://docs.oracle.com/javase/10/docs/api/java/awt/SplashScreen.html ) -magick -background none splash.svg -resize 1028x888 src/main/resources/de/thomas_oster/visicut/gui/resources/splash@2x.png -magick -background none splash.svg -resize 1542x1332 src/main/resources/de/thomas_oster/visicut/gui/resources/splash@3x.png +rsvg-convert -w 1028 -h 888 splash.svg > src/main/resources/de/thomas_oster/visicut/gui/resources/splash@2x.png +rsvg-convert -w 1542 -h 1332 splash.svg > src/main/resources/de/thomas_oster/visicut/gui/resources/splash@3x.png echo "cleaning..." rm splash.svg echo "done." From 0fc3a0e2f2ab7259a0b354ca89c8bc347787cb15 Mon Sep 17 00:00:00 2001 From: tchilov <31223839+tchilov@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:15:29 +0200 Subject: [PATCH 23/27] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 10fc66ee..43b50542 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ # VisiCut A userfriendly, platform-independent tool for preparing, saving and sending jobs to Lasercutters.
-For more information please look at the [project page](https://www.visicut.org). +For more information please look at the [project page](https://www.visicut.org).
+ +This fork is actively developed by members of Fab Lab Region Nürnberg e.V.
+It includes sane defaults and many smaller improvements over [upstream VisiCut](https://github.com/t-oster/VisiCut). ## Download -- [Latest releases](http://download.visicut.org) -- [Some older versions on Github](https://github.com/t-oster/VisiCut/releases) +[GitHub Releases](https://github.com/fablabnbg/VisiCut/releases) ## Compiling and Hacking -See: [Getting Started](https://github.com/t-oster/VisiCut/wiki/Development:-Getting-started) in the wiki +[Getting Started](https://github.com/t-oster/VisiCut/wiki/Development:-Getting-started) ## LibLaserCut From cd74e6819a7cfc30feb8927180e9a0ba982e6c4f Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Mon, 18 May 2026 15:31:30 +0200 Subject: [PATCH 24/27] show an error dialog before sending if any laser setting is zero --- .../de/thomas_oster/visicut/gui/MainView.java | 34 +++++++++++++++++++ .../visicut/gui/resources/MainView.properties | 1 + .../gui/resources/MainView_de_DE.properties | 1 + .../gui/resources/MainView_fr_FR.properties | 1 + .../gui/resources/MainView_it_IT.properties | 1 + .../gui/resources/MainView_nl_NL.properties | 1 + 6 files changed, 39 insertions(+) diff --git a/src/main/java/de/thomas_oster/visicut/gui/MainView.java b/src/main/java/de/thomas_oster/visicut/gui/MainView.java index b64fb1af..eba58859 100644 --- a/src/main/java/de/thomas_oster/visicut/gui/MainView.java +++ b/src/main/java/de/thomas_oster/visicut/gui/MainView.java @@ -22,6 +22,8 @@ import de.thomas_oster.liblasercut.IllegalJobException; import de.thomas_oster.liblasercut.LaserCutter; import de.thomas_oster.liblasercut.properties.LaserProperty; +import de.thomas_oster.liblasercut.properties.FloatPowerSpeedFrequencyProperty; +import de.thomas_oster.liblasercut.properties.FloatMinMaxPowerSpeedFrequencyProperty; import de.thomas_oster.liblasercut.ProgressListener; import de.thomas_oster.liblasercut.platform.Util; import de.thomas_oster.uicomponents.PlatformIcon; @@ -2090,6 +2092,38 @@ private synchronized void executeOrSaveJob(File saveToFile) { return; } + + + String cuttingSettingsZeroErrorMessage = ""; + for (Map.Entry> cuttingSetting : cuttingSettings.entrySet()) { + for (LaserProperty laserProperty : cuttingSetting.getValue()) { + if (laserProperty instanceof FloatPowerSpeedFrequencyProperty) { + FloatPowerSpeedFrequencyProperty floatPowerSpeedFrequencyProperty = (FloatPowerSpeedFrequencyProperty) laserProperty; + + if (floatPowerSpeedFrequencyProperty.getFrequency() == 0) + cuttingSettingsZeroErrorMessage += cuttingSetting.getKey() + ": frequency = 0\n"; + } + + if (laserProperty instanceof FloatMinMaxPowerSpeedFrequencyProperty) { + FloatMinMaxPowerSpeedFrequencyProperty floatMinMaxPowerSpeedFrequencyProperty = (FloatMinMaxPowerSpeedFrequencyProperty) laserProperty; + + if (floatMinMaxPowerSpeedFrequencyProperty.getMinPower() == 0) + cuttingSettingsZeroErrorMessage += cuttingSetting.getKey() + ": min power = 0\n"; + } + + if (laserProperty.getPower() == 0) + cuttingSettingsZeroErrorMessage += cuttingSetting.getKey() + ": power = 0\n"; + + if (laserProperty.getSpeed() == 0) + cuttingSettingsZeroErrorMessage += cuttingSetting.getKey() + ": speed = 0\n"; + } + } + if (!cuttingSettingsZeroErrorMessage.equals("")) { + JOptionPane.showMessageDialog(this, bundle.getString("CUTTING_SETTINGS_ZERO") + ":\n\n" + cuttingSettingsZeroErrorMessage, "", JOptionPane.ERROR_MESSAGE); + return; + } + + if (VisicutModel.getInstance().getStartPoint() != null) { if (!dialog.showYesNoQuestion(bundle.getString("STARTPOINTWARNING"))) diff --git a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView.properties b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView.properties index 2b69061b..a6141d8a 100644 --- a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView.properties +++ b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView.properties @@ -154,3 +154,4 @@ rotaryAxisRadiusTextField.text=100 rotaryAxisRadiusTextField.toolTipText=diameter of workpiece in rotary engrave unit rotaryAxisDiameterLabel.text=Diameter: rotaryAxisDiameterLabelMm.text=\ mm +CUTTING_SETTINGS_ZERO=One or more laser settings are not set diff --git a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_de_DE.properties b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_de_DE.properties index 924f0d10..0b2391e4 100644 --- a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_de_DE.properties +++ b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_de_DE.properties @@ -148,3 +148,4 @@ SCALED_DOWN_TO=Verkleinert auf rotaryAxisDiameterLabel.text=Durchmesser: rotaryAxisCheckBox.text=Rotationseinheit aktiv rotaryAxisRadiusTextField.toolTipText=Durchmesser des Werkst\u00fccks in der Rotationseinheit +CUTTING_SETTINGS_ZERO=Eine oder mehrere Lasereinstellungen sind nicht gesetzt diff --git a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_fr_FR.properties b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_fr_FR.properties index 975aac99..211a41ef 100644 --- a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_fr_FR.properties +++ b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_fr_FR.properties @@ -147,3 +147,4 @@ CAMERA_NOT_YET_CALIBRATED=The camera is not yet calibrated. Please open Options SETTINGS_DIR_IS_VCS_REPOSITORY=Your settings directory uses a version control system (e.g. git). This probably means that everything is already up to date.\n\nVisiCut will refuse to overwrite this to avoid data loss.\n\nIf you know what you are doing, open the settings directory and update or delete it manually: UPDATE_SETTINGS=Do you want to download updated laser settings? SCALED_DOWN_TO=R\u00e9duit \u00e0 +CUTTING_SETTINGS_ZERO=One or more laser settings are not set diff --git a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_it_IT.properties b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_it_IT.properties index 679750c6..dba18c01 100644 --- a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_it_IT.properties +++ b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_it_IT.properties @@ -146,3 +146,4 @@ CAMERA_NOT_YET_CALIBRATED=The camera is not yet calibrated. Please open Options SETTINGS_DIR_IS_VCS_REPOSITORY=Your settings directory uses a version control system (e.g. git). This probably means that everything is already up to date.\n\nVisiCut will refuse to overwrite this to avoid data loss.\n\nIf you know what you are doing, open the settings directory and update or delete it manually: UPDATE_SETTINGS=Do you want to download updated laser settings? SCALED_DOWN_TO=Ridimensionato a +CUTTING_SETTINGS_ZERO=One or more laser settings are not set diff --git a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_nl_NL.properties b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_nl_NL.properties index 14b29032..073b5960 100644 --- a/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_nl_NL.properties +++ b/src/main/resources/de/thomas_oster/visicut/gui/resources/MainView_nl_NL.properties @@ -124,3 +124,4 @@ CAMERA_NOT_YET_CALIBRATED=The camera is not yet calibrated. Please open Options SETTINGS_DIR_IS_VCS_REPOSITORY=Your settings directory uses a version control system (e.g. git). This probably means that everything is already up to date.\n\nVisiCut will refuse to overwrite this to avoid data loss.\n\nIf you know what you are doing, open the settings directory and update or delete it manually: UPDATE_SETTINGS=Do you want to download updated laser settings? SCALED_DOWN_TO=Teruggebracht tot +CUTTING_SETTINGS_ZERO=One or more laser settings are not set From 48e97814d3202aa7ddc1d2c89f449d356377725b Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Mon, 18 May 2026 21:37:17 +0200 Subject: [PATCH 25/27] improve Makefile --- Makefile | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f8651f20..737aeb5b 100644 --- a/Makefile +++ b/Makefile @@ -8,24 +8,28 @@ all: jar help: @echo "usage:" @echo " 'make': same as 'make jar'" - @echo " 'make jar': compile VisiCut (including LibLaserCut)" - @echo " 'make run': run the compiled VisiCut" + @echo " 'make fulljar': compile VisiCut (including LibLaserCut) and run it" + @echo " 'make jar': compile VisiCut and run it" + @echo " 'make run': run VisiCut" @echo " 'make dist': build setup files (in distribute/ subdirectory)" @echo " 'make clean': remove all compiled files" splash: ./generatesplash.sh -jar: splash libLaserCut - # Write version into properties file (used by Help-About screen). - ./versionnumber.sh - echo "Version = $(shell ./versionnumber.sh)" > src/main/resources/de/thomas_oster/visicut/gui/resources/VisicutAppVersion.properties +jar: + mvn package + java -Xmx2048m -Xms256m -jar target/visicut*full.jar + +fulljar: splash libLaserCut + @echo "Version = $(shell ./versionnumber.sh)" > src/main/resources/de/thomas_oster/visicut/gui/resources/VisicutAppVersion.properties mvn initialize mvn package + java -Xmx2048m -Xms256m -jar target/visicut*full.jar dist: ./distribute/distribute.sh zip - echo "Successfully built the Platform independent ZIP file. For other build variants, please run ./distribute/distribute.sh" + @echo "Successfully built the Platform independent ZIP file. For other build variants, please run ./distribute/distribute.sh" appimage: ./distribute/distribute.sh linux-appimage @@ -36,7 +40,7 @@ run: java -Xmx2048m -Xms256m -jar target/visicut*full.jar libLaserCut: - @test -f LibLaserCut/pom.xml || { echo "Error: the LibLaserCut submodule is missing. Try running 'git submodule update --init'."; false; } + @test -f LibLaserCut/pom.xml || { echo "Error: the LibLaserCut submodule is missing. Try running 'git submodule update --init'."; false; } cd LibLaserCut && mvn install cd .. From 0ab8c99c8cbfcd314fab438351efc725de76b550 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Tue, 19 May 2026 10:19:05 +0200 Subject: [PATCH 26/27] increase recent files list to 10 entries --- src/main/java/de/thomas_oster/visicut/gui/MainView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/thomas_oster/visicut/gui/MainView.java b/src/main/java/de/thomas_oster/visicut/gui/MainView.java index eba58859..47e54e6e 100644 --- a/src/main/java/de/thomas_oster/visicut/gui/MainView.java +++ b/src/main/java/de/thomas_oster/visicut/gui/MainView.java @@ -1681,9 +1681,9 @@ public void loadFile(File file, final boolean discardCurrent) List recent = this.visicutModel1.getPreferences().getRecentFiles(); recent.remove(file.getAbsolutePath()); recent.add(0, file.getAbsolutePath()); - if (recent.size() > 5) + if (recent.size() > 10) { - recent.subList(5, recent.size()).clear(); + recent.subList(10, recent.size()).clear(); } this.refreshRecentFilesMenu(); try From 9302fb12d84d86c6c45c0a6c70c6b927dfa250f1 Mon Sep 17 00:00:00 2001 From: Tom Tchilov Date: Tue, 19 May 2026 11:34:00 +0200 Subject: [PATCH 27/27] fix laser cutter settings pane being too narrow --- src/main/java/de/thomas_oster/visicut/gui/MainView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/thomas_oster/visicut/gui/MainView.java b/src/main/java/de/thomas_oster/visicut/gui/MainView.java index 47e54e6e..862e4b57 100644 --- a/src/main/java/de/thomas_oster/visicut/gui/MainView.java +++ b/src/main/java/de/thomas_oster/visicut/gui/MainView.java @@ -1188,7 +1188,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) jPanel4Layout.setHorizontalGroup( jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jScrollPane1) + + // I know I shouldn't modify this code, but since I'm not using the Form Editor, it's going to be fine. + .addComponent(jScrollPane1, 540, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE) ); jPanel4Layout.setVerticalGroup( jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)