awv3
Version:
⚡ AWV3 embedded CAD
131 lines (116 loc) • 4.85 kB
JavaScript
import { addCommand } from '../command/highlevel';
import Constraint from './index';
import { incidence, horizontality, verticality } from './type';
export default class ConstraintAdder {
constructor(sketch) {
// the sketch we are going to add constraints to
this.sketch = sketch;
// the list of constraint geomParams which really should be added
this.constraints = [];
// dsu structures for incident and inc-tangent point groups
this.incidenceDsu = new DSU();
//this.tangencyDsu = new DSU(); // TODO!
// sets of horizontal and vertical lines
this.horizontalSet = new Set();
this.verticalSet = new Set();
// preprocess all objects and constraints currently on sketch
this.init();
}
init() {
// take all present constraints into account
for (let object of this.sketch.children) {
switch (object.state.class) {
case incidence.type:
this.incidenceDsu.merge(object.entities[0].id, object.entities[1].id);
break;
case verticality.type:
this.verticalSet.add(object.entities[0].id);
break;
case horizontality.type:
this.horizontalSet.add(object.entities[0].id);
break;
}
}
}
// add a new constraint to the internal buffer (if it is not excessive)
add(type, entities, value = {}) {
if (type.type !== undefined) type = type.type;
const geomParams = { class: type, entities, value };
// if a new constraint is not excessive, then take it into account
switch (type) {
case incidence.type:
// TODO: implement more complicated handling of incidence
// Incidence is an equivalence either on points or on lines / arcs, but not on both together.
// E.g.: if a point is incident to two lines, then these lines may be NOT incident.
if (this.incidenceDsu.getHead(entities[0].id) !== this.incidenceDsu.getHead(entities[1].id))
this.incidenceDsu.merge(entities[0].id, entities[1].id);
else
return;
break;
case verticality.type:
if (!this.verticalSet.has(entities[0].id)) this.verticalSet.add(entities[0].id);
else return;
break;
case horizontality.type:
if (!this.horizontalSet.has(entities[0].id)) this.horizontalSet.add(entities[0].id);
else return;
break;
default:
// TODO: optimize this O(N) search
if (this.constraints.some(constr => isExcessive(geomParams, constr))) return;
for (let object of this.sketch.children)
if (isExcessive(geomParams, object.geomParams)) return;
break;
}
this.constraints.push(geomParams);
}
// return constraint statements in the internal buffer, clear the buffer
commit() {
const statements = this.constraints.map(geomParams => addCommand(this.sketch, geomParams));
this.constraints = [];
return statements;
}
}
function isExcessive(constr0, constr1) {
if (constr0.class !== constr1.class) return false;
if (constr0.entities.length !== constr1.entities.length) return false;
if (constr0.value.value || constr1.value.value || constr0.value.expression || constr1.value.expression)
return false; // ignore parametric constraints
if (constr0.entities.every((obj, idx) => obj.id === constr1.entities[idx].id)) return true;
const isBinaryCommutative = Constraint(constr0.class).isCommutative;
if (isBinaryCommutative && constr0.entities.every((obj, idx) => obj.id === constr1.entities[1 - idx].id))
return true;
return false;
}
// Disjoint Set Union
export class DSU {
constructor() {
this.parents = new Map();
}
getParent(obj) {
return this.parents.has(obj) ? this.parents.get(obj) : obj;
}
setParent(obj, par) {
this.parents.set(obj, par);
}
touch(obj) {
if (!this.parents.has(obj)) this.parents.set(obj, obj);
}
getHead(obj) {
const par = this.getParent(obj);
if (par === obj) return obj;
const head = this.getHead(par);
this.setParent(obj, head);
return head;
}
merge(objA, objB) {
this.touch(objA);
this.touch(objB);
objA = this.getHead(objA);
objB = this.getHead(objB);
Math.random() < 0.5 ? this.setParent(objA, objB) : this.setParent(objB, objA);
}
getTouched() {
return Array.from(this.parents.keys());
}
}