
This has several desirable consequences: * It is now possible to port SolveSpace to a later version of OpenGL, such as OpenGLES 2, so that it runs on platforms that only have that OpenGL version; * The majority of geometry is now rendered without references to the camera in C++ code, so a renderer can now submit it to the video card once and re-rasterize with a different projection matrix every time the projection is changed, avoiding expensive reuploads; * The DOGD (draw or get distance) interface is now a straightforward Canvas implementation; * There are no more direct references to SS.GW.(projection) in sketch rendering code, which allows rendering to multiple viewports; * There are no more unnecessary framebuffer flips on CPU on Cocoa and GTK; * The platform-dependent GL code is now confined to rendergl1.cpp. * The Microsoft and Apple headers required by it that are prone to identifier conflicts are no longer included globally; * The rendergl1.cpp implementation can now be omitted from compilation to run SolveSpace headless or with a different OpenGL version. Note these implementation details of Canvas: * GetCamera currently always returns a reference to the field `Camera camera;`. This is so that a future renderer that caches geometry in the video memory can define it as asserting, which would provide assurance against code that could accidentally put something projection-dependent in the cache; * Line and triangle rendering is specified through a level of indirection, hStroke and hFill. This is so that a future renderer that batches geometry could cheaply group identical styles. * DrawPixmap and DrawVectorText accept a (o,u,v) and not a matrix. This is so that a future renderer into an output format that uses 2d transforms (e.g. SVG) could easily derive those. Some additional internal changes were required to enable this: * Pixmap is now always passed as std::shared_ptr<{const ,}Pixmap>. This is so that the renderer could cache uploaded textures between API calls, which requires it to capture a (weak) reference. * The PlatformPathEqual function was properly extracted into platform-specific code. This is so that the <windows.h> header could be included only where needed (in platform/w32* as well as rendergl1.cpp). * The SBsp{2,3}::DebugDraw functions were removed. They can be rewritten using the Canvas API if they are ever needed. While no visual changes were originally intended, some minor fixes happened anyway: * The "emphasis" yellow line from top-left corner is now correctly rendered much wider. * The marquee rectangle is now pixel grid aligned. * The hidden entities now do not clobber the depth buffer, removing some minor artifacts. * The workplane "tab" now scales with the font used to render the workplane name. * The workplane name font is now taken from the normals style. * Workplane and constraint line stipple is insignificantly different. This is so that it can reuse the existing stipple codepaths; rendering of workplanes and constraints predates those. Some debug functionality was added: * In graphics window, an fps counter that becomes red when rendering under 60fps is drawn.
170 lines
4.7 KiB
C++
170 lines
4.7 KiB
C++
//-----------------------------------------------------------------------------
|
|
// The user-visible undo/redo operation; whenever they change something, we
|
|
// record our state and push it on a stack, and we pop the stack when they
|
|
// select undo.
|
|
//
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
//-----------------------------------------------------------------------------
|
|
#include "solvespace.h"
|
|
|
|
void SolveSpaceUI::UndoRemember() {
|
|
unsaved = true;
|
|
PushFromCurrentOnto(&undo);
|
|
UndoClearStack(&redo);
|
|
UndoEnableMenus();
|
|
}
|
|
|
|
void SolveSpaceUI::UndoUndo() {
|
|
if(undo.cnt <= 0) return;
|
|
|
|
PushFromCurrentOnto(&redo);
|
|
PopOntoCurrentFrom(&undo);
|
|
UndoEnableMenus();
|
|
}
|
|
|
|
void SolveSpaceUI::UndoRedo() {
|
|
if(redo.cnt <= 0) return;
|
|
|
|
PushFromCurrentOnto(&undo);
|
|
PopOntoCurrentFrom(&redo);
|
|
UndoEnableMenus();
|
|
}
|
|
|
|
void SolveSpaceUI::UndoEnableMenus() {
|
|
EnableMenuByCmd(Command::UNDO, undo.cnt > 0);
|
|
EnableMenuByCmd(Command::REDO, redo.cnt > 0);
|
|
}
|
|
|
|
void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
|
|
int i;
|
|
|
|
if(uk->cnt == MAX_UNDO) {
|
|
UndoClearState(&(uk->d[uk->write]));
|
|
// And then write in to this one again
|
|
} else {
|
|
(uk->cnt)++;
|
|
}
|
|
|
|
UndoState *ut = &(uk->d[uk->write]);
|
|
*ut = {};
|
|
for(i = 0; i < SK.group.n; i++) {
|
|
Group *src = &(SK.group.elem[i]);
|
|
Group dest = *src;
|
|
// And then clean up all the stuff that needs to be a deep copy,
|
|
// and zero out all the dynamic stuff that will get regenerated.
|
|
dest.clean = false;
|
|
dest.solved = {};
|
|
dest.polyLoops = {};
|
|
dest.bezierLoops = {};
|
|
dest.bezierOpens = {};
|
|
dest.polyError = {};
|
|
dest.thisMesh = {};
|
|
dest.runningMesh = {};
|
|
dest.thisShell = {};
|
|
dest.runningShell = {};
|
|
dest.displayMesh = {};
|
|
dest.displayEdges = {};
|
|
dest.displayOutlines = {};
|
|
|
|
dest.remap = {};
|
|
src->remap.DeepCopyInto(&(dest.remap));
|
|
|
|
dest.impMesh = {};
|
|
dest.impShell = {};
|
|
dest.impEntity = {};
|
|
ut->group.Add(&dest);
|
|
}
|
|
for(i = 0; i < SK.groupOrder.n; i++) {
|
|
ut->groupOrder.Add(&(SK.groupOrder.elem[i]));
|
|
}
|
|
for(i = 0; i < SK.request.n; i++) {
|
|
ut->request.Add(&(SK.request.elem[i]));
|
|
}
|
|
for(i = 0; i < SK.constraint.n; i++) {
|
|
Constraint *src = &(SK.constraint.elem[i]);
|
|
Constraint dest = *src;
|
|
ut->constraint.Add(&dest);
|
|
}
|
|
for(i = 0; i < SK.param.n; i++) {
|
|
ut->param.Add(&(SK.param.elem[i]));
|
|
}
|
|
for(i = 0; i < SK.style.n; i++) {
|
|
ut->style.Add(&(SK.style.elem[i]));
|
|
}
|
|
ut->activeGroup = SS.GW.activeGroup;
|
|
|
|
uk->write = WRAP(uk->write + 1, MAX_UNDO);
|
|
}
|
|
|
|
void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
|
|
int i;
|
|
|
|
ssassert(uk->cnt > 0, "Cannot pop from empty undo stack");
|
|
(uk->cnt)--;
|
|
uk->write = WRAP(uk->write - 1, MAX_UNDO);
|
|
|
|
UndoState *ut = &(uk->d[uk->write]);
|
|
|
|
// Free everything in the main copy of the program before replacing it
|
|
for(i = 0; i < SK.groupOrder.n; i++) {
|
|
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
|
|
g->Clear();
|
|
}
|
|
SK.group.Clear();
|
|
SK.groupOrder.Clear();
|
|
SK.request.Clear();
|
|
SK.constraint.Clear();
|
|
SK.param.Clear();
|
|
SK.style.Clear();
|
|
|
|
// And then do a shallow copy of the state from the undo list
|
|
ut->group.MoveSelfInto(&(SK.group));
|
|
for(i = 0; i < ut->groupOrder.n; i++)
|
|
SK.groupOrder.Add(&ut->groupOrder.elem[i]);
|
|
ut->request.MoveSelfInto(&(SK.request));
|
|
ut->constraint.MoveSelfInto(&(SK.constraint));
|
|
ut->param.MoveSelfInto(&(SK.param));
|
|
ut->style.MoveSelfInto(&(SK.style));
|
|
SS.GW.activeGroup = ut->activeGroup;
|
|
|
|
// No need to free it, since a shallow copy was made above
|
|
*ut = {};
|
|
|
|
// And reset the state everywhere else in the program, since the
|
|
// sketch just changed a lot.
|
|
SS.GW.ClearSuper();
|
|
SS.TW.ClearSuper();
|
|
SS.ReloadAllImported();
|
|
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
|
|
SS.ScheduleShowTW();
|
|
|
|
// Activate the group that was active before.
|
|
Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
|
|
activeGroup->Activate();
|
|
}
|
|
|
|
void SolveSpaceUI::UndoClearStack(UndoStack *uk) {
|
|
while(uk->cnt > 0) {
|
|
uk->write = WRAP(uk->write - 1, MAX_UNDO);
|
|
(uk->cnt)--;
|
|
UndoClearState(&(uk->d[uk->write]));
|
|
}
|
|
*uk = {}; // for good measure
|
|
}
|
|
|
|
void SolveSpaceUI::UndoClearState(UndoState *ut) {
|
|
int i;
|
|
for(i = 0; i < ut->group.n; i++) {
|
|
Group *g = &(ut->group.elem[i]);
|
|
|
|
g->remap.Clear();
|
|
}
|
|
ut->group.Clear();
|
|
ut->request.Clear();
|
|
ut->constraint.Clear();
|
|
ut->param.Clear();
|
|
ut->style.Clear();
|
|
*ut = {};
|
|
}
|
|
|