@principal-ai/file-city-react 0.4.3 → 0.4.4
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/components/ArchitectureMapHighlightLayers.d.ts.map +1 -1
- package/dist/components/ArchitectureMapHighlightLayers.js +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/render/client/drawLayeredBuildings.d.ts +24 -2
- package/dist/render/client/drawLayeredBuildings.d.ts.map +1 -1
- package/dist/render/client/drawLayeredBuildings.js +93 -5
- package/package.json +1 -1
- package/src/components/ArchitectureMapHighlightLayers.tsx +7 -0
- package/src/index.ts +1 -0
- package/src/render/client/drawLayeredBuildings.ts +102 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAQjF,OAAO,EAIL,cAAc,
|
|
1
|
+
{"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"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"}
|
|
@@ -696,6 +696,8 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
696
696
|
}
|
|
697
697
|
return layers;
|
|
698
698
|
}, [stableLayers, dynamicLayers, abstractionLayer]);
|
|
699
|
+
// Memoize layer index for O(1) path lookups - only rebuilds when layers change
|
|
700
|
+
const layerIndex = (0, react_1.useMemo)(() => new drawLayeredBuildings_1.LayerIndex(allLayers), [allLayers]);
|
|
699
701
|
// Memoize abstracted paths lookup - only recalculates when abstraction layer changes
|
|
700
702
|
const { abstractedPathsSet, abstractedPathLookup } = (0, react_1.useMemo)(() => {
|
|
701
703
|
const pathsSet = new Set();
|
|
@@ -828,9 +830,9 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
828
830
|
// Use memoized visible districts and buildings (pre-filtered, doesn't recalculate on hover)
|
|
829
831
|
// Draw districts with layer support
|
|
830
832
|
(0, drawLayeredBuildings_1.drawLayeredDistricts)(ctx, visibleDistrictsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredDistrict, fullSize, resolvedDefaultDirectoryColor, filteredCityData.metadata.layoutConfig, abstractedPathsSet, // Pass abstracted paths to skip labels
|
|
831
|
-
showDirectoryLabels, districtBorderRadius);
|
|
833
|
+
showDirectoryLabels, districtBorderRadius, layerIndex);
|
|
832
834
|
// Draw buildings with layer support
|
|
833
|
-
(0, drawLayeredBuildings_1.drawLayeredBuildings)(ctx, visibleBuildingsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredBuilding, resolvedDefaultBuildingColor, showFileNames, resolvedHoverBorderColor, disableOpacityDimming, showFileTypeIcons, buildingBorderRadius);
|
|
835
|
+
(0, drawLayeredBuildings_1.drawLayeredBuildings)(ctx, visibleBuildingsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredBuilding, resolvedDefaultBuildingColor, showFileNames, resolvedHoverBorderColor, disableOpacityDimming, showFileTypeIcons, buildingBorderRadius, layerIndex);
|
|
834
836
|
// Performance monitoring end available for debugging
|
|
835
837
|
// Performance stats available but not logged to reduce console noise
|
|
836
838
|
// Uncomment for debugging: render time, buildings/districts counts, layer counts
|
|
@@ -863,6 +865,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
863
865
|
visibleDistrictsMemo,
|
|
864
866
|
visibleBuildingsMemo,
|
|
865
867
|
abstractedPathsSet,
|
|
868
|
+
layerIndex,
|
|
866
869
|
]);
|
|
867
870
|
// Optimized hit testing
|
|
868
871
|
const performHitTest = (0, react_1.useCallback)((canvasX, canvasY) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ArchitectureMapHighlightLayers, type ArchitectureMapHighlightLayersProps, } from './components/ArchitectureMapHighlightLayers';
|
|
2
|
-
export { type LayerRenderStrategy, type LayerItem, type HighlightLayer, } from './render/client/drawLayeredBuildings';
|
|
2
|
+
export { type LayerRenderStrategy, type LayerItem, type HighlightLayer, LayerIndex, } from './render/client/drawLayeredBuildings';
|
|
3
3
|
export { type MapInteractionState, type MapDisplayOptions } from './types/react-types';
|
|
4
4
|
export { filterCityDataForSelectiveRender, filterCityDataForSubdirectory, filterCityDataForMultipleDirectories, } from './builder/cityDataUtils';
|
|
5
5
|
export { createFileColorHighlightLayers, getDefaultFileColorConfig, getFileColorMapping, } from './utils/fileColorHighlightLayers';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAG1C,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACR,UAAU,GACX,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAG7F,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useTheme = exports.ThemeProvider = exports.CityViewWithReactFlow = exports.useCodeCityData = exports.MultiVersionCityBuilder = exports.getFileColorMapping = exports.getDefaultFileColorConfig = exports.createFileColorHighlightLayers = exports.filterCityDataForMultipleDirectories = exports.filterCityDataForSubdirectory = exports.filterCityDataForSelectiveRender = exports.ArchitectureMapHighlightLayers = void 0;
|
|
3
|
+
exports.useTheme = exports.ThemeProvider = exports.CityViewWithReactFlow = exports.useCodeCityData = exports.MultiVersionCityBuilder = exports.getFileColorMapping = exports.getDefaultFileColorConfig = exports.createFileColorHighlightLayers = exports.filterCityDataForMultipleDirectories = exports.filterCityDataForSubdirectory = exports.filterCityDataForSelectiveRender = exports.LayerIndex = exports.ArchitectureMapHighlightLayers = void 0;
|
|
4
4
|
// Main component export
|
|
5
5
|
var ArchitectureMapHighlightLayers_1 = require("./components/ArchitectureMapHighlightLayers");
|
|
6
6
|
Object.defineProperty(exports, "ArchitectureMapHighlightLayers", { enumerable: true, get: function () { return ArchitectureMapHighlightLayers_1.ArchitectureMapHighlightLayers; } });
|
|
7
|
+
// Layer and rendering types
|
|
8
|
+
var drawLayeredBuildings_1 = require("./render/client/drawLayeredBuildings");
|
|
9
|
+
Object.defineProperty(exports, "LayerIndex", { enumerable: true, get: function () { return drawLayeredBuildings_1.LayerIndex; } });
|
|
7
10
|
// Utility functions
|
|
8
11
|
var cityDataUtils_1 = require("./builder/cityDataUtils");
|
|
9
12
|
Object.defineProperty(exports, "filterCityDataForSelectiveRender", { enumerable: true, get: function () { return cityDataUtils_1.filterCityDataForSelectiveRender; } });
|
|
@@ -32,6 +32,26 @@ export interface HighlightLayer {
|
|
|
32
32
|
items: LayerItem[];
|
|
33
33
|
dynamic?: boolean;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* LayerIndex provides O(1) path lookups instead of O(n) iteration.
|
|
37
|
+
* This dramatically improves performance with large numbers of layer items.
|
|
38
|
+
*/
|
|
39
|
+
export declare class LayerIndex {
|
|
40
|
+
private exactIndex;
|
|
41
|
+
private directoryPaths;
|
|
42
|
+
private sortedCache;
|
|
43
|
+
constructor(layers: HighlightLayer[]);
|
|
44
|
+
private buildIndex;
|
|
45
|
+
/**
|
|
46
|
+
* Get all layer items that apply to a given path.
|
|
47
|
+
* For 'exact' mode: only matches the exact path.
|
|
48
|
+
* For 'children' mode: matches exact path OR parent directories that contain this path.
|
|
49
|
+
*/
|
|
50
|
+
getItemsForPath(path: string, checkType?: 'exact' | 'children'): Array<{
|
|
51
|
+
layer: HighlightLayer;
|
|
52
|
+
item: LayerItem;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
35
55
|
export declare function drawGrid(ctx: CanvasRenderingContext2D, width: number, height: number, gridSize: number): void;
|
|
36
56
|
export declare function drawLayeredDistricts(ctx: CanvasRenderingContext2D, districts: CityDistrict[], worldToCanvas: (x: number, z: number) => {
|
|
37
57
|
x: number;
|
|
@@ -43,9 +63,11 @@ layers: HighlightLayer[], hoveredDistrict?: CityDistrict | null, fullSize?: bool
|
|
|
43
63
|
paddingLeft: number;
|
|
44
64
|
paddingRight: number;
|
|
45
65
|
}, abstractedPaths?: Set<string>, // Paths of directories that are abstracted (have covers)
|
|
46
|
-
showDirectoryLabels?: boolean, borderRadius?: number
|
|
66
|
+
showDirectoryLabels?: boolean, borderRadius?: number, // Border radius for districts (default: sharp corners)
|
|
67
|
+
layerIndex?: LayerIndex): void;
|
|
47
68
|
export declare function drawLayeredBuildings(ctx: CanvasRenderingContext2D, buildings: CityBuilding[], worldToCanvas: (x: number, z: number) => {
|
|
48
69
|
x: number;
|
|
49
70
|
y: number;
|
|
50
|
-
}, scale: number, layers: HighlightLayer[], hoveredBuilding?: CityBuilding | null, defaultBuildingColor?: string, showFileNames?: boolean, hoverBorderColor?: string, disableOpacityDimming?: boolean, showFileTypeIcons?: boolean, borderRadius?: number
|
|
71
|
+
}, scale: number, layers: HighlightLayer[], hoveredBuilding?: CityBuilding | null, defaultBuildingColor?: string, showFileNames?: boolean, hoverBorderColor?: string, disableOpacityDimming?: boolean, showFileTypeIcons?: boolean, borderRadius?: number, // Border radius for buildings (default: 0 - sharp corners)
|
|
72
|
+
layerIndex?: LayerIndex): void;
|
|
51
73
|
//# sourceMappingURL=drawLayeredBuildings.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawLayeredBuildings.d.ts","sourceRoot":"","sources":["../../../src/render/client/drawLayeredBuildings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAG7E,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,MAAM,GACN,MAAM,GACN,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,cAAc,CAAC,EAAE,mBAAmB,CAAC;IAErC,YAAY,CAAC,EAAE;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF,YAAY,CAAC,EAAE,CACb,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAC/D,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;CACX;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;IAEnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"drawLayeredBuildings.d.ts","sourceRoot":"","sources":["../../../src/render/client/drawLayeredBuildings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAG7E,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,MAAM,GACN,MAAM,GACN,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,cAAc,CAAC,EAAE,mBAAmB,CAAC;IAErC,YAAY,CAAC,EAAE;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF,YAAY,CAAC,EAAE,CACb,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAC/D,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;CACX;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;IAEnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsBD;;;GAGG;AACH,qBAAa,UAAU;IAErB,OAAO,CAAC,UAAU,CAA6E;IAE/F,OAAO,CAAC,cAAc,CAAuE;IAE7F,OAAO,CAAC,WAAW,CAA6E;gBAEpF,MAAM,EAAE,cAAc,EAAE;IAIpC,OAAO,CAAC,UAAU;IA0BlB;;;;OAIG;IACH,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,GAAG,UAAuB,GAC3C,KAAK,CAAC;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;CAuCrD;AAoDD,wBAAgB,QAAQ,CACtB,GAAG,EAAE,wBAAwB,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,QAqBjB;AAiVD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,KAAK,EAAE,MAAM,EAAE,wDAAwD;AACvE,MAAM,EAAE,cAAc,EAAE,EACxB,eAAe,CAAC,EAAE,YAAY,GAAG,IAAI,EACrC,QAAQ,CAAC,EAAE,OAAO,EAClB,qBAAqB,CAAC,EAAE,MAAM,EAC9B,YAAY,CAAC,EAAE;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,EACD,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,yDAAyD;AACxF,mBAAmB,GAAE,OAAc,EACnC,YAAY,GAAE,MAAU,EAAE,uDAAuD;AACjF,UAAU,CAAC,EAAE,UAAU,QAgQxB;AA4CD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,eAAe,CAAC,EAAE,YAAY,GAAG,IAAI,EACrC,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,OAAO,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,qBAAqB,CAAC,EAAE,OAAO,EAC/B,iBAAiB,CAAC,EAAE,OAAO,EAC3B,YAAY,GAAE,MAAU,EAAE,2DAA2D;AACrF,UAAU,CAAC,EAAE,UAAU,QA8IxB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LayerIndex = void 0;
|
|
3
4
|
exports.drawGrid = drawGrid;
|
|
4
5
|
exports.drawLayeredDistricts = drawLayeredDistricts;
|
|
5
6
|
exports.drawLayeredBuildings = drawLayeredBuildings;
|
|
@@ -20,6 +21,87 @@ function pathMatchesItem(path, item, checkType = 'children') {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* LayerIndex provides O(1) path lookups instead of O(n) iteration.
|
|
26
|
+
* This dramatically improves performance with large numbers of layer items.
|
|
27
|
+
*/
|
|
28
|
+
class LayerIndex {
|
|
29
|
+
constructor(layers) {
|
|
30
|
+
// Map from exact path to layer items (for file items)
|
|
31
|
+
this.exactIndex = new Map();
|
|
32
|
+
// Sorted list of directory paths for prefix matching
|
|
33
|
+
this.directoryPaths = [];
|
|
34
|
+
// Cache for sorted results by priority
|
|
35
|
+
this.sortedCache = new Map();
|
|
36
|
+
this.buildIndex(layers);
|
|
37
|
+
}
|
|
38
|
+
buildIndex(layers) {
|
|
39
|
+
for (const layer of layers) {
|
|
40
|
+
if (!layer.enabled)
|
|
41
|
+
continue;
|
|
42
|
+
for (const item of layer.items) {
|
|
43
|
+
const entry = { layer, item };
|
|
44
|
+
if (item.type === 'file') {
|
|
45
|
+
// File items: exact match only
|
|
46
|
+
const existing = this.exactIndex.get(item.path);
|
|
47
|
+
if (existing) {
|
|
48
|
+
existing.push(entry);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.exactIndex.set(item.path, [entry]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Directory items: need prefix matching
|
|
56
|
+
this.directoryPaths.push({ path: item.path, layer, item });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Sort directory paths by length (longest first) for correct matching
|
|
61
|
+
this.directoryPaths.sort((a, b) => b.path.length - a.path.length);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get all layer items that apply to a given path.
|
|
65
|
+
* For 'exact' mode: only matches the exact path.
|
|
66
|
+
* For 'children' mode: matches exact path OR parent directories that contain this path.
|
|
67
|
+
*/
|
|
68
|
+
getItemsForPath(path, checkType = 'children') {
|
|
69
|
+
// Check cache first
|
|
70
|
+
const cacheKey = `${path}:${checkType}`;
|
|
71
|
+
const cached = this.sortedCache.get(cacheKey);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
const matches = [];
|
|
75
|
+
// Check exact matches (file items)
|
|
76
|
+
const exactMatches = this.exactIndex.get(path);
|
|
77
|
+
if (exactMatches) {
|
|
78
|
+
matches.push(...exactMatches);
|
|
79
|
+
}
|
|
80
|
+
// Check directory matches
|
|
81
|
+
if (checkType === 'exact') {
|
|
82
|
+
// For exact mode, only match directories with exact path
|
|
83
|
+
for (const dir of this.directoryPaths) {
|
|
84
|
+
if (dir.path === path) {
|
|
85
|
+
matches.push({ layer: dir.layer, item: dir.item });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// For children mode, check if path is inside any directory
|
|
91
|
+
for (const dir of this.directoryPaths) {
|
|
92
|
+
if (path === dir.path || path.startsWith(dir.path + '/')) {
|
|
93
|
+
matches.push({ layer: dir.layer, item: dir.item });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Sort by priority (highest first)
|
|
98
|
+
const sorted = matches.sort((a, b) => b.layer.priority - a.layer.priority);
|
|
99
|
+
// Cache the result
|
|
100
|
+
this.sortedCache.set(cacheKey, sorted);
|
|
101
|
+
return sorted;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.LayerIndex = LayerIndex;
|
|
23
105
|
// Helper function to draw rounded rectangles
|
|
24
106
|
function drawRoundedRect(ctx, x, y, width, height, radius, fill, stroke) {
|
|
25
107
|
ctx.beginPath();
|
|
@@ -339,12 +421,15 @@ function applyLayerRendering(ctx, bounds, layer, item, scale, borderRadius = 0)
|
|
|
339
421
|
// Draw districts with layer support
|
|
340
422
|
function drawLayeredDistricts(ctx, districts, worldToCanvas, scale, // This includes the zoom scale for text proportionality
|
|
341
423
|
layers, hoveredDistrict, fullSize, defaultDirectoryColor, layoutConfig, abstractedPaths, // Paths of directories that are abstracted (have covers)
|
|
342
|
-
showDirectoryLabels = true, borderRadius = 0)
|
|
424
|
+
showDirectoryLabels = true, borderRadius = 0, // Border radius for districts (default: sharp corners)
|
|
425
|
+
layerIndex) {
|
|
426
|
+
// Build index once for all districts (O(n) instead of O(n²))
|
|
427
|
+
const index = layerIndex || new LayerIndex(layers);
|
|
343
428
|
districts.forEach(district => {
|
|
344
429
|
const districtPath = district.path || '';
|
|
345
430
|
const isRoot = !districtPath || districtPath === '';
|
|
346
431
|
// Check if this root district has layer matches (like covers) - if so, render it
|
|
347
|
-
const rootLayerMatches =
|
|
432
|
+
const rootLayerMatches = index.getItemsForPath(districtPath, 'exact');
|
|
348
433
|
const hasLayerRendering = rootLayerMatches.length > 0;
|
|
349
434
|
// Skip root districts unless they have layer rendering (covers, highlights, etc.)
|
|
350
435
|
if (isRoot && !hasLayerRendering)
|
|
@@ -371,7 +456,7 @@ showDirectoryLabels = true, borderRadius = 0) {
|
|
|
371
456
|
let opacity = 0.3;
|
|
372
457
|
let borderOpacity = 0.6;
|
|
373
458
|
// Check if district has layer highlighting - use exact matching for districts
|
|
374
|
-
const layerMatches =
|
|
459
|
+
const layerMatches = index.getItemsForPath(districtPath, 'exact');
|
|
375
460
|
const hasLayerHighlight = layerMatches.length > 0;
|
|
376
461
|
if (hasLayerHighlight) {
|
|
377
462
|
opacity = 0.5;
|
|
@@ -551,7 +636,10 @@ function isReactFile(fileExtension) {
|
|
|
551
636
|
return ext === '.jsx' || ext === '.tsx';
|
|
552
637
|
}
|
|
553
638
|
// Draw buildings with layer support
|
|
554
|
-
function drawLayeredBuildings(ctx, buildings, worldToCanvas, scale, layers, hoveredBuilding, defaultBuildingColor, showFileNames, hoverBorderColor, disableOpacityDimming, showFileTypeIcons, borderRadius = 0)
|
|
639
|
+
function drawLayeredBuildings(ctx, buildings, worldToCanvas, scale, layers, hoveredBuilding, defaultBuildingColor, showFileNames, hoverBorderColor, disableOpacityDimming, showFileTypeIcons, borderRadius = 0, // Border radius for buildings (default: 0 - sharp corners)
|
|
640
|
+
layerIndex) {
|
|
641
|
+
// Build index once for all buildings (O(n) instead of O(n²))
|
|
642
|
+
const index = layerIndex || new LayerIndex(layers);
|
|
555
643
|
buildings.forEach(building => {
|
|
556
644
|
const pos = worldToCanvas(building.position.x, building.position.z);
|
|
557
645
|
// Calculate building dimensions
|
|
@@ -572,7 +660,7 @@ function drawLayeredBuildings(ctx, buildings, worldToCanvas, scale, layers, hove
|
|
|
572
660
|
height: height,
|
|
573
661
|
};
|
|
574
662
|
// Get layer matches for this building - only check file items, not parent directories
|
|
575
|
-
const layerMatches =
|
|
663
|
+
const layerMatches = index.getItemsForPath(building.path).filter(match => match.item.type === 'file'); // Only apply file-specific highlights to buildings
|
|
576
664
|
const hasLayerHighlight = layerMatches.length > 0;
|
|
577
665
|
const isHovered = hoveredBuilding === building;
|
|
578
666
|
// Building color
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
drawGrid,
|
|
13
13
|
HighlightLayer,
|
|
14
14
|
LayerItem,
|
|
15
|
+
LayerIndex,
|
|
15
16
|
} from '../render/client/drawLayeredBuildings';
|
|
16
17
|
import {
|
|
17
18
|
CityData,
|
|
@@ -1007,6 +1008,9 @@ function ArchitectureMapHighlightLayersInner({
|
|
|
1007
1008
|
return layers;
|
|
1008
1009
|
}, [stableLayers, dynamicLayers, abstractionLayer]);
|
|
1009
1010
|
|
|
1011
|
+
// Memoize layer index for O(1) path lookups - only rebuilds when layers change
|
|
1012
|
+
const layerIndex = useMemo(() => new LayerIndex(allLayers), [allLayers]);
|
|
1013
|
+
|
|
1010
1014
|
// Memoize abstracted paths lookup - only recalculates when abstraction layer changes
|
|
1011
1015
|
const { abstractedPathsSet, abstractedPathLookup } = useMemo(() => {
|
|
1012
1016
|
const pathsSet = new Set<string>();
|
|
@@ -1189,6 +1193,7 @@ function ArchitectureMapHighlightLayersInner({
|
|
|
1189
1193
|
abstractedPathsSet, // Pass abstracted paths to skip labels
|
|
1190
1194
|
showDirectoryLabels,
|
|
1191
1195
|
districtBorderRadius,
|
|
1196
|
+
layerIndex, // Pre-built index for O(1) lookups
|
|
1192
1197
|
);
|
|
1193
1198
|
|
|
1194
1199
|
// Draw buildings with layer support
|
|
@@ -1205,6 +1210,7 @@ function ArchitectureMapHighlightLayersInner({
|
|
|
1205
1210
|
disableOpacityDimming,
|
|
1206
1211
|
showFileTypeIcons,
|
|
1207
1212
|
buildingBorderRadius,
|
|
1213
|
+
layerIndex, // Pre-built index for O(1) lookups
|
|
1208
1214
|
);
|
|
1209
1215
|
|
|
1210
1216
|
// Performance monitoring end available for debugging
|
|
@@ -1239,6 +1245,7 @@ function ArchitectureMapHighlightLayersInner({
|
|
|
1239
1245
|
visibleDistrictsMemo,
|
|
1240
1246
|
visibleBuildingsMemo,
|
|
1241
1247
|
abstractedPathsSet,
|
|
1248
|
+
layerIndex,
|
|
1242
1249
|
]);
|
|
1243
1250
|
|
|
1244
1251
|
// Optimized hit testing
|
package/src/index.ts
CHANGED
|
@@ -66,6 +66,97 @@ function pathMatchesItem(
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* LayerIndex provides O(1) path lookups instead of O(n) iteration.
|
|
71
|
+
* This dramatically improves performance with large numbers of layer items.
|
|
72
|
+
*/
|
|
73
|
+
export class LayerIndex {
|
|
74
|
+
// Map from exact path to layer items (for file items)
|
|
75
|
+
private exactIndex: Map<string, Array<{ layer: HighlightLayer; item: LayerItem }>> = new Map();
|
|
76
|
+
// Sorted list of directory paths for prefix matching
|
|
77
|
+
private directoryPaths: Array<{ path: string; layer: HighlightLayer; item: LayerItem }> = [];
|
|
78
|
+
// Cache for sorted results by priority
|
|
79
|
+
private sortedCache: Map<string, Array<{ layer: HighlightLayer; item: LayerItem }>> = new Map();
|
|
80
|
+
|
|
81
|
+
constructor(layers: HighlightLayer[]) {
|
|
82
|
+
this.buildIndex(layers);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private buildIndex(layers: HighlightLayer[]) {
|
|
86
|
+
for (const layer of layers) {
|
|
87
|
+
if (!layer.enabled) continue;
|
|
88
|
+
|
|
89
|
+
for (const item of layer.items) {
|
|
90
|
+
const entry = { layer, item };
|
|
91
|
+
|
|
92
|
+
if (item.type === 'file') {
|
|
93
|
+
// File items: exact match only
|
|
94
|
+
const existing = this.exactIndex.get(item.path);
|
|
95
|
+
if (existing) {
|
|
96
|
+
existing.push(entry);
|
|
97
|
+
} else {
|
|
98
|
+
this.exactIndex.set(item.path, [entry]);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Directory items: need prefix matching
|
|
102
|
+
this.directoryPaths.push({ path: item.path, layer, item });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Sort directory paths by length (longest first) for correct matching
|
|
108
|
+
this.directoryPaths.sort((a, b) => b.path.length - a.path.length);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get all layer items that apply to a given path.
|
|
113
|
+
* For 'exact' mode: only matches the exact path.
|
|
114
|
+
* For 'children' mode: matches exact path OR parent directories that contain this path.
|
|
115
|
+
*/
|
|
116
|
+
getItemsForPath(
|
|
117
|
+
path: string,
|
|
118
|
+
checkType: 'exact' | 'children' = 'children',
|
|
119
|
+
): Array<{ layer: HighlightLayer; item: LayerItem }> {
|
|
120
|
+
// Check cache first
|
|
121
|
+
const cacheKey = `${path}:${checkType}`;
|
|
122
|
+
const cached = this.sortedCache.get(cacheKey);
|
|
123
|
+
if (cached) return cached;
|
|
124
|
+
|
|
125
|
+
const matches: Array<{ layer: HighlightLayer; item: LayerItem }> = [];
|
|
126
|
+
|
|
127
|
+
// Check exact matches (file items)
|
|
128
|
+
const exactMatches = this.exactIndex.get(path);
|
|
129
|
+
if (exactMatches) {
|
|
130
|
+
matches.push(...exactMatches);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check directory matches
|
|
134
|
+
if (checkType === 'exact') {
|
|
135
|
+
// For exact mode, only match directories with exact path
|
|
136
|
+
for (const dir of this.directoryPaths) {
|
|
137
|
+
if (dir.path === path) {
|
|
138
|
+
matches.push({ layer: dir.layer, item: dir.item });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
// For children mode, check if path is inside any directory
|
|
143
|
+
for (const dir of this.directoryPaths) {
|
|
144
|
+
if (path === dir.path || path.startsWith(dir.path + '/')) {
|
|
145
|
+
matches.push({ layer: dir.layer, item: dir.item });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Sort by priority (highest first)
|
|
151
|
+
const sorted = matches.sort((a, b) => b.layer.priority - a.layer.priority);
|
|
152
|
+
|
|
153
|
+
// Cache the result
|
|
154
|
+
this.sortedCache.set(cacheKey, sorted);
|
|
155
|
+
|
|
156
|
+
return sorted;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
69
160
|
// Helper function to draw rounded rectangles
|
|
70
161
|
function drawRoundedRect(
|
|
71
162
|
ctx: CanvasRenderingContext2D,
|
|
@@ -496,13 +587,17 @@ export function drawLayeredDistricts(
|
|
|
496
587
|
abstractedPaths?: Set<string>, // Paths of directories that are abstracted (have covers)
|
|
497
588
|
showDirectoryLabels: boolean = true,
|
|
498
589
|
borderRadius: number = 0, // Border radius for districts (default: sharp corners)
|
|
590
|
+
layerIndex?: LayerIndex, // Optional pre-built index for performance
|
|
499
591
|
) {
|
|
592
|
+
// Build index once for all districts (O(n) instead of O(n²))
|
|
593
|
+
const index = layerIndex || new LayerIndex(layers);
|
|
594
|
+
|
|
500
595
|
districts.forEach(district => {
|
|
501
596
|
const districtPath = district.path || '';
|
|
502
597
|
const isRoot = !districtPath || districtPath === '';
|
|
503
598
|
|
|
504
599
|
// Check if this root district has layer matches (like covers) - if so, render it
|
|
505
|
-
const rootLayerMatches =
|
|
600
|
+
const rootLayerMatches = index.getItemsForPath(districtPath, 'exact');
|
|
506
601
|
const hasLayerRendering = rootLayerMatches.length > 0;
|
|
507
602
|
|
|
508
603
|
// Skip root districts unless they have layer rendering (covers, highlights, etc.)
|
|
@@ -536,7 +631,7 @@ export function drawLayeredDistricts(
|
|
|
536
631
|
let borderOpacity = 0.6;
|
|
537
632
|
|
|
538
633
|
// Check if district has layer highlighting - use exact matching for districts
|
|
539
|
-
const layerMatches =
|
|
634
|
+
const layerMatches = index.getItemsForPath(districtPath, 'exact');
|
|
540
635
|
const hasLayerHighlight = layerMatches.length > 0;
|
|
541
636
|
|
|
542
637
|
if (hasLayerHighlight) {
|
|
@@ -805,7 +900,11 @@ export function drawLayeredBuildings(
|
|
|
805
900
|
disableOpacityDimming?: boolean,
|
|
806
901
|
showFileTypeIcons?: boolean,
|
|
807
902
|
borderRadius: number = 0, // Border radius for buildings (default: 0 - sharp corners)
|
|
903
|
+
layerIndex?: LayerIndex, // Optional pre-built index for performance
|
|
808
904
|
) {
|
|
905
|
+
// Build index once for all buildings (O(n) instead of O(n²))
|
|
906
|
+
const index = layerIndex || new LayerIndex(layers);
|
|
907
|
+
|
|
809
908
|
buildings.forEach(building => {
|
|
810
909
|
const pos = worldToCanvas(building.position.x, building.position.z);
|
|
811
910
|
|
|
@@ -832,7 +931,7 @@ export function drawLayeredBuildings(
|
|
|
832
931
|
};
|
|
833
932
|
|
|
834
933
|
// Get layer matches for this building - only check file items, not parent directories
|
|
835
|
-
const layerMatches =
|
|
934
|
+
const layerMatches = index.getItemsForPath(building.path).filter(
|
|
836
935
|
match => match.item.type === 'file',
|
|
837
936
|
); // Only apply file-specific highlights to buildings
|
|
838
937
|
const hasLayerHighlight = layerMatches.length > 0;
|