@cadview/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +321 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -3
- package/dist/index.d.ts +77 -3
- package/dist/index.js +321 -14
- package/dist/index.js.map +1 -1
- package/package.json +25 -12
package/dist/index.cjs
CHANGED
|
@@ -2697,7 +2697,7 @@ function drawMText(ctx, entity, pixelSize) {
|
|
|
2697
2697
|
|
|
2698
2698
|
// src/renderer/entities/draw-insert.ts
|
|
2699
2699
|
var MAX_INSERT_DEPTH = 100;
|
|
2700
|
-
function drawInsert(ctx, entity, doc, vt, theme, pixelSize, depth = 0) {
|
|
2700
|
+
function drawInsert(ctx, entity, doc, vt, theme, pixelSize, depth = 0, stats) {
|
|
2701
2701
|
if (depth > MAX_INSERT_DEPTH) return;
|
|
2702
2702
|
const block = doc.blocks.get(entity.blockName);
|
|
2703
2703
|
if (!block) return;
|
|
@@ -2722,9 +2722,9 @@ function drawInsert(ctx, entity, doc, vt, theme, pixelSize, depth = 0) {
|
|
|
2722
2722
|
ctx.fillStyle = color;
|
|
2723
2723
|
ctx.lineWidth = adjustedPixelSize;
|
|
2724
2724
|
if (blockEntity.type === "INSERT") {
|
|
2725
|
-
drawInsert(ctx, blockEntity, doc, vt, theme, adjustedPixelSize, depth + 1);
|
|
2725
|
+
drawInsert(ctx, blockEntity, doc, vt, theme, adjustedPixelSize, depth + 1, stats);
|
|
2726
2726
|
} else {
|
|
2727
|
-
drawEntity(ctx, blockEntity, doc, vt, theme, adjustedPixelSize);
|
|
2727
|
+
drawEntity(ctx, blockEntity, doc, vt, theme, adjustedPixelSize, stats);
|
|
2728
2728
|
}
|
|
2729
2729
|
}
|
|
2730
2730
|
ctx.restore();
|
|
@@ -2733,7 +2733,7 @@ function drawInsert(ctx, entity, doc, vt, theme, pixelSize, depth = 0) {
|
|
|
2733
2733
|
}
|
|
2734
2734
|
|
|
2735
2735
|
// src/renderer/entities/draw-dimension.ts
|
|
2736
|
-
function drawDimension(ctx, entity, doc, vt, theme, pixelSize) {
|
|
2736
|
+
function drawDimension(ctx, entity, doc, vt, theme, pixelSize, stats) {
|
|
2737
2737
|
if (entity.blockName) {
|
|
2738
2738
|
const block = doc.blocks.get(entity.blockName);
|
|
2739
2739
|
if (block) {
|
|
@@ -2742,7 +2742,7 @@ function drawDimension(ctx, entity, doc, vt, theme, pixelSize) {
|
|
|
2742
2742
|
ctx.strokeStyle = color;
|
|
2743
2743
|
ctx.fillStyle = color;
|
|
2744
2744
|
ctx.lineWidth = pixelSize;
|
|
2745
|
-
drawEntity(ctx, blockEntity, doc, vt, theme, pixelSize);
|
|
2745
|
+
drawEntity(ctx, blockEntity, doc, vt, theme, pixelSize, stats);
|
|
2746
2746
|
}
|
|
2747
2747
|
return;
|
|
2748
2748
|
}
|
|
@@ -2819,7 +2819,11 @@ function drawPoint(ctx, entity, pixelSize) {
|
|
|
2819
2819
|
}
|
|
2820
2820
|
|
|
2821
2821
|
// src/renderer/entities/draw-entity.ts
|
|
2822
|
-
function drawEntity(ctx, entity, doc, vt, theme, pixelSize) {
|
|
2822
|
+
function drawEntity(ctx, entity, doc, vt, theme, pixelSize, stats) {
|
|
2823
|
+
if (stats) {
|
|
2824
|
+
stats.drawCalls++;
|
|
2825
|
+
stats.byType[entity.type] = (stats.byType[entity.type] ?? 0) + 1;
|
|
2826
|
+
}
|
|
2823
2827
|
switch (entity.type) {
|
|
2824
2828
|
case "LINE":
|
|
2825
2829
|
drawLine(ctx, entity);
|
|
@@ -2849,10 +2853,10 @@ function drawEntity(ctx, entity, doc, vt, theme, pixelSize) {
|
|
|
2849
2853
|
drawMText(ctx, entity, pixelSize);
|
|
2850
2854
|
break;
|
|
2851
2855
|
case "INSERT":
|
|
2852
|
-
drawInsert(ctx, entity, doc, vt, theme, pixelSize);
|
|
2856
|
+
drawInsert(ctx, entity, doc, vt, theme, pixelSize, 0, stats);
|
|
2853
2857
|
break;
|
|
2854
2858
|
case "DIMENSION":
|
|
2855
|
-
drawDimension(ctx, entity, doc, vt, theme, pixelSize);
|
|
2859
|
+
drawDimension(ctx, entity, doc, vt, theme, pixelSize, stats);
|
|
2856
2860
|
break;
|
|
2857
2861
|
case "HATCH":
|
|
2858
2862
|
drawHatch(ctx, entity);
|
|
@@ -2899,6 +2903,12 @@ var CanvasRenderer = class {
|
|
|
2899
2903
|
render(doc, vt, theme, visibleLayers, selectedEntityIndex) {
|
|
2900
2904
|
const ctx = this.ctx;
|
|
2901
2905
|
const dpr = window.devicePixelRatio || 1;
|
|
2906
|
+
const stats = {
|
|
2907
|
+
entitiesDrawn: 0,
|
|
2908
|
+
entitiesSkipped: 0,
|
|
2909
|
+
drawCalls: 0,
|
|
2910
|
+
byType: {}
|
|
2911
|
+
};
|
|
2902
2912
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
2903
2913
|
ctx.fillStyle = THEMES[theme].backgroundColor;
|
|
2904
2914
|
ctx.fillRect(0, 0, this.width, this.height);
|
|
@@ -2908,13 +2918,20 @@ var CanvasRenderer = class {
|
|
|
2908
2918
|
ctx.lineJoin = "round";
|
|
2909
2919
|
for (let i = 0; i < doc.entities.length; i++) {
|
|
2910
2920
|
const entity = doc.entities[i];
|
|
2911
|
-
if (!entity.visible)
|
|
2912
|
-
|
|
2921
|
+
if (!entity.visible) {
|
|
2922
|
+
stats.entitiesSkipped++;
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
if (!visibleLayers.has(entity.layer)) {
|
|
2926
|
+
stats.entitiesSkipped++;
|
|
2927
|
+
continue;
|
|
2928
|
+
}
|
|
2913
2929
|
const color = resolveEntityColor(entity, doc.layers, theme);
|
|
2914
2930
|
ctx.strokeStyle = color;
|
|
2915
2931
|
ctx.fillStyle = color;
|
|
2916
2932
|
ctx.lineWidth = pixelSize;
|
|
2917
|
-
|
|
2933
|
+
stats.entitiesDrawn++;
|
|
2934
|
+
drawEntity(ctx, entity, doc, vt, theme, pixelSize, stats);
|
|
2918
2935
|
}
|
|
2919
2936
|
if (selectedEntityIndex >= 0 && selectedEntityIndex < doc.entities.length) {
|
|
2920
2937
|
const selEntity = doc.entities[selectedEntityIndex];
|
|
@@ -2924,6 +2941,7 @@ var CanvasRenderer = class {
|
|
|
2924
2941
|
ctx.lineWidth = pixelSize * 3;
|
|
2925
2942
|
drawEntity(ctx, selEntity, doc, vt, theme, pixelSize);
|
|
2926
2943
|
}
|
|
2944
|
+
return stats;
|
|
2927
2945
|
}
|
|
2928
2946
|
renderEmpty(theme) {
|
|
2929
2947
|
const ctx = this.ctx;
|
|
@@ -2936,6 +2954,136 @@ var CanvasRenderer = class {
|
|
|
2936
2954
|
}
|
|
2937
2955
|
};
|
|
2938
2956
|
|
|
2957
|
+
// src/renderer/debug-overlay.ts
|
|
2958
|
+
var DEFAULT_DEBUG_OPTIONS = {
|
|
2959
|
+
showFps: true,
|
|
2960
|
+
showRenderStats: true,
|
|
2961
|
+
showDocumentInfo: true,
|
|
2962
|
+
showTimings: true,
|
|
2963
|
+
showCamera: true,
|
|
2964
|
+
position: "top-left"
|
|
2965
|
+
};
|
|
2966
|
+
function resolveDebugOptions(input) {
|
|
2967
|
+
return { ...DEFAULT_DEBUG_OPTIONS, ...input };
|
|
2968
|
+
}
|
|
2969
|
+
function formatBytes(bytes) {
|
|
2970
|
+
if (bytes === 0) return "0 B";
|
|
2971
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2972
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2973
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2974
|
+
}
|
|
2975
|
+
function formatZoom(scale) {
|
|
2976
|
+
if (scale >= 1) return `${scale.toFixed(2)}x`;
|
|
2977
|
+
return `1:${(1 / scale).toFixed(1)}`;
|
|
2978
|
+
}
|
|
2979
|
+
var FONT = "11px monospace";
|
|
2980
|
+
var LINE_HEIGHT = 15;
|
|
2981
|
+
var SEPARATOR_HEIGHT = 8;
|
|
2982
|
+
var PADDING = 8;
|
|
2983
|
+
var MARGIN = 10;
|
|
2984
|
+
function renderDebugOverlay(ctx, stats, theme, options, canvasWidth, canvasHeight) {
|
|
2985
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
2986
|
+
const sections = [];
|
|
2987
|
+
if (options.showFps) {
|
|
2988
|
+
sections.push([
|
|
2989
|
+
`FPS: ${stats.fps} Frame: ${stats.frameTime.toFixed(1)}ms`
|
|
2990
|
+
]);
|
|
2991
|
+
}
|
|
2992
|
+
if (options.showRenderStats) {
|
|
2993
|
+
const total = stats.renderStats.entitiesDrawn + stats.renderStats.entitiesSkipped;
|
|
2994
|
+
const lines = [
|
|
2995
|
+
`Drawn: ${stats.renderStats.entitiesDrawn} / ${total} Calls: ${stats.renderStats.drawCalls}`
|
|
2996
|
+
];
|
|
2997
|
+
const types = Object.entries(stats.renderStats.byType).sort(([, a], [, b]) => b - a).slice(0, 6).map(([type, count]) => `${type}: ${count}`).join(" ");
|
|
2998
|
+
if (types) lines.push(types);
|
|
2999
|
+
sections.push(lines);
|
|
3000
|
+
}
|
|
3001
|
+
if (options.showDocumentInfo) {
|
|
3002
|
+
const lines = [
|
|
3003
|
+
`Layers: ${stats.visibleLayerCount} / ${stats.layerCount} Blocks: ${stats.blockCount}`
|
|
3004
|
+
];
|
|
3005
|
+
if (stats.dxfVersion) lines.push(`DXF: ${stats.dxfVersion}`);
|
|
3006
|
+
if (stats.fileName) lines.push(`File: ${stats.fileName}`);
|
|
3007
|
+
if (stats.fileSize > 0) lines.push(`Size: ${formatBytes(stats.fileSize)}`);
|
|
3008
|
+
sections.push(lines);
|
|
3009
|
+
}
|
|
3010
|
+
if (options.showTimings) {
|
|
3011
|
+
const parts = [];
|
|
3012
|
+
if (stats.parseTime > 0) parts.push(`Parse: ${stats.parseTime.toFixed(0)}ms`);
|
|
3013
|
+
if (stats.spatialIndexBuildTime > 0) parts.push(`Index: ${stats.spatialIndexBuildTime.toFixed(0)}ms`);
|
|
3014
|
+
if (stats.totalLoadTime > 0) parts.push(`Load: ${stats.totalLoadTime.toFixed(0)}ms`);
|
|
3015
|
+
if (parts.length > 0) {
|
|
3016
|
+
sections.push([parts.join(" ")]);
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
if (options.showCamera) {
|
|
3020
|
+
const b = stats.viewportBounds;
|
|
3021
|
+
sections.push([
|
|
3022
|
+
`Zoom: ${formatZoom(stats.zoom)} Pixel: ${stats.pixelSize.toFixed(2)}`,
|
|
3023
|
+
`View: [${b.minX.toFixed(0)}, ${b.minY.toFixed(0)}] \u2192 [${b.maxX.toFixed(0)}, ${b.maxY.toFixed(0)}]`
|
|
3024
|
+
]);
|
|
3025
|
+
}
|
|
3026
|
+
if (sections.length === 0) return;
|
|
3027
|
+
ctx.font = FONT;
|
|
3028
|
+
const rows = [];
|
|
3029
|
+
for (let s = 0; s < sections.length; s++) {
|
|
3030
|
+
if (s > 0) rows.push({ text: "", isSeparator: true });
|
|
3031
|
+
for (const line of sections[s]) {
|
|
3032
|
+
rows.push({ text: line, isSeparator: false });
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
let maxWidth = 0;
|
|
3036
|
+
for (const row of rows) {
|
|
3037
|
+
if (!row.isSeparator) {
|
|
3038
|
+
const w = ctx.measureText(row.text).width;
|
|
3039
|
+
if (w > maxWidth) maxWidth = w;
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
const panelWidth = maxWidth + PADDING * 2;
|
|
3043
|
+
let panelHeight = PADDING * 2;
|
|
3044
|
+
for (const row of rows) {
|
|
3045
|
+
panelHeight += row.isSeparator ? SEPARATOR_HEIGHT : LINE_HEIGHT;
|
|
3046
|
+
}
|
|
3047
|
+
let x;
|
|
3048
|
+
let y;
|
|
3049
|
+
switch (options.position) {
|
|
3050
|
+
case "top-left":
|
|
3051
|
+
x = MARGIN;
|
|
3052
|
+
y = MARGIN;
|
|
3053
|
+
break;
|
|
3054
|
+
case "top-right":
|
|
3055
|
+
x = canvasWidth - panelWidth - MARGIN;
|
|
3056
|
+
y = MARGIN;
|
|
3057
|
+
break;
|
|
3058
|
+
case "bottom-left":
|
|
3059
|
+
x = MARGIN;
|
|
3060
|
+
y = canvasHeight - panelHeight - MARGIN;
|
|
3061
|
+
break;
|
|
3062
|
+
case "bottom-right":
|
|
3063
|
+
x = canvasWidth - panelWidth - MARGIN;
|
|
3064
|
+
y = canvasHeight - panelHeight - MARGIN;
|
|
3065
|
+
break;
|
|
3066
|
+
}
|
|
3067
|
+
const config = THEMES[theme];
|
|
3068
|
+
ctx.fillStyle = theme === "dark" ? "rgba(0, 0, 0, 0.75)" : "rgba(255, 255, 255, 0.85)";
|
|
3069
|
+
ctx.fillRect(x, y, panelWidth, panelHeight);
|
|
3070
|
+
ctx.strokeStyle = theme === "dark" ? "rgba(255, 255, 255, 0.15)" : "rgba(0, 0, 0, 0.15)";
|
|
3071
|
+
ctx.lineWidth = 1;
|
|
3072
|
+
ctx.strokeRect(x + 0.5, y + 0.5, panelWidth - 1, panelHeight - 1);
|
|
3073
|
+
ctx.fillStyle = theme === "dark" ? config.defaultEntityColor : "rgba(0, 0, 0, 0.85)";
|
|
3074
|
+
ctx.textAlign = "left";
|
|
3075
|
+
ctx.textBaseline = "top";
|
|
3076
|
+
let cursorY = y + PADDING;
|
|
3077
|
+
for (const row of rows) {
|
|
3078
|
+
if (row.isSeparator) {
|
|
3079
|
+
cursorY += SEPARATOR_HEIGHT;
|
|
3080
|
+
} else {
|
|
3081
|
+
ctx.fillText(row.text, x + PADDING, cursorY);
|
|
3082
|
+
cursorY += LINE_HEIGHT;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
|
|
2939
3087
|
// src/viewer/layers.ts
|
|
2940
3088
|
var LayerManager = class {
|
|
2941
3089
|
layers = /* @__PURE__ */ new Map();
|
|
@@ -3726,6 +3874,19 @@ var CadViewer = class {
|
|
|
3726
3874
|
loadGeneration = 0;
|
|
3727
3875
|
mouseScreenX = 0;
|
|
3728
3876
|
mouseScreenY = 0;
|
|
3877
|
+
// Debug mode state
|
|
3878
|
+
debugEnabled = false;
|
|
3879
|
+
debugOptions;
|
|
3880
|
+
lastRenderStats = null;
|
|
3881
|
+
lastDebugStats = null;
|
|
3882
|
+
frameTimestamps = [];
|
|
3883
|
+
lastDoRenderTime = 0;
|
|
3884
|
+
lastFrameTime = 0;
|
|
3885
|
+
parseTime = 0;
|
|
3886
|
+
spatialIndexBuildTime = 0;
|
|
3887
|
+
loadedFileName = null;
|
|
3888
|
+
loadedFileSize = 0;
|
|
3889
|
+
debugRafId = 0;
|
|
3729
3890
|
constructor(canvas, options) {
|
|
3730
3891
|
this.canvas = canvas;
|
|
3731
3892
|
this.options = {
|
|
@@ -3738,6 +3899,14 @@ var CadViewer = class {
|
|
|
3738
3899
|
initialTool: options?.initialTool ?? "pan"
|
|
3739
3900
|
};
|
|
3740
3901
|
this.formatConverters = options?.formatConverters ?? [];
|
|
3902
|
+
if (options?.debug) {
|
|
3903
|
+
this.debugEnabled = true;
|
|
3904
|
+
this.debugOptions = resolveDebugOptions(
|
|
3905
|
+
typeof options.debug === "boolean" ? void 0 : options.debug
|
|
3906
|
+
);
|
|
3907
|
+
} else {
|
|
3908
|
+
this.debugOptions = resolveDebugOptions();
|
|
3909
|
+
}
|
|
3741
3910
|
this.renderer = new CanvasRenderer(canvas);
|
|
3742
3911
|
this.camera = new Camera(this.options);
|
|
3743
3912
|
this.layerManager = new LayerManager();
|
|
@@ -3750,6 +3919,9 @@ var CadViewer = class {
|
|
|
3750
3919
|
this.resizeObserver.observe(canvas);
|
|
3751
3920
|
canvas.style.cursor = this.getCursorForTool(this.currentTool);
|
|
3752
3921
|
this.requestRender();
|
|
3922
|
+
if (this.debugEnabled) {
|
|
3923
|
+
this.startDebugLoop();
|
|
3924
|
+
}
|
|
3753
3925
|
}
|
|
3754
3926
|
// === Loading ===
|
|
3755
3927
|
/**
|
|
@@ -3788,11 +3960,15 @@ var CadViewer = class {
|
|
|
3788
3960
|
async loadFile(file) {
|
|
3789
3961
|
this.guardDestroyed();
|
|
3790
3962
|
const generation = ++this.loadGeneration;
|
|
3963
|
+
this.loadedFileName = file.name;
|
|
3964
|
+
this.loadedFileSize = file.size;
|
|
3791
3965
|
const buffer = await file.arrayBuffer();
|
|
3792
3966
|
if (this.destroyed || generation !== this.loadGeneration) return;
|
|
3793
3967
|
const dxfString = await this.runConverters(buffer);
|
|
3794
3968
|
if (this.destroyed || generation !== this.loadGeneration) return;
|
|
3969
|
+
const t0 = performance.now();
|
|
3795
3970
|
this.doc = dxfString != null ? parseDxf(dxfString) : parseDxf(buffer);
|
|
3971
|
+
this.parseTime = performance.now() - t0;
|
|
3796
3972
|
this.onDocumentLoaded();
|
|
3797
3973
|
}
|
|
3798
3974
|
/**
|
|
@@ -3803,9 +3979,13 @@ var CadViewer = class {
|
|
|
3803
3979
|
async loadBuffer(buffer) {
|
|
3804
3980
|
this.guardDestroyed();
|
|
3805
3981
|
const generation = ++this.loadGeneration;
|
|
3982
|
+
this.loadedFileName = null;
|
|
3983
|
+
this.loadedFileSize = buffer.byteLength;
|
|
3806
3984
|
const dxfString = await this.runConverters(buffer);
|
|
3807
3985
|
if (this.destroyed || generation !== this.loadGeneration) return;
|
|
3986
|
+
const t0 = performance.now();
|
|
3808
3987
|
this.doc = dxfString != null ? parseDxf(dxfString) : parseDxf(buffer);
|
|
3988
|
+
this.parseTime = performance.now() - t0;
|
|
3809
3989
|
this.onDocumentLoaded();
|
|
3810
3990
|
}
|
|
3811
3991
|
/**
|
|
@@ -3815,6 +3995,9 @@ var CadViewer = class {
|
|
|
3815
3995
|
loadDocument(doc) {
|
|
3816
3996
|
this.guardDestroyed();
|
|
3817
3997
|
++this.loadGeneration;
|
|
3998
|
+
this.loadedFileName = null;
|
|
3999
|
+
this.loadedFileSize = 0;
|
|
4000
|
+
this.parseTime = 0;
|
|
3818
4001
|
if (!doc || !Array.isArray(doc.entities) || !(doc.layers instanceof Map)) {
|
|
3819
4002
|
throw new Error("CadViewer: invalid DxfDocument \u2014 expected entities array and layers Map.");
|
|
3820
4003
|
}
|
|
@@ -3827,7 +4010,11 @@ var CadViewer = class {
|
|
|
3827
4010
|
loadString(dxf) {
|
|
3828
4011
|
this.guardDestroyed();
|
|
3829
4012
|
++this.loadGeneration;
|
|
4013
|
+
this.loadedFileName = null;
|
|
4014
|
+
this.loadedFileSize = dxf.length;
|
|
4015
|
+
const t0 = performance.now();
|
|
3830
4016
|
this.doc = parseDxf(dxf);
|
|
4017
|
+
this.parseTime = performance.now() - t0;
|
|
3831
4018
|
this.onDocumentLoaded();
|
|
3832
4019
|
}
|
|
3833
4020
|
/**
|
|
@@ -3837,7 +4024,11 @@ var CadViewer = class {
|
|
|
3837
4024
|
loadArrayBuffer(buffer) {
|
|
3838
4025
|
this.guardDestroyed();
|
|
3839
4026
|
++this.loadGeneration;
|
|
4027
|
+
this.loadedFileName = null;
|
|
4028
|
+
this.loadedFileSize = buffer.byteLength;
|
|
4029
|
+
const t0 = performance.now();
|
|
3840
4030
|
this.doc = parseDxf(buffer);
|
|
4031
|
+
this.parseTime = performance.now() - t0;
|
|
3841
4032
|
this.onDocumentLoaded();
|
|
3842
4033
|
}
|
|
3843
4034
|
/**
|
|
@@ -3856,7 +4047,9 @@ var CadViewer = class {
|
|
|
3856
4047
|
onDocumentLoaded() {
|
|
3857
4048
|
if (!this.doc) return;
|
|
3858
4049
|
this.layerManager.setLayers(this.doc.layers);
|
|
4050
|
+
const t0 = performance.now();
|
|
3859
4051
|
this.spatialIndex.build(this.doc.entities);
|
|
4052
|
+
this.spatialIndexBuildTime = performance.now() - t0;
|
|
3860
4053
|
this.selectedEntityIndex = -1;
|
|
3861
4054
|
this.measureTool.deactivate();
|
|
3862
4055
|
if (this.currentTool === "measure") {
|
|
@@ -3984,6 +4177,7 @@ var CadViewer = class {
|
|
|
3984
4177
|
}
|
|
3985
4178
|
destroy() {
|
|
3986
4179
|
this.destroyed = true;
|
|
4180
|
+
this.stopDebugLoop();
|
|
3987
4181
|
this.inputHandler.destroy();
|
|
3988
4182
|
this.resizeObserver.disconnect();
|
|
3989
4183
|
this.renderer.destroy();
|
|
@@ -3995,7 +4189,12 @@ var CadViewer = class {
|
|
|
3995
4189
|
// === Internal (called by InputHandler) ===
|
|
3996
4190
|
/** @internal */
|
|
3997
4191
|
requestRender() {
|
|
3998
|
-
if (this.
|
|
4192
|
+
if (this.destroyed) return;
|
|
4193
|
+
if (this.debugRafId) {
|
|
4194
|
+
this.doRender();
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
if (this.renderPending) return;
|
|
3999
4198
|
this.renderPending = true;
|
|
4000
4199
|
requestAnimationFrame(() => {
|
|
4001
4200
|
this.renderPending = false;
|
|
@@ -4008,13 +4207,24 @@ var CadViewer = class {
|
|
|
4008
4207
|
this.renderer.renderEmpty(this.options.theme);
|
|
4009
4208
|
return;
|
|
4010
4209
|
}
|
|
4011
|
-
|
|
4210
|
+
const renderStart = performance.now();
|
|
4211
|
+
const stats = this.renderer.render(
|
|
4012
4212
|
this.doc,
|
|
4013
4213
|
this.camera.getTransform(),
|
|
4014
4214
|
this.options.theme,
|
|
4015
4215
|
this.layerManager.getVisibleLayerNames(),
|
|
4016
4216
|
this.selectedEntityIndex
|
|
4017
4217
|
);
|
|
4218
|
+
this.lastFrameTime = performance.now() - renderStart;
|
|
4219
|
+
this.lastRenderStats = stats;
|
|
4220
|
+
const now = performance.now();
|
|
4221
|
+
if (now - this.lastDoRenderTime >= 3) {
|
|
4222
|
+
this.frameTimestamps.push(now);
|
|
4223
|
+
}
|
|
4224
|
+
this.lastDoRenderTime = now;
|
|
4225
|
+
while (this.frameTimestamps.length > 0 && this.frameTimestamps[0] < now - 1e3) {
|
|
4226
|
+
this.frameTimestamps.shift();
|
|
4227
|
+
}
|
|
4018
4228
|
if (this.currentTool === "measure" && this.measureTool.state.phase !== "idle") {
|
|
4019
4229
|
const ctx = this.renderer.getContext();
|
|
4020
4230
|
renderMeasureOverlay(
|
|
@@ -4026,7 +4236,104 @@ var CadViewer = class {
|
|
|
4026
4236
|
this.options.theme
|
|
4027
4237
|
);
|
|
4028
4238
|
}
|
|
4239
|
+
if (this.debugEnabled) {
|
|
4240
|
+
const ctx = this.renderer.getContext();
|
|
4241
|
+
const debugStats = this.buildDebugStats();
|
|
4242
|
+
this.lastDebugStats = debugStats;
|
|
4243
|
+
renderDebugOverlay(
|
|
4244
|
+
ctx,
|
|
4245
|
+
debugStats,
|
|
4246
|
+
this.options.theme,
|
|
4247
|
+
this.debugOptions,
|
|
4248
|
+
this.renderer.getWidth(),
|
|
4249
|
+
this.renderer.getHeight()
|
|
4250
|
+
);
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
// === Debug Mode ===
|
|
4254
|
+
/**
|
|
4255
|
+
* Enable or disable the debug overlay.
|
|
4256
|
+
* Pass `true` for defaults, `false` to disable, or an object for granular control.
|
|
4257
|
+
*/
|
|
4258
|
+
setDebug(debug) {
|
|
4259
|
+
this.guardDestroyed();
|
|
4260
|
+
if (typeof debug === "boolean") {
|
|
4261
|
+
this.debugEnabled = debug;
|
|
4262
|
+
} else {
|
|
4263
|
+
this.debugEnabled = true;
|
|
4264
|
+
this.debugOptions = resolveDebugOptions(debug);
|
|
4265
|
+
}
|
|
4266
|
+
if (this.debugEnabled) {
|
|
4267
|
+
this.startDebugLoop();
|
|
4268
|
+
} else {
|
|
4269
|
+
this.stopDebugLoop();
|
|
4270
|
+
this.requestRender();
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
startDebugLoop() {
|
|
4274
|
+
if (this.debugRafId) return;
|
|
4275
|
+
const loop = () => {
|
|
4276
|
+
if (!this.debugEnabled || this.destroyed) {
|
|
4277
|
+
this.debugRafId = 0;
|
|
4278
|
+
return;
|
|
4279
|
+
}
|
|
4280
|
+
this.doRender();
|
|
4281
|
+
this.debugRafId = requestAnimationFrame(loop);
|
|
4282
|
+
};
|
|
4283
|
+
this.debugRafId = requestAnimationFrame(loop);
|
|
4284
|
+
}
|
|
4285
|
+
stopDebugLoop() {
|
|
4286
|
+
if (this.debugRafId) {
|
|
4287
|
+
cancelAnimationFrame(this.debugRafId);
|
|
4288
|
+
this.debugRafId = 0;
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
/**
|
|
4292
|
+
* Get the latest debug stats snapshot, or null if debug mode is off.
|
|
4293
|
+
*/
|
|
4294
|
+
getDebugStats() {
|
|
4295
|
+
return this.debugEnabled ? this.lastDebugStats : null;
|
|
4296
|
+
}
|
|
4297
|
+
buildDebugStats() {
|
|
4298
|
+
const vt = this.camera.getTransform();
|
|
4299
|
+
const w = this.renderer.getWidth();
|
|
4300
|
+
const h = this.renderer.getHeight();
|
|
4301
|
+
const bounds = this.computeViewportBounds(vt, w, h);
|
|
4302
|
+
return {
|
|
4303
|
+
fps: this.frameTimestamps.length,
|
|
4304
|
+
frameTime: this.lastFrameTime,
|
|
4305
|
+
renderStats: this.lastRenderStats ?? {
|
|
4306
|
+
entitiesDrawn: 0,
|
|
4307
|
+
entitiesSkipped: 0,
|
|
4308
|
+
drawCalls: 0,
|
|
4309
|
+
byType: {}
|
|
4310
|
+
},
|
|
4311
|
+
entityCount: this.doc?.entities.length ?? 0,
|
|
4312
|
+
layerCount: this.doc?.layers.size ?? 0,
|
|
4313
|
+
visibleLayerCount: this.layerManager.getVisibleLayerNames().size,
|
|
4314
|
+
blockCount: this.doc?.blocks.size ?? 0,
|
|
4315
|
+
parseTime: this.parseTime,
|
|
4316
|
+
spatialIndexBuildTime: this.spatialIndexBuildTime,
|
|
4317
|
+
totalLoadTime: this.parseTime + this.spatialIndexBuildTime,
|
|
4318
|
+
zoom: vt.scale,
|
|
4319
|
+
pixelSize: vt.scale > 0 ? 1 / vt.scale : 0,
|
|
4320
|
+
viewportBounds: bounds,
|
|
4321
|
+
fileName: this.loadedFileName,
|
|
4322
|
+
fileSize: this.loadedFileSize,
|
|
4323
|
+
dxfVersion: this.doc?.header.acadVersion ?? null
|
|
4324
|
+
};
|
|
4325
|
+
}
|
|
4326
|
+
computeViewportBounds(vt, w, h) {
|
|
4327
|
+
const [x1, y1] = screenToWorld(vt, 0, h);
|
|
4328
|
+
const [x2, y2] = screenToWorld(vt, w, 0);
|
|
4329
|
+
return {
|
|
4330
|
+
minX: Math.min(x1, x2),
|
|
4331
|
+
minY: Math.min(y1, y2),
|
|
4332
|
+
maxX: Math.max(x1, x2),
|
|
4333
|
+
maxY: Math.max(y1, y2)
|
|
4334
|
+
};
|
|
4029
4335
|
}
|
|
4336
|
+
// === Internal (called by InputHandler) ===
|
|
4030
4337
|
/** @internal */
|
|
4031
4338
|
handlePan(dx, dy) {
|
|
4032
4339
|
this.camera.pan(dx, dy);
|
|
@@ -4129,6 +4436,7 @@ exports.findSnaps = findSnaps;
|
|
|
4129
4436
|
exports.fitToView = fitToView;
|
|
4130
4437
|
exports.hitTest = hitTest;
|
|
4131
4438
|
exports.parseDxf = parseDxf;
|
|
4439
|
+
exports.renderDebugOverlay = renderDebugOverlay;
|
|
4132
4440
|
exports.renderMeasureOverlay = renderMeasureOverlay;
|
|
4133
4441
|
exports.resolveEntityColor = resolveEntityColor;
|
|
4134
4442
|
exports.screenToWorld = screenToWorld;
|