diff --git a/.gitmodules b/.gitmodules
index e7d8ec04..50b32716 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "LibLaserCut"]
path = LibLaserCut
- url = https://github.com/t-oster/LibLaserCut.git
+ url = https://github.com/fablabnbg/LibLaserCut.git
diff --git a/LibLaserCut b/LibLaserCut
index ebe72ea3..5cddff94 160000
--- a/LibLaserCut
+++ b/LibLaserCut
@@ -1 +1 @@
-Subproject commit ebe72ea3af3b2ab52d797d8100c635f68722100e
+Subproject commit 5cddff94827878797b57a429522f1079e496ff16
diff --git a/Makefile b/Makefile
index ac2ca25d..737aeb5b 100644
--- a/Makefile
+++ b/Makefile
@@ -6,34 +6,49 @@ PREFIX?=/usr
all: jar
help:
- @echo "\n\n\n\
- usage: \n\
- make (or make jar): compile (includes LibLaserCut) \n\
- make run: compile and run \n\
- make dist: build setup files (in ./distribute subdirectory)\n\
- make clean: remove all compiled files\n\
- "
+ @echo "usage:"
+ @echo " 'make': same as 'make jar'"
+ @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"
-run: jar
+ @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; }
+ @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
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
diff --git a/splashsource.svg b/splashsource.svg
index f226c4e8..c3cc5568 100644
--- a/splashsource.svg
+++ b/splashsource.svg
@@ -1,19 +1,22 @@
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)
{
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 6742ea52..862e4b57 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;
@@ -365,10 +367,6 @@ public void windowDeactivated(WindowEvent e)
this.refreshRecentFilesMenu();
this.jmInstallInkscape.setEnabled(Helper.isInkscapeExtensionInstallable());
this.jmInstallIllustrator.setEnabled(Helper.isIllustratorScriptInstallable());
- if (!Helper.isInkscapeExtensionInstallable() && !Helper.isIllustratorScriptInstallable())
- {
- this.jmExtras.setVisible(false);
- }
this.refreshExampleMenu();
//initialize states coorectly
this.visicutModel1PropertyChange(new java.beans.PropertyChangeEvent(visicutModel1, VisicutModel.PROP_SELECTEDLASERDEVICE, null, null));
@@ -1190,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)
@@ -1683,9 +1683,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
@@ -2094,6 +2094,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/java/de/thomas_oster/visicut/misc/Helper.java b/src/main/java/de/thomas_oster/visicut/misc/Helper.java
index bada156b..8988b08f 100644
--- a/src/main/java/de/thomas_oster/visicut/misc/Helper.java
+++ b/src/main/java/de/thomas_oster/visicut/misc/Helper.java
@@ -289,12 +289,20 @@ else if (isMacOS()) {
}
}
- trg = new File(new File(trg, "inkscape"), "extensions");
+ trg = new File(new File(trg, "inkscape"), "extensions");
if (!trg.exists() && !trg.mkdirs())
{
throw new FileNotFoundException("Can't create directory: "+trg);
}
+
+ // clear previously installed extension files
+ for (String f_ : new String[] { "visicut_export.inx", "visicut_export_replace.inx", "visicut_export.py" }) {
+ try {
+ new File(trg, f_).delete();
+ } catch (Exception e) {}
+ }
+
for (File f :src.listFiles())
{
if ("visicut_export.py".equals(f.getName()))
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
diff --git a/tools/inkscape_extension/visicut_export.inx b/tools/inkscape_extension/visicut_export.inx
index 50962b94..ad5334e3 100644
--- a/tools/inkscape_extension/visicut_export.inx
+++ b/tools/inkscape_extension/visicut_export.inx
@@ -1,15 +1,15 @@
- <_name>Add to VisiCut
+ <_name>Open in VisiCut
visicut.exportvisicut_export.py
- true
+ false
- path
-
-
-
-
+ path
+
+
+
+
diff --git a/tools/inkscape_extension/visicut_export.py b/tools/inkscape_extension/visicut_export.py
index 50deef4d..eb703bbd 100755
--- a/tools/inkscape_extension/visicut_export.py
+++ b/tools/inkscape_extension/visicut_export.py
@@ -29,10 +29,10 @@
import subprocess
import tempfile
import unicodedata
-import codecs
import random
import string
import socket
+from pathlib import Path
try:
from os import fsencode
@@ -140,13 +140,17 @@ 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')
- assert version.startswith("Inkscape ")
- match = re.match(r"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(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
-
+
# Strip SVG to only contain selected elements, convert objects to paths, unlink clones
@@ -157,7 +161,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 +203,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 +252,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
@@ -278,7 +282,7 @@ def get_original_filename(filename):
docname = None
# parse SVG for docname tag
- with codecs.open(filename, "r", encoding='utf-8') as f:
+ with open(filename, encoding='utf-8') as f:
for line in f:
if 'sodipodi:docname="' in line:
matches = re.search('sodipodi:docname="(.*).svg"', line)
@@ -326,6 +330,16 @@ 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.)
+for parent in Path(INKSCAPEBIN).parents:
+ apprun = parent / "AppRun"
+ if apprun.is_file() and os.access(apprun, os.X_OK):
+ INKSCAPEBIN = apprun
+ break
+
tmpdir = tempfile.mkdtemp(prefix='temp-visicut-')
dest_filename = os.path.join(tmpdir, get_original_filename(filename))
@@ -384,3 +398,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.
diff --git a/tools/inkscape_extension/visicut_export_replace.inx b/tools/inkscape_extension/visicut_export_replace.inx
deleted file mode 100644
index 2b4f4c7e..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
-
-
-
-
-
-