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) { void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
int i, j, srfid = id; 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, "#%d=(\n", srfid);
fprintf(f, "BOUNDED_SURFACE()\n"); fprintf(f, "BOUNDED_SURFACE()\n");
fprintf(f, "B_SPLINE_SURFACE(%d,%d,(", ss->degm, ss->degn); 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, "SURFACE()\n");
fprintf(f, ");\n"); fprintf(f, ");\n");
// The control points for the untrimmed surface.
for(i = 0; i <= ss->degm; i++) { for(i = 0; i <= ss->degm; i++) {
for(j = 0; j <= ss->degn; j++) { for(j = 0; j <= ss->degn; j++) {
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n", fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
@ -205,119 +208,51 @@ void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1); id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
bool allClosed; // Now we do the trim curves. We must group each outer loop separately
SEdge errorAt; // along with its inner faces, so do that now.
SPolygon sp; SBezierLoopSetSet sblss;
ZERO(&sp); ZERO(&sblss);
// Assemble the Bezier trim curves into closed loops; we also get the sblss.FindOuterFacesFrom(sbl, ss);
// 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. // So in our list of SBezierLoopSet, each set contains at least one loop
SContour *contour; // (the outer boundary), plus any inner loops associated with that outer
for(contour = sp.l.First(); contour; contour = sp.l.NextAfter(contour)) { // loop.
SPoint *pt; SBezierLoopSet *sbls;
for(pt = contour->l.First(); pt; pt = contour->l.NextAfter(pt)) { for(sbls = sblss.l.First(); sbls; sbls = sblss.l.NextAfter(sbls)) {
double u, v; SBezierLoop *loop = sbls->l.First();
ss->ClosestPointTo(pt->p, &u, &v);
pt->p = Vector::From(u, v, 0); List<int> listOfLoops;
} ZERO(&listOfLoops);
} // Create the face outer boundary from the outer loop.
sp.normal = Vector::From(0, 0, 1); int fob = ExportCurveLoop(loop, false);
listOfLoops.Add(&fob);
static const int OUTER_LOOP = 10;
static const int INNER_LOOP = 20; // And create the face inner boundaries from any inner loops that
static const int USED_LOOP = 30; // lie within this contour.
// Fix the contour directions; SBezierLoopSet::From() works only for loop = sbls->l.NextAfter(loop);
// planes, since it uses the polygon xyz space. for(; loop; loop = sbls->l.NextAfter(loop)) {
sp.FixContourDirections(); int fib = ExportCurveLoop(loop, true);
for(i = 0; i < sp.l.n; i++) { listOfLoops.Add(&fib);
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;
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);
listOfLoops.Add(&fib);
inner->tag = USED_LOOP;
}
}
// And now create the face that corresponds to this outer loop
// and all of its holes.
int advFaceId = id;
fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
int *fb;
for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
fprintf(f, "#%d", *fb);
if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
}
fprintf(f, "),#%d,.T.);\n", srfid);
fprintf(f, "\n");
advancedFaces.Add(&advFaceId);
id++;
listOfLoops.Clear();
} }
// And now create the face that corresponds to this outer loop
// and all of its holes.
int advFaceId = id;
fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
int *fb;
for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
fprintf(f, "#%d", *fb);
if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
}
fprintf(f, "),#%d,.T.);\n", srfid);
fprintf(f, "\n");
advancedFaces.Add(&advFaceId);
id++;
listOfLoops.Clear();
} }
sblss.Clear();
} }
void StepFileWriter::WriteFooter(void) { 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.textAngle", 'f', &(SS.sv.s.textAngle) },
{ 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) }, { 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) },
{ 's', "Style.color", 'x', &(SS.sv.s.color) }, { '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.visible", 'b', &(SS.sv.s.visible) },
{ 's', "Style.exportable", 'b', &(SS.sv.s.exportable) }, { 's', "Style.exportable", 'b', &(SS.sv.s.exportable) },

View File

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

View File

@ -395,6 +395,13 @@ void SBezierLoop::MakePwlInto(SContour *sc) {
sc->l.elem[sc->l.n - 1] = sc->l.elem[0]; 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, SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
bool *allClosed, SEdge *errorAt) bool *allClosed, SEdge *errorAt)
@ -456,6 +463,109 @@ void SBezierLoopSet::Clear(void) {
l.Clear(); 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, SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q,
bool mirror) bool mirror)
{ {

View File

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

View File

@ -77,6 +77,8 @@ void Style::CreateDefaultStyle(hStyle h) {
ns.textAngle = 0; ns.textAngle = 0;
ns.visible = true; ns.visible = true;
ns.exportable = true; ns.exportable = true;
ns.filled = false;
ns.fillColor = RGBf(0.3, 0.3, 0.3);
ns.h = h; ns.h = h;
if(isDefaultStyle) { if(isDefaultStyle) {
ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); ns.name.strcpy(CnfPrefixToName(d->cnfPrefix));
@ -101,6 +103,8 @@ void Style::LoadFactoryDefaults(void) {
s->textAngle = 0; s->textAngle = 0;
s->visible = true; s->visible = true;
s->exportable = true; s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); s->name.strcpy(CnfPrefixToName(d->cnfPrefix));
} }
SS.backgroundColor = RGB(0, 0, 0); SS.backgroundColor = RGB(0, 0, 0);
@ -392,7 +396,7 @@ void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) {
if(link == 'w') { if(link == 'w') {
row = 16; // width for a default style row = 16; // width for a default style
} else if(link == 'W') { } else if(link == 'W') {
row = 21; // width for a custom style row = 16; // width for a custom style
} else if(link == 't') { } else if(link == 't') {
row = 27; // text height (for custom styles only) 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) { void TextWindow::ScreenChangeStyleColor(int link, DWORD v) {
hStyle hs = { v }; hStyle hs = { v };
Style *s = Style::Get(hs); 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]; char str[300];
sprintf(str, "%.2f, %.2f, %.2f", sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb));
REDf(s->color), GREENf(s->color), BLUEf(s->color)); ShowTextEditControl(row, col, str);
ShowTextEditControl(13, 12, str);
SS.TW.edit.style = hs; SS.TW.edit.style = hs;
SS.TW.edit.meaning = EDIT_STYLE_COLOR; SS.TW.edit.meaning = em;
} }
void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) {
@ -458,6 +475,10 @@ void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) {
s->visible = !(s->visible); s->visible = !(s->visible);
break; break;
case 'f':
s->filled = !(s->filled);
break;
// Horizontal text alignment // Horizontal text alignment
case 'L': case 'L':
s->textOrigin |= Style::ORIGIN_LEFT; s->textOrigin |= Style::ORIGIN_LEFT;
@ -520,6 +541,7 @@ bool TextWindow::EditControlDoneForStyles(char *str) {
break; break;
case EDIT_BACKGROUND_COLOR: case EDIT_BACKGROUND_COLOR:
case EDIT_STYLE_FILL_COLOR:
case EDIT_STYLE_COLOR: { case EDIT_STYLE_COLOR: {
double r, g, b; double r, g, b;
if(sscanf(str, "%lf, %lf, %lf", &r, &g, &b)==3) { if(sscanf(str, "%lf, %lf, %lf", &r, &g, &b)==3) {
@ -530,6 +552,10 @@ bool TextWindow::EditControlDoneForStyles(char *str) {
SS.UndoRemember(); SS.UndoRemember();
s = Style::Get(edit.style); s = Style::Get(edit.style);
s->color = RGBf(r, g, b); 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 { } else {
SS.backgroundColor = RGBf(r, g, b); SS.backgroundColor = RGBf(r, g, b);
} }
@ -568,28 +594,11 @@ void TextWindow::ShowStyleInfo(void) {
s->h.v, &ScreenDeleteStyle); 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, 0x80000000 | s->color,
REDf(s->color), GREENf(s->color), BLUEf(s->color), REDf(s->color), GREENf(s->color), BLUEf(s->color),
s->h.v, ScreenChangeStyleColor); 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"; char *unit = (SS.viewUnits == SolveSpace::UNIT_INCHES) ? "inches" : "mm";
// The line width, and its units // The line width, and its units
@ -619,6 +628,23 @@ void TextWindow::ShowStyleInfo(void) {
(!widthpx ? unit : "")); (!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 // The text height, and its units
Printf(false, ""); Printf(false, "");
char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]"; char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]";
@ -649,12 +675,11 @@ void TextWindow::ShowStyleInfo(void) {
} }
if(s->h.v >= Style::FIRST_CUSTOM) { if(s->h.v >= Style::FIRST_CUSTOM) {
bool neither;
Printf(true, "%FtTEXT ANGLE %E%@ %D%f%Ll%Fl[change]%E", Printf(true, "%FtTEXT ANGLE %E%@ %D%f%Ll%Fl[change]%E",
s->textAngle, s->textAngle,
s->h.v, &ScreenChangeStyleTextAngle); s->h.v, &ScreenChangeStyleTextAngle);
bool neither;
neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT)); neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
Printf(true, "%FtALIGN TEXT " Printf(true, "%FtALIGN TEXT "
"%Fh%D%f%LL%s%E%Fs%s%E / " "%Fh%D%f%LL%s%E%Fs%s%E / "
@ -687,6 +712,24 @@ void TextWindow::ShowStyleInfo(void) {
} }
if(s->h.v >= Style::FIRST_CUSTOM) { 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, "");
Printf(false, "To assign lines or curves to this style,"); Printf(false, "To assign lines or curves to this style,");
Printf(false, "select them on the drawing. Then commit"); 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_HEIGHT = 51;
static const int EDIT_STYLE_TEXT_ANGLE = 52; static const int EDIT_STYLE_TEXT_ANGLE = 52;
static const int EDIT_STYLE_COLOR = 53; static const int EDIT_STYLE_COLOR = 53;
static const int EDIT_STYLE_NAME = 54; static const int EDIT_STYLE_FILL_COLOR = 54;
static const int EDIT_BACKGROUND_COLOR = 55; static const int EDIT_STYLE_NAME = 55;
static const int EDIT_BACKGROUND_COLOR = 56;
struct { struct {
int meaning; int meaning;
int i; int i;