@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 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 name = manager.list.getKey(this) ?? "Finder Query";
4044
+ const guid = manager.list.getKey(this) ?? UUID.create();
4044
4045
  const result = {
4045
- name,
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
- const { modelIds } = config ?? {};
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
- if (modelIds && !modelIds.some((regex) => regex.test(id)))
4130
+ var _a2;
4131
+ if (modelsToCheck && !modelsToCheck.some((regex) => regex.test(id)))
4118
4132
  return;
4119
- const items = await model.getItemsByQuery(query);
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 { name, customData, queries, aggregation, cache } = value;
4179
- const finderQuery = this.create(name, []);
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
- fragResult = await fragments.raycast({
4636
- camera,
4637
- dom,
4638
- mouse,
4639
- snappingClasses
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;