mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-08 01:13:27 +01:00
247 lines
6.5 KiB
JavaScript
247 lines
6.5 KiB
JavaScript
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();
|
|
|
|
constraints = [];
|
|
|
|
ownParams = new Set();
|
|
|
|
readOnlyParams = new Set();
|
|
|
|
generatedParams = new Set();
|
|
|
|
beingSolvedParams = new Set();
|
|
|
|
|
|
// generators = [];
|
|
subSystems = []; //subsystems are generated by generators only
|
|
|
|
polynomials = [];
|
|
substitutedParams = [];
|
|
polyToConstr = new Map();
|
|
conflicting = new Set();
|
|
redundant = new Set();
|
|
|
|
SubSystem(generator) {
|
|
this.generator = generator;
|
|
}
|
|
|
|
addConstraint(constraint, _ancestorParams) {
|
|
|
|
if (this.canBeAdded(constraint.params)) {
|
|
// this.constraints.push(constraint);
|
|
// this.constraints
|
|
}
|
|
|
|
this.constraints.push(constraint);
|
|
}
|
|
|
|
reset() {
|
|
this.polynomials = [];
|
|
this.substitutedParams = [];
|
|
this.polyToConstr.clear();
|
|
this.conflicting.clear();
|
|
this.redundant.clear();
|
|
this.beingSolvedParams.clear();
|
|
}
|
|
|
|
evaluatePolynomials() {
|
|
this.constraints.forEach(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()));
|
|
|
|
const toEliminate = Array.from(this.readOnlyParams);
|
|
|
|
const toSubstitute = [];
|
|
const linearSub = [];
|
|
|
|
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) {
|
|
if (!eqEps(polynomial.constant, 0)) {
|
|
this.conflicting.add(this.polyToConstr.get(polynomial));
|
|
}
|
|
this.polynomials[i] = null;
|
|
} else if (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) {
|
|
term.param.set(- polynomial.constant / monomial.constant);
|
|
toEliminate.push(term.param);
|
|
}
|
|
}
|
|
|
|
this.polynomials[i] = null;
|
|
} else if (polynomial.monomials.length === 2 && polynomial.isLinear) {
|
|
const [m1, m2] = polynomial.monomials;
|
|
|
|
const constant = - m2.constant / m1.constant;
|
|
if (eqEps(polynomial.constant, 0)) {
|
|
toSubstitute.push([m1.linearParam, m2.linearParam, constant]);
|
|
this.substitutedParams.push([m1.linearParam, new Polynomial().monomial(constant).term(m2.linearParam, POW_1_FN)]);
|
|
} else {
|
|
linearSub.push([m1.param, m2.param, constant, - polynomial.constant / m1.constant]);
|
|
}
|
|
this.polynomials[i] = null;
|
|
}
|
|
}
|
|
|
|
while (toEliminate.length) {
|
|
requirePass = true;
|
|
const param = toEliminate.pop();
|
|
for (let polynomial of this.polynomials) {
|
|
if (polynomial) {
|
|
polynomial.eliminate(param, param.get());
|
|
}
|
|
}
|
|
}
|
|
while (toSubstitute.length) {
|
|
requirePass = true;
|
|
const [param, toParam, dotConstant] = toSubstitute.pop();
|
|
for (let polynomial of this.polynomials) {
|
|
if (polynomial) {
|
|
polynomial.substitute(param, toParam, dotConstant);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (linearSub.length) {
|
|
while (linearSub.length) {
|
|
const [param, toParam, k, b] = linearSub.pop();
|
|
|
|
let transaction = compositeFn();
|
|
for (let polynomial of this.polynomials) {
|
|
if (polynomial) {
|
|
const polyTransaction = polynomial.linearSubstitution(param, toParam, k, b);
|
|
if (!polyTransaction) {
|
|
transaction = null;
|
|
break;
|
|
}
|
|
transaction.push(polyTransaction);
|
|
transaction.push(() => this.substitutedParams.push(param, new Polynomial(b).monomial(k).term(toParam, POW_1_FN)));
|
|
}
|
|
}
|
|
if (transaction) {
|
|
requirePass = true;
|
|
transaction();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (requirePass) {
|
|
this.polynomials.forEach(polynomial => polynomial && polynomial.compact());
|
|
}
|
|
}
|
|
|
|
|
|
this.polynomials = this.polynomials.filter(p => p);
|
|
|
|
}
|
|
|
|
|
|
prepare() {
|
|
|
|
this.reset();
|
|
|
|
this.evaluatePolynomials();
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
solve(rough) {
|
|
this.generator();
|
|
|
|
this.beingSolvedParams.forEach(solverParam => {
|
|
solverParam.set(solverParam.objectParam.get());
|
|
});
|
|
|
|
this.numericalSolver.solveSystem(rough);
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
canBeAdded(subjectParams, ancestorParams) {
|
|
|
|
for (let p of subjectParams) {
|
|
if (!this.ownParams.has(p) && (!ancestorParams || !ancestorParams.has(p))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|