Allow generating groups in arbitrary order.

pull/4/head
whitequark 2016-02-17 10:03:07 +00:00
parent df87ac6e6f
commit c9648805ea
10 changed files with 119 additions and 87 deletions

View File

@ -689,8 +689,8 @@ nogrid:;
// specially by assigning a style with a fill color, or when the filled // 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 // paths are just being filled by default. This should go last, to make
// the transparency work. // the transparency work.
Group *g; for(i = 0; i < SK.groupOrder.n; i++) {
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(!(g->IsVisible())) continue; if(!(g->IsVisible())) continue;
g->DrawFilledPaths(); g->DrawFilledPaths();
} }

View File

@ -20,14 +20,15 @@ void SolveSpaceUI::ClearExisting(void) {
UndoClearStack(&redo); UndoClearStack(&redo);
UndoClearStack(&undo); UndoClearStack(&undo);
Group *g; for(int i = 0; i < SK.groupOrder.n; i++) {
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
g->Clear(); g->Clear();
} }
SK.constraint.Clear(); SK.constraint.Clear();
SK.request.Clear(); SK.request.Clear();
SK.group.Clear(); SK.group.Clear();
SK.groupOrder.Clear();
SK.style.Clear(); SK.style.Clear();
SK.entity.Clear(); SK.entity.Clear();
@ -39,12 +40,13 @@ hGroup SolveSpaceUI::CreateDefaultDrawingGroup(void) {
// And an empty group, for the first stuff the user draws. // And an empty group, for the first stuff the user draws.
g.visible = true; g.visible = true;
g.name = "sketch-in-plane";
g.type = Group::DRAWING_WORKPLANE; g.type = Group::DRAWING_WORKPLANE;
g.subtype = Group::WORKPLANE_BY_POINT_ORTHO; g.subtype = Group::WORKPLANE_BY_POINT_ORTHO;
g.order = 1;
g.predef.q = Quaternion::From(1, 0, 0, 0); g.predef.q = Quaternion::From(1, 0, 0, 0);
hRequest hr = Request::HREQUEST_REFERENCE_XY; hRequest hr = Request::HREQUEST_REFERENCE_XY;
g.predef.origin = hr.entity(1); g.predef.origin = hr.entity(1);
g.name = "sketch-in-plane";
SK.group.AddAndAssignId(&g); SK.group.AddAndAssignId(&g);
SK.GetGroup(g.h)->activeWorkplane = g.h.entity(0); SK.GetGroup(g.h)->activeWorkplane = g.h.entity(0);
return g.h; return g.h;
@ -58,6 +60,7 @@ void SolveSpaceUI::NewFile(void) {
g.visible = true; g.visible = true;
g.name = "#references"; g.name = "#references";
g.type = Group::DRAWING_3D; g.type = Group::DRAWING_3D;
g.order = 0;
g.h = Group::HGROUP_REFERENCES; g.h = Group::HGROUP_REFERENCES;
SK.group.Add(&g); SK.group.Add(&g);
@ -308,7 +311,8 @@ bool SolveSpaceUI::SaveToFile(const std::string &filename) {
// A group will have either a mesh or a shell, but not both; but the code // A group will have either a mesh or a shell, but not both; but the code
// to print either of those just does nothing if the mesh/shell is empty. // to print either of those just does nothing if the mesh/shell is empty.
SMesh *m = &(SK.group.elem[SK.group.n-1].runningMesh); Group *g = SK.GetGroup(SK.groupOrder.elem[SK.groupOrder.n - 1]);
SMesh *m = &g->runningMesh;
for(i = 0; i < m->l.n; i++) { for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]); STriangle *tr = &(m->l.elem[i]);
fprintf(fh, "Triangle %08x %08x " fprintf(fh, "Triangle %08x %08x "
@ -317,7 +321,7 @@ bool SolveSpaceUI::SaveToFile(const std::string &filename) {
CO(tr->a), CO(tr->b), CO(tr->c)); CO(tr->a), CO(tr->b), CO(tr->c));
} }
SShell *s = &(SK.group.elem[SK.group.n-1].runningShell); SShell *s = &g->runningShell;
SSurface *srf; SSurface *srf;
for(srf = s->surface.First(); srf; srf = s->surface.NextAfter(srf)) { for(srf = s->surface.First(); srf; srf = s->surface.NextAfter(srf)) {
fprintf(fh, "Surface %08x %08x %08x %d %d\n", fprintf(fh, "Surface %08x %08x %08x %d %d\n",

View File

@ -16,8 +16,8 @@ void SolveSpaceUI::MarkGroupDirtyByEntity(hEntity he) {
void SolveSpaceUI::MarkGroupDirty(hGroup hg) { void SolveSpaceUI::MarkGroupDirty(hGroup hg) {
int i; int i;
bool go = false; bool go = false;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(g->h.v == hg.v) { if(g->h.v == hg.v) {
go = true; go = true;
} }
@ -58,8 +58,8 @@ bool SolveSpaceUI::GroupsInOrder(hGroup before, hGroup after) {
int beforep = -1, afterp = -1; int beforep = -1, afterp = -1;
int i; int i;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(g->h.v == before.v) beforep = i; if(g->h.v == before.v) beforep = i;
if(g->h.v == after.v) afterp = i; if(g->h.v == after.v) afterp = i;
} }
@ -141,52 +141,71 @@ bool SolveSpaceUI::PruneConstraints(hGroup hg) {
return false; return false;
} }
void SolveSpaceUI::GenerateAll(void) { void SolveSpaceUI::GenerateAll(GenerateType type, bool andFindFree, bool genForBBox) {
int i; int first, last, i, j;
int firstDirty = INT_MAX, lastVisible = 0;
// Start from the first dirty group, and solve until the active group, SK.groupOrder.Clear();
// since all groups after the active group are hidden. for(int i = 0; i < SK.group.n; i++)
for(i = 0; i < SK.group.n; i++) { SK.groupOrder.Add(&SK.group.elem[i].h);
Group *g = &(SK.group.elem[i]); std::sort(&SK.groupOrder.elem[0], &SK.groupOrder.elem[SK.groupOrder.n],
g->order = i; [](const hGroup &ha, const hGroup &hb) {
if((!g->clean) || (g->solved.how != System::SOLVED_OKAY)) { return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
firstDirty = min(firstDirty, i); });
}
if(g->h.v == SS.GW.activeGroup.v) {
lastVisible = i;
}
}
if(firstDirty == INT_MAX || lastVisible == 0) {
// All clean; so just regenerate the entities, and don't solve anything.
GenerateAll(GENERATE_REGEN);
} else {
SS.nakedEdges.Clear();
GenerateAll(firstDirty, lastVisible);
}
}
void SolveSpaceUI::GenerateAll(GenerateType type, bool andFindFree) {
switch(type) { switch(type) {
case GENERATE_ALL: GenerateAll(0, INT_MAX, andFindFree); break; case GENERATE_DIRTY: {
case GENERATE_REGEN: GenerateAll(-1, -1, andFindFree); break; first = INT_MAX;
case GENERATE_UNTIL_ACTIVE: GenerateAll(0, -2, andFindFree); break; last = 0;
// Start from the first dirty group, and solve until the active group,
// since all groups after the active group are hidden.
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if((!g->clean) || (g->solved.how != System::SOLVED_OKAY)) {
first = min(first, i);
}
if(g->h.v == SS.GW.activeGroup.v) {
last = i;
}
}
if(first == INT_MAX || last == 0) {
// All clean; so just regenerate the entities, and don't solve anything.
first = -1;
last = -1;
} else {
SS.nakedEdges.Clear();
}
break;
}
case GENERATE_ALL:
first = 0;
last = INT_MAX;
break;
case GENERATE_REGEN:
first = -1;
last = -1;
break;
case GENERATE_UNTIL_ACTIVE: {
for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.groupOrder.elem[i].v == SS.GW.activeGroup.v)
break;
}
first = 0;
last = i;
break;
}
default: oops(); default: oops();
} }
}
void SolveSpaceUI::GenerateAll(int first, int last, bool andFindFree, bool genForBBox) {
int i, j;
// generate until active group
if(last == -2) {
last = SK.group.IndexOf(SS.GW.activeGroup);
if(last == -1) last = INT_MAX;
}
// If we're generating entities for display, first we need to find // If we're generating entities for display, first we need to find
// the bounding box to turn relative chord tolerance to absolute. // the bounding box to turn relative chord tolerance to absolute.
if(!SS.exportMode && !genForBBox) { if(!SS.exportMode && !genForBBox) {
GenerateAll(first, last, /*andFindFree=*/false, /*genForBBox=*/true); GenerateAll(type, /*andFindFree=*/false, /*genForBBox=*/true);
BBox box = SK.CalculateEntityBBox(/*includeInvisibles=*/true); BBox box = SK.CalculateEntityBBox(/*includeInvisibles=*/true);
Vector size = box.maxp.Minus(box.minp); Vector size = box.maxp.Minus(box.minp);
double maxSize = std::max({ size.x, size.y, size.z }); double maxSize = std::max({ size.x, size.y, size.z });
@ -207,19 +226,19 @@ void SolveSpaceUI::GenerateAll(int first, int last, bool andFindFree, bool genFo
int64_t inTime = GetMilliseconds(); int64_t inTime = GetMilliseconds();
bool displayedStatusMessage = false; bool displayedStatusMessage = false;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
int64_t now = GetMilliseconds(); int64_t now = GetMilliseconds();
// Display the status message if we've taken more than 400 ms, or // Display the status message if we've taken more than 400 ms, or
// if we've taken 200 ms but we're not even halfway done, or if // if we've taken 200 ms but we're not even halfway done, or if
// we've already started displaying the status message. // we've already started displaying the status message.
if( (now - inTime > 400) || if( (now - inTime > 400) ||
((now - inTime > 200) && i < (SK.group.n / 2)) || ((now - inTime > 200) && i < (SK.groupOrder.n / 2)) ||
displayedStatusMessage) displayedStatusMessage)
{ {
displayedStatusMessage = true; displayedStatusMessage = true;
std::string msg = ssprintf("generating group %d/%d", i, SK.group.n); std::string msg = ssprintf("generating group %d/%d", i, SK.groupOrder.n);
int w, h; int w, h;
GetGraphicsWindowSize(&w, &h); GetGraphicsWindowSize(&w, &h);
@ -377,7 +396,7 @@ pruned:
SK.param.Clear(); SK.param.Clear();
prev.MoveSelfInto(&(SK.param)); prev.MoveSelfInto(&(SK.param));
// Try again // Try again
GenerateAll(first, last, andFindFree, genForBBox); GenerateAll(type, andFindFree, genForBBox);
} }
void SolveSpaceUI::ForceReferences(void) { void SolveSpaceUI::ForceReferences(void) {
@ -507,11 +526,11 @@ void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
} }
bool SolveSpaceUI::ActiveGroupsOkay() { bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.group.n; i++) { for(int i = 0; i < SK.groupOrder.n; i++) {
Group *group = &SK.group.elem[i]; Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(!group->IsSolvedOkay()) if(!g->IsSolvedOkay())
return false; return false;
if(group->h.v == SS.GW.activeGroup.v) if(g->h.v == SS.GW.activeGroup.v)
break; break;
} }
return true; return true;

View File

@ -222,7 +222,7 @@ void GraphicsWindow::Init(void) {
orig.projUp = projUp; orig.projUp = projUp;
// And with the last group active // And with the last group active
activeGroup = SK.group.elem[SK.group.n-1].h; activeGroup = SK.groupOrder.elem[SK.groupOrder.n - 1];
SK.GetGroup(activeGroup)->Activate(); SK.GetGroup(activeGroup)->Activate();
showWorkplanes = false; showWorkplanes = false;
@ -592,12 +592,12 @@ void GraphicsWindow::EnsureValidActives(void) {
Group *g = SK.group.FindByIdNoOops(activeGroup); Group *g = SK.group.FindByIdNoOops(activeGroup);
if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) { if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
int i; int i;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.group.elem[i].h.v != Group::HGROUP_REFERENCES.v) { if(SK.groupOrder.elem[i].v != Group::HGROUP_REFERENCES.v) {
break; break;
} }
} }
if(i >= SK.group.n) { if(i >= SK.groupOrder.n) {
// This can happen if the user deletes all the groups in the // This can happen if the user deletes all the groups in the
// sketch. It's difficult to prevent that, because the last // sketch. It's difficult to prevent that, because the last
// group might have been deleted automatically, because it failed // group might have been deleted automatically, because it failed
@ -606,7 +606,7 @@ void GraphicsWindow::EnsureValidActives(void) {
// to delete the references, though. // to delete the references, though.
activeGroup = SS.CreateDefaultDrawingGroup(); activeGroup = SS.CreateDefaultDrawingGroup();
} else { } else {
activeGroup = SK.group.elem[i].h; activeGroup = SK.groupOrder.elem[i];
} }
SK.GetGroup(activeGroup)->Activate(); SK.GetGroup(activeGroup)->Activate();
change = true; change = true;

View File

@ -56,6 +56,9 @@ void Group::MenuGroup(int id) {
g.color = RGBi(100, 100, 100); g.color = RGBi(100, 100, 100);
g.scale = 1; g.scale = 1;
Group *gl = SK.GetGroup(SK.groupOrder.elem[SK.groupOrder.n - 1]);
g.order = gl->order + 1;
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) { if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
g.impFile = RecentFile[id-RECENT_IMPORT]; g.impFile = RecentFile[id-RECENT_IMPORT];
id = GraphicsWindow::MNU_GROUP_IMPORT; id = GraphicsWindow::MNU_GROUP_IMPORT;

View File

@ -414,12 +414,12 @@ void Group::GenerateDisplayItems(void) {
Group *Group::PreviousGroup(void) { Group *Group::PreviousGroup(void) {
int i; int i;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(g->h.v == h.v) break; if(g->h.v == h.v) break;
} }
if(i == 0 || i >= SK.group.n) return NULL; if(i == 0 || i >= SK.groupOrder.n) return NULL;
return &(SK.group.elem[i-1]); return SK.GetGroup(SK.groupOrder.elem[i - 1]);
} }
Group *Group::RunningMeshGroup(void) { Group *Group::RunningMeshGroup(void) {

View File

@ -811,6 +811,7 @@ void SolveSpaceUI::Clear(void) {
void Sketch::Clear(void) { void Sketch::Clear(void) {
group.Clear(); group.Clear();
groupOrder.Clear();
constraint.Clear(); constraint.Clear();
request.Clear(); request.Clear();
style.Clear(); style.Clear();

View File

@ -628,6 +628,7 @@ class Sketch {
public: public:
// These are user-editable, and define the sketch. // These are user-editable, and define the sketch.
IdList<Group,hGroup> group; IdList<Group,hGroup> group;
List<hGroup> groupOrder;
IdList<CONSTRAINT,hConstraint> constraint; IdList<CONSTRAINT,hConstraint> constraint;
IdList<Request,hRequest> request; IdList<Request,hRequest> request;
IdList<Style,hStyle> style; IdList<Style,hStyle> style;
@ -659,6 +660,7 @@ public:
// The state for undo/redo // The state for undo/redo
typedef struct { typedef struct {
IdList<Group,hGroup> group; IdList<Group,hGroup> group;
List<hGroup> groupOrder;
IdList<Request,hRequest> request; IdList<Request,hRequest> request;
IdList<Constraint,hConstraint> constraint; IdList<Constraint,hConstraint> constraint;
IdList<Param,hParam> param; IdList<Param,hParam> param;
@ -879,14 +881,14 @@ public:
bool PruneConstraints(hGroup hg); bool PruneConstraints(hGroup hg);
enum GenerateType { enum GenerateType {
GENERATE_DIRTY,
GENERATE_ALL, GENERATE_ALL,
GENERATE_REGEN, GENERATE_REGEN,
GENERATE_UNTIL_ACTIVE, GENERATE_UNTIL_ACTIVE,
}; };
void GenerateAll(GenerateType type, bool andFindFree = false); void GenerateAll(GenerateType type = GENERATE_DIRTY, bool andFindFree = false,
void GenerateAll(void); bool genForBBox = false);
void GenerateAll(int first, int last, bool andFindFree = false, bool genForBBox = false);
void SolveGroup(hGroup hg, bool andFindFree); void SolveGroup(hGroup hg, bool andFindFree);
void MarkDraggedParams(void); void MarkDraggedParams(void);
void ForceReferences(void); void ForceReferences(void);

View File

@ -56,15 +56,10 @@ void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
SS.GenerateAll(); SS.GenerateAll();
} }
void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) { void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
int i; bool state = link == 's';
for(i = 0; i < SK.group.n; i++) { for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
g->visible = state;
if(link == 's') {
g->visible = true;
} else {
g->visible = false;
}
} }
} }
void TextWindow::ScreenActivateGroup(int link, uint32_t v) { void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
@ -105,8 +100,8 @@ void TextWindow::ShowListOfGroups(void) {
Printf(false, "%Ft shown ok group-name%E"); Printf(false, "%Ft shown ok group-name%E");
int i; int i;
bool afterActive = false; bool afterActive = false;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
std::string s = g->DescriptionString(); std::string s = g->DescriptionString();
bool active = (g->h.v == SS.GW.activeGroup.v); bool active = (g->h.v == SS.GW.activeGroup.v);
bool shown = g->visible; bool shown = g->visible;
@ -260,14 +255,14 @@ void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
"before proceeding."); "before proceeding.");
return; return;
} }
SK.group.RemoveById(SS.TW.shown.group); SK.group.RemoveById(hg);
// This is a major change, so let's re-solve everything. // This is a major change, so let's re-solve everything.
SS.TW.ClearSuper(); SS.TW.ClearSuper();
SS.GW.ClearSuper(); SS.GW.ClearSuper();
SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
} }
void TextWindow::ShowGroupInfo(void) { void TextWindow::ShowGroupInfo(void) {
Group *g = SK.group.FindById(shown.group); Group *g = SK.GetGroup(shown.group);
const char *s = "???"; const char *s = "???";
if(shown.group.v == Group::HGROUP_REFERENCES.v) { if(shown.group.v == Group::HGROUP_REFERENCES.v) {
@ -465,7 +460,7 @@ void TextWindow::ScreenAllowRedundant(int link, uint32_t v) {
SS.TW.Show(); SS.TW.Show();
} }
void TextWindow::ShowGroupSolveInfo(void) { void TextWindow::ShowGroupSolveInfo(void) {
Group *g = SK.group.FindById(shown.group); Group *g = SK.GetGroup(shown.group);
if(g->IsSolvedOkay()) { if(g->IsSolvedOkay()) {
// Go back to the default group info screen // Go back to the default group info screen
shown.screen = SCREEN_GROUP_INFO; shown.screen = SCREEN_GROUP_INFO;

View File

@ -73,6 +73,9 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
dest.impEntity = {}; dest.impEntity = {};
ut->group.Add(&dest); 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++) { for(i = 0; i < SK.request.n; i++) {
ut->request.Add(&(SK.request.elem[i])); ut->request.Add(&(SK.request.elem[i]));
} }
@ -94,6 +97,8 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
} }
void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) { void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
int i;
if(uk->cnt <= 0) oops(); if(uk->cnt <= 0) oops();
(uk->cnt)--; (uk->cnt)--;
uk->write = WRAP(uk->write - 1, MAX_UNDO); uk->write = WRAP(uk->write - 1, MAX_UNDO);
@ -101,11 +106,12 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
UndoState *ut = &(uk->d[uk->write]); UndoState *ut = &(uk->d[uk->write]);
// Free everything in the main copy of the program before replacing it // Free everything in the main copy of the program before replacing it
Group *g; for(i = 0; i < SK.groupOrder.n; i++) {
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
g->Clear(); g->Clear();
} }
SK.group.Clear(); SK.group.Clear();
SK.groupOrder.Clear();
SK.request.Clear(); SK.request.Clear();
SK.constraint.Clear(); SK.constraint.Clear();
SK.param.Clear(); SK.param.Clear();
@ -113,6 +119,8 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
// And then do a shallow copy of the state from the undo list // And then do a shallow copy of the state from the undo list
ut->group.MoveSelfInto(&(SK.group)); 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->request.MoveSelfInto(&(SK.request));
ut->constraint.MoveSelfInto(&(SK.constraint)); ut->constraint.MoveSelfInto(&(SK.constraint));
ut->param.MoveSelfInto(&(SK.param)); ut->param.MoveSelfInto(&(SK.param));
@ -131,7 +139,7 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
SS.ScheduleShowTW(); SS.ScheduleShowTW();
// Activate the group that was active before. // Activate the group that was active before.
Group *activeGroup = SK.group.FindById(SS.GW.activeGroup); Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
activeGroup->Activate(); activeGroup->Activate();
} }