Move code to find outer and inner contours (and which inner

contours go with which outer contour) out of exportstep.cpp, since
I'll need that to do filled contour export for the 2d file formats.

Also add user interface to specify fill color.

[git-p4: depot-paths = "//depot/solvespace/": change = 2057]
This commit is contained in:
Jonathan Westhues 2009-10-22 09:16:20 -08:00
parent 0914a27ff4
commit e7c8d31500
7 changed files with 239 additions and 137 deletions

View File

@ -161,6 +161,8 @@ int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
int i, j, srfid = id;
// First, we create the untrimmed surface. We always specify a rational
// B-spline surface (in fact, just a Bezier surface).
fprintf(f, "#%d=(\n", srfid);
fprintf(f, "BOUNDED_SURFACE()\n");
fprintf(f, "B_SPLINE_SURFACE(%d,%d,(", ss->degm, ss->degn);
@ -194,6 +196,7 @@ void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
fprintf(f, "SURFACE()\n");
fprintf(f, ");\n");
// The control points for the untrimmed surface.
for(i = 0; i <= ss->degm; i++) {
for(j = 0; j <= ss->degn; j++) {
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
@ -205,98 +208,31 @@ void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
bool allClosed;
SEdge errorAt;
SPolygon sp;
ZERO(&sp);
// Assemble the Bezier trim curves into closed loops; we also get the
// piecewise linearization of the curves (in the SPolygon sp), as a
// calculation aid for the loop direction.
SBezierLoopSet sbls = SBezierLoopSet::From(sbl, &sp, &allClosed, &errorAt);
// Now we do the trim curves. We must group each outer loop separately
// along with its inner faces, so do that now.
SBezierLoopSetSet sblss;
ZERO(&sblss);
sblss.FindOuterFacesFrom(sbl, ss);
// Convert the xyz piecewise linear to uv piecewise linear.
SContour *contour;
for(contour = sp.l.First(); contour; contour = sp.l.NextAfter(contour)) {
SPoint *pt;
for(pt = contour->l.First(); pt; pt = contour->l.NextAfter(pt)) {
double u, v;
ss->ClosestPointTo(pt->p, &u, &v);
pt->p = Vector::From(u, v, 0);
}
}
sp.normal = Vector::From(0, 0, 1);
static const int OUTER_LOOP = 10;
static const int INNER_LOOP = 20;
static const int USED_LOOP = 30;
// Fix the contour directions; SBezierLoopSet::From() works only for
// planes, since it uses the polygon xyz space.
sp.FixContourDirections();
for(i = 0; i < sp.l.n; i++) {
SContour *contour = &(sp.l.elem[i]);
SBezierLoop *bl = &(sbls.l.elem[i]);
if(contour->tag) {
// This contour got reversed in the polygon to make the directions
// consistent, so the same must be necessary for the Bezier loop.
bl->Reverse();
}
if(contour->IsClockwiseProjdToNormal(sp.normal)) {
bl->tag = INNER_LOOP;
} else {
bl->tag = OUTER_LOOP;
}
}
bool loopsRemaining = true;
while(loopsRemaining) {
loopsRemaining = false;
for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]);
if(loop->tag != OUTER_LOOP) continue;
// Check if this contour contains any outer loops; if it does, then
// we should do those "inner outer loops" first; otherwise we
// will steal their holes, since their holes also lie inside this
// contour.
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *outer = &(sbls.l.elem[j]);
if(i == j) continue;
if(outer->tag != OUTER_LOOP) continue;
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
break;
}
}
if(j < sbls.l.n) {
// It does, can't do this one yet.
continue;
}
loopsRemaining = true;
loop->tag = USED_LOOP;
// So in our list of SBezierLoopSet, each set contains at least one loop
// (the outer boundary), plus any inner loops associated with that outer
// loop.
SBezierLoopSet *sbls;
for(sbls = sblss.l.First(); sbls; sbls = sblss.l.NextAfter(sbls)) {
SBezierLoop *loop = sbls->l.First();
List<int> listOfLoops;
ZERO(&listOfLoops);
// Create the face outer boundary from the outer loop.
int fob = ExportCurveLoop(loop, false);
listOfLoops.Add(&fob);
// And create the face inner boundaries from any inner loops that
// lie within this contour.
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *inner = &(sbls.l.elem[j]);
if(inner->tag != INNER_LOOP) continue;
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
int fib = ExportCurveLoop(inner, true);
loop = sbls->l.NextAfter(loop);
for(; loop; loop = sbls->l.NextAfter(loop)) {
int fib = ExportCurveLoop(loop, true);
listOfLoops.Add(&fib);
inner->tag = USED_LOOP;
}
}
// And now create the face that corresponds to this outer loop
@ -314,10 +250,9 @@ void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
advancedFaces.Add(&advFaceId);
id++;
listOfLoops.Clear();
}
}
sblss.Clear();
}
void StepFileWriter::WriteFooter(void) {

View File

@ -172,6 +172,8 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 's', "Style.textAngle", 'f', &(SS.sv.s.textAngle) },
{ 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) },
{ 's', "Style.color", 'x', &(SS.sv.s.color) },
{ 's', "Style.fillColor", 'x', &(SS.sv.s.fillColor) },
{ 's', "Style.filled", 'b', &(SS.sv.s.filled) },
{ 's', "Style.visible", 'b', &(SS.sv.s.visible) },
{ 's', "Style.exportable", 'b', &(SS.sv.s.exportable) },

View File

@ -673,6 +673,8 @@ public:
int textOrigin;
double textAngle;
DWORD color;
bool filled;
DWORD fillColor;
bool visible;
bool exportable;

View File

@ -395,6 +395,13 @@ void SBezierLoop::MakePwlInto(SContour *sc) {
sc->l.elem[sc->l.n - 1] = sc->l.elem[0];
}
bool SBezierLoop::IsClosed(void) {
if(l.n < 1) return false;
Vector s = l.elem[0].Start(),
f = l.elem[l.n-1].Finish();
return s.Equals(f);
}
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
bool *allClosed, SEdge *errorAt)
@ -456,6 +463,109 @@ void SBezierLoopSet::Clear(void) {
l.Clear();
}
//-----------------------------------------------------------------------------
// An export helper function. We start with a list of Bezier curves, and
// assemble them into loops. We find the outer loops, and find the outer loops'
// inner loops, and group them accordingly.
//-----------------------------------------------------------------------------
void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SSurface *srfuv) {
int i, j;
bool allClosed;
SEdge errorAt;
SPolygon sp;
ZERO(&sp);
// Assemble the Bezier trim curves into closed loops; we also get the
// piecewise linearization of the curves (in the SPolygon sp), as a
// calculation aid for the loop direction.
SBezierLoopSet sbls = SBezierLoopSet::From(sbl, &sp, &allClosed, &errorAt);
// Convert the xyz piecewise linear to uv piecewise linear.
SContour *contour;
for(contour = sp.l.First(); contour; contour = sp.l.NextAfter(contour)) {
SPoint *pt;
for(pt = contour->l.First(); pt; pt = contour->l.NextAfter(pt)) {
double u, v;
srfuv->ClosestPointTo(pt->p, &u, &v);
pt->p = Vector::From(u, v, 0);
}
}
sp.normal = Vector::From(0, 0, 1);
static const int OUTER_LOOP = 10;
static const int INNER_LOOP = 20;
static const int USED_LOOP = 30;
// Fix the contour directions; SBezierLoopSet::From() works only for
// planes, since it uses the polygon xyz space.
sp.FixContourDirections();
for(i = 0; i < sp.l.n; i++) {
SContour *contour = &(sp.l.elem[i]);
SBezierLoop *bl = &(sbls.l.elem[i]);
if(contour->tag) {
// This contour got reversed in the polygon to make the directions
// consistent, so the same must be necessary for the Bezier loop.
bl->Reverse();
}
if(contour->IsClockwiseProjdToNormal(sp.normal)) {
bl->tag = INNER_LOOP;
} else {
bl->tag = OUTER_LOOP;
}
}
bool loopsRemaining = true;
while(loopsRemaining) {
loopsRemaining = false;
for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]);
if(loop->tag != OUTER_LOOP) continue;
// Check if this contour contains any outer loops; if it does, then
// we should do those "inner outer loops" first; otherwise we
// will steal their holes, since their holes also lie inside this
// contour.
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *outer = &(sbls.l.elem[j]);
if(i == j) continue;
if(outer->tag != OUTER_LOOP) continue;
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
break;
}
}
if(j < sbls.l.n) {
// It does, can't do this one yet.
continue;
}
SBezierLoopSet outerAndInners;
ZERO(&outerAndInners);
loopsRemaining = true;
loop->tag = USED_LOOP;
outerAndInners.l.Add(loop);
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *inner = &(sbls.l.elem[j]);
if(inner->tag != INNER_LOOP) continue;
Vector p = sp.l.elem[j].AnyEdgeMidpoint();
if(sp.l.elem[i].ContainsPointProjdToNormal(sp.normal, p)) {
outerAndInners.l.Add(inner);
inner->tag = USED_LOOP;
}
}
l.Add(&outerAndInners);
}
}
sp.Clear();
// Don't free sbls; we've shallow-copied all of its members to ourself.
}
void SBezierLoopSetSet::Clear(void) {
l.Clear();
}
SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q,
bool mirror)
{

View File

@ -119,6 +119,7 @@ public:
List<SBezier> l;
inline void Clear(void) { l.Clear(); }
bool IsClosed(void);
void Reverse(void);
void MakePwlInto(SContour *sc);
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
@ -140,6 +141,14 @@ public:
void Clear(void);
};
class SBezierLoopSetSet {
public:
List<SBezierLoopSet> l;
void FindOuterFacesFrom(SBezierList *sbl, SSurface *srfuv);
void Clear(void);
};
// Stuff for the surface trim curves: piecewise linear
class SCurvePt {
public:

View File

@ -77,6 +77,8 @@ void Style::CreateDefaultStyle(hStyle h) {
ns.textAngle = 0;
ns.visible = true;
ns.exportable = true;
ns.filled = false;
ns.fillColor = RGBf(0.3, 0.3, 0.3);
ns.h = h;
if(isDefaultStyle) {
ns.name.strcpy(CnfPrefixToName(d->cnfPrefix));
@ -101,6 +103,8 @@ void Style::LoadFactoryDefaults(void) {
s->textAngle = 0;
s->visible = true;
s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
s->name.strcpy(CnfPrefixToName(d->cnfPrefix));
}
SS.backgroundColor = RGB(0, 0, 0);
@ -392,7 +396,7 @@ void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) {
if(link == 'w') {
row = 16; // width for a default style
} else if(link == 'W') {
row = 21; // width for a custom style
row = 16; // width for a custom style
} else if(link == 't') {
row = 27; // text height (for custom styles only)
}
@ -415,12 +419,25 @@ void TextWindow::ScreenChangeStyleTextAngle(int link, DWORD v) {
void TextWindow::ScreenChangeStyleColor(int link, DWORD v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
// Same function used for stroke and fill colors
int row, col, em;
DWORD rgb;
if(link == 's') {
row = 13; col = 17;
em = EDIT_STYLE_COLOR;
rgb = s->color;
} else if(link == 'f') {
row = 21; col = 17;
em = EDIT_STYLE_FILL_COLOR;
rgb = s->fillColor;
} else {
oops();
}
char str[300];
sprintf(str, "%.2f, %.2f, %.2f",
REDf(s->color), GREENf(s->color), BLUEf(s->color));
ShowTextEditControl(13, 12, str);
sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb));
ShowTextEditControl(row, col, str);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = EDIT_STYLE_COLOR;
SS.TW.edit.meaning = em;
}
void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) {
@ -458,6 +475,10 @@ void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) {
s->visible = !(s->visible);
break;
case 'f':
s->filled = !(s->filled);
break;
// Horizontal text alignment
case 'L':
s->textOrigin |= Style::ORIGIN_LEFT;
@ -520,6 +541,7 @@ bool TextWindow::EditControlDoneForStyles(char *str) {
break;
case EDIT_BACKGROUND_COLOR:
case EDIT_STYLE_FILL_COLOR:
case EDIT_STYLE_COLOR: {
double r, g, b;
if(sscanf(str, "%lf, %lf, %lf", &r, &g, &b)==3) {
@ -530,6 +552,10 @@ bool TextWindow::EditControlDoneForStyles(char *str) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->color = RGBf(r, g, b);
} else if(edit.meaning == EDIT_STYLE_FILL_COLOR) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->fillColor = RGBf(r, g, b);
} else {
SS.backgroundColor = RGBf(r, g, b);
}
@ -568,28 +594,11 @@ void TextWindow::ShowStyleInfo(void) {
s->h.v, &ScreenDeleteStyle);
}
Printf(true, "%FtCOLOR %E%Bp %Bd (%@, %@, %@) %D%f%Ll%Fl[change]%E",
Printf(true, "%FtLINE COLOR %E%Bp %Bd (%@, %@, %@) %D%f%Ls%Fl[chng]%E",
0x80000000 | s->color,
REDf(s->color), GREENf(s->color), BLUEf(s->color),
s->h.v, ScreenChangeStyleColor);
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(true, "%FtSHOW %Fh%D%f%Lv%s%E%Fs%s%E / %Fh%D%f%Lv%s%E%Fs%s%E",
s->h.v, &ScreenChangeStyleYesNo,
( s->visible ? "" : "yes"),
( s->visible ? "yes" : ""),
s->h.v, &ScreenChangeStyleYesNo,
(!s->visible ? "" : "no"),
(!s->visible ? "no" : ""));
Printf(false,"%FtEXPORT %Fh%D%f%Le%s%E%Fs%s%E / %Fh%D%f%Le%s%E%Fs%s%E",
s->h.v, &ScreenChangeStyleYesNo,
( s->exportable ? "" : "yes"),
( s->exportable ? "yes" : ""),
s->h.v, &ScreenChangeStyleYesNo,
(!s->exportable ? "" : "no"),
(!s->exportable ? "no" : ""));
}
char *unit = (SS.viewUnits == SolveSpace::UNIT_INCHES) ? "inches" : "mm";
// The line width, and its units
@ -619,6 +628,23 @@ void TextWindow::ShowStyleInfo(void) {
(!widthpx ? unit : ""));
}
if(s->h.v >= Style::FIRST_CUSTOM) {
// The fill color, and whether contours are filled
Printf(true,
"%FtFILL COLOR %E%Bp %Bd (%@, %@, %@) %D%f%Lf%Fl[chng]%E",
0x80000000 | s->fillColor,
REDf(s->fillColor), GREENf(s->fillColor), BLUEf(s->fillColor),
s->h.v, ScreenChangeStyleColor);
Printf(false, "%FtCONTOURS ARE %E"
"%Fh%D%f%Lf%s%E%Fs%s%E / %Fh%D%f%Lf%s%E%Fs%s%E",
s->h.v, &ScreenChangeStyleYesNo,
( s->filled ? "" : "filled"),
( s->filled ? "filled" : ""),
s->h.v, &ScreenChangeStyleYesNo,
(!s->filled ? "" : "not filled"),
(!s->filled ? "not filled" : ""));
}
// The text height, and its units
Printf(false, "");
char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]";
@ -649,12 +675,11 @@ void TextWindow::ShowStyleInfo(void) {
}
if(s->h.v >= Style::FIRST_CUSTOM) {
bool neither;
Printf(true, "%FtTEXT ANGLE %E%@ %D%f%Ll%Fl[change]%E",
s->textAngle,
s->h.v, &ScreenChangeStyleTextAngle);
bool neither;
neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
Printf(true, "%FtALIGN TEXT "
"%Fh%D%f%LL%s%E%Fs%s%E / "
@ -687,6 +712,24 @@ void TextWindow::ShowStyleInfo(void) {
}
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, "");
Printf(false,
"%FtOBJECTS ARE %Fh%D%f%Lv%s%E%Fs%s%E / %Fh%D%f%Lv%s%E%Fs%s%E",
s->h.v, &ScreenChangeStyleYesNo,
( s->visible ? "" : "shown"),
( s->visible ? "shown" : ""),
s->h.v, &ScreenChangeStyleYesNo,
(!s->visible ? "" : "hidden"),
(!s->visible ? "hidden" : ""));
Printf(false,
"%Ft %Fh%D%f%Le%s%E%Fs%s%E / %Fh%D%f%Le%s%E%Fs%s%E",
s->h.v, &ScreenChangeStyleYesNo,
( s->exportable ? "" : "exported"),
( s->exportable ? "exported" : ""),
s->h.v, &ScreenChangeStyleYesNo,
(!s->exportable ? "" : "not exported"),
(!s->exportable ? "not exported" : ""));
Printf(false, "");
Printf(false, "To assign lines or curves to this style,");
Printf(false, "select them on the drawing. Then commit");

5
ui.h
View File

@ -98,8 +98,9 @@ public:
static const int EDIT_STYLE_TEXT_HEIGHT = 51;
static const int EDIT_STYLE_TEXT_ANGLE = 52;
static const int EDIT_STYLE_COLOR = 53;
static const int EDIT_STYLE_NAME = 54;
static const int EDIT_BACKGROUND_COLOR = 55;
static const int EDIT_STYLE_FILL_COLOR = 54;
static const int EDIT_STYLE_NAME = 55;
static const int EDIT_BACKGROUND_COLOR = 56;
struct {
int meaning;
int i;