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
parent
2f115ec950
commit
3515748334
146
export.cpp
146
export.cpp
|
@ -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, ¬ClosedAt,
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
332
exportvector.cpp
332
exportvector.cpp
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
68
solvespace.h
68
solvespace.h
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue