awv3
Version:
⚡ AWV3 embedded CAD
115 lines (99 loc) • 4.46 kB
JavaScript
import * as THREE from 'three';
export default class Raycaster extends THREE.Raycaster {
constructor(interaction, options = { approach: Raycaster.Approach.FirstMatch }) {
super();
this.interaction = interaction;
this.view = interaction.view;
this.linePrecision = 1.5;
}
isActuallyVisible(obj) {
while (obj) {
if (obj.visible === false) return false;
if (obj.material && obj.material.visible === false) return false;
obj = obj.parent;
}
return true;
}
castObjects(objects, intersects = [], filter = undefined) {
for (let i = 0, l = objects.length, object; i < l; i++) {
object = objects[i];
if (
// No filter, filter is empty, or object is part of filter
(!filter || filter.length === 0 || filter.indexOf(object) >= 0) &&
// must be interactive
object.interactive &&
// must have interaction
object.interaction &&
// must be enabled
object.interaction.enabled &&
// muste be activ (have interaction related listeners)
object.interaction._active &&
// must be visible
this.isActuallyVisible(object)
) {
// ... then we intersect
this.intersect(object, object.interaction.recursive, intersects, object.interaction.types, object);
}
}
// Sort items after priority. The receiver has it, and each object can define one as well on the prototype.
// If no priority has been given or if it yields 0, the distance will be used to determine the hits importance
intersects.sort(
(a, b) =>
(b.receiver.interaction.priority || 0) +
b.object.interactionPriority -
((a.receiver.interaction.priority || 0) + a.object.interactionPriority) || a.distance - b.distance,
);
return intersects;
}
intersect(object, recursive, intersects, types, parent) {
let op = true;
// Inspect types
if (!!types) {
if (Array.isArray(types)) {
op = !(object.type !== 'Object3D' && object.type !== 'Group' && types.indexOf(object.type) == -1);
} else if (typeof types === 'function') op = types(object);
}
// false op stops operation right here, undefined op at least proceeds with childs
if (op == false) return;
let count = intersects.length;
// true op allows raycast
if (op == true) object.raycast(this, intersects, parent.interaction.approach);
if (intersects.length != count) {
for (let i = count, intersect, l = intersects.length; i < l; i++) {
intersect = intersects[i];
intersect.receiver = parent;
// If the parent/receiver is not recursive data.object should point back to it
if (!parent.interaction.recursive) {
intersect.receiver.object = parent;
}
}
}
// If the root is not recursive there's no point in iterating further
if (!parent.interaction.recursive) return;
for (let i = 0, l = object.children.length, child, isMultiMaterial; i < l; i++) {
child = object.children[i];
isMultiMaterial = Array.isArray(child.material);
// To proceed, a child:
if (
// is visible
child.visible &&
// is interactive
child.interactive &&
// doesn't have a material
(!child.material ||
// OR has a single material and it is visible
(!isMultiMaterial && child.material.visible === true) ||
// OR has many materials and the first is visible
// TODO: is this really what we want? seems kind of random
(isMultiMaterial && child.material[0].visible === true))
) {
this.intersect(child, true, intersects, types, parent);
}
}
}
}
// TODO: Deprecate approaches
// Do we really need to fork each objects raycast if it perhaps only brings a small benefit anyway?
Raycaster.Approach = {
Default: 'Default',
};