diff --git a/exportstep.cpp b/exportstep.cpp index f53158d..8bfac1e 100644 --- a/exportstep.cpp +++ b/exportstep.cpp @@ -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,119 +208,51 @@ 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; - - List 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(); + // 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 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. + loop = sbls->l.NextAfter(loop); + for(; loop; loop = sbls->l.NextAfter(loop)) { + int fib = ExportCurveLoop(loop, true); + listOfLoops.Add(&fib); } + + // 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) { diff --git a/file.cpp b/file.cpp index 12d774b..b2d934c 100644 --- a/file.cpp +++ b/file.cpp @@ -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) }, diff --git a/sketch.h b/sketch.h index 3087156..525a557 100644 --- a/sketch.h +++ b/sketch.h @@ -673,6 +673,8 @@ public: int textOrigin; double textAngle; DWORD color; + bool filled; + DWORD fillColor; bool visible; bool exportable; diff --git a/srf/curve.cpp b/srf/curve.cpp index b64a699..dbf697f 100644 --- a/srf/curve.cpp +++ b/srf/curve.cpp @@ -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) { diff --git a/srf/surface.h b/srf/surface.h index df3c841..bd6ad98 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -119,6 +119,7 @@ public: List 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 l; + + void FindOuterFacesFrom(SBezierList *sbl, SSurface *srfuv); + void Clear(void); +}; + // Stuff for the surface trim curves: piecewise linear class SCurvePt { public: diff --git a/style.cpp b/style.cpp index 674c021..8cbe206 100644 --- a/style.cpp +++ b/style.cpp @@ -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"); diff --git a/ui.h b/ui.h index 5666694..3cc96cf 100644 --- a/ui.h +++ b/ui.h @@ -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;