awv3
Version:
⚡ AWV3 embedded CAD
403 lines (304 loc) • 12.8 kB
JavaScript
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 };