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