UNPKG

awv3

Version:
403 lines (304 loc) 12.8 kB
import * as THREE from 'three'; import Object3 from '../../../three/object3'; import Ccref from '../ccref'; import { getArcAngles } from '../geomutils'; import ConstraintGraphics from '../graphics/constraint'; var Mode = Object.freeze({ Never: 0, Hover: 1, Always: 2 }); function uniNorm2(vec) { return Math.max(Math.abs(vec.x), Math.abs(vec.y)); } function sqr(x) { return x * x; } var Visualizer = /*#__PURE__*/ function () { function Visualizer(sketcher, textures) { this.mode = Mode.Hover; this.sketcher = sketcher; // Set of constraint ids to avoid iterating and filtering sketch.children this.constraints = new Set(); // Reverse mapping from entities to constraints this.entityFor = new Map(); // For each constraint, its based entity is stored (where icon is rendered) this.baseEntityFor = new Map(); // If set to true, constraints would be repositioned next frame this.graphicsDirty = false; } var _proto = Visualizer.prototype; _proto.refresh = function refresh() { this.graphicsDirty = true; this.sketcher.refresh(); }; _proto.isSelected = function isSelected(id) { return this.sketcher.selector.selectedIds.indexOf(id) !== -1; }; _proto.setVisibility = function setVisibility(id, newValue) { if (newValue === void 0) { newValue = undefined; } if (newValue === undefined) newValue = this.mode === Mode.Always; //note: we forbid hiding selected constraints if (!newValue && this.isSelected(id)) return; new Ccref(this.sketcher, id).graphics.visible = newValue; }; _proto.addConstraint = function addConstraint(constraint) { if (!(constraint.graphics instanceof ConstraintGraphics)) return; this.constraints.add(constraint.id); this.baseEntityFor.set(constraint.id, undefined); for (var _iterator = constraint.mergedEntities, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } var _entity = _ref; if (!this.entityFor.has(_entity.id)) this.entityFor.set(_entity.id, []); this.entityFor.get(_entity.id).push(constraint.id); } this.setVisibility(constraint.id); this.refresh(); }; _proto.removeConstraint = function removeConstraint(constraint, state) { if (state === void 0) { state = constraint.state; } if (!(constraint.graphics instanceof ConstraintGraphics)) return; var possibleEntities = []; for (var _iterator2 = state.members.entities.members, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var _ref3; if (_isArray2) { if (_i2 >= _iterator2.length) break; _ref3 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) break; _ref3 = _i2.value; } var _ref4 = _ref3; var _id2 = _ref4.value; possibleEntities.push(_id2); // this constraint could have had mergedEntity but it could have already been deleted var estate = this.sketcher.tree[_id2]; if (estate) possibleEntities.push(estate.parent); } for (var _i3 = 0; _i3 < possibleEntities.length; _i3++) { var _id = possibleEntities[_i3]; var constraints = this.entityFor.get(_id) || []; var idx = constraints.indexOf(constraint.id); if (idx !== -1) constraints.splice(idx, 1); if (constraints.length === 0) this.entityFor.delete(_id); } this.constraints.delete(constraint.id); this.baseEntityFor.delete(constraint.id); this.refresh(); }; //note: must be called if constraint has changed _proto.updateConstraint = function updateConstraint(constraint) { this.refresh(); }; //note: must be called if entity of constraint has changed _proto.updateEntity = function updateEntity(entity) { if (!this.entityFor.has(entity.id)) return; this.refresh(); }; _proto.hover = function hover(entity, first) { if (this.mode !== Mode.Hover) return; if (entity.isConstraint()) return; if (first) this.unhoverAll(); for (var _iterator3 = this.entityFor.get(entity.id) || [], _isArray3 = Array.isArray(_iterator3), _i4 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { var _ref5; if (_isArray3) { if (_i4 >= _iterator3.length) break; _ref5 = _iterator3[_i4++]; } else { _i4 = _iterator3.next(); if (_i4.done) break; _ref5 = _i4.value; } var _id3 = _ref5; this.setVisibility(_id3, true); if (!this.isSelected(_id3)) this.baseEntityFor.set(_id3, entity.id); } this.refresh(); }; _proto.unhover = function unhover(entity, first) { if (this.mode !== Mode.Hover) return; if (entity.isConstraint()) return; // do nothing }; _proto.unhoverAll = function unhoverAll() { if (this.mode !== Mode.Hover) return; for (var _iterator4 = this.constraints.keys(), _isArray4 = Array.isArray(_iterator4), _i5 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { var _ref6; if (_isArray4) { if (_i5 >= _iterator4.length) break; _ref6 = _iterator4[_i5++]; } else { _i5 = _iterator4.next(); if (_i5.done) break; _ref6 = _i5.value; } var _id4 = _ref6; this.setVisibility(_id4, false); } this.refresh(); }; //called when constraint visualizer mode changes + on init _proto.updateAll = function updateAll(mode) { if (mode === void 0) { mode = this.mode; } this.mode = mode; for (var _iterator5 = this.constraints.keys(), _isArray5 = Array.isArray(_iterator5), _i6 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { var _ref7; if (_isArray5) { if (_i6 >= _iterator5.length) break; _ref7 = _iterator5[_i6++]; } else { _i6 = _iterator5.next(); if (_i6.done) break; _ref7 = _i6.value; } var _id5 = _ref7; this.setVisibility(_id5); } this.refresh(); }; //note: called before each frame is rendered //sets proper positions for all constraint graphics _proto.render = function render() { var _this = this; if (!this.graphicsDirty) return; this.graphicsDirty = false; var iconSize = 3 * this.sketcher.graphicScale; //determine for each base entity the array of all constraints drawn near it var perEntityLists = new Map(); for (var _iterator6 = this.constraints.keys(), _isArray6 = Array.isArray(_iterator6), _i7 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { var _ref8; if (_isArray6) { if (_i7 >= _iterator6.length) break; _ref8 = _iterator6[_i7++]; } else { _i7 = _iterator6.next(); if (_i7.done) break; _ref8 = _i7.value; } var _conId = _ref8; var constr = new Ccref(this.sketcher, _conId); if (!constr.graphics.visible) continue; var baseId = this.getBaseEntityId(_conId); if (!perEntityLists.has(baseId)) perEntityLists.set(baseId, []); perEntityLists.get(baseId).push(_conId); } if (perEntityLists.size === 0) return; //create rectangle filled with constraints icons for each base entity var rectangles = Array.from(perEntityLists.keys()).map(function (id) { return { id: id, keyPos: _this.getKeyPosition(id), cnt: perEntityLists.get(id).length }; }); //sort base entities (for stability) rectangles.sort(function (a, b) { return a.id - b.id; }); //minimal distance between rectangles (in iconSize units) var colEps = 0.5; //minimal vertical distance from key position to rectangle's center (in iconSize units) var minOffsetH = 0.75; //set positions for all entity lists (each one is rectangle) for (var i = 0; i < rectangles.length; i++) { var newRect = rectangles[i]; //position of rectangle center, which is closest to key point var bestDistSqr = 1e50; var bestX = void 0, bestY = void 0; rectanglePlaceLoop: for (var vk = 0;; vk++) { for (var vs = 1; vs >= -1; vs -= 2) { //try to put into the vertical slice: var cy = newRect.keyPos.y + vs * (minOffsetH + vk * (1 + colEps)) * iconSize; //loop termination criterion if (sqr(cy - newRect.keyPos.y) >= bestDistSqr) break rectanglePlaceLoop; //try both direction for horizontal moving for (var hs = 1; hs >= -1; hs -= 2) { var cx = newRect.keyPos.x; do { var moved = false; for (var j = 0; j < i; j++) { var obstRect = rectangles[j]; //check if rectangles collide if (Math.abs(cy - obstRect.ctrPos.y) >= (1 + colEps) * iconSize - 1e-9) continue; if (Math.abs(cx - obstRect.ctrPos.x) >= ((newRect.cnt + obstRect.cnt) * 0.5 + colEps) * iconSize - 1e-9) continue; //do minimal horizontal movement which avoids collision var newSide = cx + hs * iconSize * newRect.cnt * 0.5; var obstSide = obstRect.ctrPos.x - hs * iconSize * obstRect.cnt * 0.5; var diff = newSide - obstSide + hs * colEps * iconSize; cx += diff; moved = true; } //note: if something has moved, then we have to recheck everything for collisions } while (moved); var distSqr = sqr(cx - newRect.keyPos.x) + sqr(cy - newRect.keyPos.y); if (bestDistSqr > distSqr) { bestDistSqr = distSqr; bestX = cx; bestY = cy; } //early cutoff: if no horizontal movement happened if (cx === newRect.keyPos.x) break rectanglePlaceLoop; } } } //place rectangle into found position (not changed later) newRect.ctrPos = new THREE.Vector3(bestX, bestY, 0.0); } //set positions for all constraint icons for (var _iterator7 = rectangles, _isArray7 = Array.isArray(_iterator7), _i8 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { var _ref10; if (_isArray7) { if (_i8 >= _iterator7.length) break; _ref10 = _iterator7[_i8++]; } else { _i8 = _iterator7.next(); if (_i8.done) break; _ref10 = _i8.value; } var _ref11 = _ref10; var _id6 = _ref11.id, _ctrPos = _ref11.ctrPos, _cnt = _ref11.cnt; var conList = perEntityLists.get(_id6); //sort constraint lists (for stability) conList.sort(function (a, b) { return a - b; }); for (var _i9 = 0; _i9 < _cnt; _i9++) { var pos = _ctrPos.clone().add(new THREE.Vector3(_i9 - 0.5 * (_cnt - 1), 0, 0).multiplyScalar(iconSize)); var _conId2 = conList[_i9]; var _constr = new Ccref(this.sketcher, _conId2); _constr.graphics.localMesh.position.copy(pos); _constr.graphics.localMesh.scale.set(iconSize, iconSize, 1); } } }; _proto.getKeyPosition = function getKeyPosition(entity) { if (typeof entity === 'number') entity = new Ccref(this.sketcher, entity); var gp = entity.geomParams; switch (entity.class) { case 'CC_Point': return gp.start.clone(); case 'CC_Line': return new THREE.Vector3().lerpVectors(gp.start, gp.end, 0.5); case 'CC_Arc': return getArcAngles(gp).mid.clone(); case 'CC_Circle': return new THREE.Vector3(0.6, 0.8).multiplyScalar(gp.radius).add(gp.center); default: return new THREE.Vector3(0, 0, 0); } }; _proto.getBaseEntityId = function getBaseEntityId(constraint) { if (typeof constraint === 'number') constraint = new Ccref(this.sketcher, constraint); var res = this.baseEntityFor.get(constraint.id); if (res !== undefined) return res; for (var _iterator8 = constraint.mergedEntities, _isArray8 = Array.isArray(_iterator8), _i10 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { var _ref12; if (_isArray8) { if (_i10 >= _iterator8.length) break; _ref12 = _iterator8[_i10++]; } else { _i10 = _iterator8.next(); if (_i10.done) break; _ref12 = _i10.value; } var _entity2 = _ref12; if (['CC_Point', 'CC_Line', 'CC_Arc', 'CC_Circle'].indexOf(_entity2.class) !== -1) return _entity2.id; } return constraint.entities[0].id; }; return Visualizer; }(); export { Visualizer as default };