diff --git a/bazaar/DXF/BlockRef.cpp b/bazaar/DXF/BlockRef.cpp index a599577e7..1cc59b296 100644 --- a/bazaar/DXF/BlockRef.cpp +++ b/bazaar/DXF/BlockRef.cpp @@ -74,3 +74,36 @@ DXFBlockRef &DXFBlockRef::Set(String const &n, Pointf const &p, double s, double angle = R(a); return *this; } + +// gets bounding box of element +Rectf DXFBlockRef::GetBoundingBox(void) const +{ + Rectf res; + + // get dxf object + DXF const &dxf = GetDXF(); + + // get block data by name; if none, returns + if(!dxf.HasBlock(name)) + return Rectf(1e99, -1e99, -1e99, 1e99); + DXFBlock const &blk = dxf.GetBlock(name); + + // get the bounding box of included entities + Rectf bbe = blk.entities.GetBoundingBox(); + + // now we need to apply transformation to bbox points + TransMatrix M; + M.Translate(insertPoint).Rotate(angle).Scale(scale); + Pointf bl = M(bbe.BottomLeft()); + Pointf tl = M(bbe.TopLeft()); + Pointf br = M(bbe.BottomRight()); + Pointf tr = M(bbe.TopRight()); + + // and now get the minimum and maximum coordinate values + res.left = min(min(bl.x, tl.x), min(br.x, tr.x)); + res.right = max(max(bl.x, tl.x), max(br.x, tr.x)); + res.bottom = min(min(bl.y, tl.y), min(br.y, tr.y)); + res.top = max(max(bl.y, tl.y), max(br.y, tr.y)); + + return res; +} diff --git a/bazaar/DXF/BlockRef.h b/bazaar/DXF/BlockRef.h index 9c8544845..59db529d9 100644 --- a/bazaar/DXF/BlockRef.h +++ b/bazaar/DXF/BlockRef.h @@ -40,6 +40,9 @@ class DXFBlockRef : public DXFEntity DXFBlockRef &SetAngle(double a); DXFBlockRef &Set(Pointf const &p, double s, double a); DXFBlockRef &Set(String const &n, Pointf const &p, double s, double a); + + // gets bounding box of element + virtual Rectf GetBoundingBox(void) const; }; #endif diff --git a/bazaar/DXF/Circle.cpp b/bazaar/DXF/Circle.cpp index 75e41d408..da1cdfe16 100644 --- a/bazaar/DXF/Circle.cpp +++ b/bazaar/DXF/Circle.cpp @@ -40,3 +40,9 @@ DXFCircle &DXFCircle::Set(Pointf c, double d) radius = S(d); return *this; } + +// gets bounding box of element +Rectf DXFCircle::GetBoundingBox(void) const +{ + return Rectf(center.x - radius, center.y + radius, center.x + radius, center.y - radius); +} diff --git a/bazaar/DXF/Circle.h b/bazaar/DXF/Circle.h index 4b9e24852..cc29853b0 100644 --- a/bazaar/DXF/Circle.h +++ b/bazaar/DXF/Circle.h @@ -26,6 +26,9 @@ class DXFCircle : public DXFEntity DXFCircle &Set(Pointf const &c); DXFCircle &Set(double d); DXFCircle &Set(Pointf c, double d); + + // gets bounding box of element + virtual Rectf GetBoundingBox(void) const; }; #endif diff --git a/bazaar/DXF/DXF.cpp b/bazaar/DXF/DXF.cpp index fa48cef5e..1059366a7 100644 --- a/bazaar/DXF/DXF.cpp +++ b/bazaar/DXF/DXF.cpp @@ -20,11 +20,26 @@ DXF::DXF() : nextHandle(0x1000), blocks(this), objects(this), tables(this) // do NOT scale insertions by default scaleInsertions = false; + + // initialize view center and height + viewCenter = Pointf(100, 100); + viewHeight = 50; } // write drawing to file bool DXF::Write(Stream &s) { + // if we've got some entities, calculate drawing bounding box + // and zoom to fit it before saving + if(entities.entities.GetCount()) + { + Rectf bb = entities.GetBoundingBox(); + viewCenter = Pointf( (bb.left + bb.right) / 2, (bb.top + bb.bottom) / 2); + viewHeight = bb.top - bb.bottom; + double viewWidth = bb.right - bb.left; + if(viewHeight * 2.128571428571428 < viewWidth) + viewHeight = viewWidth / 2.128571428571428; + } header.Write(s); tables.Write(s); blocks.Write(s); @@ -69,3 +84,11 @@ DXFBlock &DXF::GetBlock(String const &name) return blocks.blocks[idx]; return *(DXFBlock *)NULL; } + +DXFBlock const &DXF::GetBlock(String const &name) const +{ + int idx = blocks.blocks.Find(name); + if(idx >= 0) + return blocks.blocks[idx]; + return *(DXFBlock *)NULL; +} diff --git a/bazaar/DXF/DXF.h b/bazaar/DXF/DXF.h index 69a74ce68..2324e95e6 100644 --- a/bazaar/DXF/DXF.h +++ b/bazaar/DXF/DXF.h @@ -50,6 +50,11 @@ class DXF : public DXFBlock // flag for scaling insertions with transform matrix bool scaleInsertions; + + // view center point and height + // calculated from GetBoundingBox functions + Pointf viewCenter; + double viewHeight; protected: @@ -77,6 +82,7 @@ class DXF : public DXFBlock // gets a block by name DXFBlock &GetBlock(String const &name); + DXFBlock const &GetBlock(String const &name) const; // sets insertion scale option DXF &SetScaleInsertions(bool i = true) { scaleInsertions = i; return *this; } diff --git a/bazaar/DXF/Entities.cpp b/bazaar/DXF/Entities.cpp index 6127e1bbb..4881fcad7 100644 --- a/bazaar/DXF/Entities.cpp +++ b/bazaar/DXF/Entities.cpp @@ -133,3 +133,18 @@ DXFBlockRef &DXFEntities::InsertBlock(String const &name, Pointf const &insp, do entities.Add(blk); return *blk; } + +// get bounding box of entities +Rectf DXFEntities::GetBoundingBox(void) const +{ + Rectf bb(1e99, -1e99, -1e99, 1e99); + for(int i = 0; i < entities.GetCount(); i++) + { + Rectf r = entities[i].GetBoundingBox(); + bb.left = min(bb.left, r.left); + bb.top = max(bb.top, r.top); + bb.right = max(bb.right, r.right); + bb.bottom = min(bb.bottom, r.bottom); + } + return bb; +} diff --git a/bazaar/DXF/Entities.h b/bazaar/DXF/Entities.h index 343604e91..6c873036b 100644 --- a/bazaar/DXF/Entities.h +++ b/bazaar/DXF/Entities.h @@ -73,6 +73,9 @@ class DXFEntities : public Pte DXFBlockRef &InsertBlock(String const &name, Pointf const &insp, double scale, double angle); DXFBlockRef &InsertBlock(String const &name, Pointf const &insp, double scale) { return InsertBlock(name, insp, scale, 0.0); } DXFBlockRef &InsertBlock(String const &name, Pointf const &insp) { return InsertBlock(name, insp, 1.0, 0.0); } + + // get bounding box of entities + Rectf GetBoundingBox(void) const; }; #endif diff --git a/bazaar/DXF/Entity.h b/bazaar/DXF/Entity.h index 588f90562..05598c068 100644 --- a/bazaar/DXF/Entity.h +++ b/bazaar/DXF/Entity.h @@ -71,6 +71,9 @@ class DXFEntity // gets/sets linetype DXFEntity *SetLineType(String const &l); String const &GetLineType(void) const { return lineType; } + + // gets bounding box of element + virtual Rectf GetBoundingBox(void) const { return Rectf(1e99, -1e99, -1e99, 1e99); } }; #endif diff --git a/bazaar/DXF/Line.cpp b/bazaar/DXF/Line.cpp index f53addf2a..0d236a89d 100644 --- a/bazaar/DXF/Line.cpp +++ b/bazaar/DXF/Line.cpp @@ -30,3 +30,9 @@ bool DXFLine::Write(Stream &s) s << "11\n" << p2.x << "\n21\n" << p2.y << "\n31\n" << 0.0 << "\n"; return true; } + +// gets bounding box of element +Rectf DXFLine::GetBoundingBox(void) const +{ + return Rectf(min(p1.x, p2.x), max(p1.y, p2.y), max(p1.x, p2.x), min(p1.y, p2.y)); +} diff --git a/bazaar/DXF/Line.h b/bazaar/DXF/Line.h index 45de1f344..0916c19e2 100644 --- a/bazaar/DXF/Line.h +++ b/bazaar/DXF/Line.h @@ -25,6 +25,9 @@ class DXFLine : public DXFEntity Pointf const &GetP2(void) const { return p2; } Vector GetPoints(void) const; DXFLine &SetPoints(Pointf const &p1, Pointf const &p2); + + // gets bounding box of element + virtual Rectf GetBoundingBox(void) const; }; #endif diff --git a/bazaar/DXF/LwPolyline.cpp b/bazaar/DXF/LwPolyline.cpp index b456e96d1..13d2a076c 100644 --- a/bazaar/DXF/LwPolyline.cpp +++ b/bazaar/DXF/LwPolyline.cpp @@ -320,3 +320,77 @@ DXFLwPolyline &DXFLwPolyline::SetWidth(double w) width = w; return *this; } + +// normalizes an angle in range 0..2 * M_PI +static double norm(double a) +{ + a = fmod(a, 2 * M_PI); + if(a < 0) + a = 2 * M_PI + a; + return a; +} + +// gets bounding box of element +Rectf DXFLwPolyline::GetBoundingBox(void) const +{ + Rectf r(1e99, -1e99, -1e99, 1e99); + for(int i = 0; i < vertices.GetCount(); i++) + { + Pointf const &p = vertices[i]; + + // first, check the vertex + r.left = min(r.left, p.x); + r.top = max(r.top, p.y); + r.right = max(r.right, p.x); + r.bottom = min(r.bottom, p.y); + + // get the bulge at vertex; if not zero, we need to check + // the arc for extents + if(bulges[i] != 0) + { + Pointf const &p2 = (i < vertices.GetCount() - 1 ? vertices[i + 1] : vertices[0]); + + // skip me if points are the same OR second point is first one and pline + // is not closed + if(&p != &p2 && (closed || &p2 != &vertices[0])) + { + // first, we need radius and center + // here quite complicated, but it should be a simpler way... + double chord = sqrt(sqr(p2.x - p.x) + sqr(p2.y - p.y)); + double sagitta = fabs(chord / 2 * bulges[i]); + double radius = (sqr(chord / 2) + sqr(sagitta)) / 2 / sagitta; + double a12 = atan2(p2.y - p.y, p2.x - p.x); + double xm = (p2.x + p.x) / 2; + double ym = (p2.y - p.y) / 2; + double apot = radius - sagitta; + double ac = bulges[i] > 0 ? a12 + M_PI / 2 : a12 - M_PI / 2; + double xc = xm + apot * cos(ac); + double yc = ym + apot * sin(ac); + + // now we check how many quadrants does the arc span + // first, we take the angle of arc endpoints and adjust it + // depending on arc direction + double a1 = atan2(p.y - yc, p.x - xc); + double a2 = atan2(p2.y - yc, p2.x - xc); + if(bulges[i] < 0) + { + double t = a1; + a1 = a2; + a2 = t; + } + + // check if crossing quadrants + bool cross0 = norm(a1) > norm(a2); + bool cross90 = norm(a1 - M_PI / 2) > norm(a2 - M_PI / 2); + bool cross180 = norm(a1 - M_PI) > norm(a2 - M_PI); + bool cross270 = norm(a1 - 3 * M_PI / 2) > norm(a2 - 3 * M_PI / 2); + + if(cross0) r.right = xc + radius; + if(cross90) r.top = yc + radius; + if(cross180) r.left = xc - radius; + if(cross270) r.bottom = yc - radius; + } + } + } + return r; +} diff --git a/bazaar/DXF/LwPolyline.h b/bazaar/DXF/LwPolyline.h index fe8c2a87e..c600ac390 100644 --- a/bazaar/DXF/LwPolyline.h +++ b/bazaar/DXF/LwPolyline.h @@ -71,6 +71,9 @@ class DXFLwPolyline : public DXFEntity // get/set global width double GetWidth(void) { return width; } DXFLwPolyline &SetWidth(double w); + + // gets bounding box of element + virtual Rectf GetBoundingBox(void) const; }; #endif diff --git a/bazaar/DXF/Tables.cpp b/bazaar/DXF/Tables.cpp index b09103dfe..e6772922a 100644 --- a/bazaar/DXF/Tables.cpp +++ b/bazaar/DXF/Tables.cpp @@ -153,6 +153,23 @@ bool DXFTables::Write(Stream &s) { s << "0\nSECTION\n2\nTABLES\n"; + // active viweport -- partially hardwired, we just need to set + // zoom factor and view center point to fit created drawing + s << "0\nTABLE\n2\nVPORT\n"; + s << "5\n8\n330\n0\n100\nAcDbSymbolTable\n70\n2\n"; + s << "0\nVPORT\n5\n676\n330\n8\n100\nAcDbSymbolTableRecord\n100\nAcDbViewportTableRecord\n"; + s << "2\n*Active\n70\n0\n"; + s << "10\n0.0\n20\n0.0\n11\n1.0\n21\n1.0\n"; + s << "12\n" << dxf->viewCenter.x << "\n22\n" << dxf->viewCenter.y << "\n"; + s << "13\n0.0\n23\n0.0\n14\n10.0\n24\n10.0\n15\n10.0\n25\n10.0\n16\n0.0\n26\n0.0\n36\n1.0\n"; + s << "17\n0.0\n27\n0.0\n37\n0.0\n"; + s << "40\n" << dxf->viewHeight << "\n41\n2.128571428571428\n"; + s << "42\n50.0\n43\n0.0\n44\n0.0\n50\n0.0\n51\n0.0\n"; + s << "71\n0\n72\n1000\n73\n1\n74\n1\n75\n0\n76\n0\n77\n0\n78\n0\n"; + s << "281\n0\n65\n1\n110\n0.0\n120\n0.0\n130\n0.0\n111\n1.0\n121\n0.0\n131\n0.0\n"; + s << "112\n0.0\n122\n1.0\n132\n0.0\n79\n0\n146\n0.0\n"; + s << "0\nENDTAB\n"; + // hardwired tables from template s << (const char *)dxf_tbl; diff --git a/bazaar/DXF/dxf_tbl.txt b/bazaar/DXF/dxf_tbl.txt index 5334f89da..2963a03cb 100644 --- a/bazaar/DXF/dxf_tbl.txt +++ b/bazaar/DXF/dxf_tbl.txt @@ -1,126 +1,6 @@ 0 TABLE 2 -VPORT - 5 -8 -330 -0 -100 -AcDbSymbolTable - 70 - 2 - 0 -VPORT - 5 -676 -330 -8 -100 -AcDbSymbolTableRecord -100 -AcDbViewportTableRecord - 2 -*Active - 70 - 0 - 10 -0.0 - 20 -0.0 - 11 -1.0 - 21 -1.0 - 12 -1267.482156174376 - 22 -595.4614156523915 - 13 -0.0 - 23 -0.0 - 14 -10.0 - 24 -10.0 - 15 -10.0 - 25 -10.0 - 16 -0.0 - 26 -0.0 - 36 -1.0 - 17 -0.0 - 27 -0.0 - 37 -0.0 - 40 -1190.922831304783 - 41 -2.128571428571428 - 42 -50.0 - 43 -0.0 - 44 -0.0 - 50 -0.0 - 51 -0.0 - 71 - 0 - 72 - 1000 - 73 - 1 - 74 - 1 - 75 - 0 - 76 - 0 - 77 - 0 - 78 - 0 -281 - 0 - 65 - 1 -110 -0.0 -120 -0.0 -130 -0.0 -111 -1.0 -121 -0.0 -131 -0.0 -112 -0.0 -122 -1.0 -132 -0.0 - 79 - 0 -146 -0.0 - 0 -ENDTAB - 0 -TABLE - 2 STYLE 5 3