Bazaar/DXF : provide a GetBoundingBox() function and zoom to drawing extents upon saving

git-svn-id: svn://ultimatepp.org/upp/trunk@5276 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
micio 2012-08-12 10:37:12 +00:00
parent 2539ab8e37
commit ebcd401c53
15 changed files with 198 additions and 120 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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; }

View file

@ -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;
}

View file

@ -73,6 +73,9 @@ class DXFEntities : public Pte<DXFEntities>
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

View file

@ -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

View file

@ -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));
}

View file

@ -25,6 +25,9 @@ class DXFLine : public DXFEntity
Pointf const &GetP2(void) const { return p2; }
Vector<Pointf> GetPoints(void) const;
DXFLine &SetPoints(Pointf const &p1, Pointf const &p2);
// gets bounding box of element
virtual Rectf GetBoundingBox(void) const;
};
#endif

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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