bool SolveSpaceUI::ActiveGroupsOkay() { for(int i = 0; i < SK.groupOrder.n; i++) { Group *g = SK.GetGroup(SK.groupOrder.elem[i]); if(!g->IsSolvedOkay()) return false; if(g->h.v == SS.GW.activeGroup.v) break; } return true; }
void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) { int i; // Clear out the system to be solved. sys.entity.Clear(); sys.param.Clear(); sys.eq.Clear(); // And generate all the params for requests in this group for(i = 0; i < SK.request.n; i++) { Request *r = &(SK.request.elem[i]); if(r->group.v != hg.v) continue; r->Generate(&(sys.entity), &(sys.param)); } // And for the group itself Group *g = SK.GetGroup(hg); g->Generate(&(sys.entity), &(sys.param)); // Set the initial guesses for all the params for(i = 0; i < sys.param.n; i++) { Param *p = &(sys.param.elem[i]); p->known = false; p->val = SK.GetParam(p->h)->val; } MarkDraggedParams(); g->solved.remove.Clear(); SolveResult how = sys.Solve(g, &(g->solved.dof), &(g->solved.remove), /*andFindBad=*/true, andFindFree); bool isOkay = how == SolveResult::OKAY || (g->allowRedundant && how == SolveResult::REDUNDANT_OKAY); if(!isOkay || (isOkay && !g->IsSolvedOkay())) { TextWindow::ReportHowGroupSolved(g->h); } g->solved.how = how; FreeAllTemporary(); }
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) { int first, last, i, j; SK.groupOrder.Clear(); for(int i = 0; i < SK.group.n; i++) SK.groupOrder.Add(&SK.group.elem[i].h); std::sort(&SK.groupOrder.elem[0], &SK.groupOrder.elem[SK.groupOrder.n], [](const hGroup &ha, const hGroup &hb) { return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order; }); switch(type) { case Generate::DIRTY: { first = INT_MAX; 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->IsSolvedOkay()) { 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; } } // If we're generating entities for display, first we need to find // the bounding box to turn relative chord tolerance to absolute. if(!SS.exportMode && !genForBBox) { GenerateAll(type, andFindFree, /*genForBBox=*/true); BBox box = SK.CalculateEntityBBox(/*includeInvisibles=*/true); Vector size = box.maxp.Minus(box.minp); double maxSize = std::max({ size.x, size.y, size.z }); chordTolCalculated = maxSize * chordTol / 100.0; } // Remove any requests or constraints that refer to a nonexistent // group; can check those immediately, since we know what the list // of groups should be. while(PruneOrphans()) ; // Don't lose our numerical guesses when we regenerate. IdList<Param,hParam> prev = {}; SK.param.MoveSelfInto(&prev); SK.entity.Clear(); for(i = 0; i < SK.groupOrder.n; i++) { Group *g = SK.GetGroup(SK.groupOrder.elem[i]); // The group may depend on entities or other groups, to define its // workplane geometry or for its operands. Those must already exist // in a previous group, so check them before generating. if(PruneGroups(g->h)) goto pruned; for(j = 0; j < SK.request.n; j++) { Request *r = &(SK.request.elem[j]); if(r->group.v != g->h.v) continue; r->Generate(&(SK.entity), &(SK.param)); } g->Generate(&(SK.entity), &(SK.param)); // The requests and constraints depend on stuff in this or the // previous group, so check them after generating. if(PruneRequests(g->h) || PruneConstraints(g->h)) goto pruned; // Use the previous values for params that we've seen before, as // initial guesses for the solver. for(j = 0; j < SK.param.n; j++) { Param *newp = &(SK.param.elem[j]); if(newp->known) continue; Param *prevp = prev.FindByIdNoOops(newp->h); if(prevp) { newp->val = prevp->val; newp->free = prevp->free; } } if(g->h.v == Group::HGROUP_REFERENCES.v) { ForceReferences(); g->solved.how = SolveResult::OKAY; g->clean = true; } else { if(i >= first && i <= last) { // The group falls inside the range, so really solve it, // and then regenerate the mesh based on the solved stuff. if(genForBBox) { SolveGroup(g->h, andFindFree); } else { g->GenerateLoops(); g->GenerateShellAndMesh(); g->clean = true; } } else { // The group falls outside the range, so just assume that // it's good wherever we left it. The mesh is unchanged, // and the parameters must be marked as known. for(j = 0; j < SK.param.n; j++) { Param *newp = &(SK.param.elem[j]); Param *prevp = prev.FindByIdNoOops(newp->h); if(prevp) newp->known = true; } } } } // And update any reference dimensions with their new values for(i = 0; i < SK.constraint.n; i++) { Constraint *c = &(SK.constraint.elem[i]); if(c->reference) { c->ModifyToSatisfy(); } } // Make sure the point that we're tracing exists. if(traced.point.v && !SK.entity.FindByIdNoOops(traced.point)) { traced.point = Entity::NO_ENTITY; } // And if we're tracing a point, add its new value to the path if(traced.point.v) { Entity *pt = SK.GetEntity(traced.point); traced.path.AddPoint(pt->PointGetNum()); } prev.Clear(); InvalidateGraphics(); // Remove nonexistent selection items, for same reason we waited till // the end to put up a dialog box. GW.ClearNonexistentSelectionItems(); if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) { // All sorts of interesting things could have happened; for example, // the active group or active workplane could have been deleted. So // clear all that out. if(deleted.groups > 0) { SS.TW.ClearSuper(); } ScheduleShowTW(); GW.ClearSuper(); // People get annoyed if I complain whenever they delete any request, // and I otherwise will, since those always come with pt-coincident // constraints. if(deleted.requests > 0 || deleted.nonTrivialConstraints > 0 || deleted.groups > 0) { // Don't display any errors until we've regenerated fully. The // sketch is not necessarily in a consistent state until we've // pruned any orphaned etc. objects, and the message loop for the // messagebox could allow us to repaint and crash. But now we must // be fine. Message("Additional sketch elements were deleted, because they " "depend on the element that was just deleted explicitly. " "These include: \n" " %d request%s\n" " %d constraint%s\n" " %d group%s" "%s", deleted.requests, deleted.requests == 1 ? "" : "s", deleted.constraints, deleted.constraints == 1 ? "" : "s", deleted.groups, deleted.groups == 1 ? "" : "s", undo.cnt > 0 ? "\n\nChoose Edit -> Undo to undelete all elements." : ""); } deleted = {}; } FreeAllTemporary(); allConsistent = true; return; pruned: // Restore the numerical guesses SK.param.Clear(); prev.MoveSelfInto(&(SK.param)); // Try again GenerateAll(type, andFindFree, genForBBox); }