File indexing completed on 2024-12-22 04:04:08

0001 /* Copyright (C) 2001-2019 Peter Selinger.
0002    This file is part of Potrace. It is free software and it is covered
0003    by the GNU General Public License. See the file COPYING for details. */
0004 
0005 /* The DXF backend of Potrace. */
0006 
0007 #ifdef HAVE_CONFIG_H
0008 #include <config.h>
0009 #endif
0010 
0011 #include <stdio.h>
0012 #include <stdarg.h>
0013 #include <string.h>
0014 #include <math.h>
0015 
0016 #include "main.h"
0017 #include "backend_dxf.h"
0018 #include "potracelib.h"
0019 #include "lists.h"
0020 #include "auxiliary.h"
0021 #include "trans.h"
0022 
0023 #ifndef M_PI
0024 #define M_PI 3.14159265358979323846
0025 #endif
0026 
0027 /* ---------------------------------------------------------------------- */
0028 /* auxiliary linear algebra functions */
0029 
0030 /* subtract two vectors */
0031 static dpoint_t sub(dpoint_t v, dpoint_t w) {
0032   dpoint_t r;
0033 
0034   r.x = v.x - w.x;
0035   r.y = v.y - w.y;
0036   return r;
0037 }
0038 
0039 /* inner product */
0040 static double iprod(dpoint_t v, dpoint_t w) {
0041   return v.x * w.x + v.y * w.y;
0042 }
0043 
0044 /* cross product */
0045 static double xprod(dpoint_t v, dpoint_t w) {
0046   return v.x * w.y - v.y * w.x;
0047 }
0048 
0049 /* calculate the DXF polyline "bulge" value corresponding to the angle
0050    between two vectors. In case of "infinity" return 0.0. */
0051 static double bulge(dpoint_t v, dpoint_t w) {
0052   double v2, w2, vw, vxw, nvw;
0053 
0054   v2 = iprod(v, v);
0055   w2 = iprod(w, w);
0056   vw = iprod(v, w);
0057   vxw = xprod(v, w);
0058   nvw = sqrt(v2 * w2);
0059 
0060   if (vxw == 0.0) {
0061     return 0.0;
0062   }    
0063 
0064   return (nvw - vw) / vxw;
0065 }
0066 
0067 /* ---------------------------------------------------------------------- */
0068 /* DXF output synthesis */
0069 
0070 /* print with group code: the low-level DXF file format */
0071 static int ship(FILE *fout, int gc, const char *fmt, ...) {
0072   va_list args;
0073   int r;
0074   int c;
0075 
0076   r = fprintf(fout, "%3d\n", gc);
0077   if (r < 0) {
0078     return r;
0079   }
0080   c = r;
0081   va_start(args, fmt);
0082   r = vfprintf(fout, fmt, args);
0083   va_end(args);
0084   if (r < 0) {
0085     return r;
0086   }
0087   c += r;
0088   r = fprintf(fout, "\n");
0089   if (r < 0) {
0090     return r;
0091   }
0092   c += r;
0093   return c;
0094 }
0095 
0096 /* output the start of a polyline */
0097 static void ship_polyline(FILE *fout, const char *layer, int closed) {
0098   ship(fout, 0, "POLYLINE");
0099   ship(fout, 8, "%s", layer);
0100   ship(fout, 66, "%d", 1);
0101   ship(fout, 70, "%d", closed ? 1 : 0);
0102 }
0103 
0104 /* output a vertex */
0105 static void ship_vertex(FILE *fout, const char *layer, dpoint_t v, double bulge) {
0106   ship(fout, 0, "VERTEX");
0107   ship(fout, 8, "%s", layer);
0108   ship(fout, 10, "%f", v.x);
0109   ship(fout, 20, "%f", v.y);
0110   ship(fout, 42, "%f", bulge);
0111 }
0112 
0113 /* output the end of a polyline */
0114 static void ship_seqend(FILE *fout) {
0115   ship(fout, 0, "SEQEND");
0116 }
0117 
0118 /* output a comment */
0119 static void ship_comment(FILE *fout, const char *comment) {
0120   ship(fout, 999, "%s", comment);
0121 }
0122 
0123 /* output the start of a section */
0124 static void ship_section(FILE *fout, const char *name) {
0125   ship(fout, 0, "SECTION");
0126   ship(fout, 2, "%s", name);
0127 }
0128 
0129 static void ship_endsec(FILE *fout) {
0130   ship(fout, 0, "ENDSEC");
0131 }
0132 
0133 static void ship_eof(FILE *fout) {
0134   ship(fout, 0, "EOF");
0135 }
0136 
0137 /* ---------------------------------------------------------------------- */
0138 /* Simulated quadratic and bezier curves */
0139 
0140 /* Output vertices (with bulges) corresponding to a smooth pair of
0141    circular arcs from A to B, tangent to AC at A and to CB at
0142    B. Segments are meant to be concatenated, so don't output the final
0143    vertex. */
0144 static void pseudo_quad(FILE *fout, const char *layer, dpoint_t A, dpoint_t C, dpoint_t B) {
0145   dpoint_t v, w;
0146   double v2, w2, vw, vxw, nvw;
0147   double a, b, c, y;
0148   dpoint_t G;
0149   double bulge1, bulge2;
0150 
0151   v = sub(A, C);
0152   w = sub(B, C);
0153 
0154   v2 = iprod(v, v);
0155   w2 = iprod(w, w);
0156   vw = iprod(v, w);
0157   vxw = xprod(v, w);
0158   nvw = sqrt(v2 * w2);
0159 
0160   a = v2 + 2*vw + w2;
0161   b = v2 + 2*nvw + w2;
0162   c = 4*nvw;
0163   if (vxw == 0 || a == 0) {
0164     goto degenerate;
0165   }
0166   /* assert: a,b,c >= 0, b*b - a*c >= 0, and 0 <= b - sqrt(b*b - a*c) <= a */
0167   y = (b - sqrt(b*b - a*c)) / a;
0168   G = interval(y, C, interval(0.5, A, B));
0169 
0170   bulge1 = bulge(sub(A,G), v);
0171   bulge2 = bulge(w, sub(B,G));
0172 
0173   ship_vertex(fout, layer, A, -bulge1);
0174   ship_vertex(fout, layer, G, -bulge2);
0175   return;
0176 
0177  degenerate:
0178   ship_vertex(fout, layer, A, 0);
0179 
0180   return;
0181 }
0182 
0183 /* produce a smooth from A to D, tangent to AB at A and to CD at D.
0184    This is similar to a Bezier curve, except that our curve will be
0185    made up of up to 4 circular arcs. This is particularly intended for
0186    the case when AD and BC are parallel. Like arcs(), don't output the
0187    final vertex. */
0188 static void pseudo_bezier(FILE *fout, const char *layer, dpoint_t A, dpoint_t B, dpoint_t C, dpoint_t D) {
0189   dpoint_t E = interval(0.75, A, B);
0190   dpoint_t G = interval(0.75, D, C);
0191   dpoint_t F = interval(0.5, E, G);
0192 
0193   pseudo_quad(fout, layer, A, E, F);
0194   pseudo_quad(fout, layer, F, G, D);
0195   return;
0196 }
0197 
0198 /* ---------------------------------------------------------------------- */
0199 /* functions for converting a path to a DXF polyline */
0200 
0201 /* do one path. */
0202 static int dxf_path(FILE *fout, const char *layer, potrace_curve_t *curve, trans_t t) {
0203   int i;
0204   dpoint_t *c, *c1;
0205   int n = curve->n;
0206 
0207   ship_polyline(fout, layer, 1);
0208 
0209   for (i=0; i<n; i++) {
0210     c = curve->c[i];
0211     c1 = curve->c[mod(i-1,n)];
0212     switch (curve->tag[i]) {
0213     case POTRACE_CORNER:
0214       ship_vertex(fout, layer, trans(c1[2], t), 0);
0215       ship_vertex(fout, layer, trans(c[1], t), 0);
0216       break;
0217     case POTRACE_CURVETO:
0218       pseudo_bezier(fout, layer, trans(c1[2], t), trans(c[0], t), trans(c[1], t), trans(c[2], t));
0219       break;
0220     }
0221   }
0222   ship_seqend(fout);
0223   return 0;
0224 }
0225 
0226 /* ---------------------------------------------------------------------- */
0227 /* Backend. */
0228 
0229 /* public interface for DXF */
0230 int page_dxf(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) {
0231   potrace_path_t *p;
0232   trans_t t;
0233   const char *layer = "0";
0234 
0235   /* set up the coordinate transform (rotation) */
0236   t.bb[0] = imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar;
0237   t.bb[1] = imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar;
0238   t.orig[0] = imginfo->trans.orig[0]+imginfo->lmar;
0239   t.orig[1] = imginfo->trans.orig[1]+imginfo->bmar;
0240   t.x[0] = imginfo->trans.x[0];
0241   t.x[1] = imginfo->trans.x[1];
0242   t.y[0] = imginfo->trans.y[0];
0243   t.y[1] = imginfo->trans.y[1];
0244 
0245   ship_comment(fout, "DXF data, created by " POTRACE " " VERSION ", written by Peter Selinger 2001-2019");
0246 
0247   /* header section */
0248   ship_section(fout, "HEADER");
0249 
0250   /* variables */
0251   ship(fout, 9, "$ACADVER");
0252   ship(fout, 1, "AC1006");
0253   ship(fout, 9, "$EXTMIN");
0254   ship(fout, 10, "%f", 0.0);
0255   ship(fout, 20, "%f", 0.0);
0256   ship(fout, 30, "%f", 0.0);
0257   ship(fout, 9, "$EXTMAX");
0258   ship(fout, 10, "%f", t.bb[0]);
0259   ship(fout, 20, "%f", t.bb[1]);
0260   ship(fout, 30, "%f", 0.0);
0261 
0262   ship_endsec(fout);
0263 
0264   /* entities section */
0265   ship_section(fout, "ENTITIES");
0266 
0267   /* write paths */
0268   list_forall (p, plist) {
0269     dxf_path(fout, layer, &p->curve, t);
0270   }
0271 
0272   ship_endsec(fout);
0273   ship_eof(fout);
0274 
0275   fflush(fout);
0276   return 0;
0277 }
0278