Skip to content

Commit

Permalink
create a select util
Browse files Browse the repository at this point in the history
  • Loading branch information
ibon eskudero committed Jun 12, 2023
1 parent ba4a4c5 commit aa0800d
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 0 deletions.
60 changes: 60 additions & 0 deletions example/select/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: black;
color: white;
opacity: 50%;
font-size: 5vh;
text-align: center;
padding-top: 15vh;
z-index: 1000;
display: none;
}

#headerTitle{
font-size: 25px;
font-weight: bold;
color: white;
text-align: center;
position: fixed;
top: 0;
top: 1vh;
left: 50%;
width: 24vh;
margin-left: -12vh;
z-index: 1;
}

/*set footer bottom centered*/
#footer {
position: fixed;
bottom: 20px;
left: 0;
right: 0;
margin-left: -12vh;
z-index: 1;
font-size: 21px;
color: white;
text-align: center;
opacity: 0.25;
}

#file{
position: fixed;
top: 1vh;
left: 1vh;
color: white;
font-size: 1.5vh;
z-index: 2;
}

#canvas3d {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
21 changes: 21 additions & 0 deletions example/select/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>

<head>
<title>Select example</title>
<style type="text/css">
html,
body {
margin: 0;
padding: 0;
overflow: hidden
}
</style>
</head>

<body>
<div id="headerTitle"> Drop or Paste File </div>
<div id="footer"> Select single entity with mouse left click. If Ctrl is pressed it can select multiple entities that are inside the selection box </div>
</body>

</html>
31 changes: 31 additions & 0 deletions example/select/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DXFViewer } from '../../src/dxfViewer.js';
import { Boilerplate } from '../boilerplate.js';
import { Select } from '../../src/utils/select.js';

//global variables
const font = 'fonts/helvetiker_regular.typeface.json';
const viewer = new DXFViewer();

//init html
let html = new Boilerplate();
html.onLoad = async ( file ) => {
html.three.clear();

let dxf = await viewer.getFromFile( file, font );
if( dxf ) {

new Select( html.three.renderer.domElement, html.three.camera, dxf );

html.three.scene.add( dxf );
html.three.centerCamera();
}
};

html.init();







180 changes: 180 additions & 0 deletions src/utils/select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { Box3, Vector3 } from 'three';
import { Raycaster } from '../tools/raycaster';

export class Select extends Raycaster {
constructor( container, camera, dxf, raycasting = null ) {

super();
this.container = container;
this.camera = camera;
this.dxf = dxf;

//init raycasting
this._initRaycasting( container, camera, dxf, raycasting );

//create orange hover material that will be seeen above all other materials
this._setMaterial( 0x0000ff );

this.selecteds = [];
this._boxHelpers = {
start3dpoint: new Vector3(),
end3dpoint: new Vector3(),
boxMin : new Vector3(),
boxMax : new Vector3(),
};

this.container.addEventListener( 'pointerdown', async( e ) => await this._onPointerDown( e ), false );
this.container.addEventListener( 'pointerup', async( e ) => await this._onPointerUp( e ), false );
this.container.addEventListener( 'pointermove', async( e ) => await this._onPointerMove( e ), false );
}

async _onPointerDown( event ) {

//if control is pushed start selection box
if( event.ctrlKey && event.button !== 2 ) {
this._onSelectionBox = {
start: { x: event.clientX, y: event.clientY },
end: { x: event.clientX, y: event.clientY }
};
return;
}
}

async _onPointerUp( event ) {
//if mouse right button return
if( event.button === 2 ) return;
if( this._moving ) { this._moving = false; return; }

event.preventDefault();

this._deselectAll();


//TRY TO SELECT USING SELECTION BOX
let ss = null;
if( this._onSelectionBox ) {
const objs = this._getEntitiesUnderSelectionBox( this._onSelectionBox.start, this._onSelectionBox.end );
if( objs ) ss = objs;
} else {

//SELECT BY RAYCASTING
this.pointer.x = ( event.clientX / this.container.clientWidth ) * 2 - 1;
this.pointer.y = - ( event.clientY / this.container.clientHeight ) * 2 + 1;

const intersected = await this.raycast.raycast( this.pointer );
if( intersected ) ss = intersected.object.parent;
}

if( ss ) this._select( ss );

//CLEAN UP
this._removeSelectionBox();
this._onSelectionBox = null;
}

async _onPointerMove( event ) {
if( this._onSelectionBox ) {
this._onSelectionBox.end = { x: event.clientX, y: event.clientY };
this.drawSelectionBox( this._onSelectionBox.start, this._onSelectionBox.end );
return;
}
}

drawSelectionBox( start, end ) {
this._removeSelectionBox();

const width = Math.max( start.x, end.x ) - Math.min( start.x, end.x );
const height = Math.max( start.y, end.y ) - Math.min( start.y, end.y );

this._selectionBox = document.createElement( 'div' );
//STYLE
this._selectionBox.style.position = 'absolute';
this._selectionBox.style.border = '1px solid white';
this._selectionBox.style.left = Math.min( start.x, end.x ) + 'px';
this._selectionBox.style.top = Math.min( start.y, end.y ) + 'px';
this._selectionBox.style.width = width + 'px';
this._selectionBox.style.height = height + 'px';
this._selectionBox.style.pointerEvents = 'none'; this._selectionBox.style.background= 'blue';
this._selectionBox.style.opacity = 0.25;

document.body.appendChild( this._selectionBox );
}

_removeSelectionBox() {
if( this._selectionBox ) {
document.body.removeChild( this._selectionBox );
this._selectionBox = null;
}
}

_getEntitiesUnderSelectionBox( start, end ) {

//transform screen coordinates to 3d coordinates
start.x = ( start.x / this.container.clientWidth ) * 2 - 1;
start.y = - ( start.y / this.container.clientHeight ) * 2 + 1;
end.x = ( end.x / this.container.clientWidth ) * 2 - 1;
end.y = - ( end.y / this.container.clientHeight ) * 2 + 1;
this._boxHelpers.start3dpoint = new Vector3( start.x, start.y, 0 ).unproject( this.camera );
this._boxHelpers.end3dpoint = new Vector3( end.x, end.y, 0 ).unproject( this.camera );

//get box min and max
this._boxHelpers.boxMin.set(
Math.min( this._boxHelpers.start3dpoint.x, this._boxHelpers.end3dpoint.x ),
Math.min( this._boxHelpers.start3dpoint.y, this._boxHelpers.end3dpoint.y ),
0
);
this._boxHelpers.boxMax.set(
Math.max( this._boxHelpers.start3dpoint.x, this._boxHelpers.end3dpoint.x ),
Math.max( this._boxHelpers.start3dpoint.y, this._boxHelpers.end3dpoint.y ),
0
);

const box = new Box3( this._boxHelpers.boxMin, this._boxHelpers.boxMax );

const blocks = this._getBlocksUnderBox( box, this.dxf );

return blocks.length > 0 ? blocks : null;
}

_getBlocksUnderBox( box, root ) {

const blocks = [];
if( !root || !root.children ) return blocks;

if( root.name !== '' && this._fitInsideBox( box, root ) ) {
blocks.push( root );
} else {
for( let i = 0; i < root.children.length; i++ ) {
const child = root.children[ i ];
const b = this._getBlocksUnderBox( box, child );
if( b.length > 0 ) blocks.push( ...b );
}
}

return blocks;
}

_fitInsideBox( box, root ) {
let b = new Box3().setFromObject( root );

return box.containsBox( b );
}


_select( obj ) {
const objs = obj instanceof Array ? obj : [ obj ];
objs.forEach( o => {
const clone = this._clone( o );
clone.traverse( c => { if ( c.material ) c.material = this._material; } );
o.parent.add( clone );
this.selecteds.push( clone );
} );
}

_deselectAll() {
if( this.selecteds ) {
this.selecteds.forEach( s => s.parent.remove( s ) );
this.selecteds.length = 0;
}
}
}

0 comments on commit aa0800d

Please sign in to comment.