Interface PickingAttachment

The PickingAttachment interface defines the result of a pick request.

This is the actual attachment of a InfiniteEngineInterfaceSignal.Picked signal event.

A pick request aks to know the content (i.e. geometries, lines, boxes, points) at specific positions in the 3D display. The picking request may concern a specific position (InfiniteEngineInterface.pickAt) or a rectangular area (InfiniteEngineInterface.pickRect) inside the 3d view.

The actual picked point follows the following convention :
picking convention
As multiple elements may be picked, this interface gathers a list of PickingAttachmentItem for geometries, a list of PickingAttachmentItem for points, a list of PickingAttachmentItem for lines and a list of PickingAttachmentItem for boxes.

Warning : this is a geometric instance id and not a part instance id !!! (see Main ID Types for more information).

/** 
* Sample to illustrate the handling of a mouse pick in an InfiniteEngineInterface.
*/
import {
InfiniteEngineInterface, InfiniteFactory, PickingAttachment, PickingAttachmentItem,
Vector3, VisualStates, InfiniteEngineInterfaceSignal, Rectangle, InfiniteEvent,
PrimitiveManagerInterface
} from 'generated_files/documentation/appinfiniteapi';

// the div to render to
let lView3D: HTMLElement;

// create an 3D engine
const lInfiniteEngine: InfiniteEngineInterface = InfiniteFactory.CreateInfiniteEngine();

// What to do on pick ?
let onButtonClicked : (pEvent) => void = undefined;

// What to do on pick ?
let onPicking : (pEvent : InfiniteEvent, _pCallbackData) => void = undefined;

// We pick a rectangle if big enough, else only a point
let pickAtImpl : () => void = undefined;

// bind the 3D rendering to the div
lInfiniteEngine.setView(lView3D);

// ####################################################################################################################################
// ############################################ PICKING ###############################################################################
// ####################################################################################################################################
// Used to copy the relevant portion of a MouseEvent
// position, previous buttons, current button and type
// store also if we started showing the rubber band
interface EventPos
{
x : number;
y : number;
buttons : number;
button : number;
type : string;
// is the rubber band initialized, i.e. have we begun to draw a rectangle ?
rubberInit : boolean;
}
// store the last click to know the extent of the rectangle to pick, and after pick request
// to know the properties of the pick request
// we store a copy of the MouseEvent
const lLastClickEvent: EventPos = {
x: 0,
y: 0,
buttons: 0,
button: 0,
type: '',
rubberInit: false,
};

// What to do on pick ?
onPicking = (pEvent : InfiniteEvent, _pCallbackData) : void =>
{
const lAttachment: PickingAttachment = <PickingAttachment>pEvent.attachments;
// care only about 3d geometries (no line, point, box)
const l3dAttachment: PickingAttachmentItem[] | undefined = lAttachment.geometric;

let lFirstGeometricId: number = 0;
let l3dPosition: Vector3 | undefined = undefined;
if (l3dAttachment !== undefined && l3dAttachment.length > 0)
{
lFirstGeometricId = l3dAttachment[0].instanceId;
l3dPosition = l3dAttachment[0].position;
}

// Mid button click => set Center Of Interest (COI)
if (lLastClickEvent.button === 1) {
if (!l3dPosition) return;
// set the center coi
lInfiniteEngine.getCameraManager().lookAt(l3dPosition);
return;
}

// no left button click => do nothing
if (lLastClickEvent.button !== 0)
{
return;
}
// if double click
if (lLastClickEvent.type === 'dblclick')
{
// fit to geometry
lInfiniteEngine.getCameraManager().fitGeometry(lFirstGeometricId);
return;
}
// clear selected state on pick
let lWasSelected: boolean = false;
if (lFirstGeometricId !== 0)
{
const lGeomState = lInfiniteEngine.getGeometricState(lFirstGeometricId);
lWasSelected = ((lGeomState & VisualStates.S_Selected) !== 0);
}
// if multiple selections => do not change the selected state of a single geometry
// but select all the relevant 3D data instead
if (l3dAttachment && l3dAttachment.length > 1)
{
lWasSelected = false;
}
// unselect everyone
lInfiniteEngine.updateGeometricStateForAll(VisualStates.S_Selected, ~VisualStates.S_Selected);
// and begin
if (lFirstGeometricId !== 0 && !lWasSelected && l3dAttachment)
{
// update visual state of selected items
const lIds = new Uint32Array(l3dAttachment.length);
for (let i = 0; i < l3dAttachment.length; i += 1) {
lIds[i] = l3dAttachment[i].instanceId;
}
lInfiniteEngine.updateGeometricState(lIds, VisualStates.S_Selected, VisualStates.S_Selected);
}
};

// and bind the callback on pick result
lInfiniteEngine.addEventListener(InfiniteEngineInterfaceSignal.Picked, onPicking);

// the rectangle to pick (if relevant)
const lRubberBand: Rectangle = new Rectangle();

// We pick a rectangle if big enough, else only a point
pickAtImpl = (): void =>
{
// single pick if small rectangle
if (((lRubberBand.width <= 3) && (lRubberBand.height <= 3)) || (lRubberBand.width === 0) || (lRubberBand.height === 0))
{
// pick the last point
lInfiniteEngine.pickAt(lLastClickEvent.x, lLastClickEvent.y, false);
}
else
{
// pick an area
lInfiniteEngine.pickRect(lRubberBand, false);
}
// and clear the rectangle in all the cases
lRubberBand.clear();
};

onButtonClicked = (event) : void =>
{
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;

if (lLastClickEvent.buttons === 0)
{
// make a pick
pickAtImpl();
}
};
// on left click => pick
lView3D.addEventListener('click', onButtonClicked);

const onMouseDown = (event) : void =>
{
// store the pick start
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
// no rubber
lLastClickEvent.rubberInit = false;
};
// store the position to the first pick pos
lView3D.addEventListener('mousedown', onMouseDown);

// when we release the mouse button, make a pick only on middle mouse if close to start pos
// and hide the rubber band
const onMouseUp = (event) : void =>
{
// middle click
if (lLastClickEvent.button === 1 && event.button === 1)
{
if (!lLastClickEvent.rubberInit) {
// if no rectangle => set COI
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;
pickAtImpl();
}
}
const lPrimitiveManager : PrimitiveManagerInterface = lInfiniteEngine.getPrimitiveManager();
lPrimitiveManager.getRubberBandManager().setRubberBandVisible(false);
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;
};

lView3D.addEventListener('mouseup', onMouseUp);

// we we move the mouse, display the rubber band
const onMouseMove = (event) : void =>
{
if (lLastClickEvent.rubberInit || (Math.abs(lLastClickEvent.x - event.offsetX) > 3 || Math.abs(lLastClickEvent.y - event.offsetY) > 3))
{
lRubberBand.x = Math.min(lLastClickEvent.x, event.offsetX);
lRubberBand.y = Math.min(lLastClickEvent.y, event.offsetY);
lRubberBand.width = Math.abs(lLastClickEvent.x - event.offsetX);
lRubberBand.height = Math.abs(lLastClickEvent.y - event.offsetY);
lLastClickEvent.rubberInit = true;
}
if ((lLastClickEvent.buttons === 1) && (event.buttons === 1) && lLastClickEvent.rubberInit)
{
// show/update the rubber band
const lPrimitiveManager: PrimitiveManagerInterface = lInfiniteEngine.getPrimitiveManager();
lPrimitiveManager.getRubberBandManager().setRubberBandRectangle(lRubberBand);
lPrimitiveManager.getRubberBandManager().setRubberBandVisible(true);
}
};
lView3D.addEventListener('mousemove', onMouseMove);

or with async calls :
/** 
* Sample to illustrate the handling of an asynchronous mouse pick in an InfiniteEngineInterface.
*/
import {
InfiniteEngineInterface, InfiniteFactory, PickingAttachment, PickingAttachmentItem, Vector3, VisualStates, AsyncPickingResult,
Rectangle, AsyncResultReason, PrimitiveManagerInterface,
} from 'generated_files/documentation/appinfiniteapi';

// the div to render to
let lView3D: HTMLElement;

// create an 3D engine
const lInfiniteEngine: InfiniteEngineInterface = InfiniteFactory.CreateInfiniteEngine();

// What to do on pick ?
let onButtonClicked : (pEvent) => void = undefined;

// We pick a rectangle if big enough, else only a point
let pickAtImpl : () => Promise<void> = undefined;

// bind the 3D rendering to the div
lInfiniteEngine.setView(lView3D);

// ####################################################################################################################################
// ############################################ PICKING ###############################################################################
// ####################################################################################################################################
// Used to copy the relevant portion of a MouseEvent
// position, previous buttons, current button and type
// store also if we started showing the rubber band
interface EventPos
{
x : number;
y : number;
buttons : number;
button : number;
type : string;
// is the rubber band initialized, i.e. have we begun to draw a rectangle ?
rubberInit : boolean;
}
// store the last click to know the extent of the rectangle to pick, and after pick request
// to know the properties of the pick request
// we store a copy of the MouseEvent
const lLastClickEvent: EventPos = {
x: 0,
y: 0,
buttons: 0,
button: 0,
type: '',
rubberInit: false,
};

// What to do on pick ?
const onPicking = (pAttachment: PickingAttachment) : void =>
{
// care only about 3d geometries (no line, point, box)
const l3dAttachment: PickingAttachmentItem[] | undefined = pAttachment.geometric;

let lFirstGeometricId: number = 0;
let l3dPosition: Vector3 | undefined = undefined;
if (l3dAttachment !== undefined && l3dAttachment.length > 0)
{
lFirstGeometricId = l3dAttachment[0].instanceId;
l3dPosition = l3dAttachment[0].position;
}

// Mid button click => set Center Of Interest (COI)
if (lLastClickEvent.button === 1) {
if (!l3dPosition) return;
// set the center coi
lInfiniteEngine.getCameraManager().lookAt(l3dPosition);
return;
}

// no left button click => do nothing
if (lLastClickEvent.button !== 0)
{
return;
}
// if double click
if (lLastClickEvent.type === 'dblclick')
{
// fit to geometry
lInfiniteEngine.getCameraManager().fitGeometry(lFirstGeometricId);
return;
}
// clear selected state on pick
let lWasSelected: boolean = false;
if (lFirstGeometricId !== 0)
{
const lGeomState = lInfiniteEngine.getGeometricState(lFirstGeometricId);
lWasSelected = ((lGeomState & VisualStates.S_Selected) !== 0);
}
// if multiple selections => do not change the selected state of a single geometry
// but select all the relevant 3D data instead
if (l3dAttachment && l3dAttachment.length > 1)
{
lWasSelected = false;
}
// unselect everyone
lInfiniteEngine.updateGeometricStateForAll(VisualStates.S_Selected, ~VisualStates.S_Selected);
// and begin
if (lFirstGeometricId !== 0 && !lWasSelected && l3dAttachment)
{
// update visual state of selected items
const lIds = new Uint32Array(l3dAttachment.length);
for (let i = 0; i < l3dAttachment.length; i += 1) {
lIds[i] = l3dAttachment[i].instanceId;
}
lInfiniteEngine.updateGeometricState(lIds, VisualStates.S_Selected, VisualStates.S_Selected);
}
};

// the rectangle to pick (if relevant)
const lRubberBand: Rectangle = new Rectangle();

// We pick a rectangle if big enough, else only a point
pickAtImpl = async () : Promise<void> =>
{
let lPickingResult : AsyncPickingResult;
// single pick if small rectangle
if (((lRubberBand.width <= 3) && (lRubberBand.height <= 3)) || (lRubberBand.width === 0) || (lRubberBand.height === 0))
{
// pick the last point
lPickingResult = await lInfiniteEngine.asyncPickAt(lLastClickEvent.x, lLastClickEvent.y, false);
}
else
{
// pick an area
lPickingResult = await lInfiniteEngine.asyncPickRect(lRubberBand, false);
}
// and clear the rectangle in all the cases
lRubberBand.clear();
if (lPickingResult.reason !== AsyncResultReason.ARR_Success || lPickingResult.value === undefined)
{
return;
}
onPicking(lPickingResult.value);
};

onButtonClicked = (event) : void =>
{
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;

if (lLastClickEvent.buttons === 0)
{
// make a pick
pickAtImpl();
}
};
// on left click => pick
lView3D.addEventListener('click', onButtonClicked);

const onMouseDown = (event) : void =>
{
// store the pick start
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
// no rubber
lLastClickEvent.rubberInit = false;
};
// store the position to the first pick pos
lView3D.addEventListener('mousedown', onMouseDown);

// when we release the mouse button, make a pick only on middle mouse if close to start pos
// and hide the rubber band
const onMouseUp = (event) : void =>
{
// middle click
if (lLastClickEvent.button === 1 && event.button === 1)
{
if (!lLastClickEvent.rubberInit) {
// if no rectangle => set COI
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;
pickAtImpl();
}
}
const lPrimitiveManager : PrimitiveManagerInterface = lInfiniteEngine.getPrimitiveManager();
lPrimitiveManager.getRubberBandManager().setRubberBandVisible(false);
lLastClickEvent.x = event.offsetX;
lLastClickEvent.y = event.offsetY;
lLastClickEvent.button = event.button;
lLastClickEvent.buttons = event.buttons;
lLastClickEvent.type = event.type;
lLastClickEvent.rubberInit = false;
};

lView3D.addEventListener('mouseup', onMouseUp);

// we we move the mouse, display the rubber band
const onMouseMove = (event) : void =>
{
if (lLastClickEvent.rubberInit || (Math.abs(lLastClickEvent.x - event.offsetX) > 3 || Math.abs(lLastClickEvent.y - event.offsetY) > 3))
{
lRubberBand.x = Math.min(lLastClickEvent.x, event.offsetX);
lRubberBand.y = Math.min(lLastClickEvent.y, event.offsetY);
lRubberBand.width = Math.abs(lLastClickEvent.x - event.offsetX);
lRubberBand.height = Math.abs(lLastClickEvent.y - event.offsetY);
lLastClickEvent.rubberInit = true;
}
if ((lLastClickEvent.buttons === 1) && (event.buttons === 1) && lLastClickEvent.rubberInit)
{
// show/update the rubber band
const lPrimitiveManager: PrimitiveManagerInterface = lInfiniteEngine.getPrimitiveManager();
lPrimitiveManager.getRubberBandManager().setRubberBandRectangle(lRubberBand);
lPrimitiveManager.getRubberBandManager().setRubberBandVisible(true);
}
};
lView3D.addEventListener('mousemove', onMouseMove);

Please make sure the destination browser supports promises before using async calls.
Events

Properties

annotation: PickingAttachmentItem[]

The list of annotations that were picked, or undefined if no annotation was picked.

The list of boxes that were picked, or undefined if no box was picked.

The list of features that were picked, or undefined if no feature was picked.

The list of geometries that were picked, or undefined if no geometry was picked.

The list of lines that were picked, or undefined if no line was picked.

pickOnlyClosest: boolean

Should we pick only closest items ?

The picking origin type (pickAt, pickRect, pickFromRay).

pickingOriginData: Rectangle | Vector2 | Ray3

The picking origin data (pickAt is a Vector2, pickRect is a Rectangle, pickFromRay a Ray3).

pickingRequestId: string

The picking request id as returned by InfiniteEngineInterface.getLastPickingRequestId.

The list of points that were picked, or undefined if no point was picked.