diff --git a/README.md b/README.md index 1ff65e8bd..b6be978c8 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ It pairs really well with [Marlin 3D printer firmware](https://github.com/Margin ## Where to go from here - [Join our Discord community](https://discord.gg/QtvHqAv8yp). Share your creations, make new friends! The best place to get support. -- Buy a [Makelangelo 5](https://www.marginallyclever.com/products/makelangelo-5/) hanging plotter. - Read more of [our friendly manuals](http://mcr.dozuki.com). Pictures and arrows and everything. - Build your own plotter and setup [Marlin 3D printer firmware](https://github.com/MarginallyClever/Marlin-polargraph/tree/2.1.x-polargraph) with the help of the [documentation](https://www.marginallyclever.com/2021/10/friday-facts-4-how-to-marlin-polargraph/). +- Buy a [Makelangelo 5](https://www.marginallyclever.com/products/makelangelo-5/) vertical plotter. -## Developers +## Developers + Translators -- [Join our Discord community](https://discord.gg/QtvHqAv8yp). Share your creations, make new friends! +- Translations are organized through https://crowdin.com/project/makelangelo and your help is very welcome. - Please see the [developer getting started wiki page](https://github.com/MarginallyClever/Makelangelo-software/wiki/Getting-Started-for-Developers) for tips on setting up your local copy of the project, connecting to an IDE, and building from the console. - Also check out the [contributing guide](https://github.com/MarginallyClever/Makelangelo-software/blob/master/CONTRIBUTING.md). @@ -43,3 +43,4 @@ Makelangelo is derived from the work of Paul Fisher. It is largely inspired by [ - Icons from [Famfamfam silk icons](http://www.famfamfam.com/lab/icons/silk/), Creative Commons Attribution 3.0 License - App icons provided by http://icons8.com. - Ramps v1.4 drawing from [Reprap](Wikihttps://reprap.org/wiki/File:Rampsv14_wiring_psu.png), GPL v2 +- Inspiration from [Kinogaki](https://app.kinogaki.com/) \ No newline at end of file diff --git a/graphViewSettings.json b/graphViewSettings.json new file mode 100644 index 000000000..50c3ba942 --- /dev/null +++ b/graphViewSettings.json @@ -0,0 +1 @@ +{"connectionPointColor":-4144960,"drawCursor":true,"panelGridColor":-4934476,"nodeColorTitleFont":-1,"nodeColorBackground":-1,"nodeColorInternalBorder":-12566464,"panelColorBackground":-4144960,"drawOrigin":true,"nodeColorBorder":-16777216,"cornerRadius":5,"nodeColorFontClean":-16777216,"gridSize":20,"nodeColorTitleBackground":-16777216,"drawBackground":true,"nodeColorFontDirty":-65536,"connectionColor":-16776961} \ No newline at end of file diff --git a/pom.xml b/pom.xml index fc425695f..c333b0d18 100644 --- a/pom.xml +++ b/pom.xml @@ -399,12 +399,12 @@ It pairs really well with Marlin-polargraph, the code in the brain of the robot org.jogamp.gluegen gluegen-rt-main - 2.5.0-rc-20230523 + 2.5.0 org.jogamp.jogl jogl-all-main - 2.5.0-rc-20230523 + 2.5.0 @@ -531,6 +531,12 @@ It pairs really well with Marlin-polargraph, the code in the brain of the robot flatlaf 3.2.1 + + + com.github.weisj + jsvg + 1.3.0 + @@ -550,6 +556,41 @@ It pairs really well with Marlin-polargraph, the code in the brain of the robot 0.10.2 compile + + + + io.github.andrewauclair + modern-docking-api + 0.11.6 + + + io.github.andrewauclair + modern-docking-single-app + 0.11.6 + + + io.github.andrewauclair + modern-docking-ui + 0.11.6 + + + com.github.sarxos + webcam-capture + 0.3.12 + + + + + com.github.marginallyclever + nodegraphcore + 1.0.28 + + + com.github.marginallyclever + donatello + 1.2.9 + + com.github.umjammer diff --git a/src/main/assembly/application.xml b/src/main/assembly/application.xml index 3d6eab3b4..6de2c5e03 100644 --- a/src/main/assembly/application.xml +++ b/src/main/assembly/application.xml @@ -16,5 +16,10 @@ true + + + metaInf-services + + diff --git a/src/main/java/com/marginallyclever/convenience/helpers/DrawingHelper.java b/src/main/java/com/marginallyclever/convenience/helpers/DrawingHelper.java index 3724d94db..211997091 100644 --- a/src/main/java/com/marginallyclever/convenience/helpers/DrawingHelper.java +++ b/src/main/java/com/marginallyclever/convenience/helpers/DrawingHelper.java @@ -1,15 +1,10 @@ package com.marginallyclever.convenience.helpers; import com.jogamp.opengl.GL2; -import com.jogamp.opengl.util.texture.Texture; -import com.jogamp.opengl.util.texture.TextureIO; -import com.marginallyclever.convenience.FileAccess; +import com.marginallyclever.makelangelo.texture.TextureWithMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedInputStream; -import java.io.IOException; - public class DrawingHelper { private static final Logger logger = LoggerFactory.getLogger(DrawingHelper.class); @@ -73,21 +68,6 @@ public static void drawRectangle(GL2 gl2, double top, double right, double botto gl2.glEnd(); } - /** - * Load the given file from the classpath. Make sure the size of the picture is a power of 2 - * @param name filename - * @return a texture - */ - public static Texture loadTexture(String name) { - Texture tex = null; - try (BufferedInputStream bis = FileAccess.open(name)) { - tex = TextureIO.newTexture(bis, false, name.substring(name.lastIndexOf('.') + 1)); - } catch (IOException e) { - logger.warn("Can't load {}", name, e); - } - return tex; - } - /** * Paint a quad with the given texture * @param gl2 the render context @@ -96,7 +76,7 @@ public static Texture loadTexture(String name) { * @param width with of the texture * @param height height of the texture */ - public static void paintTexture(GL2 gl2, Texture texture, double x, double y, double width, double height) { + public static void paintTexture(GL2 gl2, TextureWithMetadata texture, double x, double y, double width, double height) { texture.bind(gl2); gl2.glColor4d(1, 1, 1, 1); gl2.glEnable(GL2.GL_TEXTURE_2D); diff --git a/src/main/java/com/marginallyclever/convenience/noise/CellularNoise.java b/src/main/java/com/marginallyclever/convenience/noise/CellularNoise.java index b1866e511..c55042896 100644 --- a/src/main/java/com/marginallyclever/convenience/noise/CellularNoise.java +++ b/src/main/java/com/marginallyclever/convenience/noise/CellularNoise.java @@ -18,6 +18,12 @@ * of SIGGRAPH 96. */ public class CellularNoise implements Noise { + /** + * The maximum distance that a point can be from another point. + */ + private final double MAX_DIST = Math.sqrt(3.0)/2.0; + private final Random random = new Random(); + /** * Represents a point in three dimensional space. * @@ -63,6 +69,11 @@ public double distanceSquared(Point other) } } + @Override + public void setSeed(int seed) { + random.setSeed(seed); + } + @Override public double noise(double xin) { return noise(xin,0,0); @@ -73,6 +84,22 @@ public double noise(double xin, double yin) { return noise(xin,yin,0); } + /** + * Gets the noise value at the provided location. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param z + * the z coordinate. + * @return the noise value at the coordinate. + */ + public double noise(double x, double y, double z) + { + return (minimumDistance(random, new Point(x, y, z)) / MAX_DIST)*2.0-1.0; + } + private int floor(double n) { return n > 0 ? (int) n : (int) n - 1; } @@ -182,7 +209,8 @@ private double minimumDistance(Random r, Point origin) { */ private double processVoxel(Random r, Point p, double s, int x, int y, int z) { // reset random number generator for the voxel - r.setSeed(x + y + z); + long seed = x * 73856093L ^ y * 19349663L ^ z * 83492791L; + r.setSeed(seed); // each voxel always has one point Point created = new Point( x + r.nextDouble(), @@ -198,20 +226,4 @@ private double processVoxel(Random r, Point p, double s, int x, int y, int z) { private double square(double n) { return n * n; } - - /** - * Gets the noise value at the provided location. - * - * @param x - * the x coordinate. - * @param y - * the y coordinate. - * @param z - * the z coordinate. - * @return the noise value at the coordinate. - */ - public double noise(double x, double y, double z) - { - return minimumDistance(new Random(), new Point(x, y, z)); - } } diff --git a/src/main/java/com/marginallyclever/convenience/noise/Noise.java b/src/main/java/com/marginallyclever/convenience/noise/Noise.java index 92e5fd73b..2974dca21 100644 --- a/src/main/java/com/marginallyclever/convenience/noise/Noise.java +++ b/src/main/java/com/marginallyclever/convenience/noise/Noise.java @@ -1,14 +1,29 @@ package com.marginallyclever.convenience.noise; public interface Noise { - // 1D noise + void setSeed(int seed); + + /** + * 1D noise + * @param xin a double + * @return a double [-1...1] + */ double noise(double xin); - // 2D noise + /** + * 2D noise + * @param xin a double + * @param yin a double + * @return a double [-1...1] + */ double noise(double xin, double yin); - // 3D noise + /** + * 3D noise + * @param xin a double + * @param yin a double + * @param zin a double + * @return a double [-1...1] + */ double noise(double xin, double yin,double zin); - - //double noise(double xin, double yin,double zin,double win); } diff --git a/src/main/java/com/marginallyclever/convenience/noise/PerlinNoise.java b/src/main/java/com/marginallyclever/convenience/noise/PerlinNoise.java index 5b741d144..195087d1e 100644 --- a/src/main/java/com/marginallyclever/convenience/noise/PerlinNoise.java +++ b/src/main/java/com/marginallyclever/convenience/noise/PerlinNoise.java @@ -34,18 +34,29 @@ public PerlinNoise() { } } + /** + * 1D Perlin noise. + * @param x + * @return value from -1 to 1. + */ @Override public double noise(double x) { return noise(x,0,0); } + /** + * 2D Perlin noise. + * @param x + * @param y + * @return value from -1 to 1. + */ @Override public double noise(double x, double y) { return noise(x,y,0); } /** - * 2D Perlin noise. Returns value from -1 to 1. + * 3D Perlin noise. * @param x * @param y * @param z @@ -90,4 +101,9 @@ private double grad(int hash, double x, double y, double z) { v = h<4 ? y : h==12||h==14 ? x : z; return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); } + + @Override + public void setSeed(int seed) { + // not used + } } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/convenience/noise/SimplexNoise.java b/src/main/java/com/marginallyclever/convenience/noise/SimplexNoise.java index 4d68c3065..d136ec5b3 100644 --- a/src/main/java/com/marginallyclever/convenience/noise/SimplexNoise.java +++ b/src/main/java/com/marginallyclever/convenience/noise/SimplexNoise.java @@ -360,4 +360,9 @@ public double noise(double x, double y, double z, double w) { // Sum up and scale the result to cover the range [-1,1] return 27.0 * (n0 + n1 + n2 + n3 + n4); } + + @Override + public void setSeed(int seed) { + // not used + } } diff --git a/src/main/java/com/marginallyclever/makelangelo/DialogBadFirmwareVersion.java b/src/main/java/com/marginallyclever/makelangelo/DialogBadFirmwareVersion.java deleted file mode 100644 index ab75217fd..000000000 --- a/src/main/java/com/marginallyclever/makelangelo/DialogBadFirmwareVersion.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.marginallyclever.makelangelo; - -import com.marginallyclever.makelangelo.select.SelectReadOnlyText; - -import java.awt.Component; -import javax.swing.JOptionPane; -import javax.swing.text.JTextComponent; - -@Deprecated -public class DialogBadFirmwareVersion { - /** - *

- * Uses {@link java.lang.StringBuilder#append(String)} to create an internationalization supported {@code String} - * representing the About Message Dialog's HTML. - *

- *

- *

- * The summation of {@link String#length()} for each of the respective values retrieved with the - * {@code "AboutHTMLBeforeVersionNumber"}, and {@code "AboutHTMLAfterVersionNumber"} {@link Translator} keys, - * in conjunction with {@link Makelangelo#VERSION} is calculated for use with {@link java.lang.StringBuilder#StringBuilder(int)}. - *

- * - * @return An HTML string used for the About Message Dialog. - */ - private String getAboutHtmlFromMultilingualString(String version) { - return Translator.get("firmwareVersionBadMessage", new String[]{version}); - } - - public void display(Component parent,String version) { - final String aboutHtml = getAboutHtmlFromMultilingualString(version); - final JTextComponent bottomText = SelectReadOnlyText.createJEditorPaneWithHyperlinkListenerAndToolTipsForDesktopBrowse(aboutHtml); - JOptionPane.showMessageDialog(parent, bottomText, Translator.get("firmwareVersionBadTitle"), JOptionPane.ERROR_MESSAGE); - } -} diff --git a/src/main/java/com/marginallyclever/makelangelo/DockingPanel.java b/src/main/java/com/marginallyclever/makelangelo/DockingPanel.java new file mode 100644 index 000000000..37f90dbc3 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/DockingPanel.java @@ -0,0 +1,42 @@ +package com.marginallyclever.makelangelo; + +import ModernDocking.Dockable; +import ModernDocking.app.Docking; + +import javax.swing.*; +import java.awt.*; + +/** + * {@link DockingPanel} is a {@link JPanel} that implements {@link Dockable}. + */ +public class DockingPanel extends JPanel implements Dockable { + private final String tabText; + private final String persistentID; + + public DockingPanel(String persistentID, String tabText) { + super(new BorderLayout()); + this.persistentID = persistentID; + this.tabText = tabText; + Docking.registerDockable(this); + } + + @Override + public String getPersistentID() { + return persistentID; + } + + @Override + public String getTabText() { + return tabText; + } + + /** + * Refuse to wrap this {@link DockingPanel} in a {@link JScrollPane}. The panel is responsibile for scrolling, + * not the docking system. + * @return false + */ + @Override + public boolean isWrappableInScrollpane() { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/makelangelo/MainFrame.java b/src/main/java/com/marginallyclever/makelangelo/MainFrame.java index 78462b595..521837684 100644 --- a/src/main/java/com/marginallyclever/makelangelo/MainFrame.java +++ b/src/main/java/com/marginallyclever/makelangelo/MainFrame.java @@ -1,79 +1,82 @@ package com.marginallyclever.makelangelo; +import ModernDocking.DockingRegion; +import ModernDocking.app.AppState; +import ModernDocking.app.Docking; +import ModernDocking.app.RootDockingPanel; +import ModernDocking.exception.DockingLayoutException; +import ModernDocking.ext.ui.DockingUI; +import com.marginallyclever.convenience.FileAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.prefs.Preferences; +import java.io.File; +import java.util.ArrayList; +import java.util.List; /** * A JFrame that remembers its size and position. */ public class MainFrame extends JFrame { private static final Logger logger = LoggerFactory.getLogger(MainFrame.class); - private final Preferences prefs; - private static final String KEY_IS_FULLSCREEN = "isFullscreen"; - private static final String KEY_WINDOW_WIDTH = "windowWidth"; - private static final String KEY_WINDOW_HEIGHT = "windowHeight"; - private static final String KEY_WINDOW_X = "windowX"; - private static final String KEY_WINDOW_Y = "windowY"; + private final List windows = new ArrayList<>(); - public MainFrame(String title, Preferences prefs) { - super(title); - this.prefs = prefs; - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - saveWindowSizeAndPosition(); - } + public MainFrame() { + super(); + setLocationByPlatform(true); + initDocking(); + } + + private void initDocking() { + Docking.initialize(this); + DockingUI.initialize(); + ModernDocking.settings.Settings.setAlwaysDisplayTabMode(true); + ModernDocking.settings.Settings.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + // create root panel + RootDockingPanel root = new RootDockingPanel(this); + add(root, BorderLayout.CENTER); + } - @Override - public void componentMoved(ComponentEvent e) { - saveWindowSizeAndPosition(); - } - }); + public void addDockingPanel(String persistentID,String tabText,Component component) { + DockingPanel panel = new DockingPanel(persistentID,tabText); + panel.add(component); + windows.add(panel); } - public void setWindowSizeAndPosition() { - // set the normal window size and position - Dimension frameSize = Toolkit.getDefaultToolkit().getScreenSize(); - int windowW = prefs.getInt(KEY_WINDOW_WIDTH, frameSize.width); - int windowH = prefs.getInt(KEY_WINDOW_HEIGHT, frameSize.height); - int windowX = prefs.getInt(KEY_WINDOW_X, (frameSize.width - windowW)/2); - int windowY = prefs.getInt(KEY_WINDOW_Y, (frameSize.height - windowH)/2); - logger.info("Set window size and position "+windowW+"x"+windowH+" pos="+windowX+","+windowY); - this.setBounds(windowX, windowY,windowW, windowH); + /** + * Reset the default layout. These depend on the order of creation in createDefaultLayout(). + */ + public void resetDefaultLayout() { + logger.info("Resetting layout to default."); + setSize(1000, 750); - if(prefs.getBoolean(KEY_IS_FULLSCREEN,false)) { - // if we were in fullscreen mode, maximize the window. - // this way the "go fullscreen" button will return the window to "normal" size. - this.setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); + for (DockingPanel w : windows) { + Docking.undock(w); } + var previewPanel = windows.get(0); + var donatelloPanel = windows.get(1); + Docking.dock(previewPanel, this, DockingRegion.CENTER); + Docking.dock(donatelloPanel, previewPanel, DockingRegion.SOUTH); + logger.debug("done."); } - // remember window location for next time. - private void saveWindowSizeAndPosition() { - int state = getExtendedState(); - boolean isFullscreen = ((state & JFrame.MAXIMIZED_BOTH)!=0); + public void saveAndRestoreLayout() { + // now that the main frame is set up with the defaults, we can restore the layout + var layoutPath = FileAccess.getHomeDirectory()+ File.separator+".makelangelo"+File.separator+"makelangelo.layout"; + logger.debug("layout file={}",layoutPath); + AppState.setPersistFile(new File(layoutPath)); + AppState.setAutoPersist(true); - prefs.putBoolean(KEY_IS_FULLSCREEN, isFullscreen); - if(!isFullscreen) { - Dimension frameSize = this.getSize(); - Point p = this.getLocation(); - prefs.putInt(KEY_WINDOW_WIDTH, frameSize.width); - prefs.putInt(KEY_WINDOW_HEIGHT, frameSize.height); - prefs.putInt(KEY_WINDOW_X, p.x); - prefs.putInt(KEY_WINDOW_Y, p.y); + try { + AppState.restore(); + } catch (DockingLayoutException e) { + logger.error("Failed to restore docking layout.", e); } } - public static void main(String[] args) { - MainFrame frame = new MainFrame("Test",Preferences.userRoot().node("com/marginallyclever/makelangelo")); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setWindowSizeAndPosition(); - frame.setVisible(true); + public List getDockingPanels() { + return windows; } } diff --git a/src/main/java/com/marginallyclever/makelangelo/MainMenu.java b/src/main/java/com/marginallyclever/makelangelo/MainMenu.java index 2b2d6c244..aea4bddc8 100644 --- a/src/main/java/com/marginallyclever/makelangelo/MainMenu.java +++ b/src/main/java/com/marginallyclever/makelangelo/MainMenu.java @@ -1,18 +1,17 @@ package com.marginallyclever.makelangelo; +import ModernDocking.app.DockableMenuItem; import com.hopding.jrpicam.exceptions.FailedToRunRaspistillException; import com.marginallyclever.convenience.helpers.StringHelper; import com.marginallyclever.convenience.log.Log; import com.marginallyclever.convenience.log.LogPanel; +import com.marginallyclever.makelangelo.applicationsettings.ApplicationSettings; import com.marginallyclever.makelangelo.firmwareuploader.FirmwareUploaderPanel; -import com.marginallyclever.makelangelo.makeart.turtletool.TurtleTool; import com.marginallyclever.makelangelo.makeart.io.OpenFileChooser; -import com.marginallyclever.makelangelo.makeart.turtletool.*; import com.marginallyclever.makelangelo.makeart.turtlegenerator.TurtleGenerator; import com.marginallyclever.makelangelo.makeart.turtlegenerator.TurtleGeneratorFactory; import com.marginallyclever.makelangelo.makeart.turtlegenerator.TurtleGeneratorPanel; -import com.marginallyclever.makelangelo.applicationsettings.GFXPreferences; -import com.marginallyclever.makelangelo.applicationsettings.ApplicationSettings; +import com.marginallyclever.makelangelo.makeart.turtletool.*; import com.marginallyclever.makelangelo.paper.PaperSettingsPanel; import com.marginallyclever.makelangelo.plotter.PiCaptureAction; import com.marginallyclever.makelangelo.plotter.marlinsimulation.MarlinSimulation; @@ -38,24 +37,26 @@ public class MainMenu extends JMenuBar { private static int SHORTCUT_CTRL = InputEvent.CTRL_DOWN_MASK; private static int SHORTCUT_ALT = InputEvent.ALT_DOWN_MASK; private final Makelangelo app; + private final MainFrame frame; private final SaveDialog saveDialog = new SaveDialog(); private RecentFiles recentFiles; private final ApplicationSettings myPreferencesPanel = new ApplicationSettings(); private boolean isMacOS = false; - public MainMenu(Makelangelo app) { + public MainMenu(Makelangelo app, MainFrame frame) { super(); this.app = app; + this.frame = frame; setSystemLookAndFeelForMacos(); add(createFileMenu()); add(createSettingsMenu()); add(createGenerateMenu()); add(createToolsMenu()); add(createViewMenu()); + add(createWindowsMenu()); add(createRobotMenu()); add(createHelpMenu()); updateUI(); - } private void setSystemLookAndFeelForMacos() { @@ -63,7 +64,6 @@ private void setSystemLookAndFeelForMacos() { if ((os.contains("mac")) || (os.contains("darwin"))) { isMacOS=true; System.setProperty("apple.laf.useScreenMenuBar", "true"); - SHORTCUT_CTRL = InputEvent.META_DOWN_MASK; SHORTCUT_ALT = InputEvent.META_DOWN_MASK; } } @@ -149,40 +149,42 @@ private JMenu createViewMenu() { JMenu menu = new JMenu(Translator.get("MenuView")); menu.setMnemonic('V'); - JMenuItem buttonZoomOut = new JMenuItem(Translator.get("MenuView.zoomOut"), KeyEvent.VK_MINUS); - buttonZoomOut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, SHORTCUT_CTRL)); - buttonZoomOut.addActionListener((e) -> app.getCamera().zoom(1)); - buttonZoomOut.setIcon(new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-zoom-out-16.png")))); - menu.add(buttonZoomOut); - - JMenuItem buttonZoomIn = new JMenuItem(Translator.get("MenuView.zoomIn"), KeyEvent.VK_EQUALS); - buttonZoomIn.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, SHORTCUT_CTRL)); - buttonZoomIn.addActionListener((e) -> app.getCamera().zoom(-1)); - buttonZoomIn.setIcon(new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-zoom-in-16.png")))); - menu.add(buttonZoomIn); - - JMenuItem buttonZoomToFit = new JMenuItem(Translator.get("MenuView.zoomFit"), KeyEvent.VK_0); - buttonZoomToFit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, SHORTCUT_CTRL)); - buttonZoomToFit.addActionListener((e) -> app.getCamera().zoomToFit(app.getPaper().getPaperWidth(),app.getPaper().getPaperHeight())); - menu.add(buttonZoomToFit); - - JCheckBoxMenuItem checkboxShowPenUpMoves = new JCheckBoxMenuItem(Translator.get("GFXPreferences.showPenUp"), GFXPreferences.getShowPenUp()); - checkboxShowPenUpMoves.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_M, SHORTCUT_CTRL));//"ctrl M" - checkboxShowPenUpMoves.addActionListener((e) -> { - boolean b = GFXPreferences.getShowPenUp(); - GFXPreferences.setShowPenUp(!b); - }); - GFXPreferences.addListener((e)->{ - checkboxShowPenUpMoves.setSelected ((boolean)e.getNewValue()); - }); - checkboxShowPenUpMoves.setIcon(new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-plane-16.png")))); - menu.add(checkboxShowPenUpMoves); - menu.add(createRenderStyleMenu()); + menu.addSeparator(); return menu; } + private JMenu createWindowsMenu() { + JMenu menuWindows = new JMenu(Translator.get("MenuWindows")); + // add each panel to the windows menu with a checkbox if the current panel is visible. + int index=0; + for(DockingPanel w : frame.getDockingPanels()) { + DockableMenuItem item = new DockableMenuItem(w.getPersistentID(),w.getTabText()); + menuWindows.add(item); + if(index<12) { + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1 + index, InputEvent.SHIFT_DOWN_MASK)); + } + index++; + } + + menuWindows.add(new JSeparator()); + menuWindows.add(new JMenuItem(new AbstractAction() { + { + putValue(Action.NAME, "Reset default layout"); + // no accelerator key. + putValue(Action.SMALL_ICON, new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-reset-16.png")))); + putValue(Action.SHORT_DESCRIPTION, "Reset the layout to the default."); + } + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + frame.resetDefaultLayout(); + } + })); + return menuWindows; + } + private JMenu createHelpMenu() { JMenu menu = new JMenu(Translator.get("Help")); menu.setMnemonic('H'); @@ -345,7 +347,7 @@ public void windowClosing(WindowEvent e) { } private JMenu createToolsMenu() { - JMenu menu = new JMenu(Translator.get("Art Pipeline")); + JMenu menu = new JMenu(Translator.get("ArtPipeline")); menu.setMnemonic('T'); try { diff --git a/src/main/java/com/marginallyclever/makelangelo/Makelangelo.java b/src/main/java/com/marginallyclever/makelangelo/Makelangelo.java index 92bf53bc3..450ecb18b 100644 --- a/src/main/java/com/marginallyclever/makelangelo/Makelangelo.java +++ b/src/main/java/com/marginallyclever/makelangelo/Makelangelo.java @@ -5,6 +5,10 @@ import com.marginallyclever.convenience.CommandLineOptions; import com.marginallyclever.convenience.FileAccess; import com.marginallyclever.convenience.log.Log; +import com.marginallyclever.donatello.Donatello; +import com.marginallyclever.donatello.FileHelper; +import com.marginallyclever.makelangelo.applicationsettings.GFXPreferences; +import com.marginallyclever.makelangelo.donatelloimpl.DonatelloDropTarget; import com.marginallyclever.makelangelo.makeart.io.LoadFilePanel; import com.marginallyclever.makelangelo.makeart.io.OpenFileChooser; import com.marginallyclever.makelangelo.makeart.io.SaveGCode; @@ -18,12 +22,16 @@ import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings; import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettingsManager; import com.marginallyclever.makelangelo.preview.Camera; +import com.marginallyclever.makelangelo.preview.PreviewDropTarget; import com.marginallyclever.makelangelo.preview.PreviewPanel; import com.marginallyclever.makelangelo.turtle.Turtle; import com.marginallyclever.makelangelo.turtle.turtlerenderer.MarlinSimulationVisualizer; import com.marginallyclever.makelangelo.turtle.turtlerenderer.TurtleRenderFacade; import com.marginallyclever.makelangelo.turtle.turtlerenderer.TurtleRenderFactory; import com.marginallyclever.makelangelo.turtle.turtlerenderer.TurtleRenderer; +import com.marginallyclever.nodegraphcore.DAO4JSONFactory; +import com.marginallyclever.nodegraphcore.NodeFactory; +import com.marginallyclever.nodegraphcore.ServiceLoaderHelper; import com.marginallyclever.util.PreferencesHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,8 +41,7 @@ import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.dnd.DropTarget; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.event.*; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; @@ -68,10 +75,11 @@ public final class Makelangelo { private Turtle myTurtle = new Turtle(); private final TurtleRenderFacade myTurtleRenderer = new TurtleRenderFacade(); private PlotterRenderer myPlotterRenderer; - + private final Donatello donatello = new Donatello(); + // GUI elements private MainFrame mainFrame; - private final MainMenu mainMenuBar = new MainMenu(this); + private MainMenu mainMenuBar; private PreviewPanel previewPanel; private final MakeleangeloRangeSlider rangeSlider = new MakeleangeloRangeSlider(); @@ -136,7 +144,7 @@ private void addPlotterRendererToPreviewPanel() { } public void run() { - createAppWindow(); + createAppWindow(); //checkSharingPermission(); Preferences preferences = PreferencesHelper.getPreferenceNode(PreferencesHelper.MakelangeloPreferenceKey.FILE); @@ -145,8 +153,8 @@ public void run() { private static void setSystemLookAndFeel() { if(!CommandLineOptions.hasOption("-nolf")) { + FlatLaf.registerCustomDefaultsSource( "com.marginallyclever.makelangelo" ); try { - FlatLaf.registerCustomDefaultsSource( "com.marginallyclever.makelangelo" ); UIManager.setLookAndFeel( new FlatLightLaf() ); } catch( Exception e ) { logger.warn("failed to set flat look and feel. falling back to default native lnf", e); @@ -334,15 +342,72 @@ private Container createContentPane() { contentPane.add(previewPanel, BorderLayout.CENTER); contentPane.add(rangeSlider, BorderLayout.SOUTH); + JToolBar toolBar = createToolBar(); + contentPane.add(toolBar, BorderLayout.NORTH); + return contentPane; } + private JToolBar createToolBar() { + var bar = new JToolBar(); + + var buttonZoomOut = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + camera.zoom(1); + } + }; + buttonZoomOut.putValue(Action.SHORT_DESCRIPTION,Translator.get("MenuView.zoomOut")); + buttonZoomOut.putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK)); + buttonZoomOut.putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-zoom-out-16.png")))); + bar.add(buttonZoomOut); + + var buttonZoomIn = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + camera.zoom(-1); + } + }; + buttonZoomIn.putValue(Action.SHORT_DESCRIPTION,Translator.get("MenuView.zoomIn")); + buttonZoomIn.putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK)); + buttonZoomIn.putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-zoom-in-16.png")))); + bar.add(buttonZoomIn); + + var buttonZoomToFit = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + camera.zoomToFit(myPaper.getPaperWidth(),myPaper.getPaperHeight()); + } + }; + buttonZoomToFit.putValue(Action.SHORT_DESCRIPTION,Translator.get("MenuView.zoomFit")); + buttonZoomToFit.putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_0, InputEvent.CTRL_DOWN_MASK)); + buttonZoomToFit.putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-zoom-to-fit-16.png")))); + bar.add(buttonZoomToFit); + + Action toggleAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + boolean b = GFXPreferences.getShowPenUp(); + GFXPreferences.setShowPenUp(!b); + } + }; + var checkboxShowPenUpMoves = new JToggleButton(toggleAction); + toggleAction.putValue(Action.SHORT_DESCRIPTION,Translator.get("GFXPreferences.showPenUp")); + toggleAction.putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_M, InputEvent.CTRL_DOWN_MASK));//"ctrl M" + toggleAction.putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("/com/marginallyclever/makelangelo/icons8-plane-16.png")))); + checkboxShowPenUpMoves.setSelected(GFXPreferences.getShowPenUp()); + GFXPreferences.addListener((e)->checkboxShowPenUpMoves.setSelected ((boolean)e.getNewValue())); + bar.add(checkboxShowPenUpMoves); + + return bar; + } + + // For thread safety this method should be invoked from the event-dispatching thread. private void createAppWindow() { logger.debug("Creating GUI..."); - Preferences preferences = PreferencesHelper.getPreferenceNode(PreferencesHelper.MakelangeloPreferenceKey.GRAPHICS); - mainFrame = new MainFrame("",preferences); + mainFrame = new MainFrame(); setMainTitle(""); mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setupDropTarget(); @@ -362,11 +427,15 @@ public void windowClosing(WindowEvent e) { logger.warn("Can't load icon", e); } - mainFrame.setJMenuBar(mainMenuBar); - mainFrame.setContentPane(createContentPane()); + mainFrame.addDockingPanel("Preview","Preview",createContentPane()); + mainFrame.addDockingPanel("Donatello","Donatello",donatello); logger.debug(" make visible..."); mainFrame.setVisible(true); - mainFrame.setWindowSizeAndPosition(); + mainFrame.resetDefaultLayout(); + mainFrame.saveAndRestoreLayout(); + + mainMenuBar = new MainMenu(this,mainFrame); + mainFrame.setJMenuBar(mainMenuBar); camera.zoomToFit( Paper.DEFAULT_WIDTH, Paper.DEFAULT_HEIGHT); @@ -391,7 +460,8 @@ public void windowClosing(WindowEvent e) { private void setupDropTarget() { logger.debug("adding drag & drop support..."); - new DropTarget(mainFrame, new MakelangeloDropTarget(this)); + new DropTarget(previewPanel, new PreviewDropTarget(this)); + new DropTarget(donatello, new DonatelloDropTarget(donatello)); } private boolean confirmClose() { @@ -455,6 +525,17 @@ public static void main(String[] args) { Log.start(); logger = LoggerFactory.getLogger(Makelangelo.class); + FileHelper.createDirectoryIfMissing(FileHelper.getExtensionPath()); + ServiceLoaderHelper.addAllPathFiles(FileHelper.getExtensionPath()); + try { + NodeFactory.loadRegistries(); + } + catch (Exception e) { + logger.error("Failed to load node factories", e); + return; + } + DAO4JSONFactory.loadRegistries(); + PreferencesHelper.start(); CommandLineOptions.setFromMain(args); Translator.start(); diff --git a/src/main/java/com/marginallyclever/makelangelo/Translator.java b/src/main/java/com/marginallyclever/makelangelo/Translator.java index f273d73f5..bce252825 100644 --- a/src/main/java/com/marginallyclever/makelangelo/Translator.java +++ b/src/main/java/com/marginallyclever/makelangelo/Translator.java @@ -1,23 +1,18 @@ package com.marginallyclever.makelangelo; -import com.marginallyclever.convenience.FileAccess; -import com.marginallyclever.util.MarginallyCleverTranslationXmlFileHelper; import com.marginallyclever.util.PreferencesHelper; -import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.net.URI; +import java.net.JarURLConnection; import java.net.URL; -import java.nio.file.*; +import java.net.URLConnection; import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; -import java.util.stream.Stream; /** *

MultilingualSupport is the translation engine. You ask for a string it finds the matching string in the currently selected language. @@ -40,20 +35,36 @@ public final class Translator { private static String defaultLanguage = "English"; // The current choice private static String currentLanguage; - // a list of all languages and their translations strings - private static final Map languages = new HashMap<>(); - private static TranslatorLanguage currentLanguageContainer; + private static ResourceBundle bundle; public static void start() { logger.info("starting translator..."); - + Locale locale = Locale.getDefault(); defaultLanguage = locale.getDisplayLanguage(Locale.ENGLISH); logger.info("Default language = {}", defaultLanguage); - - loadLanguages(); + loadConfig(); } + + private static List findAvailableBundles(String baseName) throws IOException { + List locales = new ArrayList<>(); + Enumeration resources = ClassLoader.getSystemClassLoader().getResources(""); + + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + URLConnection conn = resource.openConnection(); + if (!(conn instanceof JarURLConnection jarConnection)) { + continue; + } + JarFile jar = jarConnection.getJarFile(); + jar.stream() + .map(JarEntry::getName) + .filter(name -> name.startsWith(baseName) && name.endsWith(".properties")) + .forEach(name -> locales.add(name.replace(baseName + "_", "").replace(".properties", ""))); + } + return locales; + } /** @@ -65,7 +76,8 @@ public static boolean isThisTheFirstTimeLoadingLanguageFiles() { if (doesLanguagePreferenceExist()) { // does the list of languages contain the preferred choice? String languageNameFromPref = languagePreferenceNode.get(LANGUAGE_KEY, defaultLanguage); - if (!languages.containsKey(languageNameFromPref)) { + + if (Arrays.stream(getLanguageList()).noneMatch(languageNameFromPref::equals)) { logger.error("Language '{}' not available ...", languageNameFromPref); // To avoid some null issues in Translator.get(String key), @@ -104,119 +116,6 @@ public static void loadConfig() { setCurrentLanguage(languagePreferenceNode.get(LANGUAGE_KEY, defaultLanguage)); } - /** - * Scan folder for language files. - * See http://stackoverflow.com/questions/1429172/how-do-i-list-the-files-inside-a-jar-file - * @throws IllegalStateException No language files found - */ - public static void loadLanguages() { - languages.clear(); - - try { - int found=0; - Stream walk = getLanguagePaths(); - Iterator it = walk.iterator(); - while (it.hasNext()) { - Path p = it.next(); - String name = p.toString(); - //if( f.isDirectory() || f.isHidden() ) continue; - if (FilenameUtils.getExtension(name).equalsIgnoreCase("xml") ) { - if (name.endsWith("pom.xml")) { - continue; - } - - // found an XML file in the /languages folder. Good sign! - if (attemptToLoadLanguageXML(name)) found++; - } - } - walk.close(); - - //logger.debug("total found: "+found); - - if(found==0) { - throw new IllegalStateException("No translations found."); - } - } - catch(Exception e) { - logger.error("{}. Defaulting to {}. Language folder expected to be located at {}", e.getMessage(), defaultLanguage, WORKING_DIRECTORY); - final TranslatorLanguage languageContainer = new TranslatorLanguage(); - String path = MarginallyCleverTranslationXmlFileHelper.getDefaultLanguageFilePath(); - logger.debug("default path requested: {}", path); - URL pathFound = Translator.class.getClassLoader().getResource(path); - logger.debug("path found: {}", pathFound); - try (InputStream s = pathFound.openStream()) { - languageContainer.loadFromInputStream(s); - } catch (IOException ie) { - logger.error(ie.getMessage()); - } - languages.put(languageContainer.getName(), languageContainer); - } - } - - private static Stream getLanguagePaths() throws Exception { - URI uri = Translator.class.getClassLoader().getResource(WORKING_DIRECTORY).toURI(); - logger.trace("Looking for translations in {}", uri.toString()); - - Path myPath; - if (uri.getScheme().equals("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()); - myPath = fileSystem.getPath(WORKING_DIRECTORY); - } else { - myPath = Paths.get(uri); - } - - Path rootPath = FileSystems.getDefault().getPath(FileAccess.getWorkingDirectory()); - logger.trace("rootDir={}", rootPath); - - // we'll look inside the JAR file first, then look in the working directory. - // this way new translation files in the working directory will replace the old JAR files. - Stream walk = Stream.concat( - Files.walk(myPath, 1), // check inside the JAR file. - Files.walk(rootPath,1) // then check the working directory - ); - - return walk; - } - - - private static boolean attemptToLoadLanguageXML(String name) throws Exception { - InputStream stream; - String actualFilename; - - File externalFile = new File(name); - if(externalFile.exists()) { - stream = new FileInputStream(name); - actualFilename = name; - } else { - String nameInsideJar = WORKING_DIRECTORY+"/"+FilenameUtils.getName(name); - stream = Translator.class.getClassLoader().getResourceAsStream(nameInsideJar); - actualFilename = "Jar:"+nameInsideJar; - } - if( stream == null ) return false; - - logger.trace("Found {}", actualFilename); - TranslatorLanguage lang = new TranslatorLanguage(); - try { - lang.loadFromInputStream(stream); - } catch(Exception e) { - logger.error("Failed to load {}", actualFilename, e); - // if the xml file is invalid then an exception can occur. - // make sure lang is empty in case of a partial-load failure. - lang = new TranslatorLanguage(); - } - - stream.close(); - - if( !lang.getName().isEmpty() && - !lang.getAuthors().isEmpty()) { - // we loaded a language file that seems pretty legit. - languages.put(lang.getName(), lang); - return true; - } - - return false; - } - /** * @param key name of key to find in translation list. Keys must be Strings, not variables. * If you use a variable then the tests for missing and duplicate translations will not @@ -224,19 +123,19 @@ private static boolean attemptToLoadLanguageXML(String name) throws Exception { * @return the translated value for key, or "missing:key". */ public static String get(String key) { - String translation = currentLanguageContainer.get(key); - if (translation == null) { + try { + return bundle.getString(key); + } catch(Exception e) { logger.warn("Missing translation '{}' in language '{}'", key, currentLanguage); return MISSING +key; } - return translation; } /** * Translates a string and fills in some details. String contains the special character sequence "%N", where N is the n-th parameter passed to get() * A %1 is replaced with the first parameter, %2 with the second, and so on. There is no escape character. * @param key name of key to find in translation list - * @param params + * @param params the values to replace the %N with * @return the translated value for key, or "missing:key". */ public static String get(String key,String [] params) { @@ -253,14 +152,22 @@ public static String get(String key,String [] params) { * @return the list of language names */ public static String[] getLanguageList() { - final String[] choices = new String[languages.keySet().size()]; - final Object[] lang_keys = languages.keySet().toArray(); + String[] results = scanForResourceBundle(); + if(results.length == 0) { + return new String[] {"en","de"};//,"fr","ar","nl","cn" + } + return results; + } - for (int i = 0; i < lang_keys.length; ++i) { - choices[i] = (String) lang_keys[i]; + private static String[] scanForResourceBundle() { + try { + var availableLocales = findAvailableBundles("messages"); + return availableLocales.toArray(new String[0]); + } catch(Exception e) { + logger.error("Failed to load language list", e); } - return choices; + return new String [] {}; } /** @@ -268,7 +175,8 @@ public static String[] getLanguageList() { */ public static void setCurrentLanguage(String language) { currentLanguage = language; - currentLanguageContainer = languages.get(language); + var locale = Locale.forLanguageTag(language); + bundle = ResourceBundle.getBundle("messages",locale); } /** diff --git a/src/main/java/com/marginallyclever/makelangelo/TranslatorLanguage.java b/src/main/java/com/marginallyclever/makelangelo/TranslatorLanguage.java deleted file mode 100644 index 54a5eac2a..000000000 --- a/src/main/java/com/marginallyclever/makelangelo/TranslatorLanguage.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.marginallyclever.makelangelo; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; - -public class TranslatorLanguage { - private static final Logger logger = LoggerFactory.getLogger(TranslatorLanguage.class); - - private String name = ""; - private final List authors = new ArrayList<>(); - private final Map strings = new HashMap<>(); - - - /** - * @param languageFile - */ - public void loadFromString(String languageFile) { - final DocumentBuilder db = getDocumentBuilder(); - if (db == null) { - return; - } - Document dom = null; - try { - //Using factory get an instance of document builder - //parse using builder to get DOM representation of the XML file - dom = db.parse(languageFile); - } catch (SAXException | IOException e) { - logger.error("Failed to load file {}", languageFile, e); - } - if (dom == null) { - return; - } - load(dom); - } - - /** - * @param inputStream - */ - public void loadFromInputStream(InputStream inputStream) { - final DocumentBuilder db = getDocumentBuilder(); - if (db == null) { - return; - } - try { - Document dom = db.parse(inputStream); - load(dom); - } catch (SAXException | IOException e) { - logger.error("Failed to parse language file", e); - } - } - - private void load(Document dom) { - final Element docEle = dom.getDocumentElement(); - - name = docEle.getElementsByTagName("name").item(0).getFirstChild().getNodeValue(); - readAllAuthors(docEle); - readAllStrings(docEle); - } - - private void readAllAuthors(Element docEle) { - NodeList authors = docEle.getElementsByTagName("authors"); - for (int i = 0; i < authors.getLength(); i++) { - Node authorNode = authors.item(i); - if (authorNode.getNodeType() == Node.ELEMENT_NODE) { - Element authorElement = (Element) authorNode; - NodeList authorList = authorElement.getElementsByTagName("author"); - for (int j = 0; j < authorList.getLength(); j++) { - Node author = authorList.item(j); - if (author.getNodeType() == Node.ELEMENT_NODE) { - Element authorElement2 = (Element) author; - this.authors.add(authorElement2.getFirstChild().getNodeValue()); - } - } - } - } - } - - /** - * read all key/value pairs from the xml file. - * @param docEle the root element of the xml file - */ - private void readAllStrings(Element docEle) { - NodeList nl = docEle.getElementsByTagName("string"); - for (int i = 0; i < nl.getLength(); i++) { - - //get the element - Element el = (Element) nl.item(i); - String key = getTextValue(el, "key"); - String value = getTextValue(el, "value"); - - // store key/value pairs into a map - //logger.debug(language_file +"\t"+key+"\t=\t"+value); - strings.put(key, value); - } - } - - private DocumentBuilder getDocumentBuilder() { - DocumentBuilder db = null; - try { - db = buildDocumentBuilder().newDocumentBuilder(); - } catch (ParserConfigurationException e) { - logger.error("Failed to create a new document", e); - } - return db; - } - - private DocumentBuilderFactory buildDocumentBuilder() { - return DocumentBuilderFactory.newInstance(); - } - - public String get(String key) { - return strings.get(key); - } - - - /** - *

- * When a newline character "\n" was being read in from an xml file, - * it was being escaped ("\\n") and thus not behaving as an actual newline. - * This method replaces any "\\n" with "\n". - *

- *

- *

- * I take a xml element and the tag name, look for the tag and get - * the text content - * i.e for John xml snippet if - * the Element points to employee node and tagName is 'name' I will return John - *

- * - * @param ele XML element - * @param tagName name of 'tag' or child XML element of ele - * @return text value of tagName - */ - private String getTextValue(Element ele, String tagName) { - String textVal = null; - NodeList nl = ele.getElementsByTagName(tagName); - if (nl.getLength() > 0) { - Element el = (Element) nl.item(0); - // to allow empty value as translation - final Node firstChild = el.getFirstChild(); - if ( firstChild != null){ - textVal = firstChild.getNodeValue(); - }else{ - textVal = ""; - } - } - textVal = textVal.replace("\\n", "\n"); - return textVal; - } - - public String getName() { - return name; - } - - public List getAuthors() { - return authors; - } - - public Set getKeys() { - // return a copy of strings - return strings.keySet(); - } -} diff --git a/src/main/java/com/marginallyclever/makelangelo/applicationsettings/MetricsPreferences.java b/src/main/java/com/marginallyclever/makelangelo/applicationsettings/MetricsPreferences.java index ef947dd15..206025331 100644 --- a/src/main/java/com/marginallyclever/makelangelo/applicationsettings/MetricsPreferences.java +++ b/src/main/java/com/marginallyclever/makelangelo/applicationsettings/MetricsPreferences.java @@ -3,8 +3,10 @@ import com.marginallyclever.makelangelo.Translator; import com.marginallyclever.makelangelo.select.SelectBoolean; import com.marginallyclever.makelangelo.select.SelectPanel; +import com.marginallyclever.makelangelo.select.SelectReadOnlyText; import com.marginallyclever.util.PreferencesHelper; +import javax.swing.text.JTextComponent; import java.util.prefs.Preferences; public class MetricsPreferences { @@ -15,11 +17,15 @@ public class MetricsPreferences { static public SelectPanel buildPanel() { panel = new SelectPanel(); + + var aboutHtml = Translator.get("MetricsPreferences.collectAnonymousMetrics"); + final JTextComponent bottomText = SelectReadOnlyText.createJEditorPaneWithHyperlinkListenerAndToolTipsForDesktopBrowse(aboutHtml); + Preferences prefs = PreferencesHelper.getPreferenceNode(PreferencesHelper.MakelangeloPreferenceKey.METRICS); - collectAnonymousMetricsCheckbox = new SelectBoolean("collect", - Translator.get("MetricsPreferences.collectAnonymousMetrics"), + collectAnonymousMetricsCheckbox = new SelectBoolean("collect", "", prefs.getBoolean(COLLECT_ANONYMOUS_METRICS_LABEL, false)); + panel.add(bottomText); panel.add(collectAnonymousMetricsCheckbox); return panel; diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloDropTarget.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloDropTarget.java new file mode 100644 index 000000000..3de057bf8 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloDropTarget.java @@ -0,0 +1,110 @@ +package com.marginallyclever.makelangelo.donatelloimpl; + +import com.marginallyclever.donatello.Donatello; +import com.marginallyclever.donatello.Filename; +import com.marginallyclever.donatello.nodes.images.LoadImage; +import com.marginallyclever.makelangelo.donatelloimpl.nodes.LoadTurtle; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.imageio.ImageIO; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.io.File; +import java.util.List; + +/** + * Allows the user to drag and drop a file onto the {@link Donatello} panel. + */ +public class DonatelloDropTarget extends DropTargetAdapter { + private static final Logger logger = LoggerFactory.getLogger(DonatelloDropTarget.class); + private final Donatello donatello; + + public DonatelloDropTarget(Donatello donatello) { + super(); + this.donatello = donatello; + } + + @Override + public void drop(DropTargetDropEvent dtde) { + try { + Transferable tr = dtde.getTransferable(); + DataFlavor[] flavors = tr.getTransferDataFlavors(); + for (DataFlavor flavor : flavors) { + logger.debug("Possible flavor: {}", flavor.getMimeType()); + if (flavor.isFlavorJavaFileListType()) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + Object o = tr.getTransferData(flavor); + if (o instanceof List list && !list.isEmpty()) { + o = list.getFirst(); + if (o instanceof File file) { + Node n = loadFile(file.getAbsolutePath()); + if(n != null) { + var sp = donatello.getPaintArea().getMousePosition(); + // convert the mouse position to donatello view coordinates. + var wp = donatello.getPaintArea().transformScreenToWorldPoint(sp); + n.setPosition(wp); + } + + dtde.dropComplete(true); + return; + } + } + } + } + logger.debug("Drop failed: {}", dtde); + dtde.rejectDrop(); + } catch (Exception e) { + logger.error("Drop error", e); + dtde.rejectDrop(); + } + } + + private Node loadFile(String absolutePath) { + // determine if the file is an image or a turtle + try { + // Load the file and if it doesn't fail then it's probably an image. + // If it's stupid and it works... it's not that stupid. + if (ImageIO.read(new File(absolutePath)) != null) { + return loadImage(absolutePath); + } + } catch (Exception ignored) {} + + return loadTurtle(absolutePath); + } + + /** + * In Donatello add a {@link LoadImage} with the given file. Assumes {@link LoadImage} can load the file. + * @param absPath The absolute path to the file. + */ + private Node loadImage(String absPath) { + LoadImage loadImage = new LoadImage(); + var first = loadImage.getVariable(0); + if(!(first instanceof Input inputFile)) throw new IllegalStateException("First variable is not an Input"); + if(!(inputFile.getValue() instanceof Filename)) throw new IllegalStateException("Input value is not a Filename"); + donatello.getGraph().add(loadImage); + inputFile.setValue(new Filename(absPath)); + donatello.submit(loadImage); + return loadImage; + } + + /** + * In Donatello add a {@link LoadTurtle} with the given file. Assumes {@link LoadTurtle} can load the file. + * @param absPath The absolute path to the file. + */ + private Node loadTurtle(String absPath) { + LoadTurtle loadTurtle = new LoadTurtle(); + var first = loadTurtle.getVariable(0); + if(!(first instanceof Input inputFile)) throw new IllegalStateException("First variable is not an Input"); + if(!(inputFile.getValue() instanceof Filename)) throw new IllegalStateException("Input value is not a Filename"); + donatello.getGraph().add(loadTurtle); + inputFile.setValue(new Filename(absPath)); + donatello.submit(loadTurtle); + return loadTurtle; + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloRegistry.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloRegistry.java new file mode 100644 index 000000000..3d2b91082 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/DonatelloRegistry.java @@ -0,0 +1,35 @@ +package com.marginallyclever.makelangelo.donatelloimpl; + +import com.marginallyclever.makelangelo.donatelloimpl.nodes.TurtleDAO4JSON; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.DAO4JSONFactory; +import com.marginallyclever.nodegraphcore.DAORegistry; +import com.marginallyclever.nodegraphcore.NodeFactory; +import com.marginallyclever.nodegraphcore.NodeRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Register custom nodes for {@link Turtle}s. + * @author Dan Royer + * @since 2022-02-01 + */ +public class DonatelloRegistry implements DAORegistry, NodeRegistry { + private static final Logger logger = LoggerFactory.getLogger(DonatelloRegistry.class); + + public String getName() { + return "Makelangelo"; + } + + @Override + public void registerDAO() { + logger.info("Registering makelangelo-software DAOs"); + DAO4JSONFactory.registerDAO(Turtle.class,new TurtleDAO4JSON()); + } + + @Override + public void registerNodes() { + logger.info("Registering makelangelo-software nodes"); + NodeFactory.registerAllNodesInPackage("com.marginallyclever.makelangelo.donatelloimpl"); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/AddTurtles.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/AddTurtles.java new file mode 100644 index 000000000..17a945208 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/AddTurtles.java @@ -0,0 +1,31 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + +/** + * Add two {@link Turtle}s together. + */ +public class AddTurtles extends Node { + private final Input turtleA = new Input<>("A", Turtle.class, new Turtle()); + private final Input turtleB = new Input<>("B", Turtle.class, new Turtle()); + private final Output output = new Output<>("output", Turtle.class, new Turtle()); + + public AddTurtles() { + super("AddTurtles"); + addVariable(turtleA); + addVariable(turtleB); + addVariable(output); + } + + @Override + public void update() { + Turtle a = turtleA.getValue(); + Turtle b = turtleB.getValue(); + Turtle sum = new Turtle(a); + sum.add(b); + output.send(sum); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Canvas.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Canvas.java new file mode 100644 index 000000000..eea02a703 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Canvas.java @@ -0,0 +1,51 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.PrintWithGraphics; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +import java.awt.*; + +/** + * A node that creates a canvas with a given size and color. + */ +public class Canvas extends Node implements PrintWithGraphics { + private final Input width = new Input<>("width", Number.class,1); + private final Input height = new Input<>("height", Number.class,1); + private final Input color = new Input<>("color", Color.class,Color.WHITE); + private final Output outx = new Output<>("x", Number.class,0); + private final Output outy = new Output<>("y", Number.class,0); + private final Output outw = new Output<>("width", Number.class,width.getValue()); + private final Output outh = new Output<>("height", Number.class,height.getValue()); + + public Canvas() { + super("Canvas"); + addVariable(width); + addVariable(height); + addVariable(color); + addVariable(outx); + addVariable(outy); + addVariable(outw); + addVariable(outh); + } + + @Override + public void update() { + var w = Math.max(1,width.getValue().intValue()); + var h = Math.max(1,height.getValue().intValue()); + outx.send(0); + outy.send(0); + outw.send(w); + outh.send(h); + } + + + @Override + public void print(Graphics g) { + var w = Math.max(1,width.getValue().intValue()); + var h = Math.max(1,height.getValue().intValue()); + g.setColor(color.getValue()); + g.fillRect(0,0,w,h); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/ColorTurtle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/ColorTurtle.java new file mode 100644 index 000000000..c230c8ae0 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/ColorTurtle.java @@ -0,0 +1,48 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.MovementType; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtleMove; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + +import java.awt.*; + +/** + * Change the color of a {@link Turtle}. + */ +public class ColorTurtle extends Node { + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Input red = new Input<>("red",Number.class,0); + private final Input green = new Input<>("green",Number.class,0); + private final Input blue = new Input<>("blue",Number.class,0); + private final Output output = new Output<>("output", Turtle.class,new Turtle()); + + public ColorTurtle() { + super("ColorTurtle"); + addVariable(turtle); + addVariable(red ); + addVariable(green); + addVariable(blue ); + addVariable(output); + } + + @Override + public void update() { + Turtle input = turtle.getValue(); + int r = red.getValue().intValue(); + int g = green.getValue().intValue(); + int b = blue.getValue().intValue(); + Color c = new Color(r, g, b); + Turtle moved = new Turtle(); + for( TurtleMove m : input.history ) { + if(m.type== MovementType.TOOL_CHANGE) { + moved.history.add(new TurtleMove(c.hashCode(),m.getDiameter(),MovementType.TOOL_CHANGE)); + } else { + moved.history.add(new TurtleMove(m)); + } + } + output.send(moved); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/FlowField.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/FlowField.java new file mode 100644 index 000000000..93ec900f6 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/FlowField.java @@ -0,0 +1,123 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; +import java.util.Arrays; + +/** + * Use a bitmap intensity to control the flow field. + */ +public class FlowField extends Node { + private final Input inputImage = new Input<>("inputImage",BufferedImage.class, new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB)); + private final Input spacingValue = new Input<>("spacing",Number.class,10); + private final Input stepValue = new Input<>("step size",Number.class,3); + private final Input numStepsValue = new Input<>("step count",Number.class,3); + private final Input startAngle = new Input<>("start angle",Number.class,0); + private final Output result = new Output<>("result",Turtle.class,new Turtle()); + + public FlowField() { + super("FlowField"); + addVariable(inputImage); + addVariable(spacingValue); + addVariable(stepValue); + addVariable(numStepsValue); + addVariable(startAngle); + addVariable(result); + } + + @Override + public void update() { + var img = inputImage.getValue(); + var turtle = new Turtle(); + int step = Math.max(1,stepValue.getValue().intValue()); + int numSteps = Math.max(1,numStepsValue.getValue().intValue()); + double angle = startAngle.getValue().doubleValue(); + FlowDrawer drawer = new FlowDrawer(img, turtle, step, angle); + + // move in a grid over the image and generate a flow field + int spacing = Math.max(1,spacingValue.getValue().intValue()); + var w = img.getWidth(); + var h = img.getHeight(); + + setComplete(0); + for (int y = 0; y <= h; y+=spacing) { + for (int x = 0; x <= w; x+=spacing) { + drawer.draw(x, y, numSteps); + } + setComplete(100*y/h); + } + + setComplete(100); + result.send(turtle); + } + + /** + * Uses a {@link Turtle} to draws one flow line of a flow field based on the intensity of a {@link BufferedImage}. + */ + static class FlowDrawer { + private final Turtle turtle; + private final int [] pixel; + private final double strideLength; + private final WritableRaster raster; + private final double startDegrees; + + /** + * @param img the image to read + * @param turtle the turtle to draw with + * @param strideLength the distance to move each step + * @param startDegrees the starting angle offset + */ + public FlowDrawer(BufferedImage img, Turtle turtle, double strideLength, double startDegrees) { + this.turtle = turtle; + this.strideLength = strideLength; + this.startDegrees = startDegrees; + + ColorModel cm = img.getColorModel(); + raster = img.getRaster(); + + int numComponents = cm.getNumComponents(); + pixel = new int[numComponents]; + } + + /** + * Draw a flow line starting at x,y + * @param x the x coordinate + * @param y the y coordinate + * @param numSteps the number of steps to draw + */ + public void draw(double x, double y, int numSteps) { + turtle.jumpTo(x, y); + for(int i=0;i width = new Input<>("width", Number.class, 256); + private final Input height = new Input<>("height", Number.class, 256); + private final Input style = new Input<>("style", Number.class, 0); + private final Input seed = new Input<>("seed", Number.class, 0); + private final Input scaleX = new Input<>("scale x", Number.class, 0.05); + private final Input scaleY = new Input<>("scale y", Number.class, 0.05); + private final Input translateX = new Input<>("translate x", Number.class, 0); + private final Input translateY = new Input<>("translate y", Number.class, 0); + private final Output output = new Output<>("output", BufferedImage.class, new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB)); + + public GradientNoise() { + super("GradientNoise"); + addVariable(width); + addVariable(height); + addVariable(style); + addVariable(seed); + addVariable(scaleX); + addVariable(scaleY); + addVariable(translateX); + addVariable(translateY); + addVariable(output); + } + + @Override + public void update() { + int w = Math.max(1,width.getValue().intValue()); + int h = Math.max(1,height.getValue().intValue()); + int noiseStyle = Math.min(NoiseFactory.getNames().length-1,Math.max(0,style.getValue().intValue())); + double tx = translateX.getValue().doubleValue(); + double ty = translateY.getValue().doubleValue(); + double sx = scaleX.getValue().doubleValue(); + double sy = scaleY.getValue().doubleValue(); + + Noise noise = NoiseFactory.getNoise(noiseStyle); + assert(noise!=null); + + BufferedImage img = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB); + var raster = img.getRaster(); + var count = img.getColorModel().getNumComponents(); + // pixel buffer + int [] pixels = new int[count]; + pixels[3] = 255; + + for(int y=0;y image = new Input<>("image", BufferedImage.class,new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB)); + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Input stepSize = new Input<>("stepSize", Number.class, 5); + private final Input thickness = new Input<>("thickness", Number.class, 5); + private final Output result = new Output<>("result", Turtle.class,new Turtle()); + + private static final LinkedList unsorted = new LinkedList<>(); + + // segments sorted for drawing efficiency + private static final List sortedLines = new ArrayList<>(); + private TransformedImage sourceImage = null; + + public LineWeightByImage() { + super("LineWeightByImage"); + addVariable(image); + addVariable(turtle); + addVariable(stepSize); + addVariable(result); + addVariable(thickness); + } + + @Override + public void update() { + Turtle myTurtle = turtle.getValue(); + if(myTurtle==null || myTurtle.history.isEmpty()) return; + + sourceImage = new TransformedImage(image.getValue()); + sourceImage.setScale(1,1); + sourceImage.setTranslation(0,0); + Turtle turtle = new Turtle(); + List colors = myTurtle.splitByToolChange(); + setComplete(0); + for( Turtle t2 : colors ) { + setComplete((int)(100.0*colors.indexOf(t2)/colors.size())); + turtle.add(calculate(t2)); + } + + setComplete(100); + result.send(turtle); + } + + private Turtle calculate(Turtle from) { + Turtle turtle = new Turtle(); + buildSegmentList(from); + sortSegmentsIntoLines(); + generateThickLines(turtle); + //generateThinLines(turtle); + + // clean up + unsorted.clear(); + sortedLines.clear(); + return turtle; + } + + /** + * Generate thin lines for debugging + * @param turtle the turtle to draw with + */ + private void generateThinLines(Turtle turtle) { + logger.debug("generateThinLines {}",sortedLines.size()); + for(LineWeight line : sortedLines) { + boolean first=true; + for(LineWeightSegment w : line.segments) { + if (first) { + turtle.jumpTo(w.start.x, w.start.y); + first = false; + } + turtle.moveTo(w.end.x, w.end.y); + } + } + } + + private void generateThickLines(Turtle turtle) { + logger.debug("generateThickLines"); + for(LineWeight i : sortedLines) { + if(i.segments.isEmpty()) continue; + generateOneThickLine(turtle,i); + } + } + + private void generateOneThickLine(Turtle turtle, LineWeight line) { + // find the thickest part of the line, which tells us how many cycles we'll have to make. + double numPasses=0; + for(LineWeightSegment s : line.segments) { + numPasses = Math.max(numPasses,s.weight); + } + numPasses = Math.max(1,Math.ceil(numPasses)); + + LineWeightSegment start = line.segments.get(0); + + boolean first=true; + // collect all the points, write them at the end. + for(int pass=0; pass<=numPasses; ++pass) { + double ratio = pass/numPasses; + List offsetLine = generateOneThickLinePass(line,start,ratio); + if((pass%2)==1) Collections.reverse(offsetLine); + + // draw pass + for( Point2D p : offsetLine ) { + if(first) { + turtle.jumpTo(p.x,p.y); + first=false; + } + turtle.moveTo(p.x,p.y); + } + } + } + + private List generateOneThickLinePass(LineWeight line,LineWeightSegment start,double distance) { + List offsetSequence = new ArrayList<>(); + + // add first point at start of line + double [] s0 = getOffsetLine(start, adjustedOffset(start.weight,distance)); + + Vector2d unit = line.segments.get(0).getUnit(); + unit.scale(distance); + offsetSequence.add(new Point2D(s0[0]-unit.x,s0[1]-unit.y)); + + // add the middle points of the line + for(int i=1;i6 || Math.abs(head.iy-next.iy)>6) return false; + if(closeEnough(head.start,next.end)) return true; + if(closeEnough(head.start,next.start)) { + // next is backwards + next.flip(); + return true; + } + return false; + } + + /** + * @param tail the first line + * @param next the second line + * @return true if {@link LineWeightSegment} tail and next are in sequence. + */ + private boolean closeEnoughToTail(LineWeightSegment tail, LineWeightSegment next) { + if(next==null) { + throw new IllegalArgumentException("next is null"); + } + // fast reject if truchet index too far apart + if(Math.abs(tail.ix-next.ix)>2 || Math.abs(tail.iy-next.iy)>2) return false; + if(closeEnough(tail.end,next.start)) return true; + if(closeEnough(tail.end,next.end)) { + next.flip(); + return true; + } + return false; + } + + boolean closeEnough(Point2D p0,Point2D p1) { + return p0.distanceSquared(p1) filename = new Input<>("filename",Filename.class,new Filename("")); + private final Output contents = new Output<>("contents", Turtle.class, new Turtle()); + private final Output w = new Output<>("width", Number.class, 0); + private final Output h = new Output<>("height", Number.class, 0); + private final Output length = new Output<>("length", Number.class, 0); + + + public LoadTurtle() { + super("LoadTurtle"); + addVariable(filename); + addVariable(contents); + addVariable(w); + addVariable(h); + } + + @Override + public void update() { + try { + Turtle t = TurtleFactory.load(filename.getValue().get()); + contents.send(t); + Rectangle2D r = t.getBounds(); + w.send(r.getWidth()); + h.send(r.getHeight()); + length.send(t.getDrawDistance()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PathImageMask.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PathImageMask.java new file mode 100644 index 000000000..07cc192d9 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PathImageMask.java @@ -0,0 +1,148 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.convenience.*; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * Use an image to mask a path. Lay the path over the image and remove all parts of the path where the image is brighter + * than a cutoff value. The fine grain resolution (and the amount of testing) is controlled by the stepSize. + * @author Dan Royer + * @since 2022-03-08 + */ +public class PathImageMask extends Node { + private final Input image = new Input<>("image", BufferedImage.class,new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB)); + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Input stepSize = new Input<>("stepSize", Number.class, 5); + private final Input threshold = new Input<>("threshold", Number.class, 128); + private final Output outputAbove = new Output<>("above", Turtle.class,new Turtle()); + private final Output outputBelow = new Output<>("below", Turtle.class,new Turtle()); + + private final LineCollection listAbove = new LineCollection(); + private final LineCollection listBelow = new LineCollection(); + + public PathImageMask() { + super("PathImageMask"); + addVariable(image); + addVariable(turtle); + addVariable(stepSize); + addVariable(threshold); + addVariable(outputAbove); + addVariable(outputBelow); + } + + @Override + public void update() { + Turtle myTurtle = turtle.getValue(); + if(myTurtle==null || myTurtle.history.isEmpty()) return; + + LineCollection lines = myTurtle.getAsLineSegments(); + BufferedImage src = image.getValue(); + + listAbove.clear(); + listBelow.clear(); + + double s = Math.max(1, stepSize.getValue().doubleValue()); + double c = Math.max(0,Math.min(255, threshold.getValue().doubleValue())); + + for(LineSegment2D line : lines) { + scanLine(src,line,s,c); + } + + Turtle resultAbove = new Turtle(); + resultAbove.addLineSegments(listAbove); + outputAbove.send(resultAbove); + + Turtle resultBelow = new Turtle(); + resultBelow.addLineSegments(listBelow); + outputBelow.send(resultBelow); + } + + /** + * Drag the pen across the paper from seg.start to seg.end, taking stepSize steps. If the + * intensity of img at a step is less than or equal to the channelCutoff, keep the step. Results will be in the + * {@link #listAbove} and {@link #listBelow}. + * + * @param img the image to sample while converting along the line. + * @param segment the line to walk. + * @param stepSize millimeters level of detail for this line. + * @param channelCutoff only put pen down when color below this amount. + */ + private void scanLine(BufferedImage img, LineSegment2D segment, double stepSize, double channelCutoff) { + Point2D P0 = segment.start; + Point2D P1 = segment.end; + + LineCollection toKeep = new LineCollection(); + + // clip line to image bounds because sampling outside limits causes exception. + Point2D rMin = new Point2D(0,0); + Point2D rMax = new Point2D(img.getWidth(),img.getHeight()); + if(!Clipper2D.clipLineToRectangle(P0, P1, rMax, rMin)) { + // entire line clipped + return; + } + + // walk the line + double dx = P1.x - P0.x; + double dy = P1.y - P0.y; + double distance = Math.sqrt(dx*dx+dy*dy); + double total = Math.min(1,Math.ceil(distance / stepSize)); + Point2D a = P0; + + for( double i = 1; i <= total; ++i ) { + double fraction = i / total; + Point2D b = new Point2D(dx * fraction + P0.x,dy * fraction + P0.y); + double sampleResult = sampleImageUnderStep(img,a,b); + if(sampleResult < channelCutoff) { + listBelow.add(new LineSegment2D(a,b, Color.BLACK)); + } else { + listAbove.add(new LineSegment2D(a,b, Color.BLACK)); + } + a = b; + } + + // TODO run a mini-merge to reduce the number of new segments? + } + + /** + * Returns the average intensity of the image within the rectangle bounded by points a and b. + * @param img the source image + * @param a one corner of the rectangle. + * @param b one corner of the rectangle. + * @return the average intensity of the image within the rectangle bounded by points a and b. + */ + private double sampleImageUnderStep(BufferedImage img, Point2D a, Point2D b) { + // find the top-left and bottom-right corners + int left = (int)Math.floor(Math.min(a.x,b.x)); + int right = (int)Math.ceil(Math.max(a.x,b.x)); + int bottom = (int)Math.floor(Math.min(a.y,b.y)); + int top = (int)Math.ceil(Math.max(a.y,b.y)); + double total = Math.max(1,(right-left) * (top-bottom)); + // get the average of the intensities + double sum = 0; + for(int y=bottom; y0xRRGGBB format. + * @return the average of the red, green, and blue color channels. + */ + private double intensity(int rgb) { + double r = (rgb >> 16) & 0xff; + double g = (rgb >> 8) & 0xff; + double b = (rgb ) & 0xff; + return ( r + g + b ) / 3.0; + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PatternOnPath.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PatternOnPath.java new file mode 100644 index 000000000..304f00ce6 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PatternOnPath.java @@ -0,0 +1,48 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtlePathWalker; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +/** + * Place a pattern on a path. + */ +public class PatternOnPath extends Node { + private final Input pattern = new Input<>("pattern", Turtle.class, new Turtle()); + private final Input path = new Input<>("path", Turtle.class, new Turtle()); + private final Input count = new Input<>("count", Number.class, 10); + private final Output output = new Output<>("output", Turtle.class, new Turtle()); + + public PatternOnPath() { + super("PatternOnPath"); + addVariable(pattern); + addVariable(path); + addVariable(count); + addVariable(output); + } + + @Override + public void update() { + Turtle sum = new Turtle(); + Turtle myPattern = pattern.getValue(); + Turtle myPath = path.getValue(); + int c = count.getValue().intValue(); + if(c>0) { + TurtlePathWalker walker = new TurtlePathWalker(myPath); + double pDistance = walker.getDrawDistance(); + double step = (pDistance==0) ? 1 : pDistance/(double)c; + while(!walker.isDone()) { + Point2D p = walker.walk(step); + Turtle stamp = new Turtle(myPattern); + stamp.translate(p.x,p.y); + sum.add(stamp); + setComplete((int)(100*walker.getTSum()/pDistance)); + } + } + setComplete(100); + output.send(sum); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PointOnPath.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PointOnPath.java new file mode 100644 index 000000000..0d463737c --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PointOnPath.java @@ -0,0 +1,68 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtlePathWalker; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + + +/** + *

(px,py) = path(index), where path(0) is the start and path(path.length) is the end.

+ *

(nx,ny) = the approximate normal at path(index). This is approximated by finding

+ *
normalize(path(index+epsilon) - path(index))
+ *

for some very small epsilon, and taking into account the start and end of the path.

+ *

If the path is of zero-length then (0,0) will be generated.

+ *

path.length can be obtained from LoadTurtle.

+ */ +public class PointOnPath extends Node { + private final Input path = new Input<>("path", Turtle.class, new Turtle()); + private final Input index = new Input<>("index", Number.class, 0); + private final Output px = new Output<>("px", Number.class, 0); + private final Output py = new Output<>("py", Number.class, 0); + private final Output nx = new Output<>("nx", Number.class, 0); + private final Output ny = new Output<>("ny", Number.class, 0); + + public PointOnPath() { + super("PointOnPath"); + addVariable(path); + addVariable(px); + addVariable(py); + addVariable(nx); + addVariable(ny); + } + + private static final double EPSILON=0.00001; + + @Override + public void update() { + Turtle myPath = path.getValue(); + double total = myPath.getDrawDistance(); + double c0 = index.getValue().doubleValue(); + if(total==0 || c0 <= 0) { + px.send(0); + px.send(0); + nx.send(1); + ny.send(0); + return; + } + + double c1 = c0 + EPSILON; + if(c1>total) { + c1 = total; + c0 = total - EPSILON; + } + TurtlePathWalker walker = new TurtlePathWalker(myPath); + Point2D p0 = walker.walk(c0); + Point2D p1 = walker.walk(c1-c0); + double dx = p1.x - p0.x; + double dy = p1.y - p0.y; + Point2D n = new Point2D(dx,dy); + n.normalize(); + px.send(p0.x); + px.send(p0.y); + nx.send(n.x); + ny.send(n.y); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PrintTurtle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PrintTurtle.java new file mode 100644 index 000000000..5983c71cb --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/PrintTurtle.java @@ -0,0 +1,243 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.donatello.graphview.GraphViewPanel; +import com.marginallyclever.makelangelo.turtle.MovementType; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtleMove; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.PrintWithGraphics; +import com.marginallyclever.nodegraphcore.port.Input; + +import java.awt.*; +import java.util.List; +import java.util.ArrayList; + +/** + *

Print the {@link Turtle}'s path behind the {@link Node}s.

+ *

On {@link #update()} pass over the {@link Turtle} once and build a list of polylines for faster rendering. + * This is done using a {@link PolylineBuilder} which also optimizes to remove points on nearly straight lines.

+ */ +public class PrintTurtle extends Node implements PrintWithGraphics { + //private static final Logger logger = LoggerFactory.getLogger(PrintTurtle.class); + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Input px = new Input<>("X",Number.class,0); + private final Input py = new Input<>("Y",Number.class,0); + private final Input showTravel = new Input<>("show travel",Boolean.class,false); + private final Input travelColor = new Input<>("travel color",Color.class,Color.GREEN); + private final Input lineThickness = new Input<>("line thickness",Number.class,1); + private final List polylines = new ArrayList<>(); + + /** + * A poly line to draw. + */ + static class Polyline { + private final int[] x; + private final int[] y; + private final int n; + private final Color color; + + public Polyline(int[] x, int[] y, int n, Color color) { + if(x.length!=y.length) throw new IllegalArgumentException("x and y must be the same length"); + if(n=this.x.length) { + // grow the buffer if needed. + int[] newX = new int[this.x.length*2]; + int[] newY = new int[this.y.length*2]; + System.arraycopy(this.x,0,newX,0,n); + System.arraycopy(this.y,0,newY,0,n); + this.x = newX; + this.y = newY; + } + this.x[n] = x; + this.y[n] = y; + n++; + } + + /** + * Build a {@link Polyline} with the given color. This is the same as calling {@link #compile(Color, int)} + * with a deviation of 10 degrees. + * @param color the color of the line. + * @return a {@link Polyline} with the given color. + */ + public Polyline compile(Color color) { + return compile(color,10); + } + + /** + * Build a {@link Polyline} with the given color. Remove any points that form a nearly straight line. + * @param color the color of the line. + * @param deviationDegrees the maximum deviation in degrees between two lines to be considered a straight line. + * @return a {@link Polyline} with the given color. + */ + public Polyline compile(Color color, int deviationDegrees) { + if(n<3) { + return new Polyline(x.clone(), y.clone(), n, color); + } + + // examine the buffers and remove any points that form a nearly straight line. + // use a dot product to determine if the angle between the two lines is less than `maxDeviation` degrees. + var nx = new int[n]; + var ny = new int[n]; + int j=0; + nx[j] = x[0]; + ny[j] = y[0]; + j++; + + var maxDeviation = Math.toRadians(deviationDegrees); + var x0 = x[0]; + var y0 = y[0]; + var x1 = x[1]; + var y1 = y[1]; + for(int i=2;imaxDeviation) { + // otherwise save point 1 + nx[j] = x1; + ny[j] = y1; + j++; + x0 = x1; + y0 = y1; + } + // move on to the next point. + x1 = x2; + y1 = y2; + } + nx[j] = x1; + ny[j] = y1; + j++; + //if(j p.draw(g2)); + } + + private void generatePolylines(Turtle myTurtle) { + int size = myTurtle.history.size(); + int count = 0; + + setComplete(0); + + // where we're at in the drawing (to check if we're between first & last) + boolean showPenUp = showTravel.getValue(); + TurtleMove previousMove = null; + + Color upColor = travelColor.getValue(); + Color downColor = new Color(0,0,0); + PolylineBuilder builder = new PolylineBuilder(); + builder.add(0,0); + try { + for (TurtleMove m : myTurtle.history) { + if(m==null) throw new NullPointerException(); + if(m.type == MovementType.TOOL_CHANGE) { + downColor = m.getColor(); + continue; + } + if ( previousMove != null) { + if( previousMove.type != m.type ) { + polylines.add(builder.compile(previousMove.type == MovementType.TRAVEL ? upColor : downColor)); + builder.clear(); + builder.add((int) previousMove.x, (int) previousMove.y); + } + if ((m.type == MovementType.TRAVEL && showPenUp) || m.type == MovementType.DRAW_LINE) { + builder.add((int) m.x, (int) m.y); + } + } + previousMove = m; + setComplete((int) (100.0 * count++ / size)); + } + if(builder.n>0 && previousMove!=null) { + polylines.add(builder.compile(previousMove.type == MovementType.TRAVEL ? upColor : downColor)); + } + } + catch(Exception ignored) {} + setComplete(100); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/SaveTurtle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/SaveTurtle.java new file mode 100644 index 000000000..1d02a6c58 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/SaveTurtle.java @@ -0,0 +1,41 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings; +import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettingsManager; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.makelangelo.makeart.io.TurtleFactory; +import com.marginallyclever.makelangelo.turtle.Turtle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Save a {@link Turtle} to a file. + */ +public class SaveTurtle extends Node { + + private static final Logger logger = LoggerFactory.getLogger(SaveTurtle.class); + + private final Input filename = new Input<>("filename",String.class,null); + private final Output turtle = new Output<>("turtle", Turtle.class,new Turtle()); + + public SaveTurtle() { + super("SaveTurtle"); + addVariable(filename); + addVariable(turtle); + } + + @Override + public void update() { + if(filename.getValue().isEmpty()) return; + + try { + PlotterSettings settings = PlotterSettingsManager.buildMakelangelo5(); + TurtleFactory.save(turtle.getValue(),filename.getValue(),settings); + } catch (Exception e) { + logger.warn("Failed to update, ignoring", e); + } + } + +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Spiral.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Spiral.java new file mode 100644 index 000000000..b0d900877 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/Spiral.java @@ -0,0 +1,76 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtlePathWalker; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +/** + * Warp an existing path into a spiral. + */ +public class Spiral extends Node { + private final Input source = new Input<>("turtle", Turtle.class, new Turtle()); + private final Input r0 = new Input<>("r0", Number.class, 1); + private final Input dr = new Input<>("dr", Number.class, 1); + private final Input stepSize = new Input<>("stepSize", Number.class, 1); + private final Output output = new Output<>("output", Turtle.class, new Turtle()); + + public Spiral() { + super("Spiral"); + addVariable(source); + addVariable(r0); + addVariable(dr); + addVariable(stepSize); + addVariable(output); + } + + @Override + public void update() { + try { + var r0Value = r0.getValue().doubleValue(); + var drValue = dr.getValue().doubleValue(); + var turtle = source.getValue(); + TurtlePathWalker pathWalker = new TurtlePathWalker(turtle); + double dist = turtle.getBounds().width; + var pace = Math.max(0.0001, stepSize.getValue().doubleValue()); + + double curveSum = 0; + double px,py; + + var result = new Turtle(); + double t = 0.0; // the distance along the curve + setComplete(0); + while(!pathWalker.isDone()) { + // get the next point + var p2 = pathWalker.walk(pace); + // the x of the original path is the distance forward + var dt = p2.x-t; + t = p2.x; + // radius at this point + double rN = r0Value + drValue * (curveSum/(2*Math.PI)); + // forward direction + double nx = Math.cos(curveSum); + double ny = Math.sin(curveSum); + // position + px = rN * nx; + py = rN * ny; + // dt is the arc length of the curve between the last point and this point + // the angle represented by dt and rN is calculated as follows: + double dtAngle = dt / rN; + curveSum += dtAngle; + // the y of the original path is the distance sideways + double tx = p2.y * nx; + double ty = p2.y * ny; + + result.moveTo(px+tx,py+ty); + result.penDown(); + setComplete((int) (t / dist * 100)); + } + setComplete(100); + output.send(result); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TransformTurtle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TransformTurtle.java new file mode 100644 index 000000000..61416648a --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TransformTurtle.java @@ -0,0 +1,40 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.makelangelo.turtle.Turtle; + +/** + * Transform a {@link Turtle} by scaling, rotating, and translating it. + */ +public class TransformTurtle extends Node { + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Input sx = new Input<>("scale x",Number.class,1); + private final Input sy = new Input<>("scale y",Number.class,1); + private final Input rotate = new Input<>("rotate degrees",Number.class,0); + private final Input tx = new Input<>("translate x",Number.class,0); + private final Input ty = new Input<>("translate y",Number.class,0); + private final Output output = new Output<>("output", Turtle.class,new Turtle()); + + public TransformTurtle() { + super("TransformTurtle"); + addVariable(turtle); + addVariable(sx); + addVariable(sy); + addVariable(rotate); + addVariable(tx); + addVariable(ty); + addVariable(output); + } + + @Override + public void update() { + Turtle input = turtle.getValue(); + Turtle moved = new Turtle(input); + moved.scale(sx.getValue().doubleValue(),sy.getValue().doubleValue()); + moved.rotate(rotate.getValue().doubleValue()); + moved.translate(tx.getValue().doubleValue(),ty.getValue().doubleValue()); + output.send(moved); + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TruchetTiles.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TruchetTiles.java new file mode 100644 index 000000000..396035ee0 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TruchetTiles.java @@ -0,0 +1,69 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.makeart.truchet.TruchetTile; +import com.marginallyclever.makelangelo.makeart.truchet.TruchetTileFactory; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +/** + * Create a basic Truchet tile pattern from an image. the intensity of the image decides the tile type. + */ +public class TruchetTiles extends Node { + Input source = new Input<>("Source", BufferedImage.class, new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)); + Input spaceBetweenLines = new Input<>("Spacing", Number.class, 10); + Input linesPerTileCount = new Input<>("Qty", Number.class, 10); + Output output = new Output<>("Output", Turtle.class, new Turtle()); + + public TruchetTiles() { + super("TruchetTiles"); + addVariable(source); + addVariable(spaceBetweenLines); + addVariable(linesPerTileCount); + addVariable(output); + } + + @Override + public void update() { + var img = source.getValue(); + int space = Math.max(1,spaceBetweenLines.getValue().intValue()); + int lines = Math.max(1,linesPerTileCount.getValue().intValue()); + int tileSize = space * lines; + + try { + var c = img.getColorModel().getNumComponents(); + var raster = img.getRaster(); + int []pixels = new int[c]; + + Turtle turtle = new Turtle(); + List ttgList = new ArrayList<>(); + + for(int y=0;y128?0:1,turtle,space,lines)); + } + } + + if(!ttgList.isEmpty()) { + var i = ttgList.iterator(); + for(int y=0;y { + @Override + public Object toJSON(Object object) throws JSONException { + JSONObject json = new JSONObject(); + Turtle turtle = (Turtle)object; + // for a complete snapshot, capture all the instance details, too. + return json; + } + + @Override + public Turtle fromJSON(Object object) throws JSONException { + JSONObject json = (JSONObject)object; + Turtle turtle = new Turtle(); + // for a complete snapshot, restore all the instance details, too. + return turtle; + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToBufferedImage.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToBufferedImage.java new file mode 100644 index 000000000..3fd12ef71 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToBufferedImage.java @@ -0,0 +1,69 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtleMove; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +/** + * Convert a {@link Turtle} to a {@link BufferedImage}. + */ +public class TurtleToBufferedImage extends Node { + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Output output = new Output<>("output", BufferedImage.class, new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB)); + + public TurtleToBufferedImage() { + super("TurtleToBufferedImage"); + addVariable(turtle); + addVariable(output); + } + + @Override + public void update() { + Turtle myTurtle = turtle.getValue(); + if(myTurtle!=null && !myTurtle.history.isEmpty()) { + Rectangle2D r = myTurtle.getBounds(); + int h = (int)Math.ceil(r.getHeight()); + int w = (int)Math.ceil(r.getWidth()); + BufferedImage img = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)img.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_PURE); + g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.translate(-r.getX(),-r.getY()); + + TurtleMove previousMove = null; + Color downColor = Color.BLACK; + + for (TurtleMove m : myTurtle.history) { + if (m == null) throw new NullPointerException(); + + switch (m.type) { + case TRAVEL -> { + previousMove = m; + } + case DRAW_LINE -> { + if (previousMove != null) { + g.setColor(downColor); + g.drawLine((int) previousMove.x, (int) previousMove.y, (int) m.x, (int) m.y); + } + previousMove = m; + } + case TOOL_CHANGE -> { + downColor = m.getColor(); + g.setStroke(new BasicStroke((int) m.getDiameter())); + } + } + } + output.send(img); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToRectangle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToRectangle.java new file mode 100644 index 000000000..51bda6431 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/TurtleToRectangle.java @@ -0,0 +1,33 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.Node; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; + +import java.awt.*; +import java.awt.geom.Rectangle2D; + +/** + * Returns the bounding box of the turtle's path. + * @author Dan Royer + * @since 2022-04-14 + */ +public class TurtleToRectangle extends Node { + private final Input turtle = new Input<>("turtle", Turtle.class,new Turtle()); + private final Output output = new Output<>("output", Rectangle2D.class, new Rectangle(0,0,0,0)); + + public TurtleToRectangle() { + super("TurtleToRectangle"); + addVariable(turtle); + addVariable(output); + } + + @Override + public void update() { + Turtle myTurtle = turtle.getValue(); + if(myTurtle!=null ) { + output.send(myTurtle.getBounds()); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Circle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Circle.java new file mode 100644 index 000000000..26d68998c --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Circle.java @@ -0,0 +1,41 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes.shapes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Circle extends Node { + private static final Logger logger = LoggerFactory.getLogger(Circle.class); + + private final Input radius = new Input<>("radius", Number.class, 50); + private final Output contents = new Output<>("contents", Turtle.class, new Turtle()); + + public Circle() { + super("Circle"); + addVariable(radius); + addVariable(contents); + } + + @Override + public void update() { + try { + Turtle t = new Turtle(); + double r = radius.getValue().doubleValue()/2.0; + double circumference = Math.ceil(Math.PI*r*2.0); + t.jumpTo(r,0); + for(int i=0;i x0 = new Input<>("x0", Number.class, 0); + private final Input y0 = new Input<>("y0", Number.class, 0); + private final Input x1 = new Input<>("x1", Number.class, 1); + private final Input y1 = new Input<>("y1", Number.class, 0); + private final Output contents = new Output<>("contents", Turtle.class, new Turtle()); + + public Line() { + super("Line"); + addVariable(x0); + addVariable(y0); + addVariable(x1); + addVariable(y1); + addVariable(contents); + } + + @Override + public void update() { + try { + Turtle t = new Turtle(); + t.jumpTo(x0.getValue().doubleValue(),y0.getValue().doubleValue()); + t.moveTo(x1.getValue().doubleValue(),y1.getValue().doubleValue()); + t.penUp(); + contents.send(t); + } catch (Exception e) { + logger.warn("Failed to update, ignoring", e); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/NGon.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/NGon.java new file mode 100644 index 000000000..d695172a5 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/NGon.java @@ -0,0 +1,43 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes.shapes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NGon extends Node { + private static final Logger logger = LoggerFactory.getLogger(NGon.class); + + private final Input radius = new Input<>("radius", Number.class, 10); + private final Input steps = new Input<>("steps", Number.class, 4); + private final Output contents = new Output<>("contents", Turtle.class, new Turtle()); + + public NGon() { + super("NGon"); + addVariable(radius); + addVariable(steps); + addVariable(contents); + } + + @Override + public void update() { + try { + Turtle t = new Turtle(); + double r = radius.getValue().doubleValue(); + int s = steps.getValue().intValue(); + + t.jumpTo(r,0); + for(int i=1;i<=s;++i) { + double v = ( 2.0*Math.PI*(double)i ) / (double)s; + t.moveTo(Math.cos(v), Math.sin(v)); + } + t.penUp(); + contents.send(t); + } catch (Exception e) { + logger.warn("Failed to update, ignoring", e); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Rectangle.java b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Rectangle.java new file mode 100644 index 000000000..51d9df27a --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/donatelloimpl/nodes/shapes/Rectangle.java @@ -0,0 +1,42 @@ +package com.marginallyclever.makelangelo.donatelloimpl.nodes.shapes; + +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.port.Input; +import com.marginallyclever.nodegraphcore.port.Output; +import com.marginallyclever.nodegraphcore.Node; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Rectangle extends Node { + private static final Logger logger = LoggerFactory.getLogger(Rectangle.class); + + private final Input w = new Input<>("width", Number.class, 100); + private final Input h = new Input<>("height", Number.class, 100); + private final Output contents = new Output<>("contents", Turtle.class, new Turtle()); + + public Rectangle() { + super("Rectangle"); + addVariable(w); + addVariable(h); + addVariable(contents); + } + + @Override + public void update() { + try { + Turtle t = new Turtle(); + double ww = w.getValue().doubleValue()/2.0; + double hh = h.getValue().doubleValue()/2.0; + t.jumpTo(-ww,-hh); + t.moveTo( ww,-hh); + t.moveTo( ww, hh); + t.moveTo(-ww, hh); + t.moveTo(-ww,-hh); + t.penUp(); + contents.send(t); + } catch (Exception e) { + logger.warn("Failed to update, ignoring", e); + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/TransformedImage.java b/src/main/java/com/marginallyclever/makelangelo/makeart/TransformedImage.java index 4342e6771..e86754e0a 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/TransformedImage.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/TransformedImage.java @@ -5,7 +5,6 @@ import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.WritableRaster; -import java.util.Arrays; /** * TransformedImage is a {@link BufferedImage}, with a transformation matrix on top. @@ -43,6 +42,12 @@ public TransformedImage(TransformedImage copy) { scaleY = copy.scaleY; } + /** + * Can the image be sampled at this location? + * @param x pre-transform x + * @param y pre-transform y + * @return true if the image can be sampled at this location + */ public boolean canSampleAt(double x, double y) { int sampleX = getTransformedX(x); int sampleY = getTransformedY(y); @@ -91,81 +96,54 @@ public int sample(double cx, double cy, double radius) { } /** - * Sample the image, taking into account fractions of pixels. left must be less than right, bottom must be less than top. + * Sample the image, taking into account fractions of pixels. * @param x0 left * @param y0 top * @param x1 right * @param y1 bottom - * @return greyscale intensity in this region. [0...255]v + * @return greyscale intensity in this region. [0...255] */ public int sample(double x0, double y0, double x1, double y1) { - double sampleValue = 0; - double weightedSum = 0; - - int left = (int)Math.floor(x0); - int right = (int)Math.ceil (x1); - int bottom = (int)Math.floor(y0); - int top = (int)Math.ceil (y1); - - // calculate the weight matrix - int w = Math.max(1,right-left); - int h = Math.max(1,top-bottom); - if(w==1 && h==1) { - if (canSampleAt(left, bottom)) { - return sample1x1Unchecked(left, bottom); - } else { - return 0; - } - } - - double [] m = new double[w*h]; - Arrays.fill(m, 1); - - // bottom edge - if(bottomy1) { - double yWeightEnd = top-y1; - for(int i=0;i right) { + int temp = left; + left = right; + right = temp; } - // right edge - if(right>x1) { - double xWeightEnd = right-x1; - for(int i=0;i top) { + int temp = bottom; + bottom = top; + top = temp; } - - int i=0; + // find the bounds of the image once, instead of inside the loops. + bottom = Math.max(Math.min(bottom, sourceImage.getHeight()), 0); + top = Math.max(Math.min(top, sourceImage.getHeight()), 0); + left = Math.max(Math.min(left, sourceImage.getWidth()), 0); + right = Math.max(Math.min(right, sourceImage.getWidth()), 0); + + // now sample the entire area to average the intensity + int count = (top-bottom) * (right-left); + // if no hits, return white + if(count==0) return 255; + + var raster = sourceImage.getRaster(); + var componentCount = sourceImage.getColorModel().getNumComponents(); + var pixel = new double[componentCount]; + double sampleValue = 0; for(int y=bottom;y lowpass) { @@ -104,9 +104,9 @@ public void start(Paper paper, TransformedImage image) { } } else { // every odd line move right to left - for (x = xRight; x > xLeft; x -= fullStep) { + for (x = xRight + halfStep; x > xLeft; x -= fullStep) { // read a block of the image and find the average intensity in this block - z = img.sample( x - halfStep, y - halfStep, x + halfStep, y + halfStep); + z = img.sample( x, y, halfStep); // scale the intensity value double scaleZ = (255.0f - z) / 255.0f; if (scaleZ > lowpass) { diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_CMYK_Circles.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_CMYK_Circles.java index a989e2e7e..5f928bccd 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_CMYK_Circles.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_CMYK_Circles.java @@ -8,6 +8,7 @@ import com.marginallyclever.makelangelo.makeart.turtletool.InfillTurtle; import com.marginallyclever.makelangelo.makeart.turtletool.RemoveExtraColorChangesFromTurtle; import com.marginallyclever.makelangelo.paper.Paper; +import com.marginallyclever.makelangelo.select.SelectBoolean; import com.marginallyclever.makelangelo.select.SelectReadOnlyText; import com.marginallyclever.makelangelo.select.SelectSlider; import com.marginallyclever.makelangelo.turtle.Turtle; @@ -23,7 +24,8 @@ */ public class Converter_CMYK_Circles extends ImageConverter { private static final Logger logger = LoggerFactory.getLogger(Converter_CMYK_Circles.class); - static protected int maxCircleRadius =5; + protected static int maxCircleRadius =5; + protected static boolean fillCircles = false; public Converter_CMYK_Circles() { super(); @@ -34,6 +36,12 @@ public Converter_CMYK_Circles() { fireRestart(); }); add(maxCircleSize); + SelectBoolean fillCircles = new SelectBoolean("fillCircles",Translator.get("Converter_CMYK_Circles.fillCircles"),this.fillCircles); + fillCircles.addSelectListener((evt)->{ + Converter_CMYK_Circles.fillCircles = (boolean)evt.getNewValue(); + fireRestart(); + }); + add(fillCircles); add(new SelectReadOnlyText("note",Translator.get("Converter_CMYK_Crosshatch.Note"))); } @@ -139,10 +147,10 @@ private void circlesAlongLine(double x1, double y1, double x0, double y0, Transf double b; for( b = 0; b <= distance; b+= maxCircleRadius*2) { n = b / distance; - x = dx * n + P0.x; + x = dx * n + P0.x + halfStep; y = dy * n + P0.y; - v = img.sample( x - halfStep, y - halfStep, x + halfStep, y + halfStep); + v = img.sample( x, y, halfStep); drawCircle(cx + x, cy + y, maxCircleRadius * ((255.0-v)/255.0)); } @@ -162,13 +170,15 @@ private void drawCircle(double x,double y,double r) { } t.moveTo(x+r,y+0); - try { - InfillTurtle filler = new InfillTurtle(); - filler.setPenDiameter(t.getDiameter()); - Turtle t2 = filler.run(t); - turtle.add(t2); - } catch(Exception e) { - // shape was not closed, do nothing. + if(fillCircles) { + try { + InfillTurtle filler = new InfillTurtle(); + filler.setPenDiameter(t.getDiameter()); + Turtle t2 = filler.run(t); + turtle.add(t2); + } catch (Exception e) { + // shape was not closed, do nothing. + } } turtle.add(t); diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_FlowField.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_FlowField.java index 2c1cd90f8..1e6521ee1 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_FlowField.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_FlowField.java @@ -10,6 +10,7 @@ import com.marginallyclever.makelangelo.paper.Paper; import com.marginallyclever.makelangelo.select.*; import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.makelangelo.turtle.TurtlePathWalker; import javax.vecmath.Vector2d; import java.awt.*; @@ -58,7 +59,6 @@ public Converter_FlowField() { add(fieldRandomSeed); fieldRandomSeed.addSelectListener(evt->{ seed = (int)evt.getNewValue(); - random.setSeed(seed); fireRestart(); }); @@ -225,10 +225,11 @@ private SampleAt[] calculateSamplesOnce(TransformedImage img, Turtle line) { double len = line.getDrawDistance(); int numSamples = (int)(len/samplingRate); SampleAt [] samples = new SampleAt[numSamples]; + TurtlePathWalker walker = new TurtlePathWalker(line); - Point2D p = line.interpolate(0.0); + Point2D p = walker.walk(0); for(int i=0;i255) z=255; diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_SpiralPulse.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_SpiralPulse.java index ce793e348..ac9288230 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_SpiralPulse.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_SpiralPulse.java @@ -111,7 +111,7 @@ public void start(Paper paper, TransformedImage image) { fy = Math.sin(f) * r2; // clip to paper boundaries if( rect.contains(fx, fy) ) { - z = img.sample( fx - zigZagSpacing, fy - halfStep, fx + zigZagSpacing, fy + halfStep); + z = img.sample( fx, fy, halfStep); scale_z = (255.0f - z) / 255.0f; pulse_size = halfStep * scale_z; nx = (halfStep+pulse_size*n) * fx / r2; diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_TruchetFromImage.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_TruchetFromImage.java index 11cba82a5..f2ee6399d 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_TruchetFromImage.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_TruchetFromImage.java @@ -69,7 +69,7 @@ public void start(Paper paper, TransformedImage image) { for(double y=miny;y128) truchet.tileA(px+x,py+y); else truchet.tileB(px+x,py+y); } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Voronoi.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Voronoi.java index c224aa792..1cc57cd18 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Voronoi.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_Voronoi.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.concurrent.atomic.DoubleAdder; +import java.util.stream.IntStream; /** * Shared methods for Voronoi converters @@ -103,7 +105,10 @@ public void start(Paper paper, TransformedImage image) { @Override public boolean iterate() { + turtle.history.clear(); + iterations++; + lock.lock(); try { double noiseLevel = evolveCells(); @@ -133,10 +138,11 @@ private double evolveCells() { } private double adjustCenters(TransformedImage image) { - double change=0; GeometryFactory factory = new GeometryFactory(); - for(int i=0;i { Polygon poly = voronoiDiagram.getHull(i); PreparedPolygon hull = new PreparedPolygon(poly); VoronoiCell cell = cells.get(i); @@ -179,11 +185,12 @@ private double adjustCenters(TransformedImage image) { double dx = wx - cell.center.x; double dy = wy - cell.center.y; cell.change = (dx*dx+dy*dy); - change += cell.change; + change.add(cell.change); cell.set(wx,wy); } - } - return change; + }); + + return change.sum(); } private double getStepSize(double maxy, double miny, double xDiff) { @@ -235,14 +242,7 @@ private double findRightEdge(PreparedPolygon poly,GeometryFactory factory, doubl @Override public void stop() { super.stop(); - lock.lock(); - try { - writeOutCells(); - } - finally { - lock.unlock(); - } - fireConversionFinished(); + writeOutCells(); } protected void renderEdges(GL2 gl2) { @@ -280,8 +280,13 @@ public boolean getDrawVoronoi() { @Override public void generateOutput() { - writeOutCells(); - + lock.lock(); + try { + writeOutCells(); + } + finally { + lock.unlock(); + } fireConversionFinished(); } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_VoronoiZigZag.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_VoronoiZigZag.java index 6d4267da3..d629eb2c1 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_VoronoiZigZag.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imageconverter/Converter_VoronoiZigZag.java @@ -12,6 +12,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.IntStream; /** * Dithering using a particle system. @@ -154,19 +157,20 @@ public void flipTests() { int lpc = getLowpassCutoff(); int size = cells.size(); + // cannot run in parallel one thread might change the list while another is reading it. for (int start = 0; start < size - 2 && !isThreadCancelled(); ++start) { VoronoiCell a = cells.get(ti(start )); VoronoiCell b = cells.get(ti(start+1)); if(a.weight bestDiff = new AtomicReference<>(0.0); + AtomicInteger bestIndex = new AtomicInteger(-1); - for (int end = start + 2; end < size && !isThreadCancelled(); ++end) { + IntStream.range(start + 2, size).parallel().forEach(end -> { VoronoiCell c = cells.get(ti(end-1)); VoronoiCell d = cells.get(ti(end )); - if(c.weightcutoff && tries<1000); if(tries==1000) break; // ran out of points to try? diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterContrastAdjust.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterContrastAdjust.java index 3d3d6ad03..622ca1890 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterContrastAdjust.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterContrastAdjust.java @@ -10,6 +10,7 @@ import java.awt.image.BufferedImage; import java.io.FileInputStream; import java.io.IOException; +import java.util.stream.IntStream; /** * Adjusts the top and bottom of the constrast curve. @@ -41,17 +42,21 @@ public TransformedImage filter() { TransformedImage after = new TransformedImage(img); BufferedImage afterBI = after.getSourceImage(); - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - int color = bi.getRGB(x, y); - int red = adjust(red32(color)); - int green = adjust(green32(color)); - int blue = adjust(blue32(color)); - int alpha = alpha32(color); + var raster = bi.getRaster(); + var afterRaster = afterBI.getRaster(); + var count = bi.getColorModel().getNumComponents(); + // Temporary array to hold pixel components - afterBI.setRGB(x, y, ImageFilter.encode32bit(red,green,blue,alpha)); + IntStream.range(0, h).parallel().forEach(y -> { + int[] pixel = new int[count]; + for (int x = 0; x < w; ++x) { + raster.getPixel(x, y, pixel); + pixel[0] = adjust(pixel[0]); + pixel[1] = adjust(pixel[1]); + pixel[2] = adjust(pixel[2]); + afterRaster.setPixel(x, y, pixel); } - } + }); return after; } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDesaturate.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDesaturate.java index 05a04d572..3f8adb2e5 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDesaturate.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDesaturate.java @@ -7,6 +7,7 @@ import java.awt.image.BufferedImage; import java.io.FileInputStream; import java.io.IOException; +import java.util.stream.IntStream; /** * Converts an image to greyscale. @@ -28,20 +29,32 @@ public TransformedImage filter() { BufferedImage bi = img.getSourceImage(); TransformedImage after = new TransformedImage(img); BufferedImage afterBI = after.getSourceImage(); + var raster = bi.getRaster(); + var afterRaster = afterBI.getRaster(); - int x, y; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - double pixel = decode32bit(bi.getRGB(x, y)); - //double v2 = sRGBtoLinear(pixel); - double v2 = toneControl(pixel); - int rgb = (int) Math.min(255, Math.max(0, v2)); - afterBI.setRGB(x, y, ImageFilter.encode32bit(rgb)); + var count = bi.getColorModel().getNumComponents(); + // Temporary array to hold pixel components + + IntStream.range(0, h).parallel().forEach(y -> { + int[] pixel = new int[count]; + for (int x = 0; x < w; ++x) { + raster.getPixel(x, y, pixel); + double average = (pixel[0]+pixel[1]+pixel[2])/3.0; + int toned = (int)toneControl(average); + pixel[0] = toned; + pixel[1] = toned; + pixel[2] = toned; + afterRaster.setPixel(x,y,pixel); } - } + }); return after; } + /** + * Convert a single pixel from sRGB to linear. + * @param b a number between 0 and 255, inclusive. + * @return a number between 0 and 255, inclusive. + */ private double sRGBtoLinear(double b) { b /= 255.0; if (b <= 0.04045) b /= 12.92; @@ -50,7 +63,9 @@ private double sRGBtoLinear(double b) { } /** - * accepts and returns a number between 0 and 255, inclusive. + * Non-linear tone control. Mostly brightens highlights while leaving midtones and shadows alone. + * @param b a number between 0 and 255, inclusive. + * @return a number between 0 and 255, inclusive. */ private double toneControl(double b) { b /= 255.0; diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDifference.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDifference.java index 9ad08ec6e..f9864a382 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDifference.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterDifference.java @@ -2,7 +2,6 @@ import com.marginallyclever.makelangelo.makeart.TransformedImage; -import java.awt.*; import java.awt.image.BufferedImage; /** @@ -30,16 +29,22 @@ public TransformedImage filter() { } BufferedImage rr = result.getSourceImage(); + var rasterA = aa.getRaster(); + var rasterB = bb.getRaster(); + var rasterR = rr.getRaster(); + var cm = aa.getColorModel().getNumComponents(); + // Temporary array to hold pixel components + int[] pixelA = new int[cm]; + int[] pixelB = new int[cm]; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { - Color diff = new Color(aa.getRGB(x, y)); - Color other = new Color(bb.getRGB(x, y)); - var diff2 = new Color( - modify(diff.getRed() , other.getRed() ), - modify(diff.getGreen(), other.getGreen()), - modify(diff.getBlue() , other.getBlue() ) ); - rr.setRGB(x, y, diff2.hashCode()); + rasterA.getPixel(x, y, pixelA); + rasterB.getPixel(x, y, pixelB); + pixelA[0] = modify(pixelA[0],pixelB[0]); + pixelA[1] = modify(pixelA[1],pixelB[1]); + pixelA[2] = modify(pixelA[2],pixelB[2]); + rasterR.setPixel(x, y, pixelA); } } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterInvert.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterInvert.java index ab3640b84..11d4e1371 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterInvert.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterInvert.java @@ -1,6 +1,5 @@ package com.marginallyclever.makelangelo.makeart.imagefilter; -import com.marginallyclever.convenience.ColorRGB; import com.marginallyclever.convenience.ResizableImagePanel; import com.marginallyclever.makelangelo.makeart.TransformedImage; @@ -8,6 +7,7 @@ import java.awt.image.BufferedImage; import java.io.FileInputStream; import java.io.IOException; +import java.util.stream.IntStream; /** @@ -27,20 +27,24 @@ public TransformedImage filter() { BufferedImage src = img.getSourceImage(); int h = src.getHeight(); int w = src.getWidth(); - int x, y; TransformedImage after = new TransformedImage(img); BufferedImage afterBI = after.getSourceImage(); - - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - ColorRGB color = new ColorRGB(src.getRGB(x, y)); - color.red = 255 - color.red; - color.green = 255 - color.green; - color.blue = 255 - color.blue; - afterBI.setRGB(x, y, color.toInt()); + var raster = src.getRaster(); + var afterRaster = afterBI.getRaster(); + var componentCount = src.getColorModel().getNumComponents(); + // Temporary array to hold pixel components + + IntStream.range(0, h).parallel().forEach(y -> { + int[] pixel = new int[componentCount]; + for (int x = 0; x < w; ++x) { + raster.getPixel(x,y,pixel); + pixel[0] = 255 - pixel[0]; + pixel[1] = 255 - pixel[1]; + pixel[2] = 255 - pixel[2]; + afterRaster.setPixel(x, y, pixel); } - } + }); return after; } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterJumpFlood.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterJumpFlood.java index fc4b8c3c2..61aeacbbc 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterJumpFlood.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterJumpFlood.java @@ -8,7 +8,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Random; +import java.util.stream.IntStream; /** * Converts an image using the jump flood algorithm. On a white surface, black pixels will "spread out" creating @@ -19,7 +19,6 @@ public class FilterJumpFlood extends ImageFilter { private final List points = new ArrayList<>(); private int scale; private final TransformedImage img; - private static int seed=0; public FilterJumpFlood(TransformedImage img) { super(); @@ -40,24 +39,28 @@ public TransformedImage filter() { public BufferedImage fillImage(BufferedImage image) { points.clear(); + + var h = image.getHeight(); + var w = image.getWidth(); + // Scan the image to find the initial points (black pixels) - for (int x = 0; x < image.getWidth(); x++) { - for (int y = 0; y < image.getHeight(); y++) { + IntStream.range(0, h).parallel().forEach(y -> { + for (int x = 0; x < w; x++) { Color color = new Color(image.getRGB(x, y)); if (color.equals(Color.BLACK)) { points.add(new Point(x, y)); } } - } + }); scale = Math.min(image.getWidth(), image.getHeight()) /2; // Run the algorithm - for (int x = 0; x < image.getWidth(); x++) { - for (int y = 0; y < image.getHeight(); y++) { + IntStream.range(0, h).parallel().forEach(y -> { + for (int x = 0; x < w; x++) { updatePixel(image,points,x, y); } - } + }); return image; } @@ -79,19 +82,24 @@ private void updatePixel(BufferedImage image,List points,int x, int y) { public static void main(String[] args) throws IOException { //* BufferedImage image = new BufferedImage(400, 500, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < image.getWidth(); x++) { - for (int y = 0; y < image.getHeight(); y++) { - image.setRGB(x, y, Color.WHITE.getRGB()); - } - } + var g = image.getGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + // some random black pixels for(int i=0;i<25;++i) { - image.setRGB((int)(Math.random()*image.getWidth()), (int)(Math.random()*image.getHeight()), Color.BLACK.getRGB()); + image.setRGB( + (int)(Math.random()*image.getWidth()), + (int)(Math.random()*image.getHeight()), + Color.BLACK.getRGB()); } TransformedImage src = new TransformedImage( image ); + long start = System.currentTimeMillis(); FilterJumpFlood f = new FilterJumpFlood(src); TransformedImage dest = f.filter(); + long end = System.currentTimeMillis(); + System.out.println("FilterJumpFlood took "+(end-start)+"ms"); ResizableImagePanel.showImage(dest.getSourceImage(), "Filter_JumpFlood" ); } } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterLevels.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterLevels.java index 2f0460283..a8dc56831 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterLevels.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/FilterLevels.java @@ -11,14 +11,13 @@ import java.io.IOException; /** - * Converts an image to N levels. + * Converts an image to N levels of grey * @author Dan Royer */ public class FilterLevels extends ImageFilter { private static final Logger logger = LoggerFactory.getLogger(FilterLevels.class); private final TransformedImage img; private final double levels; - private final int mode = 1; public FilterLevels(TransformedImage img, int levels) { super(); @@ -28,113 +27,34 @@ public FilterLevels(TransformedImage img, int levels) { @Override public TransformedImage filter() { - return switch (mode) { - case 0 -> filterLevels(img); - case 1 -> filterTone(img); - case 2 -> filterSimple(img); - default -> null; - }; - } - - protected TransformedImage filterLevels(TransformedImage img) { int h = img.getSourceImage().getHeight(); int w = img.getSourceImage().getWidth(); - int x, y, i; double max_intensity = -1000; double min_intensity = 1000; BufferedImage bi = img.getSourceImage(); - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - i = decode32bit(bi.getRGB(x, y)); - if (max_intensity < i) max_intensity = i; - if (min_intensity > i) min_intensity = i; - } - } - double intensity_range = max_intensity - min_intensity; - - double ilevels = 1; - if (levels != 0) - ilevels = 1.0 / levels; - - double pixel; - TransformedImage after = new TransformedImage(img); BufferedImage afterBI = after.getSourceImage(); - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - pixel = decode32bit(bi.getRGB(x, y)); - double a = (pixel - min_intensity) / intensity_range; - double c = a * levels * ilevels; - int b = (int) Math.max(Math.min(c * 255.0, 255), 0); - afterBI.setRGB(x, y, ImageFilter.encode32bit(b)); - } - } + double step = 255.0 / (levels - 1); // Step size for quantization - return after; - } - - private double sRGBtoLinear(double b) { - b /= 255.0; - if (b <= 0.04045) b /= 12.92; - else b = Math.pow((b + 0.055) / 1.055, 2.4); - return b * 255.0; - } - - /** - * accepts and returns a number between 0 and 255, inclusive. - */ - private double toneControl(double b) { - b /= 255.0; - b = 0.017 * Math.exp(3.29 * b) + 0.005 * Math.exp(7.27 * b); - return Math.min(1, Math.max(0, b)) * 255.0; - } - - public TransformedImage filterTone(TransformedImage img) { - int h = img.getSourceImage().getHeight(); - int w = img.getSourceImage().getWidth(); - - BufferedImage bi = img.getSourceImage(); - TransformedImage after = new TransformedImage(img); - BufferedImage afterBI = after.getSourceImage(); - - int x, y; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { double pixel = decode32bit(bi.getRGB(x, y)); - //double v2 = sRGBtoLinear(pixel); - double v2 = toneControl(pixel); - int rgb = (int) Math.min(255, Math.max(0, v2)); - afterBI.setRGB(x, y, ImageFilter.encode32bit(rgb)); + double d = (int)Math.round((pixel * (levels-1)) / 255.0); + double c = (int) Math.round(d * step); + int b = (int) Math.max(Math.min(c, 255), 0); + afterBI.setRGB(x, y, ImageFilter.encode32bit(b)); } } - return after; - } - - public TransformedImage filterSimple(TransformedImage img) { - int h = img.getSourceImage().getHeight(); - int w = img.getSourceImage().getWidth(); - BufferedImage bi = img.getSourceImage(); - TransformedImage after = new TransformedImage(img); - BufferedImage afterBI = after.getSourceImage(); - - int x, y; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - double pixel = decode32bit(bi.getRGB(x, y)); - int rgb = (int) Math.min(255, Math.max(0, pixel)); - afterBI.setRGB(x, y, ImageFilter.encode32bit(rgb)); - } - } return after; } public static void main(String[] args) throws IOException { TransformedImage src = new TransformedImage( ImageIO.read(new FileInputStream("src/test/resources/mandrill.png")) ); - FilterLevels f = new FilterLevels(src,255); + FilterLevels f = new FilterLevels(src,8); ResizableImagePanel.showImage(f.filter().getSourceImage(), "Filter_Greyscale" ); } } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/ImageFilter.java b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/ImageFilter.java index 62cbab300..a35353b61 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/ImageFilter.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/imagefilter/ImageFilter.java @@ -9,48 +9,13 @@ * @author dan Royer */ public abstract class ImageFilter { - protected static int red32(int color) { - return ((color >> 16) & 0xff); - } - - protected static int green32(int color) { - return ((color >> 8) & 0xff); - } - - protected static int blue32(int color) { - return ((color) & 0xff); - } - - protected static int alpha32(int color) { - return ((color >> 24) & 0xff); - } - /** * @param color RGBA * @return grayscale value */ public static int decode32bit(int color) { - int r = red32(color); - int g = green32(color); - int b = blue32(color); - int a = alpha32(color); - - return average(r, g, b, a / 255.0); - } - - /** - * @param red 0-255 - * @param green 0-255 - * @param blue 0-255 - * @param alpha 0-255 - * @return RGB color - */ - public static int encode32bit(int red,int green,int blue,int alpha) { - red &= 0xff; - green &= 0xff; - blue &= 0xff; - alpha &= 0xff; - return (alpha << 24) | (red << 16) | (green << 8) | blue; + Color c = new Color(color); + return (c.getRed() + c.getGreen() + c.getBlue()) / 3; } /** @@ -59,34 +24,8 @@ public static int encode32bit(int red,int green,int blue,int alpha) { */ public static int encode32bit(int greyscale) { greyscale &= 0xff; - return encode32bit(greyscale,greyscale,greyscale,0xff); - } - - /** - * @param color RGBA - * @return grayscale value - */ - protected static int decodeColor(Color color) { - int r = color.getRed(); - int g = color.getGreen(); - int b = color.getBlue(); - int a = color.getAlpha(); - return average(r, g, b, a / 255.0); - } - - /** - * @param r red - * @param g green - * @param b blue - * @param a alpha - * @return grayscale value - */ - private static int average(int r, int g, int b, double a) { - int r2 = (int)(r * a); - int g2 = (int)(g * a); - int b2 = (int)(b * a); - - return (r2 + g2 + b2) / 3; + Color c = new Color(greyscale,greyscale,greyscale); + return c.getRGB(); } /** diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/io/LoadFilePanel.java b/src/main/java/com/marginallyclever/makelangelo/makeart/io/LoadFilePanel.java index 030732954..55fe3c0b9 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/io/LoadFilePanel.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/io/LoadFilePanel.java @@ -62,6 +62,10 @@ private void stopExistingImageConverter() { public boolean onNewFilenameChosen(String filename) { stopExistingImageConverter(); + if(filename.startsWith("\"") && filename.endsWith("\"")) { + // probably copy/pasted from a windows explorer window, which adds quotes around the filename. + filename = filename.substring(1,filename.length()-1); + } selectedFilename.setText(filename); try { diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/io/TurtleFactory.java b/src/main/java/com/marginallyclever/makelangelo/makeart/io/TurtleFactory.java index 8a25f9a0d..7283a511d 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/io/TurtleFactory.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/io/TurtleFactory.java @@ -47,10 +47,12 @@ public static Turtle load(String filename) throws Exception { if(isValidExtension(filename,loader.getFileNameFilter())) { try(FileInputStream in = new FileInputStream(filename)) { return loader.load(in); + } catch(Exception e) { + throw new Exception("TurtleFactory could not load '" + filename + "'.", e); } } } - throw new IllegalStateException("TurtleFactory could not load '"+filename+"'."); + throw new IllegalStateException("TurtleFactory doesn't recognize the format of '"+filename+"'."); } private static boolean isValidExtension(String filename, FileNameExtensionFilter filter) { diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/truchet/TruchetTileFactory.java b/src/main/java/com/marginallyclever/makelangelo/makeart/truchet/TruchetTileFactory.java index 0fdd94a05..611460cad 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/truchet/TruchetTileFactory.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/truchet/TruchetTileFactory.java @@ -24,17 +24,25 @@ public static List getNames() { })); } + /** + * spaceBetweenLines * linesPerTileCount = tileSize + * @param index the index of the tile to create + * @param turtle the turtle to draw with + * @param spaceBetweenLines the distance between lines + * @param linesPerTileCount the number of lines per tile + * @return a new Truchet tile + */ public static TruchetTile getTile(int index, Turtle turtle, double spaceBetweenLines, double linesPerTileCount) { - switch(index) { - case 0: return new TruchetDiagonalRising(turtle,spaceBetweenLines,linesPerTileCount); - case 1: return new TruchetDiagonalFalling(turtle,spaceBetweenLines,linesPerTileCount); - case 2: return new TruchetOrthogonalH(turtle,spaceBetweenLines,linesPerTileCount); - case 3: return new TruchetOrthogonalV(turtle,spaceBetweenLines,linesPerTileCount); - case 4: return new TruchetCurvedCurtainL(turtle,spaceBetweenLines,linesPerTileCount); - case 5: return new TruchetCurvedCurtainR(turtle,spaceBetweenLines,linesPerTileCount); - case 6: return new TruchetCurvedFanL(turtle,spaceBetweenLines,linesPerTileCount); - case 7: return new TruchetCurvedFanR(turtle,spaceBetweenLines,linesPerTileCount); - default: throw new IllegalArgumentException("Unknown Truchet tile index "+index); - } + return switch (index) { + case 0 -> new TruchetDiagonalRising(turtle, spaceBetweenLines, linesPerTileCount); + case 1 -> new TruchetDiagonalFalling(turtle, spaceBetweenLines, linesPerTileCount); + case 2 -> new TruchetOrthogonalH(turtle, spaceBetweenLines, linesPerTileCount); + case 3 -> new TruchetOrthogonalV(turtle, spaceBetweenLines, linesPerTileCount); + case 4 -> new TruchetCurvedCurtainL(turtle, spaceBetweenLines, linesPerTileCount); + case 5 -> new TruchetCurvedCurtainR(turtle, spaceBetweenLines, linesPerTileCount); + case 6 -> new TruchetCurvedFanL(turtle, spaceBetweenLines, linesPerTileCount); + case 7 -> new TruchetCurvedFanR(turtle, spaceBetweenLines, linesPerTileCount); + default -> throw new IllegalArgumentException("Unknown Truchet tile index " + index); + }; } } diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/Generator_FlowField.java b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/Generator_FlowField.java index 6de6a314a..063f85271 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/Generator_FlowField.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/Generator_FlowField.java @@ -44,7 +44,6 @@ public Generator_FlowField() { add(selectRandomSeed); selectRandomSeed.addSelectListener((evt)->{ seed = (int)evt.getNewValue(); - random.setSeed(seed); generate(); }); @@ -120,6 +119,8 @@ public String getName() { @Override public void generate() { + random.setSeed(seed); + noiseMaker.setSeed(seed); Turtle turtle = new Turtle(); if (fromEdge) { diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeight.java b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeight.java index 93721f0b7..e3ba68672 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeight.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeight.java @@ -3,7 +3,7 @@ import java.util.Collections; import java.util.LinkedList; -class LineWeight { +public class LineWeight { public LinkedList segments = new LinkedList<>(); public void flip() { diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightByImageIntensity.java b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightByImageIntensity.java index 0599fcf09..98df87b3f 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightByImageIntensity.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightByImageIntensity.java @@ -25,7 +25,6 @@ public class LineWeightByImageIntensity extends TurtleGenerator { private static final Logger logger = LoggerFactory.getLogger(LineWeightByImageIntensity.class); private final double EPSILON = 0.001; - private final double CORNER_THRESHOLD = Math.cos(Math.toRadians(15)); /** * must be greater than zero. diff --git a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightSegment.java b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightSegment.java index e98450820..eff739f51 100644 --- a/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightSegment.java +++ b/src/main/java/com/marginallyclever/makelangelo/makeart/turtlegenerator/lineweight/LineWeightSegment.java @@ -8,7 +8,7 @@ * Many segments make up a {@link LineWeight}. * @author Dan Royer */ -class LineWeightSegment { +public class LineWeightSegment { public Point2D start, end; public int ix, iy; // index for faster search public double weight; diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5.java index a7a2d381e..3e105ef7a 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5.java @@ -1,29 +1,31 @@ package com.marginallyclever.makelangelo.plotter.plotterrenderer; import com.jogamp.opengl.GL2; -import com.jogamp.opengl.util.texture.Texture; import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.texture.TextureFactory; +import com.marginallyclever.makelangelo.texture.TextureWithMetadata; import com.marginallyclever.makelangelo.plotter.Plotter; import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings; -import static com.marginallyclever.convenience.helpers.DrawingHelper.*; +import static com.marginallyclever.convenience.helpers.DrawingHelper.drawCircle; +import static com.marginallyclever.convenience.helpers.DrawingHelper.paintTexture; public class Makelangelo5 implements PlotterRenderer { - private static Texture textureMainBody; - private static Texture textureMotors; - private static Texture textureLogo; - private static Texture textureWeight; - private static Texture textureGondola; - private static Texture textureArm; + private static TextureWithMetadata textureMainBody; + private static TextureWithMetadata textureMotors; + private static TextureWithMetadata textureLogo; + private static TextureWithMetadata textureWeight; + private static TextureWithMetadata textureGondola; + private static TextureWithMetadata textureArm; @Override public void render(GL2 gl2, Plotter robot) { - if (textureMainBody == null) textureMainBody = loadTexture("/textures/makelangelo5.png"); - if (textureMotors == null) textureMotors = loadTexture("/textures/makelangelo5-motors.png"); - if (textureLogo == null) textureLogo = loadTexture("/logo.png"); - if (textureWeight == null) textureWeight = loadTexture("/textures/weight.png"); - if (textureGondola == null) textureGondola = loadTexture("/textures/phBody.png"); - if (textureArm == null) textureArm = loadTexture("/textures/phArm2.png"); + if (textureMainBody == null) textureMainBody = TextureFactory.loadTexture("/textures/makelangelo5.png"); + if (textureMotors == null) textureMotors = TextureFactory.loadTexture("/textures/makelangelo5-motors.png"); + if (textureLogo == null) textureLogo = TextureFactory.loadTexture("/logo.png"); + if (textureWeight == null) textureWeight = TextureFactory.loadTexture("/textures/weight.png"); + if (textureGondola == null) textureGondola = TextureFactory.loadTexture("/textures/phBody.png"); + if (textureArm == null) textureArm = TextureFactory.loadTexture("/textures/phArm2.png"); if (textureMainBody == null) { paintControlBoxPlain(gl2, robot); @@ -161,7 +163,7 @@ private void paintCounterweight(GL2 gl2,double x,double y) { paintTexture(gl2, textureWeight, x-20, y-74, 40,80); } - private void paintControlBoxFancy(GL2 gl2, Plotter robot,Texture texture) { + private void paintControlBoxFancy(GL2 gl2, Plotter robot,TextureWithMetadata texture) { double left = robot.getSettings().getDouble(PlotterSettings.LIMIT_LEFT); // double top = robot.getSettings().getDouble(PlotterSettings.LIMIT_TOP); diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5Huge.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5Huge.java index 9add28077..b608ca539 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5Huge.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/Makelangelo5Huge.java @@ -1,29 +1,31 @@ package com.marginallyclever.makelangelo.plotter.plotterrenderer; import com.jogamp.opengl.GL2; -import com.jogamp.opengl.util.texture.Texture; import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.texture.TextureFactory; +import com.marginallyclever.makelangelo.texture.TextureWithMetadata; import com.marginallyclever.makelangelo.plotter.Plotter; import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings; -import static com.marginallyclever.convenience.helpers.DrawingHelper.*; +import static com.marginallyclever.convenience.helpers.DrawingHelper.drawCircle; +import static com.marginallyclever.convenience.helpers.DrawingHelper.paintTexture; public class Makelangelo5Huge implements PlotterRenderer { - private static Texture textureMainBody; - private static Texture textureMotorMounts; - private static Texture textureLogo; - private static Texture textureWeight; - private static Texture textureGondola; - private static Texture textureArm; + private static TextureWithMetadata textureMainBody; + private static TextureWithMetadata textureMotorMounts; + private static TextureWithMetadata textureLogo; + private static TextureWithMetadata textureWeight; + private static TextureWithMetadata textureGondola; + private static TextureWithMetadata textureArm; @Override public void render(GL2 gl2, Plotter robot) { - if (textureMainBody == null) textureMainBody = loadTexture("/textures/huge.png"); - if (textureMotorMounts == null) textureMotorMounts = loadTexture("/textures/huge-motors.png"); - if (textureLogo == null) textureLogo = loadTexture("/logo.png"); - if (textureWeight == null) textureWeight = loadTexture("/textures/weight.png"); - if (textureGondola == null) textureGondola = loadTexture("/textures/phBody.png"); - if (textureArm == null) textureArm = loadTexture("/textures/phArm2.png"); + if (textureMainBody == null) textureMainBody = TextureFactory.loadTexture("/textures/huge.png"); + if (textureMotorMounts == null) textureMotorMounts = TextureFactory.loadTexture("/textures/huge-motors.png"); + if (textureLogo == null) textureLogo = TextureFactory.loadTexture("/logo.png"); + if (textureWeight == null) textureWeight = TextureFactory.loadTexture("/textures/weight.png"); + if (textureGondola == null) textureGondola = TextureFactory.loadTexture("/textures/phBody.png"); + if (textureArm == null) textureArm = TextureFactory.loadTexture("/textures/phArm2.png"); if (textureMainBody == null) { paintControlBoxPlain(gl2, robot); @@ -49,7 +51,7 @@ public void render(GL2 gl2, Plotter robot) { } } - private void paintControlBoxFancy(GL2 gl2, Plotter robot,Texture texture) { + private void paintControlBoxFancy(GL2 gl2, Plotter robot,TextureWithMetadata texture) { double left = robot.getSettings().getDouble(PlotterSettings.LIMIT_LEFT); final double scaleX = 1366 / 943.0; // machine is 1366 motor-to-motor. texture is 922. scaleX accordingly. diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/MakelangeloCustom.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/MakelangeloCustom.java index cbc972577..776bfc3f7 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/MakelangeloCustom.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plotterrenderer/MakelangeloCustom.java @@ -1,8 +1,9 @@ package com.marginallyclever.makelangelo.plotter.plotterrenderer; import com.jogamp.opengl.GL2; -import com.jogamp.opengl.util.texture.Texture; import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.texture.TextureFactory; +import com.marginallyclever.makelangelo.texture.TextureWithMetadata; import com.marginallyclever.makelangelo.plotter.Plotter; import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings; @@ -14,7 +15,7 @@ public class MakelangeloCustom implements PlotterRenderer { public final static double COUNTERWEIGHT_H = 60; public final static double PULLEY_RADIUS = 1.27; public final static double MOTOR_WIDTH = 42; - private static Texture controlBoard; + private static TextureWithMetadata controlBoard; @Override public void render(GL2 gl2,Plotter robot) { @@ -58,7 +59,7 @@ private void paintControlBox(GL2 gl2, PlotterSettings settings) { gl2.glEnd(); float shiftX = (float) right / 2; - if (controlBoard == null) controlBoard = loadTexture("/textures/rampsv14.png"); + if (controlBoard == null) controlBoard = TextureFactory.loadTexture("/textures/rampsv14.png"); if (controlBoard != null) { final double scale = 0.1; if (shiftX < 100) { diff --git a/src/main/java/com/marginallyclever/makelangelo/plotter/plottersettings/PlotterSettingsPanel.java b/src/main/java/com/marginallyclever/makelangelo/plotter/plottersettings/PlotterSettingsPanel.java index fef8ff5eb..e81fa483c 100644 --- a/src/main/java/com/marginallyclever/makelangelo/plotter/plottersettings/PlotterSettingsPanel.java +++ b/src/main/java/com/marginallyclever/makelangelo/plotter/plottersettings/PlotterSettingsPanel.java @@ -135,8 +135,8 @@ private SelectPanel rebuildTabPen() { addToPanel(panel,penLowerRate = new SelectDouble("lowerSpeed", Translator.get("PlotterSettingsPanel.penToolLowerSpeed" ),settings.getDouble(PlotterSettings.PEN_ANGLE_DOWN_TIME))); addToPanel(panel,penUpAngle = new SelectDouble("up", Translator.get("PlotterSettingsPanel.penToolUp" ),settings.getDouble(PlotterSettings.PEN_ANGLE_UP))); addToPanel(panel,penDownAngle = new SelectDouble("down", Translator.get("PlotterSettingsPanel.penToolDown" ),settings.getDouble(PlotterSettings.PEN_ANGLE_DOWN))); - addToPanel(panel,selectPenUpColor = new SelectColor("colorUp", Translator.get("PlotterSettingsPanel.pen up color" ),settings.getColor(PlotterSettings.PEN_UP_COLOR),this)); - addToPanel(panel,selectPenDownColor = new SelectColor("colorDown", Translator.get("PlotterSettingsPanel.pen down color" ),settings.getColor(PlotterSettings.PEN_DOWN_COLOR_DEFAULT),this)); + addToPanel(panel,selectPenUpColor = new SelectColor("colorUp", Translator.get("PlotterSettingsPanel.penUpColor" ),settings.getColor(PlotterSettings.PEN_UP_COLOR),this)); + addToPanel(panel,selectPenDownColor = new SelectColor("colorDown", Translator.get("PlotterSettingsPanel.penDownColor" ),settings.getColor(PlotterSettings.PEN_DOWN_COLOR_DEFAULT),this)); addToPanel(panel,zMotorType = new SelectOneOfMany("zMotorType",Translator.get("PlotterSettings.zMotorType"),new String[]{ Translator.get("PlotterSettings.zMotorType.servo"), // PlotterSettings.Z_MOTOR_TYPE_SERVO = 1 Translator.get("PlotterSettings.zMotorType.stepper"), // PlotterSettings.Z_MOTOR_TYPE_STEPPER = 2 diff --git a/src/main/java/com/marginallyclever/makelangelo/MakelangeloDropTarget.java b/src/main/java/com/marginallyclever/makelangelo/preview/PreviewDropTarget.java similarity index 65% rename from src/main/java/com/marginallyclever/makelangelo/MakelangeloDropTarget.java rename to src/main/java/com/marginallyclever/makelangelo/preview/PreviewDropTarget.java index 591b73604..25ab7ffba 100644 --- a/src/main/java/com/marginallyclever/makelangelo/MakelangeloDropTarget.java +++ b/src/main/java/com/marginallyclever/makelangelo/preview/PreviewDropTarget.java @@ -1,5 +1,6 @@ -package com.marginallyclever.makelangelo; +package com.marginallyclever.makelangelo.preview; +import com.marginallyclever.makelangelo.Makelangelo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,11 +12,14 @@ import java.io.File; import java.util.List; -public class MakelangeloDropTarget extends DropTargetAdapter { - private static final Logger logger = LoggerFactory.getLogger(MakelangeloDropTarget.class); +/** + * Allows the user to drag and drop a file onto the {@link PreviewPanel}. + */ +public class PreviewDropTarget extends DropTargetAdapter { + private static final Logger logger = LoggerFactory.getLogger(PreviewDropTarget.class); private final Makelangelo app; - public MakelangeloDropTarget(Makelangelo app) { + public PreviewDropTarget(Makelangelo app) { super(); this.app = app; } @@ -30,15 +34,12 @@ public void drop(DropTargetDropEvent dtde) { if (flavor.isFlavorJavaFileListType()) { dtde.acceptDrop(DnDConstants.ACTION_COPY); Object o = tr.getTransferData(flavor); - if (o instanceof List) { - List list = (List) o; - if (list.size() > 0) { - o = list.get(0); - if (o instanceof File) { - app.openFile(((File) o).getAbsolutePath()); - dtde.dropComplete(true); - return; - } + if (o instanceof List list && !list.isEmpty()) { + o = list.getFirst(); + if (o instanceof File file) { + app.openFile(file.getAbsolutePath()); + dtde.dropComplete(true); + return; } } } diff --git a/src/main/java/com/marginallyclever/makelangelo/preview/PreviewPanel.java b/src/main/java/com/marginallyclever/makelangelo/preview/PreviewPanel.java index 0a930840f..8970500a5 100644 --- a/src/main/java/com/marginallyclever/makelangelo/preview/PreviewPanel.java +++ b/src/main/java/com/marginallyclever/makelangelo/preview/PreviewPanel.java @@ -5,6 +5,7 @@ import com.jogamp.opengl.glu.GLU; import com.jogamp.opengl.util.FPSAnimator; import com.marginallyclever.convenience.Point2D; +import com.marginallyclever.makelangelo.texture.TextureFactory; import com.marginallyclever.util.PreferencesHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,9 +13,7 @@ import javax.swing.*; import javax.vecmath.Vector2d; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; +import java.awt.event.*; import java.util.ArrayList; import java.util.List; import java.util.prefs.Preferences; @@ -24,12 +23,14 @@ * @author Dan Royer * */ -public class PreviewPanel extends GLJPanel implements GLEventListener { +public class PreviewPanel extends JPanel implements GLEventListener, MouseWheelListener, MouseListener, MouseMotionListener { private static final Logger logger = LoggerFactory.getLogger(PreviewPanel.class); // Use debug pipeline? private static final boolean DEBUG_GL_ON = false; private static final boolean TRACE_GL_ON = false; + private GLJPanel glCanvas; + private int canvasWidth,canvasHeight; private final List previewListeners = new ArrayList<>(); @@ -58,104 +59,111 @@ public class PreviewPanel extends GLJPanel implements GLEventListener { private FPSAnimator animator; public PreviewPanel() { - super(); + super(new BorderLayout()); try { - logger.debug(" get GL capabilities..."); - GLProfile glProfile = GLProfile.getDefault(); - GLCapabilities caps = new GLCapabilities(glProfile); - // caps.setSampleBuffers(true); - // caps.setHardwareAccelerated(true); - // caps.setNumSamples(4); - setRequestedGLCapabilities(caps); + logger.info("availability="+ GLProfile.glAvailabilityToString()); + GLCapabilities capabilities = getCapabilities(); + logger.info("create canvas"); + glCanvas = new GLJPanel(capabilities); } catch(GLException e) { logger.error("I failed the very first call to OpenGL. Are your native libraries missing?", e); System.exit(1); } - addGLEventListener(this); - - final JPanel me = this; - - // scroll the mouse wheel to zoom - addMouseWheelListener(new MouseAdapter() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - int notches = e.getWheelRotation(); - if (notches == 0) return; - - Point2D p = new Point2D(e.getPoint().x,e.getPoint().y); - Rectangle r = me.getBounds(); - p.x -= r.getCenterX(); - p.y -= r.getCenterY(); - - if (notches < 0) { - if (mouseLastZoomDirection == -1) camera.zoom(-1,p); - mouseLastZoomDirection = -1; - } else { - if (mouseLastZoomDirection == 1) camera.zoom(1,p); - mouseLastZoomDirection = 1; - } - repaint(); - } - }); - - // remember button states for when we need them. - addMouseListener(new MouseAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - buttonPressed = e.getButton(); - mouseOldX = e.getX(); - mouseOldY = e.getY(); - } - - @Override - public void mouseReleased(MouseEvent e) { - buttonPressed = MouseEvent.NOBUTTON; - } - }); - - - // left click + drag to move the camera around. - addMouseMotionListener(new MouseAdapter() { - @Override - public void mouseDragged(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - mouseX = x; - mouseY = y; - setTipXY(); - - if (buttonPressed == MouseEvent.BUTTON1) { - int dx = x - mouseOldX; - int dy = y - mouseOldY; - mouseOldX = x; - mouseOldY = y; - camera.moveRelative(-dx, -dy); - repaint(); - } - } + add(glCanvas, BorderLayout.CENTER); - @Override - public void mouseMoved(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - mouseOldX = x; - mouseOldY = y; - mouseX = x; - mouseY = y; - setTipXY(); - } - }); - // start animation system logger.debug(" starting animator..."); animator = new FPSAnimator(1); - animator.add(this); + animator.add(glCanvas); animator.start(); } + @Override + public void mousePressed(MouseEvent e) { + buttonPressed = e.getButton(); + mouseOldX = e.getX(); + mouseOldY = e.getY(); + } + + @Override + public void mouseReleased(MouseEvent e) { + buttonPressed = MouseEvent.NOBUTTON; + } + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) {} + + @Override + public void mouseClicked(MouseEvent e) {} + + @Override + public void mouseDragged(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + mouseX = x; + mouseY = y; + setTipXY(); + + if (buttonPressed == MouseEvent.BUTTON1) { + int dx = x - mouseOldX; + int dy = y - mouseOldY; + mouseOldX = x; + mouseOldY = y; + camera.moveRelative(-dx, -dy); + repaint(); + } + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + int notches = e.getWheelRotation(); + if (notches == 0) return; + + Point2D p = new Point2D(e.getPoint().x,e.getPoint().y); + Rectangle r = this.getBounds(); + p.x -= r.getCenterX(); + p.y -= r.getCenterY(); + + if (notches < 0) { + if (mouseLastZoomDirection == -1) camera.zoom(-1,p); + mouseLastZoomDirection = -1; + } else { + if (mouseLastZoomDirection == 1) camera.zoom(1,p); + mouseLastZoomDirection = 1; + } + repaint(); + } + + @Override + public void mouseMoved(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + mouseOldX = x; + mouseOldY = y; + mouseX = x; + mouseY = y; + setTipXY(); + } + + private GLCapabilities getCapabilities() { + GLProfile profile = GLProfile.getMaxProgrammable(true); + GLCapabilities capabilities = new GLCapabilities(profile); + capabilities.setHardwareAccelerated(true); + capabilities.setBackgroundOpaque(true); + capabilities.setDoubleBuffered(true); + capabilities.setStencilBits(8); + capabilities.setDepthBits(32); // 32 bit depth buffer is floating point + StringBuilder sb = new StringBuilder(); + capabilities.toString(sb); + logger.info("capabilities="+sb); + return capabilities; + } + public void addListener(PreviewListener arg0) { previewListeners.add(arg0); } @@ -169,17 +177,9 @@ public void removeListener(PreviewListener arg0) { */ @Override public void reshape(GLAutoDrawable glautodrawable, int x, int y, int width, int height) { - GL2 gl2 = glautodrawable.getGL().getGL2(); - - camera.setWidth(width); - camera.setHeight(height); - - gl2.glMatrixMode(GL2.GL_PROJECTION); - gl2.glLoadIdentity(); - // orthographic projection - float w2 = width/2.0f; - float h2 = height/2.0f; - glu.gluOrtho2D(-w2, w2, -h2, h2); + System.out.println("reshape "+width+"x"+height); + canvasWidth = width; + canvasHeight = height; } public Vector2d getMousePositionInWorld() { @@ -202,6 +202,7 @@ private void setTipXY() { */ @Override public void init(GLAutoDrawable glautodrawable) { + logger.debug("init"); GL gl = glautodrawable.getGL(); if (DEBUG_GL_ON) { @@ -224,10 +225,22 @@ public void init(GLAutoDrawable glautodrawable) { } glu = GLU.createGLU(gl); + + // turn on vsync + gl.setSwapInterval(1); + + // make things pretty + gl.glEnable(GL3.GL_LINE_SMOOTH); + gl.glEnable(GL3.GL_POLYGON_SMOOTH); + gl.glHint(GL3.GL_POLYGON_SMOOTH_HINT, GL3.GL_NICEST); + gl.glEnable(GL3.GL_MULTISAMPLE); } @Override - public void dispose(GLAutoDrawable glautodrawable) {} + public void dispose(GLAutoDrawable glautodrawable) { + logger.info("dispose"); + TextureFactory.dispose(glautodrawable.getGL()); + } /** * Refresh the image in the view. This is where drawing begins. @@ -237,6 +250,9 @@ public void display(GLAutoDrawable glautodrawable) { // draw the world GL2 gl2 = glautodrawable.getGL().getGL2(); + camera.setWidth(canvasWidth); + camera.setHeight(canvasHeight); + // set some render quality options Preferences prefs = PreferencesHelper.getPreferenceNode(PreferencesHelper.MakelangeloPreferenceKey.GRAPHICS); if(prefs != null && prefs.getBoolean("antialias", true)) { @@ -263,12 +279,20 @@ public void display(GLAutoDrawable glautodrawable) { } } + /** * Set up the correct modelview so the robot appears where it should. * * @param gl2 OpenGL context */ private void paintCamera(GL2 gl2) { + gl2.glMatrixMode(GL2.GL_PROJECTION); + gl2.glLoadIdentity(); + // orthographic projection + float w2 = canvasWidth/2.0f; + float h2 = canvasHeight/2.0f; + glu.gluOrtho2D(-w2, w2, -h2, h2); + gl2.glMatrixMode(GL2.GL_MODELVIEW); gl2.glLoadIdentity(); gl2.glScaled(camera.getZoom(),camera.getZoom(),1); @@ -288,15 +312,7 @@ private void paintBackground(GL2 gl2) { (float)backgroundColor.getBlue()/255.0f, 0.0f); - // Special handling for the case where the GLJPanel is translucent - // and wants to be composited with other Java 2D content - if (GLProfile.isAWTAvailable() - && !isOpaque() - && shouldPreserveColorBufferIfTranslucent()) { - gl2.glClear(GL2.GL_DEPTH_BUFFER_BIT); - } else { - gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); - } + gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); } public void stop() { @@ -306,4 +322,22 @@ public void stop() { public void setCamera(Camera camera) { this.camera = camera; } + + @Override + public void addNotify() { + super.addNotify(); + glCanvas.addGLEventListener(this); + glCanvas.addMouseListener(this); + glCanvas.addMouseMotionListener(this); + glCanvas.addMouseWheelListener(this); + } + + @Override + public void removeNotify() { + super.removeNotify(); + glCanvas.removeGLEventListener(this); + glCanvas.removeMouseListener(this); + glCanvas.removeMouseMotionListener(this); + glCanvas.removeMouseWheelListener(this); + } } \ No newline at end of file diff --git a/src/main/java/com/marginallyclever/makelangelo/texture/TextureFactory.java b/src/main/java/com/marginallyclever/makelangelo/texture/TextureFactory.java new file mode 100644 index 000000000..797e24e45 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/texture/TextureFactory.java @@ -0,0 +1,38 @@ +package com.marginallyclever.makelangelo.texture; + +import com.jogamp.opengl.GL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * By default OpenGL uses a Texture class. this class can become invalidated if the OpenGL context is lost. + * This class is a wrapper around Texture that can be reloaded if the context is lost. + */ +public class TextureFactory { + private static final Logger logger = LoggerFactory.getLogger(TextureFactory.class); + + private static final List textures = new ArrayList<>(); + + /** + * The OpenGL context has just died. release all Textures. + */ + public static void dispose(GL gl) { + for (TextureWithMetadata tex : textures) { + tex.dispose(gl); + } + } + + /** + * Load the given file from the classpath. Make sure the size of the picture is a power of 2 + * @param name filename + * @return a texture + */ + public static TextureWithMetadata loadTexture(String name) { + TextureWithMetadata tex = new TextureWithMetadata(name); + textures.add(tex); + return tex; + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/texture/TextureWithMetadata.java b/src/main/java/com/marginallyclever/makelangelo/texture/TextureWithMetadata.java new file mode 100644 index 000000000..0bf2f5382 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/texture/TextureWithMetadata.java @@ -0,0 +1,50 @@ +package com.marginallyclever.makelangelo.texture; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureIO; +import com.marginallyclever.convenience.FileAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.IOException; + +/** + * Contains the raw OpenGL texture and the source filename. + */ +public class TextureWithMetadata { + private static final Logger logger = LoggerFactory.getLogger(TextureWithMetadata.class); + + private final String source; + private Texture texture; + + public TextureWithMetadata(String source) { + this.source = source; + } + + public Texture getTexture() { + try (BufferedInputStream bis = FileAccess.open(source)) { + return TextureIO.newTexture(bis, false, source.substring(source.lastIndexOf('.') + 1)); + } catch (IOException e) { + logger.warn("Can't load {}", source, e); + } + return null; + } + + public void bind(GL gl) { + if(texture==null) { + texture = getTexture(); + } + if(texture==null) return; + + texture.bind(gl); + } + + public void dispose(GL gl) { + if(texture!=null) { + texture.destroy(gl); + texture=null; + } + } +} diff --git a/src/main/java/com/marginallyclever/makelangelo/turtle/Turtle.java b/src/main/java/com/marginallyclever/makelangelo/turtle/Turtle.java index 927e9df20..0b3a770b8 100644 --- a/src/main/java/com/marginallyclever/makelangelo/turtle/Turtle.java +++ b/src/main/java/com/marginallyclever/makelangelo/turtle/Turtle.java @@ -548,27 +548,28 @@ public double getDrawDistance() { * Returns a point along the drawn lines of this {@link Turtle} * @param t a value from 0...{@link Turtle#getDrawDistance()}, inclusive. * @return a point along the drawn lines of this {@link Turtle} + * @deprecated since 7.63.0 use {@link TurtlePathWalker} instead. */ + @Deprecated(since="7.63.0") public Point2D interpolate(double t) { - double d=0; + double segmentDistanceSum=0; TurtleMove prev = new TurtleMove(0,0,MovementType.TRAVEL); for( TurtleMove m : history) { if(m.type == MovementType.DRAW_LINE) { double dx = m.x-prev.x; double dy = m.y-prev.y; - double change = Math.sqrt(dx*dx+dy*dy); - if(d+change>=t) { // d < t < d+change - double v = (t-d==0)? 0 : (t-d) / change; - v = Math.max(Math.min(v,1),0); + double segmentDistance = Math.sqrt(dx*dx+dy*dy); + if(segmentDistanceSum+segmentDistance>=t) { // currentDistance < t < currentDistance+segmentDistance + double ratio = Math.max(Math.min((t-segmentDistanceSum) / segmentDistance,1),0); return new Point2D( - prev.x + dx * v, - prev.y + dy * v); + prev.x + dx * ratio, + prev.y + dy * ratio); } - d += change; + segmentDistanceSum += segmentDistance; prev = m; } else if(m.type == MovementType.TRAVEL) { prev = m; - } + } // else tool change, ignore. } return new Point2D(prev.x,prev.y); } diff --git a/src/main/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalker.java b/src/main/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalker.java new file mode 100644 index 000000000..7cfb93389 --- /dev/null +++ b/src/main/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalker.java @@ -0,0 +1,93 @@ +package com.marginallyclever.makelangelo.turtle; + +import com.marginallyclever.convenience.Point2D; + +import java.util.Iterator; + +/** + * Walks a turtle along a path more efficiently than using {@link Turtle#interpolate(double)}. + */ +public class TurtlePathWalker { + private final Iterator iterator; + private TurtleMove prev; + private TurtleMove m; + private final double drawDistance; + private double tSum; + private double segmentDistance; + private double segmentDistanceSum; + + public TurtlePathWalker(Turtle turtle) { + iterator = turtle.history.iterator(); + prev = new TurtleMove(0, 0, MovementType.TRAVEL); + drawDistance = turtle.getDrawDistance(); + tSum = 0; + segmentDistanceSum = 0; + advance(); + } + + /** + * Advance to the next movement that is a draw command. + */ + private void advance() { + while (iterator.hasNext()) { + m = iterator.next(); + if (m.type == MovementType.DRAW_LINE) { + double dx = m.x - prev.x; + double dy = m.y - prev.y; + segmentDistance = Math.sqrt(dx*dx+dy*dy); + return; + } else if(m.type == MovementType.TRAVEL) { + prev = m; + } + } + // in case we run out of moves + m = null; + segmentDistance = 0; + } + + /** + * Advance along the drawn portion of the {@link Turtle} path by the given relative distance. + * @param distance the relative distance to move + * @return the new position of the turtle + * @throws IllegalArgumentException if distance is negative. + */ + public Point2D walk(double distance) { + if(distance<0) throw new IllegalArgumentException("distance must be positive"); + + tSum+=distance; + while (segmentDistanceSum <= tSum ) { + if (segmentDistanceSum+segmentDistance>=tSum) { + double dx = m.x - prev.x; + double dy = m.y - prev.y; + double ratio = Math.max(Math.min((tSum - segmentDistanceSum) / segmentDistance,1),0); + double newX = prev.x + ratio * dx; + double newY = prev.y + ratio * dy; + return new Point2D(newX, newY); + } else { + segmentDistanceSum += segmentDistance; + prev = m; + advance(); + if(m==null) break; + } + } + return new Point2D(prev.x, prev.y); + } + + public boolean isDone() { + return drawDistance <= tSum; + } + + /** + * @return the distanced travelled so far. + */ + public double getTSum() { + return tSum; + } + + /** + * @return the total distance of the path. + */ + public double getDrawDistance() { + return drawDistance; + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4d6993884..106bd7304 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -16,18 +16,48 @@ requires org.jetbrains.annotations; requires com.formdev.flatlaf; requires org.reflections; - requires org.locationtech.jts; requires org.apache.httpcomponents.httpclient; requires org.apache.httpcomponents.httpcore; + requires modern_docking.api; + requires modern_docking.single_app; + requires modern_docking.ui_ext; + requires com.github.weisj.jsvg; + requires com.marginallyclever.donatello; + requires com.marginallyclever.nodegraphcore; + + exports com.marginallyclever.communications; + exports com.marginallyclever.makelangelo.select; + exports com.marginallyclever.makelangelo.makeart; + exports com.marginallyclever.makelangelo.makeart.imagefilter; + exports com.marginallyclever.makelangelo.makeart.turtletool; + exports com.marginallyclever.makelangelo.paper; + exports com.marginallyclever.makelangelo.donatelloimpl to com.marginallyclever.nodegraphcore; + exports com.marginallyclever.makelangelo.donatelloimpl.nodes to com.marginallyclever.nodegraphcore; + exports com.marginallyclever.makelangelo.donatelloimpl.nodes.shapes to com.marginallyclever.nodegraphcore; + exports com.marginallyclever.convenience.log to ch.qos.logback.core; opens com.marginallyclever.convenience; - opens com.marginallyclever.convenience.voronoi; - opens com.marginallyclever.makelangelo.makeart.io; - opens com.marginallyclever.makelangelo.plotter.plottercontrols; opens com.marginallyclever.makelangelo.turtle; + opens com.marginallyclever.makelangelo; + opens com.marginallyclever.makelangelo.makeart.io; + opens com.marginallyclever.makelangelo.texture; opens com.marginallyclever.convenience.noise; opens com.marginallyclever.convenience.helpers; - opens com.marginallyclever.convenience.log to ch.qos.logback.core; + opens com.marginallyclever.makelangelo.preview; + + // A Java module that wants to implement a service interface from a service interface module must: + // - Require the service interface module in its own module descriptor. + // - Implement the service interface with a Java class. + // - Declare the service interface implementation in its module descriptor. + // In order to use the service, the client module must declare in its module descriptor that it uses the service. + // http://tutorials.jenkov.com/java/modules.html + uses com.marginallyclever.nodegraphcore.NodeRegistry; + provides com.marginallyclever.nodegraphcore.NodeRegistry with + com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry; + + uses com.marginallyclever.nodegraphcore.DAORegistry; + provides com.marginallyclever.nodegraphcore.DAORegistry with + com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry; } \ No newline at end of file diff --git a/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.DAORegistry b/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.DAORegistry index da0cf2df4..c5a03b0be 100644 --- a/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.DAORegistry +++ b/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.DAORegistry @@ -1 +1 @@ -com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry +com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry \ No newline at end of file diff --git a/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.NodeRegistry b/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.NodeRegistry index da0cf2df4..c5a03b0be 100644 --- a/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.NodeRegistry +++ b/src/main/resources/META-INF/services/com.marginallyclever.nodegraphcore.NodeRegistry @@ -1 +1 @@ -com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry +com.marginallyclever.makelangelo.donatelloimpl.DonatelloRegistry \ No newline at end of file diff --git a/src/main/resources/com/marginallyclever/makelangelo/icons8-reset-16.png b/src/main/resources/com/marginallyclever/makelangelo/icons8-reset-16.png new file mode 100644 index 000000000..21a688a73 Binary files /dev/null and b/src/main/resources/com/marginallyclever/makelangelo/icons8-reset-16.png differ diff --git a/src/main/resources/com/marginallyclever/makelangelo/icons8-zoom-to-fit-16.png b/src/main/resources/com/marginallyclever/makelangelo/icons8-zoom-to-fit-16.png new file mode 100644 index 000000000..0058c4430 Binary files /dev/null and b/src/main/resources/com/marginallyclever/makelangelo/icons8-zoom-to-fit-16.png differ diff --git a/src/main/resources/languages/arabic.xml b/src/main/resources/languages/arabic.xml deleted file mode 100644 index 6dd2ac29c..000000000 --- a/src/main/resources/languages/arabic.xml +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - Arabic - - Zainab Abdul Kareem Dinar - github.com/ZainabAKD - - - BorderNameورقة التخطيط - Generator_TruchetTiles.Nameمربعات الألواح - Generator_TruchetTiles.LineSpacingالمساÙØ© بين الخطوط (ملم) - Generator_TruchetTiles.LinesPerTileعدد الخطوط ÙÙŠ المربع - LissajousNameالأشكال - LissajousA منحني Ø£ أكبر من صÙر - LissajousBمنحني ب أكبر من صÙر - LissajousDeltaمثلث - SpirographNameرسم هندسي - SpirographMajorRadiusالقطر الرئيسي (R) - SpirographMinorRadiusالقطر الثانوي (r) - SpirographPScaleالحجم (p) - SpirographNumSamplesنماذج (الجودة) - SpirographEpitrochoid الدحروج - ConverterRandomLinesNameخطوط عشوائية - ConverterRandomLinesCountعدد الخطوط - StartAtLastPenDownالرجوع لأخر نقطة رسم - StartAtAddPenDownأضاÙØ© نقطة رسم - StartAtExactlyلا تÙعل شيئاً مميزاً - ConfirmQuitQuestionهل أنت متأكد أنك تريد الخروج؟ - ConfirmQuitTitleخروج - ConvertImagePaperFillملئ الورقة - ConvertImagePaperFitملائمة الصورة على الورقة - Converter_CMYK_Crosshatch.NameCMYK - Converter_CMYK_Crosshatch.Passesتخطي - Converter_CMYK_Crosshatch.Note - Converter_CMYK_Spiral.NameCMYK حلزوني - LoadFilePanel.titleمعاينة المل٠- OKمواÙÙ‚ - Cancelالغاء - SaveØ­Ùظ - Translateنقل - Scaleالحجم - Rotateتدوير - Widthالعرض - HeightالأرتÙاع - PaperSettings.Titleاعدادات الورقة - PaperSettings.PaperSizeحجم الورقة - PaperSettings.PaperWidthعرض الورقة (ملم) - PaperSettings.PaperHeightأرتÙاع الورقة (ملم) - PaperSettings.ShiftXأنتقال الى اليسار - PaperSettings.ShiftYانتقال الى اعلى - PaperSettings.Rotationتدوير - PaperSettings.Landscapeمشهد - PaperSettings.PaperMarginالحوا٠(%) - PaperSettings.PaperColorلون الورقة - TitlePrefixمايكل انجلو - LoadErrorتعذر تحميل المل٠- SaveErrorتعذر Ø­Ùظ المل٠- ConversionStyleنمط - ConversionFillملئ - OpenFileChooser.FileTypeImage(%1) نوع الصورة - OpenFileChooser.AllSupportedFilesالملÙات المدعومة - MetricsPreferences.Titleقياس - MetricsPreferences.collectAnonymousMetrics - أتعهد بمشاركة بيانات الأستخدام المجهول مع شركة
Maginally Clever Robots ltd ]]>
- MetricsPreferences.collectAnonymousMetricsOnUpdateالسماح للتطبيق بجمع البيانات مجهولة المصدر

يمكنك تغيير الصلاحية ÙÙŠ أي وقت بالذهاب الى Makelangelo > التÙضيلات > القياس]]>
- SoundPreferences.Titleالأصوات - SoundPreferences.Connectاتصال - SoundPreferences.Disconnectالغاء الأتصال - SoundPreferences.FinishConvertإنهاء التحويل - SoundPreferences.FinishDrawإنهاء الرسم - GFXPreferences.Titleالرسومات - GFXPreferences.showPenUpإظهار تحركات القلم - GFXPreferences.antialiasخطوط انسيابية - GFXPreferences.speedVSQuality تسريع (اهمال الدقة) - GFXPreferences.showAllWhileDrawingإظهار كامل الصورة أثناء الرسم - GFXPreferences.dragSpeedسرعة الكاميرا - LanguagePreferences.Titleاللغة - StartAtتحديد نقطة البداية - Robotمايكل انجلو - UpdateNoticeيتوÙر إصدار جديد من التطبيق
قم بزيارة https://www.marginallyclever.com/product/makelangelo-software/ للحصول على أحدث المميزات]]>
- UpToDateلديك أحدث نسخة من التطبيق - UpdateCheckFailedعذرا, حصل خطأ ما, الرجاء زيارة http://www.marginallyclever.com/ للعثور على حل مناسب - MenuMakelangeloمل٠- ApplicationSettings.titleتÙضيلات - MenuUpdateبحث عن تحديثات - MenuQuitخروج - MenuForumsزيارة نادي الدعم - MenuAboutحول - MenuSettingsاعدادات - MenuOpenFileÙتح مل٠- MenuSaveFileØ­Ùظ مل٠- MenuReopenFileأعادة Ùتح أخر مل٠- MenuViewعرض - MenuView.zoomInتكبير - MenuView.zoomOutتصغير - MenuView.zoomFitملائمة الشاشة - ShowLogإظهار السجل - SierpinskiTriangleNameمثلث متكرر - Converter_VoronoiStippling.Nameتنقيط - VoronoiZigZagNameتعرج - - Converter_Voronoi.Nameمخطط - Converter_VoronoiStippling.CellCountعدد الخلايا - Converter_VoronoiStippling.DotMaxحجم النقطة الأكبر - Converter_VoronoiStippling.DotMinحجم النقطة الأصغر - BoxGeneratorNameإنشاء صندوق - BoxGeneratorMaxSizeالحجم الأقصى - BoxGeneratorCutoffقطع - HilbertCurveNameمنحني هيلبرت - HilbertCurveSizeالحجم - HilbertCurveOrderالترتيب - penDiameterقطر القلم - LSystemBranchesتÙرعات - LSystemOrderScaleمقياس - LSystemAngleمدى الزاوية - LSystemNoiseتشويش - Sizeالحجم - Flapحجم الحوا٠- Modelنموذج - Lengthطول - PulseLineNameخطوط الدقة - YourMsgHereNameأكتب رسالتك هنا - SpiralNameحلزوني - SpiralPulseNameشكل حلزوني دقيق - SharePromoجرب هاشتاك #plotter - DialogAbout.AboutHTML - -

Makelangelo %VERSION% %DETAILED_VERSION%

-

- http://www.makelangelo.com/ -

-

To get the latest version please visit
- https://www.marginallyclever.com/product/makelangelo-software/

-

Created by Dan Royer.

-

Additional software and hardware contributions by
- Joseph Cottam, Peter Colapietro, Miguel Sánchez
- Mark Raynsford, and
Kington Chu.

- - - ]]>
- MenuResetMachinePreferencesWarningامتاكد أنك تريد حذ٠تÙضيلاتك؟ - MenuGenerateأنشاء ÙÙ† - MenuNewFileمل٠جديد - MenuCaptureImageإلتقاط من الكاميرا - LSystemTreeNameإنشاء شجرة - KochTreeNameمنحنى نجمي - FibonacciSpiralNameمنحني النسبة الذهبية - DragonNameمنحنى التنين - ClickAndDragسحب واÙلات - ConverterWanderNameأستكشا٠- ConverterWanderLineCountخطوط - ConverterMultipassNameMultipass - ConverterMultipassAngleالزاوية (بالدرجات) - ConverterMultipassLevelsمستويات - firmwareVersionBadTitleتحديث البرنامج - TextSizeالحجم - FontFaceنوع الخط - TextMessageرسالة - PiCaptureAction.CaptureImageTitleالتقاط صورة الكاميرا - PiCaptureAction.CaptureImageالتقاط - PiCaptureAction.UseCaptureأستخدام - PiCaptureAction.CancelCaptureإلغاء - PiCaptureAction.AWBموازنة تلقائية للون الأبيض - PiCaptureAction.DRCالمدى الديناميكي - PiCaptureAction.Exposureالتعرض - PiCaptureAction.Contrastالتباين - PiCaptureAction.Qualityالجودة - PiCaptureAction.Sharpnessالوضوح - PiCaptureAction.OffإطÙاء - PiCaptureAction.Autoتلقائي - PiCaptureAction.Sunمشمس - PiCaptureAction.Cloudغائم - PiCaptureAction.Shadeظل - PiCaptureAction.Tungstenأضاءة صÙراء - PiCaptureAction.Fluorescentأضاءة Ùلورسنت - PiCaptureAction.Incandescentسطوع - PiCaptureAction.FlashÙلاش - PiCaptureAction.HorizonالأÙÙ‚ - PiCaptureAction.Highعالي - PiCaptureAction.Mediumمتوسط - PiCaptureAction.LowمنخÙض - PiCaptureAction.Antishakeمنع الأهتزاز - PiCaptureAction.Backlightضوء خلÙÙŠ - PiCaptureAction.Beachشاطئ - PiCaptureAction.Fireworksألعاب نارية - PiCaptureAction.FixedFPSتثبيت عدد الاطارات - PiCaptureAction.Nightالوضع الليلي - PiCaptureAction.NightPreviewعرض ÙÙŠ الوضع الليلي - PiCaptureAction.Snowمثلج - PiCaptureAction.Sportsرياضي - PiCaptureAction.Spotlightتركيز - PiCaptureAction.VerylongVerylong - Importإستيراد - Exportتصدير - Resetإعادة ضبط - Helpمساعدة - Art Pipelineالادوات - CrosshatchCrosshatch - MoireNameMoire - horizontalØ£Ùقي - verticalعمودي - SandyNoble.titleSandy Noble Style - SandyNoble.ringsحلقات - SandyNoble.centerالمنتص٠- ConverterIntensityالحدة - top rightاعلى اليمين - top leftأعلى اليسار - bottom rightأسÙÙ„ اليمين - bottom leftأسÙÙ„ اليسار - centerالمركز - Spiral.toCornersإلتÙا٠عند الزاوية - SpiralPulse.intensityالحدّة - SpiralPulse.spacingالمساÙØ© بين الحلقات - SpiralPulse.heightإرتÙاع الذبذبة - SelectImageConverterPanel.Contrastتعديل التباين - Converter_VoronoiStippling.DrawBordersرسم الحدود - Converter_VoronoiStippling.Cutoffقطع - ConverterWanderCMYKCMYK? - FillPageNameملئ الصÙحة - GosperCurveNameGosper منحني - pass90Pass 90 - pass75Pass 75 - pass45Pass 45 - pass15Pass 15 - Directionاتجاه - FlipHدوران اÙقي - FlipVدوران عمودي - Reorderاعادة ترتيب - Simplifyتبسيط - GraphPaperNameورقة رسم بياني - Packageتخطيط صندوق - Polyederصندوق متعدد الأشكال - FirmwareUpdateتحديث البرنامج - PortمنÙØ° - OpenPaperSettingsإعدادات الورقة - OpenPlotterSettingsإعدادات الرسام - OpenÙتح مل٠- InfillTurtleAction.titleFill in closed loops - LogPanel.LogFilesسجل الدخول - LogPanel.Titleدخول - LogPanel.CopyClipboardنسخ - QuestionTitleسؤال - InfoTitleمعلومات - ErrorTitleخطأ - MenuItemPayPalDonationتبرع - - EstimatedTimeIsالوقت المقدر هو %1 - firmwareVersionBadMessageلا يمكنني التعر٠على إصدار البرنامج
الرجاء قم بزيارة https://www.marginallyclever.com/product/makelangelo-firmware/
لتحميل احدث إصدار
الرجاء زيارة https://www.marginallyclever.com/product/makelangelo-software/ للعثور على آخر التطبيقات]]>
- - TurtleGenerators.LearnMore.Link.TextمعرÙØ© المزيد - LoadScratch3.foreverNotAllowedلا يمكن تنÙيذ الأمر - SaveGCode.splitGCodeTitleتوجد عدة الوان! - SaveGCode.splitGCodeQuestionهناك تغير ÙÙŠ الأدوات المستخدمة ÙÙŠ هذا الرسم, Ø­Ùظ ÙÙŠ مل٠مختلÙØŸ - - Converter_CMYK_Circles.nameCMYK دوائر - Converter_CMYK_Circles.maxCircleSizeأكبر حجم للدائرة - - Converter_EdgeDetection.nameكش٠الحاÙØ© - Converter_EdgeDetection.passesPasses - Converter_EdgeDetection.stepSizeحجم الخطوة - Converter_EdgeDetection.sampleSizeحجم النموذج - - Generator_FlowField.nameFlow field - Generator_FlowField.scaleXX محور - Generator_FlowField.scaleYY محور - Generator_FlowField.offsetXX إزاحة - Generator_FlowField.offsetYY إزاحة - Generator_FlowField.stepSizeحجم الخطوة - Generator_FlowField.stepVariationتنوع الخطوة - Generator_FlowField.stepLengthطول الخطوة - Generator_FlowField.fromEdgeاستمر - Generator_FlowField.rightAngleإضاÙØ© خطوط متعامدة - Generator_FlowField.noiseTypeنوع الضوضاء - - VoronoiZigZag.optimizePathتخصيص المسار - Generator_GridFit.Nameملائمة للشبكة - Generator_GridFit.marginالحد الأدنى للهامش على كل جانب (mm) - Generator_GridFit.cellsWideخلايا Ø£Ùقية - Generator_GridFit.cellsHighخلايا عمودية - PlotterSettingsPanel.TabGCodeمخصص GCode - - PlotterSettings.zMotorTypeنوع محور Z - PlotterSettings.zMotorType.servoتكرار - PlotterSettings.zMotorType.stepperStepper - PlotterSettings.StartGcodeبدء GCode - PlotterSettings.EndGcodeإنهاء GCode - PlotterSettings.minimumPlannerSpeedادنى سرعة تخطيط (mm/s) - PlotterSettings.blockBufferSizeموازنة حجم الكتلة - PlotterSettings.segmentsPerSecondعدد الأجزاء ÙÙŠ الثانية - PlotterSettings.minSegmentLengthأقل طول للقطعة (ملم) - PlotterSettings.minSegTimeأقل وقت للقطعة (ns) - PlotterSettings.handleSmallSegmentsمعالحة القطع الصغيرة - PlotterSettings.minAccelerationأقل تسريع (mm/s²) - - PlotterSettingsPanel.TitlePlotter إعدادات - PlotterSettingsPanel.TabEssentialأساسي - PlotterSettingsPanel.TabPenقلم - PlotterSettingsPanel.TabSimulationمحاكاة - PlotterSettingsPanel.MachineWidthعرض الماكنة (ملم) - PlotterSettingsPanel.MachineHeightطول الماكنة (ملم) - PlotterSettingsPanel.ServoLengthNeededطول سلك التكرار (Ù…) - PlotterSettingsPanel.StepperLengthNeededStepper طول سلك (m,each) - PlotterSettingsPanel.BeltLengthNeededعرض نطاق التوقيت (m,each) - PlotterSettingsPanel.penToolDiameterالقطر (ملم) - PlotterSettingsPanel.penToolMaxFeedRateسرعة الحركة (mm/min) - PlotterSettingsPanel.penToolUpالزاوية العليا (درجة) - PlotterSettingsPanel.penToolDownالزاوية السÙلى (درجة) - PlotterSettingsPanel.penToolLiftSpeedوقت رÙع القلم (ms) - PlotterSettingsPanel.penToolLowerSpeedوقت إستخدام القلم (ms) - PlotterSettingsPanel.Speedسرعة الرسم (mm/min) - PlotterSettingsPanel.AdjustAccelerationالتسارع (mm/s²) - PlotterSettingsPanel.pen up colorUp color - PlotterSettingsPanel.pen down colorDefault down color - - PlotterSettingsManagerPanel.RemoveProfile- - PlotterSettingsManagerPanel.AddProfile+ - PlotterSettingsManagerPanel.NewProfileNameإسم جديد - PlotterSettingsManagerPanel.NewProfileNameAlreadyExistsالاسم مستخدم بالÙعل, حاول مجددا - PlotterSettingsManagerPanel.NewProfileNameCannotBeBlankلا يمكن أن يكون الاسم Ùارغا, حاول مجددا - - FirmwareUploader.helpمساعدة]]> - - FirmwareUploaderPanel.statusنسبة تحميل البرنامج - FirmwareUploaderPanel.startM5M5 - FirmwareUploaderPanel.startHugeضخم - FirmwareUploaderPanel.avrdudeNotDownloadedAVRDude لم يتم تنزيل - FirmwareUploaderPanel.notFoundلا يمكن إيجاد %1 - FirmwareUploaderPanel.failedÙشل تحميل البرنامج - FirmwareUploaderPanel.noPortSelectedلم يتم إختيار منÙØ° - FirmwareUploaderPanel.downloadFailedÙشل التنزيل - FirmwareUploaderPanel.finishedتم الانتهاء - - DefaultTurtleRenderer.nameقياسي - BarberPoleTurtleRenderer.nameBarber pole - SeparateLoopTurtleRenderer.nameSeparate loops - MarlinSimulationVisualizer.nameMarlin simulation - - ChooseConnection.ButtonConnectإتصال - ChooseConnection.ButtonDisconnectالغاء الاتصال - - PlotterControls.Titleادوات تحكم الروبوت - PlotterControls.ConnectControlsاتصال - PlotterControls.DrawControlsادوات تحكم الرسم - PlotterControls.FindHomeالبحث ÙÙŠ الصÙحة الرئيسية - PlotterControls.Pauseإيقا٠مؤقت - PlotterControls.Rewindإعادة - PlotterControls.Playرسم - PlotterControls.EmergencyStopتوق٠طارئ - PlotterControls.MarlinTabMarlin - PlotterControls.ProgramTabالبرنامج - PlotterControls.JogTabادوات تحكم الروبوت - PlotterControls.AdvancedControlsأدوات تحكم متقدمة - PlotterControls.homeXYFirstرجوع الطابعة للصÙحة الرئيسية - PlotterControls.didNotFindخطأ ÙÙŠ الاتصال بالروبوت - PlotterControls.communicationFailureلا توجد إستجابة من الروبوت, الرجاء التأكد من الكابل او سرعة الرسم - PlotterControls.haltedإيقا٠الطابعة - PlotterControls.Stepخطوة - - RobotMenu.RobotStyleنمط الروبوت - RobotMenu.RenderStyleنمط التحويل - RobotMenu.OpenControls الاعدادات - RobotMenu.SaveGCodeØ­Ùظ Gcode الى مل٠- RobotMenu.GetTimeEstimateتقدير الوقت - - JogInterface.FindHomeالصÙحة الرئيسية - JogInterface.PenUpبدء استخدام القلم - JogInterface.PenDownإيقا٠استخدام القلم - JogInterface.DisengageMotorsقطع الاتصال - JogInterface.EngageMotorsاتصال - - ConversationHistory.Clearحذ٠- ConversationHistory.SaveØ­Ùظ - ConversationHistory.Copyنسخ - - CartesianButtons.buttonCenter - - Converter_IntensityToHeight.nameنسبة الكثاÙØ© الى الارتÙاع - Converter_IntensityToHeight.spacingتباعد الموجة - Converter_IntensityToHeight.sampleRateنسبة العينة - Converter_IntensityToHeight.waveIntensityشدة الموجة - - Converter_QuadTreeInstantQuad tree instant - Converter_QuadTreeInstant.maxDepthأقصى عمق - Converter_QuadTreeInstant.baseCutOffBase cut-off - Converter_QuadTreeInstant.cutOffIncrementCut-off increment -
diff --git a/src/main/resources/languages/chinese.xml b/src/main/resources/languages/chinese.xml deleted file mode 100644 index 241aa851a..000000000 --- a/src/main/resources/languages/chinese.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - Chinese - - Kmimax(kmimax2016@gmail.com) - - - BorderName纸轮廓 - LissajousNameLissajous - LissajousAA (>0) - LissajousBB (>0) - LissajousDeltaDelta (0...1) - SpirographName螺旋图 - SpirographMajorRadius大åŠå¾„(R) - SpirographMinorRadiuså°åŠå¾„(r) - SpirographPScaleå•ä½ (p) - SpirographNumSamples样本(æ•°) - SpirographEpitrochoid是æµè¡Œçš„ - ConverterRandomLinesNameéšæœºçº¿ - ConverterRandomLinesCount线数 - StartAtLastPenDown备份到最åŽè½ç¬” - StartAtAddPenDown在此处添加è½ç¬”命令 - StartAtExactlyæ— ç‰¹æ®Šå¤„ç† - ConfirmQuitQuestion确定è¦é€€å‡ºå—? - ConfirmQuitTitle退出 - ConvertImagePaperFill填满整张纸 - ConvertImagePaperFit使图åƒé€‚åˆçº¸å¼  - Cancelå–消 - Saveä¿å­˜ - PaperSettings.PaperWidth绘图纸宽度 - PaperSettings.PaperHeight绘图纸高度 - TitlePrefixMakelangelo - LoadError无法加载文件: - SaveError无法ä¿å­˜æ–‡ä»¶: - FlipH水平翻转 - FlipV垂直翻转 - ConversionStyleæ ·å¼ - ConversionFillå¡«å…… - OpenFileChooser.FileTypeImageå›¾åƒ (%1) - OpenFileChooser.AllSupportedFilesAll supported files - MetricsPreferences.Title指标 - MetricsPreferences.collectAnonymousMetricsI consent to sharing anonymous usage data
with Marginally Clever Robots, Limited.]]>
- MetricsPreferences.collectAnonymousMetricsOnUpdateMay Marginally Clever Robots, Ltd. collect anonymous usage metrics about this app?

You can update this any time in Makelangelo > Preferences > Metrics]]>
- SoundPreferences.Title声音 - SoundPreferences.Connect连接 - SoundPreferences.Disconnectæ–­å¼€ - SoundPreferences.FinishConvert完æˆè½¬æ¢ - SoundPreferences.FinishDraw完æˆç»˜åˆ¶ - GFXPreferences.Title图形 - GFXPreferences.showPenUp显示抬笔移动 - GFXPreferences.antialiasAntialias lines - GFXPreferences.speedVSQuality速度vsè´¨é‡ - GFXPreferences.showAllWhileDrawingç»˜å›¾æ—¶æ˜¾ç¤ºæ•´ä¸ªå›¾åƒ - LanguagePreferences.Title语言 - StartAt开始于 - PlotterControls.Pauseæš‚åœ - RobotMakelangelo - RobotMenu.OpenControlsOpen Controls - PlotterControls.RewindRewind - PlotterControls.PlayDraw - JogInterface.PenUp抬笔 - JogInterface.PenDownè½ç¬” - UpdateNoticeA new version of this software is available.
Please visit
https://www.marginallyclever.com/product/makelangelo-software/ to get the new hotness.]]>
- UpToDate该软件是最新的. - UpdateCheckFailed对ä¸èµ·ï¼Œå¤±è´¥äº†ã€‚ 请访问https://www.marginallyclever.com/ 检查自己. - MenuMakelangeloMakelangelo - ApplicationSettings.title首选项 - MenuUpdate检查更新 - MenuQuit退出 - MenuForumsåœ¨çº¿å¸®åŠ©è®ºå› - MenuAbout关于 - MenuSettings设置 - MenuOpenFile打开文件... - MenuReopenFileé‡æ–°æ‰“开最åŽä¸€ä¸ªæ–‡ä»¶ - RobotMenu.SaveGCodeä¿å­˜åˆ°æ–‡ä»¶/ SDå¡ - MenuView查看 - MenuView.zoomIn放大 + - MenuView.zoomOutç¼©å° - - MenuView.zoomFit适度缩放 - ShowLog显示日志 - SierpinskiTriangleNameSierpinski三角形形 - Converter_VoronoiStippling.NameVoronoi点画 - VoronoiZigZagNameVoronoi之形 - Converter_VoronoiStippling.CellCountå•å…ƒæ•° - Converter_VoronoiStippling.DotMax最大点尺寸 - Converter_VoronoiStippling.DotMin最å°ç‚¹å°ºå¯¸ - BoxGeneratorNameå°ç›’ - BoxGeneratorMaxSize最大尺寸 - BoxGeneratorCutoff切断 - HilbertCurveNameHilbert曲线形 - HilbertCurveSize尺寸 - HilbertCurveOrderæ¬¡åº - LSystemBranches分支 - LSystemOrderScale比例 - LSystemAngle角度跨度 - PulseLineName脉冲线 - YourMsgHereName您的留言在这里 - SpiralName螺旋 - SpiralPulseName脉冲螺旋 - SharePromo试用社交媒体标签#绘图仪 - JogInterface.DisengageMotors关闭 - JogInterface.EngageMotorså¯ç”¨ - DialogAbout.AboutHTML - -

Makelangelo %VERSION% %DETAILED_VERSION%

-

- http://www.makelangelo.com/ -

-

To get the latest version please visit
- https://www.marginallyclever.com/product/makelangelo-software/

-

Created by Dan Royer.

-

Additional software and hardware contributions by
- Joseph Cottam, Peter Colapietro, Miguel Sè°©nchez
- Mark Raynsford, and
Kington Chu.

- - - ]]>
- MenuResetMachinePreferencesWarning确定è¦åˆ é™¤æ‰€æœ‰å‚æ•°å—? - MenuGenerate产生艺术 - MenuNewFile新建 - MenuCaptureImage从相机æ•æ‰ - LSystemTreeNameL系统树形 - KochTreeNameKoch曲线形 - FibonacciSpiralNameFibonacci螺旋形 - DragonName龙形 - ClickAndDrag点击&拖动 - ConverterWanderName漫游 - ConverterWanderLineCount# 线数 - ConverterMultipassName多线段 - ConverterMultipassAngle角(度) - ConverterMultipassLevels层 - firmwareVersionBadTitle固件å‡çº§ - firmwareVersionBadMessageThe firmware (code in the brain of your robot) is not the version I expect. I found v%VERSION%.
Please visit https://www.marginallyclever.com/product/makelangelo-firmware/
for the latest firmware.
Please visit https://www.marginallyclever.com/product/makelangelo-software/ for the latest software.]]>
- TextSize尺寸 - TextMessageä¿¡æ¯ - PiCaptureAction.CaptureImageTitle获å–ç›¸æœºå›¾åƒ - PiCaptureAction.CaptureImageèŽ·å– - PiCaptureAction.UseCapture使用 - PiCaptureAction.CancelCaptureå–消 - PiCaptureAction.AWB自动白平衡 - PiCaptureAction.DRC动æ€èŒƒå›´ - PiCaptureAction.Exposureæ›å…‰ - PiCaptureAction.Contrast对比度 - PiCaptureAction.Quality画质 - PiCaptureAction.Sharpnessé”度 - PiCaptureAction.Off关闭 - PiCaptureAction.Auto自动 - PiCaptureAction.Sun阳光 - PiCaptureAction.Cloud多云 - PiCaptureAction.Shade阴影 - PiCaptureAction.Tungstené’¨ä¸ç¯ - PiCaptureAction.Fluorescentè§å…‰ç¯ - PiCaptureAction.Incandescentç™½ç‚½ç¯ - PiCaptureAction.Flashé—ªå…‰ç¯ - PiCaptureAction.Horizon水平线 - PiCaptureAction.High高 - PiCaptureAction.Medium中 - PiCaptureAction.Low低 - PiCaptureAction.Antishake防抖 - PiCaptureAction.BacklightèƒŒå…‰ç¯ - PiCaptureAction.Beach海滩 - PiCaptureAction.Fireworks烟花 - PiCaptureAction.FixedFPS固定FPS - PiCaptureAction.Night夜晚 - PiCaptureAction.NightPreviewé»„æ˜ - PiCaptureAction.Snow雪景 - PiCaptureAction.Sportsè¿åŠ¨ - PiCaptureAction.Spotlightèšå…‰ç¯ - PiCaptureAction.Verylong长时间 - Import导入 - Export导出 - Reseté‡ç½® - Help帮助 - Art Pipeline艺术途径 - PaperSettings.PaperColor纸张颜色 - Crosshatch交å‰å½±çº¿ - MoireName云纹 - horizontalæ°´å¹³ - verticalåž‚ç›´ - SandyNoble.titleSandy风格 - ConverterIntensity强度 - top rightå³ä¸Š - top left左上方 - bottom rightå³ä¸‹ - bottom left左下方 - center中心 - Spiral.toCornersèžºæ—‹åˆ°è§’è½ - SpiralPulse.spacing环间隙 - SpiralPulse.height脉冲高度 - Converter_VoronoiStippling.Cutoff低通截止 - Converter_VoronoiStippling.DrawBorders画边框? - ConverterWanderCMYKCMYK? - FillPageNameå¡«å……é¡µé¢ - GosperCurveNameGosper曲线 - diff --git a/src/main/resources/languages/dutch.xml b/src/main/resources/languages/dutch.xml deleted file mode 100644 index 8c757b7fa..000000000 --- a/src/main/resources/languages/dutch.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - Nederlands - - Bart Derks (bderks@zeelandnet.nl) - - - OpenFileChooser.AllSupportedFilesAll supported files - ApplicationSettings.titleInstellingen - BoxGeneratorNameBoxxy - CancelAnnuleren - ClickAndDragKlik & Sleep - ConversionStyleConversie stijl - Converter_VoronoiStippling.CellCountAantal Cellen - Converter_VoronoiStippling.DotMaxMax stip grootte - Converter_VoronoiStippling.DotMinMininmum stip grootte - Converter_VoronoiStippling.NameVoronoi stippels - DragonNameDragon Fractal - GFXPreferences.TitleGraphics - GFXPreferences.antialiasAnti-alias lijnen - GFXPreferences.showAllWhileDrawingToon volledige afbeelding tijdens het tekenen - GFXPreferences.showPenUpLaat pen-up bewegingen zien - GFXPreferences.speedVSQualitySnelheid boven kwaliteit - HilbertCurveNameHilbert Curve - HilbertCurveOrderOrder - HilbertCurveSizeGrootte - JogInterface.DisengageMotorsMotoren uitzetten - JogInterface.PenDownPen omlaag - JogInterface.PenUpPen omhoog - KochTreeNameKoch Curve Fractal - LSystemTreeNameL Systeem Tree Fractal - LanguagePreferences.TitleTalen - MenuAboutOver - MenuGenerateGenereer kunst - MenuMakelangeloMakelangelo - MenuNewFileNieuw - MenuOpenFileOpen bestand... - MenuQuitStoppen - MenuResetMachinePreferencesWarningWeet je zeker dat je alle instellingen wil resetten? - MenuSettingsInstellingen - MenuUpdateControleer op updates - MenuView.zoomFitComplete afbeelding weergeven - MenuView.zoomInZoom + - MenuView.zoomOutZoom - - MenuViewBekijk - OpenFileChooser.FileTypeImageAfbeelding (%1) - PaperSettings.PaperHeightPapier hoogte - PaperSettings.PaperWidthPapier breedte - PlotterControls.PausePause - PlotterControls.PlayDraw - PlotterControls.RewindRewind - PulseLineNamePulse Lijn - RobotMakelangelo - RobotMenu.OpenControlsOpen Controls - RobotMenu.SaveGCodeBewaar Gcode in bestand/SD - SaveOpslaan - SharePromoGebruik Twitter tag #makelangelo om je kunst te delen en te kijken wat anderen maakten. - ShowLogBekijk Log - SoundPreferences.ConnectVerbinden - SoundPreferences.DisconnectUitschakelen - SoundPreferences.FinishConvertVoltooien Conversie - SoundPreferences.FinishDrawVoltooien tekening - SoundPreferences.TitleGeluiden - SpiralNameSpiraal - StartAtStart bij - TitlePrefixMakelangelo - UpToDateDeze software is up-to-date. - UpdateCheckFailedSorry, mislukt. Bezoek http://www.marginallyclever.com/ voor evt. update/informatie. - UpdateNoticeEen nieuwe versie van deze software is beschikbaar.
\nBezoek https://www.marginallyclever.com/product/makelangelo-software/ om de laatste versie te halen.]]>
- YourMsgHereNameUw bericht hier -
diff --git a/src/main/resources/languages/english.xml b/src/main/resources/languages/english.xml deleted file mode 100644 index d4d37b554..000000000 --- a/src/main/resources/languages/english.xml +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - English - - Dan Royer (dan@marginallyclever.com) - - - - ApplicationSettings.titlePreferences - Art PipelineTools - BarberPoleTurtleRenderer.nameBarber pole - BorderNameOutline paper - BoxGeneratorCutoffCutoff - BoxGeneratorMaxSizeMax size - BoxGeneratorNameBoxxy - CancelCancel - CartesianButtons.buttonCenter - ChooseConnection.ButtonConnectConnect - ChooseConnection.ButtonDisconnectDisconnect - ClickAndDragClick & Drag - ConfirmQuitQuestionAre you sure you want to quit? - ConfirmQuitTitleExit - ConversationHistory.ClearClear - ConversationHistory.CopyCopy - ConversationHistory.SaveSave - ConversionFillFill - ConversionStyleStyle - ConvertImagePaperCenterCenter image - ConvertImagePaperFillFill entire paper - ConvertImagePaperFitFit image on paper - ConverterIntensityIntensity - ConverterMultipassAngleAngle (degrees) - ConverterMultipassLevelsLevels - ConverterMultipassNameMultipass - ConverterRandomLinesCountNumber of lines - ConverterRandomLinesNameRandom lines - ConverterWanderCMYKCMYK? - ConverterWanderLineCount# Lines - ConverterWanderNameWander - Converter_CMYK_Circles.maxCircleSizemax circle size - Converter_CMYK_Circles.nameCMYK circles - Converter_CMYK_Crosshatch.NameCMYK - Converter_CMYK_Crosshatch.Note - Converter_CMYK_Crosshatch.PassesPasses - Converter_CMYK_Spiral.NameCMYK Spiral - Converter_EdgeDetection.borderDraw border - Converter_EdgeDetection.nameEdge detection - Converter_EdgeDetection.passesPasses - Converter_EdgeDetection.sampleSizeSampleSize - Converter_EdgeDetection.stepSizeStep size - Converter_IntensityToHeight.nameIntensity to height - Converter_IntensityToHeight.sampleRateSample rate - Converter_IntensityToHeight.spacingWave spacing - Converter_IntensityToHeight.waveIntensityWave intensity - Converter_PulseCMYK.NamePulse CMYK - Converter_PulseCMYK.SampleRateSample rate - Converter_QuadTreeInstant.baseCutOffBase cut-off - Converter_QuadTreeInstant.cutOffIncrementCut-off increment - Converter_QuadTreeInstant.maxDepthMax depth - Converter_QuadTreeInstantQuad tree instant - Converter_TruchetFromImage.linesPerTileLines per tile - Converter_TruchetFromImage.nameTruchet from image - Converter_TruchetFromImage.spacingSpace between lines - Converter_Voronoi.NameVoronoi diagram - Converter_Voronoi.ShowCentersShow centers - Converter_VoronoiStippling.CellCountNumber of cells - Converter_VoronoiStippling.CutoffCutoff - Converter_VoronoiStippling.DotMaxMax dot size - Converter_VoronoiStippling.DotMinMin dot size - Converter_VoronoiStippling.DrawBordersDraw borders? - Converter_VoronoiStippling.NameVoronoi stipples - CrosshatchCrosshatch - DefaultTurtleRenderer.nameDefault - DialogAbout.AboutHTML

Makelangelo %VERSION% %DETAILED_VERSION%

http://www.makelangelo.com/

To get the latest version please visit
https://www.marginallyclever.com/product/makelangelo-software/

Created by Dan Royer.

Additional contributions by
Joseph Cottam, Peter Colapietro, Miguel Sánchez
Mark Raynsford, and
Kington Chu.

]]>
- DirectionDirection - DirectionLoopTurtleRenderer.nameDirection loops - DragonNameDragon - ErrorTitleError - EstimatedTimeIsEstimated time is %1 - ExportExport - FibonacciSpiralNameFibonacci Spiral - FillPageNameFill page - FirmwareUpdateUpdate firmware - FirmwareUploader.helphelp]]> - FirmwareUploaderPanel.avrdudeNotDownloadedAVRDude not downloaded. - FirmwareUploaderPanel.downloadFailedDownload failed - FirmwareUploaderPanel.failedFirmware upload failed. - FirmwareUploaderPanel.finishedFinished OK! - FirmwareUploaderPanel.noPortSelectedNo port selected. - FirmwareUploaderPanel.notFound%1 not found. - FirmwareUploaderPanel.startHugeHuge - FirmwareUploaderPanel.startM5M5 - FirmwareUploaderPanel.statusFirmware upload status - FlapFlap - FlipHFlip horizontal - FlipVFlip vertical - FontFaceTypeface - GFXPreferences.TitleGraphics - GFXPreferences.antialiasAntialias lines - GFXPreferences.dragSpeedcamera travel speed - GFXPreferences.showAllWhileDrawingShow entire image while drawing - GFXPreferences.showPenUpShow travel moves - GFXPreferences.speedVSQualitySpeed over quality - Generator.randomSeedRandom seed value - Generator_FlowField.fromEdgeContinuous - Generator_FlowField.nameFlow field - Generator_FlowField.noiseTypeNoise type - Generator_FlowField.offsetXX offset - Generator_FlowField.offsetYY offset - Generator_FlowField.rightAngleAdd orthogonal lines - Generator_FlowField.scaleXX scale - Generator_FlowField.scaleYY scale - Generator_FlowField.stepLengthStep length - Generator_FlowField.stepSizeStep size - Generator_FlowField.stepVariationStep variation - Generator_GridFit.NameRectangles - Generator_GridFit.cellsHighCells vertical - Generator_GridFit.cellsWideCells horizontal - Generator_GridFit.marginMinimum margin each side (mm) - Generator_GridHexagons.NameHexagons - Generator_GridHexagons.radiusMajor radius - Generator_MazeCircle.nameCircle - Generator_MazeCircle.ringsRings - Generator_MazeHoneycomb.nameHoneycomb - Generator_MazeRectangle.columnsColumns - Generator_MazeRectangle.nameRectangle - Generator_MazeRectangle.rowsRows - Generator_Spiral.nameSpiral - Generator_Spiral.radiusRadius - Generator_TruchetTiles.LineSpacingSpace between lines (mm) - Generator_TruchetTiles.LinesPerTileLines per tile - Generator_TruchetTiles.NameTruchet Tiles - Generator_TruchetTiles.allowAllow '%1' - GosperCurveNameGosper curve - GraphPaperNameGraph Paper - HeightHeight - HelpHelp - HilbertCurveNameHilbert Curve - HilbertCurveOrderOrder - HilbertCurveSizeSize - ImportImport - InfillTurtleAction.titleFill in closed loops - InfoTitleInformation - JogInterface.DisengageMotorsDisengage - JogInterface.EngageMotorsEngage - JogInterface.FindHomeHome - JogInterface.PenDownPen down - JogInterface.PenUpPen up - KochTreeNameKoch Curve - LSystemAngleAngle span - LSystemBranchesBranches - LSystemNoiseNoise - LSystemOrderScaleScale - LSystemTreeNameL System Tree - LanguagePreferences.TitleLanguage - LengthLength - LineWeightByImageIntensity.imageImage - LineWeightByImageIntensity.nameLine image weight - LineWeightByImageIntensity.thicknessThickness - LissajousAA (>0) - LissajousBB (>0) - LissajousDeltaDelta (0...1) - LissajousNameLissajous - LoadErrorFile could not be loaded: - LoadFilePanel.titleLoad file preview - LoadScratch3.foreverNotAllowedForever loops are forever forbidden. - LogPanel.CopyClipboardCopy - LogPanel.LogFilesLog files - LogPanel.TitleLog - MarlinSimulationVisualizer.nameMarlin simulation - MenuAboutAbout - MenuCaptureImageCapture From Camera - MenuForumsOnline help forums - MenuGenerate.FractalsFractals - MenuGenerate.GridsGrids - MenuGenerate.MazesMazes - MenuGenerate.SpaceFillersSpace Fillers - MenuGenerateGenerate art - MenuImportFileImport File... - MenuItemPayPalDonationPayPal donation - MenuItemTranslateHelp Translate - MenuMakelangeloFile - MenuManualRead the Friendly Manual - MenuNewFileNew - MenuOpenFileOpen File... - MenuQuitQuit - MenuReopenFileReopen last file - MenuResetMachinePreferencesWarningAre you sure you want to erase all your preferences? - MenuSaveFileSave File... - MenuSettingsSettings - MenuUpdateCheck for updates - MenuView.zoomFitZoom to fit - MenuView.zoomInZoom + - MenuView.zoomOutZoom - - MenuViewView - MetricsPreferences.TitleMetrics - MetricsPreferences.collectAnonymousMetricsI consent to sharing anonymous usage data
with Marginally Clever Robots, Limited.]]>
- MetricsPreferences.collectAnonymousMetricsOnUpdateMay Marginally Clever Robots, Ltd. collect anonymous usage metrics about this app?

You can update this any time in Makelangelo > Preferences > Metrics]]>
- ModelModel - MoireNameMoire - OKOK - OpenOpen file... - OpenFileChooser.AllSupportedFilesAll supported files - OpenFileChooser.FileTypeImageImage (%1) - OpenLogFolderOpen log folder - OpenPaperSettingsPaper settings - OpenPlotterSettingsPlotter settings - PackagePapercraft Box - PaperSettings.LandscapeLandscape - PaperSettings.PaperColorPaper color - PaperSettings.PaperHeightPaper height (mm) - PaperSettings.PaperMarginMargin (%) - PaperSettings.PaperSizePaper Size - PaperSettings.PaperWidthPaper width (mm) - PaperSettings.RotationRotation - PaperSettings.ShiftXShift left - PaperSettings.ShiftYShift up - PaperSettings.TitlePaper settings - PiCaptureAction.AWBAutomatic White Balance - PiCaptureAction.AntishakeAntishake - PiCaptureAction.AutoAuto - PiCaptureAction.BacklightBacklight - PiCaptureAction.BeachBeach - PiCaptureAction.CancelCaptureCancel - PiCaptureAction.CaptureImageCapture - PiCaptureAction.CaptureImageTitleCapture Camera Image - PiCaptureAction.CloudCloud - PiCaptureAction.ContrastContrast - PiCaptureAction.DRCDynamic Range - PiCaptureAction.ExposureExposure - PiCaptureAction.FireworksFireworks - PiCaptureAction.FixedFPSFixedFPS - PiCaptureAction.FlashFlash - PiCaptureAction.FluorescentFluorescent - PiCaptureAction.HighHigh - PiCaptureAction.HorizonHorizon - PiCaptureAction.IncandescentIncandescent - PiCaptureAction.LowLow - PiCaptureAction.MediumMedium - PiCaptureAction.NightNight - PiCaptureAction.NightPreviewNightPreview - PiCaptureAction.OffOff - PiCaptureAction.QualityQuality - PiCaptureAction.ShadeShade - PiCaptureAction.SharpnessSharpness - PiCaptureAction.SnowSnow - PiCaptureAction.SportsSports - PiCaptureAction.SpotlightSpotlight - PiCaptureAction.SunSun - PiCaptureAction.TungstenTungsten - PiCaptureAction.UseCaptureUse - PiCaptureAction.VerylongVerylong - PlotterControls.AdvancedControlsAdvanced controls - PlotterControls.ConnectControlsConnect - PlotterControls.DrawControlsDraw controls - PlotterControls.EmergencyStopEmergency Stop - PlotterControls.FindHomeFind home - PlotterControls.JogTabRobot controls - PlotterControls.MarlinTabMarlin - PlotterControls.PausePause - PlotterControls.PlayDraw - PlotterControls.ProgramTabProgram - PlotterControls.RewindRewind - PlotterControls.StepStep - PlotterControls.TitleRobot controls - PlotterControls.communicationFailureNo answer from the robot, please check the USB cable or speed settings. - PlotterControls.didNotFindError in communicating with the robot. - PlotterControls.haltedPrinter halted! - PlotterControls.homeXYFirstHome the printer first. - PlotterSettings.EndGcodeEnd GCode - PlotterSettings.FindHomeGcodeFind Home GCode - PlotterSettings.StartGcodeStart GCode - PlotterSettings.blockBufferSizeBlock Buffer Size - PlotterSettings.handleSmallSegmentsHandle Small Segments - PlotterSettings.minAccelerationMin Acceleration (mm/s²) - PlotterSettings.minSegTimeMin Seg Time (ns) - PlotterSettings.minSegmentLengthMin Segment Length (mm) - PlotterSettings.minimumPlannerSpeedMin Planner Speed (mm/s) - PlotterSettings.segmentsPerSecondSegments Per Second - PlotterSettings.zMotorType.servoServo - PlotterSettings.zMotorType.stepperStepper - PlotterSettings.zMotorTypeZ axis type - PlotterSettingsManagerPanel.AddProfile+ - PlotterSettingsManagerPanel.NewProfileNameNew unique name: - PlotterSettingsManagerPanel.NewProfileNameAlreadyExistsName already in use. Try again! - PlotterSettingsManagerPanel.NewProfileNameCannotBeBlankName cannot be blank. Try again! - PlotterSettingsManagerPanel.RemoveProfile- - PlotterSettingsPanel.AdjustAccelerationAcceleration (mm/s²) - PlotterSettingsPanel.BeltLengthNeededTiming belt length (m,each) - PlotterSettingsPanel.MachineHeightMachine height (mm) - PlotterSettingsPanel.MachineWidthMachine width (mm) - PlotterSettingsPanel.ServoLengthNeededServo wire length (m) - PlotterSettingsPanel.SpeedDraw speed (mm/min) - PlotterSettingsPanel.StepperLengthNeededStepper wire length (m,each) - PlotterSettingsPanel.TabEssentialEssential - PlotterSettingsPanel.TabGCodeCustom GCode - PlotterSettingsPanel.TabPenPen - PlotterSettingsPanel.TabSimulationSimulation - PlotterSettingsPanel.TitlePlotter settings - PlotterSettingsPanel.pen down colorDefault down color - PlotterSettingsPanel.pen up colorUp color - PlotterSettingsPanel.penToolDiameterDiameter (mm) - PlotterSettingsPanel.penToolDownDown angle (degrees) - PlotterSettingsPanel.penToolLiftSpeedPen lift time (ms) - PlotterSettingsPanel.penToolLowerSpeedPen lower time (ms) - PlotterSettingsPanel.penToolMaxFeedRateTravel speed (mm/min) - PlotterSettingsPanel.penToolUpUp angle (degrees) - PolyederPapercraft Polyhedron - PortPort - PulseLineNamePulse Line - QuestionTitleQuestion - ReorderReorder - ReportBugReport an issue - ResetReset - RobotMakelangelo - RobotMenu.GetTimeEstimateEstimate time - RobotMenu.OpenControlsOpen Controls - RobotMenu.RenderStyleRender style - RobotMenu.RobotStyleRobot style - RobotMenu.SaveGCodeSave Gcode to file/SD - RotateRotate - SandyNoble.centerCenter - SandyNoble.ringsRings - SandyNoble.titleSandy Noble Style - SaveSave - SaveErrorFile could not be saved: - SaveGCode.splitGCodeQuestionThere are %1 tool changes in this drawing. Split into separate files? - SaveGCode.splitGCodeTitleMany colors detected - ScaleScale - SelectImageConverterPanel.ContrastContrast adjust - SeparateLoopTurtleRenderer.nameSeparate loops - SharePromoTry social media tag #plotter - ShowLogShow Log - SierpinskiTriangleNameSierpinski triangle - SimplifySimplify - SizeSize - SoundPreferences.ConnectConnect - SoundPreferences.DisconnectDisconnect - SoundPreferences.FinishConvertFinish converting - SoundPreferences.FinishDrawFinish drawing - SoundPreferences.TitleSounds - Spiral.toCornersSpiral to corners - SpiralNameSpiral - SpiralPulse.heightPulse height - SpiralPulse.intensityIntensity - SpiralPulse.spacingSpace between rings - SpiralPulseNamePulsing Spiral - SpirographEpitrochoidis epitrochoid - SpirographMajorRadiusMajor radius (R) - SpirographMinorRadiusMinor radius (r) - SpirographNameSpirograph - SpirographNumSamplesSamples (quality) - SpirographPScaleScale (p) - StartAtStart at - StartAtAddPenDownAdd pen down command here - StartAtExactlyDo nothing special - StartAtLastPenDownBack up to last pen down - TextMessageMessage - TextSizeSize - TitlePrefixMakelangelo - TranslateTranslate - TurtleGenerators.LearnMore.Link.TextLearn more - UpToDateThis software is up to date. - UpdateCheckFailedSorry, I failed. Please visit http://www.marginallyclever.com/ to check yourself. - UpdateNoticeA new version of this software is available.
Please visit
https://www.marginallyclever.com/product/makelangelo-software/ to get the new hotness.]]>
- VoronoiZigZag.optimizePathOptimize path - VoronoiZigZagNameVoronoi zigzag - WidthWidth - YourMsgHereNameYour message here - bottom leftBottom left - bottom rightBottom right - centerCenter - firmwareVersionBadMessageThe firmware (code in the brain of your robot) is not the version I expect. I found %1%.
Please visit https://www.marginallyclever.com/product/makelangelo-firmware/
for the latest firmware.
Please visit https://www.marginallyclever.com/product/makelangelo-software/ for the latest software.]]>
- firmwareVersionBadTitleFirmware update - horizontalHorizontal - pass15Pass 15 - pass45Pass 45 - pass75Pass 75 - pass90Pass 90 - penDiameterPen diameter - top leftTop left - top rightTop right - verticalVertical - \ No newline at end of file diff --git a/src/main/resources/languages/french.xml b/src/main/resources/languages/french.xml deleted file mode 100644 index e57bcaf9c..000000000 --- a/src/main/resources/languages/french.xml +++ /dev/null @@ -1,261 +0,0 @@ - - - - - Français (fr_FR) - - https://github.com/PPAC37 - - - - ApplicationSettings.titlePréférences - Art PipelineOutils - BorderNameContour marges - BoxGeneratorCutoffCouper - BoxGeneratorMaxSizetaille max - BoxGeneratorNameBoxxy - CancelAnnuler - CartesianButtons.buttonCenter - ClickAndDragCliquez et faites glisser - ConfirmQuitQuestionÊtes-vous sûr de vouloir quitter? - ConfirmQuitTitleSortir - ConversationHistory.ClearEffacer - ConversationHistory.SaveSauvegarder - ConversionFillRemplir - ConversionStyleStyle de conversion - ConvertImagePaperFillRemplir tout le papier - ConvertImagePaperFitAjuster l'image sur le papier - ConverterMultipassAngleAngle (degrés) - ConverterMultipassLevelsNiveaux - ConverterMultipassNameMultipasse - ConverterRandomLinesCountNombre de lignes - ConverterRandomLinesNameLignes aléatoires - ConverterWanderCMYKCMJN ? - ConverterWanderLineCount# Lignes - ConverterWanderNameErrer - Converter_VoronoiStippling.CellCountNombre de cellules - Converter_VoronoiStippling.CutoffCoupure passe-bas - Converter_VoronoiStippling.DotMaxTaille maximale des points - Converter_VoronoiStippling.DotMinTaille de point minimale - Converter_VoronoiStippling.DrawBordersDessiner des frontières ? - Converter_VoronoiStippling.NamePointillés de Voronoï - CrosshatchHachure - DialogAbout.AboutHTML <html> <body> <h1>Makelangelo %VERSION% <span style="font-size: small; font-weight: normal">%DETAILED_VERSION%</span> </h1> <h3><a href='http://www.makelangelo.com/'>http://www.makelangelo.com/</a > </h3> <p>Pour obtenir la dernière version, veuillez visiter<br> <a href='https://www.marginallyclever.com/product/makelangelo-software/'>https://www.marginallyclever.com /product/makelangelo-software/</a></p> <p>Créé par <a href="mailto:dan@marginallyclever.com">Dan Royer</a>.</p> <p>Logiciel supplémentaire et les contributions matérielles de<br> Joseph Cottam, <a href='https://github.com/virtuoushub'>Peter Colapietro<a/>, Miguel Sánchez<br> Mark Raynsford et <a href='https://ca.linkedin.com/in/kington-chu-219923123'>Kington Chu</a>.</p> </body> </html> - DirectionDirection - DragonNameCourbe du dragon - ErrorTitleErreur - EstimatedTimeIsLe temps estimé est %1 - ExportExportation - FibonacciSpiralNameSpirale de Fibonacci - FillPageNameRemplir la page - FirmwareUpdateMise à jour du firmware - FlapRabat - FlipHRetourner horizontalement - FlipVRetourner verticalement - FontFacePolice de caractères - GFXPreferences.TitleGraphique - GFXPreferences.antialiasLignes d'anticrénelage - GFXPreferences.dragSpeedVitesse de déplacement de la souris - GFXPreferences.showAllWhileDrawingAfficher l'image entière pendant le dessin - GFXPreferences.showPenUpAfficher les déplacements - GFXPreferences.speedVSQualityLa rapidité plutôt que la qualité - GosperCurveNameCourbe de Gosper - GraphPaperNamePapier millimétré - HeightHauteur - HelpAide - HilbertCurveNameCourbe de Hilbert - HilbertCurveOrderOrdre - HilbertCurveSizeTaille - ImportImporter - InfillTurtleAction.titleRemplir les boucles fermées - InfoTitleInformation - JogInterface.DisengageMotorsDé-engager les moteurs - JogInterface.EngageMotorsEngager les moteurs - JogInterface.FindHomeTrouver l'origine ("Home") - JogInterface.PenDownStylo vers le bas - JogInterface.PenUpStylo vers le haut - KochTreeNameCourbe de Koch - LSystemAnglePortée angulaire - LSystemBranchesBranches - LSystemNoiseBruit - LSystemOrderScaleÉchelle - LSystemTreeNameArbre L-Système - LanguagePreferences.TitleLangue - LengthLongueur - LissajousAA (>0) - LissajousBB (>0) - LissajousDeltaDelta (0...1) - LissajousNameCourbe de Lissajous - LoadErrorLe fichier n'a pas pu être chargé : - LoadFilePanel.titleCharger l'aperçu du fichier - LogPanel.CopyClipboardCopier dans le presse-papier - LogPanel.LogFilesFichiers de logs - LogPanel.TitleLog - MenuAboutÀ propos de Makelangelo - MenuCaptureImageCapturer depuis l'appareil photo - MenuForumsForums d'aide en ligne - MenuGenerateGénérer de l'art - MenuItemPayPalDonationFaire un don PayPal - MenuMakelangeloFichier - MenuNewFileNouveau - MenuOpenFileOuvrir... - MenuQuitQuitter - MenuReopenFileRécents - MenuResetMachinePreferencesWarningÊtes-vous sûr de vouloir effacer toutes vos préférences ? - MenuSaveFileEnregistrer... - MenuSettingsParamètres - MenuUpdateVérifier les mises à jour - MenuView.zoomFitZoomer pour s'adapter - MenuView.zoomInAgrandir + - MenuView.zoomOutZoom - - MenuViewVoir - MetricsPreferences.TitleMétrique - MetricsPreferences.collectAnonymousMetrics<html><body>J'accepte de partager des données d'utilisation anonymes<br>avec Marginally Clever Robots, Limited.</html></body> - MetricsPreferences.collectAnonymousMetricsOnUpdate<html><body>Est-ce que Marginally Clever Robots, Ltd. peut collecter des statistiques d'utilisation anonymes sur cette application ?<br><br>Vous pouvez mettre à jour cela à tout moment dans <i>Makelangelo &gt; Préférences &gt; Métriques</i></html></body> - ModelModèle - MoireNameMoiré - OKd'accord - OpenFichier ouvert... - OpenFileChooser.AllSupportedFilesTous les fichiers pris en charge - OpenFileChooser.FileTypeImageImage (%1) - OpenPaperSettingsParamètres papier - OpenPlotterSettingsParamètres du traceur - PackageBoîte en papier - PaperSettings.LandscapeOrientation paysage - PaperSettings.PaperColorCouleur - PaperSettings.PaperHeightHauteur - PaperSettings.PaperMarginMarge (%) - PaperSettings.PaperSizeDimensions - PaperSettings.PaperWidthLargeur - PaperSettings.RotationRotation - PaperSettings.ShiftXDécalage à gauche ( Axe X ) - PaperSettings.ShiftYDécalage vers le haut ( Axe Y ) - PaperSettings.TitleParamètres papier - PiCaptureAction.AWBBalance des blancs automatique - PiCaptureAction.AntishakeAnti-vibrations - PiCaptureAction.AutoAuto - PiCaptureAction.BacklightRétroéclairage - PiCaptureAction.Beachplage - PiCaptureAction.CancelCaptureAnnuler - PiCaptureAction.CaptureImageCapturer - PiCaptureAction.CaptureImageTitleCapturer l'image de la caméra - PiCaptureAction.CloudNuage - PiCaptureAction.ContrastContraste - PiCaptureAction.DRCPlage dynamique - PiCaptureAction.ExposureExposition - PiCaptureAction.FireworksLes feux d'artifices - PiCaptureAction.FixedFPSFixedFPS - PiCaptureAction.FlashÉclat - PiCaptureAction.FluorescentFluorescent - PiCaptureAction.HighHaut - PiCaptureAction.HorizonHorizon - PiCaptureAction.IncandescentIncandescent - PiCaptureAction.LowMeugler - PiCaptureAction.MediumMoyen - PiCaptureAction.NightNuit - PiCaptureAction.NightPreviewNuitAperçu - PiCaptureAction.OffDésactivé - PiCaptureAction.QualityQualité - PiCaptureAction.ShadeOmbre - PiCaptureAction.SharpnessAcuité - PiCaptureAction.SnowNeige - PiCaptureAction.SportsDes sports - PiCaptureAction.SpotlightProjecteur - PiCaptureAction.Sunsoleil - PiCaptureAction.TungstenTungstène - PiCaptureAction.UseCaptureUtiliser - PiCaptureAction.VerylongTrès long - PlotterControls.AdvancedControlsContrôles avancés - PlotterControls.ConnectControlsConnecter - PlotterControls.DrawControlsContrôles de dessin - PlotterControls.EmergencyStopArrêt d'urgence - PlotterControls.FindHomeTrouver l'origine ("Home") - PlotterControls.JogTabCommandes de robots - PlotterControls.MarlinTabMarlin - PlotterControls.PausePause - PlotterControls.PlayDessiner - PlotterControls.ProgramTabProgramme - PlotterControls.RewindRembobiner - PlotterControls.StepÉtape - PlotterControls.TitleCommandes de robots - PlotterControls.homeXYFirstFaire une "prise d'origine" (Home) de l'imprimante, avant. - PlotterSettings.EndGcodeGCode fin - PlotterSettings.StartGcodeGCode début - PlotterSettings.blockBufferSizeTaille du tampon de bloc - PlotterSettings.handleSmallSegmentsGérer les petits segments - PlotterSettings.minAccelerationAccélération minimale - PlotterSettings.minSegTimeTemps de segment minimum - PlotterSettings.minSegmentLengthLongueur minimale du segment - PlotterSettings.minimumPlannerSpeedVitesse minimale du planificateur - PlotterSettings.segmentsPerSecondSegments par seconde - PlotterSettingsPanel.TabGCodeParamètres du traceur - GCode début/fin - PlotterSettingsPanel.TitleParamètres du traceur - PolyederPaper craft 3D Polyèdre - PortPort - PulseLineNameLigne d'impulsion - QuestionTitleQuestion - ReorderRéorganiser - ResetRéinitialiser - RobotMakelangelo - RobotMenu.GetTimeEstimateTemps estimé - RobotMenu.OpenControlsOuvrir les contrôles - RobotMenu.RenderStyleStyle de rendu - RobotMenu.RobotStyleStyle robotique - RobotMenu.SaveGCodeEnregistrer le Gcode dans un fichier/SD - SandyNoble.centerCentre - SandyNoble.ringsAnneaux - SandyNoble.titleStyle "Sandy Noble" - SaveEnregistrer - SaveErrorImpossible d'enregistrer le fichier : - ScaleÉchelle - SharePromoEssayez la balise de médias sociaux #plotter - ShowLogAfficher le journal - SierpinskiTriangleNameTriangle de Sierpiński - SimplifySimplifier - SizeTaille - SoundPreferences.ConnectConnexion - SoundPreferences.DisconnectDéconnexion - SoundPreferences.FinishConvertFin de conversion - SoundPreferences.FinishDrawFin du dessin - SoundPreferences.TitleSons - Spiral.toCornersSpirale aux coins - SpiralNameSpirale - SpiralPulse.heightHauteur d'impulsion - SpiralPulse.intensityIntensité - SpiralPulse.spacingEspace entre les anneaux - SpiralPulseNameSpirale pulsée - SpirographEpitrochoidest épitrochoïde - SpirographMajorRadiusGrand rayon (R) - SpirographMinorRadiusPetit rayon (r) - SpirographNameSpirographe - SpirographNumSamplesÉchantillons (qualité) - SpirographPScaleÉchelle (p) - StartAtCommencer à - StartAtAddPenDownAjouter la commande "pen down" ici - StartAtExactlyNe rien faire de spécial - StartAtLastPenDownRemonter jusqu'au dernier "pen down" - TextMessageMessage - TextSizeTaille - TitlePrefixMakelangelo - TurtleGenerators.LearnMore.Link.TextEn savoir plus - UpToDateCe logiciel est à jour. - UpdateCheckFailedDésolé, j'ai échoué. Veuillez visiter http://www.marginallyclever.com/ pour vérifier par vous-même. - UpdateNotice<html><body>Une nouvelle version de ce logiciel est disponible.<br/>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-software/">https://www. marginallyclever.com/product/makelangelo-software/</a> pour de bonnes surprises.</html></body> - VoronoiZigZagNamezigzag de Voronoï - WidthLargeur - YourMsgHereNameVotre message ici - bottom leftEn bas à gauche - bottom rightEn bas à droite - centerCentre - firmwareVersionBadMessage<html><body>Le firmware (code dans le cerveau de votre robot) n'est pas la version que j'attends. J'ai trouvé <strong>%1%</strong>.<br/>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-firmware/">https://www.marginallyclever. com/product/makelangelo-firmware/</a><br> pour le dernier firmware.<br>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-software/">https : //www.marginallyclever.com/product/makelangelo-software/</a> pour la dernière version du logiciel.</html></body> - firmwareVersionBadTitleMise à jour du firmware - horizontalHorizontal - pass15Passe 15 - pass45Passe 45 - pass75Passe 75 - pass90Passe 90 - penDiameterDiamètre du stylo - top leftEn haut à gauche - top rightEn haut à droite - verticalVerticale - diff --git a/src/main/resources/languages/german.xml b/src/main/resources/languages/german.xml deleted file mode 100644 index 3989acb8d..000000000 --- a/src/main/resources/languages/german.xml +++ /dev/null @@ -1,387 +0,0 @@ - - - - - Deutsch (de) - - Chris Tarantl (chris@tarantl.com) - Pablo Geilert (pgeilert) - Peter Prisille (peterprisil@gmail.com) - - - Converter_IntensityToHeight.sampleRateAbtastrate - Converter_IntensityToHeight.spacingWellenabstand - Converter_IntensityToHeight.waveIntensityWellenintensität - Converter_PulseCMYK.SampleRateAbtastrate - PlotterControls.didNotFindFehler in der Kommunikation mit dem Plotter - SelectImageConverterPanel.ContrastKontastkorrektur - VoronoiZigZag.optimizePathOptimierter Pfad - - ApplicationSettings.titleEinstellungen - Art PipelineWerkzeuge - BarberPoleTurtleRenderer.nameBarber pole - BorderNamePapier umrahmen - BoxGeneratorCutoffGrenze - BoxGeneratorMaxSizeMax Größe - BoxGeneratorNameBoxxy - CancelAbbrechen - CartesianButtons.buttonCenter - ChooseConnection.ButtonConnectVerbinden - ChooseConnection.ButtonDisconnectTrennen - ClickAndDragKlicke & ziehe - ConfirmQuitQuestionBist du sicher, dass du beenden willst? - ConfirmQuitTitleBeenden - ConversationHistory.ClearLöschen - ConversationHistory.CopyKopieren - ConversationHistory.SaveSpeichern - ConversionFillFüllung - ConversionStyleStil - ConvertImagePaperCenterBild zentrieren - ConvertImagePaperFillGanzes Papier füllen - ConvertImagePaperFitBild auf Papier einpassen - ConverterIntensityIntensität - ConverterMultipassAngleWinkel (Grad) - ConverterMultipassLevelsStufen - ConverterMultipassNameMultipass - ConverterRandomLinesCountLinienanzahl - ConverterRandomLinesNameZufällige Linien - ConverterWanderCMYKCMYK? - ConverterWanderLineCount# Linien - ConverterWanderNameSchweifen - Converter_CMYK_Circles.maxCircleSizeMaximaler Kreisdurchmesser - Converter_CMYK_Circles.nameCMYK Kreise - Converter_CMYK_Crosshatch.NameCMYK - Converter_CMYK_Crosshatch.Note - Converter_CMYK_Crosshatch.PassesDurchgänge - Converter_CMYK_Spiral.NameCMYK Spirale - Converter_EdgeDetection.borderZeichne Rand - Converter_EdgeDetection.nameKantenerkennung - Converter_EdgeDetection.passesDurchgänge - Converter_EdgeDetection.sampleSizeAbtastabstand - Converter_EdgeDetection.stepSizeSchrittweite - Converter_IntensityToHeight.nameIntensität - Höhe - Converter_PulseCMYK.NamePulse CMYK - Converter_QuadTreeInstant.baseCutOffBasis cut-off - Converter_QuadTreeInstant.cutOffIncrementCut-off Schrittweite - Converter_QuadTreeInstant.maxDepthMaximale Tiefe - Converter_QuadTreeInstantQuad tree instant - Converter_TruchetFromImage.linesPerTileLinien pro Kachel - Converter_TruchetFromImage.nameTruchet-Kachel vom Bild - Converter_TruchetFromImage.spacingRaum zwischen den Linien - Converter_Voronoi.NameVoronoi Diagramm - Converter_Voronoi.ShowCentersZeige die Zentren - Converter_VoronoiStippling.CellCountZellenanzahl - Converter_VoronoiStippling.CutoffTiefpassgrenze - Converter_VoronoiStippling.DotMaxMax Punktgröße - Converter_VoronoiStippling.DotMinMin Punktgröße - Converter_VoronoiStippling.DrawBordersRand zeichnen? - Converter_VoronoiStippling.NameVoronoi Punkte - CrosshatchKreuzschraffur - DefaultTurtleRenderer.nameDefault - DialogAbout.AboutHTML <html> <body> <h1>Makelangelo %VERSION% <span style="font-size: small; font-weight: normal">%DETAILED_VERSION%</span></h1> <h3><a href='http://www.makelangelo.com/'>http://www.makelangelo.com/</a> </h3> <p>Um die aktuellste Version zu bekommen besuche<br> <a href='https://www.marginallyclever.com/product/makelangelo-software/'>https://www.marginallyclever.com/product/makelangelo-software/</a></p> <p>Erstellt von <a href=mailto:dan@marginallyclever.com>Dan Royer</a>.</p> <p>Zusätzliche Software- und Hardwarebeiträge von<br> Joseph Cottam, <a href='https://github.com/virtuoushub'>Peter Colapietro<a/>, Miguel Sánchez<br> Mark Raynsford, and <a href='https://ca.linkedin.com/in/kington-chu-219923123'>Kington Chu</a>.</p> </body> </html> - DirectionRichtung - DirectionLoopTurtleRenderer.nameRichtungsschleifen - DragonNameDrachenfraktal - ErrorTitleFehler - EstimatedTimeIsGeschätzte Zeit ist %1 - ExportExportiere - FibonacciSpiralNameFibonacci Spiralenfraktal - FillPageNameSeite füllen - FirmwareUpdateFirmwareupdate - FirmwareUploader.helphelp]]> - FirmwareUploaderPanel.avrdudeNotDownloadedAVRDude wurde nicht heruntergeladen. - FirmwareUploaderPanel.downloadFailedDownload fehlgeschlagen. - FirmwareUploaderPanel.failedFirmware upload fehlgeschlagen. - FirmwareUploaderPanel.finishedFirmwareupdate OK! - FirmwareUploaderPanel.noPortSelectedKein Port ausgewählt - FirmwareUploaderPanel.notFound%1 nicht gefunden. - FirmwareUploaderPanel.startHugeHuge - FirmwareUploaderPanel.startM5M5 - FirmwareUploaderPanel.statusFirmware-Upload Status - FlapKlappe - FlipHHorizontal umkehren - FlipVVertikal umkehren - FontFaceSchrifttype - GFXPreferences.TitleGrafik - GFXPreferences.antialiasLinien glätten - GFXPreferences.dragSpeedKamerageschwindigkeit - GFXPreferences.showAllWhileDrawingZeige gesamtes Bild während desZeichnens - GFXPreferences.showPenUpZeige Verfahrbewegungen - GFXPreferences.speedVSQualityGeschwindigkeit über Qualität - Generator.randomSeedStartwert (Seed) - Generator_FlowField.fromEdgeKontinuierlich - Generator_FlowField.nameStrömungsfeld - Generator_FlowField.noiseTypeRauschart - Generator_FlowField.offsetXX Offset - Generator_FlowField.offsetYY Offset - Generator_FlowField.rightAngleFüge orthogonale Linien hinzu - Generator_FlowField.scaleXX Maßstab - Generator_FlowField.scaleYY Maßstab - Generator_FlowField.stepLengthSchrittlänge - Generator_FlowField.stepSizeSchrittgröße - Generator_FlowField.stepVariationSchrittvariation - Generator_GridFit.NameRechteck - Generator_GridFit.cellsHighZellen vertical - Generator_GridFit.cellsWideZellen horizontal - Generator_GridFit.marginMinimaler Rand an jeder Seite (mm) - Generator_GridHexagons.NameSechseck - Generator_GridHexagons.radiusUmkreisradius - Generator_MazeCircle.nameKreis - Generator_MazeCircle.ringsRinge - Generator_MazeHoneycomb.nameBienenwabe - Generator_MazeRectangle.columnsSpalten - Generator_MazeRectangle.nameRechteck - Generator_MazeRectangle.rowsZeilen - Generator_Spiral.nameSpirale - Generator_Spiral.radiusRadius - Generator_TruchetTiles.LineSpacingAbstand zwischen den Linien (mm) - Generator_TruchetTiles.LinesPerTileLinien pro Fliese - Generator_TruchetTiles.NameTruchet-Fliesen - Generator_TruchetTiles.allowErlaube '%1' - GosperCurveNameGosper Kurve - GraphPaperNameMillimeterpapier - HeightHöhe - HelpHilfe - HilbertCurveNameHilbert Kurvenfraktal - HilbertCurveOrderReihenfolge - HilbertCurveSizeGröße - ImportImportiere - InfillTurtleAction.titleFülle geschlossene Bereiche - InfoTitleInformation - JogInterface.DisengageMotorsMotoren deaktivieren - JogInterface.EngageMotorsMotoren aktivieren - JogInterface.FindHomeZuhause finden - JogInterface.PenDownStift absenken - JogInterface.PenUpStift anheben - KochTreeNameKoch Kurvenfraktal - LSystemAngleWinkelweite - LSystemBranchesVerzweigungen - LSystemNoiseRauschen - LSystemOrderScaleGröße - LSystemTreeNameL System Baumfraktal - LanguagePreferences.TitleSprache - LengthLänge - LineWeightByImageIntensity.imageBild - LineWeightByImageIntensity.nameLinienstärke - LineWeightByImageIntensity.thicknessStrichstärke - LissajousAA (>0) - LissajousBB (>0) - LissajousDeltaDelta (0...1) - LissajousNameLissajous - LoadErrorDatei konnte nicht geladen werden: - LoadFilePanel.titleDateivorschau laden - LoadScratch3.foreverNotAllowedForever Schleifen sind für immer verboten. - LogPanel.CopyClipboardKopieren - LogPanel.LogFilesProtokoll Dateien - LogPanel.TitleProtokoll - MarlinSimulationVisualizer.nameMarlin Simulation - MenuAboutÜber - MenuCaptureImageVon Kamera aufnehmen - MenuForumsOnline Hilfeforum - MenuGenerate.FractalsFraktals - MenuGenerate.GridsRaster - MenuGenerate.MazesIrrgarten - MenuGenerate.SpaceFillersZwischenräume auffüllen - MenuGenerateKunst generieren - MenuImportFileDatei importieren... - MenuItemPayPalDonationPayPal Spende - MenuMakelangeloDatei - MenuManualAnleitung lesen - MenuNewFileNeu - MenuOpenFileDatei öffnen... - MenuQuitBeenden - MenuReopenFileLetzte Datei erneut öffnen - MenuResetMachinePreferencesWarningBist du sicher, dass du all deine Einstellungen zurücksetzen willst? - MenuSaveFileDatei speichern... - MenuSettingsEinstellungen - MenuUpdateNeue Version suchen - MenuView.zoomFitEinpassen - MenuView.zoomInZoom + - MenuView.zoomOutZoom - - MenuViewAnsicht - MetricsPreferences.TitleMaße - MetricsPreferences.collectAnonymousMetrics<html><body>Ich stimme zu, anonyme Benutzungsdaten mit<br>Marginally Clever Robots, Limited zu teilen.</html></body> - MetricsPreferences.collectAnonymousMetricsOnUpdate<html><body>Darf Marginally Clever Robots, Ltd. anonyme Metriken über diese app sammeln?<br><br>Du kannst dies jederzeit unter <i>Makelangelo &gt; Einstellungen &gt; Metriken ändern</i></html></body> - ModelModell - MoireNameMoiré - OKOK - OpenDatei öffnen... - OpenFileChooser.AllSupportedFilesAlle unterstützte Formate - OpenFileChooser.FileTypeImageBild (%1) - OpenLogFolderÖffne Protokoll Verzeichnis - OpenPaperSettingsPapier-Einstellungen - OpenPlotterSettingsPlotter-Einstellungen - PackagePapierschachtel - PaperSettings.LandscapeQuerformat - PaperSettings.PaperColorPapierfarbe - PaperSettings.PaperHeightPapierhöhe - PaperSettings.PaperMarginRand (%) - PaperSettings.PaperSizePapiergröße - PaperSettings.PaperWidthPapierbreite - PaperSettings.RotationDrehung - PaperSettings.ShiftXVerschiebung links - PaperSettings.ShiftYVerschiebung hoch - PaperSettings.TitlePapiereinstellungen - PiCaptureAction.AWBAutomatischer Weißabgleich - PiCaptureAction.AntishakeStabilisierung - PiCaptureAction.AutoAuto - PiCaptureAction.BacklightHintergrundbeleuchtung - PiCaptureAction.BeachStrand - PiCaptureAction.CancelCaptureAbbruch - PiCaptureAction.CaptureImageAufnehmen - PiCaptureAction.CaptureImageTitleKamerabild aufnehmen - PiCaptureAction.CloudWolken - PiCaptureAction.ContrastKontrast - PiCaptureAction.DRCDynamikbereich - PiCaptureAction.ExposureBelichtung - PiCaptureAction.FireworksFeuerwerk - PiCaptureAction.FixedFPSFeste Framerate - PiCaptureAction.FlashBlitz - PiCaptureAction.FluorescentLeuchtstoffröhre - PiCaptureAction.HighHoch - PiCaptureAction.HorizonHorizont - PiCaptureAction.IncandescentGlühlampe - PiCaptureAction.LowNiedrig - PiCaptureAction.MediumMittel - PiCaptureAction.NightNacht - PiCaptureAction.NightPreviewNachtvorschau - PiCaptureAction.OffAus - PiCaptureAction.QualityQualität - PiCaptureAction.ShadeSchattierung - PiCaptureAction.SharpnessSchärfe - PiCaptureAction.SnowSchnee - PiCaptureAction.SportsSport - PiCaptureAction.SpotlightSpotlicht - PiCaptureAction.SunSonne - PiCaptureAction.TungstenKunstlicht - PiCaptureAction.UseCaptureBenutze - PiCaptureAction.VerylongLangzeit - PlotterControls.AdvancedControlsErweiterte Steuerung - PlotterControls.ConnectControlsVerbinden - PlotterControls.DrawControlsPlottersteuerung - PlotterControls.EmergencyStopNotfall-Stop - PlotterControls.FindHomeHome finden - PlotterControls.JogTabRobotersteuerung - PlotterControls.MarlinTabMarlin - PlotterControls.PausePause - PlotterControls.PlayZeichnen - PlotterControls.ProgramTabProgramm - PlotterControls.RewindZurückspulen - PlotterControls.StepSchritt - PlotterControls.TitleRobotersteuerung - PlotterControls.communicationFailureKeine Anwort vom Plotter. Bitte das USB-Kabel und die Geschwindikeitseinstellungen prüfen - PlotterControls.haltedPlotter angehalten! - PlotterControls.homeXYFirstBitte zuerst Home finden. - PlotterSettings.EndGcodeGCode für das Ende - PlotterSettings.FindHomeGcodeHome GCode finden - PlotterSettings.StartGcodeGCode für den Start - PlotterSettings.blockBufferSizeBlock-Puffergröße - PlotterSettings.handleSmallSegmentsKleine Segmente bearbeiten - PlotterSettings.minAccelerationMin. Beschleunigung - PlotterSettings.minSegTimeMin. Segmentzeit - PlotterSettings.minSegmentLengthMin. Segmentlänge - PlotterSettings.minimumPlannerSpeedMin. Planer-Geschwindigkeit - PlotterSettings.segmentsPerSecondSegmente pro Sekunde - PlotterSettings.zMotorType.servoServo - PlotterSettings.zMotorType.stepperSchrittmotor - PlotterSettings.zMotorTypeZ-Achsen Typ - PlotterSettingsManagerPanel.AddProfile+ - PlotterSettingsManagerPanel.NewProfileNameNeuer eindeutiger Name - PlotterSettingsManagerPanel.NewProfileNameAlreadyExistsDer Name exisiert bereits. Neuer Versuch: - PlotterSettingsManagerPanel.NewProfileNameCannotBeBlankDer Name darf nicht Leer sein. Neuer Versuch: - PlotterSettingsManagerPanel.RemoveProfile- - PlotterSettingsPanel.AdjustAccelerationBeschleuningung (mm/s²) - PlotterSettingsPanel.BeltLengthNeededTiming belt length (m,each) - PlotterSettingsPanel.MachineHeightMaschinenhöhe (mm) - PlotterSettingsPanel.MachineWidthMaschinenbreite (mm) - PlotterSettingsPanel.ServoLengthNeededServokabellänge (m) - PlotterSettingsPanel.SpeedZeichengeschwindigkeit (mm/min) - PlotterSettingsPanel.StepperLengthNeededSchrittmotorkabellänge (m,jeweils) - PlotterSettingsPanel.TabEssentialBasis - PlotterSettingsPanel.TabGCodeBenutzer spezifischer GCode - PlotterSettingsPanel.TabPenStift - PlotterSettingsPanel.TabSimulationSimulation - PlotterSettingsPanel.TitlePlotter Einstellungen - PlotterSettingsPanel.pen down colorStift-Unten Farbe - PlotterSettingsPanel.pen up colorStift-Oben Farbe - PlotterSettingsPanel.penToolDiameterDurchmesser (mm) - PlotterSettingsPanel.penToolDownUnten-Winkel (Grad) - PlotterSettingsPanel.penToolLiftSpeedStift-Anheben-Zeit (ms) - PlotterSettingsPanel.penToolLowerSpeedStift-Senken-Zeit (ms) - PlotterSettingsPanel.penToolMaxFeedRateBewegungsgeschwindigkeit (mm/min) - PlotterSettingsPanel.penToolUpOben-Winkel (Grad) - PolyederPapier Polyeder - PortPort - PulseLineNamePulslinie - QuestionTitleFrage - ReorderNeu sortieren - ReportBugEin Problem melden - ResetZurücksetzen - RobotMakelangelo - RobotMenu.GetTimeEstimateGeschätzte Zeit - RobotMenu.OpenControlsSteuerung öffnen - RobotMenu.RenderStyleAnzeigestil - RobotMenu.RobotStyleRoboter-Stil - RobotMenu.SaveGCodeSpeichere GCode in Datei/SD - RotateRotieren - SandyNoble.centerMitte - SandyNoble.ringsRinge - SandyNoble.titleSandy Noble Stil - SaveSpeichern - SaveErrorDatei konnte nicht gespeichert werden: - SaveGCode.splitGCodeQuestionDiese Zeichung benötigt %1 Stiftwechsel. In mehrere Dateien zerlegen? - SaveGCode.splitGCodeTitleMehrere Farben entdeckt - ScaleSkalierung - SeparateLoopTurtleRenderer.nameSeparate Schleifen - SharePromoVerwende Hashtag #makelangelo - ShowLogZeige Protokoll - SierpinskiTriangleNameSierpinski Dreieck Fraktal - SimplifyVereinfachen - SizeGröße - SoundPreferences.ConnectVerbinden - SoundPreferences.DisconnectTrennen - SoundPreferences.FinishConvertUmwandlung fertig - SoundPreferences.FinishDrawZeichnen abgeschlossen - SoundPreferences.TitleKlänge - Spiral.toCornersSpirale bis in Ecken - SpiralNameSpirale - SpiralPulse.heightPulshöhe - SpiralPulse.intensityIntensität - SpiralPulse.spacingAbstand zwischen Ringen - SpiralPulseNamePulsierende Spirale - SpirographEpitrochoidIst ein Epitrochoid - SpirographMajorRadiusGroßer Radius (R) - SpirographMinorRadiusKleiner radius (r) - SpirographNameSpirograph - SpirographNumSamplesZwischenwerte (qualität) - SpirographPScaleSkalierung (p) - StartAtStarte bei - StartAtAddPenDownFüge Stift runter hier ein - StartAtExactlyMache nichts spezielles - StartAtLastPenDownFahre zum letzten Stift hoch - TextMessageText - TextSizeGröße - TitlePrefixMakelangelo - TranslateVerschieben - TurtleGenerators.LearnMore.Link.TextWeitere Informationen... - UpToDateDiese Software ist aktuell. - UpdateCheckFailedTut mir leid es hat nicht funktioniert. Bitte besuche http://www.marginallyclever.com/ um selbst zu suchen. - UpdateNotice<html><body>Eine neuere Version dieser Software ist verfügbar.<br/>Bitte besuche <a href=https://www.marginallyclever.com/product/makelangelo-software/>https://www.marginallyclever.com/product/makelangelo-software/</a> um die neue Version zu bekommen.</html></body> - VoronoiZigZagNameVoronoi Zickzack - WidthBreite - YourMsgHereNameText für Schriftzug eingeben - bottom leftUnten links - bottom rightUnten rechts - centerMitte - firmwareVersionBadMessageDie Firmware (Code im Gehirn Ihres Roboters) ist nicht die erwartete Version. Ich habe %1% gefunden.
Bitte besuchen Sie
https://www.marginallyclever.com/product/makelangelo-firmware/
für die neueste Firmware.
Bitte besuchen Sie https://www.marginallyclever.com/product/makelangelo-software/ für die neueste Software.]]>
- firmwareVersionBadTitleFirmware-Update - horizontalHorizontal - pass15Durchgang 15 - pass45Durchgang 45 - pass75Durchgang 75 - pass90Durchgang 90 - penDiameterStift-Durchmesser - top leftOben links - top rightOben rechts - verticalVertikal - \ No newline at end of file diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 000000000..bc9d74b52 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,377 @@ +ApplicationSettings.title=Missing ApplicationSettings.title +ArtPipeline=Missing ArtPipeline +BarberPoleTurtleRenderer.name=Missing BarberPoleTurtleRenderer.name +BorderName=Missing BorderName +BoxGeneratorCutoff=Missing BoxGeneratorCutoff +BoxGeneratorMaxSize=Missing BoxGeneratorMaxSize +BoxGeneratorName=Missing BoxGeneratorName +Cancel=Missing Cancel +CartesianButtons.buttonCenter=Missing CartesianButtons.buttonCenter +ChooseConnection.ButtonConnect=Missing ChooseConnection.ButtonConnect +ChooseConnection.ButtonDisconnect=Missing ChooseConnection.ButtonDisconnect +ClickAndDrag=Missing ClickAndDrag +ConfirmQuitQuestion=Missing ConfirmQuitQuestion +ConfirmQuitTitle=Missing ConfirmQuitTitle +ConversationHistory.Clear=Missing ConversationHistory.Clear +ConversationHistory.Copy=Missing ConversationHistory.Copy +ConversationHistory.Save=Missing ConversationHistory.Save +ConversionFill=Missing ConversionFill +ConversionStyle=Missing ConversionStyle +ConvertImagePaperCenter=Missing ConvertImagePaperCenter +ConvertImagePaperFill=Missing ConvertImagePaperFill +ConvertImagePaperFit=Missing ConvertImagePaperFit +ConverterIntensity=Missing ConverterIntensity +ConverterMultipassAngle=Missing ConverterMultipassAngle +ConverterMultipassLevels=Missing ConverterMultipassLevels +ConverterMultipassName=Missing ConverterMultipassName +ConverterRandomLinesCount=Missing ConverterRandomLinesCount +ConverterRandomLinesName=Missing ConverterRandomLinesName +ConverterWanderCMYK=Missing ConverterWanderCMYK +ConverterWanderLineCount=Missing ConverterWanderLineCount +ConverterWanderName=Missing ConverterWanderName +Converter_CMYK_Circles.name=Missing Converter_CMYK_Circles.name +Converter_CMYK_Circles.maxCircleSize=Missing Converter_CMYK_Circles.maxCircleSize +Converter_CMYK_Circles.fillCircles=Missing Converter_CMYK_Circles.fillCircles +Converter_CMYK_Crosshatch.Name=Missing Converter_CMYK_Crosshatch.Name +Converter_CMYK_Crosshatch.Note=Missing Converter_CMYK_Crosshatch.Note +Converter_CMYK_Crosshatch.Passes=Missing Converter_CMYK_Crosshatch.Passes +Converter_CMYK_Spiral.Name=Missing Converter_CMYK_Spiral.Name +Converter_EdgeDetection.border=Missing Converter_EdgeDetection.border +Converter_EdgeDetection.name=Missing Converter_EdgeDetection.name +Converter_EdgeDetection.passes=Missing Converter_EdgeDetection.passes +Converter_EdgeDetection.sampleSize=Missing Converter_EdgeDetection.sampleSize +Converter_EdgeDetection.stepSize=Missing Converter_EdgeDetection.stepSize +Converter_IntensityToHeight.name=Missing Converter_IntensityToHeight.name +Converter_IntensityToHeight.sampleRate=Missing Converter_IntensityToHeight.sampleRate +Converter_IntensityToHeight.spacing=Missing Converter_IntensityToHeight.spacing +Converter_IntensityToHeight.waveIntensity=Missing Converter_IntensityToHeight.waveIntensity +Converter_PulseCMYK.Name=Missing Converter_PulseCMYK.Name +Converter_PulseCMYK.SampleRate=Missing Converter_PulseCMYK.SampleRate +Converter_QuadTreeInstant.baseCutOff=Missing Converter_QuadTreeInstant.baseCutOff +Converter_QuadTreeInstant.cutOffIncrement=Missing Converter_QuadTreeInstant.cutOffIncrement +Converter_QuadTreeInstant.maxDepth=Missing Converter_QuadTreeInstant.maxDepth +Converter_QuadTreeInstant=Missing Converter_QuadTreeInstant +Converter_TruchetFromImage.linesPerTile=Missing Converter_TruchetFromImage.linesPerTile +Converter_TruchetFromImage.name=Missing Converter_TruchetFromImage.name +Converter_TruchetFromImage.spacing=Missing Converter_TruchetFromImage.spacing +Converter_Voronoi.Name=Missing Converter_Voronoi.Name +Converter_Voronoi.ShowCenters=Missing Converter_Voronoi.ShowCenters +Converter_VoronoiStippling.CellCount=Missing Converter_VoronoiStippling.CellCount +Converter_VoronoiStippling.Cutoff=Missing Converter_VoronoiStippling.Cutoff +Converter_VoronoiStippling.DotMax=Missing Converter_VoronoiStippling.DotMax +Converter_VoronoiStippling.DotMin=Missing Converter_VoronoiStippling.DotMin +Converter_VoronoiStippling.DrawBorders=Missing Converter_VoronoiStippling.DrawBorders +Converter_VoronoiStippling.Name=Missing Converter_VoronoiStippling.Name +Crosshatch=Missing Crosshatch +DefaultTurtleRenderer.name=Missing DefaultTurtleRenderer.name +DialogAbout.AboutHTML=Missing DialogAbout.AboutHTML +Direction=Missing Direction +DirectionLoopTurtleRenderer.name=Missing DirectionLoopTurtleRenderer.name +DragonName=Missing DragonName +ErrorTitle=Missing ErrorTitle +EstimatedTimeIs=Missing EstimatedTimeIs +Export=Missing Export +FibonacciSpiralName=Missing FibonacciSpiralName +FillPageName=Missing FillPageName +FirmwareUpdate=Missing FirmwareUpdate +FirmwareUploader.help=Missing FirmwareUploader.help +FirmwareUploaderPanel.avrdudeNotDownloaded=Missing FirmwareUploaderPanel.avrdudeNotDownloaded +FirmwareUploaderPanel.downloadFailed=Missing FirmwareUploaderPanel.downloadFailed +FirmwareUploaderPanel.failed=Missing FirmwareUploaderPanel.failed +FirmwareUploaderPanel.finished=Missing FirmwareUploaderPanel.finished +FirmwareUploaderPanel.noPortSelected=Missing FirmwareUploaderPanel.noPortSelected +FirmwareUploaderPanel.notFound=Missing FirmwareUploaderPanel.notFound +FirmwareUploaderPanel.startHuge=Missing FirmwareUploaderPanel.startHuge +FirmwareUploaderPanel.startM5=Missing FirmwareUploaderPanel.startM5 +FirmwareUploaderPanel.status=Missing FirmwareUploaderPanel.status +Flap=Missing Flap +FlipH=Missing FlipH +FlipV=Missing FlipV +FontFace=Missing FontFace +GFXPreferences.Title=Missing GFXPreferences.Title +GFXPreferences.antialias=Missing GFXPreferences.antialias +GFXPreferences.dragSpeed=Missing GFXPreferences.dragSpeed +GFXPreferences.showAllWhileDrawing=Missing GFXPreferences.showAllWhileDrawing +GFXPreferences.showPenUp=Missing GFXPreferences.showPenUp +GFXPreferences.speedVSQuality=Missing GFXPreferences.speedVSQuality +Generator.randomSeed=Missing Generator.randomSeed +Generator_FlowField.fromEdge=Missing Generator_FlowField.fromEdge +Generator_FlowField.name=Missing Generator_FlowField.name +Generator_FlowField.noiseType=Missing Generator_FlowField.noiseType +Generator_FlowField.offsetX=Missing Generator_FlowField.offsetX +Generator_FlowField.offsetY=Missing Generator_FlowField.offsetY +Generator_FlowField.rightAngle=Missing Generator_FlowField.rightAngle +Generator_FlowField.scaleX=Missing Generator_FlowField.scaleX +Generator_FlowField.scaleY=Missing Generator_FlowField.scaleY +Generator_FlowField.stepLength=Missing Generator_FlowField.stepLength +Generator_FlowField.stepSize=Missing Generator_FlowField.stepSize +Generator_FlowField.stepVariation=Missing Generator_FlowField.stepVariation +Generator_GridFit.Name=Missing Generator_GridFit.Name +Generator_GridFit.cellsHigh=Missing Generator_GridFit.cellsHigh +Generator_GridFit.cellsWide=Missing Generator_GridFit.cellsWide +Generator_GridFit.margin=Missing Generator_GridFit.margin +Generator_GridHexagons.Name=Missing Generator_GridHexagons.Name +Generator_GridHexagons.radius=Missing Generator_GridHexagons.radius +Generator_MazeCircle.name=Missing Generator_MazeCircle.name +Generator_MazeCircle.rings=Missing Generator_MazeCircle.rings +Generator_MazeHoneycomb.name=Missing Generator_MazeHoneycomb.name +Generator_MazeRectangle.columns=Missing Generator_MazeRectangle.columns +Generator_MazeRectangle.name=Missing Generator_MazeRectangle.name +Generator_MazeRectangle.rows=Missing Generator_MazeRectangle.rows +Generator_Spiral.name=Missing Generator_Spiral.name +Generator_Spiral.radius=Missing Generator_Spiral.radius +Generator_TruchetTiles.LineSpacing=Missing Generator_TruchetTiles.LineSpacing +Generator_TruchetTiles.LinesPerTile=Missing Generator_TruchetTiles.LinesPerTile +Generator_TruchetTiles.Name=Missing Generator_TruchetTiles.Name +Generator_TruchetTiles.allow=Missing Generator_TruchetTiles.allow +GosperCurveName=Missing GosperCurveName +GraphPaperName=Missing GraphPaperName +Height=Missing Height +Help=Missing Help +HilbertCurveName=Missing HilbertCurveName +HilbertCurveOrder=Missing HilbertCurveOrder +HilbertCurveSize=Missing HilbertCurveSize +Import=Missing Import +InfillTurtleAction.title=Missing InfillTurtleAction.title +InfoTitle=Missing InfoTitle +JogInterface.DisengageMotors=Missing JogInterface.DisengageMotors +JogInterface.EngageMotors=Missing JogInterface.EngageMotors +JogInterface.FindHome=Missing JogInterface.FindHome +JogInterface.PenDown=Missing JogInterface.PenDown +JogInterface.PenUp=Missing JogInterface.PenUp +KochTreeName=Missing KochTreeName +LSystemAngle=Missing LSystemAngle +LSystemBranches=Missing LSystemBranches +LSystemNoise=Missing LSystemNoise +LSystemOrderScale=Missing LSystemOrderScale +LSystemTreeName=Missing LSystemTreeName +LanguagePreferences.Title=Missing LanguagePreferences.Title +Length=Missing Length +LineWeightByImageIntensity.image=Missing LineWeightByImageIntensity.image +LineWeightByImageIntensity.name=Missing LineWeightByImageIntensity.name +LineWeightByImageIntensity.thickness=Missing LineWeightByImageIntensity.thickness +LissajousA=Missing LissajousA +LissajousB=Missing LissajousB +LissajousDelta=Missing LissajousDelta +LissajousName=Missing LissajousName +LoadError=Missing LoadError +LoadFilePanel.title=Missing LoadFilePanel.title +LoadScratch3.foreverNotAllowed=Missing LoadScratch3.foreverNotAllowed +LogPanel.CopyClipboard=Missing LogPanel.CopyClipboard +LogPanel.LogFiles=Missing LogPanel.LogFiles +LogPanel.Title=Missing LogPanel.Title +MarlinSimulationVisualizer.name=Missing MarlinSimulationVisualizer.name +MenuAbout=Missing MenuAbout +MenuCaptureImage=Missing MenuCaptureImage +MenuForums=Missing MenuForums +MenuGenerate.Fractals=Missing MenuGenerate.Fractals +MenuGenerate.Grids=Missing MenuGenerate.Grids +MenuGenerate.Mazes=Missing MenuGenerate.Mazes +MenuGenerate.SpaceFillers=Missing MenuGenerate.SpaceFillers +MenuGenerate=Missing MenuGenerate +MenuImportFile=Missing MenuImportFile +MenuItemPayPalDonation=Missing MenuItemPayPalDonation +MenuItemTranslate=Missing MenuItemTranslate +MenuMakelangelo=Missing MenuMakelangelo +MenuManual=Missing MenuManual +MenuNewFile=Missing MenuNewFile +MenuOpenFile=Missing MenuOpenFile +MenuQuit=Missing MenuQuit +MenuReopenFile=Missing MenuReopenFile +MenuResetMachinePreferencesWarning=Missing MenuResetMachinePreferencesWarning +MenuSaveFile=Missing MenuSaveFile +MenuSettings=Missing MenuSettings +MenuUpdate=Missing MenuUpdate +MenuView.zoomFit=Missing MenuView.zoomFit +MenuView.zoomIn=Missing MenuView.zoomIn +MenuView.zoomOut=Missing MenuView.zoomOut +MenuView=Missing MenuView +MenuWindows=Missing MenuWindows +MetricsPreferences.Title=Missing MetricsPreferences.Title +MetricsPreferences.collectAnonymousMetrics=Missing MetricsPreferences.collectAnonymousMetrics +MetricsPreferences.collectAnonymousMetricsOnUpdate=Missing MetricsPreferences.collectAnonymousMetricsOnUpdate +Model=Missing Model +MoireName=Missing MoireName +OK=Missing OK +Open=Missing Open +OpenFileChooser.AllSupportedFiles=Missing OpenFileChooser.AllSupportedFiles +OpenFileChooser.FileTypeImage=Missing OpenFileChooser.FileTypeImage +OpenLogFolder=Missing OpenLogFolder +OpenPaperSettings=Missing OpenPaperSettings +OpenPlotterSettings=Missing OpenPlotterSettings +Package=Missing Package +PaperSettings.Landscape=Missing PaperSettings.Landscape +PaperSettings.PaperColor=Missing PaperSettings.PaperColor +PaperSettings.PaperHeight=Missing PaperSettings.PaperHeight +PaperSettings.PaperMargin=Missing PaperSettings.PaperMargin +PaperSettings.PaperSize=Missing PaperSettings.PaperSize +PaperSettings.PaperWidth=Missing PaperSettings.PaperWidth +PaperSettings.Rotation=Missing PaperSettings.Rotation +PaperSettings.ShiftX=Missing PaperSettings.ShiftX +PaperSettings.ShiftY=Missing PaperSettings.ShiftY +PaperSettings.Title=Missing PaperSettings.Title +PiCaptureAction.AWB=Missing PiCaptureAction.AWB +PiCaptureAction.Antishake=Missing PiCaptureAction.Antishake +PiCaptureAction.Auto=Missing PiCaptureAction.Auto +PiCaptureAction.Backlight=Missing PiCaptureAction.Backlight +PiCaptureAction.Beach=Missing PiCaptureAction.Beach +PiCaptureAction.CancelCapture=Missing PiCaptureAction.CancelCapture +PiCaptureAction.CaptureImage=Missing PiCaptureAction.CaptureImage +PiCaptureAction.CaptureImageTitle=Missing PiCaptureAction.CaptureImageTitle +PiCaptureAction.Cloud=Missing PiCaptureAction.Cloud +PiCaptureAction.Contrast=Missing PiCaptureAction.Contrast +PiCaptureAction.DRC=Missing PiCaptureAction.DRC +PiCaptureAction.Exposure=Missing PiCaptureAction.Exposure +PiCaptureAction.Fireworks=Missing PiCaptureAction.Fireworks +PiCaptureAction.FixedFPS=Missing PiCaptureAction.FixedFPS +PiCaptureAction.Flash=Missing PiCaptureAction.Flash +PiCaptureAction.Fluorescent=Missing PiCaptureAction.Fluorescent +PiCaptureAction.High=Missing PiCaptureAction.High +PiCaptureAction.Horizon=Missing PiCaptureAction.Horizon +PiCaptureAction.Incandescent=Missing PiCaptureAction.Incandescent +PiCaptureAction.Low=Missing PiCaptureAction.Low +PiCaptureAction.Medium=Missing PiCaptureAction.Medium +PiCaptureAction.Night=Missing PiCaptureAction.Night +PiCaptureAction.NightPreview=Missing PiCaptureAction.NightPreview +PiCaptureAction.Off=Missing PiCaptureAction.Off +PiCaptureAction.Quality=Missing PiCaptureAction.Quality +PiCaptureAction.Shade=Missing PiCaptureAction.Shade +PiCaptureAction.Sharpness=Missing PiCaptureAction.Sharpness +PiCaptureAction.Snow=Missing PiCaptureAction.Snow +PiCaptureAction.Sports=Missing PiCaptureAction.Sports +PiCaptureAction.Spotlight=Missing PiCaptureAction.Spotlight +PiCaptureAction.Sun=Missing PiCaptureAction.Sun +PiCaptureAction.Tungsten=Missing PiCaptureAction.Tungsten +PiCaptureAction.UseCapture=Missing PiCaptureAction.UseCapture +PiCaptureAction.Verylong=Missing PiCaptureAction.Verylong +PlotterControls.AdvancedControls=Missing PlotterControls.AdvancedControls +PlotterControls.ConnectControls=Missing PlotterControls.ConnectControls +PlotterControls.DrawControls=Missing PlotterControls.DrawControls +PlotterControls.EmergencyStop=Missing PlotterControls.EmergencyStop +PlotterControls.FindHome=Missing PlotterControls.FindHome +PlotterControls.JogTab=Missing PlotterControls.JogTab +PlotterControls.MarlinTab=Missing PlotterControls.MarlinTab +PlotterControls.Pause=Missing PlotterControls.Pause +PlotterControls.Play=Missing PlotterControls.Play +PlotterControls.ProgramTab=Missing PlotterControls.ProgramTab +PlotterControls.Rewind=Missing PlotterControls.Rewind +PlotterControls.Step=Missing PlotterControls.Step +PlotterControls.Title=Missing PlotterControls.Title +PlotterControls.communicationFailure=Missing PlotterControls.communicationFailure +PlotterControls.didNotFind=Missing PlotterControls.didNotFind +PlotterControls.halted=Missing PlotterControls.halted +PlotterControls.homeXYFirst=Missing PlotterControls.homeXYFirst +PlotterSettings.EndGcode=Missing PlotterSettings.EndGcode +PlotterSettings.FindHomeGcode=Missing PlotterSettings.FindHomeGcode +PlotterSettings.StartGcode=Missing PlotterSettings.StartGcode +PlotterSettings.blockBufferSize=Missing PlotterSettings.blockBufferSize +PlotterSettings.handleSmallSegments=Missing PlotterSettings.handleSmallSegments +PlotterSettings.minAcceleration=Missing PlotterSettings.minAcceleration +PlotterSettings.minSegTime=Missing PlotterSettings.minSegTime +PlotterSettings.minSegmentLength=Missing PlotterSettings.minSegmentLength +PlotterSettings.minimumPlannerSpeed=Missing PlotterSettings.minimumPlannerSpeed +PlotterSettings.segmentsPerSecond=Missing PlotterSettings.segmentsPerSecond +PlotterSettings.zMotorType.servo=Missing PlotterSettings.zMotorType.servo +PlotterSettings.zMotorType.stepper=Missing PlotterSettings.zMotorType.stepper +PlotterSettings.zMotorType=Missing PlotterSettings.zMotorType +PlotterSettingsManagerPanel.AddProfile=Missing PlotterSettingsManagerPanel.AddProfile +PlotterSettingsManagerPanel.NewProfileName=Missing PlotterSettingsManagerPanel.NewProfileName +PlotterSettingsManagerPanel.NewProfileNameAlreadyExists=Missing PlotterSettingsManagerPanel.NewProfileNameAlreadyExists +PlotterSettingsManagerPanel.NewProfileNameCannotBeBlank=Missing PlotterSettingsManagerPanel.NewProfileNameCannotBeBlank +PlotterSettingsManagerPanel.RemoveProfile=Missing PlotterSettingsManagerPanel.RemoveProfile +PlotterSettingsPanel.AdjustAcceleration=Missing PlotterSettingsPanel.AdjustAcceleration +PlotterSettingsPanel.BeltLengthNeeded=Missing PlotterSettingsPanel.BeltLengthNeeded +PlotterSettingsPanel.MachineHeight=Missing PlotterSettingsPanel.MachineHeight +PlotterSettingsPanel.MachineWidth=Missing PlotterSettingsPanel.MachineWidth +PlotterSettingsPanel.ServoLengthNeeded=Missing PlotterSettingsPanel.ServoLengthNeeded +PlotterSettingsPanel.Speed=Missing PlotterSettingsPanel.Speed +PlotterSettingsPanel.StepperLengthNeeded=Missing PlotterSettingsPanel.StepperLengthNeeded +PlotterSettingsPanel.TabEssential=Missing PlotterSettingsPanel.TabEssential +PlotterSettingsPanel.TabGCode=Missing PlotterSettingsPanel.TabGCode +PlotterSettingsPanel.TabPen=Missing PlotterSettingsPanel.TabPen +PlotterSettingsPanel.TabSimulation=Missing PlotterSettingsPanel.TabSimulation +PlotterSettingsPanel.Title=Missing PlotterSettingsPanel.Title +PlotterSettingsPanel.penDownColor=Missing PlotterSettingsPanel.penDownColor +PlotterSettingsPanel.penUpColor=Missing PlotterSettingsPanel.penUpColor +PlotterSettingsPanel.penToolDiameter=Missing PlotterSettingsPanel.penToolDiameter +PlotterSettingsPanel.penToolDown=Missing PlotterSettingsPanel.penToolDown +PlotterSettingsPanel.penToolLiftSpeed=Missing PlotterSettingsPanel.penToolLiftSpeed +PlotterSettingsPanel.penToolLowerSpeed=Missing PlotterSettingsPanel.penToolLowerSpeed +PlotterSettingsPanel.penToolMaxFeedRate=Missing PlotterSettingsPanel.penToolMaxFeedRate +PlotterSettingsPanel.penToolUp=Missing PlotterSettingsPanel.penToolUp +Polyeder=Missing Polyeder +Port=Missing Port +PulseLineName=Missing PulseLineName +QuestionTitle=Missing QuestionTitle +Reorder=Missing Reorder +ReportBug=Missing ReportBug +Reset=Missing Reset +Robot=Missing Robot +RobotMenu.GetTimeEstimate=Missing RobotMenu.GetTimeEstimate +RobotMenu.OpenControls=Missing RobotMenu.OpenControls +RobotMenu.RenderStyle=Missing RobotMenu.RenderStyle +RobotMenu.RobotStyle=Missing RobotMenu.RobotStyle +RobotMenu.SaveGCode=Missing RobotMenu.SaveGCode +Rotate=Missing Rotate +SandyNoble.center=Missing SandyNoble.center +SandyNoble.rings=Missing SandyNoble.rings +SandyNoble.title=Missing SandyNoble.title +Save=Missing Save +SaveError=Missing SaveError +SaveGCode.splitGCodeQuestion=Missing SaveGCode.splitGCodeQuestion +SaveGCode.splitGCodeTitle=Missing SaveGCode.splitGCodeTitle +Scale=Missing Scale +SelectImageConverterPanel.Contrast=Missing SelectImageConverterPanel.Contrast +SeparateLoopTurtleRenderer.name=Missing SeparateLoopTurtleRenderer.name +SharePromo=Missing SharePromo +ShowLog=Missing ShowLog +SierpinskiTriangleName=Missing SierpinskiTriangleName +Simplify=Missing Simplify +Size=Missing Size +SoundPreferences.Connect=Missing SoundPreferences.Connect +SoundPreferences.Disconnect=Missing SoundPreferences.Disconnect +SoundPreferences.FinishConvert=Missing SoundPreferences.FinishConvert +SoundPreferences.FinishDraw=Missing SoundPreferences.FinishDraw +SoundPreferences.Title=Missing SoundPreferences.Title +Spiral.toCorners=Missing Spiral.toCorners +SpiralName=Missing SpiralName +SpiralPulse.height=Missing SpiralPulse.height +SpiralPulse.intensity=Missing SpiralPulse.intensity +SpiralPulse.spacing=Missing SpiralPulse.spacing +SpiralPulseName=Missing SpiralPulseName +SpirographEpitrochoid=Missing SpirographEpitrochoid +SpirographMajorRadius=Missing SpirographMajorRadius +SpirographMinorRadius=Missing SpirographMinorRadius +SpirographName=Missing SpirographName +SpirographNumSamples=Missing SpirographNumSamples +SpirographPScale=Missing SpirographPScale +StartAt=Missing StartAt +StartAtAddPenDown=Missing StartAtAddPenDown +StartAtExactly=Missing StartAtExactly +StartAtLastPenDown=Missing StartAtLastPenDown +TextMessage=Missing TextMessage +TextSize=Missing TextSize +TitlePrefix=Missing TitlePrefix +Translate=Missing Translate +TurtleGenerators.LearnMore.Link.Text=Missing TurtleGenerators.LearnMore.Link.Text +UpToDate=Missing UpToDate +UpdateCheckFailed=Missing UpdateCheckFailed +UpdateNotice=Missing UpdateNotice +VoronoiZigZag.optimizePath=Missing VoronoiZigZag.optimizePath +VoronoiZigZagName=Missing VoronoiZigZagName +Width=Missing Width +YourMsgHereName=Missing YourMsgHereName +bottomLeft=Missing bottomLeft +bottomRight=Missing bottomRight +center=Missing center +firmwareVersionBadMessage=Missing firmwareVersionBadMessage +firmwareVersionBadTitle=Missing firmwareVersionBadTitle +horizontal=Missing horizontal +pass15=Missing pass15 +pass45=Missing pass45 +pass75=Missing pass75 +pass90=Missing pass90 +penDiameter=Missing penDiameter +topLeft=Missing topLeft +topRight=Missing topRight +vertical=Missing vertical \ No newline at end of file diff --git a/src/main/resources/messages_ar.properties b/src/main/resources/messages_ar.properties new file mode 100644 index 000000000..2417ca4eb --- /dev/null +++ b/src/main/resources/messages_ar.properties @@ -0,0 +1,363 @@ +# Zainab Abdul Kareem Dinar - github.com/ZainabAKD +BorderName=???? ??????? +Generator_TruchetTiles.Name=?????? ??????? +Generator_TruchetTiles.LineSpacing=??????? ??? ?????? (???) +Generator_TruchetTiles.LinesPerTile=??? ?????? ?? ?????? +LissajousName=??????? +LissajousA= ????? ? ???? ?? ??? +LissajousB=????? ? ???? ?? ??? +LissajousDelta=???? +SpirographName=??? ????? +SpirographMajorRadius=????? ??????? (R) +SpirographMinorRadius=????? ??????? (r) +SpirographPScale=????? (p) +SpirographNumSamples=????? (??????) +SpirographEpitrochoid= ??????? +ConverterRandomLinesName=???? ??????? +ConverterRandomLinesCount=??? ?????? +StartAtLastPenDown=?????? ???? ???? ??? +StartAtAddPenDown=????? ???? ??? +StartAtExactly=?? ???? ????? ?????? +ConfirmQuitQuestion=?? ??? ????? ??? ???? ??????? +ConfirmQuitTitle=???? +ConvertImagePaperFill=??? ?????? +ConvertImagePaperFit=?????? ?????? ??? ?????? +Converter_CMYK_Crosshatch.Name=CMYK +Converter_CMYK_Crosshatch.Passes=???? +Converter_CMYK_Crosshatch.Note= +Converter_CMYK_Spiral.Name=CMYK ?????? +LoadFilePanel.title=?????? ????? +OK=????? +Cancel=????? +Save=??? +Translate=??? +Scale=????? +Rotate=????? +Width=????? +Height=???????? +PaperSettings.Title=??????? ?????? +PaperSettings.PaperSize=??? ?????? +PaperSettings.PaperWidth=??? ?????? (???) +PaperSettings.PaperHeight=?????? ?????? (???) +PaperSettings.ShiftX=?????? ??? ?????? +PaperSettings.ShiftY=?????? ??? ???? +PaperSettings.Rotation=????? +PaperSettings.Landscape=???? +PaperSettings.PaperMargin=?????? (%) +PaperSettings.PaperColor=??? ?????? +TitlePrefix=????? ????? +LoadError=???? ????? ????? +SaveError=???? ??? ????? +ConversionStyle=??? +ConversionFill=??? +OpenFileChooser.FileTypeImage=(%1) ??? ?????? +OpenFileChooser.AllSupportedFiles=??????? ???????? +MetricsPreferences.Title=???? +MetricsPreferences.collectAnonymousMetrics= ????? ??????? ?????? ????????? ??????? ?? ????
Maginally Clever Robots ltd ]]> +MetricsPreferences.collectAnonymousMetricsOnUpdate=?????? ??????? ???? ???????? ?????? ??????

????? ????? ???????? ?? ?? ??? ??????? ??? Makelangelo > ????????? > ??????]]> +SoundPreferences.Title=??????? +SoundPreferences.Connect=????? +SoundPreferences.Disconnect=????? ??????? +SoundPreferences.FinishConvert=????? ??????? +SoundPreferences.FinishDraw=????? ????? +GFXPreferences.Title=???????? +GFXPreferences.showPenUp=????? ?????? ????? +GFXPreferences.antialias=???? ???????? +GFXPreferences.speedVSQuality= ????? (????? ?????) +GFXPreferences.showAllWhileDrawing=????? ???? ?????? ????? ????? +GFXPreferences.dragSpeed=???? ???????? +LanguagePreferences.Title=????? +StartAt=????? ???? ??????? +Robot=????? ????? +UpdateNotice=????? ????? ???? ?? ???????
?? ?????? https://www.marginallyclever.com/product/makelangelo-software/ ?????? ??? ???? ????????]]> +UpToDate=???? ???? ???? ?? ??????? +UpdateCheckFailed=????, ??? ??? ??, ?????? ????? http://www.marginallyclever.com/ ?????? ??? ?? ????? +MenuMakelangelo=??? +ApplicationSettings.title=??????? +MenuUpdate=??? ?? ??????? +MenuQuit=???? +MenuForums=????? ???? ????? +MenuAbout=??? +MenuSettings=??????? +MenuOpenFile=??? ??? +MenuSaveFile=??? ??? +MenuReopenFile=????? ??? ??? ??? +MenuView=??? +MenuView.zoomIn=????? +MenuView.zoomOut=????? +MenuView.zoomFit=?????? ?????? +ShowLog=????? ????? +SierpinskiTriangleName=???? ????? +Converter_VoronoiStippling.Name=????? +VoronoiZigZagName=???? + +Converter_Voronoi.Name=???? +Converter_VoronoiStippling.CellCount=??? ??????? +Converter_VoronoiStippling.DotMax=??? ?????? ?????? +Converter_VoronoiStippling.DotMin=??? ?????? ?????? +BoxGeneratorName=????? ????? +BoxGeneratorMaxSize=????? ?????? +BoxGeneratorCutoff=??? +HilbertCurveName=????? ?????? +HilbertCurveSize=????? +HilbertCurveOrder=??????? +penDiameter=??? ????? +LSystemBranches=?????? +LSystemOrderScale=????? +LSystemAngle=??? ??????? +LSystemNoise=????? +Size=????? +Flap=??? ?????? +Model=????? +Length=??? +PulseLineName=???? ????? +YourMsgHereName=???? ?????? ??? +SpiralName=?????? +SpiralPulseName=??? ?????? ???? +SharePromo=??? ?????? #plotter +DialogAbout.AboutHTML=

Makelangelo %VERSION% %DETAILED_VERSION%

http://www.makelangelo.com/

To get the latest version please visit
https://www.marginallyclever.com/product/makelangelo-software/

Created by Dan Royer.

Additional software and hardware contributions by
Joseph Cottam, Peter Colapietro, Miguel Sánchez
Mark Raynsford, and
Kington Chu.

]]> +MenuResetMachinePreferencesWarning=?????? ??? ???? ??? ????????? +MenuGenerate=????? ?? +MenuNewFile=??? ???? +MenuCaptureImage=?????? ?? ???????? +LSystemTreeName=????? ???? +KochTreeName=????? ???? +FibonacciSpiralName=????? ?????? ??????? +DragonName=????? ?????? +ClickAndDrag=??? ?????? +ConverterWanderName=??????? +ConverterWanderLineCount=???? +ConverterMultipassName=Multipass +ConverterMultipassAngle=??????? (????????) +ConverterMultipassLevels=??????? +firmwareVersionBadTitle=????? ???????? +TextSize=????? +FontFace=??? ???? +TextMessage=????? +PiCaptureAction.CaptureImageTitle=?????? ???? ???????? +PiCaptureAction.CaptureImage=?????? +PiCaptureAction.UseCapture=??????? +PiCaptureAction.CancelCapture=????? +PiCaptureAction.AWB=?????? ??????? ???? ?????? +PiCaptureAction.DRC=????? ?????????? +PiCaptureAction.Exposure=?????? +PiCaptureAction.Contrast=??????? +PiCaptureAction.Quality=?????? +PiCaptureAction.Sharpness=?????? +PiCaptureAction.Off=????? +PiCaptureAction.Auto=?????? +PiCaptureAction.Sun=???? +PiCaptureAction.Cloud=???? +PiCaptureAction.Shade=?? +PiCaptureAction.Tungsten=????? ????? +PiCaptureAction.Fluorescent=????? ??????? +PiCaptureAction.Incandescent=???? +PiCaptureAction.Flash=???? +PiCaptureAction.Horizon=????? +PiCaptureAction.High=???? +PiCaptureAction.Medium=????? +PiCaptureAction.Low=????? +PiCaptureAction.Antishake=??? ???????? +PiCaptureAction.Backlight=??? ???? +PiCaptureAction.Beach=???? +PiCaptureAction.Fireworks=????? ????? +PiCaptureAction.FixedFPS=????? ??? ???????? +PiCaptureAction.Night=????? ?????? +PiCaptureAction.NightPreview=??? ?? ????? ?????? +PiCaptureAction.Snow=???? +PiCaptureAction.Sports=????? +PiCaptureAction.Spotlight=????? +PiCaptureAction.Verylong=Verylong +Import=??????? +Export=????? +Reset=????? ??? +Help=?????? +ArtPipeline=??????? +Crosshatch=Crosshatch +MoireName=Moire +horizontal=???? +vertical=????? +SandyNoble.title=Sandy Noble Style +SandyNoble.rings=????? +SandyNoble.center=??????? +ConverterIntensity=????? +topRight=???? ?????? +topLeft=???? ?????? +bottomRight=???? ?????? +bottomLeft=???? ?????? +center=?????? +Spiral.toCorners=?????? ??? ??????? +SpiralPulse.intensity=?????? +SpiralPulse.spacing=??????? ??? ??????? +SpiralPulse.height=?????? ??????? +SelectImageConverterPanel.Contrast=????? ??????? +Converter_VoronoiStippling.DrawBorders=??? ?????? +Converter_VoronoiStippling.Cutoff=??? +ConverterWanderCMYK=CMYK? +FillPageName=??? ?????? +GosperCurveName=Gosper ????? +pass90=Pass 90 +pass75=Pass 75 +pass45=Pass 45 +pass15=Pass 15 +Direction=????? +FlipH=????? ???? +FlipV=????? ????? +Reorder=????? ????? +Simplify=????? +GraphPaperName=???? ??? ????? +Package=????? ????? +Polyeder=????? ????? ??????? +FirmwareUpdate=????? ???????? +Port=???? +OpenPaperSettings=??????? ?????? +OpenPlotterSettings=??????? ?????? +Open=??? ??? +InfillTurtleAction.title=Fill in closed loops +LogPanel.LogFiles=??? ?????? +LogPanel.Title=???? +LogPanel.CopyClipboard=??? +QuestionTitle=???? +InfoTitle=??????? +ErrorTitle=??? +MenuItemPayPalDonation=???? + +EstimatedTimeIs=????? ?????? ?? %1 +firmwareVersionBadMessage=?? ?????? ?????? ??? ????? ????????
?????? ?? ?????? https://www.marginallyclever.com/product/makelangelo-firmware/
?????? ???? ?????
?????? ????? https://www.marginallyclever.com/product/makelangelo-software/ ?????? ??? ??? ?????????]]> + +TurtleGenerators.LearnMore.Link.Text=????? ?????? +LoadScratch3.foreverNotAllowed=?? ???? ????? ????? +SaveGCode.splitGCodeTitle=???? ??? ?????! +SaveGCode.splitGCodeQuestion=???? ???? ?? ??????? ????????? ?? ??? ?????, ??? ?? ??? ?????? + +Converter_CMYK_Circles.name=CMYK ????? +Converter_CMYK_Circles.maxCircleSize=???? ??? ??????? + +Converter_EdgeDetection.name=??? ?????? +Converter_EdgeDetection.passes=Passes +Converter_EdgeDetection.stepSize=??? ?????? +Converter_EdgeDetection.sampleSize=??? ??????? + +Generator_FlowField.name=Flow field +Generator_FlowField.scaleX=X ???? +Generator_FlowField.scaleY=Y ???? +Generator_FlowField.offsetX=X ????? +Generator_FlowField.offsetY=Y ????? +Generator_FlowField.stepSize=??? ?????? +Generator_FlowField.stepVariation=???? ?????? +Generator_FlowField.stepLength=??? ?????? +Generator_FlowField.fromEdge=????? +Generator_FlowField.rightAngle=????? ???? ??????? +Generator_FlowField.noiseType=??? ??????? + +VoronoiZigZag.optimizePath=????? ?????? +Generator_GridFit.Name=?????? ?????? +Generator_GridFit.margin=???? ?????? ?????? ??? ?? ???? (mm) +Generator_GridFit.cellsWide=????? ????? +Generator_GridFit.cellsHigh=????? ?????? +PlotterSettingsPanel.TabGCode=???? GCode + +PlotterSettings.zMotorType=??? ???? Z +PlotterSettings.zMotorType.servo=????? +PlotterSettings.zMotorType.stepper=Stepper +PlotterSettings.StartGcode=??? GCode +PlotterSettings.EndGcode=????? GCode +PlotterSettings.minimumPlannerSpeed=???? ???? ????? (mm/s) +PlotterSettings.blockBufferSize=?????? ??? ?????? +PlotterSettings.segmentsPerSecond=??? ??????? ?? ??????? +PlotterSettings.minSegmentLength=??? ??? ?????? (???) +PlotterSettings.minSegTime=??? ??? ?????? (ns) +PlotterSettings.handleSmallSegments=?????? ????? ??????? +PlotterSettings.minAcceleration=??? ????? (mm/s²) + +PlotterSettingsPanel.Title=Plotter ??????? +PlotterSettingsPanel.TabEssential=????? +PlotterSettingsPanel.TabPen=??? +PlotterSettingsPanel.TabSimulation=?????? +PlotterSettingsPanel.MachineWidth=??? ??????? (???) +PlotterSettingsPanel.MachineHeight=??? ??????? (???) +PlotterSettingsPanel.ServoLengthNeeded=??? ??? ??????? (?) +PlotterSettingsPanel.StepperLengthNeeded=Stepper ??? ??? (m,each) +PlotterSettingsPanel.BeltLengthNeeded=??? ???? ??????? (m,each) +PlotterSettingsPanel.penToolDiameter=????? (???) +PlotterSettingsPanel.penToolMaxFeedRate=???? ?????? (mm/min) +PlotterSettingsPanel.penToolUp=??????? ?????? (????) +PlotterSettingsPanel.penToolDown=??????? ?????? (????) +PlotterSettingsPanel.penToolLiftSpeed=??? ??? ????? (ms) +PlotterSettingsPanel.penToolLowerSpeed=??? ??????? ????? (ms) +PlotterSettingsPanel.Speed=???? ????? (mm/min) +PlotterSettingsPanel.AdjustAcceleration=??????? (mm/s²) +PlotterSettingsPanel.penUpColor=Up color +PlotterSettingsPanel.penDownColor=Default down color + +PlotterSettingsManagerPanel.RemoveProfile=- +PlotterSettingsManagerPanel.AddProfile=+ +PlotterSettingsManagerPanel.NewProfileName=??? ???? +PlotterSettingsManagerPanel.NewProfileNameAlreadyExists=????? ?????? ??????, ???? ????? +PlotterSettingsManagerPanel.NewProfileNameCannotBeBlank=?? ???? ?? ???? ????? ?????, ???? ????? + +FirmwareUploader.help=??????]]> + +FirmwareUploaderPanel.status=???? ????? ???????? +FirmwareUploaderPanel.startM5=M5 +FirmwareUploaderPanel.startHuge=??? +FirmwareUploaderPanel.avrdudeNotDownloaded=AVRDude ?? ??? ????? +FirmwareUploaderPanel.notFound=?? ???? ????? %1 +FirmwareUploaderPanel.failed=??? ????? ???????? +FirmwareUploaderPanel.noPortSelected=?? ??? ?????? ???? +FirmwareUploaderPanel.downloadFailed=??? ??????? +FirmwareUploaderPanel.finished=?? ???????? + +DefaultTurtleRenderer.name=????? +BarberPoleTurtleRenderer.name=Barber pole +SeparateLoopTurtleRenderer.name=Separate loops +MarlinSimulationVisualizer.name=Marlin simulation + +ChooseConnection.ButtonConnect=????? +ChooseConnection.ButtonDisconnect=????? ??????? + +PlotterControls.Title=????? ???? ??????? +PlotterControls.ConnectControls=????? +PlotterControls.DrawControls=????? ???? ????? +PlotterControls.FindHome=????? ?? ?????? ???????? +PlotterControls.Pause=????? ???? +PlotterControls.Rewind=????? +PlotterControls.Play=??? +PlotterControls.EmergencyStop=???? ???? +PlotterControls.MarlinTab=Marlin +PlotterControls.ProgramTab=???????? +PlotterControls.JogTab=????? ???? ??????? +PlotterControls.AdvancedControls=????? ???? ?????? +PlotterControls.homeXYFirst=???? ??????? ?????? ???????? +PlotterControls.didNotFind=??? ?? ??????? ???????? +PlotterControls.communicationFailure=?? ???? ??????? ?? ???????, ?????? ?????? ?? ?????? ?? ???? ????? +PlotterControls.halted=????? ??????? +PlotterControls.Step=???? + +RobotMenu.RobotStyle=??? ??????? +RobotMenu.RenderStyle=??? ??????? +RobotMenu.OpenControls= ????????? +RobotMenu.SaveGCode=??? Gcode ??? ??? +RobotMenu.GetTimeEstimate=????? ????? + +JogInterface.FindHome=?????? ???????? +JogInterface.PenUp=??? ??????? ????? +JogInterface.PenDown=????? ??????? ????? +JogInterface.DisengageMotors=??? ??????? +JogInterface.EngageMotors=????? + +ConversationHistory.Clear=??? +ConversationHistory.Save=??? +ConversationHistory.Copy=??? + +CartesianButtons.buttonCenter= + +Converter_IntensityToHeight.name=???? ??????? ??? ???????? +Converter_IntensityToHeight.spacing=????? ?????? +Converter_IntensityToHeight.sampleRate=???? ?????? +Converter_IntensityToHeight.waveIntensity=??? ?????? + +Converter_QuadTreeInstant=Quad tree instant +Converter_QuadTreeInstant.maxDepth=???? ??? +Converter_QuadTreeInstant.baseCutOff=Base cut-off +Converter_QuadTreeInstant.cutOffIncrement=Cut-off increment \ No newline at end of file diff --git a/src/main/resources/messages_cn.properties b/src/main/resources/messages_cn.properties new file mode 100644 index 000000000..24d06a62e --- /dev/null +++ b/src/main/resources/messages_cn.properties @@ -0,0 +1,174 @@ +# Kmimax(kmimax2016@gmail.com) +BorderName=??? +LissajousName=Lissajous +LissajousA=A (>0) +LissajousB=B (>0) +LissajousDelta=Delta (0...1) +SpirographName=??? +SpirographMajorRadius=???(R) +SpirographMinorRadius=???(r) +SpirographPScale=?? (p) +SpirographNumSamples=??(?) +SpirographEpitrochoid=???? +ConverterRandomLinesName=??? +ConverterRandomLinesCount=?? +StartAtLastPenDown=??????? +StartAtAddPenDown=????????? +StartAtExactly=????? +ConfirmQuitQuestion=??????? +ConfirmQuitTitle=?? +ConvertImagePaperFill=????? +ConvertImagePaperFit=??????? +Cancel=?? +Save=?? +PaperSettings.PaperWidth=????? +PaperSettings.PaperHeight=????? +TitlePrefix=Makelangelo +LoadError=??????: +SaveError=??????: +FlipH=???? +FlipV=???? +ConversionStyle=?? +ConversionFill=?? +OpenFileChooser.FileTypeImage=?? (%1) +OpenFileChooser.AllSupportedFiles=All supported files +MetricsPreferences.Title=?? +MetricsPreferences.collectAnonymousMetrics=I consent to sharing anonymous usage data
with Marginally Clever Robots, Limited.]]> +MetricsPreferences.collectAnonymousMetricsOnUpdate=May Marginally Clever Robots, Ltd. collect anonymous usage metrics about this app?

You can update this any time in Makelangelo > Preferences > Metrics]]> +SoundPreferences.Title=?? +SoundPreferences.Connect=?? +SoundPreferences.Disconnect=?? +SoundPreferences.FinishConvert=???? +SoundPreferences.FinishDraw=???? +GFXPreferences.Title=?? +GFXPreferences.showPenUp=?????? +GFXPreferences.antialias=Antialias lines +GFXPreferences.speedVSQuality=??vs?? +GFXPreferences.showAllWhileDrawing=????????? +LanguagePreferences.Title=?? +StartAt=??? +PlotterControls.Pause=?? +Robot=Makelangelo +RobotMenu.OpenControls=Open Controls +PlotterControls.Rewind=Rewind +PlotterControls.Play=Draw +JogInterface.PenUp=?? +JogInterface.PenDown=?? +UpdateNotice=A new version of this software is available.
Please visit
https://www.marginallyclever.com/product/makelangelo-software/ to get the new hotness.]]> +UpToDate=???????. +UpdateCheckFailed=???????? ???https://www.marginallyclever.com/ ????. +MenuMakelangelo=Makelangelo +ApplicationSettings.title=??? +MenuUpdate=???? +MenuQuit=?? +MenuForums=?????? +MenuAbout=?? +MenuSettings=?? +MenuOpenFile=????... +MenuReopenFile=?????????? +RobotMenu.SaveGCode=?????/ SD? +MenuView=?? +MenuView.zoomIn=?? + +MenuView.zoomOut=?? - +MenuView.zoomFit=???? +ShowLog=???? +SierpinskiTriangleName=Sierpinski???? +Converter_VoronoiStippling.Name=Voronoi?? +VoronoiZigZagName=Voronoi?? +Converter_VoronoiStippling.CellCount=??? +Converter_VoronoiStippling.DotMax=????? +Converter_VoronoiStippling.DotMin=????? +BoxGeneratorName=?? +BoxGeneratorMaxSize=???? +BoxGeneratorCutoff=?? +HilbertCurveName=Hilbert??? +HilbertCurveSize=?? +HilbertCurveOrder=?? +LSystemBranches=?? +LSystemOrderScale=?? +LSystemAngle=???? +PulseLineName=??? +YourMsgHereName=??????? +SpiralName=?? +SpiralPulseName=???? +SharePromo=????????#??? +JogInterface.DisengageMotors=?? +JogInterface.EngageMotors=?? +DialogAbout.AboutHTML=

Makelangelo %VERSION% %DETAILED_VERSION%

http://www.makelangelo.com/

To get the latest version please visit
https://www.marginallyclever.com/product/makelangelo-software/

Created by Dan Royer.

Additional software and hardware contributions by
Joseph Cottam, Peter Colapietro, Miguel S?nchez
Mark Raynsford, and
Kington Chu.

]]> +MenuResetMachinePreferencesWarning=??????????? +MenuGenerate=???? +MenuNewFile=?? +MenuCaptureImage=????? +LSystemTreeName=L???? +KochTreeName=Koch??? +FibonacciSpiralName=Fibonacci??? +DragonName=?? +ClickAndDrag=????? +ConverterWanderName=?? +ConverterWanderLineCount=# ?? +ConverterMultipassName=??? +ConverterMultipassAngle=?(?) +ConverterMultipassLevels=? +firmwareVersionBadTitle=???? +firmwareVersionBadMessage=The firmware (code in the brain of your robot) is not the version I expect. I found v%VERSION%.
Please visit https://www.marginallyclever.com/product/makelangelo-firmware/
for the latest firmware.
Please visit https://www.marginallyclever.com/product/makelangelo-software/ for the latest software.]]> +TextSize=?? +TextMessage=?? +PiCaptureAction.CaptureImageTitle=?????? +PiCaptureAction.CaptureImage=?? +PiCaptureAction.UseCapture=?? +PiCaptureAction.CancelCapture=?? +PiCaptureAction.AWB=????? +PiCaptureAction.DRC=???? +PiCaptureAction.Exposure=?? +PiCaptureAction.Contrast=??? +PiCaptureAction.Quality=?? +PiCaptureAction.Sharpness=?? +PiCaptureAction.Off=?? +PiCaptureAction.Auto=?? +PiCaptureAction.Sun=?? +PiCaptureAction.Cloud=?? +PiCaptureAction.Shade=?? +PiCaptureAction.Tungsten=??? +PiCaptureAction.Fluorescent=??? +PiCaptureAction.Incandescent=??? +PiCaptureAction.Flash=??? +PiCaptureAction.Horizon=??? +PiCaptureAction.High=? +PiCaptureAction.Medium=? +PiCaptureAction.Low=? +PiCaptureAction.Antishake=?? +PiCaptureAction.Backlight=??? +PiCaptureAction.Beach=?? +PiCaptureAction.Fireworks=?? +PiCaptureAction.FixedFPS=??FPS +PiCaptureAction.Night=?? +PiCaptureAction.NightPreview=?? +PiCaptureAction.Snow=?? +PiCaptureAction.Sports=?? +PiCaptureAction.Spotlight=??? +PiCaptureAction.Verylong=??? +Import=?? +Export=?? +Reset=?? +Help=?? +ArtPipeline=???? +PaperSettings.PaperColor=???? +Crosshatch=???? +MoireName=?? +horizontal=?? +vertical=?? +SandyNoble.title=Sandy?? +ConverterIntensity=?? +topRight=?? +topLeft=??? +bottomRight=?? +bottomLeft=??? +center=?? +Spiral.toCorners=????? +SpiralPulse.spacing=??? +SpiralPulse.height=???? +Converter_VoronoiStippling.Cutoff=???? +Converter_VoronoiStippling.DrawBorders=???? +ConverterWanderCMYK=CMYK? +FillPageName=???? +GosperCurveName=Gosper?? \ No newline at end of file diff --git a/src/main/resources/messages_de.properties b/src/main/resources/messages_de.properties new file mode 100644 index 000000000..69c0a1337 --- /dev/null +++ b/src/main/resources/messages_de.properties @@ -0,0 +1,377 @@ +Converter_IntensityToHeight.sampleRate=Abtastrate +Converter_IntensityToHeight.spacing=Wellenabstand +Converter_IntensityToHeight.waveIntensity=Wellenintensität +Converter_PulseCMYK.SampleRate=Abtastrate +PlotterControls.didNotFind=Fehler in der Kommunikation mit dem Plotter +SelectImageConverterPanel.Contrast=Kontastkorrektur +VoronoiZigZag.optimizePath=Optimierter Pfad +ApplicationSettings.title=Einstellungen +ArtPipeline=Werkzeuge +BarberPoleTurtleRenderer.name=Barber pole +BorderName=Papier umrahmen +BoxGeneratorCutoff=Grenze +BoxGeneratorMaxSize=Max Größe +BoxGeneratorName=Boxxy +Cancel=Abbrechen +CartesianButtons.buttonCenter= +ChooseConnection.ButtonConnect=Verbinden +ChooseConnection.ButtonDisconnect=Trennen +ClickAndDrag=Klicke & ziehe +ConfirmQuitQuestion=Bist du sicher, dass du beenden willst? +ConfirmQuitTitle=Beenden +ConversationHistory.Clear=Löschen +ConversationHistory.Copy=Kopieren +ConversationHistory.Save=Speichern +ConversionFill=Füllung +ConversionStyle=Stil +ConvertImagePaperCenter=Bild zentrieren +ConvertImagePaperFill=Ganzes Papier füllen +ConvertImagePaperFit=Bild auf Papier einpassen +ConverterIntensity=Intensität +ConverterMultipassAngle=Winkel (Grad) +ConverterMultipassLevels=Stufen +ConverterMultipassName=Multipass +ConverterRandomLinesCount=Linienanzahl +ConverterRandomLinesName=Zufällige Linien +ConverterWanderCMYK=CMYK? +ConverterWanderLineCount=# Linien +ConverterWanderName=Schweifen +Converter_CMYK_Circles.name=CMYK Kreise +Converter_CMYK_Circles.maxCircleSize=Maximaler Kreisdurchmesser +Converter_CMYK_Circles.fillCircles=Kreise füllen +Converter_CMYK_Crosshatch.Name=CMYK +Converter_CMYK_Crosshatch.Note= +Converter_CMYK_Crosshatch.Passes=Durchgänge +Converter_CMYK_Spiral.Name=CMYK Spirale +Converter_EdgeDetection.border=Zeichne Rand +Converter_EdgeDetection.name=Kantenerkennung +Converter_EdgeDetection.passes=Durchgänge +Converter_EdgeDetection.sampleSize=Abtastabstand +Converter_EdgeDetection.stepSize=Schrittweite +Converter_IntensityToHeight.name=Intensität - Höhe +Converter_PulseCMYK.Name=Pulse CMYK +Converter_QuadTreeInstant.baseCutOff=Basis cut-off +Converter_QuadTreeInstant.cutOffIncrement=Cut-off Schrittweite +Converter_QuadTreeInstant.maxDepth=Maximale Tiefe +Converter_QuadTreeInstant=Quad tree instant +Converter_TruchetFromImage.linesPerTile=Linien pro Kachel +Converter_TruchetFromImage.name=Truchet-Kachel vom Bild +Converter_TruchetFromImage.spacing=Raum zwischen den Linien +Converter_Voronoi.Name=Voronoi Diagramm +Converter_Voronoi.ShowCenters=Zeige die Zentren +Converter_VoronoiStippling.CellCount=Zellenanzahl +Converter_VoronoiStippling.Cutoff=Tiefpassgrenze +Converter_VoronoiStippling.DotMax=Max Punktgröße +Converter_VoronoiStippling.DotMin=Min Punktgröße +Converter_VoronoiStippling.DrawBorders=Rand zeichnen? +Converter_VoronoiStippling.Name=Voronoi Punkte +Crosshatch=Kreuzschraffur +DefaultTurtleRenderer.name=Default +DialogAbout.AboutHTML= <html> <body> <h1>Makelangelo %VERSION% <span style="font-size: small; font-weight: normal">%DETAILED_VERSION%</span></h1> <h3><a href='http://www.makelangelo.com/'>http://www.makelangelo.com/</a> </h3> <p>Um die aktuellste Version zu bekommen besuche<br> <a href='https://www.marginallyclever.com/product/makelangelo-software/'>https://www.marginallyclever.com/product/makelangelo-software/</a></p> <p>Erstellt von <a href=mailto:dan@marginallyclever.com>Dan Royer</a>.</p> <p>Zusätzliche Software- und Hardwarebeiträge von<br> Joseph Cottam, <a href='https://github.com/virtuoushub'>Peter Colapietro<a/>, Miguel Sánchez<br> Mark Raynsford, and <a href='https://ca.linkedin.com/in/kington-chu-219923123'>Kington Chu</a>.</p> </body> </html> +Direction=Richtung +DirectionLoopTurtleRenderer.name=Richtungsschleifen +DragonName=Drachenfraktal +ErrorTitle=Fehler +EstimatedTimeIs=Geschätzte Zeit ist %1 +Export=Exportiere +FibonacciSpiralName=Fibonacci Spiralenfraktal +FillPageName=Seite füllen +FirmwareUpdate=Firmwareupdate +FirmwareUploader.help=help]]> +FirmwareUploaderPanel.avrdudeNotDownloaded=AVRDude wurde nicht heruntergeladen. +FirmwareUploaderPanel.downloadFailed=Download fehlgeschlagen. +FirmwareUploaderPanel.failed=Firmware upload fehlgeschlagen. +FirmwareUploaderPanel.finished=Firmwareupdate OK! +FirmwareUploaderPanel.noPortSelected=Kein Port ausgewählt +FirmwareUploaderPanel.notFound=%1 nicht gefunden. +FirmwareUploaderPanel.startHuge=Huge +FirmwareUploaderPanel.startM5=M5 +FirmwareUploaderPanel.status=Firmware-Upload Status +Flap=Klappe +FlipH=Horizontal umkehren +FlipV=Vertikal umkehren +FontFace=Schrifttype +GFXPreferences.Title=Grafik +GFXPreferences.antialias=Linien glätten +GFXPreferences.dragSpeed=Kamerageschwindigkeit +GFXPreferences.showAllWhileDrawing=Zeige gesamtes Bild während desZeichnens +GFXPreferences.showPenUp=Zeige Verfahrbewegungen +GFXPreferences.speedVSQuality=Geschwindigkeit über Qualität +Generator.randomSeed=Startwert (Seed) +Generator_FlowField.fromEdge=Kontinuierlich +Generator_FlowField.name=Strömungsfeld +Generator_FlowField.noiseType=Rauschart +Generator_FlowField.offsetX=X Offset +Generator_FlowField.offsetY=Y Offset +Generator_FlowField.rightAngle=Füge orthogonale Linien hinzu +Generator_FlowField.scaleX=X Maßstab +Generator_FlowField.scaleY=Y Maßstab +Generator_FlowField.stepLength=Schrittlänge +Generator_FlowField.stepSize=Schrittgröße +Generator_FlowField.stepVariation=Schrittvariation +Generator_GridFit.Name=Rechteck +Generator_GridFit.cellsHigh=Zellen vertical +Generator_GridFit.cellsWide=Zellen horizontal +Generator_GridFit.margin=Minimaler Rand an jeder Seite (mm) +Generator_GridHexagons.Name=Sechseck +Generator_GridHexagons.radius=Umkreisradius +Generator_MazeCircle.name=Kreis +Generator_MazeCircle.rings=Ringe +Generator_MazeHoneycomb.name=Bienenwabe +Generator_MazeRectangle.columns=Spalten +Generator_MazeRectangle.name=Rechteck +Generator_MazeRectangle.rows=Zeilen +Generator_Spiral.name=Spirale +Generator_Spiral.radius=Radius +Generator_TruchetTiles.LineSpacing=Abstand zwischen den Linien (mm) +Generator_TruchetTiles.LinesPerTile=Linien pro Fliese +Generator_TruchetTiles.Name=Truchet-Fliesen +Generator_TruchetTiles.allow=Erlaube '%1' +GosperCurveName=Gosper Kurve +GraphPaperName=Millimeterpapier +Height=Höhe +Help=Hilfe +HilbertCurveName=Hilbert Kurvenfraktal +HilbertCurveOrder=Reihenfolge +HilbertCurveSize=Größe +Import=Importiere +InfillTurtleAction.title=Fülle geschlossene Bereiche +InfoTitle=Information +JogInterface.DisengageMotors=Motoren deaktivieren +JogInterface.EngageMotors=Motoren aktivieren +JogInterface.FindHome=Zuhause finden +JogInterface.PenDown=Stift absenken +JogInterface.PenUp=Stift anheben +KochTreeName=Koch Kurvenfraktal +LSystemAngle=Winkelweite +LSystemBranches=Verzweigungen +LSystemNoise=Rauschen +LSystemOrderScale=Größe +LSystemTreeName=L System Baumfraktal +LanguagePreferences.Title=Sprache +Length=Länge +LineWeightByImageIntensity.image=Bild +LineWeightByImageIntensity.name=Linienstärke +LineWeightByImageIntensity.thickness=Strichstärke +LissajousA=A (>0) +LissajousB=B (>0) +LissajousDelta=Delta (0...1) +LissajousName=Lissajous +LoadError=Datei konnte nicht geladen werden: +LoadFilePanel.title=Dateivorschau laden +LoadScratch3.foreverNotAllowed=Forever Schleifen sind für immer verboten. +LogPanel.CopyClipboard=Kopieren +LogPanel.LogFiles=Protokoll Dateien +LogPanel.Title=Protokoll +MarlinSimulationVisualizer.name=Marlin Simulation +MenuAbout=Über +MenuCaptureImage=Von Kamera aufnehmen +MenuForums=Online Hilfeforum +MenuGenerate.Fractals=Fraktals +MenuGenerate.Grids=Raster +MenuGenerate.Mazes=Irrgarten +MenuGenerate.SpaceFillers=Zwischenräume auffüllen +MenuGenerate=Kunst generieren +MenuImportFile=Datei importieren... +MenuItemPayPalDonation=PayPal Spende +MenuItemTranslate=Übersetzen helfen +MenuMakelangelo=Datei +MenuManual=Anleitung lesen +MenuNewFile=Neu +MenuOpenFile=Datei öffnen... +MenuQuit=Beenden +MenuReopenFile=Letzte Datei erneut öffnen +MenuResetMachinePreferencesWarning=Bist du sicher, dass du all deine Einstellungen zurücksetzen willst? +MenuSaveFile=Datei speichern... +MenuSettings=Einstellungen +MenuUpdate=Neue Version suchen +MenuView.zoomFit=Einpassen +MenuView.zoomIn=Zoom + +MenuView.zoomOut=Zoom - +MenuView=Ansicht +MenuWindows=Fenster +MetricsPreferences.Title=Maße +MetricsPreferences.collectAnonymousMetrics=<html><body>Ich stimme zu, anonyme Benutzungsdaten mit<br>Marginally Clever Robots, Limited zu teilen.</html></body> +MetricsPreferences.collectAnonymousMetricsOnUpdate=<html><body>Darf Marginally Clever Robots, Ltd. anonyme Metriken über diese app sammeln?<br><br>Du kannst dies jederzeit unter <i>Makelangelo &gt; Einstellungen &gt; Metriken ändern</i></html></body> +Model=Modell +MoireName=Moiré +OK=OK +Open=Datei öffnen... +OpenFileChooser.AllSupportedFiles=Alle unterstützte Formate +OpenFileChooser.FileTypeImage=Bild (%1) +OpenLogFolder=Öffne Protokoll Verzeichnis +OpenPaperSettings=Papier-Einstellungen +OpenPlotterSettings=Plotter-Einstellungen +Package=Papierschachtel +PaperSettings.Landscape=Querformat +PaperSettings.PaperColor=Papierfarbe +PaperSettings.PaperHeight=Papierhöhe +PaperSettings.PaperMargin=Rand (%) +PaperSettings.PaperSize=Papiergröße +PaperSettings.PaperWidth=Papierbreite +PaperSettings.Rotation=Drehung +PaperSettings.ShiftX=Verschiebung links +PaperSettings.ShiftY=Verschiebung hoch +PaperSettings.Title=Papiereinstellungen +PiCaptureAction.AWB=Automatischer Weißabgleich +PiCaptureAction.Antishake=Stabilisierung +PiCaptureAction.Auto=Auto +PiCaptureAction.Backlight=Hintergrundbeleuchtung +PiCaptureAction.Beach=Strand +PiCaptureAction.CancelCapture=Abbruch +PiCaptureAction.CaptureImage=Aufnehmen +PiCaptureAction.CaptureImageTitle=Kamerabild aufnehmen +PiCaptureAction.Cloud=Wolken +PiCaptureAction.Contrast=Kontrast +PiCaptureAction.DRC=Dynamikbereich +PiCaptureAction.Exposure=Belichtung +PiCaptureAction.Fireworks=Feuerwerk +PiCaptureAction.FixedFPS=Feste Framerate +PiCaptureAction.Flash=Blitz +PiCaptureAction.Fluorescent=Leuchtstoffröhre +PiCaptureAction.High=Hoch +PiCaptureAction.Horizon=Horizont +PiCaptureAction.Incandescent=Glühlampe +PiCaptureAction.Low=Niedrig +PiCaptureAction.Medium=Mittel +PiCaptureAction.Night=Nacht +PiCaptureAction.NightPreview=Nachtvorschau +PiCaptureAction.Off=Aus +PiCaptureAction.Quality=Qualität +PiCaptureAction.Shade=Schattierung +PiCaptureAction.Sharpness=Schärfe +PiCaptureAction.Snow=Schnee +PiCaptureAction.Sports=Sport +PiCaptureAction.Spotlight=Spotlicht +PiCaptureAction.Sun=Sonne +PiCaptureAction.Tungsten=Kunstlicht +PiCaptureAction.UseCapture=Benutze +PiCaptureAction.Verylong=Langzeit +PlotterControls.AdvancedControls=Erweiterte Steuerung +PlotterControls.ConnectControls=Verbinden +PlotterControls.DrawControls=Plottersteuerung +PlotterControls.EmergencyStop=Notfall-Stop +PlotterControls.FindHome=Home finden +PlotterControls.JogTab=Robotersteuerung +PlotterControls.MarlinTab=Marlin +PlotterControls.Pause=Pause +PlotterControls.Play=Zeichnen +PlotterControls.ProgramTab=Programm +PlotterControls.Rewind=Zurückspulen +PlotterControls.Step=Schritt +PlotterControls.Title=Robotersteuerung +PlotterControls.communicationFailure=Keine Anwort vom Plotter. Bitte das USB-Kabel und die Geschwindikeitseinstellungen prüfen +PlotterControls.halted=Plotter angehalten! +PlotterControls.homeXYFirst=Bitte zuerst Home finden. +PlotterSettings.EndGcode=GCode für das Ende +PlotterSettings.FindHomeGcode=Home GCode finden +PlotterSettings.StartGcode=GCode für den Start +PlotterSettings.blockBufferSize=Block-Puffergröße +PlotterSettings.handleSmallSegments=Kleine Segmente bearbeiten +PlotterSettings.minAcceleration=Min. Beschleunigung +PlotterSettings.minSegTime=Min. Segmentzeit +PlotterSettings.minSegmentLength=Min. Segmentlänge +PlotterSettings.minimumPlannerSpeed=Min. Planer-Geschwindigkeit +PlotterSettings.segmentsPerSecond=Segmente pro Sekunde +PlotterSettings.zMotorType.servo=Servo +PlotterSettings.zMotorType.stepper=Schrittmotor +PlotterSettings.zMotorType=Z-Achsen Typ +PlotterSettingsManagerPanel.AddProfile=+ +PlotterSettingsManagerPanel.NewProfileName=Neuer eindeutiger Name +PlotterSettingsManagerPanel.NewProfileNameAlreadyExists=Der Name exisiert bereits. Neuer Versuch: +PlotterSettingsManagerPanel.NewProfileNameCannotBeBlank=Der Name darf nicht Leer sein. Neuer Versuch: +PlotterSettingsManagerPanel.RemoveProfile=- +PlotterSettingsPanel.AdjustAcceleration=Beschleuningung (mm/s²) +PlotterSettingsPanel.BeltLengthNeeded=Timing belt length (m,each) +PlotterSettingsPanel.MachineHeight=Maschinenhöhe (mm) +PlotterSettingsPanel.MachineWidth=Maschinenbreite (mm) +PlotterSettingsPanel.ServoLengthNeeded=Servokabellänge (m) +PlotterSettingsPanel.Speed=Zeichengeschwindigkeit (mm/min) +PlotterSettingsPanel.StepperLengthNeeded=Schrittmotorkabellänge (m,jeweils) +PlotterSettingsPanel.TabEssential=Basis +PlotterSettingsPanel.TabGCode=Benutzer spezifischer GCode +PlotterSettingsPanel.TabPen=Stift +PlotterSettingsPanel.TabSimulation=Simulation +PlotterSettingsPanel.Title=Plotter Einstellungen +PlotterSettingsPanel.penDownColor=Stift-Unten Farbe +PlotterSettingsPanel.penUpColor=Stift-Oben Farbe +PlotterSettingsPanel.penToolDiameter=Durchmesser (mm) +PlotterSettingsPanel.penToolDown=Unten-Winkel (Grad) +PlotterSettingsPanel.penToolLiftSpeed=Stift-Anheben-Zeit (ms) +PlotterSettingsPanel.penToolLowerSpeed=Stift-Senken-Zeit (ms) +PlotterSettingsPanel.penToolMaxFeedRate=Bewegungsgeschwindigkeit (mm/min) +PlotterSettingsPanel.penToolUp=Oben-Winkel (Grad) +Polyeder=Papier Polyeder +Port=Port +PulseLineName=Pulslinie +QuestionTitle=Frage +Reorder=Neu sortieren +ReportBug=Ein Problem melden +Reset=Zurücksetzen +Robot=Makelangelo +RobotMenu.GetTimeEstimate=Geschätzte Zeit +RobotMenu.OpenControls=Steuerung öffnen +RobotMenu.RenderStyle=Anzeigestil +RobotMenu.RobotStyle=Roboter-Stil +RobotMenu.SaveGCode=Speichere GCode in Datei/SD +Rotate=Rotieren +SandyNoble.center=Mitte +SandyNoble.rings=Ringe +SandyNoble.title=Sandy Noble Stil +Save=Speichern +SaveError=Datei konnte nicht gespeichert werden: +SaveGCode.splitGCodeQuestion=Diese Zeichung benötigt %1 Stiftwechsel. In mehrere Dateien zerlegen? +SaveGCode.splitGCodeTitle=Mehrere Farben entdeckt +Scale=Skalierung +SeparateLoopTurtleRenderer.name=Separate Schleifen +SharePromo=Verwende Hashtag #makelangelo +ShowLog=Zeige Protokoll +SierpinskiTriangleName=Sierpinski Dreieck Fraktal +Simplify=Vereinfachen +Size=Größe +SoundPreferences.Connect=Verbinden +SoundPreferences.Disconnect=Trennen +SoundPreferences.FinishConvert=Umwandlung fertig +SoundPreferences.FinishDraw=Zeichnen abgeschlossen +SoundPreferences.Title=Klänge +Spiral.toCorners=Spirale bis in Ecken +SpiralName=Spirale +SpiralPulse.height=Pulshöhe +SpiralPulse.intensity=Intensität +SpiralPulse.spacing=Abstand zwischen Ringen +SpiralPulseName=Pulsierende Spirale +SpirographEpitrochoid=Ist ein Epitrochoid +SpirographMajorRadius=Großer Radius (R) +SpirographMinorRadius=Kleiner radius (r) +SpirographName=Spirograph +SpirographNumSamples=Zwischenwerte (qualität) +SpirographPScale=Skalierung (p) +StartAt=Starte bei +StartAtAddPenDown=Füge Stift runter hier ein +StartAtExactly=Mache nichts spezielles +StartAtLastPenDown=Fahre zum letzten Stift hoch +TextMessage=Text +TextSize=Größe +TitlePrefix=Makelangelo +Translate=Verschieben +TurtleGenerators.LearnMore.Link.Text=Weitere Informationen... +UpToDate=Diese Software ist aktuell. +UpdateCheckFailed=Tut mir leid es hat nicht funktioniert. Bitte besuche http://www.marginallyclever.com/ um selbst zu suchen. +UpdateNotice=<html><body>Eine neuere Version dieser Software ist verfügbar.<br/>Bitte besuche <a href=https://www.marginallyclever.com/product/makelangelo-software/>https://www.marginallyclever.com/product/makelangelo-software/</a> um die neue Version zu bekommen.</html></body> +VoronoiZigZagName=Voronoi Zickzack +Width=Breite +YourMsgHereName=Text für Schriftzug eingeben +bottomLeft=Unten links +bottomRight=Unten rechts +center=Mitte +firmwareVersionBadMessage=Die Firmware (Code im Gehirn Ihres Roboters) ist nicht die erwartete Version. Ich habe %1% gefunden.
Bitte besuchen Sie
https://www.marginallyclever.com/product/makelangelo-firmware/
für die neueste Firmware.
Bitte besuchen Sie https://www.marginallyclever.com/product/makelangelo-software/ für die neueste Software.]]> +firmwareVersionBadTitle=Firmware-Update +horizontal=Horizontal +pass15=Durchgang 15 +pass45=Durchgang 45 +pass75=Durchgang 75 +pass90=Durchgang 90 +penDiameter=Stift-Durchmesser +topLeft=Oben links +topRight=Oben rechts +vertical=Vertikal \ No newline at end of file diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties new file mode 100644 index 000000000..82bdd3704 --- /dev/null +++ b/src/main/resources/messages_en.properties @@ -0,0 +1,377 @@ +ApplicationSettings.title=Preferences +ArtPipeline=Tools +BarberPoleTurtleRenderer.name=Barber pole +BorderName=Outline paper +BoxGeneratorCutoff=Cutoff +BoxGeneratorMaxSize=Max size +BoxGeneratorName=Boxxy +Cancel=Cancel +CartesianButtons.buttonCenter= +ChooseConnection.ButtonConnect=Connect +ChooseConnection.ButtonDisconnect=Disconnect +ClickAndDrag=Click & Drag +ConfirmQuitQuestion=Are you sure you want to quit? +ConfirmQuitTitle=Exit +ConversationHistory.Clear=Clear +ConversationHistory.Copy=Copy +ConversationHistory.Save=Save +ConversionFill=Fill +ConversionStyle=Style +ConvertImagePaperCenter=Center image +ConvertImagePaperFill=Fill entire paper +ConvertImagePaperFit=Fit image on paper +ConverterIntensity=Intensity +ConverterMultipassAngle=Angle (degrees) +ConverterMultipassLevels=Levels +ConverterMultipassName=Multipass +ConverterRandomLinesCount=Number of lines +ConverterRandomLinesName=Random lines +ConverterWanderCMYK=CMYK? +ConverterWanderLineCount=# Lines +ConverterWanderName=Wander +Converter_CMYK_Circles.name=CMYK circles +Converter_CMYK_Circles.maxCircleSize=max circle size +Converter_CMYK_Circles.fillCircles=fill circles +Converter_CMYK_Crosshatch.Name=CMYK +Converter_CMYK_Crosshatch.Note= +Converter_CMYK_Crosshatch.Passes=Passes +Converter_CMYK_Spiral.Name=CMYK Spiral +Converter_EdgeDetection.border=Draw border +Converter_EdgeDetection.name=Edge detection +Converter_EdgeDetection.passes=Passes +Converter_EdgeDetection.sampleSize=SampleSize +Converter_EdgeDetection.stepSize=Step size +Converter_IntensityToHeight.name=Intensity to height +Converter_IntensityToHeight.sampleRate=Sample rate +Converter_IntensityToHeight.spacing=Wave spacing +Converter_IntensityToHeight.waveIntensity=Wave intensity +Converter_PulseCMYK.Name=Pulse CMYK +Converter_PulseCMYK.SampleRate=Sample rate +Converter_QuadTreeInstant.baseCutOff=Base cut-off +Converter_QuadTreeInstant.cutOffIncrement=Cut-off increment +Converter_QuadTreeInstant.maxDepth=Max depth +Converter_QuadTreeInstant=Quad tree instant +Converter_TruchetFromImage.linesPerTile=Lines per tile +Converter_TruchetFromImage.name=Truchet from image +Converter_TruchetFromImage.spacing=Space between lines +Converter_Voronoi.Name=Voronoi diagram +Converter_Voronoi.ShowCenters=Show centers +Converter_VoronoiStippling.CellCount=Number of cells +Converter_VoronoiStippling.Cutoff=Cutoff +Converter_VoronoiStippling.DotMax=Max dot size +Converter_VoronoiStippling.DotMin=Min dot size +Converter_VoronoiStippling.DrawBorders=Draw borders? +Converter_VoronoiStippling.Name=Voronoi stipples +Crosshatch=Crosshatch +DefaultTurtleRenderer.name=Default +DialogAbout.AboutHTML=

Makelangelo %VERSION% %DETAILED_VERSION%

http://www.makelangelo.com/

To get the latest version please visit
https://www.marginallyclever.com/product/makelangelo-software/

Created by Dan Royer.

Additional contributions by
Joseph Cottam, Peter Colapietro, Miguel Sánchez
Mark Raynsford, and
Kington Chu.

]]> +Direction=Direction +DirectionLoopTurtleRenderer.name=Direction loops +DragonName=Dragon +ErrorTitle=Error +EstimatedTimeIs=Estimated time is %1 +Export=Export +FibonacciSpiralName=Fibonacci Spiral +FillPageName=Fill page +FirmwareUpdate=Update firmware +FirmwareUploader.help=help]]> +FirmwareUploaderPanel.avrdudeNotDownloaded=AVRDude not downloaded. +FirmwareUploaderPanel.downloadFailed=Download failed +FirmwareUploaderPanel.failed=Firmware upload failed. +FirmwareUploaderPanel.finished=Finished OK! +FirmwareUploaderPanel.noPortSelected=No port selected. +FirmwareUploaderPanel.notFound=%1 not found. +FirmwareUploaderPanel.startHuge=Huge +FirmwareUploaderPanel.startM5=M5 +FirmwareUploaderPanel.status=Firmware upload status +Flap=Flap +FlipH=Flip horizontal +FlipV=Flip vertical +FontFace=Typeface +GFXPreferences.Title=Graphics +GFXPreferences.antialias=Antialias lines +GFXPreferences.dragSpeed=camera travel speed +GFXPreferences.showAllWhileDrawing=Show entire image while drawing +GFXPreferences.showPenUp=Show travel moves +GFXPreferences.speedVSQuality=Speed over quality +Generator.randomSeed=Random seed value +Generator_FlowField.fromEdge=Continuous +Generator_FlowField.name=Flow field +Generator_FlowField.noiseType=Noise type +Generator_FlowField.offsetX=X offset +Generator_FlowField.offsetY=Y offset +Generator_FlowField.rightAngle=Add orthogonal lines +Generator_FlowField.scaleX=X scale +Generator_FlowField.scaleY=Y scale +Generator_FlowField.stepLength=Step length +Generator_FlowField.stepSize=Step size +Generator_FlowField.stepVariation=Step variation +Generator_GridFit.Name=Rectangles +Generator_GridFit.cellsHigh=Cells vertical +Generator_GridFit.cellsWide=Cells horizontal +Generator_GridFit.margin=Minimum margin each side (mm) +Generator_GridHexagons.Name=Hexagons +Generator_GridHexagons.radius=Major radius +Generator_MazeCircle.name=Circle +Generator_MazeCircle.rings=Rings +Generator_MazeHoneycomb.name=Honeycomb +Generator_MazeRectangle.columns=Columns +Generator_MazeRectangle.name=Rectangle +Generator_MazeRectangle.rows=Rows +Generator_Spiral.name=Spiral +Generator_Spiral.radius=Radius +Generator_TruchetTiles.LineSpacing=Space between lines (mm) +Generator_TruchetTiles.LinesPerTile=Lines per tile +Generator_TruchetTiles.Name=Truchet Tiles +Generator_TruchetTiles.allow=Allow '%1' +GosperCurveName=Gosper curve +GraphPaperName=Graph Paper +Height=Height +Help=Help +HilbertCurveName=Hilbert Curve +HilbertCurveOrder=Order +HilbertCurveSize=Size +Import=Import +InfillTurtleAction.title=Fill in closed loops +InfoTitle=Information +JogInterface.DisengageMotors=Disengage +JogInterface.EngageMotors=Engage +JogInterface.FindHome=Home +JogInterface.PenDown=Pen down +JogInterface.PenUp=Pen up +KochTreeName=Koch Curve +LSystemAngle=Angle span +LSystemBranches=Branches +LSystemNoise=Noise +LSystemOrderScale=Scale +LSystemTreeName=L System Tree +LanguagePreferences.Title=Language +Length=Length +LineWeightByImageIntensity.image=Image +LineWeightByImageIntensity.name=Line image weight +LineWeightByImageIntensity.thickness=Thickness +LissajousA=A (>0) +LissajousB=B (>0) +LissajousDelta=Delta (0...1) +LissajousName=Lissajous +LoadError=File could not be loaded: +LoadFilePanel.title=Load file preview +LoadScratch3.foreverNotAllowed=Forever loops are forever forbidden. +LogPanel.CopyClipboard=Copy +LogPanel.LogFiles=Log files +LogPanel.Title=Log +MarlinSimulationVisualizer.name=Marlin simulation +MenuAbout=About +MenuCaptureImage=Capture From Camera +MenuForums=Online help forums +MenuGenerate.Fractals=Fractals +MenuGenerate.Grids=Grids +MenuGenerate.Mazes=Mazes +MenuGenerate.SpaceFillers=Space Fillers +MenuGenerate=Generate art +MenuImportFile=Import File... +MenuItemPayPalDonation=PayPal donation +MenuItemTranslate=Help Translate +MenuMakelangelo=File +MenuManual=Read the Friendly Manual +MenuNewFile=New +MenuOpenFile=Open File... +MenuQuit=Quit +MenuReopenFile=Reopen last file +MenuResetMachinePreferencesWarning=Are you sure you want to erase all your preferences? +MenuSaveFile=Save File... +MenuSettings=Settings +MenuUpdate=Check for updates +MenuView.zoomFit=Zoom to fit +MenuView.zoomIn=Zoom + +MenuView.zoomOut=Zoom - +MenuView=View +MenuWindows=Window +MetricsPreferences.Title=Metrics +MetricsPreferences.collectAnonymousMetrics=I consent to sharing anonymous usage data
with Marginally Clever Robots, Limited.]]> +MetricsPreferences.collectAnonymousMetricsOnUpdate=May Marginally Clever Robots, Ltd. collect anonymous usage metrics about this app?

You can update this any time in Makelangelo > Preferences > Metrics]]> +Model=Model +MoireName=Moire +OK=OK +Open=Open file... +OpenFileChooser.AllSupportedFiles=All supported files +OpenFileChooser.FileTypeImage=Image (%1) +OpenLogFolder=Open log folder +OpenPaperSettings=Paper settings +OpenPlotterSettings=Plotter settings +Package=Papercraft Box +PaperSettings.Landscape=Landscape +PaperSettings.PaperColor=Paper color +PaperSettings.PaperHeight=Paper height (mm) +PaperSettings.PaperMargin=Margin (%) +PaperSettings.PaperSize=Paper Size +PaperSettings.PaperWidth=Paper width (mm) +PaperSettings.Rotation=Rotation +PaperSettings.ShiftX=Shift left +PaperSettings.ShiftY=Shift up +PaperSettings.Title=Paper settings +PiCaptureAction.AWB=Automatic White Balance +PiCaptureAction.Antishake=Antishake +PiCaptureAction.Auto=Auto +PiCaptureAction.Backlight=Backlight +PiCaptureAction.Beach=Beach +PiCaptureAction.CancelCapture=Cancel +PiCaptureAction.CaptureImage=Capture +PiCaptureAction.CaptureImageTitle=Capture Camera Image +PiCaptureAction.Cloud=Cloud +PiCaptureAction.Contrast=Contrast +PiCaptureAction.DRC=Dynamic Range +PiCaptureAction.Exposure=Exposure +PiCaptureAction.Fireworks=Fireworks +PiCaptureAction.FixedFPS=FixedFPS +PiCaptureAction.Flash=Flash +PiCaptureAction.Fluorescent=Fluorescent +PiCaptureAction.High=High +PiCaptureAction.Horizon=Horizon +PiCaptureAction.Incandescent=Incandescent +PiCaptureAction.Low=Low +PiCaptureAction.Medium=Medium +PiCaptureAction.Night=Night +PiCaptureAction.NightPreview=NightPreview +PiCaptureAction.Off=Off +PiCaptureAction.Quality=Quality +PiCaptureAction.Shade=Shade +PiCaptureAction.Sharpness=Sharpness +PiCaptureAction.Snow=Snow +PiCaptureAction.Sports=Sports +PiCaptureAction.Spotlight=Spotlight +PiCaptureAction.Sun=Sun +PiCaptureAction.Tungsten=Tungsten +PiCaptureAction.UseCapture=Use +PiCaptureAction.Verylong=Verylong +PlotterControls.AdvancedControls=Advanced controls +PlotterControls.ConnectControls=Connect +PlotterControls.DrawControls=Draw controls +PlotterControls.EmergencyStop=Emergency Stop +PlotterControls.FindHome=Find home +PlotterControls.JogTab=Robot controls +PlotterControls.MarlinTab=Marlin +PlotterControls.Pause=Pause +PlotterControls.Play=Draw +PlotterControls.ProgramTab=Program +PlotterControls.Rewind=Rewind +PlotterControls.Step=Step +PlotterControls.Title=Robot controls +PlotterControls.communicationFailure=No answer from the robot, please check the USB cable or speed settings. +PlotterControls.didNotFind=Error in communicating with the robot. +PlotterControls.halted=Printer halted! +PlotterControls.homeXYFirst=Home the printer first. +PlotterSettings.EndGcode=End GCode +PlotterSettings.FindHomeGcode=Find Home GCode +PlotterSettings.StartGcode=Start GCode +PlotterSettings.blockBufferSize=Block Buffer Size +PlotterSettings.handleSmallSegments=Handle Small Segments +PlotterSettings.minAcceleration=Min Acceleration (mm/s²) +PlotterSettings.minSegTime=Min Seg Time (ns) +PlotterSettings.minSegmentLength=Min Segment Length (mm) +PlotterSettings.minimumPlannerSpeed=Min Planner Speed (mm/s) +PlotterSettings.segmentsPerSecond=Segments Per Second +PlotterSettings.zMotorType.servo=Servo +PlotterSettings.zMotorType.stepper=Stepper +PlotterSettings.zMotorType=Z axis type +PlotterSettingsManagerPanel.AddProfile=+ +PlotterSettingsManagerPanel.NewProfileName=New unique name: +PlotterSettingsManagerPanel.NewProfileNameAlreadyExists=Name already in use. Try again! +PlotterSettingsManagerPanel.NewProfileNameCannotBeBlank=Name cannot be blank. Try again! +PlotterSettingsManagerPanel.RemoveProfile=- +PlotterSettingsPanel.AdjustAcceleration=Acceleration (mm/s²) +PlotterSettingsPanel.BeltLengthNeeded=Timing belt length (m,each) +PlotterSettingsPanel.MachineHeight=Machine height (mm) +PlotterSettingsPanel.MachineWidth=Machine width (mm) +PlotterSettingsPanel.ServoLengthNeeded=Servo wire length (m) +PlotterSettingsPanel.Speed=Draw speed (mm/min) +PlotterSettingsPanel.StepperLengthNeeded=Stepper wire length (m,each) +PlotterSettingsPanel.TabEssential=Essential +PlotterSettingsPanel.TabGCode=Custom GCode +PlotterSettingsPanel.TabPen=Pen +PlotterSettingsPanel.TabSimulation=Simulation +PlotterSettingsPanel.Title=Plotter settings +PlotterSettingsPanel.penDownColor=Default down color +PlotterSettingsPanel.penUpColor=Up color +PlotterSettingsPanel.penToolDiameter=Diameter (mm) +PlotterSettingsPanel.penToolDown=Down angle (degrees) +PlotterSettingsPanel.penToolLiftSpeed=Pen lift time (ms) +PlotterSettingsPanel.penToolLowerSpeed=Pen lower time (ms) +PlotterSettingsPanel.penToolMaxFeedRate=Travel speed (mm/min) +PlotterSettingsPanel.penToolUp=Up angle (degrees) +Polyeder=Papercraft Polyhedron +Port=Port +PulseLineName=Pulse Line +QuestionTitle=Question +Reorder=Reorder +ReportBug=Report an issue +Reset=Reset +Robot=Makelangelo +RobotMenu.GetTimeEstimate=Estimate time +RobotMenu.OpenControls=Open Controls +RobotMenu.RenderStyle=Render style +RobotMenu.RobotStyle=Robot style +RobotMenu.SaveGCode=Save Gcode to file/SD +Rotate=Rotate +SandyNoble.center=Center +SandyNoble.rings=Rings +SandyNoble.title=Sandy Noble Style +Save=Save +SaveError=File could not be saved: +SaveGCode.splitGCodeQuestion=There are %1 tool changes in this drawing. Split into separate files? +SaveGCode.splitGCodeTitle=Many colors detected +Scale=Scale +SelectImageConverterPanel.Contrast=Contrast adjust +SeparateLoopTurtleRenderer.name=Separate loops +SharePromo=Try social media tag #plotter +ShowLog=Show Log +SierpinskiTriangleName=Sierpinski triangle +Simplify=Simplify +Size=Size +SoundPreferences.Connect=Connect +SoundPreferences.Disconnect=Disconnect +SoundPreferences.FinishConvert=Finish converting +SoundPreferences.FinishDraw=Finish drawing +SoundPreferences.Title=Sounds +Spiral.toCorners=Spiral to corners +SpiralName=Spiral +SpiralPulse.height=Pulse height +SpiralPulse.intensity=Intensity +SpiralPulse.spacing=Space between rings +SpiralPulseName=Pulsing Spiral +SpirographEpitrochoid=is epitrochoid +SpirographMajorRadius=Major radius (R) +SpirographMinorRadius=Minor radius (r) +SpirographName=Spirograph +SpirographNumSamples=Samples (quality) +SpirographPScale=Scale (p) +StartAt=Start at +StartAtAddPenDown=Add pen down command here +StartAtExactly=Do nothing special +StartAtLastPenDown=Back up to last pen down +TextMessage=Message +TextSize=Size +TitlePrefix=Makelangelo +Translate=Translate +TurtleGenerators.LearnMore.Link.Text=Learn more +UpToDate=This software is up to date. +UpdateCheckFailed=Sorry, I failed. Please visit http://www.marginallyclever.com/ to check yourself. +UpdateNotice=A new version of this software is available.
Please visit
https://www.marginallyclever.com/product/makelangelo-software/ to get the new hotness.]]> +VoronoiZigZag.optimizePath=Optimize path +VoronoiZigZagName=Voronoi zigzag +Width=Width +YourMsgHereName=Your message here +bottomLeft=Bottom left +bottomRight=Bottom right +center=Center +firmwareVersionBadMessage=The firmware (code in the brain of your robot) is not the version I expect. I found %1%.
Please visit https://www.marginallyclever.com/product/makelangelo-firmware/
for the latest firmware.
Please visit https://www.marginallyclever.com/product/makelangelo-software/ for the latest software.]]> +firmwareVersionBadTitle=Firmware update +horizontal=Horizontal +pass15=Pass 15 +pass45=Pass 45 +pass75=Pass 75 +pass90=Pass 90 +penDiameter=Pen diameter +topLeft=Top left +topRight=Top right +vertical=Vertical \ No newline at end of file diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties new file mode 100644 index 000000000..558943ff6 --- /dev/null +++ b/src/main/resources/messages_fr.properties @@ -0,0 +1,251 @@ +# https://github.com/PPAC37 +ApplicationSettings.title=Préférences +ArtPipeline=Outils +BorderName=Contour marges +BoxGeneratorCutoff=Couper +BoxGeneratorMaxSize=taille max +BoxGeneratorName=Boxxy +Cancel=Annuler +CartesianButtons.buttonCenter= +ClickAndDrag=Cliquez et faites glisser +ConfirmQuitQuestion=Êtes-vous sûr de vouloir quitter? +ConfirmQuitTitle=Sortir +ConversationHistory.Clear=Effacer +ConversationHistory.Save=Sauvegarder +ConversionFill=Remplir +ConversionStyle=Style de conversion +ConvertImagePaperFill=Remplir tout le papier +ConvertImagePaperFit=Ajuster l'image sur le papier +ConverterMultipassAngle=Angle (degrés) +ConverterMultipassLevels=Niveaux +ConverterMultipassName=Multipasse +ConverterRandomLinesCount=Nombre de lignes +ConverterRandomLinesName=Lignes aléatoires +ConverterWanderCMYK=CMYK? +ConverterWanderLineCount=# Lignes +ConverterWanderName=Errer +Converter_VoronoiStippling.CellCount=Nombre de cellules +Converter_VoronoiStippling.Cutoff=Coupure passe-bas +Converter_VoronoiStippling.DotMax=Taille maximale des points +Converter_VoronoiStippling.DotMin=Taille de point minimale +Converter_VoronoiStippling.DrawBorders=Dessiner des frontières ? +Converter_VoronoiStippling.Name=Pointillés de Voronoï +Crosshatch=Hachure +DialogAbout.AboutHTML= <html> <body> <h1>Makelangelo %VERSION% <span style="font-size: small; font-weight: normal">%DETAILED_VERSION%</span> </h1> <h3><a href='http://www.makelangelo.com/'>http://www.makelangelo.com/</a > </h3> <p>Pour obtenir la dernière version, veuillez visiter<br> <a href='https://www.marginallyclever.com/product/makelangelo-software/'>https://www.marginallyclever.com /product/makelangelo-software/</a></p> <p>Créé par <a href="mailto:dan@marginallyclever.com">Dan Royer</a>.</p> <p>Logiciel supplémentaire et les contributions matérielles de<br> Joseph Cottam, <a href='https://github.com/virtuoushub'>Peter Colapietro<a/>, Miguel Sánchez<br> Mark Raynsford et <a href='https://ca.linkedin.com/in/kington-chu-219923123'>Kington Chu</a>.</p> </body> </html> +Direction=Direction +DragonName=Courbe du dragon +ErrorTitle=Erreur +EstimatedTimeIs=Le temps estimé est %1 +Export=Exportation +FibonacciSpiralName=Spirale de Fibonacci +FillPageName=Remplir la page +FirmwareUpdate=Mise à jour du firmware +Flap=Rabat +FlipH=Retourner horizontalement +FlipV=Retourner verticalement +FontFace=Police de caractères +GFXPreferences.Title=Graphique +GFXPreferences.antialias=Lignes d'anticrénelage +GFXPreferences.dragSpeed=Vitesse de déplacement de la souris +GFXPreferences.showAllWhileDrawing=Afficher l'image entière pendant le dessin +GFXPreferences.showPenUp=Afficher les déplacements +GFXPreferences.speedVSQuality=La rapidité plutôt que la qualité +GosperCurveName=Courbe de Gosper +GraphPaperName=Papier millimétré +Height=Hauteur +Help=Aide +HilbertCurveName=Courbe de Hilbert +HilbertCurveOrder=Ordre +HilbertCurveSize=Taille +Import=Importer +InfillTurtleAction.title=Remplir les boucles fermées +InfoTitle=Information +JogInterface.DisengageMotors=Dé-engager les moteurs +JogInterface.EngageMotors=Engager les moteurs +JogInterface.FindHome=Trouver l'origine ("Home") +JogInterface.PenDown=Stylo vers le bas +JogInterface.PenUp=Stylo vers le haut +KochTreeName=Courbe de Koch +LSystemAngle=Portée angulaire +LSystemBranches=Branches +LSystemNoise=Bruit +LSystemOrderScale=Échelle +LSystemTreeName=Arbre L-Système +LanguagePreferences.Title=Langue +Length=Longueur +LissajousA=A (>0) +LissajousB=B (>0) +LissajousDelta=Delta (0...1) +LissajousName=Courbe de Lissajous +LoadError=Le fichier n'a pas pu être chargé : +LoadFilePanel.title=Charger l'aperçu du fichier +LogPanel.CopyClipboard=Copier dans le presse-papier +LogPanel.LogFiles=Fichiers de logs +LogPanel.Title=Log +MenuAbout=À propos de Makelangelo +MenuCaptureImage=Capturer depuis l'appareil photo +MenuForums=Forums d'aide en ligne +MenuGenerate=Générer de l'art +MenuItemPayPalDonation=Faire un don PayPal +MenuMakelangelo=Fichier +MenuNewFile=Nouveau +MenuOpenFile=Ouvrir... +MenuQuit=Quitter +MenuReopenFile=Récents +MenuResetMachinePreferencesWarning=Êtes-vous sûr de vouloir effacer toutes vos préférences ? +MenuSaveFile=Enregistrer... +MenuSettings=Paramètres +MenuUpdate=Vérifier les mises à jour +MenuView.zoomFit=Zoomer pour s'adapter +MenuView.zoomIn=Agrandir + +MenuView.zoomOut=Zoom - +MenuView=Voir +MetricsPreferences.Title=Métrique +MetricsPreferences.collectAnonymousMetrics=<html><body>J'accepte de partager des données d'utilisation anonymes<br>avec Marginally Clever Robots, Limited.</html></body> +MetricsPreferences.collectAnonymousMetricsOnUpdate=<html><body>Est-ce que Marginally Clever Robots, Ltd. peut collecter des statistiques d'utilisation anonymes sur cette application ?<br><br>Vous pouvez mettre à jour cela à tout moment dans <i>Makelangelo &gt; Préférences &gt; Métriques</i></html></body> +Model=Modèle +MoireName=Moiré +OK=d'accord +Open=Fichier ouvert... +OpenFileChooser.AllSupportedFiles=Tous les fichiers pris en charge +OpenFileChooser.FileTypeImage=Image (%1) +OpenPaperSettings=Paramètres papier +OpenPlotterSettings=Paramètres du traceur +Package=Boîte en papier +PaperSettings.Landscape=Orientation paysage +PaperSettings.PaperColor=Couleur +PaperSettings.PaperHeight=Hauteur +PaperSettings.PaperMargin=Marge (%) +PaperSettings.PaperSize=Dimensions +PaperSettings.PaperWidth=Largeur +PaperSettings.Rotation=Rotation +PaperSettings.ShiftX=Décalage à gauche ( Axe X ) +PaperSettings.ShiftY=Décalage vers le haut ( Axe Y ) +PaperSettings.Title=Paramètres papier +PiCaptureAction.AWB=Balance des blancs automatique +PiCaptureAction.Antishake=Anti-vibrations +PiCaptureAction.Auto=Auto +PiCaptureAction.Backlight=Rétroéclairage +PiCaptureAction.Beach=plage +PiCaptureAction.CancelCapture=Annuler +PiCaptureAction.CaptureImage=Capturer +PiCaptureAction.CaptureImageTitle=Capturer l'image de la caméra +PiCaptureAction.Cloud=Nuage +PiCaptureAction.Contrast=Contraste +PiCaptureAction.DRC=Plage dynamique +PiCaptureAction.Exposure=Exposition +PiCaptureAction.Fireworks=Les feux d'artifices +PiCaptureAction.FixedFPS=FixedFPS +PiCaptureAction.Flash=Éclat +PiCaptureAction.Fluorescent=Fluorescent +PiCaptureAction.High=Haut +PiCaptureAction.Horizon=Horizon +PiCaptureAction.Incandescent=Incandescent +PiCaptureAction.Low=Meugler +PiCaptureAction.Medium=Moyen +PiCaptureAction.Night=Nuit +PiCaptureAction.NightPreview=NuitAperçu +PiCaptureAction.Off=Désactivé +PiCaptureAction.Quality=Qualité +PiCaptureAction.Shade=Ombre +PiCaptureAction.Sharpness=Acuité +PiCaptureAction.Snow=Neige +PiCaptureAction.Sports=Des sports +PiCaptureAction.Spotlight=Projecteur +PiCaptureAction.Sun=soleil +PiCaptureAction.Tungsten=Tungstène +PiCaptureAction.UseCapture=Utiliser +PiCaptureAction.Verylong=Très long +PlotterControls.AdvancedControls=Contrôles avancés +PlotterControls.ConnectControls=Connecter +PlotterControls.DrawControls=Contrôles de dessin +PlotterControls.EmergencyStop=Arrêt d'urgence +PlotterControls.FindHome=Trouver l'origine ("Home") +PlotterControls.JogTab=Commandes de robots +PlotterControls.MarlinTab=Marlin +PlotterControls.Pause=Pause +PlotterControls.Play=Dessiner +PlotterControls.ProgramTab=Programme +PlotterControls.Rewind=Rembobiner +PlotterControls.Step=Étape +PlotterControls.Title=Commandes de robots +PlotterControls.homeXYFirst=Faire une "prise d'origine" (Home) de l'imprimante, avant. +PlotterSettings.EndGcode=GCode fin +PlotterSettings.StartGcode=GCode début +PlotterSettings.blockBufferSize=Taille du tampon de bloc +PlotterSettings.handleSmallSegments=Gérer les petits segments +PlotterSettings.minAcceleration=Accélération minimale +PlotterSettings.minSegTime=Temps de segment minimum +PlotterSettings.minSegmentLength=Longueur minimale du segment +PlotterSettings.minimumPlannerSpeed=Vitesse minimale du planificateur +PlotterSettings.segmentsPerSecond=Segments par seconde +PlotterSettingsPanel.TabGCode=Paramètres du traceur - GCode début/fin +PlotterSettingsPanel.Title=Paramètres du traceur +Polyeder=Paper craft 3D Polyèdre +Port=Port +PulseLineName=Ligne d'impulsion +QuestionTitle=Question +Reorder=Réorganiser +Reset=Réinitialiser +Robot=Makelangelo +RobotMenu.GetTimeEstimate=Temps estimé +RobotMenu.OpenControls=Ouvrir les contrôles +RobotMenu.RenderStyle=Style de rendu +RobotMenu.RobotStyle=Style robotique +RobotMenu.SaveGCode=Enregistrer le Gcode dans un fichier/SD +SandyNoble.center=Centre +SandyNoble.rings=Anneaux +SandyNoble.title=Style "Sandy Noble" +Save=Enregistrer +SaveError=Impossible d'enregistrer le fichier : +Scale=Échelle +SharePromo=Essayez la balise de médias sociaux #plotter +ShowLog=Afficher le journal +SierpinskiTriangleName=Triangle de Sierpi?ski +Simplify=Simplifier +Size=Taille +SoundPreferences.Connect=Connexion +SoundPreferences.Disconnect=Déconnexion +SoundPreferences.FinishConvert=Fin de conversion +SoundPreferences.FinishDraw=Fin du dessin +SoundPreferences.Title=Sons +Spiral.toCorners=Spirale aux coins +SpiralName=Spirale +SpiralPulse.height=Hauteur d'impulsion +SpiralPulse.intensity=Intensité +SpiralPulse.spacing=Espace entre les anneaux +SpiralPulseName=Spirale pulsée +SpirographEpitrochoid=est épitrochoïde +SpirographMajorRadius=Grand rayon (R) +SpirographMinorRadius=Petit rayon (r) +SpirographName=Spirographe +SpirographNumSamples=Échantillons (qualité) +SpirographPScale=Échelle (p) +StartAt=Commencer à +StartAtAddPenDown=Ajouter la commande "pen down" ici +StartAtExactly=Ne rien faire de spécial +StartAtLastPenDown=Remonter jusqu'au dernier "pen down" +TextMessage=Message +TextSize=Taille +TitlePrefix=Makelangelo +TurtleGenerators.LearnMore.Link.Text=En savoir plus +UpToDate=Ce logiciel est à jour. +UpdateCheckFailed=Désolé, j'ai échoué. Veuillez visiter http://www.marginallyclever.com/ pour vérifier par vous-même. +UpdateNotice=<html><body>Une nouvelle version de ce logiciel est disponible.<br/>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-software/">https://www. marginallyclever.com/product/makelangelo-software/</a> pour de bonnes surprises.</html></body> +VoronoiZigZagName=zigzag de Voronoï +Width=Largeur +YourMsgHereName=Votre message ici +bottomLeft=En bas à gauche +bottomRight=En bas à droite +center=Centre +firmwareVersionBadMessage=<html><body>Le firmware (code dans le cerveau de votre robot) n'est pas la version que j'attends. J'ai trouvé <strong>%1%</strong>.<br/>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-firmware/">https://www.marginallyclever. com/product/makelangelo-firmware/</a><br> pour le dernier firmware.<br>Veuillez visiter <a href="https://www.marginallyclever.com/product/makelangelo-software/">https : //www.marginallyclever.com/product/makelangelo-software/</a> pour la dernière version du logiciel.</html></body> +firmwareVersionBadTitle=Mise à jour du firmware +horizontal=Horizontal +pass15=Passe 15 +pass45=Passe 45 +pass75=Passe 75 +pass90=Passe 90 +penDiameter=Diamètre du stylo +topLeft=En haut à gauche +topRight=En haut à droite +vertical=Verticale \ No newline at end of file diff --git a/src/main/resources/messages_nl.properties b/src/main/resources/messages_nl.properties new file mode 100644 index 000000000..621e990e7 --- /dev/null +++ b/src/main/resources/messages_nl.properties @@ -0,0 +1,64 @@ +# Bart Derks (bderks@zeelandnet.nl) +OpenFileChooser.AllSupportedFiles=All supported files +ApplicationSettings.title=Instellingen +BoxGeneratorName=Boxxy +Cancel=Annuleren +ClickAndDrag=Klik & Sleep +ConversionStyle=Conversie stijl +Converter_VoronoiStippling.CellCount=Aantal Cellen +Converter_VoronoiStippling.DotMax=Max stip grootte +Converter_VoronoiStippling.DotMin=Mininmum stip grootte +Converter_VoronoiStippling.Name=Voronoi stippels +DragonName=Dragon Fractal +GFXPreferences.Title=Graphics +GFXPreferences.antialias=Anti-alias lijnen +GFXPreferences.showAllWhileDrawing=Toon volledige afbeelding tijdens het tekenen +GFXPreferences.showPenUp=Laat pen-up bewegingen zien +GFXPreferences.speedVSQuality=Snelheid boven kwaliteit +HilbertCurveName=Hilbert Curve +HilbertCurveOrder=Order +HilbertCurveSize=Grootte +JogInterface.DisengageMotors=Motoren uitzetten +JogInterface.PenDown=Pen omlaag +JogInterface.PenUp=Pen omhoog +KochTreeName=Koch Curve Fractal +LSystemTreeName=L Systeem Tree Fractal +LanguagePreferences.Title=Talen +MenuAbout=Over +MenuGenerate=Genereer kunst +MenuMakelangelo=Makelangelo +MenuNewFile=Nieuw +MenuOpenFile=Open bestand... +MenuQuit=Stoppen +MenuResetMachinePreferencesWarning=Weet je zeker dat je alle instellingen wil resetten? +MenuSettings=Instellingen +MenuUpdate=Controleer op updates +MenuView.zoomFit=Complete afbeelding weergeven +MenuView.zoomIn=Zoom + +MenuView.zoomOut=Zoom - +MenuView=Bekijk +OpenFileChooser.FileTypeImage=Afbeelding (%1) +PaperSettings.PaperHeight=Papier hoogte +PaperSettings.PaperWidth=Papier breedte +PlotterControls.Pause=Pause +PlotterControls.Play=Draw +PlotterControls.Rewind=Rewind +PulseLineName=Pulse Lijn +Robot=Makelangelo +RobotMenu.OpenControls=Open Controls +RobotMenu.SaveGCode=Bewaar Gcode in bestand/SD +Save=Opslaan +SharePromo=Gebruik Twitter tag #makelangelo om je kunst te delen en te kijken wat anderen maakten. +ShowLog=Bekijk Log +SoundPreferences.Connect=Verbinden +SoundPreferences.Disconnect=Uitschakelen +SoundPreferences.FinishConvert=Voltooien Conversie +SoundPreferences.FinishDraw=Voltooien tekening +SoundPreferences.Title=Geluiden +SpiralName=Spiraal +StartAt=Start bij +TitlePrefix=Makelangelo +UpToDate=Deze software is up-to-date. +UpdateCheckFailed=Sorry, mislukt. Bezoek http://www.marginallyclever.com/ voor evt. update/informatie. +UpdateNotice=Een nieuwe versie van deze software is beschikbaar.
\nBezoek https://www.marginallyclever.com/product/makelangelo-software/ om de laatste versie te halen.]]> +YourMsgHereName=Uw bericht hier diff --git a/src/test/java/com/marginallyclever/experiments/ShowCellularNoise.java b/src/test/java/com/marginallyclever/experiments/ShowCellularNoise.java new file mode 100644 index 000000000..9009f9c29 --- /dev/null +++ b/src/test/java/com/marginallyclever/experiments/ShowCellularNoise.java @@ -0,0 +1,42 @@ +package com.marginallyclever.experiments; + +import com.marginallyclever.convenience.noise.CellularNoise; + +import javax.swing.*; +import java.awt.image.BufferedImage; +import java.util.Arrays; + +public class ShowCellularNoise { + public static void main(String[] args) { + // create a jframe, put a panel in it, and show a cellular noise pattern + CellularNoise noise = new CellularNoise(); + noise.setSeed(0); + + int width = 512; + int height = 512; + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int c = image.getColorModel().getNumComponents(); + int [] pixel = new int[c]; + var r = image.getRaster(); + + double scale = 0.1; + double px = 0; + double py = 0; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + var v2 = (noise.noise(px+x*scale, py+y*scale) + 1.0) / 2.0; + int v = (int)(255 * Math.max(0.0,Math.min(1.0, v2 ))); + Arrays.fill(pixel, v); + r.setPixel(x, y, pixel); + } + } + + JFrame frame = new JFrame("Cellular Noise"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLocationRelativeTo(null); + frame.setSize(width, height); + frame.add(new JLabel(new ImageIcon(image))); + frame.setVisible(true); + } +} diff --git a/src/test/java/com/marginallyclever/makelangelo/LanguagesXmlValidationForNoDupKeyTest.java b/src/test/java/com/marginallyclever/makelangelo/LanguagesXmlValidationForNoDupKeyTest.java deleted file mode 100644 index 8d0b7ca5d..000000000 --- a/src/test/java/com/marginallyclever/makelangelo/LanguagesXmlValidationForNoDupKeyTest.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.marginallyclever.makelangelo; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - -import javax.xml.XMLConstants; -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import javax.xml.validation.Validator; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import static com.marginallyclever.makelangelo.TranslationsMissingTest.listFiles; -import static org.junit.jupiter.api.Assertions.*; - -public class LanguagesXmlValidationForNoDupKeyTest { - - private static final Logger logger = LoggerFactory.getLogger(LanguagesXmlValidationForNoDupKeyTest.class); - - public final static String ressourceStringForXmlShemaFile = "/translator/language_no_dup_key.xsd"; - - @Test - public void validateLaguagesXmlFiles() throws IOException { - - List xmlFilesWithValidationDefect = new ArrayList<>(); - File srcDir = new File("src" + File.separator + "main" + File.separator + "resources" + File.separator + "languages"); - - // Pre requi -// //URL schemaFile = new File("src/test/resources/translator/language_no_dup_key.xsd").toURI().toURL(); - URL schemaFile = LanguagesXmlValidationForNoDupKeyTest.class.getResource(ressourceStringForXmlShemaFile); - - // Pre requi schema xsd file - assertNotNull(schemaFile, "The test need a redable schema xsd file (" + ressourceStringForXmlShemaFile + ") to validate the language files"); - - // Pre requi some language xml file to test - List files = listFiles(srcDir.toPath(), ".xml"); - - // assert some files - // ?TODO - // valide the files found - files.forEach(file -> { - boolean isValide = xmlValidationWithExternalXsdShema(file, schemaFile); - if (!isValide) { - xmlFilesWithValidationDefect.add(file.toString()); - } - }); - - StringBuilder sb = new StringBuilder(); - if (!xmlFilesWithValidationDefect.isEmpty()) { - logger.info("invalide with " + ressourceStringForXmlShemaFile + " :"); - for (String result : xmlFilesWithValidationDefect) { - sb.append(" "); - sb.append(result); - sb.append("\n"); - logger.info(" {}", result); - } - } - assertEquals(0, xmlFilesWithValidationDefect.size(), "Some language xml file do not validate with " + ressourceStringForXmlShemaFile + "\n" + sb.toString() + ", see previous logs for details"); - } - - @Test - public void verifyThatValidationWorks_notvalide() throws IOException { - - URL schemaFile = LanguagesXmlValidationForNoDupKeyTest.class.getResource(ressourceStringForXmlShemaFile); - assertNotNull(schemaFile, "The test need a redable schema xsd file (" + ressourceStringForXmlShemaFile + ") to validate the language files"); - - String myTestFileWithWithDupKeyInRessources = "/translator/english_with_dup_key.xml"; - URL resource = LanguagesXmlValidationForNoDupKeyTest.class.getResource(myTestFileWithWithDupKeyInRessources); - assertNotNull(resource, "The test need a redable xml file (" + myTestFileWithWithDupKeyInRessources + ") to test itself"); - - // english_with_dup_key.xml contains some duplicated an en empty key //language/string/key - boolean isValide = xmlValidationWithExternalXsdShema(new File(resource.getFile()), schemaFile); - - assertFalse(isValide); - } - - @Test - public void verifyThatValidationWorks_valide() throws IOException { - - URL schemaFile = LanguagesXmlValidationForNoDupKeyTest.class.getResource(ressourceStringForXmlShemaFile); - assertNotNull(schemaFile, "The test need a redable schema xsd file (" + ressourceStringForXmlShemaFile + ") to validate the language files"); - - String myTestFileWithNoDupKeyInRessources = "/translator/english_with_no_dup_key.xml"; - URL resource = LanguagesXmlValidationForNoDupKeyTest.class.getResource(myTestFileWithNoDupKeyInRessources); - assertNotNull(resource, "The test need a redable xml file (" + myTestFileWithNoDupKeyInRessources + ") to test itself"); - - // english_with_no_dup_key.xml do not containe duplicated or empty //language/string/key - boolean isValide = xmlValidationWithExternalXsdShema(new File(resource.getFile()), schemaFile); - - assertTrue(isValide); - } - - /** - * Validate an xml file with an external xsd Schema. src inspiration : - * https://stackoverflow.com/questions/15732/whats-the-best-way-to-validate-an-xml-file-against-an-xsd-file - * - * @param xmlFileToValidate the xml file to validate. - * @param schemaFile the xsd schema file to use to validate the xml file. - * @return true if the validation is a succes ( no warm, error or fatal - * error ) false otherwise and if other exception ( cant found file xml, or - * xsd, or xsd not valid) occure. - */ - public static boolean xmlValidationWithExternalXsdShema(File xmlFileToValidate, URL schemaFile) { - Source xmlStreamSource = new StreamSource(xmlFileToValidate); - SchemaFactory schemaFactory = SchemaFactory - .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - try { - Schema schema = schemaFactory.newSchema(schemaFile); - Validator validator = schema.newValidator(); - // a list to store the validation errors / warms used to find if finnaly a validated (if this list empty) xml file or not latter. - List errorHandlerException = new ArrayList<>(); - // to get full error list, we set a ErrorEandler - validator.setErrorHandler(new ErrorHandler() { - @Override - public void warning(SAXParseException e) throws SAXException { - logger.info(String.format("[ %7s ] %s:%-4d %s", "warning", e.getSystemId(), e.getLineNumber(), e.getMessage())); - errorHandlerException.add(e.toString()); - } - - @Override - public void error(SAXParseException e) throws SAXException { - logger.info(String.format("[ %7s ] %s:%-4d %s", "error", e.getSystemId(), e.getLineNumber(), e.getMessage())); - errorHandlerException.add(e.toString()); - } - - @Override - public void fatalError(SAXParseException e) throws SAXException { - logger.info(String.format("[ %7s ] %s:%-4d %s", "fatalError", e.getSystemId(), e.getLineNumber(), e.getMessage())); - errorHandlerException.add(e.toString()); - } - }); - // the validation. - validator.validate(xmlStreamSource); - return errorHandlerException.isEmpty(); - } catch (SAXException e) { - // avec le setErrorHandler on ne devrait plus passer ici. - logger.info(String.format("[ %7s ] %s\n%s", "invalid", xmlStreamSource.getSystemId(), e)); - } catch (IOException ex) { - logger.info(String.format("[ %7s ] %s\n%s", "error", xmlStreamSource.getSystemId(), ex)); - } catch (Exception ex) { - // le shema n'existe pas ? - logger.info(String.format("[ %7s ] %s\n%s", "error", xmlStreamSource.getSystemId(), ex)); - } - return false; - } - -} diff --git a/src/test/java/com/marginallyclever/makelangelo/TranslationsMissingTest.java b/src/test/java/com/marginallyclever/makelangelo/TranslationsMissingTest.java index 355748a1d..1e0114f98 100644 --- a/src/test/java/com/marginallyclever/makelangelo/TranslationsMissingTest.java +++ b/src/test/java/com/marginallyclever/makelangelo/TranslationsMissingTest.java @@ -6,17 +6,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.nio.file.Paths; +import java.util.*; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; @@ -60,7 +60,7 @@ public void findMissingTranslations() throws IOException { } }); - if (results.size() != 0) { + if (!results.isEmpty()) { logger.info("translations missing:"); for (String result: results) { logger.info(" {}", result); @@ -70,15 +70,20 @@ public void findMissingTranslations() throws IOException { } public void searchAllSourceFiles(Consumer consumer) throws IOException { - File srcDir = new File("src" + File.separator + "main" + File.separator + "java"); - List files = listFiles(srcDir.toPath(), ".java"); - files.forEach(file -> { - try { - searchInAFile(file, consumer); - } catch (IOException e) { - logger.warn("Can read file {}", file, e); - } - }); + Path srcDir = Paths.get("src", "main", "java"); + try (Stream paths = Files.walk(srcDir)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + //.filter(Files::isRegularFile) + //.filter(Files::isReadable) + .forEach(path -> { + try { + searchInAFile(path.toFile(), consumer); + } catch (IOException e) { + logger.warn("Can read file {}", path, e); + } + }); + } } @Test @@ -131,73 +136,36 @@ public void searchInAFile(File file, Consumer consumer) } } - - /** - * List all files and sub files in this path. Using - * Files.walk(path) (so this take care of recursive path - * exploration ) And applying filter ( RegularFile and ReadableFile ) and - * filtering FileName ... - * - * @param path where to look. - * @param fileNameEndsWithSuffix use ".java" to get only ... ( this is not a - * regexp so no '.' despecialization required ) can be set to - * "" to get all files. - * @return a list of files (may be empty if nothing is found) or null if - * something is wrong. - * @throws IOException - */ - public static List listFiles(Path path, String fileNameEndsWithSuffix) throws IOException { - List result; - try ( Stream walk = Files.walk(path)) { - result = walk - .filter(Files::isRegularFile) - .filter(Files::isReadable) - .map(Path::toFile) - .filter(f -> f.getName().endsWith(fileNameEndsWithSuffix)) - .collect(Collectors.toList()); - } - return result; - } - /** - * Compare all translation files in target/classes/languages with the english.xml file. Display a list of all missing translations. + * Compare all translations to the english version. + * Display a list of all missing translations. * Fail if any translations are missing. */ @Test - public void findMissingTranslationsInAllLanguages() { - // check every file in target/classes/languages. - File english = new File("target/classes/languages/english.xml"); + public void findMissingAndExtraTranslationsInAllLanguages() { + // load resource bundle for english + ResourceBundle english = ResourceBundle.getBundle("messages",Locale.forLanguageTag("en")); + // find all other languages in the same bundle. + String[] availableLocales = Translator.getLanguageList(); + // compare all other languages to english boolean perfect = true; - File folder = new File("target/classes/languages"); - File[] files = folder.listFiles(); - for(File file : files) { - if(file.isFile() && !file.equals(english)) { - // compare xml keys in file to english - perfect |= compareTranslations(english, file); + for(String locale : availableLocales) { + if(locale.equals("en")) continue; + ResourceBundle other = ResourceBundle.getBundle("messages",Locale.forLanguageTag(locale)); + Set missingKeys = new HashSet<>(english.keySet()); + missingKeys.removeAll(other.keySet()); + if (!missingKeys.isEmpty()) { + logger.info("translations missing in {}: {}", locale, missingKeys); } + Set extraKeys = new HashSet<>(other.keySet()); + extraKeys.removeAll(english.keySet()); + if (!extraKeys.isEmpty()) { + logger.info("translations not in english in {}: {}", locale, extraKeys); + } + perfect &= missingKeys.isEmpty(); } - assertTrue(perfect, "Some translations are missing, see previous logs for details"); - } - - private boolean compareTranslations(File english, File file) { - TranslatorLanguage englishSet = new TranslatorLanguage(); - TranslatorLanguage otherSet = new TranslatorLanguage(); - try { - englishSet.loadFromInputStream(new FileInputStream(english)); - otherSet.loadFromInputStream(new FileInputStream(file)); - } catch (Exception e) { - throw new RuntimeException(e); - } - - Set englishKeys = englishSet.getKeys(); - Set otherKeys = otherSet.getKeys(); - Set missingKeys = new HashSet<>(englishKeys); - missingKeys.removeAll(otherKeys); - if (!missingKeys.isEmpty()) { - logger.info("translations missing in {}: {}", file, missingKeys); - } - return missingKeys.isEmpty(); + assertTrue(perfect, "Some translations are missing, see previous logs for details"); } } diff --git a/src/test/java/com/marginallyclever/makelangelo/TranslationsUnusedTest.java b/src/test/java/com/marginallyclever/makelangelo/TranslationsUnusedTest.java deleted file mode 100644 index b8dccc5be..000000000 --- a/src/test/java/com/marginallyclever/makelangelo/TranslationsUnusedTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.marginallyclever.makelangelo; - -import org.apache.commons.io.FilenameUtils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; - -public class TranslationsUnusedTest { - private InputStream getInputStream(String filename) throws FileNotFoundException { - String nameInsideJar = Translator.WORKING_DIRECTORY+"/"+ FilenameUtils.getName(filename); - InputStream stream = Translator.class.getClassLoader().getResourceAsStream(nameInsideJar); - String actualFilename = "Jar:"+nameInsideJar; - File externalFile = new File(filename); - if(externalFile.exists()) { - stream = new FileInputStream(filename); - actualFilename = filename; - } - if( stream == null ) throw new FileNotFoundException(actualFilename); - return stream; - } - - @Test - public void findUnusedTranslations() throws Exception { - // check every file in target/classes/languages. - File folder = new File("target/classes/languages"); - File[] files = folder.listFiles(); - for(File file : files) { - if(file.isFile()) { - findUnusedTranslations(file.getAbsolutePath()); - } - } - } - - private void findUnusedTranslations(String filename) throws Exception { - System.out.println("testing "+filename); - TranslatorLanguage set = new TranslatorLanguage(); - set.loadFromInputStream(getInputStream(filename)); - Set keys = set.getKeys(); - Set found = new HashSet<>(); - - TranslationsMissingTest search = new TranslationsMissingTest(); - search.searchAllSourceFiles((e)->found.add(e.key)); - keys.removeAll(found); - Assertions.assertTrue(keys.isEmpty(),"Unused translations: "+keys); - } -} diff --git a/src/test/java/com/marginallyclever/makelangelo/TranslatorTest.java b/src/test/java/com/marginallyclever/makelangelo/TranslatorTest.java index e69b5df67..5ab6cc172 100644 --- a/src/test/java/com/marginallyclever/makelangelo/TranslatorTest.java +++ b/src/test/java/com/marginallyclever/makelangelo/TranslatorTest.java @@ -24,10 +24,11 @@ public void startTranslatorTwiceTest() { @Test public void loadLanguageTest() { String [] available = Translator.getLanguageList(); + assertTrue(available.length > 1, "More than one language needed to complete test."); int current = Translator.getCurrentLanguageIndex(); assertNotNull(available[current]); } - + @Test public void changeLanguageTest() { String[] available = Translator.getLanguageList(); diff --git a/src/test/java/com/marginallyclever/makelangelo/donatelloimpl/TestNodeGraphMakelangelo.java b/src/test/java/com/marginallyclever/makelangelo/donatelloimpl/TestNodeGraphMakelangelo.java new file mode 100644 index 000000000..0657fb5b3 --- /dev/null +++ b/src/test/java/com/marginallyclever/makelangelo/donatelloimpl/TestNodeGraphMakelangelo.java @@ -0,0 +1,84 @@ +package com.marginallyclever.makelangelo.donatelloimpl; + +import com.marginallyclever.makelangelo.donatelloimpl.nodes.TurtleDAO4JSON; +import com.marginallyclever.makelangelo.turtle.Turtle; +import com.marginallyclever.nodegraphcore.DAO4JSONFactory; +import com.marginallyclever.nodegraphcore.port.*; +import com.marginallyclever.nodegraphcore.Graph; +import com.marginallyclever.nodegraphcore.NodeFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test custom {@link com.marginallyclever.nodegraphcore.Node}s for Makelangelo. + * @author Dan Royer + * @since 2022-02-01 + */ +public class TestNodeGraphMakelangelo { + private static final Logger logger = LoggerFactory.getLogger(TestNodeGraphMakelangelo.class); + + private static Graph model = new Graph(); + + @BeforeAll + public static void beforeAll() throws Exception { + NodeFactory.loadRegistries(); + DAO4JSONFactory.loadRegistries(); + + assertNotEquals(0,NodeFactory.getNames().length); + logger.info("NodeFactory.getNames().length="+NodeFactory.getNames().length); + for(String s : NodeFactory.getNames()) { + logger.info(" found="+s); + } + } + + public static void afterAll() { + NodeFactory.clear(); + DAO4JSONFactory.clear(); + } + + @BeforeEach + public void beforeEach() { + model.clear(); + } + + private void testNodeVariableToJSONAndBack(Class myClass,T instA,T instB) throws Exception { + Input a = new Input<>(myClass.getSimpleName(),myClass,instA); + Input b = new Input<>(myClass.getSimpleName(),myClass,instB); + + b.fromJSON(a.toJSON()); + assertEquals(a.toString(),b.toString()); + assertEquals(a.getValue(),b.getValue()); + } + + @Test + public void testNodeVariablesToJSONAndBack() throws Exception { + Turtle t = new Turtle(); + //t.jumpTo(10,20); + //t.moveTo(30,40); + testNodeVariableToJSONAndBack(Turtle.class, t,new Turtle()); + } + + @Test + public void testFactoryCreatesAllSwingTypes() { + assertNotEquals(0,NodeFactory.getNames().length); + for(String s : NodeFactory.getNames()) { + assertNotNull(NodeFactory.createNode(s)); + } + } + + /** + * Test {@link Turtle}. + */ + @Test + public void testTurtleDAO() { + TurtleDAO4JSON dao = new TurtleDAO4JSON(); + Turtle r1 = new Turtle(); + Turtle r2=dao.fromJSON(dao.toJSON(r1)); + assertEquals(r1,r2); + } +} \ No newline at end of file diff --git a/src/test/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalkerTest.java b/src/test/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalkerTest.java new file mode 100644 index 000000000..532990268 --- /dev/null +++ b/src/test/java/com/marginallyclever/makelangelo/turtle/TurtlePathWalkerTest.java @@ -0,0 +1,27 @@ +package com.marginallyclever.makelangelo.turtle; + +import com.marginallyclever.makelangelo.makeart.io.TurtleFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TurtlePathWalkerTest { + /** + * Compare a TurtlePathWalker to Turtle.interpolate for a simple path. + */ + @Test + public void compareToInterpolate() throws Exception { + Turtle t0 = TurtleFactory.load("src/test/resources/com/marginallyclever/makelangelo/makeart/io/file_example_MP3_1MG.mp3"); + TurtlePathWalker walker = new TurtlePathWalker(t0); + double dist = t0.getDrawDistance(); + double pace = 1.0; + int maxIter = 5000; + for( double i=pace; i0; i+=pace ) { + maxIter--; + var p1 = t0.interpolate(i); + var p2 = walker.walk(pace); + System.out.println("i="+i+" d="+walker.getTSum()+" p1="+p1.x+","+p1.y+" p2="+p2.x+","+p2.y); + Assertions.assertEquals(p1.x,p2.x, 1e-1,"i="+i); + Assertions.assertEquals(p1.y,p2.y, 1e-1,"i="+i); + } + } +}