@ifc-lite/renderer 1.15.2 → 1.16.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.
Files changed (57) hide show
  1. package/dist/bvh.d.ts +1 -1
  2. package/dist/bvh.d.ts.map +1 -1
  3. package/dist/constants.d.ts +8 -2
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/constants.js +8 -2
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.d.ts +4 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +153 -30
  10. package/dist/index.js.map +1 -1
  11. package/dist/pipeline.d.ts +11 -1
  12. package/dist/pipeline.d.ts.map +1 -1
  13. package/dist/pipeline.js +49 -12
  14. package/dist/pipeline.js.map +1 -1
  15. package/dist/scene-geometry.d.ts +31 -0
  16. package/dist/scene-geometry.d.ts.map +1 -0
  17. package/dist/scene-geometry.js +137 -0
  18. package/dist/scene-geometry.js.map +1 -0
  19. package/dist/scene-raycaster.d.ts +65 -0
  20. package/dist/scene-raycaster.d.ts.map +1 -0
  21. package/dist/scene-raycaster.js +234 -0
  22. package/dist/scene-raycaster.js.map +1 -0
  23. package/dist/scene.d.ts +9 -39
  24. package/dist/scene.d.ts.map +1 -1
  25. package/dist/scene.js +15 -339
  26. package/dist/scene.js.map +1 -1
  27. package/dist/section-2d-overlay.d.ts +22 -7
  28. package/dist/section-2d-overlay.d.ts.map +1 -1
  29. package/dist/section-2d-overlay.js +219 -42
  30. package/dist/section-2d-overlay.js.map +1 -1
  31. package/dist/section-cap-style.d.ts +37 -0
  32. package/dist/section-cap-style.d.ts.map +1 -0
  33. package/dist/section-cap-style.js +38 -0
  34. package/dist/section-cap-style.js.map +1 -0
  35. package/dist/section-plane.d.ts +0 -3
  36. package/dist/section-plane.d.ts.map +1 -1
  37. package/dist/section-plane.js +33 -6
  38. package/dist/section-plane.js.map +1 -1
  39. package/dist/shaders/main.wgsl.d.ts +1 -1
  40. package/dist/shaders/main.wgsl.d.ts.map +1 -1
  41. package/dist/shaders/main.wgsl.js +7 -4
  42. package/dist/shaders/main.wgsl.js.map +1 -1
  43. package/dist/snap-detector.d.ts +1 -17
  44. package/dist/snap-detector.d.ts.map +1 -1
  45. package/dist/snap-detector.js +16 -213
  46. package/dist/snap-detector.js.map +1 -1
  47. package/dist/snap-geometry-cache.d.ts +24 -0
  48. package/dist/snap-geometry-cache.d.ts.map +1 -0
  49. package/dist/snap-geometry-cache.js +155 -0
  50. package/dist/snap-geometry-cache.js.map +1 -0
  51. package/dist/snap-geometry-utils.d.ts +27 -0
  52. package/dist/snap-geometry-utils.d.ts.map +1 -0
  53. package/dist/snap-geometry-utils.js +56 -0
  54. package/dist/snap-geometry-utils.js.map +1 -0
  55. package/dist/types.d.ts +26 -0
  56. package/dist/types.d.ts.map +1 -1
  57. package/package.json +3 -3
@@ -1,6 +1,10 @@
1
1
  /* This Source Code Form is subject to the terms of the Mozilla Public
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Section plane renderer - renders a visible plane at the section cut location
6
+ */
7
+ import { PIPELINE_CONSTANTS } from './constants.js';
4
8
  export class SectionPlaneRenderer {
5
9
  device;
6
10
  bindGroupLayout = null; // Shared layout for both pipelines
@@ -56,8 +60,15 @@ export class SectionPlaneRenderer {
56
60
  return output;
57
61
  }
58
62
 
63
+ // Two outputs so the pipeline matches the main pass's two colour
64
+ // attachments. objectId is masked off at the pipeline level.
65
+ struct FragOut {
66
+ @location(0) color: vec4<f32>,
67
+ @location(1) objectId: vec4<f32>,
68
+ }
69
+
59
70
  @fragment
60
- fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
71
+ fn fs_main(input: VertexOutput) -> FragOut {
61
72
  // Create fine grid pattern
62
73
  let gridSize = 0.01; // Fine grid cells (100 divisions)
63
74
  let lineWidth = 0.001; // Very thin lines
@@ -104,7 +115,10 @@ export class SectionPlaneRenderer {
104
115
  // Clamp alpha
105
116
  color.a = min(color.a, 0.5);
106
117
 
107
- return color;
118
+ var out: FragOut;
119
+ out.color = color;
120
+ out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);
121
+ return out;
108
122
  }
109
123
  `,
110
124
  });
@@ -127,7 +141,18 @@ export class SectionPlaneRenderer {
127
141
  fragment: {
128
142
  module: shaderModule,
129
143
  entryPoint: 'fs_main',
130
- targets: [{
144
+ // The main render pass has two colour attachments (main colour +
145
+ // the picker's objectId texture). WebGPU requires every pipeline
146
+ // used inside a pass to declare exactly the same target count and
147
+ // formats. The preview plane only paints into the main colour
148
+ // target — the objectId target is declared with writeMask 0 so
149
+ // cap-less picking IDs underneath are preserved. Without this,
150
+ // `setPipeline` raises "Incompatible color attachments at
151
+ // indices []: RenderPass uses formats [Bgra8Unorm, Rgba8Unorm]
152
+ // but RenderPipeline uses formats [Bgra8Unorm]" and the whole
153
+ // frame is dropped.
154
+ targets: [
155
+ {
131
156
  format: this.format,
132
157
  blend: {
133
158
  color: {
@@ -141,7 +166,9 @@ export class SectionPlaneRenderer {
141
166
  operation: 'add',
142
167
  },
143
168
  },
144
- }],
169
+ },
170
+ { format: 'rgba8unorm', writeMask: 0 },
171
+ ],
145
172
  },
146
173
  primitive: {
147
174
  topology: 'triangle-list',
@@ -155,7 +182,7 @@ export class SectionPlaneRenderer {
155
182
  this.previewPipeline = this.device.createRenderPipeline({
156
183
  ...pipelineBase,
157
184
  depthStencil: {
158
- format: 'depth32float',
185
+ format: PIPELINE_CONSTANTS.DEPTH_FORMAT,
159
186
  depthWriteEnabled: false,
160
187
  depthCompare: 'greater', // Only draw where plane is behind geometry (empty space)
161
188
  },
@@ -164,7 +191,7 @@ export class SectionPlaneRenderer {
164
191
  this.cutPipeline = this.device.createRenderPipeline({
165
192
  ...pipelineBase,
166
193
  depthStencil: {
167
- format: 'depth32float',
194
+ format: PIPELINE_CONSTANTS.DEPTH_FORMAT,
168
195
  depthWriteEnabled: false,
169
196
  depthCompare: 'always', // Always draw on top
170
197
  },
@@ -1 +1 @@
1
- {"version":3,"file":"section-plane.js","sourceRoot":"","sources":["../src/section-plane.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"section-plane.js","sourceRoot":"","sources":["../src/section-plane.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  * Features: PBR lighting, section plane clipping, selection highlight,
4
4
  * glass fresnel, ACES tone mapping, screen-space edge enhancement.
5
5
  */
6
- export declare const mainShaderSource = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n model: mat4x4<f32>,\n baseColor: vec4<f32>,\n metallicRoughness: vec2<f32>, // x = metallic, y = roughness\n _padding1: vec2<f32>,\n sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance\n flags: vec4<u32>, // x = isSelected, y = sectionEnabled, z = edgeEnabled, w = edgeIntensityMilli\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) entityId: u32,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) @interpolate(flat) entityId: u32,\n @location(3) viewPos: vec3<f32>, // For edge detection\n }\n\n @vertex\n fn vs_main(input: VertexInput, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\n var output: VertexOutput;\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\n output.position = uniforms.viewProj * worldPos;\n // Anti z-fighting: deterministic depth nudge per entity.\n // Knuth multiplicative hash spreads sequential IDs across 0-255\n // so coplanar faces from different entities always get distinct depths.\n // At 1e-6 per step the max world-space offset is <3mm at 10m \u2014 invisible.\n let zHash = (input.entityId * 2654435761u) & 255u;\n output.position.z *= 1.0 + f32(zHash) * 1e-6;\n output.worldPos = worldPos.xyz;\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\n output.entityId = input.entityId;\n // Store view-space position for edge detection\n output.viewPos = (uniforms.viewProj * worldPos).xyz;\n return output;\n }\n\n // PBR helper functions\n fn fresnelSchlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {\n return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);\n }\n\n fn distributionGGX(NdotH: f32, roughness: f32) -> f32 {\n let a = roughness * roughness;\n let a2 = a * a;\n let NdotH2 = NdotH * NdotH;\n let num = a2;\n let denomBase = (NdotH2 * (a2 - 1.0) + 1.0);\n let denom = 3.14159265 * denomBase * denomBase;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySchlickGGX(NdotV: f32, roughness: f32) -> f32 {\n let r = (roughness + 1.0);\n let k = (r * r) / 8.0;\n let num = NdotV;\n let denom = NdotV * (1.0 - k) + k;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySmith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 {\n let ggx2 = geometrySchlickGGX(NdotV, roughness);\n let ggx1 = geometrySchlickGGX(NdotL, roughness);\n return ggx1 * ggx2;\n }\n\n fn encodeId24(id: u32) -> vec4<f32> {\n let r = f32((id >> 16u) & 255u) / 255.0;\n let g = f32((id >> 8u) & 255u) / 255.0;\n let b = f32(id & 255u) / 255.0;\n return vec4<f32>(r, g, b, 1.0);\n }\n\n struct FragmentOutput {\n @location(0) color: vec4<f32>,\n @location(1) objectIdEncoded: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragmentOutput {\n // Section plane clipping - discard fragments ABOVE the plane\n // For Down axis (normal +Y), keeps everything below cut height (look down into building)\n if (uniforms.flags.y == 1u) {\n let planeNormal = uniforms.sectionPlane.xyz;\n let planeDistance = uniforms.sectionPlane.w;\n let distToPlane = dot(input.worldPos, planeNormal) - planeDistance;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n\n // Compute normal \u2014 with fallback for zero normals\n // dpdx/dpdy must be called outside non-uniform control flow (WGSL spec),\n // so we compute the flat normal unconditionally and select below.\n let faceN = cross(dpdx(input.worldPos), dpdy(input.worldPos));\n var N = input.normal;\n let nLen2 = dot(N, N);\n if (nLen2 < 0.0001) {\n // Fallback: use flat normal from screen-space derivatives\n let fLen2 = dot(faceN, faceN);\n N = select(vec3<f32>(0.0, 1.0, 0.0), faceN * inverseSqrt(fLen2), fLen2 > 1e-10);\n } else {\n N = N * inverseSqrt(nLen2);\n }\n\n // Enhanced lighting with multiple sources\n let sunLight = normalize(vec3<f32>(0.5, 1.0, 0.3)); // Main directional light\n let fillLight = normalize(vec3<f32>(-0.5, 0.3, -0.3)); // Fill light\n let rimLight = normalize(vec3<f32>(0.0, 0.2, -1.0)); // Rim light for edge definition\n\n // Hemisphere ambient - reduced for less washed-out look\n let skyColor = vec3<f32>(0.3, 0.35, 0.4); // Darker sky\n let groundColor = vec3<f32>(0.15, 0.1, 0.08); // Darker ground\n let hemisphereFactor = N.y * 0.5 + 0.5;\n let ambient = mix(groundColor, skyColor, hemisphereFactor) * 0.25;\n\n // Two-sided sun light so inner faces (I-beam channels) stay visible\n let NdotL = abs(dot(N, sunLight));\n let wrap = 0.3;\n let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) * 0.55;\n\n // Fill light - two-sided\n let NdotFill = abs(dot(N, fillLight));\n let diffuseFill = NdotFill * 0.15;\n\n // Rim light for edge definition\n let NdotRim = max(dot(N, rimLight), 0.0);\n let rim = pow(NdotRim, 4.0) * 0.15;\n\n var baseColor = uniforms.baseColor.rgb;\n\n // Detect if the color is close to white/gray (low saturation)\n let baseGray = dot(baseColor, vec3<f32>(0.299, 0.587, 0.114));\n let baseSaturation = length(baseColor - vec3<f32>(baseGray)) / max(baseGray, 0.001);\n let isWhiteish = 1.0 - smoothstep(0.0, 0.3, baseSaturation);\n\n // Darken whites/grays more to reduce washed-out appearance\n baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);\n\n // Combine all lighting\n var color = baseColor * (ambient + diffuseSun + diffuseFill + rim);\n\n // Selection highlight - add glow/fresnel effect\n if (uniforms.flags.x == 1u) {\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n let fresnel = pow(1.0 - NdotV, 2.0);\n let highlightColor = vec3<f32>(0.3, 0.6, 1.0);\n color = mix(color, highlightColor, fresnel * 0.5 + 0.2);\n }\n\n // Beautiful fresnel effect for transparent materials (glass)\n // Skip when selected \u2014 the glass shine and desaturation wash out the\n // blue highlight, making it appear white instead of blue.\n // Also force alpha to 1.0 for selected objects so the highlight is\n // fully opaque (the selection pipeline has no alpha blending).\n var finalAlpha = select(uniforms.baseColor.a, 1.0, uniforms.flags.x == 1u);\n if (finalAlpha < 0.99 && uniforms.flags.x != 1u) {\n // Calculate view direction for fresnel\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n\n // Enhanced fresnel effect - stronger at edges (grazing angles)\n // Using Schlick's approximation for realistic glass reflection\n let fresnelPower = 1.5; // Higher = softer edge reflections\n let fresnel = pow(1.0 - NdotV, fresnelPower);\n\n // Glass reflection tint (sky/environment reflection at edges)\n let reflectionTint = vec3<f32>(0.92, 0.96, 1.0); // Cool sky reflection\n let reflectionStrength = fresnel * 0.6; // Strong edge reflections\n\n // Mix in reflection tint at edges\n color = mix(color, color * reflectionTint, reflectionStrength);\n\n // Add realistic glass shine - brighter at edges where light reflects\n let glassShine = fresnel * 0.12;\n color += glassShine;\n\n // Slight desaturation at edges (glass reflects environment, not just color)\n let edgeDesaturation = fresnel * 0.25;\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n color = mix(color, vec3<f32>(gray), edgeDesaturation);\n\n // Make glass more transparent (reduce opacity by 30%)\n finalAlpha = finalAlpha * 0.7;\n }\n\n // Exposure adjustment - darken overall\n color *= 0.85;\n\n // Contrast enhancement\n color = (color - 0.5) * 1.15 + 0.5;\n color = max(color, vec3<f32>(0.0));\n\n // Saturation boost - stronger for colored surfaces, less for whites\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n let satBoost = mix(1.4, 1.1, isWhiteish); // More saturation for colored surfaces\n color = mix(vec3<f32>(gray), color, satBoost);\n\n // ACES filmic tone mapping\n let a = 2.51;\n let b = 0.03;\n let c = 2.43;\n let d = 0.59;\n let e = 0.14;\n color = clamp((color * (a * color + b)) / (color * (c * color + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));\n\n // Subtle edge enhancement using screen-space derivatives\n let depthGradient = length(vec2<f32>(\n dpdx(input.viewPos.z),\n dpdy(input.viewPos.z)\n ));\n let normalGradient = length(vec2<f32>(\n length(dpdx(input.normal)),\n length(dpdy(input.normal))\n ));\n\n if (uniforms.flags.z == 1u) {\n // Threshold filters subtle normal discontinuities at internal\n // triangle edges between coplanar entities in the same batch.\n let edgeFactor = smoothstep(0.02, 0.12, depthGradient * 10.0 + normalGradient * 5.0);\n let edgeIntensity = f32(uniforms.flags.w) / 1000.0;\n let edgeDarkenStrength = clamp(0.25 * edgeIntensity, 0.0, 0.85);\n let edgeDarken = mix(1.0, 1.0 - edgeDarkenStrength, edgeFactor);\n color *= edgeDarken;\n }\n\n // Gamma correction\n color = pow(color, vec3<f32>(1.0 / 2.2));\n\n var out: FragmentOutput;\n out.color = vec4<f32>(color, finalAlpha);\n out.objectIdEncoded = encodeId24(input.entityId);\n return out;\n }\n ";
6
+ export declare const mainShaderSource = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n model: mat4x4<f32>,\n baseColor: vec4<f32>,\n metallicRoughness: vec2<f32>, // x = metallic, y = roughness\n _padding1: vec2<f32>,\n sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance\n flags: vec4<u32>, // x = isSelected, y = sectionEnabled, z = edgeEnabled, w = edgeIntensityMilli\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) entityId: u32,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n @location(1) normal: vec3<f32>,\n @location(2) @interpolate(flat) entityId: u32,\n @location(3) viewPos: vec3<f32>, // For edge detection\n }\n\n @vertex\n fn vs_main(input: VertexInput, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\n var output: VertexOutput;\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\n output.position = uniforms.viewProj * worldPos;\n // Anti z-fighting: deterministic depth nudge per entity.\n // Knuth multiplicative hash spreads sequential IDs across 0-255\n // so coplanar faces from different entities always get distinct depths.\n // At 1e-6 per step the max world-space offset is <3mm at 10m \u2014 invisible.\n let zHash = (input.entityId * 2654435761u) & 255u;\n output.position.z *= 1.0 + f32(zHash) * 1e-6;\n output.worldPos = worldPos.xyz;\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\n output.entityId = input.entityId;\n // Store view-space position for edge detection\n output.viewPos = (uniforms.viewProj * worldPos).xyz;\n return output;\n }\n\n // PBR helper functions\n fn fresnelSchlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {\n return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);\n }\n\n fn distributionGGX(NdotH: f32, roughness: f32) -> f32 {\n let a = roughness * roughness;\n let a2 = a * a;\n let NdotH2 = NdotH * NdotH;\n let num = a2;\n let denomBase = (NdotH2 * (a2 - 1.0) + 1.0);\n let denom = 3.14159265 * denomBase * denomBase;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySchlickGGX(NdotV: f32, roughness: f32) -> f32 {\n let r = (roughness + 1.0);\n let k = (r * r) / 8.0;\n let num = NdotV;\n let denom = NdotV * (1.0 - k) + k;\n return num / max(denom, 0.0000001);\n }\n\n fn geometrySmith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 {\n let ggx2 = geometrySchlickGGX(NdotV, roughness);\n let ggx1 = geometrySchlickGGX(NdotL, roughness);\n return ggx1 * ggx2;\n }\n\n fn encodeId24(id: u32) -> vec4<f32> {\n let r = f32((id >> 16u) & 255u) / 255.0;\n let g = f32((id >> 8u) & 255u) / 255.0;\n let b = f32(id & 255u) / 255.0;\n return vec4<f32>(r, g, b, 1.0);\n }\n\n struct FragmentOutput {\n @location(0) color: vec4<f32>,\n @location(1) objectIdEncoded: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragmentOutput {\n // Section plane clipping - discard fragments ABOVE the plane.\n // flags.y packs two bits: bit 0 = enabled, bit 1 = flipped.\n let sectionEnabled = (uniforms.flags.y & 1u) == 1u;\n if (sectionEnabled) {\n let planeNormal = uniforms.sectionPlane.xyz;\n let planeDistance = uniforms.sectionPlane.w;\n let flipped = (uniforms.flags.y & 2u) == 2u;\n let side = select(1.0, -1.0, flipped);\n let distToPlane = (dot(input.worldPos, planeNormal) - planeDistance) * side;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n\n // Compute normal \u2014 with fallback for zero normals\n // dpdx/dpdy must be called outside non-uniform control flow (WGSL spec),\n // so we compute the flat normal unconditionally and select below.\n let faceN = cross(dpdx(input.worldPos), dpdy(input.worldPos));\n var N = input.normal;\n let nLen2 = dot(N, N);\n if (nLen2 < 0.0001) {\n // Fallback: use flat normal from screen-space derivatives\n let fLen2 = dot(faceN, faceN);\n N = select(vec3<f32>(0.0, 1.0, 0.0), faceN * inverseSqrt(fLen2), fLen2 > 1e-10);\n } else {\n N = N * inverseSqrt(nLen2);\n }\n\n // Enhanced lighting with multiple sources\n let sunLight = normalize(vec3<f32>(0.5, 1.0, 0.3)); // Main directional light\n let fillLight = normalize(vec3<f32>(-0.5, 0.3, -0.3)); // Fill light\n let rimLight = normalize(vec3<f32>(0.0, 0.2, -1.0)); // Rim light for edge definition\n\n // Hemisphere ambient - reduced for less washed-out look\n let skyColor = vec3<f32>(0.3, 0.35, 0.4); // Darker sky\n let groundColor = vec3<f32>(0.15, 0.1, 0.08); // Darker ground\n let hemisphereFactor = N.y * 0.5 + 0.5;\n let ambient = mix(groundColor, skyColor, hemisphereFactor) * 0.25;\n\n // Two-sided sun light so inner faces (I-beam channels) stay visible\n let NdotL = abs(dot(N, sunLight));\n let wrap = 0.3;\n let diffuseSun = max((NdotL + wrap) / (1.0 + wrap), 0.0) * 0.55;\n\n // Fill light - two-sided\n let NdotFill = abs(dot(N, fillLight));\n let diffuseFill = NdotFill * 0.15;\n\n // Rim light for edge definition\n let NdotRim = max(dot(N, rimLight), 0.0);\n let rim = pow(NdotRim, 4.0) * 0.15;\n\n var baseColor = uniforms.baseColor.rgb;\n\n // Detect if the color is close to white/gray (low saturation)\n let baseGray = dot(baseColor, vec3<f32>(0.299, 0.587, 0.114));\n let baseSaturation = length(baseColor - vec3<f32>(baseGray)) / max(baseGray, 0.001);\n let isWhiteish = 1.0 - smoothstep(0.0, 0.3, baseSaturation);\n\n // Darken whites/grays more to reduce washed-out appearance\n baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);\n\n // Combine all lighting\n var color = baseColor * (ambient + diffuseSun + diffuseFill + rim);\n\n // Selection highlight - add glow/fresnel effect\n if (uniforms.flags.x == 1u) {\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n let fresnel = pow(1.0 - NdotV, 2.0);\n let highlightColor = vec3<f32>(0.3, 0.6, 1.0);\n color = mix(color, highlightColor, fresnel * 0.5 + 0.2);\n }\n\n // Beautiful fresnel effect for transparent materials (glass)\n // Skip when selected \u2014 the glass shine and desaturation wash out the\n // blue highlight, making it appear white instead of blue.\n // Also force alpha to 1.0 for selected objects so the highlight is\n // fully opaque (the selection pipeline has no alpha blending).\n var finalAlpha = select(uniforms.baseColor.a, 1.0, uniforms.flags.x == 1u);\n if (finalAlpha < 0.99 && uniforms.flags.x != 1u) {\n // Calculate view direction for fresnel\n let V = normalize(-input.worldPos);\n let NdotV = max(dot(N, V), 0.0);\n\n // Enhanced fresnel effect - stronger at edges (grazing angles)\n // Using Schlick's approximation for realistic glass reflection\n let fresnelPower = 1.5; // Higher = softer edge reflections\n let fresnel = pow(1.0 - NdotV, fresnelPower);\n\n // Glass reflection tint (sky/environment reflection at edges)\n let reflectionTint = vec3<f32>(0.92, 0.96, 1.0); // Cool sky reflection\n let reflectionStrength = fresnel * 0.6; // Strong edge reflections\n\n // Mix in reflection tint at edges\n color = mix(color, color * reflectionTint, reflectionStrength);\n\n // Add realistic glass shine - brighter at edges where light reflects\n let glassShine = fresnel * 0.12;\n color += glassShine;\n\n // Slight desaturation at edges (glass reflects environment, not just color)\n let edgeDesaturation = fresnel * 0.25;\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n color = mix(color, vec3<f32>(gray), edgeDesaturation);\n\n // Make glass more transparent (reduce opacity by 30%)\n finalAlpha = finalAlpha * 0.7;\n }\n\n // Exposure adjustment - darken overall\n color *= 0.85;\n\n // Contrast enhancement\n color = (color - 0.5) * 1.15 + 0.5;\n color = max(color, vec3<f32>(0.0));\n\n // Saturation boost - stronger for colored surfaces, less for whites\n let gray = dot(color, vec3<f32>(0.299, 0.587, 0.114));\n let satBoost = mix(1.4, 1.1, isWhiteish); // More saturation for colored surfaces\n color = mix(vec3<f32>(gray), color, satBoost);\n\n // ACES filmic tone mapping\n let a = 2.51;\n let b = 0.03;\n let c = 2.43;\n let d = 0.59;\n let e = 0.14;\n color = clamp((color * (a * color + b)) / (color * (c * color + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));\n\n // Subtle edge enhancement using screen-space derivatives\n let depthGradient = length(vec2<f32>(\n dpdx(input.viewPos.z),\n dpdy(input.viewPos.z)\n ));\n let normalGradient = length(vec2<f32>(\n length(dpdx(input.normal)),\n length(dpdy(input.normal))\n ));\n\n if (uniforms.flags.z == 1u) {\n // Threshold filters subtle normal discontinuities at internal\n // triangle edges between coplanar entities in the same batch.\n let edgeFactor = smoothstep(0.02, 0.12, depthGradient * 10.0 + normalGradient * 5.0);\n let edgeIntensity = f32(uniforms.flags.w) / 1000.0;\n let edgeDarkenStrength = clamp(0.25 * edgeIntensity, 0.0, 0.85);\n let edgeDarken = mix(1.0, 1.0 - edgeDarkenStrength, edgeFactor);\n color *= edgeDarken;\n }\n\n // Gamma correction\n color = pow(color, vec3<f32>(1.0 / 2.2));\n\n var out: FragmentOutput;\n out.color = vec4<f32>(color, finalAlpha);\n out.objectIdEncoded = encodeId24(input.entityId);\n return out;\n }\n ";
7
7
  //# sourceMappingURL=main.wgsl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,iqVAmPtB,CAAC"}
1
+ {"version":3,"file":"main.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,mzVAsPtB,CAAC"}
@@ -94,12 +94,15 @@ export const mainShaderSource = `
94
94
 
95
95
  @fragment
96
96
  fn fs_main(input: VertexOutput) -> FragmentOutput {
97
- // Section plane clipping - discard fragments ABOVE the plane
98
- // For Down axis (normal +Y), keeps everything below cut height (look down into building)
99
- if (uniforms.flags.y == 1u) {
97
+ // Section plane clipping - discard fragments ABOVE the plane.
98
+ // flags.y packs two bits: bit 0 = enabled, bit 1 = flipped.
99
+ let sectionEnabled = (uniforms.flags.y & 1u) == 1u;
100
+ if (sectionEnabled) {
100
101
  let planeNormal = uniforms.sectionPlane.xyz;
101
102
  let planeDistance = uniforms.sectionPlane.w;
102
- let distToPlane = dot(input.worldPos, planeNormal) - planeDistance;
103
+ let flipped = (uniforms.flags.y & 2u) == 2u;
104
+ let side = select(1.0, -1.0, flipped);
105
+ let distToPlane = (dot(input.worldPos, planeNormal) - planeDistance) * side;
103
106
  if (distToPlane > 0.0) {
104
107
  discard;
105
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmPzB,CAAC"}
1
+ {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsPzB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { MeshData } from '@ifc-lite/geometry';
2
- import type { Ray, Vec3, Intersection } from './raycaster';
2
+ import type { Ray, Vec3, Intersection } from './raycaster.js';
3
3
  export declare enum SnapType {
4
4
  VERTEX = "vertex",
5
5
  EDGE = "edge",
@@ -75,14 +75,6 @@ export declare class SnapDetector {
75
75
  * Detect if position is at a corner (vertex with multiple edges)
76
76
  */
77
77
  private detectCorner;
78
- /**
79
- * Get closest point on edge segment with parameter t (0-1)
80
- */
81
- private closestPointOnEdgeWithT;
82
- /**
83
- * Check if two vectors are approximately equal
84
- */
85
- private vecEquals;
86
78
  /**
87
79
  * Get or compute geometry cache for a mesh
88
80
  */
@@ -107,13 +99,5 @@ export declare class SnapDetector {
107
99
  * Select best snap target based on confidence and priority
108
100
  */
109
101
  private getBestSnapTarget;
110
- /**
111
- * Convert screen-space radius to world-space radius
112
- */
113
- private screenToWorldRadius;
114
- /**
115
- * Vector utilities
116
- */
117
- private distance;
118
102
  }
119
103
  //# sourceMappingURL=snap-detector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"snap-detector.d.ts","sourceRoot":"","sources":["../src/snap-detector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snap-detector.d.ts","sourceRoot":"","sources":["../src/snap-detector.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- import { Raycaster } from './raycaster';
1
+ import { Raycaster } from './raycaster.js';
2
+ import { distance, vecEquals, closestPointOnEdgeWithT, screenToWorldRadius } from './snap-geometry-utils.js';
3
+ import { buildGeometryCache } from './snap-geometry-cache.js';
2
4
  export var SnapType;
3
5
  (function (SnapType) {
4
6
  SnapType["VERTEX"] = "vertex";
@@ -51,8 +53,8 @@ export class SnapDetector {
51
53
  }
52
54
  const targets = [];
53
55
  // Calculate world-space snap radius based on screen-space radius and distance
54
- const distanceToCamera = this.distance(camera.position, intersection.point);
55
- const worldSnapRadius = this.screenToWorldRadius(opts.screenSnapRadius, distanceToCamera, camera.fov, screenHeight);
56
+ const distanceToCamera = distance(camera.position, intersection.point);
57
+ const worldSnapRadius = screenToWorldRadius(opts.screenSnapRadius, distanceToCamera, camera.fov, screenHeight);
56
58
  // Only check the intersected mesh for snap targets (performance optimization)
57
59
  // Checking all meshes was causing severe framerate drops with large models
58
60
  const intersectedMesh = meshes[intersection.meshIndex];
@@ -94,8 +96,8 @@ export class SnapDetector {
94
96
  },
95
97
  };
96
98
  }
97
- const distanceToCamera = this.distance(camera.position, intersection.point);
98
- const worldSnapRadius = this.screenToWorldRadius(opts.screenSnapRadius, distanceToCamera, camera.fov, screenHeight);
99
+ const distanceToCamera = distance(camera.position, intersection.point);
100
+ const worldSnapRadius = screenToWorldRadius(opts.screenSnapRadius, distanceToCamera, camera.fov, screenHeight);
99
101
  const intersectedMesh = meshes[intersection.meshIndex];
100
102
  if (!intersectedMesh) {
101
103
  return {
@@ -165,7 +167,7 @@ export class SnapDetector {
165
167
  // Find all nearby edges (filtered for visibility)
166
168
  const nearbyEdges = [];
167
169
  for (const edge of cache.edges) {
168
- const result = this.closestPointOnEdgeWithT(intersection.point, edge.v0, edge.v1);
170
+ const result = closestPointOnEdgeWithT(intersection.point, edge.v0, edge.v1);
169
171
  if (result.distance < edgeRadius) {
170
172
  // Visibility check: edge should be on front-facing side
171
173
  // Compute vector from intersection point to edge closest point
@@ -272,7 +274,7 @@ export class SnapDetector {
272
274
  }
273
275
  const { v0, v1 } = currentLock.edge;
274
276
  // Project point onto the locked edge
275
- const result = this.closestPointOnEdgeWithT(point, v0, v1);
277
+ const result = closestPointOnEdgeWithT(point, v0, v1);
276
278
  // Calculate perpendicular distance (distance from point to edge line)
277
279
  const perpDistance = result.distance;
278
280
  // Calculate escape threshold based on lock strength
@@ -298,8 +300,8 @@ export class SnapDetector {
298
300
  // Check for corner at current position
299
301
  const cornerRadius = worldSnapRadius * MAGNETIC_CONFIG.EDGE_ATTRACTION_MULTIPLIER * MAGNETIC_CONFIG.CORNER_ATTRACTION_MULTIPLIER;
300
302
  // Find the matching edge in cache to get proper index
301
- let matchingEdge = cache.edges.find(e => (this.vecEquals(e.v0, v0) && this.vecEquals(e.v1, v1)) ||
302
- (this.vecEquals(e.v0, v1) && this.vecEquals(e.v1, v0)));
303
+ let matchingEdge = cache.edges.find(e => (vecEquals(e.v0, v0) && vecEquals(e.v1, v1)) ||
304
+ (vecEquals(e.v0, v1) && vecEquals(e.v1, v0)));
303
305
  const edgeForCorner = matchingEdge || { v0, v1, index: -1 };
304
306
  const cornerInfo = this.detectCorner(edgeForCorner, edgeT, cache, cornerRadius, point);
305
307
  // Calculate snap position (on the edge)
@@ -366,7 +368,7 @@ export class SnapDetector {
366
368
  // Get valence from cache
367
369
  const valence = cache.vertexValence.get(vertexKey) || 0;
368
370
  // Also check distance to vertex
369
- const distToVertex = this.distance(point, vertex);
371
+ const distToVertex = distance(point, vertex);
370
372
  const isCloseEnough = distToVertex < radius;
371
373
  return {
372
374
  isCorner: isCloseEnough && valence >= MAGNETIC_CONFIG.MIN_CORNER_VALENCE,
@@ -374,39 +376,6 @@ export class SnapDetector {
374
376
  vertex,
375
377
  };
376
378
  }
377
- /**
378
- * Get closest point on edge segment with parameter t (0-1)
379
- */
380
- closestPointOnEdgeWithT(point, v0, v1) {
381
- const dx = v1.x - v0.x;
382
- const dy = v1.y - v0.y;
383
- const dz = v1.z - v0.z;
384
- const lengthSq = dx * dx + dy * dy + dz * dz;
385
- if (lengthSq < 0.0000001) {
386
- // Degenerate edge
387
- return { point: v0, distance: this.distance(point, v0), t: 0 };
388
- }
389
- // Project point onto line
390
- const t = Math.max(0, Math.min(1, ((point.x - v0.x) * dx + (point.y - v0.y) * dy + (point.z - v0.z) * dz) / lengthSq));
391
- const closest = {
392
- x: v0.x + dx * t,
393
- y: v0.y + dy * t,
394
- z: v0.z + dz * t,
395
- };
396
- return {
397
- point: closest,
398
- distance: this.distance(point, closest),
399
- t,
400
- };
401
- }
402
- /**
403
- * Check if two vectors are approximately equal
404
- */
405
- vecEquals(a, b, epsilon = 0.0001) {
406
- return (Math.abs(a.x - b.x) < epsilon &&
407
- Math.abs(a.y - b.y) < epsilon &&
408
- Math.abs(a.z - b.z) < epsilon);
409
- }
410
379
  /**
411
380
  * Get or compute geometry cache for a mesh
412
381
  */
@@ -415,154 +384,7 @@ export class SnapDetector {
415
384
  if (cached) {
416
385
  return cached;
417
386
  }
418
- // Compute and cache vertices
419
- const positions = mesh.positions;
420
- // Validate input
421
- if (!positions || positions.length === 0) {
422
- const emptyCache = {
423
- vertices: [],
424
- edges: [],
425
- vertexValence: new Map(),
426
- vertexEdges: new Map(),
427
- };
428
- this.geometryCache.set(mesh.expressId, emptyCache);
429
- return emptyCache;
430
- }
431
- const vertexMap = new Map();
432
- for (let i = 0; i < positions.length; i += 3) {
433
- const vertex = {
434
- x: positions[i],
435
- y: positions[i + 1],
436
- z: positions[i + 2],
437
- };
438
- // Skip invalid vertices
439
- if (!isFinite(vertex.x) || !isFinite(vertex.y) || !isFinite(vertex.z)) {
440
- continue;
441
- }
442
- // Use reduced precision for deduplication
443
- const key = `${vertex.x.toFixed(4)}_${vertex.y.toFixed(4)}_${vertex.z.toFixed(4)}`;
444
- vertexMap.set(key, vertex);
445
- }
446
- const vertices = Array.from(vertexMap.values());
447
- // Compute and cache edges + vertex valence for corner detection
448
- // Filter out internal triangulation edges (diagonals) - only keep real model edges
449
- const edges = [];
450
- const vertexValence = new Map();
451
- const vertexEdges = new Map();
452
- const indices = mesh.indices;
453
- if (indices) {
454
- // First pass: collect edges and their adjacent triangle normals
455
- const edgeData = new Map();
456
- // Helper to compute triangle normal
457
- const computeTriangleNormal = (i) => {
458
- const i0 = indices[i] * 3;
459
- const i1 = indices[i + 1] * 3;
460
- const i2 = indices[i + 2] * 3;
461
- const ax = positions[i1] - positions[i0];
462
- const ay = positions[i1 + 1] - positions[i0 + 1];
463
- const az = positions[i1 + 2] - positions[i0 + 2];
464
- const bx = positions[i2] - positions[i0];
465
- const by = positions[i2 + 1] - positions[i0 + 1];
466
- const bz = positions[i2 + 2] - positions[i0 + 2];
467
- // Cross product
468
- const nx = ay * bz - az * by;
469
- const ny = az * bx - ax * bz;
470
- const nz = ax * by - ay * bx;
471
- // Normalize
472
- const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
473
- return len > 0 ? { x: nx / len, y: ny / len, z: nz / len } : { x: 0, y: 1, z: 0 };
474
- };
475
- for (let i = 0; i < indices.length; i += 3) {
476
- const triNormal = computeTriangleNormal(i);
477
- const triangleEdges = [
478
- [indices[i], indices[i + 1]],
479
- [indices[i + 1], indices[i + 2]],
480
- [indices[i + 2], indices[i]],
481
- ];
482
- for (const [idx0, idx1] of triangleEdges) {
483
- const i0 = idx0 * 3;
484
- const i1 = idx1 * 3;
485
- const v0 = {
486
- x: positions[i0],
487
- y: positions[i0 + 1],
488
- z: positions[i0 + 2],
489
- };
490
- const v1 = {
491
- x: positions[i1],
492
- y: positions[i1 + 1],
493
- z: positions[i1 + 2],
494
- };
495
- // Create canonical edge key (smaller index first)
496
- const key = idx0 < idx1 ? `${idx0}_${idx1}` : `${idx1}_${idx0}`;
497
- if (!edgeData.has(key)) {
498
- edgeData.set(key, { v0, v1, idx0, idx1, normals: [triNormal] });
499
- }
500
- else {
501
- const existing = edgeData.get(key);
502
- if (existing) {
503
- existing.normals.push(triNormal);
504
- }
505
- }
506
- }
507
- }
508
- // Second pass: filter to only real edges (boundary or crease edges)
509
- // Skip internal triangulation edges (shared by coplanar triangles)
510
- const COPLANAR_THRESHOLD = 0.98; // Dot product threshold for coplanar check
511
- for (const [key, data] of edgeData) {
512
- const { v0, v1, normals } = data;
513
- // Boundary edge: only one triangle uses it - always a real edge
514
- if (normals.length === 1) {
515
- const edgeIndex = edges.length;
516
- edges.push({ v0, v1, index: edgeIndex });
517
- // Track vertex valence
518
- const v0Key = `${v0.x.toFixed(4)}_${v0.y.toFixed(4)}_${v0.z.toFixed(4)}`;
519
- const v1Key = `${v1.x.toFixed(4)}_${v1.y.toFixed(4)}_${v1.z.toFixed(4)}`;
520
- vertexValence.set(v0Key, (vertexValence.get(v0Key) || 0) + 1);
521
- vertexValence.set(v1Key, (vertexValence.get(v1Key) || 0) + 1);
522
- if (!vertexEdges.has(v0Key))
523
- vertexEdges.set(v0Key, []);
524
- if (!vertexEdges.has(v1Key))
525
- vertexEdges.set(v1Key, []);
526
- const v0Edges = vertexEdges.get(v0Key);
527
- const v1Edges = vertexEdges.get(v1Key);
528
- if (v0Edges)
529
- v0Edges.push(edgeIndex);
530
- if (v1Edges)
531
- v1Edges.push(edgeIndex);
532
- continue;
533
- }
534
- // Shared edge: check if triangles are coplanar (internal triangulation edge)
535
- if (normals.length >= 2) {
536
- const n1 = normals[0];
537
- const n2 = normals[1];
538
- const dot = Math.abs(n1.x * n2.x + n1.y * n2.y + n1.z * n2.z);
539
- // If normals are nearly parallel, triangles are coplanar - skip this edge
540
- // (it's an internal triangulation diagonal, not a real model edge)
541
- if (dot > COPLANAR_THRESHOLD) {
542
- continue; // Skip internal edge
543
- }
544
- // Crease edge: triangles meet at an angle - this is a real edge
545
- const edgeIndex = edges.length;
546
- edges.push({ v0, v1, index: edgeIndex });
547
- // Track vertex valence
548
- const v0Key = `${v0.x.toFixed(4)}_${v0.y.toFixed(4)}_${v0.z.toFixed(4)}`;
549
- const v1Key = `${v1.x.toFixed(4)}_${v1.y.toFixed(4)}_${v1.z.toFixed(4)}`;
550
- vertexValence.set(v0Key, (vertexValence.get(v0Key) || 0) + 1);
551
- vertexValence.set(v1Key, (vertexValence.get(v1Key) || 0) + 1);
552
- if (!vertexEdges.has(v0Key))
553
- vertexEdges.set(v0Key, []);
554
- if (!vertexEdges.has(v1Key))
555
- vertexEdges.set(v1Key, []);
556
- const v0CreaseEdges = vertexEdges.get(v0Key);
557
- const v1CreaseEdges = vertexEdges.get(v1Key);
558
- if (v0CreaseEdges)
559
- v0CreaseEdges.push(edgeIndex);
560
- if (v1CreaseEdges)
561
- v1CreaseEdges.push(edgeIndex);
562
- }
563
- }
564
- }
565
- const cache = { vertices, edges, vertexValence, vertexEdges };
387
+ const cache = buildGeometryCache(mesh);
566
388
  this.geometryCache.set(mesh.expressId, cache);
567
389
  return cache;
568
390
  }
@@ -574,7 +396,7 @@ export class SnapDetector {
574
396
  const cache = this.getGeometryCache(mesh);
575
397
  // Find vertices within radius - ONLY when VERY close for smooth edge sliding
576
398
  for (const vertex of cache.vertices) {
577
- const dist = this.distance(vertex, point);
399
+ const dist = distance(vertex, point);
578
400
  // Only snap to vertices when within 20% of snap radius (very tight) to avoid sticky behavior
579
401
  if (dist < radius * 0.2) {
580
402
  targets.push({
@@ -598,7 +420,7 @@ export class SnapDetector {
598
420
  // Find edges near point using cached data
599
421
  for (const edge of cache.edges) {
600
422
  const closestPoint = this.raycaster.closestPointOnSegment(point, edge.v0, edge.v1);
601
- const dist = this.distance(closestPoint, point);
423
+ const dist = distance(closestPoint, point);
602
424
  if (dist < edgeRadius) {
603
425
  // Edge snap - ABSOLUTE HIGHEST priority for smooth sliding along edges
604
426
  // Maximum confidence ensures edges ALWAYS win over vertices/faces
@@ -661,7 +483,7 @@ export class SnapDetector {
661
483
  y: (v0.y + v1.y + v2.y) / 3,
662
484
  z: (v0.z + v1.z + v2.z) / 3,
663
485
  };
664
- const dist = this.distance(center, intersection.point);
486
+ const dist = distance(center, intersection.point);
665
487
  if (dist < radius) {
666
488
  targets.push({
667
489
  type: SnapType.FACE_CENTER,
@@ -697,24 +519,5 @@ export class SnapDetector {
697
519
  });
698
520
  return targets[0];
699
521
  }
700
- /**
701
- * Convert screen-space radius to world-space radius
702
- */
703
- screenToWorldRadius(screenRadius, distance, fov, screenHeight) {
704
- // Calculate world height at distance
705
- const fovRadians = (fov * Math.PI) / 180;
706
- const worldHeight = 2 * distance * Math.tan(fovRadians / 2);
707
- // Convert screen pixels to world units
708
- return (screenRadius / screenHeight) * worldHeight;
709
- }
710
- /**
711
- * Vector utilities
712
- */
713
- distance(a, b) {
714
- const dx = a.x - b.x;
715
- const dy = a.y - b.y;
716
- const dz = a.z - b.z;
717
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
718
- }
719
522
  }
720
523
  //# sourceMappingURL=snap-detector.js.map