Skip to content

Commit

Permalink
Added ability to animate icons movements to different locations in th…
Browse files Browse the repository at this point in the history
…e model
  • Loading branch information
Ibrahim Saad committed Nov 20, 2024
1 parent 6abcb10 commit 21f54ec
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 9 deletions.
3 changes: 3 additions & 0 deletions examples/3d-tracking/icons.ts

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions examples/3d-tracking/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html>

<head>
<title>3D Tracking example</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<style>
html,
body {
height: 100%;
padding: 0;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}

canvas {
height: 100%;
width: 100%;
display: block;
border: none;
}

#viewer-container {
flex: 6;
width: 100%;
overflow: hidden;
}

.flex-container {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}

.left-panel {
position: absolute;
top: 40px;
z-index: 1;
padding: 2em;
background-color: white;
box-shadow: 0 0 10px darkgray;
border-radius: 10px;
left: 20px;
width: 500px;
max-width: 500px;
user-select: none;
pointer-events: fill;
}
.left-panel div {
font-weight: 600;
padding-top: 1em;
}

#gradient{
margin-top: 10px;
width: 100%;
}

#gradient-parent{
display: flex;
flex-direction: row;
}
#channels{
width: 100%;
height: 20px;
}

.ranges-class {
display: flex;
justify-content: center;
gap: 10px;
padding: 10px;
}

.ranges-class div {
width: 160px;
height: 15px;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 12px;
text-align: center;
font-weight: bold;
border-radius: 10px;
padding: 10px;
}

.clipping-controls{
margin-bottom: 20px;
}

</style>

</head>
<body>
<div class="flex-container">
<div id="viewer-container">
<canvas id="viewer"></canvas>
</div>
</div>
</body>

</html>
176 changes: 176 additions & 0 deletions examples/3d-tracking/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { Viewer, Heatmap, InteractiveClippingPlane, ContinuousHeatmapChannel, ValueRange, ValueRangesHeatmapChannel, HeatmapSource, Icons, CameraType, ViewType, ClippingPlane, ProductType, IHeatmapChannel, ChannelType, } from '../..';
import { Icon } from '../../src/plugins/DataVisualization/Icons/icon';
import { IconsData } from './icons';

const viewer = new Viewer("viewer");
const heatmap = new Heatmap();
const icons = new Icons();
viewer.addPlugin(heatmap);
viewer.addPlugin(icons);

var plane = new InteractiveClippingPlane();
viewer.addPlugin(plane);

const sourceIcon = new Icon("Digger #1", "Tracking digger location along the bridge", 1, null, IconsData.diggerIcon);

viewer.on('loaded', args => {
try {

viewer.camera = CameraType.PERSPECTIVE;
clipModel();
viewer.resetState(ProductType.IFCSPACE)
viewer.show(ViewType.DEFAULT);

const wcs = viewer.getCurrentWcs();
var courses = viewer.getProductsOfType(ProductType.IFCCOURSE, 1);
var centroids = courses.map(id => {
const bb : Float32Array = viewer.getProductBoundingBox(id, 1);

return [
bb[0] - wcs[0] + (bb[3] / 2),
bb[1] - wcs[1] + (bb[4] / 2),
bb[2] - wcs[2] + (bb[5] / 2)
];
});
centroids = removeDuplicateXYPoints(centroids);
sourceIcon.location = new Float32Array([centroids[0][0], centroids[0][1], centroids[0][2]])
icons.addIcon(sourceIcon);

let currentIndex = 0;
let progress = 0;
const speed = 50;
const intervalTime = 2000; // this controls the rate of incoming changes to the digger location
let direction = 1;

setInterval(function () {
if (centroids.length < 2) return;
let nextIndex = (currentIndex + direction + centroids.length) % centroids.length;
if (progress >= 1) {
progress = 0;
currentIndex = nextIndex;

if (currentIndex === 0 || currentIndex === centroids.length - 1) {
direction *= -1;
}

nextIndex = (currentIndex + direction + centroids.length) % centroids.length;
}
const nextCentroid = centroids[nextIndex];
progress += speed;
icons.moveIconTo(sourceIcon, new Float32Array([nextCentroid[0], nextCentroid[1], nextCentroid[2]]), speed);
}, intervalTime);

} catch (e) {

}
});

viewer.on("pick", (arg) => {
console.log(`Product id: ${arg.id}, model: ${arg.model}`)
});


viewer.loadAsync('/tests/data/v4/Viadotto Acerno.wexbim')
viewer.hoverPickEnabled = true;
viewer.adaptivePerformanceOn = false;
viewer.highlightingColour = [0, 0, 255, 255];
viewer.start();
window['viewer'] = viewer;

let clipModel = () => {
var planes: ClippingPlane[] = [
{
direction: [1, 0, 0],
location: [10000, 0, 0]
},
{
direction: [0, 1, 0],
location: [0, 10000, 0]
},
{
direction: [0, 0, 1],
location: [0, 0, 2000]
},
{
direction: [-1, 0, 0],
location: [-10000, 0, 0]
},
{
direction: [0, -1, 0],
location: [0, -10000, 0]
},
{
direction: [0, 0, -1],
location: [0, 0, -10000]
}
];

viewer.sectionBox.setToPlanes(planes);
}

document['clip'] = () => {
plane.stopped = false;
};
document['hideClippingControl'] = () => {
plane.stopped = true;
};
document['unclip'] = () => {
viewer.unclip();
plane.stopped = true;
};

window['clipBox'] = () => {
var planes: ClippingPlane[] = [
{
direction: [1, 0, 0],
location: [5000, 0, 0]
},
{
direction: [0, 1, 0],
location: [0, 2000, 0]
},
{
direction: [0, 0, 1],
location: [0, 0, 2100]
},
{
direction: [-1, 0, 0],
location: [-100, 0, 0]
},
{
direction: [0, -1, 0],
location: [0, -2000, 0]
},
{
direction: [0, 0, -1],
location: [0, 0, -1000]
}
];

viewer.sectionBox.setToPlanes(planes);
viewer.zoomTo();
};

window['releaseClipBox'] = () => {
clipModel();
viewer.zoomTo();
};

function removeDuplicateXYPoints(centroids: number[][]): number[][] {
const filteredCentroids: number[][] = [];
const tolerance = 1;

centroids.forEach(centroid => {
const isUnique = filteredCentroids.every(existing => {
const dx = Math.abs(centroid[0] - existing[0]);
const dy = Math.abs(centroid[1] - existing[1]);
return dx > tolerance || dy > tolerance; // Check if the point is outside the tolerance
});

if (isUnique) {
filteredCentroids.push(centroid);
}
});

return filteredCentroids;
}
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ <h1>
<li>
<a href="dist/data-visualization/index.html">Data Visualization</a>
</li>
<li>
<a href="dist/3d-tracking/index.html">3D Tracking</a>
</li>
</ul>
<p>
You can also read <a href="docs" target="_blanc">documentation</a> if you are that kind of developer.
Expand Down
1 change: 0 additions & 1 deletion src/model-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ export class ModelHandle {
}

var typeIds = Product.getAllSubTypes(type);

Object.getOwnPropertyNames(this._model.productMaps).forEach(id => {
var map: ProductMap = this._model.productMaps[id];
if (typeIds[map.type]) {
Expand Down
49 changes: 46 additions & 3 deletions src/plugins/DataVisualization/Icons/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Icons } from './icons'
*/
export class Icon {
private _modelId: number;
private _productId: number;
private _productId: number | null;
private _location: Float32Array;
private _imageData: string;
private _description: string;
Expand All @@ -17,6 +17,9 @@ export class Icon {
private _enabled: boolean;
private _onIconSelected: () => void;

private _movementQueue: Array<{ location: Float32Array; speed: number }> = [];
private _isMoving: boolean = false;

/**
* Creates an instance of Icon.
*
Expand All @@ -36,7 +39,7 @@ export class Icon {
name: string,
description: string,
modelId: number,
productId: number,
productId: number | null,
imageData: string | null,
location: Float32Array | null = null,
width: number | null = null,
Expand Down Expand Up @@ -67,7 +70,7 @@ export class Icon {
* Gets the product ID associated with the icon.
* @returns {number} The product ID.
*/
public get productId(): number {
public get productId(): number | null {
return this._productId;
}

Expand Down Expand Up @@ -190,4 +193,44 @@ export class Icon {
public set isEnabled(value: boolean) {
this._enabled = value;
}

/**
* Gets the movement queue for the icon.
* @returns {Array<{ location: Float32Array; speed: number }>} The queue of movements.
*/
public get movementQueue(): Array<{ location: Float32Array; speed: number }> {
return this._movementQueue;
}

/**
* Adds a movement task to the queue.
* @param {Float32Array} location - The target location.
* @param {number} speed - The speed of the movement.
*/
public addMovementToQueue(location: Float32Array, speed: number): void {
this._movementQueue.push({ location, speed });
}

/**
* Clears the movement queue.
*/
public clearMovementQueue(): void {
this._movementQueue = [];
}

/**
* Gets whether the icon is currently moving.
* @returns {boolean} True if the icon is moving, otherwise false.
*/
public get isMoving(): boolean {
return this._isMoving;
}

/**
* Sets whether the icon is currently moving.
* @param {boolean} value - True to set the icon as moving, otherwise false.
*/
public set isMoving(value: boolean) {
this._isMoving = value;
}
}
Loading

0 comments on commit 21f54ec

Please sign in to comment.