@thatopen/components 3.2.6 → 3.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 +547 -29
- package/dist/index.d.ts +205 -12
- package/dist/index.min.cjs +3 -3
- package/dist/index.min.mjs +3 -3
- package/dist/index.mjs +548 -30
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -3097,7 +3097,7 @@ const _ConfigManager = class _ConfigManager extends Component {
|
|
|
3097
3097
|
/**
|
|
3098
3098
|
* The list of all configurations of this app.
|
|
3099
3099
|
*/
|
|
3100
|
-
__publicField(this, "list", new DataMap());
|
|
3100
|
+
__publicField(this, "list", new FRAGS.DataMap());
|
|
3101
3101
|
/** {@link Component.enabled} */
|
|
3102
3102
|
__publicField(this, "enabled", true);
|
|
3103
3103
|
components.add(_ConfigManager.uuid, this);
|
|
@@ -3916,6 +3916,7 @@ __publicField(_BoundingBoxer, "uuid", "d1444724-dba6-4cdd-a0c7-68ee1450d166");
|
|
|
3916
3916
|
let BoundingBoxer = _BoundingBoxer;
|
|
3917
3917
|
class FinderQuery {
|
|
3918
3918
|
constructor(components, queries) {
|
|
3919
|
+
__publicField(this, "name", "Query");
|
|
3919
3920
|
__publicField(this, "customData", {});
|
|
3920
3921
|
__publicField(this, "_components");
|
|
3921
3922
|
__publicField(this, "_queries", []);
|
|
@@ -4040,9 +4041,10 @@ class FinderQuery {
|
|
|
4040
4041
|
*/
|
|
4041
4042
|
toJSON() {
|
|
4042
4043
|
const manager = this._components.get(ItemsFinder);
|
|
4043
|
-
const
|
|
4044
|
+
const guid = manager.list.getKey(this) ?? UUID.create();
|
|
4044
4045
|
const result = {
|
|
4045
|
-
|
|
4046
|
+
guid,
|
|
4047
|
+
name: this.name,
|
|
4046
4048
|
customData: this.customData,
|
|
4047
4049
|
queries: this.queries.map(this.serializeQueryParameters),
|
|
4048
4050
|
aggregation: this.aggregation,
|
|
@@ -4075,6 +4077,7 @@ class FinderQuery {
|
|
|
4075
4077
|
* @returns A `FinderQuery` instance.
|
|
4076
4078
|
*/
|
|
4077
4079
|
fromJSON(data) {
|
|
4080
|
+
this.name = data.name;
|
|
4078
4081
|
this.customData = data.customData;
|
|
4079
4082
|
this.aggregation = data.aggregation;
|
|
4080
4083
|
this.cache = data.cache;
|
|
@@ -4106,7 +4109,17 @@ const _ItemsFinder = class _ItemsFinder extends Component {
|
|
|
4106
4109
|
* @returns A map of model IDs to sets of item IDs that match the query.
|
|
4107
4110
|
*/
|
|
4108
4111
|
async getItems(queries, config) {
|
|
4109
|
-
|
|
4112
|
+
let modelsToCheck;
|
|
4113
|
+
if (config) {
|
|
4114
|
+
const { modelIds, items } = config;
|
|
4115
|
+
if (items) {
|
|
4116
|
+
const itemsModels = Object.keys(items);
|
|
4117
|
+
if (itemsModels.length > 0)
|
|
4118
|
+
modelsToCheck = itemsModels.map((entry) => new RegExp(`^${entry}$`));
|
|
4119
|
+
} else if (modelIds) {
|
|
4120
|
+
modelsToCheck = modelIds;
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4110
4123
|
const aggregation = (config == null ? void 0 : config.aggregation) ?? "exclusive";
|
|
4111
4124
|
const fragments = this.components.get(FragmentsManager);
|
|
4112
4125
|
const results = await Promise.all(
|
|
@@ -4114,9 +4127,11 @@ const _ItemsFinder = class _ItemsFinder extends Component {
|
|
|
4114
4127
|
const result = {};
|
|
4115
4128
|
await Promise.all(
|
|
4116
4129
|
Array.from(fragments.list).map(async ([id, model]) => {
|
|
4117
|
-
|
|
4130
|
+
var _a2;
|
|
4131
|
+
if (modelsToCheck && !modelsToCheck.some((regex) => regex.test(id)))
|
|
4118
4132
|
return;
|
|
4119
|
-
const
|
|
4133
|
+
const localIds = (_a2 = config == null ? void 0 : config.items) == null ? void 0 : _a2[id];
|
|
4134
|
+
const items = await model.getItemsByQuery(query, { localIds: localIds ? [...localIds] : void 0 });
|
|
4120
4135
|
result[id] = new Set(items);
|
|
4121
4136
|
})
|
|
4122
4137
|
);
|
|
@@ -4175,9 +4190,8 @@ const _ItemsFinder = class _ItemsFinder extends Component {
|
|
|
4175
4190
|
if (!data)
|
|
4176
4191
|
return instances;
|
|
4177
4192
|
for (const value of data) {
|
|
4178
|
-
const
|
|
4179
|
-
|
|
4180
|
-
finderQuery.fromJSON({ customData, queries, aggregation, cache });
|
|
4193
|
+
const finderQuery = this.create(value.guid, []);
|
|
4194
|
+
finderQuery.fromJSON(value);
|
|
4181
4195
|
instances.push(finderQuery);
|
|
4182
4196
|
}
|
|
4183
4197
|
return instances;
|
|
@@ -4566,6 +4580,448 @@ const _Classifier = class _Classifier extends Component {
|
|
|
4566
4580
|
*/
|
|
4567
4581
|
__publicField(_Classifier, "uuid", "e25a7f3c-46c4-4a14-9d3d-5115f24ebeb7");
|
|
4568
4582
|
let Classifier = _Classifier;
|
|
4583
|
+
class FastModelPicker {
|
|
4584
|
+
constructor(components, world) {
|
|
4585
|
+
/** {@link Component.enabled} */
|
|
4586
|
+
__publicField(this, "enabled", true);
|
|
4587
|
+
/** The components instance to which this FastModelPicker belongs. */
|
|
4588
|
+
__publicField(this, "components");
|
|
4589
|
+
/** {@link Disposable.onDisposed} */
|
|
4590
|
+
__publicField(this, "onDisposed", new Event());
|
|
4591
|
+
/** The position of the mouse in the screen. */
|
|
4592
|
+
__publicField(this, "mouse");
|
|
4593
|
+
/**
|
|
4594
|
+
* A reference to the world instance to which this FastModelPicker belongs.
|
|
4595
|
+
* This is used to access the camera and scene.
|
|
4596
|
+
*/
|
|
4597
|
+
__publicField(this, "world");
|
|
4598
|
+
/**
|
|
4599
|
+
* Whether debug mode is enabled. When enabled, shows the color-coded canvas.
|
|
4600
|
+
*/
|
|
4601
|
+
__publicField(this, "debugMode", false);
|
|
4602
|
+
/**
|
|
4603
|
+
* Map from color (as RGB number) to model ID.
|
|
4604
|
+
* Color is encoded as: (r << 16) | (g << 8) | b
|
|
4605
|
+
*/
|
|
4606
|
+
__publicField(this, "colorToModelId", /* @__PURE__ */ new Map());
|
|
4607
|
+
/**
|
|
4608
|
+
* Map from model ID to color.
|
|
4609
|
+
*/
|
|
4610
|
+
__publicField(this, "modelIdToColor", /* @__PURE__ */ new Map());
|
|
4611
|
+
/**
|
|
4612
|
+
* Render target for the color-coded scene.
|
|
4613
|
+
*/
|
|
4614
|
+
__publicField(this, "renderTarget");
|
|
4615
|
+
/**
|
|
4616
|
+
* Size of the render target (stored separately since getSize doesn't exist).
|
|
4617
|
+
*/
|
|
4618
|
+
__publicField(this, "renderTargetSize", new THREE__namespace.Vector2());
|
|
4619
|
+
/**
|
|
4620
|
+
* Debug canvas element (shown when debugMode is true).
|
|
4621
|
+
*/
|
|
4622
|
+
__publicField(this, "debugCanvas");
|
|
4623
|
+
/**
|
|
4624
|
+
* Debug container element.
|
|
4625
|
+
*/
|
|
4626
|
+
__publicField(this, "debugContainer");
|
|
4627
|
+
/**
|
|
4628
|
+
* Material used for color-coding models.
|
|
4629
|
+
*/
|
|
4630
|
+
__publicField(this, "colorMaterials", /* @__PURE__ */ new Map());
|
|
4631
|
+
/**
|
|
4632
|
+
* Original materials cache (to restore after picking).
|
|
4633
|
+
*/
|
|
4634
|
+
__publicField(this, "originalMaterials", /* @__PURE__ */ new Map());
|
|
4635
|
+
__publicField(this, "originalLodColors", /* @__PURE__ */ new Map());
|
|
4636
|
+
/**
|
|
4637
|
+
* Whether colors need to be reassigned (when models change).
|
|
4638
|
+
*/
|
|
4639
|
+
__publicField(this, "colorsNeedUpdate", true);
|
|
4640
|
+
const renderer = world.renderer;
|
|
4641
|
+
if (!renderer) {
|
|
4642
|
+
throw new Error("A renderer is needed for the FastModelPicker to work!");
|
|
4643
|
+
}
|
|
4644
|
+
this.world = world;
|
|
4645
|
+
this.mouse = new Mouse(renderer.three.domElement);
|
|
4646
|
+
this.components = components;
|
|
4647
|
+
this.setupRenderTarget();
|
|
4648
|
+
this.setupFragmentListeners();
|
|
4649
|
+
}
|
|
4650
|
+
/**
|
|
4651
|
+
* Sets up listeners for fragment model changes.
|
|
4652
|
+
*/
|
|
4653
|
+
setupFragmentListeners() {
|
|
4654
|
+
const fragments = this.components.get(FragmentsManager);
|
|
4655
|
+
fragments.list.onItemSet.add(() => {
|
|
4656
|
+
this.colorsNeedUpdate = true;
|
|
4657
|
+
});
|
|
4658
|
+
fragments.list.onItemDeleted.add(() => {
|
|
4659
|
+
this.colorsNeedUpdate = true;
|
|
4660
|
+
});
|
|
4661
|
+
}
|
|
4662
|
+
/**
|
|
4663
|
+
* Sets up the render target for color-coded picking.
|
|
4664
|
+
*/
|
|
4665
|
+
setupRenderTarget() {
|
|
4666
|
+
const renderer = this.world.renderer.three;
|
|
4667
|
+
const size = renderer.getSize(new THREE__namespace.Vector2());
|
|
4668
|
+
this.renderTargetSize.copy(size);
|
|
4669
|
+
this.renderTarget = new THREE__namespace.WebGLRenderTarget(size.x, size.y);
|
|
4670
|
+
this.renderTarget.texture.format = THREE__namespace.RGBAFormat;
|
|
4671
|
+
this.renderTarget.texture.type = THREE__namespace.UnsignedByteType;
|
|
4672
|
+
if (this.debugMode) {
|
|
4673
|
+
this.setupDebugCanvas();
|
|
4674
|
+
}
|
|
4675
|
+
this.world.renderer.onResize.add((size2) => {
|
|
4676
|
+
this.renderTargetSize.copy(size2);
|
|
4677
|
+
this.renderTarget.setSize(size2.x, size2.y);
|
|
4678
|
+
if (this.debugCanvas) {
|
|
4679
|
+
this.debugCanvas.width = size2.x;
|
|
4680
|
+
this.debugCanvas.height = size2.y;
|
|
4681
|
+
}
|
|
4682
|
+
});
|
|
4683
|
+
}
|
|
4684
|
+
/**
|
|
4685
|
+
* Sets up the debug canvas for visualization.
|
|
4686
|
+
*/
|
|
4687
|
+
setupDebugCanvas() {
|
|
4688
|
+
if (this.debugCanvas)
|
|
4689
|
+
return;
|
|
4690
|
+
const size = this.world.renderer.three.getSize(new THREE__namespace.Vector2());
|
|
4691
|
+
this.debugContainer = document.createElement("div");
|
|
4692
|
+
this.debugContainer.style.position = "fixed";
|
|
4693
|
+
this.debugContainer.style.top = "10px";
|
|
4694
|
+
this.debugContainer.style.right = "10px";
|
|
4695
|
+
this.debugContainer.style.width = "300px";
|
|
4696
|
+
this.debugContainer.style.height = "300px";
|
|
4697
|
+
this.debugContainer.style.border = "2px solid #fff";
|
|
4698
|
+
this.debugContainer.style.backgroundColor = "#000";
|
|
4699
|
+
this.debugContainer.style.zIndex = "10000";
|
|
4700
|
+
this.debugContainer.style.pointerEvents = "none";
|
|
4701
|
+
this.debugCanvas = document.createElement("canvas");
|
|
4702
|
+
this.debugCanvas.width = size.x;
|
|
4703
|
+
this.debugCanvas.height = size.y;
|
|
4704
|
+
this.debugCanvas.style.width = "100%";
|
|
4705
|
+
this.debugCanvas.style.height = "100%";
|
|
4706
|
+
this.debugCanvas.style.imageRendering = "pixelated";
|
|
4707
|
+
this.debugContainer.appendChild(this.debugCanvas);
|
|
4708
|
+
document.body.appendChild(this.debugContainer);
|
|
4709
|
+
}
|
|
4710
|
+
/**
|
|
4711
|
+
* Generates a deterministic color for a model based on its ID.
|
|
4712
|
+
* This ensures the same model always gets the same color.
|
|
4713
|
+
*/
|
|
4714
|
+
generateColorForModel(modelId) {
|
|
4715
|
+
let hash = 0;
|
|
4716
|
+
for (let i = 0; i < modelId.length; i++) {
|
|
4717
|
+
hash = (hash << 5) - hash + modelId.charCodeAt(i);
|
|
4718
|
+
hash &= hash;
|
|
4719
|
+
}
|
|
4720
|
+
let colorId = Math.abs(hash) % 16777215;
|
|
4721
|
+
if (colorId === 0) {
|
|
4722
|
+
colorId = 1;
|
|
4723
|
+
}
|
|
4724
|
+
const r = colorId >> 16 & 255 || 1;
|
|
4725
|
+
const g = colorId >> 8 & 255 || 1;
|
|
4726
|
+
const b = colorId & 255 || 1;
|
|
4727
|
+
return new THREE__namespace.Color(r / 255, g / 255, b / 255);
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* Converts a color to a numeric ID.
|
|
4731
|
+
*/
|
|
4732
|
+
colorToId(color) {
|
|
4733
|
+
const r = Math.round(color.r * 255);
|
|
4734
|
+
const g = Math.round(color.g * 255);
|
|
4735
|
+
const b = Math.round(color.b * 255);
|
|
4736
|
+
return r << 16 | g << 8 | b;
|
|
4737
|
+
}
|
|
4738
|
+
/**
|
|
4739
|
+
* Assigns unique colors to all fragment models.
|
|
4740
|
+
* Colors are deterministic based on model ID, so the same model always gets the same color.
|
|
4741
|
+
*/
|
|
4742
|
+
assignColors() {
|
|
4743
|
+
const fragments = this.components.get(FragmentsManager);
|
|
4744
|
+
if (!fragments.initialized) {
|
|
4745
|
+
return;
|
|
4746
|
+
}
|
|
4747
|
+
if (!this.colorsNeedUpdate) {
|
|
4748
|
+
const currentModelIds = new Set(fragments.list.keys());
|
|
4749
|
+
const cachedModelIds = new Set(this.modelIdToColor.keys());
|
|
4750
|
+
if (currentModelIds.size !== cachedModelIds.size || [...currentModelIds].some((id) => !cachedModelIds.has(id))) {
|
|
4751
|
+
this.colorsNeedUpdate = true;
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
if (!this.colorsNeedUpdate) {
|
|
4755
|
+
return;
|
|
4756
|
+
}
|
|
4757
|
+
this.colorToModelId.clear();
|
|
4758
|
+
this.modelIdToColor.clear();
|
|
4759
|
+
for (const material of this.colorMaterials.values()) {
|
|
4760
|
+
material.dispose();
|
|
4761
|
+
}
|
|
4762
|
+
this.colorMaterials.clear();
|
|
4763
|
+
for (const [modelId] of fragments.list) {
|
|
4764
|
+
const color = this.generateColorForModel(modelId);
|
|
4765
|
+
const colorId = this.colorToId(color);
|
|
4766
|
+
this.colorToModelId.set(colorId, modelId);
|
|
4767
|
+
this.modelIdToColor.set(modelId, color);
|
|
4768
|
+
const material = new THREE__namespace.MeshBasicMaterial({
|
|
4769
|
+
color,
|
|
4770
|
+
depthTest: true,
|
|
4771
|
+
depthWrite: true
|
|
4772
|
+
});
|
|
4773
|
+
this.colorMaterials.set(modelId, material);
|
|
4774
|
+
}
|
|
4775
|
+
this.colorsNeedUpdate = false;
|
|
4776
|
+
}
|
|
4777
|
+
/**
|
|
4778
|
+
* Applies color materials to fragment models.
|
|
4779
|
+
*/
|
|
4780
|
+
applyColorMaterials() {
|
|
4781
|
+
const fragments = this.components.get(FragmentsManager);
|
|
4782
|
+
if (!fragments.initialized) {
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4785
|
+
for (const [modelId, model] of fragments.list) {
|
|
4786
|
+
const modelMaterial = this.colorMaterials.get(modelId);
|
|
4787
|
+
if (!modelMaterial)
|
|
4788
|
+
continue;
|
|
4789
|
+
model.object.traverse((child) => {
|
|
4790
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
4791
|
+
if ("isLODGeometry" in child.geometry) {
|
|
4792
|
+
const originalLodMaterial = child.material[0];
|
|
4793
|
+
const lodColor = originalLodMaterial.uniforms.lodColor;
|
|
4794
|
+
if (!this.originalLodColors.has(lodColor)) {
|
|
4795
|
+
this.originalLodColors.set(lodColor, lodColor.value);
|
|
4796
|
+
}
|
|
4797
|
+
lodColor.value = modelMaterial.color;
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4800
|
+
if (!this.originalMaterials.has(child)) {
|
|
4801
|
+
this.originalMaterials.set(child, child.material);
|
|
4802
|
+
}
|
|
4803
|
+
child.material = modelMaterial;
|
|
4804
|
+
}
|
|
4805
|
+
});
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
/**
|
|
4809
|
+
* Restores original materials to fragment models.
|
|
4810
|
+
*/
|
|
4811
|
+
restoreOriginalMaterials() {
|
|
4812
|
+
for (const [object, material] of this.originalMaterials) {
|
|
4813
|
+
object.material = material;
|
|
4814
|
+
}
|
|
4815
|
+
for (const [lodColor, color] of this.originalLodColors) {
|
|
4816
|
+
lodColor.value = color;
|
|
4817
|
+
}
|
|
4818
|
+
this.originalMaterials.clear();
|
|
4819
|
+
}
|
|
4820
|
+
/**
|
|
4821
|
+
* Renders the scene with color-coded models.
|
|
4822
|
+
*/
|
|
4823
|
+
renderColorCoded() {
|
|
4824
|
+
if (!this.renderTarget || !this.world.renderer) {
|
|
4825
|
+
throw new Error("Render target not initialized!");
|
|
4826
|
+
}
|
|
4827
|
+
const renderer = this.world.renderer.three;
|
|
4828
|
+
const scene = this.world.scene.three;
|
|
4829
|
+
const camera = this.world.camera.three;
|
|
4830
|
+
const currentRenderTarget = renderer.getRenderTarget();
|
|
4831
|
+
const currentAutoClear = renderer.autoClear;
|
|
4832
|
+
const currentClearColor = new THREE__namespace.Color();
|
|
4833
|
+
const currentClearAlpha = renderer.getClearAlpha();
|
|
4834
|
+
renderer.getClearColor(currentClearColor);
|
|
4835
|
+
this.applyColorMaterials();
|
|
4836
|
+
renderer.setRenderTarget(this.renderTarget);
|
|
4837
|
+
renderer.autoClear = true;
|
|
4838
|
+
renderer.setClearColor(0, 1);
|
|
4839
|
+
renderer.clear(true, true, false);
|
|
4840
|
+
renderer.render(scene, camera);
|
|
4841
|
+
renderer.setRenderTarget(currentRenderTarget);
|
|
4842
|
+
renderer.autoClear = currentAutoClear;
|
|
4843
|
+
renderer.setClearColor(currentClearColor, currentClearAlpha);
|
|
4844
|
+
this.restoreOriginalMaterials();
|
|
4845
|
+
if (this.debugMode && this.debugCanvas) {
|
|
4846
|
+
this.updateDebugCanvas();
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
/**
|
|
4850
|
+
* Updates the debug canvas with the color-coded render.
|
|
4851
|
+
*/
|
|
4852
|
+
updateDebugCanvas() {
|
|
4853
|
+
if (!this.debugCanvas || !this.renderTarget || !this.world.renderer)
|
|
4854
|
+
return;
|
|
4855
|
+
const renderer = this.world.renderer.three;
|
|
4856
|
+
const size = this.renderTargetSize;
|
|
4857
|
+
const pixels = new Uint8Array(size.x * size.y * 4);
|
|
4858
|
+
renderer.readRenderTargetPixels(
|
|
4859
|
+
this.renderTarget,
|
|
4860
|
+
0,
|
|
4861
|
+
0,
|
|
4862
|
+
size.x,
|
|
4863
|
+
size.y,
|
|
4864
|
+
pixels
|
|
4865
|
+
);
|
|
4866
|
+
const ctx = this.debugCanvas.getContext("2d");
|
|
4867
|
+
if (!ctx)
|
|
4868
|
+
return;
|
|
4869
|
+
const imageData = ctx.createImageData(size.x, size.y);
|
|
4870
|
+
const rowSize = size.x * 4;
|
|
4871
|
+
for (let y = 0; y < size.y; y++) {
|
|
4872
|
+
const srcRow = y;
|
|
4873
|
+
const dstRow = size.y - 1 - y;
|
|
4874
|
+
const srcOffset = srcRow * rowSize;
|
|
4875
|
+
const dstOffset = dstRow * rowSize;
|
|
4876
|
+
imageData.data.set(
|
|
4877
|
+
pixels.subarray(srcOffset, srcOffset + rowSize),
|
|
4878
|
+
dstOffset
|
|
4879
|
+
);
|
|
4880
|
+
}
|
|
4881
|
+
ctx.putImageData(imageData, 0, 0);
|
|
4882
|
+
}
|
|
4883
|
+
/**
|
|
4884
|
+
* Gets the model ID at the given screen position.
|
|
4885
|
+
*
|
|
4886
|
+
* @param position - Optional screen position. If not provided, uses current mouse position.
|
|
4887
|
+
* @returns The model ID at the position, or null if no model is found.
|
|
4888
|
+
*/
|
|
4889
|
+
async getModelAt(position) {
|
|
4890
|
+
if (!this.renderTarget || !this.world.renderer) {
|
|
4891
|
+
throw new Error("Render target not initialized!");
|
|
4892
|
+
}
|
|
4893
|
+
const fragments = this.components.get(FragmentsManager);
|
|
4894
|
+
if (!fragments.initialized || fragments.list.size === 0) {
|
|
4895
|
+
return null;
|
|
4896
|
+
}
|
|
4897
|
+
this.assignColors();
|
|
4898
|
+
this.renderColorCoded();
|
|
4899
|
+
const pos = position || this.mouse.position;
|
|
4900
|
+
const size = this.renderTargetSize;
|
|
4901
|
+
const x = Math.floor((pos.x + 1) * 0.5 * size.x);
|
|
4902
|
+
const y = Math.floor((pos.y + 1) * 0.5 * (size.y - 1));
|
|
4903
|
+
const clampedX = Math.max(0, Math.min(size.x - 1, x));
|
|
4904
|
+
const clampedY = Math.max(0, Math.min(size.y - 1, y));
|
|
4905
|
+
const renderer = this.world.renderer.three;
|
|
4906
|
+
const pixels = new Uint8Array(4);
|
|
4907
|
+
renderer.readRenderTargetPixels(
|
|
4908
|
+
this.renderTarget,
|
|
4909
|
+
clampedX,
|
|
4910
|
+
clampedY,
|
|
4911
|
+
1,
|
|
4912
|
+
1,
|
|
4913
|
+
pixels
|
|
4914
|
+
);
|
|
4915
|
+
const r = pixels[0];
|
|
4916
|
+
const g = pixels[1];
|
|
4917
|
+
const b = pixels[2];
|
|
4918
|
+
const colorId = r << 16 | g << 8 | b;
|
|
4919
|
+
const modelId = this.colorToModelId.get(colorId);
|
|
4920
|
+
return modelId || null;
|
|
4921
|
+
}
|
|
4922
|
+
/**
|
|
4923
|
+
* Enables or disables debug mode.
|
|
4924
|
+
* When enabled, shows a canvas with the color-coded render.
|
|
4925
|
+
*/
|
|
4926
|
+
setDebugMode(enabled) {
|
|
4927
|
+
this.debugMode = enabled;
|
|
4928
|
+
if (enabled) {
|
|
4929
|
+
this.setupDebugCanvas();
|
|
4930
|
+
} else {
|
|
4931
|
+
this.removeDebugCanvas();
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
/**
|
|
4935
|
+
* Removes the debug canvas.
|
|
4936
|
+
*/
|
|
4937
|
+
removeDebugCanvas() {
|
|
4938
|
+
if (this.debugContainer) {
|
|
4939
|
+
this.debugContainer.remove();
|
|
4940
|
+
this.debugContainer = void 0;
|
|
4941
|
+
this.debugCanvas = void 0;
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
/** {@link Disposable.dispose} */
|
|
4945
|
+
dispose() {
|
|
4946
|
+
this.mouse.dispose();
|
|
4947
|
+
this.removeDebugCanvas();
|
|
4948
|
+
for (const material of this.colorMaterials.values()) {
|
|
4949
|
+
material.dispose();
|
|
4950
|
+
}
|
|
4951
|
+
this.colorMaterials.clear();
|
|
4952
|
+
if (this.renderTarget) {
|
|
4953
|
+
this.renderTarget.dispose();
|
|
4954
|
+
}
|
|
4955
|
+
this.colorToModelId.clear();
|
|
4956
|
+
this.modelIdToColor.clear();
|
|
4957
|
+
this.originalMaterials.clear();
|
|
4958
|
+
this.originalLodColors.clear();
|
|
4959
|
+
this.onDisposed.trigger();
|
|
4960
|
+
this.onDisposed.reset();
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
const _FastModelPickers = class _FastModelPickers extends Component {
|
|
4964
|
+
constructor(components) {
|
|
4965
|
+
super(components);
|
|
4966
|
+
/** {@link Component.enabled} */
|
|
4967
|
+
__publicField(this, "enabled", true);
|
|
4968
|
+
/**
|
|
4969
|
+
* A Map that stores FastModelPicker instances for each world.
|
|
4970
|
+
* The key is the world's UUID, and the value is the corresponding FastModelPicker instance.
|
|
4971
|
+
*/
|
|
4972
|
+
__publicField(this, "list", /* @__PURE__ */ new Map());
|
|
4973
|
+
/** {@link Disposable.onDisposed} */
|
|
4974
|
+
__publicField(this, "onDisposed", new Event());
|
|
4975
|
+
components.add(_FastModelPickers.uuid, this);
|
|
4976
|
+
}
|
|
4977
|
+
/**
|
|
4978
|
+
* Retrieves a FastModelPicker instance for the given world.
|
|
4979
|
+
* If a FastModelPicker instance already exists for the world, it will be returned.
|
|
4980
|
+
* Otherwise, a new FastModelPicker instance will be created and added to the list.
|
|
4981
|
+
*
|
|
4982
|
+
* @param world - The world for which to retrieve or create a FastModelPicker instance.
|
|
4983
|
+
* @returns The FastModelPicker instance for the given world.
|
|
4984
|
+
*/
|
|
4985
|
+
get(world) {
|
|
4986
|
+
if (this.list.has(world.uuid)) {
|
|
4987
|
+
return this.list.get(world.uuid);
|
|
4988
|
+
}
|
|
4989
|
+
const picker = new FastModelPicker(this.components, world);
|
|
4990
|
+
this.list.set(world.uuid, picker);
|
|
4991
|
+
world.onDisposed.add(() => {
|
|
4992
|
+
this.delete(world);
|
|
4993
|
+
});
|
|
4994
|
+
return picker;
|
|
4995
|
+
}
|
|
4996
|
+
/**
|
|
4997
|
+
* Deletes the FastModelPicker instance associated with the given world.
|
|
4998
|
+
* If a FastModelPicker instance exists for the given world, it will be disposed and removed from the list.
|
|
4999
|
+
*
|
|
5000
|
+
* @param world - The world for which to delete the FastModelPicker instance.
|
|
5001
|
+
* @returns {void}
|
|
5002
|
+
*/
|
|
5003
|
+
delete(world) {
|
|
5004
|
+
const picker = this.list.get(world.uuid);
|
|
5005
|
+
if (picker) {
|
|
5006
|
+
picker.dispose();
|
|
5007
|
+
}
|
|
5008
|
+
this.list.delete(world.uuid);
|
|
5009
|
+
}
|
|
5010
|
+
/** {@link Disposable.dispose} */
|
|
5011
|
+
dispose() {
|
|
5012
|
+
for (const [_id, picker] of this.list) {
|
|
5013
|
+
picker.dispose();
|
|
5014
|
+
}
|
|
5015
|
+
this.list.clear();
|
|
5016
|
+
this.onDisposed.trigger();
|
|
5017
|
+
}
|
|
5018
|
+
};
|
|
5019
|
+
/**
|
|
5020
|
+
* A unique identifier for the component.
|
|
5021
|
+
* This UUID is used to register the component within the Components system.
|
|
5022
|
+
*/
|
|
5023
|
+
__publicField(_FastModelPickers, "uuid", "4a82430c-7ff2-49ea-9401-60807502dad6");
|
|
5024
|
+
let FastModelPickers = _FastModelPickers;
|
|
4569
5025
|
class SimpleRaycaster {
|
|
4570
5026
|
constructor(components, world) {
|
|
4571
5027
|
/** {@link Component.enabled} */
|
|
@@ -4586,6 +5042,13 @@ class SimpleRaycaster {
|
|
|
4586
5042
|
* This is used to access the camera and meshes.
|
|
4587
5043
|
*/
|
|
4588
5044
|
__publicField(this, "world");
|
|
5045
|
+
/**
|
|
5046
|
+
* Whether to use fast model picking to optimize raycasting.
|
|
5047
|
+
* When enabled, the raycaster will first use FastModelPicker to identify
|
|
5048
|
+
* which model is under the mouse, then only raycast that specific model.
|
|
5049
|
+
* This can significantly improve performance when there are many models.
|
|
5050
|
+
*/
|
|
5051
|
+
__publicField(this, "useFastModelPicking", false);
|
|
4589
5052
|
const renderer = world.renderer;
|
|
4590
5053
|
if (!renderer) {
|
|
4591
5054
|
throw new Error("A renderer is needed for the raycaster to work!");
|
|
@@ -4632,12 +5095,46 @@ class SimpleRaycaster {
|
|
|
4632
5095
|
const mouse = this.mouse.rawPosition;
|
|
4633
5096
|
let fragResult = null;
|
|
4634
5097
|
if (fragments.initialized) {
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
5098
|
+
if (this.useFastModelPicking) {
|
|
5099
|
+
const fastPickers = this.components.get(FastModelPickers);
|
|
5100
|
+
const fastPicker = fastPickers.get(this.world);
|
|
5101
|
+
const modelId = await fastPicker.getModelAt(position);
|
|
5102
|
+
if (modelId) {
|
|
5103
|
+
const model = fragments.list.get(modelId);
|
|
5104
|
+
if (model) {
|
|
5105
|
+
if (snappingClasses && snappingClasses.length > 0) {
|
|
5106
|
+
const snappingRaycast = await model.raycastWithSnapping({
|
|
5107
|
+
camera,
|
|
5108
|
+
dom,
|
|
5109
|
+
mouse,
|
|
5110
|
+
snappingClasses
|
|
5111
|
+
});
|
|
5112
|
+
if (snappingRaycast && snappingRaycast.length > 0) {
|
|
5113
|
+
fragResult = snappingRaycast[0];
|
|
5114
|
+
} else {
|
|
5115
|
+
fragResult = await model.raycast({
|
|
5116
|
+
camera,
|
|
5117
|
+
dom,
|
|
5118
|
+
mouse
|
|
5119
|
+
});
|
|
5120
|
+
}
|
|
5121
|
+
} else {
|
|
5122
|
+
fragResult = await model.raycast({
|
|
5123
|
+
camera,
|
|
5124
|
+
dom,
|
|
5125
|
+
mouse
|
|
5126
|
+
});
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
} else {
|
|
5131
|
+
fragResult = await fragments.raycast({
|
|
5132
|
+
camera,
|
|
5133
|
+
dom,
|
|
5134
|
+
mouse,
|
|
5135
|
+
snappingClasses
|
|
5136
|
+
});
|
|
5137
|
+
}
|
|
4641
5138
|
if (items.length === 0) {
|
|
4642
5139
|
return fragResult;
|
|
4643
5140
|
}
|
|
@@ -7663,7 +8160,7 @@ const _Worlds = class _Worlds extends Component {
|
|
|
7663
8160
|
* A collection of worlds managed by this component.
|
|
7664
8161
|
* The key is the unique identifier (UUID) of the world, and the value is the World instance.
|
|
7665
8162
|
*/
|
|
7666
|
-
__publicField(this, "list", new DataMap());
|
|
8163
|
+
__publicField(this, "list", new FRAGS.DataMap());
|
|
7667
8164
|
/** {@link Component.enabled} */
|
|
7668
8165
|
__publicField(this, "enabled", true);
|
|
7669
8166
|
components.add(_Worlds.uuid, this);
|
|
@@ -11535,10 +12032,11 @@ const _Components = class _Components {
|
|
|
11535
12032
|
* @throws Will throw an error if a component with the same UUID already exists.
|
|
11536
12033
|
*/
|
|
11537
12034
|
get(Component2) {
|
|
12035
|
+
var _a2;
|
|
11538
12036
|
const uuid = Component2.uuid;
|
|
11539
12037
|
if (!this.list.has(uuid)) {
|
|
11540
12038
|
const toolInstance = new Component2(this);
|
|
11541
|
-
if (toolInstance.isDisposeable()) {
|
|
12039
|
+
if ((_a2 = toolInstance.isDisposeable) == null ? void 0 : _a2.call(toolInstance)) {
|
|
11542
12040
|
toolInstance.onDisposed.add(() => this.list.delete(uuid));
|
|
11543
12041
|
}
|
|
11544
12042
|
if (!this.list.has(uuid)) {
|
|
@@ -11988,6 +12486,14 @@ class OrthoPerspectiveCamera extends SimpleCamera {
|
|
|
11988
12486
|
this.disableUserInput();
|
|
11989
12487
|
}
|
|
11990
12488
|
}
|
|
12489
|
+
/**
|
|
12490
|
+
* Adds a custom {@link NavigationMode} to the camera that can be used using the {@link OrthoPerspectiveCamera.set} method.
|
|
12491
|
+
*
|
|
12492
|
+
* @param mode - The custom {@link NavigationMode} to add.
|
|
12493
|
+
*/
|
|
12494
|
+
addCustomNavigationMode(mode) {
|
|
12495
|
+
this._navigationModes.set(mode.id, mode);
|
|
12496
|
+
}
|
|
11991
12497
|
disableUserInput() {
|
|
11992
12498
|
this._userInputButtons.left = this.controls.mouseButtons.left;
|
|
11993
12499
|
this._userInputButtons.right = this.controls.mouseButtons.right;
|
|
@@ -14770,13 +15276,13 @@ const _Topic = class _Topic {
|
|
|
14770
15276
|
__publicField(this, "creationDate", /* @__PURE__ */ new Date());
|
|
14771
15277
|
__publicField(this, "creationAuthor", "");
|
|
14772
15278
|
// Store viewpoint guids instead of the actual Viewpoint to prevent a possible memory leak
|
|
14773
|
-
__publicField(this, "viewpoints", new DataSet());
|
|
15279
|
+
__publicField(this, "viewpoints", new FRAGS.DataSet());
|
|
14774
15280
|
// Store topic guids instead of the actual Topic to prevent a possible memory leak
|
|
14775
|
-
__publicField(this, "relatedTopics", new DataSet());
|
|
15281
|
+
__publicField(this, "relatedTopics", new FRAGS.DataSet());
|
|
14776
15282
|
// There is no problem to store the comment it-self as it is not referenced anywhere else
|
|
14777
|
-
__publicField(this, "comments", new DataMap());
|
|
15283
|
+
__publicField(this, "comments", new FRAGS.DataMap());
|
|
14778
15284
|
// Store document reference guids instead of the actual thing to prevent a possible memory leak
|
|
14779
|
-
__publicField(this, "documentReferences", new DataSet());
|
|
15285
|
+
__publicField(this, "documentReferences", new FRAGS.DataSet());
|
|
14780
15286
|
__publicField(this, "customData", {});
|
|
14781
15287
|
__publicField(this, "description");
|
|
14782
15288
|
__publicField(this, "serverAssignedId");
|
|
@@ -15386,8 +15892,8 @@ const _BCFTopics = class _BCFTopics extends Component {
|
|
|
15386
15892
|
"BCF Topics",
|
|
15387
15893
|
_BCFTopics.uuid
|
|
15388
15894
|
));
|
|
15389
|
-
__publicField(this, "list", new DataMap());
|
|
15390
|
-
__publicField(this, "documents", new DataMap());
|
|
15895
|
+
__publicField(this, "list", new FRAGS.DataMap());
|
|
15896
|
+
__publicField(this, "documents", new FRAGS.DataMap());
|
|
15391
15897
|
__publicField(this, "onSetup", new Event());
|
|
15392
15898
|
__publicField(this, "isSetup", false);
|
|
15393
15899
|
__publicField(this, "onBCFImported", new Event());
|
|
@@ -17321,6 +17827,15 @@ class SimplePlane {
|
|
|
17321
17827
|
get helper() {
|
|
17322
17828
|
return this._helper;
|
|
17323
17829
|
}
|
|
17830
|
+
/**
|
|
17831
|
+
* Getter for the transform controls of the clipping plane.
|
|
17832
|
+
* The controls allow interactive manipulation (translation, rotation, etc.) of the clipping plane.
|
|
17833
|
+
*
|
|
17834
|
+
* @returns {TransformControls} The transform controls of the clipping plane.
|
|
17835
|
+
*/
|
|
17836
|
+
get controls() {
|
|
17837
|
+
return this._controls;
|
|
17838
|
+
}
|
|
17324
17839
|
/**
|
|
17325
17840
|
* Sets the clipping plane's normal and origin from the given normal and point.
|
|
17326
17841
|
* This method resets the clipping plane's state, updates the normal and origin,
|
|
@@ -17585,6 +18100,7 @@ const _Clipper = class _Clipper extends Component {
|
|
|
17585
18100
|
__publicField(this, "_size", 5);
|
|
17586
18101
|
__publicField(this, "_enabled", false);
|
|
17587
18102
|
__publicField(this, "_visible", true);
|
|
18103
|
+
__publicField(this, "onStateChanged", new Event());
|
|
17588
18104
|
__publicField(this, "_onStartDragging", () => {
|
|
17589
18105
|
this.onBeforeDrag.trigger();
|
|
17590
18106
|
});
|
|
@@ -17601,6 +18117,7 @@ const _Clipper = class _Clipper extends Component {
|
|
|
17601
18117
|
/** {@link Component.enabled} */
|
|
17602
18118
|
set enabled(state) {
|
|
17603
18119
|
this._enabled = state;
|
|
18120
|
+
this.onStateChanged.trigger(["enabled"]);
|
|
17604
18121
|
}
|
|
17605
18122
|
/** {@link Hideable.visible } */
|
|
17606
18123
|
get visible() {
|
|
@@ -17612,6 +18129,7 @@ const _Clipper = class _Clipper extends Component {
|
|
|
17612
18129
|
for (const [_, plane] of this.list) {
|
|
17613
18130
|
plane.visible = state;
|
|
17614
18131
|
}
|
|
18132
|
+
this.onStateChanged.trigger(["visibility"]);
|
|
17615
18133
|
}
|
|
17616
18134
|
/** The material of the clipping plane representation. */
|
|
17617
18135
|
get material() {
|
|
@@ -17623,6 +18141,7 @@ const _Clipper = class _Clipper extends Component {
|
|
|
17623
18141
|
for (const [_, plane] of this.list) {
|
|
17624
18142
|
plane.planeMaterial = material;
|
|
17625
18143
|
}
|
|
18144
|
+
this.onStateChanged.trigger(["material"]);
|
|
17626
18145
|
}
|
|
17627
18146
|
/** The size of the geometric representation of the clippings planes. */
|
|
17628
18147
|
get size() {
|
|
@@ -17634,6 +18153,7 @@ const _Clipper = class _Clipper extends Component {
|
|
|
17634
18153
|
for (const [_, plane] of this.list) {
|
|
17635
18154
|
plane.size = size;
|
|
17636
18155
|
}
|
|
18156
|
+
this.onStateChanged.trigger(["size"]);
|
|
17637
18157
|
}
|
|
17638
18158
|
setEvents() {
|
|
17639
18159
|
this.list.onBeforeDelete.add(({ value: plane }) => {
|
|
@@ -17867,6 +18387,7 @@ class Viewpoint {
|
|
|
17867
18387
|
camera_view_point: { x: 0, y: 0, z: 0 },
|
|
17868
18388
|
camera_up_vector: { x: 0, y: 1, z: 0 }
|
|
17869
18389
|
});
|
|
18390
|
+
__publicField(this, "customData", {});
|
|
17870
18391
|
/**
|
|
17871
18392
|
* A list of components GUIDs to hide when defaultVisibility = true or to show when defaultVisibility = false
|
|
17872
18393
|
*/
|
|
@@ -21011,7 +21532,7 @@ const _IDSSpecifications = class _IDSSpecifications extends Component {
|
|
|
21011
21532
|
super(components);
|
|
21012
21533
|
__publicField(this, "enabled", true);
|
|
21013
21534
|
__publicField(this, "IDSInfo");
|
|
21014
|
-
__publicField(this, "list", new DataMap());
|
|
21535
|
+
__publicField(this, "list", new FRAGS.DataMap());
|
|
21015
21536
|
components.add(_IDSSpecifications.uuid, this);
|
|
21016
21537
|
}
|
|
21017
21538
|
/**
|
|
@@ -21538,11 +22059,6 @@ const _MeasurementUtils = class _MeasurementUtils extends Component {
|
|
|
21538
22059
|
throw new Error("Precision must be an integer between 0 and 5.");
|
|
21539
22060
|
}
|
|
21540
22061
|
let factor = unitFactors[fromUnit] / unitFactors[toUnit];
|
|
21541
|
-
if (fromUnit.endsWith("2") && toUnit.endsWith("2")) {
|
|
21542
|
-
factor **= 2;
|
|
21543
|
-
} else if (fromUnit.endsWith("3") && toUnit.endsWith("3")) {
|
|
21544
|
-
factor **= 3;
|
|
21545
|
-
}
|
|
21546
22062
|
const convertedValue = value * factor;
|
|
21547
22063
|
const roundingFactor = 10 ** precision;
|
|
21548
22064
|
return Math.round(convertedValue * roundingFactor) / roundingFactor;
|
|
@@ -21576,6 +22092,8 @@ exports.DataSet = DataSet;
|
|
|
21576
22092
|
exports.Disposer = Disposer;
|
|
21577
22093
|
exports.Event = Event;
|
|
21578
22094
|
exports.EventManager = EventManager;
|
|
22095
|
+
exports.FastModelPicker = FastModelPicker;
|
|
22096
|
+
exports.FastModelPickers = FastModelPickers;
|
|
21579
22097
|
exports.FinderQuery = FinderQuery;
|
|
21580
22098
|
exports.FirstPersonMode = FirstPersonMode;
|
|
21581
22099
|
exports.FragmentsManager = FragmentsManager;
|