import {createByConstraintName} from "./solverConstraints"; import {Param, prepare} from "./solver"; import {findConstructionCluster} from "./constructions"; import {GCCircle, GCPoint} from "./constractibles"; import {eqEps, eqTol} from "../../brep/geom/tolerance"; import {Polynomial, POW_1_FN} from "./polynomial"; import {compositeFn, NOOP} from "../../../../modules/gems/func"; export class AlgNumSubSystem { generator = NOOP;//new BoundaryObjectsGenerator(); allConstraints = []; ownParams = new Set(); readOnlyParams = new Set(); generatedParams = new Set(); beingSolvedParams = new Set(); eliminatedParams = new Map(); // generators = []; subSystems = []; //subsystems are generated by generators only polynomials = []; substitutedParams = []; polyToConstr = new Map(); conflicting = new Set(); redundant = new Set(); snapshot = new Map(); dof = 0; SubSystem(generator) { this.generator = generator; } get fullyConstrained() { return this.dof === 0; } validConstraints(callback) { this.allConstraints.forEach(c => { if (!this.conflicting.has(c)) { callback(c); } }); } addConstraint(constraint, _ancestorParams) { if (this.canBeAdded(constraint.params)) { // this.constraints.push(constraint); // this.constraints } this.makeSnapshot(); this.allConstraints.push(constraint); this.prepare(); if (!this.isConflicting(constraint)) { this.solveFine(); console.log(this.solveStatus); if (!this.solveStatus.success) { console.log("adding to conflicts"); this.conflicting.add(constraint); } } if (this.isConflicting(constraint)) { this.rollback(); // } else if (this.fullyConstrained) { // this.rollback(); // this.conflicting.add(constraint); // this.redundant.add(constraint); } else { this.updateDOF(); this.updateFullyConstrainedObjects(); } } isConflicting(constraint) { return this.conflicting.has(constraint); } updateDOF() { let vars = 0; let equations = 0; this.validConstraints(c => { equations ++; c.params.forEach(p => { if (!this.readOnlyParams.has(p)) { vars++ } }) }); this.dof = vars - equations; } makeSnapshot() { this.snapshot.clear(); this.validConstraints(c => c.params.forEach(p => this.snapshot.set(p, p.get()))); } rollback() { this.snapshot.forEach((val, param) => param.set(val)); } reset() { this.polynomials = []; this.substitutedParams = []; this.eliminatedParams.clear(); this.beingSolvedParams.clear(); this.polyToConstr.clear(); } evaluatePolynomials() { this.validConstraints(c => { c.collectPolynomials(this.polynomials); this.polynomials.forEach(p => this.polyToConstr.set(p, c)) }); console.log('reducing system:'); this.polynomials.forEach(p => console.log(p.toString())); let requirePass = true; while (requirePass) { requirePass = false; for (let i = 0; i < this.polynomials.length; ++i) { const polynomial = this.polynomials[i]; if (!polynomial) { continue; } if (polynomial.monomials.length === 0) { this.conflicting.add(this.polyToConstr.get(polynomial)); console.log("CONFLICT: " + polynomial.toString()); if (eqEps(polynomial.constant, 0)) { this.redundant.add(this.polyToConstr.get(polynomial)); console.log("REDUNDANT"); } this.polynomials[i] = null; } else if (polynomial.isLinear && polynomial.monomials.length === 1) { const monomial = polynomial.monomials[0]; const terms = monomial.terms; if (terms.length === 1) { const term = terms[0]; if (term.fn.degree === 1) { const p = term.param; const val = - polynomial.constant / monomial.constant; p.set(val); this.eliminatedParams.set(p, val); for (let polynomial of this.polynomials) { if (polynomial) { polynomial.eliminate(p, val); } } requirePass = true; } } this.polynomials[i] = null; } else if (polynomial.monomials.length === 2 && polynomial.isLinear) { const [m1, m2] = polynomial.monomials; let p1 = m1.linearParam; let p2 = m2.linearParam; const constant = - m2.constant / m1.constant; if (eqEps(polynomial.constant, 0)) { for (let polynomial of this.polynomials) { if (polynomial) { polynomial.substitute(p1, p2, constant); } } this.substitutedParams.push([p1, new Polynomial().monomial(constant).term(p2, POW_1_FN)]); this.polynomials[i] = null; requirePass = true; } else { const b = - polynomial.constant / m1.constant; let transaction = compositeFn(); for (let polynomial of this.polynomials) { if (polynomial) { const polyTransaction = polynomial.linearSubstitution(p1, p2, constant, b); if (!polyTransaction) { transaction = null; break; } transaction.push(polyTransaction); transaction.push(() => { this.substitutedParams.push([p1, new Polynomial(b).monomial(constant).term(p2, POW_1_FN)]); this.polynomials[i] = null; }); } } if (transaction) { transaction(); requirePass = true; } } } } if (requirePass) { this.polynomials.forEach(polynomial => polynomial && polynomial.compact()); } } this.polynomials = this.polynomials.filter(p => p); } prepare() { this.reset(); this.evaluatePolynomials(); // this.polynomialClusters = splitByIsolatedClusters(this.polynomials); console.log('solving system:'); this.polynomials.forEach(p => console.log(p.toString())); console.log('with respect to:'); this.substitutedParams.forEach(([x, expr]) => console.log('X' + x.id + ' = ' + expr.toString())); const residuals = []; this.polynomials.forEach(p => residuals.push(p.asResidual())); for (let residual of residuals) { residual.params.forEach(solverParam => { if (!this.beingSolvedParams.has(solverParam)) { solverParam.reset(solverParam.objectParam.get()); this.beingSolvedParams.add(solverParam); } }); } this.numericalSolver = prepare(residuals); } splitByIsolatedClusters(polynomials) { const graph = [polynomials.length]; const params = []; const paramMap = new Map(); polynomials.forEach((pl, i) => pl.visitParams(p => { let pIndex = paramMap.get(p); if (pIndex === undefined) { pIndex = paramMap.size; paramMap.set(p, pIndex); params.push(p); } graph[i][pIndex] = 1; })); const visited = new Set(); const clusters = []; for (let param of params) { if (visited.has(param)) { continue } const stack = [param]; const isolation = []; while (stack.length) { const p = stack.pop(); if (visited.has(p)) { continue; } isolation.push(p); const index = paramMap.get(p); for (let i = 0; i < graph.length; ++i) { if (graph[i][index] === 1) { for (let j = 0; j < params.length; j ++) { if (j !== index) { stack.push(params[j]); } } } } } if (isolation.length) { clusters.push(new Cluster(isolation)); } } return clusters; } solveRough() { this.solve(true); } solveFine() { this.solve(false); } solve(rough) { this.generator(); this.beingSolvedParams.forEach(solverParam => { solverParam.set(solverParam.objectParam.get()); }); const solveStatus = this.numericalSolver.solveSystem(rough); if (!rough) { this.solveStatus = solveStatus; } console.log('numerical result: ' + solveStatus.success); for (let [p, val] of this.eliminatedParams) { p.set(val); } this.beingSolvedParams.forEach(solverParam => { solverParam.objectParam.set(solverParam.get()); }); for (let i = this.substitutedParams.length - 1; i >= 0; i--) { let [param, expression] = this.substitutedParams[i]; param.set(expression.value()); } } updateFullyConstrainedObjects() { this.validConstraints(c => { c.objects.forEach(obj => { let allLocked = true; obj.visitParams(p => { if (!this.eliminatedParams.has(p)) { allLocked = false; } }); obj.fullyConstrained = allLocked; }); }); } canBeAdded(subjectParams, ancestorParams) { for (let p of subjectParams) { if (!this.ownParams.has(p) && (!ancestorParams || !ancestorParams.has(p))) { return false; } } return true; } } class Cluster { constructor(polynomials) { this.polynomials = polynomials; this.beingSolvedParams = new Set(); const residuals = []; this.polynomials.forEach(p => residuals.push(p.asResidual())); for (let residual of residuals) { residual.params.forEach(solverParam => { if (!this.beingSolvedParams.has(solverParam)) { solverParam.reset(solverParam.objectParam.get()); this.beingSolvedParams.add(solverParam); } }); } } solve(rough) { this.beingSolvedParams.forEach(solverParam => { solverParam.set(solverParam.objectParam.get()); }); this.solveStatus = this.numericalSolver.solveSystem(rough); this.beingSolvedParams.forEach(solverParam => { solverParam.objectParam.set(solverParam.get()); }); } } export class AlgNumSystem { constraints = []; generators = []; locked = new Set(); constantParams = new Set(); constructor(visitAllObjects) { this.visitAllObjects = visitAllObjects; } addConstraint(constraint) { this.constraints.push(constraint); if (constraint.schema.generator) { } } startTransaction(interactiveLock = []) { this.systemTransaction.prepare(interactiveLock); return this.systemTransaction; } }