1 #include "solvespace.h"
2 #include "libdxfrw.h"
3 #include "libdwgr.h"
4 
5 #ifdef WIN32
6 // Conflicts with DRW::TEXT.
7 #   undef TEXT
8 #endif
9 
10 namespace SolveSpace {
11 
ToUpper(std::string str)12 static std::string ToUpper(std::string str) {
13     std::transform(str.begin(), str.end(), str.begin(), ::toupper);
14     return str;
15 }
16 
17 class DxfReadInterface : public DRW_Interface {
18 public:
19     Vector blockX;
20     Vector blockY;
21     Vector blockT;
22 
invertXTransform()23     void invertXTransform() {
24         blockX.x = -blockX.x;
25         blockY.x = -blockY.x;
26         blockT.x = -blockT.x;
27     }
28 
multBlockTransform(double x,double y,double sx,double sy,double angle)29     void multBlockTransform(double x, double y, double sx, double sy, double angle) {
30         Vector oldX = blockX;
31         Vector oldY = blockY;
32         Vector oldT = blockT;
33 
34         Vector newX = Vector::From(sx, 0.0, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
35         Vector newY = Vector::From(0.0, sy, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
36         Vector newT = Vector::From(x, y, 0.0);
37 
38         blockX = oldX.ScaledBy(newX.x).Plus(
39                  oldY.ScaledBy(newX.y));
40 
41         blockY = oldX.ScaledBy(newY.x).Plus(
42                  oldY.ScaledBy(newY.y));
43 
44         blockT = oldX.ScaledBy(newT.x).Plus(
45                  oldY.ScaledBy(newT.y)).Plus(oldT);
46     }
47 
clearBlockTransform()48     void clearBlockTransform() {
49         blockX = Vector::From(1.0, 0.0, 0.0);
50         blockY = Vector::From(0.0, 1.0, 0.0);
51         blockT = Vector::From(0.0, 0.0, 0.0);
52     }
53 
blockTransform(Vector v)54     Vector blockTransform(Vector v) {
55         Vector r = blockT;
56         r = r.Plus(blockX.ScaledBy(v.x));
57         r = r.Plus(blockY.ScaledBy(v.y));
58         return r;
59     }
60 
blockTransformArc(Vector * c,Vector * p0,Vector * p1)61     void blockTransformArc(Vector *c, Vector *p0, Vector *p1) {
62         bool oldSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
63 
64         *c = blockTransform(*c);
65         *p0 = blockTransform(*p0);
66         *p1 = blockTransform(*p1);
67 
68         bool newSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
69         if(oldSign != newSign) std::swap(*p0, *p1);
70     }
71 
toVector(const DRW_Coord & c,bool transform=true)72     Vector toVector(const DRW_Coord &c, bool transform = true) {
73         Vector result = Vector::From(c.x, c.y, 0.0);
74         if(transform) return blockTransform(result);
75         return result;
76     }
77 
toVector(const DRW_Vertex2D & c)78     Vector toVector(const DRW_Vertex2D &c) {
79         Vector result = Vector::From(c.x, c.y, 0.0);
80         return blockTransform(result);
81     }
82 
toVector(const DRW_Vertex & c)83     Vector toVector(const DRW_Vertex &c) {
84         Vector result = Vector::From(c.basePoint.x, c.basePoint.y, 0.0);
85         return blockTransform(result);
86     }
87 
angleTo(Vector v0,Vector v1)88     double angleTo(Vector v0, Vector v1) {
89         Vector d = v1.Minus(v0);
90         double a = atan2(d.y, d.x);
91         return M_PI + remainder(a - M_PI, 2 * M_PI);
92     }
93 
polar(double radius,double angle)94     Vector polar(double radius, double angle) {
95         return Vector::From(radius * cos(angle), radius * sin(angle), 0.0);
96     }
97 
createBulge(Vector p0,Vector p1,double bulge)98     hRequest createBulge(Vector p0, Vector p1, double bulge) {
99         bool reversed = bulge < 0.0;
100         double alpha = atan(bulge) * 4.0;
101 
102         Vector middle = p1.Plus(p0).ScaledBy(0.5);
103         double dist = p1.Minus(p0).Magnitude() / 2.0;
104         double angle = angleTo(p0, p1);
105 
106         // alpha can't be 0.0 at this point
107         double radius = fabs(dist / sin(alpha / 2.0));
108         double wu = fabs(radius * radius - dist * dist);
109         double h = sqrt(wu);
110 
111         if(bulge > 0.0) {
112             angle += M_PI_2;
113         } else {
114             angle -= M_PI_2;
115         }
116 
117         if (fabs(alpha) > M_PI) {
118             h *= -1.0;
119         }
120 
121         Vector center = polar(h, angle);
122         center = center.Plus(middle);
123 
124         if(reversed) std::swap(p0, p1);
125         blockTransformArc(&center, &p0, &p1);
126 
127         hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
128         SK.GetEntity(hr.entity(1))->PointForceTo(center);
129         SK.GetEntity(hr.entity(2))->PointForceTo(p0);
130         SK.GetEntity(hr.entity(3))->PointForceTo(p1);
131         processPoint(hr.entity(1));
132         processPoint(hr.entity(2));
133         processPoint(hr.entity(3));
134         return hr;
135     }
136 
137     struct Block {
138         std::vector<std::unique_ptr<DRW_Entity>> entities;
139         DRW_Block data;
140     };
141 
142     unsigned unknownEntities = 0;
143     std::map<std::string, hStyle> styles;
144     std::map<std::string, Block> blocks;
145     std::map<std::string, DRW_Layer> layers;
146     Block *readBlock = NULL;
147     const DRW_Insert *insertInsert = NULL;
148 
149     template<class T>
addPendingBlockEntity(const T & e)150     bool addPendingBlockEntity(const T &e) {
151         if(readBlock == NULL) return false;
152         readBlock->entities.emplace_back(new T(e));
153         return true;
154     }
155 
addEntity(DRW_Entity * e)156     void addEntity(DRW_Entity *e) {
157         switch(e->eType) {
158             case DRW::POINT:
159                 addPoint(*static_cast<DRW_Point *>(e));
160                 break;
161             case DRW::LINE:
162                 addLine(*static_cast<DRW_Line *>(e));
163                 break;
164             case DRW::ARC:
165                 addArc(*static_cast<DRW_Arc *>(e));
166                 break;
167             case DRW::CIRCLE:
168                 addCircle(*static_cast<DRW_Circle *>(e));
169                 break;
170             case DRW::POLYLINE:
171                 addPolyline(*static_cast<DRW_Polyline *>(e));
172                 break;
173             case DRW::LWPOLYLINE:
174                 addLWPolyline(*static_cast<DRW_LWPolyline *>(e));
175                 break;
176             case DRW::SPLINE:
177                 addSpline(static_cast<DRW_Spline *>(e));
178                 break;
179             case DRW::INSERT:
180                 addInsert(*static_cast<DRW_Insert *>(e));
181                 break;
182             case DRW::TEXT:
183                 addText(*static_cast<DRW_Text *>(e));
184                 break;
185             case DRW::MTEXT:
186                 addMText(*static_cast<DRW_MText *>(e));
187                 break;
188             case DRW::DIMALIGNED:
189                 addDimAlign(static_cast<DRW_DimAligned *>(e));
190                 break;
191             case DRW::DIMLINEAR:
192                 addDimLinear(static_cast<DRW_DimLinear *>(e));
193                 break;
194             case DRW::DIMRADIAL:
195                 addDimRadial(static_cast<DRW_DimRadial *>(e));
196                 break;
197             case DRW::DIMDIAMETRIC:
198                 addDimDiametric(static_cast<DRW_DimDiametric *>(e));
199                 break;
200             case DRW::DIMANGULAR:
201                 addDimAngular(static_cast<DRW_DimAngular *>(e));
202                 break;
203             default:
204                 unknownEntities++;
205         }
206     }
207 
dxfAlignToOrigin(DRW_Text::HAlign alignH,DRW_Text::VAlign alignV)208     int dxfAlignToOrigin(DRW_Text::HAlign alignH, DRW_Text::VAlign alignV) {
209         int origin = 0;
210         switch(alignH) {
211             case DRW_Text::HLeft:
212                 origin |= Style::ORIGIN_LEFT;
213                 break;
214 
215             case DRW_Text::HMiddle:
216             case DRW_Text::HCenter:
217                 break;
218 
219             case DRW_Text::HRight:
220                 origin |= Style::ORIGIN_RIGHT;
221                 break;
222 
223             case DRW_Text::HAligned:
224             case DRW_Text::HFit:
225             default:
226                 origin |= Style::ORIGIN_LEFT;
227                 break;
228         }
229 
230         switch(alignV) {
231             case DRW_Text::VBaseLine:
232             case DRW_Text::VBottom:
233                 origin |= Style::ORIGIN_BOT;
234                 break;
235 
236             case DRW_Text::VMiddle:
237                 break;
238 
239             case DRW_Text::VTop:
240                 origin |= Style::ORIGIN_TOP;
241                 break;
242 
243             default:
244                 origin |= Style::ORIGIN_BOT;
245                 break;
246         }
247 
248         return origin;
249     }
250 
getSourceLayer(const DRW_Entity * e)251     DRW_Layer *getSourceLayer(const DRW_Entity *e) {
252         DRW_Layer *layer = NULL;
253         if(insertInsert != NULL) {
254             std::string l = insertInsert->layer;
255             auto bi = layers.find(l);
256             if(bi != layers.end()) layer = &bi->second;
257         } else {
258             std::string l = e->layer;
259             auto bi = layers.find(l);
260             if(bi != layers.end()) layer = &bi->second;
261         }
262         return layer;
263     }
264 
getColor(const DRW_Entity * e)265     int getColor(const DRW_Entity *e) {
266         int col = e->color;
267         if(col == DRW::ColorByBlock) {
268             if(insertInsert != NULL) {
269                 col = insertInsert->color;
270             } else {
271                 col = 7;
272             }
273         }
274         if(col == DRW::ColorByLayer) {
275             DRW_Layer *layer = getSourceLayer(e);
276             if(layer != NULL) {
277                 col = layer->color;
278             } else {
279                 col = 7;
280             }
281         }
282         return col;
283     }
284 
getLineWidth(const DRW_Entity * e)285     DRW_LW_Conv::lineWidth getLineWidth(const DRW_Entity *e) {
286         DRW_LW_Conv::lineWidth result = e->lWeight;
287         if(result == DRW_LW_Conv::widthByBlock) {
288             if(insertInsert != NULL) {
289                 result = insertInsert->lWeight;
290             } else {
291                 result = DRW_LW_Conv::widthDefault;
292             }
293         }
294         if(result == DRW_LW_Conv::widthByLayer) {
295             DRW_Layer *layer = getSourceLayer(e);
296             if(layer != NULL) {
297                 result = layer->lWeight;
298             } else {
299                 result = DRW_LW_Conv::widthDefault;
300             }
301         }
302         return result;
303     }
304 
getLineType(const DRW_Entity * e)305     std::string getLineType(const DRW_Entity *e) {
306         std::string  result = e->lineType;
307         if(result == "BYBLOCK") {
308             if(insertInsert != NULL) {
309                 result = ToUpper(insertInsert->lineType);
310             } else {
311                 result = "CONTINUOUS";
312             }
313         }
314         if(result == "BYLAYER") {
315             DRW_Layer *layer = getSourceLayer(e);
316             if(layer != NULL) {
317                 result = ToUpper(layer->lineType);
318             } else {
319                 result = "CONTINUOUS";
320             }
321         }
322         return result;
323     }
324 
invisibleStyle()325     hStyle invisibleStyle() {
326         std::string id = "@dxf-invisible";
327 
328         auto si = styles.find(id);
329         if(si != styles.end()) {
330             return si->second;
331         }
332 
333         hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
334         Style *s = Style::Get(hs);
335         s->name = id;
336         s->visible = false;
337 
338         styles.emplace(id, hs);
339         return hs;
340     }
341 
styleFor(const DRW_Entity * e)342     hStyle styleFor(const DRW_Entity *e) {
343         // Color.
344         // TODO: which color to choose: index or RGB one?
345         int col = getColor(e);
346         RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
347                                       DRW::dxfColors[col][1],
348                                       DRW::dxfColors[col][2]);
349 
350         // Line width.
351         DRW_LW_Conv::lineWidth lw = getLineWidth(e);
352         double width = DRW_LW_Conv::lineWidth2dxfInt(e->lWeight) / 100.0;
353         if(width < 0.0) width = 1.0;
354 
355         // Line stipple.
356         // TODO: Probably, we can load default autocad patterns and match it with ours.
357         std::string lineType = getLineType(e);
358         int stipple = Style::STIPPLE_CONTINUOUS;
359         for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
360             if(lineType == DxfFileWriter::lineTypeName(i)) {
361                 stipple = i;
362                 break;
363             }
364         }
365 
366         // Text properties.
367         DRW_Text::HAlign alignH = DRW_Text::HLeft;
368         DRW_Text::VAlign alignV = DRW_Text::VBaseLine;
369         double textAngle = 0.0;
370         double textHeight = Style::DefaultTextHeight();
371 
372         if(e->eType == DRW::TEXT || e->eType == DRW::MTEXT) {
373             const DRW_Text *text = static_cast<const DRW_Text *>(e);
374             alignH = text->alignH;
375             alignV = text->alignV;
376             textHeight = text->height;
377             textAngle = text->angle;
378             // I have no idea why, but works
379             if(alignH == DRW_Text::HMiddle) {
380                 alignV = DRW_Text::VMiddle;
381             }
382         }
383 
384         // Unique identifier based on style properties.
385         std::string id = "@dxf";
386         if(lw != DRW_LW_Conv::widthDefault)
387             id += ssprintf("-w%.4g", width);
388         if(lineType != "CONTINUOUS")
389             id += ssprintf("-%s", lineType.c_str());
390         if(c.red != 0 || c.green != 0 || c.blue != 0)
391             id += ssprintf("-#%02x%02x%02x", c.red, c.green, c.blue);
392         if(textHeight != Style::DefaultTextHeight())
393             id += ssprintf("-h%.4g", textHeight);
394         if(textAngle != 0.0)
395             id += ssprintf("-a%.5g", textAngle);
396         if(alignH != DRW_Text::HLeft)
397             id += ssprintf("-oh%d", alignH);
398         if(alignV != DRW_Text::VBaseLine)
399             id += ssprintf("-ov%d", alignV);
400 
401         auto si = styles.find(id);
402         if(si != styles.end()) {
403             return si->second;
404         }
405 
406         hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
407         Style *s = Style::Get(hs);
408         if(lw != DRW_LW_Conv::widthDefault) {
409             s->widthAs = Style::UNITS_AS_MM;
410             s->width = width;
411             s->stippleScale = 1.0 + width * 2.0;
412         }
413         s->name = id;
414         s->stippleType = stipple;
415         if(c.red != 0 || c.green != 0 || c.blue != 0) s->color = c;
416         s->textHeightAs = Style::UNITS_AS_MM;
417         s->textHeight = textHeight;
418         s->textAngle = textAngle;
419         s->textOrigin = dxfAlignToOrigin(alignH, alignV);
420 
421         styles.emplace(id, hs);
422         return hs;
423     }
424 
setStyle(hRequest hr,hStyle hs)425     void setStyle(hRequest hr, hStyle hs) {
426         Request *r = SK.GetRequest(hr);
427         r->style = hs;
428     }
429 
430     struct VectorHash {
operator ()SolveSpace::DxfReadInterface::VectorHash431         size_t operator()(const Vector &v) const {
432             static const size_t size = std::numeric_limits<size_t>::max() / 2 - 1;
433             static const double eps = (4.0 * LENGTH_EPS);
434 
435             double x = fabs(v.x) / eps;
436             double y = fabs(v.y) / eps;
437 
438             size_t xs = size_t(fmod(x, double(size)));
439             size_t ys = size_t(fmod(y, double(size)));
440 
441             return ys * size + xs;
442         }
443     };
444 
445     struct VectorPred {
operator ()SolveSpace::DxfReadInterface::VectorPred446         bool operator()(Vector a, Vector b) const {
447             return a.Equals(b, LENGTH_EPS);
448         }
449     };
450 
451     std::unordered_map<Vector, hEntity, VectorHash, VectorPred> points;
452 
processPoint(hEntity he,bool constrain=true)453     void processPoint(hEntity he, bool constrain = true) {
454         Entity *e = SK.GetEntity(he);
455         Vector pos = e->PointGetNum();
456         hEntity p = findPoint(pos);
457         if(p.v == he.v) return;
458         if(p.v != Entity::NO_ENTITY.v) {
459             if(constrain) {
460                 Constraint::ConstrainCoincident(he, p);
461             }
462             // We don't add point because we already
463             // have point in this position
464             return;
465         }
466         points.emplace(pos, he);
467     }
468 
findPoint(const Vector & p)469     hEntity findPoint(const Vector &p) {
470         auto it = points.find(p);
471         if(it == points.end()) return Entity::NO_ENTITY;
472         return it->second;
473     }
474 
createOrGetPoint(const Vector & p)475     hEntity createOrGetPoint(const Vector &p) {
476         hEntity he = findPoint(p);
477         if(he.v != Entity::NO_ENTITY.v) return he;
478 
479         hRequest hr = SS.GW.AddRequest(Request::DATUM_POINT, false);
480         he = hr.entity(0);
481         SK.GetEntity(he)->PointForceTo(p);
482         points.emplace(p, he);
483         return he;
484     }
485 
createLine(Vector p0,Vector p1,uint32_t style,bool constrainHV=false)486     hEntity createLine(Vector p0, Vector p1, uint32_t style, bool constrainHV = false) {
487         if(p0.Equals(p1)) return Entity::NO_ENTITY;
488         hRequest hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false);
489         SK.GetEntity(hr.entity(1))->PointForceTo(p0);
490         SK.GetEntity(hr.entity(2))->PointForceTo(p1);
491         processPoint(hr.entity(1));
492         processPoint(hr.entity(2));
493 
494         if(constrainHV) {
495             int cType = -1;
496             if(fabs(p0.x - p1.x) < LENGTH_EPS) {
497                 cType = Constraint::VERTICAL;
498             }
499             else if(fabs(p0.y - p1.y) < LENGTH_EPS) {
500                 cType = Constraint::HORIZONTAL;
501             }
502             if(cType != -1) {
503                 Constraint::Constrain(
504                     cType,
505                     Entity::NO_ENTITY,
506                     Entity::NO_ENTITY,
507                     hr.entity(0)
508                 );
509             }
510         }
511 
512         if(style != 0) {
513             Request *r = SK.GetRequest(hr);
514             r->style = hStyle{ style };
515         }
516         return hr.entity(0);
517     }
518 
createCircle(const Vector & c,double r,uint32_t style)519     hEntity createCircle(const Vector &c, double r, uint32_t style) {
520         hRequest hr = SS.GW.AddRequest(Request::CIRCLE, false);
521         SK.GetEntity(hr.entity(1))->PointForceTo(c);
522         processPoint(hr.entity(1));
523         SK.GetEntity(hr.entity(64))->DistanceForceTo(r);
524         if(style != 0) {
525             Request *r = SK.GetRequest(hr);
526             r->style = hStyle{ style };
527         }
528         return hr.entity(0);
529     }
530 
addLayer(const DRW_Layer & data)531     virtual void addLayer(const DRW_Layer &data) {
532         layers.emplace(data.name, data);
533     }
534 
addBlock(const DRW_Block & data)535     virtual void addBlock(const DRW_Block &data) {
536         readBlock = &blocks[data.name];
537         readBlock->data = data;
538     }
539 
endBlock()540     virtual void endBlock() {
541         readBlock = NULL;
542     }
543 
addPoint(const DRW_Point & data)544     virtual void addPoint(const DRW_Point &data) {
545         if(data.space != DRW::ModelSpace) return;
546         if(addPendingBlockEntity<DRW_Point>(data)) return;
547 
548         hRequest hr = SS.GW.AddRequest(Request::DATUM_POINT, false);
549         SK.GetEntity(hr.entity(0))->PointForceTo(toVector(data.basePoint));
550         processPoint(hr.entity(0));
551     }
552 
addLine(const DRW_Line & data)553     virtual void addLine(const DRW_Line &data) {
554         if(data.space != DRW::ModelSpace) return;
555         if(addPendingBlockEntity<DRW_Line>(data)) return;
556 
557         createLine(toVector(data.basePoint), toVector(data.secPoint), styleFor(&data).v, true);
558     }
559 
addArc(const DRW_Arc & data)560     virtual void addArc(const DRW_Arc &data) {
561         if(data.space != DRW::ModelSpace) return;
562         if(addPendingBlockEntity<DRW_Arc>(data)) return;
563 
564         hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
565         double r = data.radious;
566         double sa = data.staangle;
567         double ea = data.endangle;
568         Vector c = Vector::From(data.basePoint.x, data.basePoint.y, 0.0);
569         Vector rvs = Vector::From(r * cos(sa), r * sin(sa), data.basePoint.z).Plus(c);
570         Vector rve = Vector::From(r * cos(ea), r * sin(ea), data.basePoint.z).Plus(c);
571 
572         if(data.extPoint.z == -1.0) {
573             c.x = -c.x;
574             rvs.x = - rvs.x;
575             rve.x = - rve.x;
576             std::swap(rvs, rve);
577         }
578 
579         blockTransformArc(&c, &rvs, &rve);
580 
581         SK.GetEntity(hr.entity(1))->PointForceTo(c);
582         SK.GetEntity(hr.entity(2))->PointForceTo(rvs);
583         SK.GetEntity(hr.entity(3))->PointForceTo(rve);
584         processPoint(hr.entity(1));
585         processPoint(hr.entity(2));
586         processPoint(hr.entity(3));
587         setStyle(hr, styleFor(&data));
588     }
589 
addCircle(const DRW_Circle & data)590     virtual void addCircle(const DRW_Circle &data) {
591         if(data.space != DRW::ModelSpace) return;
592         if(addPendingBlockEntity<DRW_Circle>(data)) return;
593 
594         createCircle(toVector(data.basePoint), data.radious, styleFor(&data).v);
595     }
596 
addLWPolyline(const DRW_LWPolyline & data)597     virtual void addLWPolyline(const DRW_LWPolyline &data)  {
598         if(data.space != DRW::ModelSpace) return;
599         if(addPendingBlockEntity<DRW_LWPolyline>(data)) return;
600 
601         size_t vNum = data.vertlist.size();
602 
603         // Check for closed polyline.
604         if((data.flags & 1) != 1) vNum--;
605 
606         // Correct coordinate system for the case where z=-1, as described in
607         // http://paulbourke.net/dataformats/dxf/dxf10.html.
608         bool needSwapX = data.extPoint.z == -1.0;
609 
610         for(size_t i = 0; i < vNum; i++) {
611             DRW_Vertex2D c0 = *data.vertlist[i];
612             DRW_Vertex2D c1 = *data.vertlist[(i + 1) % data.vertlist.size()];
613 
614             if(needSwapX) {
615                 c0.x = -c0.x;
616                 c1.x = -c1.x;
617                 c0.bulge = -c0.bulge;
618             }
619 
620             Vector p0 = Vector::From(c0.x, c0.y, 0.0);
621             Vector p1 = Vector::From(c1.x, c1.y, 0.0);
622             hStyle hs = styleFor(&data);
623 
624             if(EXACT(data.vertlist[i]->bulge == 0.0)) {
625                 createLine(blockTransform(p0), blockTransform(p1), hs.v, true);
626             } else {
627                 hRequest hr = createBulge(p0, p1, c0.bulge);
628                 setStyle(hr, hs);
629             }
630         }
631     }
632 
addPolyline(const DRW_Polyline & data)633     virtual void addPolyline(const DRW_Polyline &data) {
634         if(data.space != DRW::ModelSpace) return;
635         if(addPendingBlockEntity<DRW_Polyline>(data)) return;
636 
637         int vNum = data.vertlist.size();
638 
639         // Check for closed polyline.
640         if((data.flags & 1) != 1) vNum--;
641 
642         // Correct coordinate system for the case where z=-1, as described in
643         // http://paulbourke.net/dataformats/dxf/dxf10.html.
644         bool needSwapX = data.extPoint.z == -1.0;
645 
646         for(int i = 0; i < vNum; i++) {
647             DRW_Coord c0 = data.vertlist[i]->basePoint;
648             DRW_Coord c1 = data.vertlist[(i + 1) % data.vertlist.size()]->basePoint;
649 
650             double bulge = data.vertlist[i]->bulge;
651             if(needSwapX) {
652                 c0.x = -c0.x;
653                 c1.x = -c1.x;
654                 bulge = -bulge;
655             }
656 
657             Vector p0 = Vector::From(c0.x, c0.y, 0.0);
658             Vector p1 = Vector::From(c1.x, c1.y, 0.0);
659             hStyle hs = styleFor(&data);
660 
661             if(EXACT(bulge == 0.0)) {
662                 createLine(blockTransform(p0), blockTransform(p1), hs.v, true);
663             } else {
664                 hRequest hr = createBulge(p0, p1, bulge);
665                 setStyle(hr, hs);
666             }
667         }
668     }
669 
addSpline(const DRW_Spline * data)670     virtual void addSpline(const DRW_Spline *data) {
671         if(data->space != DRW::ModelSpace) return;
672         if(data->degree != 3) return;
673         if(addPendingBlockEntity<DRW_Spline>(*data)) return;
674 
675         hRequest hr = SS.GW.AddRequest(Request::CUBIC, false);
676         for(int i = 0; i < 4; i++) {
677             SK.GetEntity(hr.entity(i + 1))->PointForceTo(toVector(*data->controllist[i]));
678             processPoint(hr.entity(i + 1));
679         }
680         setStyle(hr, styleFor(data));
681     }
682 
addInsert(const DRW_Insert & data)683     virtual void addInsert(const DRW_Insert &data) {
684         if(data.space != DRW::ModelSpace) return;
685         if(addPendingBlockEntity<DRW_Insert>(data)) return;
686 
687         auto bi = blocks.find(data.name);
688         if(bi == blocks.end()) oops();
689         Block *block = &bi->second;
690 
691         // Push transform.
692         Vector x = blockX;
693         Vector y = blockY;
694         Vector t = blockT;
695 
696         const DRW_Insert *oldInsert = insertInsert;
697         insertInsert = &data;
698 
699         if(data.extPoint.z == -1.0) invertXTransform();
700         multBlockTransform(data.basePoint.x, data.basePoint.y, data.xscale, data.yscale, data.angle);
701         for(auto &e : block->entities) {
702             addEntity(&*e);
703         }
704 
705         insertInsert = oldInsert;
706 
707         // Pop transform.
708         blockX = x;
709         blockY = y;
710         blockT = t;
711     }
712 
addMText(const DRW_MText & data)713     virtual void addMText(const DRW_MText &data) {
714         if(data.space != DRW::ModelSpace) return;
715         if(addPendingBlockEntity<DRW_MText>(data)) return;
716 
717         DRW_MText text = data;
718         text.secPoint = text.basePoint;
719         addText(text);
720     }
721 
addText(const DRW_Text & data)722     virtual void addText(const DRW_Text &data) {
723         if(data.space != DRW::ModelSpace) return;
724         if(addPendingBlockEntity<DRW_Text>(data)) return;
725 
726         Constraint c = {};
727         c.group         = SS.GW.activeGroup;
728         c.workplane     = SS.GW.ActiveWorkplane();
729         c.type          = Constraint::COMMENT;
730         if(data.alignH == DRW_Text::HLeft && data.alignV == DRW_Text::VBaseLine) {
731             c.disp.offset   = toVector(data.basePoint);
732         } else {
733             c.disp.offset   = toVector(data.secPoint);
734         }
735         c.comment       = data.text;
736         c.disp.style    = styleFor(&data);
737         Constraint::AddConstraint(&c, false);
738     }
739 
addDimAlign(const DRW_DimAligned * data)740     virtual void addDimAlign(const DRW_DimAligned *data) {
741         if(data->space != DRW::ModelSpace) return;
742         if(addPendingBlockEntity<DRW_DimAligned>(*data)) return;
743 
744         Vector p0 = toVector(data->getDef1Point());
745         Vector p1 = toVector(data->getDef2Point());
746         Vector p2 = toVector(data->getTextPoint());
747         hConstraint hc = Constraint::Constrain(
748             Constraint::PT_PT_DISTANCE,
749             createOrGetPoint(p0),
750             createOrGetPoint(p1),
751             Entity::NO_ENTITY
752         );
753 
754         Constraint *c = SK.GetConstraint(hc);
755         if(data->hasActualMeasurement()) {
756             c->valA = data->getActualMeasurement();
757         } else {
758             c->ModifyToSatisfy();
759         }
760         c->disp.offset = p2.Minus(p0.Plus(p1).ScaledBy(0.5));
761     }
762 
addDimLinear(const DRW_DimLinear * data)763     virtual void addDimLinear(const DRW_DimLinear *data) {
764         if(data->space != DRW::ModelSpace) return;
765         if(addPendingBlockEntity<DRW_DimLinear>(*data)) return;
766 
767         Vector p0 = toVector(data->getDef1Point(), false);
768         Vector p1 = toVector(data->getDef2Point(), false);
769         Vector p2 = toVector(data->getTextPoint(), false);
770 
771         double angle = data->getAngle() * PI / 180.0;
772         Vector dir = Vector::From(cos(angle), sin(angle), 0.0);
773         Vector p3 = p1.Minus(p1.ClosestPointOnLine(p2, dir)).Plus(p1);
774         if(p1.Minus(p3).Magnitude() < LENGTH_EPS) {
775             p3 = p0.Minus(p0.ClosestPointOnLine(p2, dir)).Plus(p1);
776         }
777 
778         Vector p4 = p0.ClosestPointOnLine(p1, p3.Minus(p1)).Plus(p0).ScaledBy(0.5);
779 
780         p0 = blockTransform(p0);
781         p1 = blockTransform(p1);
782         p2 = blockTransform(p2);
783         p3 = blockTransform(p3);
784         p4 = blockTransform(p4);
785 
786         hConstraint hc = Constraint::Constrain(
787             Constraint::PT_LINE_DISTANCE,
788             createOrGetPoint(p0),
789             Entity::NO_ENTITY,
790             createLine(p1, p3, invisibleStyle().v)
791         );
792 
793         Constraint *c = SK.GetConstraint(hc);
794         if(data->hasActualMeasurement()) {
795             c->valA = data->getActualMeasurement();
796         } else {
797             c->ModifyToSatisfy();
798         }
799         c->disp.offset = p2.Minus(p4);
800     }
801 
addDimAngular(const DRW_DimAngular * data)802     virtual void addDimAngular(const DRW_DimAngular *data) {
803         if(data->space != DRW::ModelSpace) return;
804         if(addPendingBlockEntity<DRW_DimAngular>(*data)) return;
805 
806         Vector l0p0 = toVector(data->getFirstLine1());
807         Vector l0p1 = toVector(data->getFirstLine2());
808         Vector l1p0 = toVector(data->getSecondLine1());
809         Vector l1p1 = toVector(data->getSecondLine2());
810 
811         hConstraint hc = Constraint::Constrain(
812             Constraint::ANGLE,
813             Entity::NO_ENTITY,
814             Entity::NO_ENTITY,
815             createLine(l0p0, l0p1, invisibleStyle().v),
816             createLine(l1p1, l1p0, invisibleStyle().v),
817             /*other=*/false,
818             /*other2=*/false
819         );
820 
821         Constraint *c = SK.GetConstraint(hc);
822         c->ModifyToSatisfy();
823         if(data->hasActualMeasurement()) {
824             double actual = data->getActualMeasurement() / PI * 180.0;
825             if(fabs(180.0 - actual - c->valA) < fabs(actual - c->valA)) {
826                 c->other = true;
827             }
828             c->valA = actual;
829         }
830 
831         bool skew = false;
832         Vector pi = Vector::AtIntersectionOfLines(l0p0, l0p1, l1p0, l1p1, &skew);
833         if(!skew) {
834             c->disp.offset = toVector(data->getTextPoint()).Minus(pi);
835         }
836     }
837 
createDiametric(Vector cp,double r,Vector tp,double actual,bool asRadius=false)838     hConstraint createDiametric(Vector cp, double r, Vector tp, double actual, bool asRadius = false) {
839         hEntity he = createCircle(cp, r, invisibleStyle().v);
840 
841         hConstraint hc = Constraint::Constrain(
842             Constraint::DIAMETER,
843             Entity::NO_ENTITY,
844             Entity::NO_ENTITY,
845             he
846         );
847 
848         Constraint *c = SK.GetConstraint(hc);
849         if(actual > 0.0) {
850             c->valA = asRadius ? actual * 2.0 : actual;
851         } else {
852             c->ModifyToSatisfy();
853         }
854         c->disp.offset = tp.Minus(cp);
855         if(asRadius) c->other = true;
856         return hc;
857     }
858 
addDimRadial(const DRW_DimRadial * data)859     virtual void addDimRadial(const DRW_DimRadial *data) {
860         if(data->space != DRW::ModelSpace) return;
861         if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
862 
863         Vector cp = toVector(data->getCenterPoint());
864         Vector dp = toVector(data->getDiameterPoint());
865         Vector tp = toVector(data->getTextPoint());
866         double actual = -1.0;
867         if(data->hasActualMeasurement()) {
868             actual = data->getActualMeasurement();
869         }
870 
871         createDiametric(cp, cp.Minus(dp).Magnitude(), tp, actual, /*asRadius=*/true);
872     }
873 
addDimDiametric(const DRW_DimDiametric * data)874     virtual void addDimDiametric(const DRW_DimDiametric *data) {
875         if(data->space != DRW::ModelSpace) return;
876         if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
877 
878         Vector dp1 = toVector(data->getDiameter1Point());
879         Vector dp2 = toVector(data->getDiameter2Point());
880 
881         Vector cp = dp1.Plus(dp2).ScaledBy(0.5);
882         Vector tp = toVector(data->getTextPoint());
883         double actual = -1.0;
884         if(data->hasActualMeasurement()) {
885             actual = data->getActualMeasurement();
886         }
887 
888         createDiametric(cp, cp.Minus(dp1).Magnitude(), tp, actual, /*asRadius=*/false);
889     }
890 
addDimAngular3P(const DRW_DimAngular3p * data)891     virtual void addDimAngular3P(const DRW_DimAngular3p *data) {
892         if(data->space != DRW::ModelSpace) return;
893         if(addPendingBlockEntity<DRW_DimAngular3p>(*data)) return;
894 
895         DRW_DimAngular dim = *static_cast<const DRW_Dimension *>(data);
896         dim.setFirstLine1(data->getVertexPoint());
897         dim.setFirstLine2(data->getFirstLine());
898         dim.setSecondLine1(data->getVertexPoint());
899         dim.setSecondLine2(data->getSecondLine());
900         addDimAngular(&dim);
901     }
902 };
903 
ImportDxf(const std::string & filename)904 void ImportDxf(const std::string &filename) {
905     DxfReadInterface interface;
906     interface.clearBlockTransform();
907 
908     std::string data;
909     if(!ReadFile(filename, &data)) {
910         Error("Couldn't read from '%s'", filename.c_str());
911         return;
912     }
913 
914     SS.UndoRemember();
915     std::stringstream stream(data);
916     if(!dxfRW().read(stream, &interface, /*ext=*/false)) {
917         Error("Corrupted DXF file.");
918     }
919 
920     if(interface.unknownEntities > 0) {
921         Message(ssprintf("%u DXF entities of unknown type were ignored.",
922                          interface.unknownEntities).c_str());
923     }
924 }
925 
ImportDwg(const std::string & filename)926 void ImportDwg(const std::string &filename) {
927     DxfReadInterface interface;
928     interface.clearBlockTransform();
929 
930     std::string data;
931     if(!ReadFile(filename, &data)) {
932         Error("Couldn't read from '%s'", filename.c_str());
933         return;
934     }
935 
936     SS.UndoRemember();
937     std::stringstream stream(data);
938     if(!dwgR().read(stream, &interface, /*ext=*/false)) {
939         Error("Corrupted DWG file.");
940     }
941 
942     if(interface.unknownEntities > 0) {
943         Message(ssprintf("%u DWG entities of unknown type were ignored.",
944                          interface.unknownEntities).c_str());
945     }
946 }
947 
948 }
949