From 01eed576153dd7d3a1952bf11fbf26b38c952106 Mon Sep 17 00:00:00 2001
From: Max Gaukler <development@maxgaukler.de>
Date: Thu, 27 Dec 2012 02:10:30 +0100
Subject: [PATCH] dummy driver: add SVG output

---
 .../t_oster/liblasercut/drivers/Dummy.java    | 206 +++++++++++++++++-
 .../resources/visicut-svg-output-viewer.xhtml |  61 ++++++
 2 files changed, 265 insertions(+), 2 deletions(-)
 create mode 100644 src/com/t_oster/liblasercut/drivers/resources/visicut-svg-output-viewer.xhtml

diff --git a/src/com/t_oster/liblasercut/drivers/Dummy.java b/src/com/t_oster/liblasercut/drivers/Dummy.java
index 8c27a0d..e0a3c0d 100644
--- a/src/com/t_oster/liblasercut/drivers/Dummy.java
+++ b/src/com/t_oster/liblasercut/drivers/Dummy.java
@@ -21,6 +21,12 @@ package com.t_oster.liblasercut.drivers;
 
 import com.t_oster.liblasercut.*;
 import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.*;
 
 /**
@@ -33,26 +39,204 @@ public class Dummy extends LaserCutter {
   private static final String SETTING_BEDWIDTH = "Laserbed width";
   private static final String SETTING_BEDHEIGHT = "Laserbed height";
   private static final String SETTING_RUNTIME = "Fake estimated run-time in seconds (-1 to disable)";
+  private static final String SETTING_SVG_OUTDIR = "SVG Debug output directory (set empty to disable)";
+  
+  /**
+   * SVG output creator, mostly for testing vector-sorting
+   */
+  class SVGWriter  {
+    private double xPrev,xNow,yPrev,yNow;
+    private StringBuilder svg = new StringBuilder();
+    private boolean vectorPathActive=false;
+    private boolean partActive=false;
+    private int idCounter=0;
+    private int partCounter=0;
+    private LaserCutter cutter;
+    private double dpi;
+
+
+    public SVGWriter(LaserCutter cutter) {
+      this.cutter = cutter;
+    }
+
+    /**
+     * start a new JobPart
+     * @param title some string that will be included in the group ID
+     * @param dpi 
+     */
+    public void startPart(String title, double dpi) {
+      endPart();
+      partCounter += 1;
+      this.dpi=dpi;
+      this.partActive=true;
+      svg.append("<g style=\"fill:none;stroke:#000000;stroke-width:0.1mm;\" id=\"");
+      svg.append("visicut-part").append(partCounter).append("-");
+      svg.append(title.replaceAll("[^a-zA-Z0-9]","_"));
+      svg.append("\">\n");
+    }
+    
+    /**
+     * end a JobPart
+     */
+    public void endPart() {
+      moveTo(0,0); // end path
+      if (partActive) {
+        partActive=false;
+        svg.append("</g>\n");
+      }
+    }
+    
+    private void setLocation(int x, int y) {
+      xPrev=xNow;
+      yPrev=yNow;
+      double factor = 25.4/dpi; // convert units to millimeters
+      xNow=x*factor;
+      yNow=y*factor;
+    }
+    
+    /**
+     * move to somewhere with laser off
+     * @param x
+     * @param y 
+     */
+    void moveTo(int x, int y) {
+      setLocation(x,y);
+      if (vectorPathActive) {
+        // end the previous path
+        svg.append("\"/>\n");
+        vectorPathActive=false;
+      }
+    }
+
+    /**
+     * move to somewhere with laser on
+     * @param x
+     * @param y 
+     */
+    void lineTo(int x, int y) {
+      setLocation(x,y);
+      if (!partActive) {
+        throw new RuntimeException("lineTo called outside of a part!");
+      }
+      if (!vectorPathActive) {
+        // start a new path
+        vectorPathActive=true;
+        svg.append("<path id=\"visicut-").append(idCounter).append("\" d=\"M ");
+        idCounter += 1;
+        svg.append(xPrev).append(",").append(yPrev).append(" ");
+      }
+      svg.append(xNow).append(",").append(yNow).append(" ");
+    }
+
+    /**
+     * generate SVG output string and reset everything (delete all path data)
+     * @return 
+     */
+    private String getSVG() {
+      endPart();
+      svg.insert(0,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> \n"
+              + "<!-- Created by VisiCut Debug output -->\n"
+              + "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" "
+              + "xmlns=\"http://www.w3.org/2000/svg\" "
+              + "width=\"" + cutter.getBedWidth() + "mm\" "
+              + "height=\"" + cutter.getBedHeight() + "mm\" "
+              + "viewBox=\"0 0 " + cutter.getBedWidth() + " " + cutter.getBedHeight() + "\" "
+              + "version=\"1.1\" id=\"svg\"> \n");
+      svg.append("</svg>\n");
+      String result=svg.toString();
+      svg = new StringBuilder();
+      idCounter=0;
+      return result;
+    }
+    
+    /**
+     * store a String into a file
+     * @param path the filename
+     * @param str the content to be stored
+     */
+    private void storeString(String path, String str) {
+      try {
+        FileWriter f = new FileWriter(path);
+        BufferedWriter b = new BufferedWriter(f);
+        b.write(str);
+        b.close();
+      } catch (Exception e) {
+        System.out.println("Could not write debug SVG: Exception: " + e);
+      }
+    }
+    
+    /**
+     * store XHTML viewer to file
+     * @param path
+     * @param svgString 
+     */
+    void storeXHTML(String path, String svgString) {
+      BufferedReader br = null;
+      StringBuilder xhtml = new StringBuilder();
+      try {
+        InputStream stream = new Dummy().getClass().getResourceAsStream("resources/visicut-svg-output-viewer.xhtml");
+        StringBuilder s = new StringBuilder();
+        br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+        String line="";
+        while ((line=br.readLine()) != null) {
+          if (line.contains("<!-- REPLACE THIS WITH SVG -->")) {
+            // insert svg, but skip first line with <?xml...
+            line=svgString.substring(svgString.indexOf("\n"));
+          }
+          xhtml.append(line).append("\n");
+        }
+        storeString(path, xhtml.toString());
+      } catch (Exception e) {
+        System.out.println("could not store debug XHTML: " + e);
+      } finally {
+        try {
+          br.close();
+        } catch (IOException ex) {
+          System.out.println("could not close bufferedWriter when storing debug XHTML");
+        }
+      }
+    }
+    
+    void store(String directory) {
+      if (directory == null || directory.isEmpty()) {
+        System.out.println("Not writing debug SVG - no output directory set (edit lasercutter settings to change)");
+      } else {
+        String pathSVG=directory + "/visicut-debug.svg";
+        System.out.println("storing SVG debug output to "+pathSVG);
+        String svgString=getSVG();
+        storeString(pathSVG,svgString);
+        String pathXHTML=directory + "/visicut-svg-output-viewer.xhtml";
+        System.out.println("storing SVG debug output (XHTML viewer) to "+pathXHTML);
+        storeXHTML(pathXHTML, svgString);
+      }
+    }
+    
+  }
+  
   @Override
   public String getModelName() {
     return "Dummy";
   }
+  
+
 
   @Override
   public void sendJob(LaserJob job, ProgressListener pl) throws IllegalJobException, Exception {
     pl.progressChanged(this, 0);
-
+    
     BufferedOutputStream out;
     pl.taskChanged(this, "checking job");
     checkJob(job);
     job.applyStartPoint();
     pl.taskChanged(this, "sending");
     pl.taskChanged(this, "sent.");
+    SVGWriter svg = new SVGWriter(this); // SVG debug output
     System.out.println("dummy-driver got LaserJob: ");
     // TODO don't just print the parts and settins, but also the commands
     // TODO if you have too much time, also implement some preview output (svg animation???) - would be nice for testing optimisations
      for (JobPart p : job.getParts())
         {
+          svg.startPart(p.getClass().getSimpleName(), p.getDPI());
           if (p instanceof VectorPart)
           {
             System.out.println("VectorPart");
@@ -60,16 +244,25 @@ public class Dummy extends LaserCutter {
             {
               if (cmd.getType() == VectorCommand.CmdType.SETPROPERTY)
               {
+                
                 if (!(cmd.getProperty() instanceof PowerSpeedFocusFrequencyProperty))
                 {
                   throw new IllegalJobException("This driver expects Power,Speed,Frequency and Focus as settings");
                 }
                 System.out.println(((PowerSpeedFocusFrequencyProperty) cmd.getProperty()).toString());
+              } else if (cmd.getType() == VectorCommand.CmdType.LINETO) {
+                System.out.println("LINETO \t" + cmd.getX() + ", \t" + cmd.getY());
+                svg.lineTo(cmd.getX(),cmd.getY());
+              } else if (cmd.getType() == VectorCommand.CmdType.MOVETO) {
+                System.out.println("MOVETO \t" + cmd.getX() + ", \t" + cmd.getY());
+                svg.moveTo(cmd.getX(),cmd.getY());
               }
             }
+            
           }
           if (p instanceof RasterPart)
           {
+            // TODO add raster output for SVG debug output
             RasterPart rp = ((RasterPart) p);
             if (rp.getLaserProperty() != null && !(rp.getLaserProperty() instanceof PowerSpeedFocusProperty))
             {
@@ -89,6 +282,7 @@ public class Dummy extends LaserCutter {
           }
       }
       System.out.println("end of job.");
+      svg.store(svgOutdir);
       pl.progressChanged(this, 100);
   }
 
@@ -161,10 +355,14 @@ public class Dummy extends LaserCutter {
   public void setBedHeight(double bedHeight) {
     this.bedHeight = bedHeight;
   }
+  
+  public String svgOutdir="";
+  
   private static String[] settingAttributes = new String[]{
     SETTING_BEDWIDTH,
     SETTING_BEDHEIGHT,
-    SETTING_RUNTIME
+    SETTING_RUNTIME,
+    SETTING_SVG_OUTDIR
   };
 
   @Override
@@ -180,6 +378,8 @@ public class Dummy extends LaserCutter {
       return this.getBedHeight();
     } else if (SETTING_RUNTIME.equals(attribute)) {
       return this.fakeRunTime;
+    } else if (SETTING_SVG_OUTDIR.equals(attribute)) {
+      return this.svgOutdir;
     }
     return null;
   }
@@ -192,6 +392,8 @@ public class Dummy extends LaserCutter {
       this.setBedHeight((Double) value);
     } else if (SETTING_RUNTIME.equals(attribute)) {
        this.fakeRunTime=Integer.parseInt(value.toString());
+    } else if (SETTING_SVG_OUTDIR.equals(attribute)) {
+      this.svgOutdir=value.toString();
     }
 
   }
diff --git a/src/com/t_oster/liblasercut/drivers/resources/visicut-svg-output-viewer.xhtml b/src/com/t_oster/liblasercut/drivers/resources/visicut-svg-output-viewer.xhtml
new file mode 100644
index 0000000..938338c
--- /dev/null
+++ b/src/com/t_oster/liblasercut/drivers/resources/visicut-svg-output-viewer.xhtml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html 
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" 
+xmlns:svg="http://www.w3.org/2000/svg"
+xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+<title>VisiCut SVG debug view</title>
+</head>
+<style>
+.done{stroke:black;}
+.todo{stroke:grey; fill:none;}
+</style>
+<script>
+<![CDATA[
+var maximum=0;
+function load(n) {
+	// get maximum
+	var i=0;
+	while (true) {
+		var obj=document.getElementById('visicut-'+i);
+		if (obj==null) {
+			// end of elements reached
+			break;
+		}
+		i++;
+		maximum=i;
+	}
+	document.getElementById('slider').max=maximum;
+}
+function showUntil(n) {
+	for (var i=0; i<n; i++) {
+		document.getElementById('visicut-'+i).setAttribute('class','done');
+	}
+	var i=n;
+	while (true) {
+		var obj=document.getElementById('visicut-'+i);
+		if (obj==null) {
+			// end of elements reached
+			break;
+		}
+		obj.setAttribute('class','todo');
+		i++;
+	}
+}
+]]>
+</script>
+<body onload="load()">
+
+<h1>VisiCut debug output</h1>
+<div style="display:block; width:100%;">
+  Slide to see individual steps. Please use this with Chrome or another browser that supports &lt;input type=&quot;range&quot;&gt; (not Firefox).<br/>
+Start <input id="slider" type="range" value="0" min="0" max="0" onchange="showUntil(this.value)" style="width:60%"/> End
+</div>
+
+
+<!-- REPLACE THIS WITH SVG -->
+
+</body>
+</html>
\ No newline at end of file
-- 
GitLab