#include "solvespace.h"
void SolveSpaceUI::MarkGroupDirtyByEntity(hEntity he) {
Entity *e = SK.GetEntity(he);
MarkGroupDirty(e->group);
}
void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
bool go = false;
for(auto const &gh : SK.groupOrder) {
Group *g = SK.GetGroup(gh);
if(g->h == hg) {
go = true;
}
if(go) {
g->clean = false;
if(onlyThis) break;
}
}
unsaved = true;
ScheduleGenerateAll();
}
bool SolveSpaceUI::PruneOrphans() {
auto r = std::find_if(SK.request.begin(), SK.request.end(),
[&](Request &r) { return !GroupExists(r.group); });
if(r != SK.request.end()) {
(deleted.requests)++;
SK.request.RemoveById(r->h);
return true;
}
auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(),
[&](Constraint &c) { return !GroupExists(c.group); });
if(c != SK.constraint.end()) {
(deleted.constraints)++;
(deleted.nonTrivialConstraints)++;
SK.constraint.RemoveById(c->h);
return true;
}
return false;
}
bool SolveSpaceUI::GroupsInOrder(hGroup before, hGroup after) {
if(before.v == 0) return true;
if(after.v == 0) return true;
if(!GroupExists(before)) return false;
if(!GroupExists(after)) return false;
int beforep = SK.GetGroup(before)->order;
int afterp = SK.GetGroup(after)->order;
if(beforep >= afterp) return false;
return true;
}
bool SolveSpaceUI::GroupExists(hGroup hg) {
return SK.group.FindByIdNoOops(hg) ? true : false;
}
bool SolveSpaceUI::EntityExists(hEntity he) {
if(he == Entity::NO_ENTITY) return true;
return SK.entity.FindByIdNoOops(he) ? true : false;
}
bool SolveSpaceUI::PruneGroups(hGroup hg) {
Group *g = SK.GetGroup(hg);
if(GroupsInOrder(g->opA, hg) &&
EntityExists(g->predef.origin) &&
EntityExists(g->predef.entityB) &&
EntityExists(g->predef.entityC))
{
return false;
}
(deleted.groups)++;
SK.group.RemoveById(g->h);
return true;
}
bool SolveSpaceUI::PruneRequests(hGroup hg) {
auto e = std::find_if(SK.entity.begin(), SK.entity.end(),
[&](Entity &e) { return e.group == hg && !EntityExists(e.workplane); });
if(e != SK.entity.end()) {
(deleted.requests)++;
SK.entity.RemoveById(e->h);
return true;
}
return false;
}
bool SolveSpaceUI::PruneConstraints(hGroup hg) {
auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(), [&](Constraint &c) {
if(c.group != hg)
return false;
if(EntityExists(c.workplane) &&
EntityExists(c.ptA) &&
EntityExists(c.ptB) &&
EntityExists(c.entityA) &&
EntityExists(c.entityB) &&
EntityExists(c.entityC) &&
EntityExists(c.entityD)) {
return false;
}
return true;
});
if(c != SK.constraint.end()) {
(deleted.constraints)++;
if(c->type != Constraint::Type::POINTS_COINCIDENT &&
c->type != Constraint::Type::HORIZONTAL &&
c->type != Constraint::Type::VERTICAL) {
(deleted.nonTrivialConstraints)++;
}
SK.constraint.RemoveById(c->h);
return true;
}
return false;
}
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
int first = 0, last = 0, i;
uint64_t startMillis = GetMilliseconds(),
endMillis;
SK.groupOrder.Clear();
for(auto &g : SK.group) { SK.groupOrder.Add(&g.h); }
std::sort(SK.groupOrder.begin(), SK.groupOrder.end(),
[](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;
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder[i]);
if((!g->clean) || !g->IsSolvedOkay()) {
first = min(first, i);
}
if(g->h == SS.GW.activeGroup) {
last = i;
}
}
if(first == INT_MAX || last == 0) {
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[i] == SS.GW.activeGroup)
break;
}
first = 0;
last = i;
break;
}
}
if(!SS.exportMode && !genForBBox) {
GenerateAll(type, andFindFree, true);
BBox box = SK.CalculateEntityBBox(true);
Vector size = box.maxp.Minus(box.minp);
double maxSize = std::max({ size.x, size.y, size.z });
chordTolCalculated = maxSize * chordTol / 100.0;
}
while(PruneOrphans())
;
IdList<Param,hParam> prev = {};
SK.param.MoveSelfInto(&prev);
SK.param.ReserveMore(prev.n);
int oldEntityCount = SK.entity.n;
SK.entity.Clear();
SK.entity.ReserveMore(oldEntityCount);
for(i = 0; i < SK.groupOrder.n; i++) {
hGroup hg = SK.groupOrder[i];
if(PruneGroups(hg))
goto pruned;
int groupRequestIndex = 0;
for(auto &req : SK.request) {
Request *r = &req;
if(r->group != hg) continue;
r->groupRequestIndex = groupRequestIndex++;
r->Generate(&(SK.entity), &(SK.param));
}
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->group != hg) continue;
c->Generate(&(SK.param));
}
SK.GetGroup(hg)->Generate(&(SK.entity), &(SK.param));
if(PruneRequests(hg) || PruneConstraints(hg))
goto pruned;
for(auto &p : SK.param) {
Param *newp = &p;
if(newp->known) continue;
Param *prevp = prev.FindByIdNoOops(newp->h);
if(prevp) {
newp->val = prevp->val;
newp->free = prevp->free;
}
}
if(hg == Group::HGROUP_REFERENCES) {
ForceReferences();
Group *g = SK.GetGroup(hg);
g->solved.how = SolveResult::OKAY;
g->clean = true;
} else {
if(i >= first && i <= last) {
Group *g = SK.GetGroup(hg);
if(genForBBox) {
SolveGroupAndReport(hg, andFindFree);
g->GenerateLoops();
} else {
g->GenerateShellAndMesh();
g->clean = true;
}
} else {
for(auto &p : SK.param) {
Param *newp = &p;
Param *prevp = prev.FindByIdNoOops(newp->h);
if(prevp) newp->known = true;
}
}
}
}
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->reference) {
c->ModifyToSatisfy();
}
}
if(traced.point.v && !SK.entity.FindByIdNoOops(traced.point)) {
traced.point = Entity::NO_ENTITY;
}
if(traced.point.v) {
Entity *pt = SK.GetEntity(traced.point);
traced.path.AddPoint(pt->PointGetNum());
}
prev.Clear();
GW.Invalidate();
GW.ClearNonexistentSelectionItems();
if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) {
if(deleted.groups > 0) {
SS.TW.ClearSuper();
}
ScheduleShowTW();
GW.ClearSuper();
if(deleted.requests > 0 || deleted.nonTrivialConstraints > 0 ||
deleted.groups > 0)
{
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;
SS.GW.persistentDirty = true;
SS.centerOfMass.dirty = true;
endMillis = GetMilliseconds();
if(endMillis - startMillis > 30) {
const char *typeStr = "";
switch(type) {
case Generate::DIRTY: typeStr = "DIRTY"; break;
case Generate::ALL: typeStr = "ALL"; break;
case Generate::REGEN: typeStr = "REGEN"; break;
case Generate::UNTIL_ACTIVE: typeStr = "UNTIL_ACTIVE"; break;
}
if(endMillis)
dbp("Generate::%s%s took %lld ms",
typeStr,
(genForBBox ? " (for bounding box)" : ""),
GetMilliseconds() - startMillis);
}
return;
pruned:
SK.param.Clear();
prev.MoveSelfInto(&(SK.param));
GenerateAll(type, andFindFree, genForBBox);
}
void SolveSpaceUI::ForceReferences() {
static const struct {
hRequest hr;
Quaternion q;
} Quat[] = {
{ Request::HREQUEST_REFERENCE_XY, { 1, 0, 0, 0, } },
{ Request::HREQUEST_REFERENCE_YZ, { 0.5, 0.5, 0.5, 0.5, } },
{ Request::HREQUEST_REFERENCE_ZX, { 0.5, -0.5, -0.5, -0.5, } },
};
for(int i = 0; i < 3; i++) {
hRequest hr = Quat[i].hr;
Entity *wrkpl = SK.GetEntity(hr.entity(0));
Entity *origin = SK.GetEntity(wrkpl->point[0]);
origin->PointForceTo(Vector::From(0, 0, 0));
origin->construction = true;
SK.GetParam(origin->param[0])->known = true;
SK.GetParam(origin->param[1])->known = true;
SK.GetParam(origin->param[2])->known = true;
Entity *normal = SK.GetEntity(wrkpl->normal);
normal->NormalForceTo(Quat[i].q);
SK.GetParam(normal->param[0])->known = true;
SK.GetParam(normal->param[1])->known = true;
SK.GetParam(normal->param[2])->known = true;
SK.GetParam(normal->param[3])->known = true;
}
}
void SolveSpaceUI::UpdateCenterOfMass() {
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
SS.centerOfMass.position = m->GetCenterOfMass();
SS.centerOfMass.dirty = false;
}
void SolveSpaceUI::MarkDraggedParams() {
sys.dragged.Clear();
for(int i = -1; i < SS.GW.pending.points.n; i++) {
hEntity hp;
if(i == -1) {
hp = SS.GW.pending.point;
} else {
hp = SS.GW.pending.points[i];
}
if(!hp.v) continue;
Entity *pt = SK.entity.FindByIdNoOops(hp);
if(pt) {
switch(pt->type) {
case Entity::Type::POINT_N_TRANS:
case Entity::Type::POINT_IN_3D:
case Entity::Type::POINT_N_ROT_AXIS_TRANS:
sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1]));
sys.dragged.Add(&(pt->param[2]));
break;
case Entity::Type::POINT_IN_2D:
sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1]));
break;
default: break;
}
}
}
if(SS.GW.pending.circle.v) {
Entity *circ = SK.entity.FindByIdNoOops(SS.GW.pending.circle);
if(circ) {
Entity *dist = SK.GetEntity(circ->distance);
switch(dist->type) {
case Entity::Type::DISTANCE:
sys.dragged.Add(&(dist->param[0]));
break;
default: break;
}
}
}
if(SS.GW.pending.normal.v) {
Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
if(norm) {
switch(norm->type) {
case Entity::Type::NORMAL_IN_3D:
sys.dragged.Add(&(norm->param[0]));
sys.dragged.Add(&(norm->param[1]));
sys.dragged.Add(&(norm->param[2]));
sys.dragged.Add(&(norm->param[3]));
break;
default: break;
}
}
}
}
void SolveSpaceUI::SolveGroupAndReport(hGroup hg, bool andFindFree) {
SolveGroup(hg, andFindFree);
Group *g = SK.GetGroup(hg);
bool isOkay = g->solved.how == SolveResult::OKAY ||
(g->allowRedundant && g->solved.how == SolveResult::REDUNDANT_OKAY);
if(!isOkay || (isOkay && !g->IsSolvedOkay())) {
TextWindow::ReportHowGroupSolved(g->h);
}
}
void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
sys.entity.Clear();
sys.param.Clear();
sys.eq.Clear();
for(auto &req : SK.request) {
Request *r = &req;
if(r->group != hg) continue;
r->Generate(&(sys.entity), &(sys.param));
}
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->group != hg) continue;
c->Generate(&(sys.param));
}
Group *g = SK.GetGroup(hg);
g->Generate(&(sys.entity), &(sys.param));
for(auto ¶m : sys.param) {
Param *p = ¶m;
p->known = false;
p->val = SK.GetParam(p->h)->val;
}
MarkDraggedParams();
}
void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
WriteEqSystemForGroup(hg);
Group *g = SK.GetGroup(hg);
g->solved.remove.Clear();
g->solved.findToFixTimeout = SS.timeoutRedundantConstr;
SolveResult how = sys.Solve(g, NULL,
&(g->solved.dof),
&(g->solved.remove),
!g->allowRedundant,
andFindFree,
!g->dofCheckOk);
if(how == SolveResult::OKAY) {
g->dofCheckOk = true;
}
g->solved.how = how;
FreeAllTemporary();
}
SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg, int *rank) {
Group *g = SK.GetGroup(hg);
if(g->suppressDofCalculation || g->allowRedundant) return SolveResult::OKAY;
WriteEqSystemForGroup(hg);
SolveResult result = sys.SolveRank(g, rank);
FreeAllTemporary();
return result;
}
bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder[i]);
if(!g->IsSolvedOkay())
return false;
if(g->h == SS.GW.activeGroup)
break;
}
return true;
}