Make the EPS, PDF, and SVG targets output filled contours. This

also means that closed contours will get output as a single path
now, vs. one open path per Bezier segment before.

I've simplified the 2d/3d wireframe export targets somewhat; they
now support only Beziers, without an additional special case for
line segments. The performance penalty for that should not be worth
caring about, since that's infrequent.

And fix a memory leak in FindOuterFacesFrom(), fix ugly output of
filled triangles in PDF (because the line join style did bad things
on long skinny triangles), fix non-zero Z coordinates for exported
views or sections in DXF or STEP.

[git-p4: depot-paths = "//depot/solvespace/": change = 2061]
solver
Jonathan Westhues 2009-10-30 02:38:34 -08:00
parent 2f115ec950
commit 3515748334
6 changed files with 382 additions and 272 deletions

View File

@ -181,17 +181,27 @@ void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) {
beziers.Clear();
}
void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
VectorFileWriter *out)
{
sbl->ScaleSelfBy(1.0/SS.exportScale);
SBezierLoopSetSet sblss;
ZERO(&sblss);
SEdge *se;
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
se->a = (se->a).ScaledBy(1.0/SS.exportScale);
se->b = (se->b).ScaledBy(1.0/SS.exportScale);
SBezier sb = SBezier::From(
(se->a).ScaledBy(1.0 / SS.exportScale),
(se->b).ScaledBy(1.0 / SS.exportScale));
sblss.AddOpenPath(&sb);
}
out->Output(sel, sbl, NULL);
sbl->ScaleSelfBy(1.0/SS.exportScale);
SBezier *sb;
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
sblss.AddOpenPath(sb);
}
out->Output(&sblss, NULL);
sblss.Clear();
}
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
@ -327,9 +337,49 @@ void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
sel = &hlrd;
}
// Now write the lines and triangles to the output file
out->Output(sel, sbl, &sms);
// We kept the line segments and Beziers separate until now; but put them
// all together, and also project everything into the xy plane, since not
// all export targets ignore the z component of the points.
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
SBezier sb = SBezier::From(e->a, e->b);
sb.auxA = e->auxA;
sbl->l.Add(&sb);
}
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(int i = 0; i <= b->deg; i++) {
b->ctrl[i].z = 0;
}
}
// If possible, then we will assemble these output curves into loops. They
// will then get exported as closed paths.
SBezierLoopSetSet sblss;
ZERO(&sblss);
SBezierList leftovers;
ZERO(&leftovers);
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
Vector::From(1, 0, 0),
Vector::From(0, 1, 0));
SPolygon spxyz;
ZERO(&spxyz);
bool allClosed;
SEdge notClosedAt;
sbl->l.ClearTags();
sblss.FindOuterFacesFrom(sbl, &spxyz, &srf,
SS.ChordTolMm()*s,
&allClosed, &notClosedAt,
NULL, NULL,
&leftovers);
for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) {
sblss.AddOpenPath(b);
}
// Now write the lines and triangles to the output file
out->Output(&sblss, &sms);
leftovers.Clear();
spxyz.Clear();
sblss.Clear();
smp.Clear();
sms.Clear();
hlrd.Clear();
@ -379,24 +429,19 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
static void AddUnregMessageCallback(void *fndata, Vector a, Vector b)
{
SEdgeList *sel = (SEdgeList *)fndata;
sel->AddEdge(a, b, Style::SELECTED);
SBezierLoopSetSet *sblss = (SBezierLoopSetSet *)fndata;
SBezier sb = SBezier::From(a, b);
sb.auxA = Style::SELECTED;
sblss->AddOpenPath(&sb);
}
void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
void VectorFileWriter::Output(SBezierLoopSetSet *sblss, SMesh *sm) {
STriangle *tr;
SEdge *e;
SBezier *b;
// First calculate the bounding box.
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
if(sel) {
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
(e->a).MakeMaxMin(&ptMax, &ptMin);
(e->b).MakeMaxMin(&ptMax, &ptMin);
}
}
if(sm) {
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
(tr->a).MakeMaxMin(&ptMax, &ptMin);
@ -404,11 +449,16 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
(tr->c).MakeMaxMin(&ptMax, &ptMin);
}
}
if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
int i;
for(i = 0; i <= b->deg; i++) {
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin);
if(sblss) {
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
SBezierLoop *sbl;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(int i = 0; i <= b->deg; i++) {
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin);
}
}
}
}
}
@ -431,7 +481,7 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
// If the demo period has expired and there's no license, then print
// a message in any exported file.
if((!SS.license.licensed) && (SS.license.trialDaysRemaining <= 0)) {
if((!SS.license.licensed) && (SS.license.trialDaysRemaining <= 0) && sblss){
char *str =
"eval / nonprofit use only -- buy at http://solvespace.com/";
double aspect = (glxStrWidth(str, 1) / glxStrHeight(1));
@ -453,7 +503,7 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
str,
0.9 * th * SS.GW.scale,
t, u, v,
AddUnregMessageCallback, sel);
AddUnregMessageCallback, sblss);
if(w > h) {
ptMin.y -= th*3;
} else {
@ -467,41 +517,45 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
Triangle(tr);
}
}
if(sel) {
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
if(!Style::Exportable(e->auxA)) continue;
if(sblss) {
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
SBezierLoop *sbl;
sbl = sbls->l.First();
if(!sbl) continue;
b = sbl->l.First();
if(!b || !Style::Exportable(b->auxA)) continue;
DWORD rgb = Style::Color (e->auxA, true);
double w = Style::WidthMm(e->auxA)*s;
LineSegment(rgb, w, e->a, e->b);
}
}
if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
if(!Style::Exportable(b->auxA)) continue;
hStyle hs = { b->auxA };
Style *stl = Style::Get(hs);
double lineWidth = Style::WidthMm(b->auxA)*s;
DWORD strokeRgb = Style::Color(b->auxA, true);
DWORD rgb = Style::Color (b->auxA, true);
double w = Style::WidthMm(b->auxA)*s;
Bezier(rgb, w, b);
StartPath(strokeRgb, lineWidth, stl->filled, stl->fillColor);
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
Bezier(b);
}
}
FinishPath(strokeRgb, lineWidth, stl->filled, stl->fillColor);
}
}
FinishAndCloseFile();
}
void VectorFileWriter::BezierAsPwl(DWORD rgb, double width, SBezier *sb) {
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
List<Vector> lv;
ZERO(&lv);
sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale);
int i;
for(i = 1; i < lv.n; i++) {
LineSegment(rgb, width, lv.elem[i-1], lv.elem[i]);
SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]);
Bezier(&sb);
}
lv.Clear();
}
void VectorFileWriter::BezierAsNonrationalCubic(DWORD rgb, double width,
SBezier *sb, int depth)
{
void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1);
// The curve is correct, and the first derivatives are correct, at the
// endpoints.
@ -529,12 +583,12 @@ void VectorFileWriter::BezierAsNonrationalCubic(DWORD rgb, double width,
}
if(closeEnough || depth > 3) {
Bezier(rgb, width, &bnr);
Bezier(&bnr);
} else {
SBezier bef, aft;
sb->SplitAt(0.5, &bef, &aft);
BezierAsNonrationalCubic(rgb, width, &bef, depth+1);
BezierAsNonrationalCubic(rgb, width, &aft, depth+1);
BezierAsNonrationalCubic(&bef, depth+1);
BezierAsNonrationalCubic(&aft, depth+1);
}
}

View File

@ -62,8 +62,23 @@ void DxfFileWriter::StartFile(void) {
"ENTITIES\r\n");
}
void DxfFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
void DxfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
}
void DxfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
}
void DxfFileWriter::Triangle(STriangle *tr) {
}
void DxfFileWriter::Bezier(SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->deg == 1) {
fprintf(f,
" 0\r\n"
"LINE\r\n"
" 8\r\n" // Layer code
@ -80,18 +95,10 @@ void DxfFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
"%.6f\r\n"
" 31\r\n" // zB
"%.6f\r\n",
0,
ptA.x, ptA.y, ptA.z,
ptB.x, ptB.y, ptB.z);
}
void DxfFileWriter::Triangle(STriangle *tr) {
}
void DxfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) {
0,
sb->ctrl[0].x, sb->ctrl[0].y, sb->ctrl[0].z,
sb->ctrl[1].x, sb->ctrl[1].y, sb->ctrl[1].z);
} else if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) {
double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x),
theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x),
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
@ -121,7 +128,7 @@ void DxfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
r,
theta0*180/PI, theta1*180/PI);
} else {
BezierAsPwl(rgb, w, sb);
BezierAsPwl(sb);
}
}
@ -137,17 +144,6 @@ void DxfFileWriter::FinishAndCloseFile(void) {
//-----------------------------------------------------------------------------
// Routines for EPS output
//-----------------------------------------------------------------------------
char *EpsFileWriter::StyleString(DWORD rgb, double w) {
static char ret[300];
sprintf(ret, " %.3f setlinewidth\r\n"
" %.3f %.3f %.3f setrgbcolor\r\n"
" 1 setlinejoin\r\n" // rounded
" 1 setlinecap\r\n", // rounded
MmToPts(w),
REDf(rgb), GREENf(rgb), BLUEf(rgb));
return ret;
}
void EpsFileWriter::StartFile(void) {
fprintf(f,
"%%!PS-Adobe-2.0\r\n"
@ -167,16 +163,35 @@ void EpsFileWriter::StartFile(void) {
MmToPts(ptMax.y - ptMin.y));
}
void EpsFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f lineto\r\n"
"%s"
"stroke\r\n",
MmToPts(ptA.x - ptMin.x), MmToPts(ptA.y - ptMin.y),
MmToPts(ptB.x - ptMin.x), MmToPts(ptB.y - ptMin.y),
StyleString(rgb, w));
void EpsFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
fprintf(f, "newpath\r\n");
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
}
void EpsFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
fprintf(f, " %.3f setlinewidth\r\n"
" %.3f %.3f %.3f setrgbcolor\r\n"
" 1 setlinejoin\r\n" // rounded
" 1 setlinecap\r\n" // rounded
" gsave stroke grestore\r\n",
MmToPts(lineWidth),
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
if(filled) {
fprintf(f, " %.3f %.3f %.3f setrgbcolor\r\n"
" gsave fill grestore\r\n",
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
}
}
void EpsFileWriter::MaybeMoveTo(Vector st, Vector fi) {
if(!prevPt.Equals(st)) {
fprintf(f, " %.3f %.3f moveto\r\n",
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
}
prevPt = fi;
}
void EpsFileWriter::Triangle(STriangle *tr) {
@ -187,7 +202,7 @@ void EpsFileWriter::Triangle(STriangle *tr) {
" %.3f %.3f lineto\r\n"
" %.3f %.3f lineto\r\n"
" closepath\r\n"
"fill\r\n",
"gsave fill grestore\r\n",
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
@ -196,25 +211,22 @@ void EpsFileWriter::Triangle(STriangle *tr) {
// same issue with cracks, stroke it to avoid them
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f,
"%.3f %.3f %.3f setrgbcolor\r\n"
"1 setlinejoin\r\n"
"1 setlinecap\r\n"
"%.3f setlinewidth\r\n"
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f lineto\r\n"
" %.3f %.3f lineto\r\n"
" closepath\r\n"
"stroke\r\n",
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
MmToPts(sw),
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
"gsave stroke grestore\r\n",
MmToPts(sw));
}
void EpsFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
void EpsFileWriter::Bezier(SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
if(sb->deg == 1) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
fprintf(f, " %.3f %.3f lineto\r\n",
MmToPts(sb->ctrl[1].x - ptMin.x),
MmToPts(sb->ctrl[1].y - ptMin.y));
} else if(sb->IsCircle(n, &c, &r)) {
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
theta1 = atan2(p1.y - c.y, p1.x - c.x),
@ -223,31 +235,21 @@ void EpsFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
SWAP(double, theta0, theta1);
SWAP(Vector, p0, p1);
}
MaybeMoveTo(p0, p1);
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f %.3f %.3f %.3f arc\r\n"
"%s"
"stroke\r\n",
MmToPts(p0.x - ptMin.x), MmToPts(p0.y - ptMin.y),
" %.3f %.3f %.3f %.3f %.3f arc\r\n",
MmToPts(c.x - ptMin.x), MmToPts(c.y - ptMin.y),
MmToPts(r),
theta0*180/PI, theta1*180/PI,
StyleString(rgb, w));
theta0*180/PI, theta1*180/PI);
} else if(sb->deg == 3 && !sb->IsRational()) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n"
"%s"
"stroke\r\n",
MmToPts(sb->ctrl[0].x - ptMin.x), MmToPts(sb->ctrl[0].y - ptMin.y),
" %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n",
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y),
StyleString(rgb, w));
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
} else {
BezierAsNonrationalCubic(rgb, w, sb);
BezierAsNonrationalCubic(sb);
}
}
@ -263,16 +265,6 @@ void EpsFileWriter::FinishAndCloseFile(void) {
// Routines for PDF output, some extra complexity because we have to generate
// a correct xref table.
//-----------------------------------------------------------------------------
char *PdfFileWriter::StyleString(DWORD rgb, double w) {
static char ret[300];
sprintf(ret, "1 J 1 j " // round endcaps and joins
"%.3f w "
"%.3f %.3f %.3f RG\r\n",
MmToPts(w),
REDf(rgb), GREENf(rgb), BLUEf(rgb));
return ret;
}
void PdfFileWriter::StartFile(void) {
fprintf(f,
"%%PDF-1.1\r\n"
@ -392,21 +384,44 @@ void PdfFileWriter::FinishAndCloseFile(void) {
}
void PdfFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
"%s"
"%.3f %.3f m\r\n"
"%.3f %.3f l\r\n"
"S\r\n",
StyleString(rgb, w),
MmToPts(ptA.x - ptMin.x), MmToPts(ptA.y - ptMin.y),
MmToPts(ptB.x - ptMin.x), MmToPts(ptB.y - ptMin.y));
void PdfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
fprintf(f, "1 J 1 j " // round endcaps and joins
"%.3f w "
"%.3f %.3f %.3f RG\r\n",
MmToPts(lineWidth),
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
if(filled) {
fprintf(f, "%.3f %.3f %.3f rg\r\n",
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
}
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
}
void PdfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
if(filled) {
fprintf(f, "b\r\n");
} else {
fprintf(f, "S\r\n");
}
}
void PdfFileWriter::MaybeMoveTo(Vector st, Vector fi) {
if(!prevPt.Equals(st)) {
fprintf(f, "%.3f %.3f m\r\n",
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
}
prevPt = fi;
}
void PdfFileWriter::Triangle(STriangle *tr) {
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f,
"1 J 1 j\r\n"
"%.3f %.3f %.3f RG\r\n"
"%.3f %.3f %.3f rg\r\n"
"%.3f w\r\n"
@ -422,35 +437,27 @@ void PdfFileWriter::Triangle(STriangle *tr) {
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
}
void PdfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
if(sb->deg == 3 && !sb->IsRational()) {
void PdfFileWriter::Bezier(SBezier *sb) {
if(sb->deg == 1) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
fprintf(f,
"%s"
"%.3f %.3f m\r\n"
"%.3f %.3f %.3f %.3f %.3f %.3f c\r\n"
"S\r\n",
StyleString(rgb, w),
MmToPts(sb->ctrl[0].x - ptMin.x), MmToPts(sb->ctrl[0].y - ptMin.y),
"%.3f %.3f l\r\n",
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y));
} else if(sb->deg == 3 && !sb->IsRational()) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
fprintf(f,
"%.3f %.3f %.3f %.3f %.3f %.3f c\r\n",
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
} else {
BezierAsNonrationalCubic(rgb, w, sb);
BezierAsNonrationalCubic(sb);
}
}
//-----------------------------------------------------------------------------
// Routines for SVG output
//-----------------------------------------------------------------------------
char *SvgFileWriter::StyleString(DWORD rgb, double w) {
static char ret[200];
sprintf(ret, "stroke-width='%.3f' stroke='#%02x%02x%02x' "
"style='fill: none;' "
"stroke-linecap='round' stroke-linejoin='round' ",
w, RED(rgb), GREEN(rgb), BLUE(rgb));
return ret;
}
void SvgFileWriter::StartFile(void) {
fprintf(f,
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
@ -467,13 +474,35 @@ void SvgFileWriter::StartFile(void) {
// A little bit of extra space for the stroke width.
}
void SvgFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
void SvgFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
fprintf(f, "<path d='");
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
}
void SvgFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
char fill[100];
if(filled) {
sprintf(fill, "#%02x%02x%02x",
RED(fillRgb), GREEN(fillRgb), BLUE(fillRgb));
} else {
strcpy(fill, "none");
}
fprintf(f, "' stroke-width='%.3f' stroke='#%02x%02x%02x' "
"stroke-linecap='round' stroke-linejoin='round' "
"fill='%s' />\r\n",
lineWidth, RED(strokeRgb), GREEN(strokeRgb), BLUE(strokeRgb),
fill);
}
void SvgFileWriter::MaybeMoveTo(Vector st, Vector fi) {
// SVG uses a coordinate system with the origin at top left, +y down
fprintf(f,
"<polyline points='%.3f,%.3f %.3f,%.3f' %s />\r\n",
(ptA.x - ptMin.x), (ptMax.y - ptA.y),
(ptB.x - ptMin.x), (ptMax.y - ptB.y),
StyleString(rgb, w));
if(!prevPt.Equals(st)) {
fprintf(f, "M%.3f %.3f ", (st.x - ptMin.x), (ptMax.y - st.y));
}
prevPt = fi;
}
void SvgFileWriter::Triangle(STriangle *tr) {
@ -493,10 +522,14 @@ void SvgFileWriter::Triangle(STriangle *tr) {
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
}
void SvgFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
void SvgFileWriter::Bezier(SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
if(sb->deg == 1) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
fprintf(f, "L%.3f,%.3f ",
(sb->ctrl[1].x - ptMin.x), (ptMax.y - sb->ctrl[1].y));
} else if(sb->IsCircle(n, &c, &r)) {
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
theta1 = atan2(p1.y - c.y, p1.x - c.x),
@ -508,36 +541,25 @@ void SvgFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
if(dtheta < 0) {
SWAP(Vector, p0, p1);
}
fprintf(f,
"<path d='M%.3f,%.3f "
"A%.3f,%.3f 0 0,0 %.3f,%.3f' %s />\r\n",
p0.x - ptMin.x, ptMax.y - p0.y,
r, r,
p1.x - ptMin.x, ptMax.y - p1.y,
StyleString(rgb, w));
MaybeMoveTo(p0, p1);
fprintf(f, "A%.3f,%.3f 0 0,0 %.3f,%.3f ",
r, r,
p1.x - ptMin.x, ptMax.y - p1.y);
} else if(!sb->IsRational()) {
if(sb->deg == 1) {
LineSegment(rgb, w, sb->ctrl[0], sb->ctrl[1]);
} else if(sb->deg == 2) {
fprintf(f,
"<path d='M%.3f,%.3f "
"Q%.3f,%.3f %.3f,%.3f' %s />\r\n",
sb->ctrl[0].x - ptMin.x, ptMax.y - sb->ctrl[0].y,
if(sb->deg == 2) {
MaybeMoveTo(sb->ctrl[0], sb->ctrl[2]);
fprintf(f, "Q%.3f,%.3f %.3f,%.3f ",
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
StyleString(rgb, w));
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y);
} else if(sb->deg == 3) {
fprintf(f,
"<path d='M%.3f,%.3f "
"C%.3f,%.3f %.3f,%.3f %.3f,%.3f' %s />\r\n",
sb->ctrl[0].x - ptMin.x, ptMax.y - sb->ctrl[0].y,
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
fprintf(f, "C%.3f,%.3f %.3f,%.3f %.3f,%.3f ",
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y,
StyleString(rgb, w));
sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y);
}
} else {
BezierAsNonrationalCubic(rgb, w, sb);
BezierAsNonrationalCubic(sb);
}
}
@ -558,20 +580,29 @@ void HpglFileWriter::StartFile(void) {
fprintf(f, "SP1;\r\n");
}
void HpglFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f, "PU%d,%d;\r\n",
(int)MmToHpglUnits(ptA.x),
(int)MmToHpglUnits(ptA.y));
fprintf(f, "PD%d,%d;\r\n",
(int)MmToHpglUnits(ptB.x),
(int)MmToHpglUnits(ptB.y));
void HpglFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
}
void HpglFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
}
void HpglFileWriter::Triangle(STriangle *tr) {
}
void HpglFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
BezierAsPwl(rgb, w, sb);
void HpglFileWriter::Bezier(SBezier *sb) {
if(sb->deg == 1) {
fprintf(f, "PU%d,%d;\r\n",
(int)MmToHpglUnits(sb->ctrl[0].x),
(int)MmToHpglUnits(sb->ctrl[0].y));
fprintf(f, "PD%d,%d;\r\n",
(int)MmToHpglUnits(sb->ctrl[1].x),
(int)MmToHpglUnits(sb->ctrl[1].y));
} else {
BezierAsPwl(sb);
}
}
void HpglFileWriter::FinishAndCloseFile(void) {
@ -591,13 +622,16 @@ void Step2dFileWriter::StartFile(void) {
void Step2dFileWriter::Triangle(STriangle *tr) {
}
void Step2dFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB)
void Step2dFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
}
void Step2dFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb)
{
SBezier sb = SBezier::From(ptA, ptB);
Bezier(rgb, w, &sb);
}
void Step2dFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
void Step2dFileWriter::Bezier(SBezier *sb) {
int c = sfw.ExportCurve(sb);
sfw.curves.Add(&c);
}

View File

@ -467,7 +467,8 @@ void Group::Draw(void) {
glxVertex3v(polyError.notClosedAt.b);
glEnd();
glxColorRGB(Style::Color(Style::DRAW_ERROR));
glxWriteText("not closed contour!", DEFAULT_TEXT_HEIGHT,
glxWriteText("not closed contour, or not all same style!",
DEFAULT_TEXT_HEIGHT,
polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp,
NULL, NULL);
glEnable(GL_DEPTH_TEST);
@ -492,45 +493,6 @@ void Group::Draw(void) {
}
}
//-----------------------------------------------------------------------------
// Verify that the Beziers in this loop set all have the same auxA, and return
// that value. If they don't, then set allSame to be false, and indicate a
// point on the non-matching curve.
//-----------------------------------------------------------------------------
DWORD Group::GetLoopSetFillColor(SBezierLoopSet *sbls,
bool *allSame, Vector *errorAt)
{
bool first = true;
DWORD fillRgb = (DWORD)-1;
SBezierLoop *sbl;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
SBezier *sb;
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
DWORD thisRgb = (DWORD)-1;
if(sb->auxA != 0) {
hStyle hs = { sb->auxA };
Style *s = Style::Get(hs);
if(s->filled) {
thisRgb = s->fillColor;
}
}
if(first) {
fillRgb = thisRgb;
first = false;
} else {
if(fillRgb != thisRgb) {
*allSame = false;
*errorAt = sb->Start();
return fillRgb;
}
}
}
}
*allSame = true;
return fillRgb;
}
void Group::FillLoopSetAsPolygon(SBezierLoopSet *sbls) {
SPolygon sp;
ZERO(&sp);
@ -545,18 +507,16 @@ void Group::DrawFilledPaths(void) {
SBezierLoopSet *sbls;
SBezierLoopSetSet *sblss = &bezierLoops;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
bool allSame;
Vector errorPt;
DWORD fillRgb = GetLoopSetFillColor(sbls, &allSame, &errorPt);
if(allSame && fillRgb != (DWORD)-1) {
glxColorRGBa(fillRgb, 1);
if(sbls->l.n == 0 || sbls->l.elem[0].l.n == 0) continue;
// In an assembled loop, all the styles should be the same; so doesn't
// matter which one we grab.
SBezier *sb = &(sbls->l.elem[0].l.elem[0]);
hStyle hs = { sb->auxA };
Style *s = Style::Get(hs);
if(s->filled) {
// This is a filled loop, where the user specified a fill color.
glxColorRGBa(s->fillColor, 1);
FillLoopSetAsPolygon(sbls);
} else if(!allSame) {
glDisable(GL_DEPTH_TEST);
glxColorRGB(Style::Color(Style::DRAW_ERROR));
glxWriteText("not all same fill color!", DEFAULT_TEXT_HEIGHT,
errorPt, SS.GW.projRight, SS.GW.projUp, NULL, NULL);
glEnable(GL_DEPTH_TEST);
} else {
if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour &&
polyError.how == POLY_GOOD)

View File

@ -415,33 +415,42 @@ public:
static VectorFileWriter *ForFile(char *file);
void Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm);
void Output(SBezierLoopSetSet *sblss, SMesh *sm);
void BezierAsPwl(DWORD rgb, double width, SBezier *sb);
void BezierAsNonrationalCubic(DWORD rgb, double width,
SBezier *sb, int depth=0);
void BezierAsPwl(SBezier *sb);
void BezierAsNonrationalCubic(SBezier *sb, int depth=0);
virtual void Bezier(DWORD rgb, double width, SBezier *sb) = 0;
virtual void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB)
= 0;
virtual void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb) = 0;
virtual void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb) = 0;
virtual void Bezier(SBezier *sb) = 0;
virtual void Triangle(STriangle *tr) = 0;
virtual void StartFile(void) = 0;
virtual void FinishAndCloseFile(void) = 0;
};
class DxfFileWriter : public VectorFileWriter {
public:
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class EpsFileWriter : public VectorFileWriter {
public:
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
Vector prevPt;
void MaybeMoveTo(Vector s, Vector f);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
@ -449,37 +458,52 @@ class PdfFileWriter : public VectorFileWriter {
public:
DWORD xref[10];
DWORD bodyStart;
Vector prevPt;
void MaybeMoveTo(Vector s, Vector f);
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class SvgFileWriter : public VectorFileWriter {
public:
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
Vector prevPt;
void MaybeMoveTo(Vector s, Vector f);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class HpglFileWriter : public VectorFileWriter {
public:
static double MmToHpglUnits(double mm);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class Step2dFileWriter : public VectorFileWriter {
StepFileWriter sfw;
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void StartPath( DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void FinishPath(DWORD strokeRgb, double lineWidth,
bool filled, DWORD fillRgb);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void Bezier(SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};

View File

@ -402,6 +402,7 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
loop.l.Add(first);
Vector start = first->Start();
Vector hanging = first->Finish();
int auxA = first->auxA;
sbl->l.RemoveTagged();
@ -411,11 +412,11 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
for(i = 0; i < sbl->l.n; i++) {
SBezier *test = &(sbl->l.elem[i]);
if((test->Finish()).Equals(hanging)) {
if((test->Finish()).Equals(hanging) && test->auxA == auxA) {
test->Reverse();
// and let the next test catch it
}
if((test->Start()).Equals(hanging)) {
if((test->Start()).Equals(hanging) && test->auxA == auxA) {
test->tag = 1;
loop.l.Add(test);
hanging = test->Finish();
@ -690,11 +691,48 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
l.Add(&outerAndInners);
}
}
// If we have poorly-formed loops--for example, overlapping zero-area
// stuff--then we can end up with leftovers. We use this function to
// group stuff into closed paths for export when possible, so it's bad
// to screw up on that stuff. So just add them onto the open curve list.
// Very ugly, but better than losing curves.
for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]);
if(loop->tag == USED_LOOP) continue;
if(openContours) {
SBezier *sb;
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
openContours->l.Add(sb);
}
}
loop->Clear();
// but don't free the used loops, since we shallow-copied them to
// ourself
}
sbls.l.Clear(); // not sbls.Clear(), since that would deep-clear
spuv.Clear();
// Don't free sbls; we've shallow-copied all of its members to ourself.
}
void SBezierLoopSetSet::AddOpenPath(SBezier *sb) {
SBezierLoop sbl;
ZERO(&sbl);
sbl.l.Add(sb);
SBezierLoopSet sbls;
ZERO(&sbls);
sbls.l.Add(&sbl);
l.Add(&sbls);
}
void SBezierLoopSetSet::Clear(void) {
SBezierLoopSet *sbls;
for(sbls = l.First(); sbls; sbls = l.NextAfter(sbls)) {
sbls->Clear();
}
l.Clear();
}

View File

@ -144,7 +144,6 @@ public:
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
void MakePwlInto(SPolygon *sp);
int GetAuxA(bool *allSame, Vector *errorAt);
void Clear(void);
};
@ -157,6 +156,7 @@ public:
bool *allClosed, SEdge *notClosedAt,
bool *allCoplanar, Vector *notCoplanarAt,
SBezierList *openContours);
void AddOpenPath(SBezier *sb);
void Clear(void);
};