void GraphicsWindow::Selection::Draw(void) { Vector refp; if(entity.v) { Entity *e = SK.GetEntity(entity); e->Draw(); if(emphasized) refp = e->GetReferencePos(); } if(constraint.v) { Constraint *c = SK.GetConstraint(constraint); c->Draw(); if(emphasized) refp = c->GetReferencePos(); } if(emphasized && (constraint.v || entity.v)) { // We want to emphasize this constraint or entity, by drawing a thick // line from the top left corner of the screen to the reference point // of that entity or constraint. double s = 0.501/SS.GW.scale; Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s); topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s)); topLeft = topLeft.Minus(SS.GW.offset); glLineWidth(40); DWORD rgb = Style::Color(Style::HOVERED); glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), 0.2); glBegin(GL_LINES); glxVertex3v(topLeft); glxVertex3v(refp); glEnd(); glLineWidth(1); } }
void TextWindow::MakeColorTable(const Color *in, float *out) { int i; for(i = 0; in[i].c != 0; i++) { int c = in[i].c; if(c < 0 || c > 255) oops(); out[c*3 + 0] = REDf(in[i].color); out[c*3 + 1] = GREENf(in[i].color); out[c*3 + 2] = BLUEf(in[i].color); } }
void TextWindow::ShowEditControlWithColorPicker(int halfRow, int col, DWORD rgb) { char str[1024]; sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb)); SS.later.showTW = true; editControl.colorPicker.show = true; editControl.colorPicker.rgb = rgb; editControl.colorPicker.h = 0; editControl.colorPicker.s = 0; editControl.colorPicker.v = 1; ShowEditControl(halfRow, col, str); }
void TextWindow::ShowGroupInfo(void) { Group *pg, *g = SK.group.FindById(shown.group); const char *s = "???"; if(shown.group.v == Group::HGROUP_REFERENCES.v) { Printf(true, "%FtGROUP %E%s", g->DescriptionString()); goto list_items; } else { Printf(true, "%FtGROUP %E%s [%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", g->DescriptionString(), g->h.v, &TextWindow::ScreenChangeGroupName, g->h.v, &TextWindow::ScreenDeleteGroup); } if(g->type == Group::LATHE) { Printf(true, " %Ftlathe plane sketch"); } else if(g->type == Group::EXTRUDE || g->type == Group::ROTATE || g->type == Group::TRANSLATE) { if(g->type == Group::EXTRUDE) { s = "extrude plane sketch"; } else if(g->type == Group::TRANSLATE) { s = "translate original sketch"; } else if(g->type == Group::ROTATE) { s = "rotate original sketch"; } Printf(true, " %Ft%s%E", s); bool one = (g->subtype == Group::ONE_SIDED); Printf(false, "%Ba %f%Ls%Fd%c one-sided%E " "%f%LS%Fd%c two-sided%E", &TextWindow::ScreenChangeGroupOption, one ? RADIO_TRUE : RADIO_FALSE, &TextWindow::ScreenChangeGroupOption, !one ? RADIO_TRUE : RADIO_FALSE); if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) { if(g->subtype == Group::ONE_SIDED) { bool skip = g->skipFirst; Printf(false, "%Bd %Ftstart %f%LK%Fd%c with original%E " "%f%Lk%Fd%c with copy #1%E", &ScreenChangeGroupOption, !skip ? RADIO_TRUE : RADIO_FALSE, &ScreenChangeGroupOption, skip ? RADIO_TRUE : RADIO_FALSE); } int times = (int)(g->valA); Printf(false, "%Bp %Ftrepeat%E %d time%s %Fl%Ll%D%f[change]%E", (g->subtype == Group::ONE_SIDED) ? 'a' : 'd', times, times == 1 ? "" : "s", g->h.v, &TextWindow::ScreenChangeExprA); } } else if(g->type == Group::IMPORTED) { Printf(true, " %Ftimport geometry from file%E"); Printf(false, "%Ba '%s'", g->impFileRel.c_str()); Printf(false, "%Bd %Ftscaled by%E %# %Fl%Ll%f%D[change]%E", g->scale, &TextWindow::ScreenChangeGroupScale, g->h.v); } else if(g->type == Group::DRAWING_3D) { Printf(true, " %Ftsketch in 3d%E"); } else if(g->type == Group::DRAWING_WORKPLANE) { Printf(true, " %Ftsketch in new workplane%E"); } else { Printf(true, "???"); } Printf(false, ""); if(g->type == Group::EXTRUDE || g->type == Group::LATHE || g->type == Group::IMPORTED) { bool un = (g->meshCombine == Group::COMBINE_AS_UNION); bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE); bool asa = (g->type == Group::IMPORTED); Printf(false, " %Ftsolid model as"); Printf(false, "%Ba %f%D%Lc%Fd%c union%E " "%f%D%Lc%Fd%c difference%E " "%f%D%Lc%Fd%c%s%E ", &TextWindow::ScreenChangeGroupOption, Group::COMBINE_AS_UNION, un ? RADIO_TRUE : RADIO_FALSE, &TextWindow::ScreenChangeGroupOption, Group::COMBINE_AS_DIFFERENCE, diff ? RADIO_TRUE : RADIO_FALSE, &TextWindow::ScreenChangeGroupOption, Group::COMBINE_AS_ASSEMBLE, asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : 0, asa ? " assemble" : ""); if(g->type == Group::EXTRUDE || g->type == Group::LATHE) { Printf(false, "%Bd %Ftcolor %E%Bp %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", 0x80000000 | g->color, REDf(g->color), GREENf(g->color), BLUEf(g->color), ScreenColor, top[rows-1] + 2); } else if(g->type == Group::IMPORTED) { Printf(false, " %Fd%f%LP%c suppress this group's solid model", &TextWindow::ScreenChangeGroupOption, g->suppress ? CHECK_TRUE : CHECK_FALSE); } Printf(false, ""); } Printf(false, " %f%Lv%Fd%c show entities from this group", &TextWindow::ScreenChangeGroupOption, g->visible ? CHECK_TRUE : CHECK_FALSE); pg = g->PreviousGroup(); if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) { Printf(false, " %f%Lf%Fd%c force NURBS surfaces to triangle mesh", &TextWindow::ScreenChangeGroupOption, g->forceToMesh ? CHECK_TRUE : CHECK_FALSE); } else { Printf(false, " (model already forced to triangle mesh)"); } Printf(true, " %f%Lr%Fd%c relax constraints and dimensions", &TextWindow::ScreenChangeGroupOption, g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE); Printf(false, " %f%Ld%Fd%c treat all dimensions as reference", &TextWindow::ScreenChangeGroupOption, g->allDimsReference ? CHECK_TRUE : CHECK_FALSE); if(g->booleanFailed) { Printf(false, ""); Printf(false, "The Boolean operation failed. It may be "); Printf(false, "possible to fix the problem by choosing "); Printf(false, "'force NURBS surfaces to triangle mesh'."); } list_items: Printf(false, ""); Printf(false, "%Ft requests in group"); int i, a = 0; for(i = 0; i < SK.request.n; i++) { Request *r = &(SK.request.elem[i]); if(r->group.v == shown.group.v) { char *s = r->DescriptionString(); Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", (a & 1) ? 'd' : 'a', r->h.v, (&TextWindow::ScreenSelectRequest), &(TextWindow::ScreenHoverRequest), s); a++; } } if(a == 0) Printf(false, "%Ba (none)"); a = 0; Printf(false, ""); Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof); for(i = 0; i < SK.constraint.n; i++) { Constraint *c = &(SK.constraint.elem[i]); if(c->group.v == shown.group.v) { char *s = c->DescriptionString(); Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s", (a & 1) ? 'd' : 'a', c->h.v, (&TextWindow::ScreenSelectConstraint), (&TextWindow::ScreenHoverConstraint), s, c->reference ? "(ref)" : ""); a++; } } if(a == 0) Printf(false, "%Ba (none)"); }
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm, Vector u, Vector v, Vector n, Vector origin, double cameraTan, VectorFileWriter *out) { double s = 1.0 / SS.exportScale; // Project into the export plane; so when we're done, z doesn't matter, // and x and y are what goes in the DXF. SEdge *e; for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) { // project into the specified csys, and apply export scale (e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); (e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); } SBezier *b; if(sbl) { for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { *b = b->InPerspective(u, v, n, origin, cameraTan); int i; for(i = 0; i <= b->deg; i++) { b->ctrl[i] = (b->ctrl[i]).ScaledBy(s); } } } // If cutter radius compensation is requested, then perform it now if(fabs(SS.exportOffset) > LENGTH_EPS) { // assemble those edges into a polygon, and clear the edge list SPolygon sp; ZERO(&sp); sel->AssemblePolygon(&sp, NULL); sel->Clear(); SPolygon compd; ZERO(&compd); sp.normal = Vector::From(0, 0, -1); sp.FixContourDirections(); sp.OffsetInto(&compd, SS.exportOffset*s); sp.Clear(); compd.MakeEdgesInto(sel); compd.Clear(); } // Now the triangle mesh; project, then build a BSP to perform // occlusion testing and generated the shaded surfaces. SMesh smp; ZERO(&smp); if(sm) { Vector l0 = (SS.lightDir[0]).WithMagnitude(1), l1 = (SS.lightDir[1]).WithMagnitude(1); STriangle *tr; for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) { STriangle tt = *tr; tt.a = (tt.a).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); tt.b = (tt.b).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); tt.c = (tt.c).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); // And calculate lighting for the triangle Vector n = tt.Normal().WithMagnitude(1); double lighting = SS.ambientIntensity + max(0, (SS.lightIntensity[0])*(n.Dot(l0))) + max(0, (SS.lightIntensity[1])*(n.Dot(l1))); double r = min(1, REDf (tt.meta.color)*lighting), g = min(1, GREENf(tt.meta.color)*lighting), b = min(1, BLUEf (tt.meta.color)*lighting); tt.meta.color = RGBf(r, g, b); smp.AddTriangle(&tt); } } // Use the BSP routines to generate the split triangles in paint order. SBsp3 *bsp = SBsp3::FromMesh(&smp); SMesh sms; ZERO(&sms); bsp->GenerateInPaintOrder(&sms); // And cull the back-facing triangles STriangle *tr; sms.l.ClearTags(); for(tr = sms.l.First(); tr; tr = sms.l.NextAfter(tr)) { Vector n = tr->Normal(); if(n.z < 0) { tr->tag = 1; } } sms.l.RemoveTagged(); // And now we perform hidden line removal if requested SEdgeList hlrd; ZERO(&hlrd); if(sm && !SS.GW.showHdnLines) { SKdNode *root = SKdNode::From(&smp); // Generate the edges where a curved surface turns from front-facing // to back-facing. if(SS.GW.showEdges) { root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES, false, NULL, NULL); } root->ClearTags(); int cnt = 1234; SEdge *se; for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { if(se->auxA == Style::CONSTRAINT) { // Constraints should not get hidden line removed; they're // always on top. hlrd.AddEdge(se->a, se->b, se->auxA); continue; } SEdgeList out; ZERO(&out); // Split the original edge against the mesh out.AddEdge(se->a, se->b, se->auxA); root->OcclusionTestLine(*se, &out, cnt); // the occlusion test splits unnecessarily; so fix those out.MergeCollinearSegments(se->a, se->b); cnt++; // And add the results to our output SEdge *sen; for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) { hlrd.AddEdge(sen->a, sen->b, sen->auxA); } out.Clear(); } sel = &hlrd; } // We kept the line segments and Beziers separate until now; but put them // 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(); sms.Clear(); hlrd.Clear(); }
void GraphicsWindow::Paint(void) { int i; havePainted = true; int w, h; GetGraphicsWindowSize(&w, &h); width = w; height = h; glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000); double mat[16]; // Last thing before display is to apply the perspective double clp = SS.CameraTangent()*scale; MakeMatrix(mat, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, clp, 1); glMultMatrixd(mat); // Before that, we apply the rotation Vector n = projUp.Cross(projRight); MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0, projUp.x, projUp.y, projUp.z, 0, n.x, n.y, n.z, 0, 0, 0, 0, 1); glMultMatrixd(mat); // And before that, the translation MakeMatrix(mat, 1, 0, 0, offset.x, 0, 1, 0, offset.y, 0, 0, 1, offset.z, 0, 0, 0, 1); glMultMatrixd(mat); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glShadeModel(GL_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); // don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards, // drawn with leaks in the mesh glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_DEPTH_TEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glEnable(GL_NORMALIZE); // At the same depth, we want later lines drawn over earlier. glDepthFunc(GL_LEQUAL); if(SS.AllGroupsOkay()) { glClearColor(REDf(SS.backgroundColor), GREENf(SS.backgroundColor), BLUEf(SS.backgroundColor), 1.0f); } else { // Draw a different background whenever we're having solve problems. DWORD rgb = Style::Color(Style::DRAW_ERROR); glClearColor(0.4f*REDf(rgb), 0.4f*GREENf(rgb), 0.4f*BLUEf(rgb), 1.0f); // And show the text window, which has info to debug it ForceTextWindowShown(); } glClear(GL_COLOR_BUFFER_BIT); glClearDepth(1.0); glClear(GL_DEPTH_BUFFER_BIT); if(SS.bgImage.fromFile) { // If a background image is loaded, then we draw it now as a texture. // This handles the resizing for us nicely. glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SS.bgImage.rw, SS.bgImage.rh, 0, GL_RGB, GL_UNSIGNED_BYTE, SS.bgImage.fromFile); double tw = ((double)SS.bgImage.w) / SS.bgImage.rw, th = ((double)SS.bgImage.h) / SS.bgImage.rh; double mmw = SS.bgImage.w / SS.bgImage.scale, mmh = SS.bgImage.h / SS.bgImage.scale; Vector origin = SS.bgImage.origin; origin = origin.DotInToCsys(projRight, projUp, n); // Place the depth of our origin at the point that corresponds to // w = 1, so that it's unaffected by perspective. origin.z = (offset.ScaledBy(-1)).Dot(n); origin = origin.ScaleOutOfCsys(projRight, projUp, n); // Place the background at the very back of the Z order, though, by // mucking with the depth range. glDepthRange(1, 1); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2d(0, 0); glxVertex3v(origin); glTexCoord2d(0, th); glxVertex3v(origin.Plus(projUp.ScaledBy(mmh))); glTexCoord2d(tw, th); glxVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus( projUp. ScaledBy(mmh)))); glTexCoord2d(tw, 0); glxVertex3v(origin.Plus(projRight.ScaledBy(mmw))); glEnd(); glDisable(GL_TEXTURE_2D); } glxDepthRangeOffset(0); // Nasty case when we're reloading the imported files; could be that // we get an error, so a dialog pops up, and a message loop starts, and // we have to get called to paint ourselves. If the sketch is screwed // up, then we could trigger an oops trying to draw. if(!SS.allConsistent) return; // Let's use two lights, at the user-specified locations GLfloat f; glEnable(GL_LIGHT0); f = (GLfloat)SS.lightIntensity[0]; GLfloat li0[] = { f, f, f, 1.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, li0); glLightfv(GL_LIGHT0, GL_SPECULAR, li0); glEnable(GL_LIGHT1); f = (GLfloat)SS.lightIntensity[1]; GLfloat li1[] = { f, f, f, 1.0f }; glLightfv(GL_LIGHT1, GL_DIFFUSE, li1); glLightfv(GL_LIGHT1, GL_SPECULAR, li1); Vector ld; ld = VectorFromProjs(SS.lightDir[0]); GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; glLightfv(GL_LIGHT0, GL_POSITION, ld0); ld = VectorFromProjs(SS.lightDir[1]); GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; glLightfv(GL_LIGHT1, GL_POSITION, ld1); if(SS.drawBackFaces) { // For debugging, draw the backs of the triangles in red, so that we // notice when a shell is open glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1); } else { glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0); } GLfloat ambient[4] = { (float)SS.ambientIntensity, (float)SS.ambientIntensity, (float)SS.ambientIntensity, 1 }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); glxUnlockColor(); if(showSnapGrid && LockedInWorkplane()) { hEntity he = ActiveWorkplane(); EntityBase *wrkpl = SK.GetEntity(he), *norm = wrkpl->Normal(); Vector wu, wv, wn, wp; wp = SK.GetEntity(wrkpl->point[0])->PointGetNum(); wu = norm->NormalU(); wv = norm->NormalV(); wn = norm->NormalN(); double g = SS.gridSpacing; double umin = VERY_POSITIVE, umax = VERY_NEGATIVE, vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE; int a; for(a = 0; a < 4; a++) { // Ideally, we would just do +/- half the width and height; but // allow some extra slop for rounding. Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g), vert = projUp. ScaledBy((0.6*height)/scale + 2*g); if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1); if(a == 1 || a == 3) vert = vert. ScaledBy(-1); Vector tp = horiz.Plus(vert).Minus(offset); // Project the point into our grid plane, normal to the screen // (not to the grid plane). If the plane is on edge then this is // impossible so don't try to draw the grid. bool parallel; Vector tpp = Vector::AtIntersectionOfPlaneAndLine( wn, wn.Dot(wp), tp, tp.Plus(n), ¶llel); if(parallel) goto nogrid; tpp = tpp.Minus(wp); double uu = tpp.Dot(wu), vv = tpp.Dot(wv); umin = min(uu, umin); umax = max(uu, umax); vmin = min(vv, vmin); vmax = max(vv, vmax); } int i, j, i0, i1, j0, j1; i0 = (int)(umin / g); i1 = (int)(umax / g); j0 = (int)(vmin / g); j1 = (int)(vmax / g); if(i0 > i1 || i1 - i0 > 400) goto nogrid; if(j0 > j1 || j1 - j0 > 400) goto nogrid; glLineWidth(1); glxColorRGBa(Style::Color(Style::DATUM), 0.3); glBegin(GL_LINES); for(i = i0 + 1; i < i1; i++) { glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g))); glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g))); } for(j = j0 + 1; j < j1; j++) { glxVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g))); glxVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g))); } glEnd(); // Clear the depth buffer, so that the grid is at the very back of // the Z order. glClear(GL_DEPTH_BUFFER_BIT); nogrid:; } // Draw the active group; this does stuff like the mesh and edges. (SK.GetGroup(activeGroup))->Draw(); // Now draw the entities if(showHdnLines) glDisable(GL_DEPTH_TEST); Entity::DrawAll(); // Draw filled paths in all groups, when those filled paths were requested // specially by assigning a style with a fill color, or when the filled // paths are just being filled by default. This should go last, to make // the transparency work. Group *g; for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { if(!(g->IsVisible())) continue; g->DrawFilledPaths(); } glDisable(GL_DEPTH_TEST); // Draw the constraints for(i = 0; i < SK.constraint.n; i++) { SK.constraint.elem[i].Draw(); } // Draw the traced path, if one exists glLineWidth(Style::Width(Style::ANALYZE)); glxColorRGB(Style::Color(Style::ANALYZE)); SContour *sc = &(SS.traced.path); glBegin(GL_LINE_STRIP); for(i = 0; i < sc->l.n; i++) { glxVertex3v(sc->l.elem[i].p); } glEnd(); // And the naked edges, if the user did Analyze -> Show Naked Edges. glLineWidth(Style::Width(Style::DRAW_ERROR)); glxColorRGB(Style::Color(Style::DRAW_ERROR)); glxDrawEdges(&(SS.nakedEdges), true); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); glxLockColorTo(Style::Color(Style::HOVERED)); hover.Draw(); // And finally draw the selection, same mechanism. glxLockColorTo(Style::Color(Style::SELECTED)); for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) { s->Draw(); } glxUnlockColor(); // If a marquee selection is in progress, then draw the selection // rectangle, as an outline and a transparent fill. if(pending.operation == DRAGGING_MARQUEE) { Point2d begin = ProjectPoint(orig.marqueePoint); double xmin = min(orig.mouse.x, begin.x), xmax = max(orig.mouse.x, begin.x), ymin = min(orig.mouse.y, begin.y), ymax = max(orig.mouse.y, begin.y); Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)), tr = UnProjectPoint(Point2d::From(xmax, ymin)), br = UnProjectPoint(Point2d::From(xmax, ymax)), bl = UnProjectPoint(Point2d::From(xmin, ymax)); glLineWidth((GLfloat)1.3); glxColorRGB(Style::Color(Style::HOVERED)); glBegin(GL_LINE_LOOP); glxVertex3v(tl); glxVertex3v(tr); glxVertex3v(br); glxVertex3v(bl); glEnd(); glxColorRGBa(Style::Color(Style::HOVERED), 0.10); glBegin(GL_QUADS); glxVertex3v(tl); glxVertex3v(tr); glxVertex3v(br); glxVertex3v(bl); glEnd(); } // An extra line, used to indicate the origin when rotating within the // plane of the monitor. if(SS.extraLine.draw) { glLineWidth(1); glxLockColorTo(Style::Color(Style::DATUM)); glBegin(GL_LINES); glxVertex3v(SS.extraLine.ptA); glxVertex3v(SS.extraLine.ptB); glEnd(); } // A note to indicate the origin in the just-exported file. if(SS.justExportedInfo.draw) { glxColorRGB(Style::Color(Style::DATUM)); Vector p = SS.justExportedInfo.pt, u = SS.justExportedInfo.u, v = SS.justExportedInfo.v; glLineWidth(1.5); glBegin(GL_LINES); glxVertex3v(p.Plus(u.WithMagnitude(-15/scale))); glxVertex3v(p.Plus(u.WithMagnitude(30/scale))); glxVertex3v(p.Plus(v.WithMagnitude(-15/scale))); glxVertex3v(p.Plus(v.WithMagnitude(30/scale))); glEnd(); glxWriteText("(x, y) = (0, 0) for file just exported", DEFAULT_TEXT_HEIGHT, p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)), u, v, NULL, NULL); glxWriteText("press Esc to clear this message", DEFAULT_TEXT_HEIGHT, p.Plus(u.ScaledBy(40/scale)).Plus( v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)), u, v, NULL, NULL); } // And finally the toolbar. if(SS.showToolbar) { ToolbarDraw(); } }