Skip to content

Commit

Permalink
Merge pull request #1623 from xeokit/XCD-101-picking-overlapping-objects
Browse files Browse the repository at this point in the history
XCD-101 Picking overlapping objects
  • Loading branch information
xeolabs authored Aug 13, 2024
2 parents a03f6cb + 5a16d66 commit 8e5e66f
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 18 deletions.
25 changes: 23 additions & 2 deletions dist/xeokit-sdk.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -97512,7 +97512,7 @@ class MousePanRotateDollyHandler {
let secsNowLast = null;

canvas.addEventListener("wheel", this._mouseWheelHandler = (e) => {
if (!(configs.active && configs.pointerEnabled)) {
if (!(configs.active && configs.pointerEnabled && configs.zoomOnMouseWheel)) {
return;
}
const secsNow = performance.now() / 1000.0;
Expand Down Expand Up @@ -99747,7 +99747,8 @@ class CameraControl extends Component {
constrainVertical: false,
smartPivot: false,
doubleClickTimeFrame: 250,

zoomOnMouseWheel: true,

snapToVertex: DEFAULT_SNAP_VERTEX,
snapToEdge: DEFAULT_SNAP_EDGE,
snapRadius: DEFAULT_SNAP_PICK_RADIUS,
Expand Down Expand Up @@ -100838,6 +100839,26 @@ class CameraControl extends Component {
return this._configs.doubleClickTimeFrame;
}

/**
* Sets whether to zoom the camera on mouse wheel
*
* Default is ````true````
*
* @param {Boolean} enabled
*/
set zoomOnMouseWheel(enabled) {
this._configs.zoomOnMouseWheel = !!enabled;
}

/**
* Gets whether to zoom the camera on mouse wheel
*
* @returns {Boolean}
*/
get zoomOnMouseWheel() {
return this._configs.zoomOnMouseWheel;
}

/**
* Destroys this ````CameraControl````.
* @private
Expand Down
25 changes: 23 additions & 2 deletions dist/xeokit-sdk.es.js
Original file line number Diff line number Diff line change
Expand Up @@ -97508,7 +97508,7 @@ class MousePanRotateDollyHandler {
let secsNowLast = null;

canvas.addEventListener("wheel", this._mouseWheelHandler = (e) => {
if (!(configs.active && configs.pointerEnabled)) {
if (!(configs.active && configs.pointerEnabled && configs.zoomOnMouseWheel)) {
return;
}
const secsNow = performance.now() / 1000.0;
Expand Down Expand Up @@ -99743,7 +99743,8 @@ class CameraControl extends Component {
constrainVertical: false,
smartPivot: false,
doubleClickTimeFrame: 250,

zoomOnMouseWheel: true,

snapToVertex: DEFAULT_SNAP_VERTEX,
snapToEdge: DEFAULT_SNAP_EDGE,
snapRadius: DEFAULT_SNAP_PICK_RADIUS,
Expand Down Expand Up @@ -100834,6 +100835,26 @@ class CameraControl extends Component {
return this._configs.doubleClickTimeFrame;
}

/**
* Sets whether to zoom the camera on mouse wheel
*
* Default is ````true````
*
* @param {Boolean} enabled
*/
set zoomOnMouseWheel(enabled) {
this._configs.zoomOnMouseWheel = !!enabled;
}

/**
* Gets whether to zoom the camera on mouse wheel
*
* @returns {Boolean}
*/
get zoomOnMouseWheel() {
return this._configs.zoomOnMouseWheel;
}

/**
* Destroys this ````CameraControl````.
* @private
Expand Down
16 changes: 13 additions & 3 deletions dist/xeokit-sdk.es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -22966,7 +22966,7 @@ mouseDownLeft=false;mouseDownMiddle=false;mouseDownRight=false;break;case 2:// M
mouseDownLeft=false;mouseDownMiddle=false;mouseDownRight=false;break;case 3:// Right button
mouseDownLeft=false;mouseDownMiddle=false;mouseDownRight=false;break;}});canvas.addEventListener("mouseup",this._mouseUpHandler=function(e){if(!(configs.active&&configs.pointerEnabled)){return;}switch(e.which){case 3:// Right button
getCanvasPosFromEvent$3(e,canvasPos);var x=canvasPos[0];var y=canvasPos[1];if(Math.abs(x-lastXDown)<3&&Math.abs(y-lastYDown)<3){controllers.cameraControl.fire("rightClick",{// For context menus
pagePos:[Math.round(e.pageX),Math.round(e.pageY)],canvasPos:canvasPos,event:e},true);}break;}canvas.style.removeProperty("cursor");});canvas.addEventListener("mouseenter",this._mouseEnterHandler=function(){if(!(configs.active&&configs.pointerEnabled)){return;}});var maxElapsed=1/20;var minElapsed=1/60;var secsNowLast=null;canvas.addEventListener("wheel",this._mouseWheelHandler=function(e){if(!(configs.active&&configs.pointerEnabled)){return;}var secsNow=performance.now()/1000.0;var secsElapsed=secsNowLast!==null?secsNow-secsNowLast:0;secsNowLast=secsNow;if(secsElapsed>maxElapsed){secsElapsed=maxElapsed;}if(secsElapsed<minElapsed){secsElapsed=minElapsed;}var delta=Math.max(-1,Math.min(1,-e.deltaY*40));if(delta===0){return;}var normalizedDelta=delta/Math.abs(delta);updates.dollyDelta+=-normalizedDelta*secsElapsed*configs.mouseWheelDollyRate;if(mouseMovedOnCanvasSinceLastWheel){states.followPointerDirty=true;mouseMovedOnCanvasSinceLastWheel=false;}},{passive:true});}_createClass(MousePanRotateDollyHandler,[{key:"reset",value:function reset(){}},{key:"destroy",value:function destroy(){var canvas=this._scene.canvas.canvas;document.removeEventListener("keydown",this._documentKeyDownHandler);document.removeEventListener("keyup",this._documentKeyUpHandler);canvas.removeEventListener("mousedown",this._mouseDownHandler);document.removeEventListener("mousemove",this._documentMouseMoveHandler);canvas.removeEventListener("mousemove",this._canvasMouseMoveHandler);document.removeEventListener("mouseup",this._documentMouseUpHandler);canvas.removeEventListener("mouseup",this._mouseUpHandler);canvas.removeEventListener("mouseenter",this._mouseEnterHandler);canvas.removeEventListener("wheel",this._mouseWheelHandler);}}]);return MousePanRotateDollyHandler;}();var center=math.vec3();var tempVec3a$5=math.vec3();var tempVec3b$2=math.vec3();var tempVec3c$1=math.vec3();var tempVec3d=math.vec3();var tempCameraTarget={eye:math.vec3(),look:math.vec3(),up:math.vec3()};/**
pagePos:[Math.round(e.pageX),Math.round(e.pageY)],canvasPos:canvasPos,event:e},true);}break;}canvas.style.removeProperty("cursor");});canvas.addEventListener("mouseenter",this._mouseEnterHandler=function(){if(!(configs.active&&configs.pointerEnabled)){return;}});var maxElapsed=1/20;var minElapsed=1/60;var secsNowLast=null;canvas.addEventListener("wheel",this._mouseWheelHandler=function(e){if(!(configs.active&&configs.pointerEnabled&&configs.zoomOnMouseWheel)){return;}var secsNow=performance.now()/1000.0;var secsElapsed=secsNowLast!==null?secsNow-secsNowLast:0;secsNowLast=secsNow;if(secsElapsed>maxElapsed){secsElapsed=maxElapsed;}if(secsElapsed<minElapsed){secsElapsed=minElapsed;}var delta=Math.max(-1,Math.min(1,-e.deltaY*40));if(delta===0){return;}var normalizedDelta=delta/Math.abs(delta);updates.dollyDelta+=-normalizedDelta*secsElapsed*configs.mouseWheelDollyRate;if(mouseMovedOnCanvasSinceLastWheel){states.followPointerDirty=true;mouseMovedOnCanvasSinceLastWheel=false;}},{passive:true});}_createClass(MousePanRotateDollyHandler,[{key:"reset",value:function reset(){}},{key:"destroy",value:function destroy(){var canvas=this._scene.canvas.canvas;document.removeEventListener("keydown",this._documentKeyDownHandler);document.removeEventListener("keyup",this._documentKeyUpHandler);canvas.removeEventListener("mousedown",this._mouseDownHandler);document.removeEventListener("mousemove",this._documentMouseMoveHandler);canvas.removeEventListener("mousemove",this._canvasMouseMoveHandler);document.removeEventListener("mouseup",this._documentMouseUpHandler);canvas.removeEventListener("mouseup",this._mouseUpHandler);canvas.removeEventListener("mouseenter",this._mouseEnterHandler);canvas.removeEventListener("wheel",this._mouseWheelHandler);}}]);return MousePanRotateDollyHandler;}();var center=math.vec3();var tempVec3a$5=math.vec3();var tempVec3b$2=math.vec3();var tempVec3c$1=math.vec3();var tempVec3d=math.vec3();var tempCameraTarget={eye:math.vec3(),look:math.vec3(),up:math.vec3()};/**
* @private
*/var KeyboardAxisViewHandler=/*#__PURE__*/function(){function KeyboardAxisViewHandler(scene,controllers,configs,states){_classCallCheck(this,KeyboardAxisViewHandler);this._scene=scene;var cameraControl=controllers.cameraControl;var camera=scene.camera;this._onSceneKeyDown=scene.input.on("keydown",function(){if(!(configs.active&&configs.pointerEnabled)||!scene.input.keyboardEnabled){return;}if(configs.keyboardEnabledOnlyIfMouseover&&!states.mouseover){return;}var axisViewRight=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_RIGHT);var axisViewBack=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_BACK);var axisViewLeft=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_LEFT);var axisViewFront=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_FRONT);var axisViewTop=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_TOP);var axisViewBottom=cameraControl._isKeyDownForAction(cameraControl.AXIS_VIEW_BOTTOM);if(!axisViewRight&&!axisViewBack&&!axisViewLeft&&!axisViewFront&&!axisViewTop&&!axisViewBottom){return;}var aabb=scene.aabb;var diag=math.getAABB3Diag(aabb);math.getAABB3Center(aabb,center);var perspectiveDist=Math.abs(diag/Math.tan(controllers.cameraFlight.fitFOV*math.DEGTORAD));var orthoScale=diag*1.1;tempCameraTarget.orthoScale=orthoScale;if(axisViewRight){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldRight,perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(camera.worldUp);}else if(axisViewBack){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldForward,perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(camera.worldUp);}else if(axisViewLeft){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldRight,-perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(camera.worldUp);}else if(axisViewFront){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldForward,-perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(camera.worldUp);}else if(axisViewTop){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldUp,perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward,1,tempVec3b$2),tempVec3c$1));}else if(axisViewBottom){tempCameraTarget.eye.set(math.addVec3(center,math.mulVec3Scalar(camera.worldUp,-perspectiveDist,tempVec3a$5),tempVec3d));tempCameraTarget.look.set(center);tempCameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward,-1,tempVec3b$2)));}if(!configs.firstPerson&&configs.followPointer){controllers.pivotController.setPivotPos(center);}if(controllers.cameraFlight.duration>0){controllers.cameraFlight.flyTo(tempCameraTarget,function(){if(controllers.pivotController.getPivoting()&&configs.followPointer){controllers.pivotController.showPivot();}});}else{controllers.cameraFlight.jumpTo(tempCameraTarget);if(controllers.pivotController.getPivoting()&&configs.followPointer){controllers.pivotController.showPivot();}}});}_createClass(KeyboardAxisViewHandler,[{key:"reset",value:function reset(){}},{key:"destroy",value:function destroy(){this._scene.input.off(this._onSceneKeyDown);}}]);return KeyboardAxisViewHandler;}();/**
* @private
Expand Down Expand Up @@ -23611,7 +23611,7 @@ _this114._configs={// Private
longTapTimeout:600,// Millisecs
longTapRadius:5,// Pixels
// General
active:true,keyboardLayout:"qwerty",navMode:"orbit",planView:false,firstPerson:false,followPointer:true,doublePickFlyTo:true,panRightClick:true,showPivot:false,pointerEnabled:true,constrainVertical:false,smartPivot:false,doubleClickTimeFrame:250,snapToVertex:DEFAULT_SNAP_VERTEX,snapToEdge:DEFAULT_SNAP_EDGE,snapRadius:DEFAULT_SNAP_PICK_RADIUS,keyboardEnabledOnlyIfMouseover:true,// Rotation
active:true,keyboardLayout:"qwerty",navMode:"orbit",planView:false,firstPerson:false,followPointer:true,doublePickFlyTo:true,panRightClick:true,showPivot:false,pointerEnabled:true,constrainVertical:false,smartPivot:false,doubleClickTimeFrame:250,zoomOnMouseWheel:true,snapToVertex:DEFAULT_SNAP_VERTEX,snapToEdge:DEFAULT_SNAP_EDGE,snapRadius:DEFAULT_SNAP_PICK_RADIUS,keyboardEnabledOnlyIfMouseover:true,// Rotation
dragRotationRate:360.0,keyboardRotationRate:90.0,rotationInertia:0.0,// Panning
keyboardPanRate:1.0,touchPanRate:1.0,panInertia:0.5,// Dollying
keyboardDollyRate:10,mouseWheelDollyRate:100,touchDollyRate:0.2,dollyInertia:0,dollyProximityThreshold:30.0,dollyMinSpeed:0.04};// Current runtime state of the CameraControl
Expand Down Expand Up @@ -24130,9 +24130,19 @@ value=value||"qwerty";if(value!=="qwerty"&&value!=="azerty"){this.error("Unsuppo
*
* @param {Number} value Current double click time frame.
*/function get(){return this._configs.doubleClickTimeFrame;}/**
* Sets whether to zoom the camera on mouse wheel
*
* Default is ````true````
*
* @param {Boolean} enabled
*/,set:function set(value){this._configs.doubleClickTimeFrame=value!==undefined&&value!==null?value:250;}},{key:"zoomOnMouseWheel",get:/**
* Gets whether to zoom the camera on mouse wheel
*
* @returns {Boolean}
*/function get(){return this._configs.zoomOnMouseWheel;}/**
* Destroys this ````CameraControl````.
* @private
*/,set:function set(value){this._configs.doubleClickTimeFrame=value!==undefined&&value!==null?value:250;}},{key:"destroy",value:function destroy(){this._destroyHandlers();this._destroyControllers();this._cameraUpdater.destroy();_get(_getPrototypeOf(CameraControl.prototype),"destroy",this).call(this);}},{key:"_destroyHandlers",value:function _destroyHandlers(){for(var _i491=0,len=this._handlers.length;_i491<len;_i491++){var handler=this._handlers[_i491];if(handler.destroy){handler.destroy();}}}},{key:"_destroyControllers",value:function _destroyControllers(){for(var _i492=0,len=this._controllers.length;_i492<len;_i492++){var controller=this._controllers[_i492];if(controller.destroy){controller.destroy();}}}}]);return CameraControl;}(Component);/**
*/,set:function set(enabled){this._configs.zoomOnMouseWheel=!!enabled;}},{key:"destroy",value:function destroy(){this._destroyHandlers();this._destroyControllers();this._cameraUpdater.destroy();_get(_getPrototypeOf(CameraControl.prototype),"destroy",this).call(this);}},{key:"_destroyHandlers",value:function _destroyHandlers(){for(var _i491=0,len=this._handlers.length;_i491<len;_i491++){var handler=this._handlers[_i491];if(handler.destroy){handler.destroy();}}}},{key:"_destroyControllers",value:function _destroyControllers(){for(var _i492=0,len=this._controllers.length;_i492<len;_i492++){var controller=this._controllers[_i492];if(controller.destroy){controller.destroy();}}}}]);return CameraControl;}(Component);/**
* @desc A property within a {@link PropertySet}.
*
* @class Property
Expand Down
8 changes: 4 additions & 4 deletions dist/xeokit-sdk.min.cjs.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dist/xeokit-sdk.min.es.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/xeokit-sdk.min.es5.js

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions examples/picking/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@
"Picking Options": [

["pick_setObjectsPickable", "Controlling which objects are pickable"]
],

"Picking Overlapping Objects": [

["overlapping_press", "Pick overlapping objects with mouse press"],
["overlapping_wheel", "Pick overlapping objects with mouse wheel"]
]
};

Expand Down
94 changes: 94 additions & 0 deletions examples/picking/overlapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {math} from "../../dist/xeokit-sdk.min.es.js";
/**
* A function that can act as a stand-in template for Scene::pick.
*
* The details that might require tweaking for a particular use-case are:
*
* 1. `rayOriginPrecision` and `rayDirectionPrecision` variables:
* These determine how sensitive the overlapping cycle is to slight picking ray changes.
* Both variables set to zero will allow no tolerance for a ray change,
* so the pick will have to happen with exactly same ray.
*
* 2. `const pickResult = viewer.scene.pick(...)` call
* Depending on a specific use-case, the actual pick call might require other Scene::pick parameters to be used
*/

export const overlappingPick = (function() {

// Arbitrary values - depends on required end-user precision
const rayOriginPrecision = 0.001;
const rayDirectionPrecision = 0.2 * math.DEGTORAD;

let alreadyPicked = [ ];
let recentPickEntity = null;
let referenceRay = null;

const sentinel = { };

return function(scene, pickRay, { wrapAround = false, pickCloser = false } = { }) {
const rayChanged = referenceRay && ((math.distVec3(pickRay.origin, referenceRay.origin) > rayOriginPrecision)
||
(math.angleVec3(pickRay.direction, referenceRay.direction) > (rayDirectionPrecision)));

if ((! referenceRay) || rayChanged) {
referenceRay = { origin: pickRay.origin, direction: pickRay.direction };
}

if (rayChanged)
{
// If ray is different than before, than make all ignored entities pickable again
alreadyPicked.forEach(i => i.pickable = true);
alreadyPicked = [ ];
}

if (pickCloser)
{
for (let i = 0; i < 2; ++i) {
if (alreadyPicked.length > 0)
alreadyPicked.pop().pickable = true;
}
}

return (function pick(resetAndRepeat) {

// The following Scene::pick call might require some configuration according to the use-case
const pickResult = scene.pick({ origin: referenceRay.origin, direction: referenceRay.direction });

const pickEntity = pickResult && pickResult.entity;
if (pickEntity)
{
if (rayChanged && (pickEntity === recentPickEntity) && (! pickCloser)) {
// This happens when:
// 1. Some entity had already been picked
// 2. A camera was just moved
// 3. The previously-picked entity is being picked again, so we skip it
alreadyPicked.push(pickEntity);
pickEntity.pickable = false;
return pick(true);
}
else
{
alreadyPicked.push(pickEntity);
pickEntity.pickable = false;
recentPickEntity = pickEntity;
return pickResult;
}
}
else if (wrapAround && resetAndRepeat && (alreadyPicked.length > 0))
{
// If no entity got picked then reenable all ignored entities and start the cycle over
alreadyPicked.forEach(i => i.pickable = true);
alreadyPicked = [ ];
return pick(false);
}
else
{
if ((alreadyPicked.length > 0) && (alreadyPicked[alreadyPicked.length - 1] !== sentinel)) {
alreadyPicked.push(sentinel); // mock object to be popped for pickCloser
}
recentPickEntity = null;
return null;
}
})(true);
};
})();
Loading

0 comments on commit 8e5e66f

Please sign in to comment.