UNPKG

awv3

Version:
131 lines (116 loc) 4.85 kB
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()); } }