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(); beziers.Clear();
} }
void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl, void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
VectorFileWriter *out) VectorFileWriter *out)
{ {
sbl->ScaleSelfBy(1.0/SS.exportScale); SBezierLoopSetSet sblss;
ZERO(&sblss);
SEdge *se; SEdge *se;
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
se->a = (se->a).ScaledBy(1.0/SS.exportScale); SBezier sb = SBezier::From(
se->b = (se->b).ScaledBy(1.0/SS.exportScale); (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, void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
@ -327,9 +337,49 @@ void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
sel = &hlrd; sel = &hlrd;
} }
// Now write the lines and triangles to the output file // We kept the line segments and Beziers separate until now; but put them
out->Output(sel, sbl, &sms); // 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(); smp.Clear();
sms.Clear(); sms.Clear();
hlrd.Clear(); hlrd.Clear();
@ -379,24 +429,19 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
static void AddUnregMessageCallback(void *fndata, Vector a, Vector b) static void AddUnregMessageCallback(void *fndata, Vector a, Vector b)
{ {
SEdgeList *sel = (SEdgeList *)fndata; SBezierLoopSetSet *sblss = (SBezierLoopSetSet *)fndata;
sel->AddEdge(a, b, Style::SELECTED); 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; STriangle *tr;
SEdge *e;
SBezier *b; SBezier *b;
// First calculate the bounding box. // First calculate the bounding box.
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE); ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE); 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) { if(sm) {
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) { for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
(tr->a).MakeMaxMin(&ptMax, &ptMin); (tr->a).MakeMaxMin(&ptMax, &ptMin);
@ -404,11 +449,16 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
(tr->c).MakeMaxMin(&ptMax, &ptMin); (tr->c).MakeMaxMin(&ptMax, &ptMin);
} }
} }
if(sbl) { if(sblss) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { SBezierLoopSet *sbls;
int i; for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
for(i = 0; i <= b->deg; i++) { SBezierLoop *sbl;
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin); 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 // If the demo period has expired and there's no license, then print
// a message in any exported file. // 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 = char *str =
"eval / nonprofit use only -- buy at http://solvespace.com/"; "eval / nonprofit use only -- buy at http://solvespace.com/";
double aspect = (glxStrWidth(str, 1) / glxStrHeight(1)); double aspect = (glxStrWidth(str, 1) / glxStrHeight(1));
@ -453,7 +503,7 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
str, str,
0.9 * th * SS.GW.scale, 0.9 * th * SS.GW.scale,
t, u, v, t, u, v,
AddUnregMessageCallback, sel); AddUnregMessageCallback, sblss);
if(w > h) { if(w > h) {
ptMin.y -= th*3; ptMin.y -= th*3;
} else { } else {
@ -467,41 +517,45 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
Triangle(tr); Triangle(tr);
} }
} }
if(sel) { if(sblss) {
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) { SBezierLoopSet *sbls;
if(!Style::Exportable(e->auxA)) continue; 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); hStyle hs = { b->auxA };
double w = Style::WidthMm(e->auxA)*s; Style *stl = Style::Get(hs);
LineSegment(rgb, w, e->a, e->b); double lineWidth = Style::WidthMm(b->auxA)*s;
} DWORD strokeRgb = Style::Color(b->auxA, true);
}
if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
if(!Style::Exportable(b->auxA)) continue;
DWORD rgb = Style::Color (b->auxA, true); StartPath(strokeRgb, lineWidth, stl->filled, stl->fillColor);
double w = Style::WidthMm(b->auxA)*s; for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
Bezier(rgb, w, b); for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
Bezier(b);
}
}
FinishPath(strokeRgb, lineWidth, stl->filled, stl->fillColor);
} }
} }
FinishAndCloseFile(); FinishAndCloseFile();
} }
void VectorFileWriter::BezierAsPwl(DWORD rgb, double width, SBezier *sb) { void VectorFileWriter::BezierAsPwl(SBezier *sb) {
List<Vector> lv; List<Vector> lv;
ZERO(&lv); ZERO(&lv);
sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale); sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale);
int i; int i;
for(i = 1; i < lv.n; 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(); lv.Clear();
} }
void VectorFileWriter::BezierAsNonrationalCubic(DWORD rgb, double width, void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
SBezier *sb, int depth)
{
Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1); Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1);
// The curve is correct, and the first derivatives are correct, at the // The curve is correct, and the first derivatives are correct, at the
// endpoints. // endpoints.
@ -529,12 +583,12 @@ void VectorFileWriter::BezierAsNonrationalCubic(DWORD rgb, double width,
} }
if(closeEnough || depth > 3) { if(closeEnough || depth > 3) {
Bezier(rgb, width, &bnr); Bezier(&bnr);
} else { } else {
SBezier bef, aft; SBezier bef, aft;
sb->SplitAt(0.5, &bef, &aft); sb->SplitAt(0.5, &bef, &aft);
BezierAsNonrationalCubic(rgb, width, &bef, depth+1); BezierAsNonrationalCubic(&bef, depth+1);
BezierAsNonrationalCubic(rgb, width, &aft, depth+1); BezierAsNonrationalCubic(&aft, depth+1);
} }
} }

View File

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

View File

@ -467,7 +467,8 @@ void Group::Draw(void) {
glxVertex3v(polyError.notClosedAt.b); glxVertex3v(polyError.notClosedAt.b);
glEnd(); glEnd();
glxColorRGB(Style::Color(Style::DRAW_ERROR)); 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, polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp,
NULL, NULL); NULL, NULL);
glEnable(GL_DEPTH_TEST); 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) { void Group::FillLoopSetAsPolygon(SBezierLoopSet *sbls) {
SPolygon sp; SPolygon sp;
ZERO(&sp); ZERO(&sp);
@ -545,18 +507,16 @@ void Group::DrawFilledPaths(void) {
SBezierLoopSet *sbls; SBezierLoopSet *sbls;
SBezierLoopSetSet *sblss = &bezierLoops; SBezierLoopSetSet *sblss = &bezierLoops;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
bool allSame; if(sbls->l.n == 0 || sbls->l.elem[0].l.n == 0) continue;
Vector errorPt; // In an assembled loop, all the styles should be the same; so doesn't
DWORD fillRgb = GetLoopSetFillColor(sbls, &allSame, &errorPt); // matter which one we grab.
if(allSame && fillRgb != (DWORD)-1) { SBezier *sb = &(sbls->l.elem[0].l.elem[0]);
glxColorRGBa(fillRgb, 1); 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); 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 { } else {
if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour && if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour &&
polyError.how == POLY_GOOD) polyError.how == POLY_GOOD)

View File

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

View File

@ -402,6 +402,7 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
loop.l.Add(first); loop.l.Add(first);
Vector start = first->Start(); Vector start = first->Start();
Vector hanging = first->Finish(); Vector hanging = first->Finish();
int auxA = first->auxA;
sbl->l.RemoveTagged(); sbl->l.RemoveTagged();
@ -411,11 +412,11 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
for(i = 0; i < sbl->l.n; i++) { for(i = 0; i < sbl->l.n; i++) {
SBezier *test = &(sbl->l.elem[i]); SBezier *test = &(sbl->l.elem[i]);
if((test->Finish()).Equals(hanging)) { if((test->Finish()).Equals(hanging) && test->auxA == auxA) {
test->Reverse(); test->Reverse();
// and let the next test catch it // and let the next test catch it
} }
if((test->Start()).Equals(hanging)) { if((test->Start()).Equals(hanging) && test->auxA == auxA) {
test->tag = 1; test->tag = 1;
loop.l.Add(test); loop.l.Add(test);
hanging = test->Finish(); hanging = test->Finish();
@ -690,11 +691,48 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
l.Add(&outerAndInners); 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(); 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) { void SBezierLoopSetSet::Clear(void) {
SBezierLoopSet *sbls;
for(sbls = l.First(); sbls; sbls = l.NextAfter(sbls)) {
sbls->Clear();
}
l.Clear(); l.Clear();
} }

View File

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