Now actually export the line styles, for PDF, EPS, and SVG file

formats, with the proper color and width. This may need a bit of
cleanup for stuff like the hidden line removal, which currently
loses the style.

Also fix a bug in the test for arcs of a circle. A second-order
Bezier with collinear control points really is an arc, but it's an
arc with infinite radius so stuff tends to blow up. So return false
for that one.

[git-p4: depot-paths = "//depot/solvespace/": change = 2030]
This commit is contained in:
Jonathan Westhues 2009-09-21 21:46:30 -08:00
parent 517c5edbfa
commit 9b8f32dad7
16 changed files with 285 additions and 79 deletions

View File

@ -20,7 +20,7 @@ bool Constraint::HasLabel(void) {
void Constraint::LineDrawOrGetDistance(Vector a, Vector b) {
if(dogd.drawing) {
if(dogd.sel) {
dogd.sel->AddEdge(a, b);
dogd.sel->AddEdge(a, b, Style::CONSTRAINT);
} else {
glBegin(GL_LINE_STRIP);
glxVertex3v(a);

View File

@ -179,7 +179,7 @@ void Entity::GenerateEdges(SEdgeList *el, bool includingConstruction) {
ZERO(&lv);
sb->MakePwlInto(&lv);
for(j = 1; j < lv.n; j++) {
el->AddEdge(lv.elem[j-1], lv.elem[j]);
el->AddEdge(lv.elem[j-1], lv.elem[j], style.v);
}
lv.Clear();
}
@ -230,6 +230,11 @@ bool Entity::IsVisible(void) {
}
}
if(style.v) {
Style *s = Style::Get(style);
if(!s->visible) return false;
}
if(forceHidden) return false;
return true;
@ -259,9 +264,11 @@ bool Entity::PointIsFromReferences(void) {
return h.request().IsFromReferences();
}
void Entity::GenerateBezierCurves(SBezierList *sbl) {
void Entity::GenerateBezierCurves(SBezierList *sbl) {
SBezier sb;
int i = sbl->l.n;
switch(type) {
case LINE_SEGMENT: {
Vector a = SK.GetEntity(point[0])->PointGetNum();
@ -357,6 +364,11 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) {
// Not a problem, points and normals and such don't generate curves
break;
}
// Record our style for all of the Beziers that we just created.
for(; i < sbl->l.n; i++) {
sbl->l.elem[i].auxA = style.v;
}
}
void Entity::DrawOrGetDistance(void) {

View File

@ -82,6 +82,16 @@ void SolveSpace::ExportSectionTo(char *filename) {
&el,
(SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
// All of these are solid model edges, so use the appropriate style.
SEdge *se;
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
se->auxA = Style::SOLID_EDGE;
}
SBezier *sb;
for(sb = bl.l.First(); sb; sb = bl.l.NextAfter(sb)) {
sb->auxA = Style::SOLID_EDGE;
}
el.CullExtraneousEdges();
bl.CullIdenticalBeziers();
@ -137,7 +147,7 @@ void SolveSpace::ExportViewTo(char *filename) {
SEdgeList *selr = &(g->displayEdges);
SEdge *se;
for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
edges.AddEdge(se->a, se->b);
edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
}
}
@ -406,30 +416,40 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
}
if(sel) {
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
LineSegment(e->a.x, e->a.y, e->b.x, e->b.y);
if(!Style::Exportable(e->auxA)) continue;
DWORD rgb = Style::Color (e->auxA, true);
double w = Style::WidthMm(e->auxA);
LineSegment(rgb, w, e->a.x, e->a.y, e->b.x, e->b.y);
}
}
if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
Bezier(b);
if(!Style::Exportable(b->auxA)) continue;
DWORD rgb = Style::Color (b->auxA, true);
double w = Style::WidthMm(b->auxA);
Bezier(rgb, w, b);
}
}
FinishAndCloseFile();
}
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
void VectorFileWriter::BezierAsPwl(DWORD rgb, double width, SBezier *sb) {
List<Vector> lv;
ZERO(&lv);
sb->MakePwlInto(&lv);
int i;
for(i = 1; i < lv.n; i++) {
LineSegment(lv.elem[i-1].x, lv.elem[i-1].y,
lv.elem[i ].x, lv.elem[i ].y);
LineSegment(rgb, width, lv.elem[i-1].x, lv.elem[i-1].y,
lv.elem[i ].x, lv.elem[i ].y);
}
lv.Clear();
}
void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
void VectorFileWriter::BezierAsNonrationalCubic(DWORD rgb, double width,
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.
@ -457,12 +477,12 @@ void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
}
if(closeEnough || depth > 3) {
Bezier(&bnr);
Bezier(rgb, width, &bnr);
} else {
SBezier bef, aft;
sb->SplitAt(0.5, &bef, &aft);
BezierAsNonrationalCubic(&bef, depth+1);
BezierAsNonrationalCubic(&aft, depth+1);
BezierAsNonrationalCubic(rgb, width, &bef, depth+1);
BezierAsNonrationalCubic(rgb, width, &aft, depth+1);
}
}

View File

@ -62,7 +62,9 @@ void DxfFileWriter::StartFile(void) {
"ENTITIES\r\n");
}
void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void DxfFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
fprintf(f,
" 0\r\n"
"LINE\r\n"
@ -88,7 +90,7 @@ void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void DxfFileWriter::Triangle(STriangle *tr) {
}
void DxfFileWriter::Bezier(SBezier *sb) {
void DxfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
@ -121,7 +123,7 @@ void DxfFileWriter::Bezier(SBezier *sb) {
r,
theta0*180/PI, theta1*180/PI);
} else {
BezierAsPwl(sb);
BezierAsPwl(rgb, w, sb);
}
}
@ -137,6 +139,17 @@ 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"
@ -156,16 +169,18 @@ void EpsFileWriter::StartFile(void) {
MmToPts(ptMax.y - ptMin.y));
}
void EpsFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void EpsFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f lineto\r\n"
" 1 setlinewidth\r\n"
" 0 setgray\r\n"
"%s"
"stroke\r\n",
MmToPts(x0 - ptMin.x), MmToPts(y0 - ptMin.y),
MmToPts(x1 - ptMin.x), MmToPts(y1 - ptMin.y));
MmToPts(x1 - ptMin.x), MmToPts(y1 - ptMin.y),
StyleString(rgb, w));
}
void EpsFileWriter::Triangle(STriangle *tr) {
@ -200,7 +215,7 @@ void EpsFileWriter::Triangle(STriangle *tr) {
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
}
void EpsFileWriter::Bezier(SBezier *sb) {
void EpsFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
@ -216,27 +231,27 @@ void EpsFileWriter::Bezier(SBezier *sb) {
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f %.3f %.3f %.3f arc\r\n"
" 1 setlinewidth\r\n"
" 0 setgray\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(r),
theta0*180/PI, theta1*180/PI);
theta0*180/PI, theta1*180/PI,
StyleString(rgb, w));
} else if(sb->deg == 3 && !sb->IsRational()) {
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n"
" 1 setlinewidth\r\n"
" 0 setgray\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[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 {
BezierAsNonrationalCubic(sb);
BezierAsNonrationalCubic(rgb, w, sb);
}
}
@ -252,6 +267,16 @@ 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"
@ -371,12 +396,15 @@ void PdfFileWriter::FinishAndCloseFile(void) {
}
void PdfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void PdfFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
fprintf(f,
"1 w 0 0 0 RG\r\n"
"%s"
"%.3f %.3f m\r\n"
"%.3f %.3f l\r\n"
"S\r\n",
StyleString(rgb, w),
MmToPts(x0 - ptMin.x), MmToPts(y0 - ptMin.y),
MmToPts(x1 - ptMin.x), MmToPts(y1 - ptMin.y));
}
@ -400,29 +428,34 @@ void PdfFileWriter::Triangle(STriangle *tr) {
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
}
void PdfFileWriter::Bezier(SBezier *sb) {
void PdfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
if(sb->deg == 3 && !sb->IsRational()) {
fprintf(f,
"1 w 0 0 0 RG\r\n"
"%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),
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(sb);
BezierAsNonrationalCubic(rgb, w, sb);
}
}
//-----------------------------------------------------------------------------
// Routines for SVG output
//-----------------------------------------------------------------------------
const char *SvgFileWriter::SVG_STYLE =
"stroke-width='0.1' stroke='black' style='fill: none;'";
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,
@ -440,13 +473,15 @@ void SvgFileWriter::StartFile(void) {
// A little bit of extra space for the stroke width.
}
void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void SvgFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
// SVG uses a coordinate system with the origin at top left, +y down
fprintf(f,
"<polyline points='%.3f,%.3f %.3f,%.3f' %s />\r\n",
(x0 - ptMin.x), (ptMax.y - y0),
(x1 - ptMin.x), (ptMax.y - y1),
SVG_STYLE);
StyleString(rgb, w));
}
void SvgFileWriter::Triangle(STriangle *tr) {
@ -466,7 +501,7 @@ void SvgFileWriter::Triangle(STriangle *tr) {
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
}
void SvgFileWriter::Bezier(SBezier *sb) {
void SvgFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
@ -487,11 +522,11 @@ void SvgFileWriter::Bezier(SBezier *sb) {
p0.x - ptMin.x, ptMax.y - p0.y,
r, r,
p1.x - ptMin.x, ptMax.y - p1.y,
SVG_STYLE);
StyleString(rgb, w));
} else if(!sb->IsRational()) {
if(sb->deg == 1) {
LineSegment(sb->ctrl[0].x, sb->ctrl[0].y,
sb->ctrl[1].x, sb->ctrl[1].y);
LineSegment(rgb, w, sb->ctrl[0].x, sb->ctrl[0].y,
sb->ctrl[1].x, sb->ctrl[1].y);
} else if(sb->deg == 2) {
fprintf(f,
"<path d='M%.3f,%.3f "
@ -499,7 +534,7 @@ void SvgFileWriter::Bezier(SBezier *sb) {
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[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
SVG_STYLE);
StyleString(rgb, w));
} else if(sb->deg == 3) {
fprintf(f,
"<path d='M%.3f,%.3f "
@ -508,11 +543,10 @@ void SvgFileWriter::Bezier(SBezier *sb) {
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,
SVG_STYLE);
StyleString(rgb, w));
}
} else {
BezierAsNonrationalCubic(sb);
BezierAsNonrationalCubic(rgb, w, sb);
}
}
@ -533,7 +567,9 @@ void HpglFileWriter::StartFile(void) {
fprintf(f, "SP1;\r\n");
}
void HpglFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void HpglFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
fprintf(f, "PU%d,%d;\r\n", (int)MmToHpglUnits(x0), (int)MmToHpglUnits(y0));
fprintf(f, "PD%d,%d;\r\n", (int)MmToHpglUnits(x1), (int)MmToHpglUnits(y1));
}
@ -541,8 +577,8 @@ void HpglFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void HpglFileWriter::Triangle(STriangle *tr) {
}
void HpglFileWriter::Bezier(SBezier *sb) {
BezierAsPwl(sb);
void HpglFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
BezierAsPwl(rgb, w, sb);
}
void HpglFileWriter::FinishAndCloseFile(void) {
@ -562,13 +598,15 @@ void Step2dFileWriter::StartFile(void) {
void Step2dFileWriter::Triangle(STriangle *tr) {
}
void Step2dFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
void Step2dFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
SBezier sb = SBezier::From(Vector::From(x0, y0, 0),
Vector::From(x1, y1, 0));
Bezier(&sb);
Bezier(rgb, w, &sb);
}
void Step2dFileWriter::Bezier(SBezier *sb) {
void Step2dFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
int c = sfw.ExportCurve(sb);
sfw.curves.Add(&c);
}

View File

@ -579,6 +579,8 @@ void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {
en.point[0] = Remap(ep->h, REMAP_TOP);
en.point[1] = Remap(ep->h, REMAP_BOTTOM);
en.group = h;
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_PT_TO_LINE);
en.type = Entity::LINE_SEGMENT;
el->Add(&en);
@ -596,6 +598,8 @@ void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {
en.numNormal = Quaternion::From(0, ab.x, ab.y, ab.z);
en.group = h;
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_LINE_TO_FACE);
en.type = Entity::FACE_XPROD;
el->Add(&en);
@ -636,6 +640,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
en.timesApplied = timesApplied;
en.group = h;
en.construction = ep->construction;
en.style = ep->style;
switch(ep->type) {
case Entity::WORKPLANE:

View File

@ -353,12 +353,8 @@ void SContour::AddPoint(Vector p) {
void SContour::MakeEdgesInto(SEdgeList *el) {
int i;
for(i = 0; i < (l.n-1); i++) {
SEdge e;
e.tag = 0;
e.a = l.elem[i].p;
e.b = l.elem[i+1].p;
el->l.Add(&e);
for(i = 0; i < (l.n - 1); i++) {
el->AddEdge(l.elem[i].p, l.elem[i+1].p);
}
}

View File

@ -659,6 +659,8 @@ public:
static float Width(hStyle hs);
static DWORD Color(int hs, bool forExport=false);
static float Width(int hs);
static double WidthMm(int hs);
static bool Exportable(int hs);
static hStyle ForEntity(hEntity he);
char *DescriptionString(void);

View File

@ -68,6 +68,8 @@ void SolveSpace::Init(char *cmdLine) {
exportScale = CnfThawFloat(1.0f, "ExportScale");
// Export offset (cutter radius comp)
exportOffset = CnfThawFloat(0.0f, "ExportOffset");
// Rewrite exported colors close to white into black (assuming white bg)
fixExportColors = CnfThawDWORD(1, "FixExportColors");
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
drawBackFaces = CnfThawDWORD(1, "DrawBackFaces");
// Export shaded triangles in a 2d view
@ -152,6 +154,8 @@ void SolveSpace::Exit(void) {
CnfFreezeFloat(exportScale, "ExportScale");
// Export offset (cutter radius comp)
CnfFreezeFloat(exportOffset, "ExportOffset");
// Rewrite exported colors close to white into black (assuming white bg)
CnfFreezeDWORD(fixExportColors, "FixExportColors");
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
CnfFreezeDWORD(drawBackFaces, "DrawBackFaces");
// Export shaded triangles in a 2d view

View File

@ -395,28 +395,34 @@ public:
static VectorFileWriter *ForFile(char *file);
void Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm);
void BezierAsPwl(SBezier *sb);
void BezierAsNonrationalCubic(SBezier *sb, int depth=0);
virtual void Bezier(SBezier *sb) = 0;
virtual void LineSegment(double x0, double y0, double x1, double y1) = 0;
void BezierAsPwl(DWORD rgb, double width, SBezier *sb);
void BezierAsNonrationalCubic(DWORD rgb, double width,
SBezier *sb, int depth=0);
virtual void Bezier(DWORD rgb, double width, SBezier *sb) = 0;
virtual void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1) = 0;
virtual void Triangle(STriangle *tr) = 0;
virtual void StartFile(void) = 0;
virtual void FinishAndCloseFile(void) = 0;
};
class DxfFileWriter : public VectorFileWriter {
public:
void LineSegment(double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class EpsFileWriter : public VectorFileWriter {
public:
void LineSegment(double x0, double y0, double x1, double y1);
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
@ -425,35 +431,40 @@ public:
DWORD xref[10];
DWORD bodyStart;
void LineSegment(double x0, double y0, double x1, double y1);
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class SvgFileWriter : public VectorFileWriter {
public:
static const char *SVG_STYLE;
void LineSegment(double x0, double y0, double x1, double y1);
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class HpglFileWriter : public VectorFileWriter {
public:
static double MmToHpglUnits(double mm);
void LineSegment(double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
class Step2dFileWriter : public VectorFileWriter {
StepFileWriter sfw;
void LineSegment(double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void Triangle(STriangle *tr);
void Bezier(SBezier *sb);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
void FinishAndCloseFile(void);
};
@ -530,6 +541,7 @@ public:
double cameraTangent;
float exportScale;
float exportOffset;
int fixExportColors;
int drawBackFaces;
int showToolbar;
DWORD backgroundColor;

View File

@ -104,6 +104,13 @@ SBezier SBezier::TransformedBy(Vector t, Quaternion q) {
bool SBezier::IsCircle(Vector axis, Vector *center, double *r) {
if(deg != 2) return false;
if(ctrl[1].DistanceToLine(ctrl[0], ctrl[2].Minus(ctrl[0])) < LENGTH_EPS) {
// This is almost a line segment. So it's a circle with very large
// radius, which is likely to make code that tries to handle circles
// blow up. So return false.
return false;
}
Vector t0 = (ctrl[0]).Minus(ctrl[1]),
t2 = (ctrl[2]).Minus(ctrl[1]),
r0 = axis.Cross(t0),

View File

@ -62,6 +62,8 @@ public:
class SBezier {
public:
int tag;
int auxA, auxB;
int deg;
Vector ctrl[4];
double weight[4];

View File

@ -111,6 +111,8 @@ void Style::FreezeDefaultStyles(void) {
// the style, according to our table of default styles.
//-----------------------------------------------------------------------------
Style *Style::Get(hStyle h) {
if(h.v == 0) h.v = ACTIVE_GRP;
Style *s = SK.style.FindByIdNoOops(h);
if(s) {
// It exists, good.
@ -140,6 +142,19 @@ float Style::Width(int s) {
//-----------------------------------------------------------------------------
DWORD Style::Color(hStyle h, bool forExport) {
Style *s = Get(h);
if(forExport) {
Vector rgb = Vector::From(REDf(s->color),
GREENf(s->color),
BLUEf(s->color));
rgb = rgb.Minus(Vector::From(1, 1, 1));
if(rgb.Magnitude() < 0.4 && SS.fixExportColors) {
// This is an almost-white color in a default style, which is
// good for the default on-screen view (black bg) but probably
// not desired in the exported files, which typically are shown
// against white backgrounds.
return RGB(0, 0, 0);
}
}
return s->color;
}
@ -158,6 +173,24 @@ float Style::Width(hStyle h) {
return (float)r;
}
//-----------------------------------------------------------------------------
// Return the width associated with our style in millimeters..
//-----------------------------------------------------------------------------
double Style::WidthMm(int hs) {
double widthpx = Width(hs);
return widthpx / SS.GW.scale;
}
//-----------------------------------------------------------------------------
// Should lines and curves from this style appear in the output file? Only
// if it's both shown and exportable.
//-----------------------------------------------------------------------------
bool Style::Exportable(int si) {
hStyle hs = { si };
Style *s = Get(hs);
return (s->exportable) && (s->visible);
}
//-----------------------------------------------------------------------------
// Return the appropriate style for our entity. If the entity has a style
// explicitly assigned, then it's that style. Otherwise it's the appropriate
@ -432,6 +465,39 @@ void TextWindow::ShowStyleInfo(void) {
s->h.v, &ScreenChangeStyleYesNo,
(!s->exportable ? "" : "no"),
(!s->exportable ? "no" : ""));
Printf(false, "");
Printf(false, "To assign lines or curves to this style,");
Printf(false, "select them on the drawing. Then commit");
Printf(false, "by clicking the link at the bottom of");
Printf(false, "this window.");
}
}
void TextWindow::ScreenAssignSelectionToStyle(int link, DWORD v) {
bool showError = false;
SS.GW.GroupSelection();
SS.UndoRemember();
for(int i = 0; i < SS.GW.gs.entities; i++) {
hEntity he = SS.GW.gs.entity[i];
if(!he.isFromRequest()) {
showError = true;
continue;
}
hRequest hr = he.request();
Request *r = SK.GetRequest(hr);
r->style.v = v;
SS.later.generateAll = true;
}
if(showError) {
Error("Can't assign style to an entity that's derived from another "
"entity; try assigning a style to this entity's parent.");
}
SS.GW.ClearSelection();
InvalidateGraphics();
}

View File

@ -636,6 +636,9 @@ void TextWindow::ScreenChangeExportOffset(int link, DWORD v) {
ShowTextEditControl(57, 3, SS.MmToString(SS.exportOffset));
SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
}
void TextWindow::ScreenChangeFixExportColors(int link, DWORD v) {
SS.fixExportColors = !SS.fixExportColors;
}
void TextWindow::ScreenChangeBackFaces(int link, DWORD v) {
SS.drawBackFaces = !SS.drawBackFaces;
InvalidateGraphics();
@ -786,7 +789,14 @@ void TextWindow::ShowConfiguration(void) {
}
Printf(false, "");
Printf(false, "%Ft draw back faces: "
Printf(false, "%Ft fix white exported lines: "
"%Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E",
&ScreenChangeFixExportColors,
( SS.fixExportColors ? "" : "yes"), ( SS.fixExportColors ? "yes" : ""),
&ScreenChangeFixExportColors,
(!SS.fixExportColors ? "" : "no"), (!SS.fixExportColors ? "no" : ""));
Printf(false, "%Ft draw triangle back faces: "
"%Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E",
&ScreenChangeBackFaces,
(SS.drawBackFaces ? "" : "yes"), (SS.drawBackFaces ? "yes" : ""),

View File

@ -414,6 +414,12 @@ void TextWindow::DescribeSelection(void) {
Entity *w = SK.GetEntity(e->workplane);
Printf(false, "%FtIN WORKPLANE%E %s", w->DescriptionString());
}
if(e->style.v) {
Style *s = Style::Get(e->style);
Printf(false, "%FtIN STYLE%E %s", s->DescriptionString());
} else {
Printf(false, "%FtIN STYLE%E none");
}
} else if(gs.n == 2 && gs.points == 2) {
Printf(false, "%FtTWO POINTS");
Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
@ -481,6 +487,31 @@ void TextWindow::DescribeSelection(void) {
Printf(true, "%FtSELECTED:%E %d item%s", gs.n, gs.n == 1 ? "" : "s");
}
if(shown.screen == SCREEN_STYLE_INFO &&
shown.style.v >= Style::FIRST_CUSTOM)
{
// If we are showing a screen for a particular style, then offer the
// option to assign our selected entities to that style.
Style *s = Style::Get(shown.style);
Printf(true, "%Fl%D%f%Ll(assign to style %s)%E",
shown.style.v,
&ScreenAssignSelectionToStyle,
s->DescriptionString());
}
// If any of the selected entities have an assigned style, then offer
// the option to remove that style.
for(i = 0; i < gs.entities; i++) {
Entity *e = SK.GetEntity(gs.entity[i]);
if(e->style.v != 0) {
break;
}
}
if(i < gs.entities) {
Printf(true, "%Fl%D%f%Ll(remove assigned style)%E",
0,
&ScreenAssignSelectionToStyle);
}
Printf(true, "%Fl%f%Ll(unselect all)%E", &TextWindow::ScreenUnselectAll);
}

2
ui.h
View File

@ -159,10 +159,12 @@ public:
static void ScreenChangeStyleYesNo(int link, DWORD v);
static void ScreenCreateCustomStyle(int link, DWORD v);
static void ScreenLoadFactoryDefaultStyles(int link, DWORD v);
static void ScreenAssignSelectionToStyle(int link, DWORD v);
static void ScreenShowConfiguration(int link, DWORD v);
static void ScreenGoToWebsite(int link, DWORD v);
static void ScreenChangeFixExportColors(int link, DWORD v);
static void ScreenChangeBackFaces(int link, DWORD v);
static void ScreenChangePwlCurves(int link, DWORD v);
static void ScreenChangeCanvasSizeAuto(int link, DWORD v);

View File

@ -1,8 +1,7 @@
grid
line styles (color, thickness)
background color setting
better text
right-click menu
faster triangulation
interpolating splines