mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-03 02:39:59 +08:00
Added framework for progress bars
This commit is contained in:
parent
8fb1804571
commit
5e7f10bae6
@ -7,6 +7,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="progress-bar-container">
|
||||
<div id="progress-bar" class="progress-bar" style="width:0%"></div>
|
||||
</div>
|
||||
|
||||
<!-- The UI is dynamically built at runtime, see ./src/ui/layout.ts for details -->
|
||||
<div class="column-properties" id="properties">
|
||||
</div>
|
||||
|
@ -73,6 +73,7 @@ export class AppContext {
|
||||
default: {
|
||||
this._ui.enableTo(action + 1);
|
||||
|
||||
ASSERT(payload.action !== 'Progress');
|
||||
const { builder, style } = this._getActionMessageBuilder(action, payload.statusMessages);
|
||||
uiOutput.setMessage(builder, style as OutputStyle);
|
||||
|
||||
|
@ -3,6 +3,9 @@ import { LOG } from './util/log_util';
|
||||
|
||||
/* eslint-disable */
|
||||
export enum EAppEvent {
|
||||
onTaskStart,
|
||||
onTaskProgress,
|
||||
onTaskEnd,
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
@ -18,7 +21,7 @@ export class EventManager {
|
||||
this._eventsToListeners = new Map();
|
||||
}
|
||||
|
||||
public add(event: EAppEvent, delegate: () => void) {
|
||||
public add(event: EAppEvent, delegate: (...args: any[]) => void) {
|
||||
if (!this._eventsToListeners.has(event)) {
|
||||
this._eventsToListeners.set(event, []);
|
||||
}
|
||||
|
52
src/progress.ts
Normal file
52
src/progress.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { EAppEvent, EventManager } from './event';
|
||||
import { ASSERT } from './util/error_util';
|
||||
|
||||
|
||||
export class ProgressManager {
|
||||
/* Singleton */
|
||||
private static _instance: ProgressManager;
|
||||
public static get Get() {
|
||||
return this._instance || (this._instance = new this());
|
||||
}
|
||||
|
||||
private _tasks: string[];
|
||||
|
||||
private constructor() {
|
||||
this._tasks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking the progress of a task.
|
||||
* @param taskId The id of the task (created here).
|
||||
* @param max The maximum number the task progress index can reach.
|
||||
* For example, if you are iterating over an array of 1000 elements, this will be 1000.
|
||||
* @param divisions The number of updates the manager should track.
|
||||
* For example, 4 means an event will be emitted for 20%, 40%, 60%, 80% progress.
|
||||
*/
|
||||
public start(taskId: string) {
|
||||
ASSERT(!this._tasks.includes(taskId), 'Task with that Id already being tracked');
|
||||
this._tasks.push(taskId);
|
||||
EventManager.Get.broadcast(EAppEvent.onTaskStart, taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce progress has been made on a task.
|
||||
* @param taskId The id of the task (created in `start`).
|
||||
* @param progressIndex The index of the progress made so far.
|
||||
* For example, if you are iteratinve over an array of 1000 elements, and are on index 230, this should be 230.
|
||||
*/
|
||||
public progress(taskId: string, percentage: number) {
|
||||
EventManager.Get.broadcast(EAppEvent.onTaskProgress, taskId, percentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce a task has completed.
|
||||
* @param taskId The id of the task (created in `start`).
|
||||
*/
|
||||
public end(taskId: string) {
|
||||
const taskIndex = this._tasks.findIndex((task) => { return task === taskId });
|
||||
ASSERT(taskIndex !== -1, 'Task with that Id is not being tracked');
|
||||
this._tasks.splice(taskIndex, 1);
|
||||
EventManager.Get.broadcast(EAppEvent.onTaskEnd, taskId);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { Mesh } from '../mesh';
|
||||
import { ProgressManager } from '../progress';
|
||||
import { Axes, axesToDirection, Ray } from '../ray';
|
||||
import { ASSERT } from '../util/error_util';
|
||||
import { LOG } from '../util/log_util';
|
||||
@ -19,12 +20,12 @@ export class BVHRayVoxeliser extends IVoxeliser {
|
||||
const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight;
|
||||
const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
|
||||
const useMesh = mesh.copy(); // TODO: Voxelise without copying mesh, too expensive for dense meshes
|
||||
|
||||
|
||||
useMesh.scaleMesh(scale);
|
||||
useMesh.translateMesh(offset);
|
||||
|
||||
// Build BVH
|
||||
const triangles = Array<{x: Number, y: Number, z: Number}[]>(useMesh._tris.length);
|
||||
const triangles = Array<{ x: Number, y: Number, z: Number }[]>(useMesh._tris.length);
|
||||
for (let triIndex = 0; triIndex < useMesh.getTriangleCount(); ++triIndex) {
|
||||
const positionData = useMesh.getVertices(triIndex);
|
||||
triangles[triIndex] = [positionData.v0, positionData.v1, positionData.v2];
|
||||
@ -77,7 +78,15 @@ export class BVHRayVoxeliser extends IVoxeliser {
|
||||
LOG('Rays created...');
|
||||
|
||||
// Ray test BVH
|
||||
let nextPercentage = 0.0;
|
||||
ProgressManager.Get.start('Voxelising');
|
||||
for (rayIndex = 0; rayIndex < rays.length; ++rayIndex) {
|
||||
const percentage = rayIndex / rays.length;
|
||||
if (rayIndex / rays.length >= nextPercentage) {
|
||||
ProgressManager.Get.progress('Voxelising', percentage);
|
||||
nextPercentage += 0.1;
|
||||
}
|
||||
|
||||
const ray = rays[rayIndex];
|
||||
const intersections = bvh.intersectRay(ray.origin, axesToDirection(ray.axis), false);
|
||||
for (const intersection of intersections) {
|
||||
@ -98,6 +107,7 @@ export class BVHRayVoxeliser extends IVoxeliser {
|
||||
}
|
||||
}
|
||||
}
|
||||
ProgressManager.Get.end('Voxelising');
|
||||
|
||||
return voxelMesh;
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import { Atlas } from './atlas';
|
||||
import { BlockMesh } from './block_mesh';
|
||||
import { BufferGenerator } from './buffer';
|
||||
import { EAppEvent, EventManager } from './event';
|
||||
import { IExporter } from './exporters/base_exporter';
|
||||
import { ExporterFactory } from './exporters/exporters';
|
||||
import { ObjImporter } from './importers/obj_importer';
|
||||
import { Mesh } from './mesh';
|
||||
import { ASSERT } from './util/error_util';
|
||||
import { Logger } from './util/log_util';
|
||||
import { VoxelMesh } from './voxel_mesh';
|
||||
import { IVoxeliser } from './voxelisers/base-voxeliser';
|
||||
import { VoxeliserFactory } from './voxelisers/voxelisers';
|
||||
import { AssignParams, ExportParams, ImportParams, RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams, VoxeliseParams } from './worker_types';
|
||||
import { AssignParams, ExportParams, ImportParams, RenderBlockMeshParams, RenderMeshParams, RenderVoxelMeshParams, TFromWorkerMessage, VoxeliseParams } from './worker_types';
|
||||
|
||||
export class WorkerClient {
|
||||
private static _instance: WorkerClient;
|
||||
@ -18,6 +20,43 @@ export class WorkerClient {
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
Logger.Get.enableLOG();
|
||||
Logger.Get.enableLOGMAJOR();
|
||||
Logger.Get.enableLOGWARN();
|
||||
|
||||
EventManager.Get.add(EAppEvent.onTaskStart, (e: any) => {
|
||||
const message: TFromWorkerMessage = {
|
||||
action: 'Progress',
|
||||
payload: {
|
||||
type: 'Started',
|
||||
taskId: e[0],
|
||||
},
|
||||
};
|
||||
postMessage(message);
|
||||
});
|
||||
|
||||
EventManager.Get.add(EAppEvent.onTaskProgress, (e: any) => {
|
||||
const message: TFromWorkerMessage = {
|
||||
action: 'Progress',
|
||||
payload: {
|
||||
type: 'Progress',
|
||||
taskId: e[0],
|
||||
percentage: e[1],
|
||||
},
|
||||
};
|
||||
postMessage(message);
|
||||
});
|
||||
|
||||
EventManager.Get.add(EAppEvent.onTaskEnd, (e: any) => {
|
||||
const message: TFromWorkerMessage = {
|
||||
action: 'Progress',
|
||||
payload: {
|
||||
type: 'Finished',
|
||||
taskId: e[0],
|
||||
},
|
||||
};
|
||||
postMessage(message);
|
||||
});
|
||||
}
|
||||
|
||||
private _loadedMesh?: Mesh;
|
||||
|
@ -37,8 +37,27 @@ export class WorkerController {
|
||||
return this._jobPending !== undefined;
|
||||
}
|
||||
|
||||
private _onWorkerMessage(payload: any) {
|
||||
private _onWorkerMessage(payload: MessageEvent<TFromWorkerMessage>) {
|
||||
ASSERT(this._jobPending !== undefined, `Received worker message when no job is pending`);
|
||||
|
||||
if (payload.data.action === 'Progress') {
|
||||
const element = document.getElementById('progress-bar') as HTMLDivElement;
|
||||
if (element) {
|
||||
switch (payload.data.payload.type) {
|
||||
case 'Started':
|
||||
element.style.width = `0%`;
|
||||
break;
|
||||
case 'Progress':
|
||||
element.style.width = `${payload.data.payload.percentage * 100}%`;
|
||||
break;
|
||||
case 'Finished':
|
||||
element.style.width = `100%`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TIME_END(this._jobPending.id);
|
||||
LOG(`[WorkerController]: Job '${this._jobPending.id}' finished:`);
|
||||
|
||||
@ -54,7 +73,7 @@ export class WorkerController {
|
||||
if (this.isBusy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this._jobPending = this._jobQueue.shift();
|
||||
if (this._jobPending === undefined) {
|
||||
return;
|
||||
|
@ -104,6 +104,11 @@ export type TStatus = {
|
||||
statusMessages: StatusMessage[],
|
||||
}
|
||||
|
||||
export type TaskParams =
|
||||
| { type: 'Started', taskId: string }
|
||||
| { type: 'Progress', taskId: string, percentage: number }
|
||||
| { type: 'Finished', taskId: string }
|
||||
|
||||
export type TToWorkerMessage =
|
||||
| { action: 'Import', params: ImportParams.Input }
|
||||
| { action: 'RenderMesh', params: RenderMeshParams.Input }
|
||||
@ -116,6 +121,7 @@ export type TToWorkerMessage =
|
||||
export type TFromWorkerMessage =
|
||||
| { action: 'KnownError', error: AppError }
|
||||
| { action: 'UnknownError', error: Error }
|
||||
| { action: 'Progress', payload: TaskParams }
|
||||
| (TStatus & (
|
||||
| { action: 'Import', result: ImportParams.Output }
|
||||
| { action: 'RenderMesh', result: RenderMeshParams.Output }
|
||||
|
13
styles.css
13
styles.css
@ -575,4 +575,17 @@ svg {
|
||||
100% {
|
||||
opacity: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.progress-bar-container {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
background-color: var(--prop-accent-standard);
|
||||
height: 100%;
|
||||
transition: width 0.1s;
|
||||
}
|
Loading…
Reference in New Issue
Block a user