mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
big rewrite focusing on faster performance and selection improvements
This commit is contained in:
@@ -26,7 +26,6 @@ function getCenterAndVertexList(obj: MObject3D, scene: ModelScene): {
|
||||
vertices.push(vertex);
|
||||
}
|
||||
center = center.divideScalar(ind.count);
|
||||
console.log("center", center)
|
||||
return {center, vertices};
|
||||
}
|
||||
|
||||
@@ -46,7 +45,6 @@ export function distances(a: MObject3D, b: MObject3D, scene: ModelScene): {
|
||||
|
||||
// Find the closest and farthest vertices.
|
||||
// TODO: Compute actual min and max distances between the two objects.
|
||||
// FIXME: Working for points and lines, but not triangles...
|
||||
// FIXME: Really slow... (use a BVH or something)
|
||||
let minDistance = Infinity;
|
||||
let minDistanceVertices = [new Vector3(), new Vector3()];
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
import {settings} from "./settings";
|
||||
|
||||
export class NetworkUpdateEvent extends Event {
|
||||
const batchTimeout = 250; // ms
|
||||
|
||||
class NetworkUpdateEventModel {
|
||||
name: string;
|
||||
url: string;
|
||||
// TODO: Detect and manage instances of the same object (same hash, different name)
|
||||
hash: string | null;
|
||||
isRemove: boolean;
|
||||
|
||||
constructor(name: string, url: string) {
|
||||
super("update");
|
||||
constructor(name: string, url: string, hash: string | null, isDelete: boolean) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.hash = hash;
|
||||
this.isRemove = isDelete;
|
||||
}
|
||||
}
|
||||
|
||||
export class NetworkUpdateEvent extends Event {
|
||||
models: NetworkUpdateEventModel[];
|
||||
|
||||
constructor(models: NetworkUpdateEventModel[]) {
|
||||
super("update");
|
||||
this.models = models;
|
||||
}
|
||||
}
|
||||
|
||||
/** Listens for updates and emits events when a model changes */
|
||||
export class NetworkManager extends EventTarget {
|
||||
private knownObjectHashes: { [name: string]: string | null } = {};
|
||||
private bufferedUpdates: NetworkUpdateEventModel[] = [];
|
||||
private batchTimeout: number | null = null;
|
||||
|
||||
/**
|
||||
* Tries to load a new model (.glb) from the given URL.
|
||||
@@ -36,7 +53,7 @@ export class NetworkManager extends EventTarget {
|
||||
let response = await fetch(url, {method: "HEAD"});
|
||||
let hash = response.headers.get("etag");
|
||||
// Only trigger an update if the hash has changed
|
||||
this.foundModel(name, hash, url);
|
||||
this.foundModel(name, hash, url, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,17 +61,17 @@ export class NetworkManager extends EventTarget {
|
||||
try {
|
||||
// WARNING: This will spam the console logs with failed requests when the server is down
|
||||
let response = await fetch(url.toString());
|
||||
console.log("Monitoring", url.toString(), response);
|
||||
// console.log("Monitoring", url.toString(), response);
|
||||
if (response.status === 200) {
|
||||
let lines = readLinesStreamings(response.body!.getReader());
|
||||
for await (let line of lines) {
|
||||
if (!line || !line.startsWith("data:")) continue;
|
||||
let data = JSON.parse(line.slice(5));
|
||||
console.debug("WebSocket message", data);
|
||||
// console.debug("WebSocket message", data);
|
||||
let urlObj = new URL(url);
|
||||
urlObj.searchParams.delete("api_updates");
|
||||
urlObj.searchParams.set("api_object", data.name);
|
||||
this.foundModel(data.name, data.hash, urlObj.toString());
|
||||
this.foundModel(data.name, data.hash, urlObj.toString(), data.is_remove);
|
||||
}
|
||||
}
|
||||
} catch (e) { // Ignore errors (retry very soon)
|
||||
@@ -63,12 +80,21 @@ export class NetworkManager extends EventTarget {
|
||||
return;
|
||||
}
|
||||
|
||||
private foundModel(name: string, hash: string | null, url: string) {
|
||||
private foundModel(name: string, hash: string | null, url: string, isRemove: boolean) {
|
||||
let prevHash = this.knownObjectHashes[name];
|
||||
// TODO: Detect and manage instances of the same object (same hash, different name)
|
||||
if (!hash || hash !== prevHash) {
|
||||
let hashToCheck = hash + (isRemove ? "-remove" : "");
|
||||
// console.debug("Found model", name, "with hash", hash, "and previous hash", prevHash);
|
||||
if (!hash || hashToCheck !== prevHash) {
|
||||
this.knownObjectHashes[name] = hash;
|
||||
this.dispatchEvent(new NetworkUpdateEvent(name, url));
|
||||
let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove);
|
||||
this.bufferedUpdates.push(newModel);
|
||||
|
||||
// Optimization: try to batch updates automatically for faster rendering
|
||||
if (this.batchTimeout !== null) clearTimeout(this.batchTimeout);
|
||||
this.batchTimeout = setTimeout(() => {
|
||||
this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates));
|
||||
this.bufferedUpdates = [];
|
||||
}, batchTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {Matrix4} from "three/src/math/Matrix4.js"
|
||||
/** This class helps manage SceneManagerData. All methods are static to support reactivity... */
|
||||
export class SceneMgr {
|
||||
/** Loads a GLB model from a URL and adds it to the viewer or replaces it if the names match */
|
||||
static async loadModel(sceneUrl: Ref<string>, document: Document, name: string, url: string): Promise<Document> {
|
||||
static async loadModel(sceneUrl: Ref<string>, document: Document, name: string, url: string, updateHelpers: boolean = true, reloadScene: boolean = true): Promise<Document> {
|
||||
let loadStart = performance.now();
|
||||
|
||||
// Start merging into the current document, replacing or adding as needed
|
||||
@@ -17,11 +17,13 @@ export class SceneMgr {
|
||||
|
||||
console.log("Model", name, "loaded in", performance.now() - loadStart, "ms");
|
||||
|
||||
if (name !== extrasNameValueHelpers) {
|
||||
if (updateHelpers) {
|
||||
// Reload the helpers to fit the new model
|
||||
// TODO: Only reload the helpers after a few milliseconds of no more models being added/removed
|
||||
await this.reloadHelpers(sceneUrl, document);
|
||||
} else {
|
||||
await this.reloadHelpers(sceneUrl, document, reloadScene);
|
||||
reloadScene = false;
|
||||
}
|
||||
|
||||
if (reloadScene) {
|
||||
// Display the final fully loaded model
|
||||
let displayStart = performance.now();
|
||||
document = await this.showCurrentDoc(sceneUrl, document);
|
||||
@@ -31,7 +33,7 @@ export class SceneMgr {
|
||||
return document;
|
||||
}
|
||||
|
||||
private static async reloadHelpers(sceneUrl: Ref<string>, document: Document): Promise<Document> {
|
||||
private static async reloadHelpers(sceneUrl: Ref<string>, document: Document, reloadScene: boolean): Promise<Document> {
|
||||
let bb = SceneMgr.getBoundingBox(document);
|
||||
|
||||
// Create the helper axes and grid box
|
||||
@@ -40,7 +42,7 @@ export class SceneMgr {
|
||||
newAxes(helpersDoc, bb.getSize(new Vector3()).multiplyScalar(0.5), transform);
|
||||
newGridBox(helpersDoc, bb.getSize(new Vector3()), transform);
|
||||
let helpersUrl = URL.createObjectURL(new Blob([await toBuffer(helpersDoc)]));
|
||||
return await SceneMgr.loadModel(sceneUrl, document, extrasNameValueHelpers, helpersUrl);
|
||||
return await SceneMgr.loadModel(sceneUrl, document, extrasNameValueHelpers, helpersUrl, false, reloadScene);
|
||||
}
|
||||
|
||||
static getBoundingBox(document: Document): Box3 {
|
||||
@@ -67,7 +69,7 @@ export class SceneMgr {
|
||||
}
|
||||
|
||||
/** Removes a model from the viewer */
|
||||
static async removeModel(sceneUrl: Ref<string>, document: Document, name: string): Promise<Document> {
|
||||
static async removeModel(sceneUrl: Ref<string>, document: Document, name: string, updateHelpers: boolean = true, reloadScene: boolean = true): Promise<Document> {
|
||||
let loadStart = performance.now();
|
||||
|
||||
// Remove the model from the document
|
||||
@@ -75,8 +77,10 @@ export class SceneMgr {
|
||||
|
||||
console.log("Model", name, "removed in", performance.now() - loadStart, "ms");
|
||||
|
||||
// Reload the helpers to fit the new model (will also show the document)
|
||||
document = await this.reloadHelpers(sceneUrl, document);
|
||||
if (updateHelpers) {
|
||||
// Reload the helpers to fit the new model (will also show the document)
|
||||
document = await this.reloadHelpers(sceneUrl, document, reloadScene);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user