SVG output for cad3d

John Pybus john.pybus@zoology.oxford.ac.uk
Tue, 04 May 2004 00:46:05 +0100


This is a multi-part message in MIME format.
--------------080301030902060208030408
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Hi Olly,

Attached is a patch against the 1.1 CVS version of cad3d.c.  It adds the 
option of SVG as an output format.  SVG is arguably supported by more 
editors than DXF these days, and though I have been able to generate it 
by going via sketch, it's neater to do so directly.

The styles could be tidied up a bit (e.g. text height is hard coded), 
and there are more things that could be added such as an option to use 
real world lengths rather than user space co-ordinates.  The SVG 
standard even specifies a metadata format for describing the projection 
used should one wish to go that far:

http://www.w3.org/TR/SVG11/coords.html#GeographicCoordinates

However it's already useful as is.  It has been tested against inkscape 
and the Batik toolkit's viewer though it's simple enough that it should 
work with any svg implementation.

Yours,

John

--------------080301030902060208030408
Content-Type: text/plain;
 name="cad3d.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="cad3d.patch"

--- src/cad3d.c.orig	2004-05-03 23:45:21.000000000 +0100
+++ src/cad3d.c	2004-05-04 00:18:32.000000000 +0100
@@ -49,6 +49,12 @@
 #define POINTS_PER_INCH	72.0
 #define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
 
+#define SQRT_2          1.41421356237309504880168872420969
+
+#define LEGS 1
+#define STNS 2
+#define LABELS 4
+
 /* default to DXF */
 #define FMT_DEFAULT FMT_DXF
 
@@ -333,26 +339,6 @@
 static point **htab;
 
 static void
-plt_header(void)
-{
-   size_t i;
-   htab = osmalloc(HTAB_SIZE * ossizeof(point *));
-   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
-   /* Survex is E, N, Alt - PLT file is N, E, Alt */
-   fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
-           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
-           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
-           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
-   fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X", pimg->title);
-}
-
-static void
-plt_start_pass(int layer)
-{
-   layer = layer;
-}
-
-static void
 set_name(const img_point *p, const char *s)
 {
    int hash;
@@ -368,9 +354,9 @@
    hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
    for (pt = htab[hash]; pt; pt = pt->next) {
       if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
-	 /* already got name for these coordinates */
-	 /* FIXME: what about multiple names for the same station? */
-	 return;
+         /* already got name for these coordinates */
+         /* FIXME: what about multiple names for the same station? */
+         return;
       }
    }
 
@@ -406,6 +392,118 @@
 }
 
 static void
+svg_header(void)
+{
+   size_t i;
+   htab = osmalloc(HTAB_SIZE * ossizeof(point *));
+   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
+   fprintf(fh, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+   fprintf(fh, "<svg width=\"%.3f\" height=\"%.3f\">\n",
+           (max_x - min_x) * factor, (max_y - min_y) * factor);
+   fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
+           min_x * -factor, min_y * -factor);
+}
+
+static bool to_close = 0;
+static bool close_g = 0;
+
+static void
+svg_start_pass(int layer)
+{
+   if (to_close) {
+      fprintf(fh, "\"/>\n");
+      to_close = 0;
+   }
+   if (close_g) {
+      fprintf(fh, "</g>\n");
+   }
+   close_g = 1;
+
+   fprintf(fh, "<g id=\"%s\"", layer_names[layer]);
+   if (layer & LEGS)
+      fprintf(fh, " style=\"fill:none;stroke:#000000;stroke-width:0.5\"");
+   else if (layer & STNS)
+      fprintf(fh, " style=\"fill:none;stroke:#000000;stroke-width:0.05\"");
+   else if (layer & LABELS)
+      fprintf(fh, " style=\"font-size:5\"");
+   fprintf(fh, ">\n");
+}
+
+static void
+svg_move(const img_point *p)
+{
+   if (to_close) {
+      fprintf(fh, "\"/>\n");
+   }
+   fprintf(fh, "<path d=\"M %.3f %.3f", p->x * factor, p->y * factor);
+   to_close = 1;
+}
+
+static void
+svg_line(const img_point *p1, const img_point *p, bool fSurface)
+{
+   fSurface = fSurface; /* unused */
+   p1 = p1; /* unused */
+   fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * factor);
+   to_close = 1;
+}
+
+static void
+svg_label(const img_point *p, const char *s, bool fSurface)
+{
+   fSurface = fSurface; /* unused */
+   fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
+           p->x * factor, p->y * factor);
+   fprintf(fh, s);
+   fprintf(fh, "</text>\n");
+   set_name(p, s);
+}
+
+static void
+svg_cross(const img_point *p, bool fSurface)
+{
+   fSurface = fSurface; /* unused */
+   fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
+           find_name(p), p->x * factor, p->y * factor, MARKER_SIZE * SQRT_2);
+   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
+	   p->x * factor - MARKER_SIZE, p->y * factor - MARKER_SIZE,
+	   p->x * factor + MARKER_SIZE, p->y * factor + MARKER_SIZE,
+	   p->x * factor + MARKER_SIZE, p->y * factor - MARKER_SIZE,
+	   p->x * factor - MARKER_SIZE, p->y * factor + MARKER_SIZE );
+}
+
+static void
+svg_footer(void) ////
+{
+   if (to_close) {
+      fprintf(fh, "\"/>\n");
+      to_close = 0;
+   }
+   fprintf(fh, "</g>\n");
+   fprintf(fh, "</g>\n</svg>");
+}
+
+static void
+plt_header(void)
+{
+   size_t i;
+   htab = osmalloc(HTAB_SIZE * ossizeof(point *));
+   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
+   /* Survex is E, N, Alt - PLT file is N, E, Alt */
+   fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
+           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
+           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
+           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
+   fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X", pimg->title);
+}
+
+static void
+plt_start_pass(int layer)
+{
+   layer = layer;
+}
+
+static void
 plt_move(const img_point *p)
 {
    /* Survex is E, N, Alt - PLT file is N, E, Alt */
@@ -458,12 +556,10 @@
    putc('\x1a', fh);
 }
 
-#define LEGS 1
-#define STNS 2
-#define LABELS 4
 static int dxf_passes[] = { LEGS|STNS|LABELS, 0 };
 static int sketch_passes[] = { LEGS, STNS, LABELS, 0 };
 static int plt_passes[] = { LABELS, LEGS, 0 };
+static int svg_passes[] = { LEGS, LABELS, STNS, 0 };
 
 int
 main(int argc, char **argv)
@@ -476,8 +572,8 @@
    int elevation = 0;
    double elev_angle = 0;
    double s = 0, c = 0;
-   enum { FMT_DXF = 0, FMT_SKETCH, FMT_PLT, FMT_AUTO } format;
-   static const char *extensions[] = { "dxf", "sk", "plt" };
+   enum { FMT_DXF = 0, FMT_SKETCH, FMT_PLT, FMT_SVG, FMT_AUTO } format;
+   static const char *extensions[] = { "dxf", "sk", "plt", "svg" };
    int *pass;
 
    void (*header)(void);
@@ -504,12 +600,13 @@
 	{"dxf", no_argument, 0, 'D'},
 	{"sketch", no_argument, 0, 'S'},
 	{"plt", no_argument, 0, 'P'},
+        {"svg", no_argument, 0, 'V'},
 	{"help", no_argument, 0, HLP_HELP},
 	{"version", no_argument, 0, HLP_VERSION},
 	{0,0,0,0}
    };
 
-#define short_opts "s:cnlg::t:m:e:r:DSP"
+#define short_opts "s:cnlg::t:m:e:r:DSPV"
 
    /* TRANSLATE */
    static struct help_msg help[] = {
@@ -525,6 +622,7 @@
 	{HLP_ENCODELONG(9), "produce DXF output"},
 	{HLP_ENCODELONG(10), "produce Sketch output"},
 	{HLP_ENCODELONG(11), "produce Compass PLT output for Carto"},
+        {HLP_ENCODELONG(12), "produce SVG output"},
 	{0,0}
    };
 
@@ -588,6 +686,9 @@
        case 'P':
 	 format = FMT_PLT;
 	 break;
+       case 'V':
+         format = FMT_SVG;
+         break;
        case 's':
 	 survey = optarg;
 	 break;
@@ -656,6 +757,18 @@
       pass = plt_passes;
       mode = "wb"; /* Binary file output */
       break;
+    case FMT_SVG:
+      header = svg_header;
+      start_pass = svg_start_pass;
+      move = svg_move;
+      line = svg_line;
+      label = svg_label;
+      cross = svg_cross;
+      footer = svg_footer;
+      pass = svg_passes;
+      factor = 1000.0 / scale;
+      mode = "wb"; /* Binary file output: to avoid messed up line seps*/
+      break;     
     default:
       exit(1);
    }

--------------080301030902060208030408--