PageSourceSearch

https://pixlane.media/assets/js/three-d-registry.js?v=2026092402&runtime=bf3904c1cd16

js pixlane.media collected 2026-10-09 03:20:50 UTC 80,820 bytes, 1,232 lines download raw bytes

1import * as base from "./xray-registry.js?v=2026092402&runtime=bf3904c1cd16";
2
3const privacy = "Supported file contents stay on this device while the selected tool runs; site analytics and the assistant are separate.";
4const researchBoundary = "Research preview only. Photo processing stays unavailable until the geometry model passes Pixlane's accuracy, compatibility and reliability checks.";
5
6// Keep result handling, category totals, schema eligibility, and UI labels on
7// one explicit contract. A viewer is not an export tool, and a research route
8// is not an available WebApplication.
9export const THREE_D_RESULT_KINDS = Object.freeze(["viewer", "report", "export", "research"]);
10export const THREE_D_TOOL_STATES = Object.freeze(["functional", "research-preview"]);
11
12const THREE_D_INPUT_SCENES = Object.freeze({
13  "3d-model-viewer": "glb-sneaker-orbit-studio-v1",
14  "mesh-inspector": "mechanical-bracket-mesh-audit-v1",
15  "gaussian-splat-viewer": "mediterranean-scooter-splat-orbit-v2",
16  "point-cloud-viewer": "forest-trail-point-cloud-height-v1",
17  "obj-to-glb": "desk-lamp-obj-package-v1",
18  "stl-to-glb": "turbine-impeller-stl-material-v1",
19  "glb-optimizer": "arcade-robot-glb-size-review-v1",
20  "stl-repair": "planter-stl-print-repair-v1",
21  "svg-to-stl": "mountain-badge-svg-extrusion-v1",
22  "heightmap-to-stl": "twin-peaks-heightmap-terrain-tile-v1",
23  "depth-map-to-3d": "stone-arch-depth-surface-v1",
24  "photo-to-3d-geometry": "hiking-boot-photo-geometry-research-v1",
25  "gcode-viewer": "spiral-vase-gcode-layer-path-v1",
26  "3mf-viewer": "multi-part-rocket-3mf-buildplate-v1",
27  "gltf-validator": "sneaker-gltf-package-validation-v1",
28  "3mf-validator": "multi-part-rocket-3mf-package-validation-v1",
29  "3mf-to-stl": "multi-part-rocket-3mf-stl-conversion-v1",
30  "3mf-to-glb": "multi-part-rocket-3mf-glb-conversion-v1",
31  "mesh-simplifier": "mechanical-bracket-mesh-simplification-v1",
32  "stl-viewer": "turbine-impeller-stl-orbit-v1",
33  "glb-viewer": "arcade-robot-glb-orbit-v1",
34  "gltf-viewer": "sneaker-gltf-package-orbit-v1",
35  "obj-viewer": "desk-lamp-obj-package-orbit-v1",
36  "ply-viewer": "forest-trail-ply-orbit-v1",
37  "glb-to-stl": "arcade-robot-glb-stl-conversion-v1",
38  "obj-to-stl": "desk-lamp-obj-stl-conversion-v1",
39  "stl-to-obj": "turbine-impeller-stl-obj-conversion-v1",
40  "ply-to-stl": "forest-trail-ply-stl-conversion-v1",
41});
42
43function preview(slug, beforeLabel, afterLabel, note, beforeAlt, afterAlt, options = {}) {
44  return Object.freeze({
45    kind: "compare",
46    before: `preview-${slug}-before-v1.png`,
47    after: `preview-${slug}-after-v1.png`,
48    cardBefore: `preview-${slug}-before-card-v1.jpg`,
49    cardAfter: `preview-${slug}-after-card-v1.jpg`,
50    beforeLabel,
51    afterLabel,
52    note,
53    beforeAlt,
54    afterAlt,
55    fit: options.fit || "cover",
56    focalPoint: options.focalPoint || "50% 50%",
57    badge: options.badge || "Interactive example",
58    ogImage: options.ogImage || `preview-${slug}-after-v1.png`,
59  });
60}
61
62function orbitPreview(slug, label, formatLabel, note, alt, options = {}) {
63  const assetVersion = options.assetVersion || "v1";
64  return Object.freeze({
65    kind: "orbit",
66    poster: `preview-${slug}-orbit-${assetVersion}.png`,
67    cardPoster: `preview-${slug}-orbit-card-${assetVersion}.jpg`,
68    video: `preview-${slug}-orbit-card-${assetVersion}.webm`,
69    label,
70    formatLabel,
71    note,
72    alt,
73    fit: options.fit || "cover",
74    focalPoint: options.focalPoint || "50% 50%",
75    badge: options.badge || "Open to explore",
76    ogImage: options.ogImage || `preview-${slug}-orbit-og-${assetVersion}.jpg`,
77  });
78}
79
80function setting(id, kind, label, defaultValue, options = {}) {
81  return Object.freeze({ id, kind, label, defaultValue, ...options });
82}
83
84function formatContract(input, output, notes) {
85  return Object.freeze({ input: Object.freeze(input), output: Object.freeze(output), notes });
86}
87
88function standardFaq(name, formatAnswer, purposeAnswer, limitationAnswer) {
89  return [
90    { question: `What does ${name} do?`, answer: purposeAnswer },
91    { question: `Which files does ${name} support?`, answer: formatAnswer },
92    { question: "Is my 3D file uploaded?", answer: `${privacy} The selected file is not uploaded for the supported processing workflow.` },
93    { question: `What should I review after using ${name}?`, answer: limitationAnswer },
94  ];
95}
96
97function standardViewerSettings(defaultMode = "solid", defaultBackgroun
97d = "studio") {
98  return [
99    setting("viewMode", "select", "Display mode", defaultMode, { options: [{ label: "Solid", value: "solid" }, { label: "Wireframe", value: "wireframe" }, { label: "Solid with edges", value: "edges" }] }),
100    setting("background", "select", "Background", defaultBackground, { options: [{ label: "Studio", value: "studio" }, { label: "Dark", value: "dark" }, { label: "Transparent", value: "transparent" }] }),
101    setting("showGrid", "toggle", "Show ground grid", true),
102  ];
103}
104
105function threeDTool(definition) {
106  const inputSceneId = THREE_D_INPUT_SCENES[definition.slug];
107  if (!inputSceneId) throw new Error(`Missing unique 3D input scene for ${definition.slug}`);
108  const tool = {
109    category: "three-d",
110    badgeKey: "new",
111    iconKey: "cube",
112    supportText: privacy,
113    availability: "available",
114    toolState: "functional",
115    countsTowardCategory: true,
116    webApplicationSchema: true,
117    inputSceneId,
118    ...definition,
119  };
120  if (!THREE_D_RESULT_KINDS.includes(tool.resultKind)) throw new Error(`Invalid 3D result kind for ${tool.slug}`);
121  if (!THREE_D_TOOL_STATES.includes(tool.toolState)) throw new Error(`Invalid 3D tool state for ${tool.slug}`);
122  if (!tool.toolTypeLabel || !tool.ctaLabel || !tool.resultTitle || !tool.resultDescription) {
123    throw new Error(`Incomplete 3D result metadata for ${tool.slug}`);
124  }
125  if (tool.resultKind === "research") {
126    if (tool.toolState !== "research-preview" || tool.countsTowardCategory || tool.webApplicationSchema) {
127      throw new Error(`Research preview metadata must remain non-countable and non-WebApplication for ${tool.slug}`);
128    }
129  } else if (tool.toolState !== "functional" || !tool.countsTowardCategory || !tool.webApplicationSchema) {
130    throw new Error(`Functional 3D metadata must remain countable and WebApplication-eligible for ${tool.slug}`);
131  }
132  return Object.freeze(tool);
133}
134
135export const THREE_D_TOOLS = Object.freeze([
136  threeDTool({
137    group: "View & Inspect",
138    name: "3D Model Viewer",
139    slug: "3d-model-viewer",
140    resultKind: "viewer",
141    toolTypeLabel: "Interactive 3D viewer",
142    ctaLabel: "Open 3D model",
143    resultTitle: "Interactive model view",
144    resultDescription: "Orbit, zoom and inspect the loaded model with its reported dimensions and display mode. This viewer does not create an export.",
145    shortDescription: "Open GLB, glTF, OBJ, STL and PLY models online, orbit around them and inspect dimensions.",
146    seoTitle: "3D Model Viewer Online — View GLB, OBJ & STL | Pixlane",
147    seoDescription: "Open and inspect GLB, glTF, OBJ, STL and PLY models online with orbit controls, dimensions and display modes.",
148    preview: preview(
149      "3d-model-viewer", "Model file", "Interactive view",
150      "Open a detailed sneaker model, frame it automatically and inspect it from every side.",
151      "A detailed sneaker model prepared for the online 3D viewer",
152      "The same sneaker model framed in an interactive studio view",
153      { badge: "Orbit and inspect" },
154    ),
155    accept: ".glb,.gltf,.obj,.mtl,.stl,.ply,.bin,.png,.jpg,.jpeg,.webp,.zip,model/gltf-binary,model/gltf+json,model/obj,model/stl,application/octet-stream",
156    acceptText: "GLB, glTF/OBJ bundle, STL or PLY",
157    formats: formatContract(["GLB", "glTF", "OBJ", "local BIN/MTL/textures", "STL", "PLY", "ZIP bundle"], [], "Select a matching folder or safe ZIP bundle for glTF buffers, OBJ materials and local textures. Remote references are blocked and a missing selected resource is named instead of rendering a partial model."),
158    settings: [
159      setting("viewMode", "select", "Display mode", "solid", { options: [{ label: "Solid", value: "solid" }, { label: "Wireframe", value: "wireframe" }, { label: "Solid with edges", value: "edges" }] }),
160      setting("background", "select", "Background", "studio", { options: [{ label: "Studio", value: "studio" }, { label: "Dark", value: "dark" }, { label: "Transparent", value: "transparent" }] }),
161      setting("showGrid", "toggle", "Show ground grid", true),
162    ],
163    relatedToolSlugs: ["mesh-inspector", "glb-optimizer", "3mf-viewer"],
164    faq: standardFaq(
165      "3D Model Viewer",
166      "GLB, glTF, OBJ, STL and PLY models are supported. Select local BIN, MTL and image files with the model—or use a safe ZIP bundle—when the model declares relative resources; remote references are blocked.",
167      "It opens a model in an interactive scene so you can orbit, zoom, change display mode and review its overall dimensions.",
168      "Check the reported dimensions, material appearance and missing-file notices before relying on the view for a downstream workflow.",
169    ),
170  }),
171  threeDTool({
172    group: "View & Inspect",
173    name: "Mesh Inspector",
174    slug: "mesh-inspector",
175    resultKind: "report",
176    toolTypeLabel: "Mesh inspection report",
177    ctaLabel: "Inspect mesh",
178    resultTitle: "Mesh inspection report",
179    resultDescription: "Review geometry counts, bounds and detected conditions. A JSON inspection report is available only when the report is created.",
180    shortDescription: "Inspect triangle count, dimensions, open edges, duplicate points and surface direction in a 3D mesh.",
181    seoTitle: "Mesh Inspector Online — Analyze OBJ, STL & PLY | Pixlane",
182    seoDescription: "Inspect OBJ, STL, PLY and GLB mesh dimensions, triangle count, open edges and common geometry issues online.",
183    preview: preview(
184      "mesh-inspector", "Mechanical bracket", "Issue overlay",
185      "Use a mechanical bracket with small mesh defects to make open edges and surface direction easy to spot.",
186      "A mechanical bracket mesh containing carefully placed geometry issues",
187      "The same bracket with open edges and surface direction highlighted",
188      { badge: "Geometry checks" },
189    ),
190    accept: ".obj,.stl,.ply,.glb,model/obj,model/stl,model/gltf-binary,application/octet-stream",
191    acceptText: "OBJ, STL, PLY or GLB mesh",
192    formats: formatContract(["OBJ", "STL", "PLY", "GLB"], ["JSON report"], "Inspection does not modify the source file."),
193    settings: [
194      setting("units", "select", "Treat each coordinate as", "mm", { options: [{ label: "Millimeters", value: "mm" }, { label: "Centimeters", value: "cm" }, { label: "Meters", value: "m" }, { label: "Inches", value: "in" }] }),
195      setting("showOpenEdges", "toggle", "Highlight open edges", true),
196      setting("showDirection", "toggle", "Show surface direction", true),
197    ],
198    relatedToolSlugs: ["stl-repair", "3d-model-viewer", "glb-optimizer"],
199    faq: standardFaq(
200      "Mesh Inspector",
201      "OBJ, STL, PLY and GLB mesh files can be inspected; scene-only objects without triangle geometry are ignored.",
202      "It summarizes mesh size and complexity, labels raw coordinate dimensions with the unit you choose, then highlights common geometry conditions that deserve review.",
203      "Automated checks are a first pass. Confirm scale, print tolerances, intended openings and visual appearance in the destination application.",
204    ),
205  }),
206  threeDTool({
207    group: "View & Inspect",
208    name: "Gaussian Splat Viewer",
209    slug: "gaussian-splat-viewer",
210    resultKind: "viewer",
211    toolTypeLabel: "Interactive Gaussian splat viewer",
212    ctaLabel: "Open your PLY / SPLAT file",
213    resultTitle: "Interactive splat scene",
214    resultDescription: "Open an existing PLY or SPLAT scene, then orbit, pan and zoom while reviewing its detail, splat size and background. This viewer does not generate a new scene or splat file.",
215    shortDescription: "Open and explore existing PLY and SPLAT scenes directly in your browser.",
216    seoTitle: "Gaussian Splat Viewer — Open Existing PLY & SPLAT Scenes | Pixlane",
217    seoDescription: "Open existing Gaussian splat PLY and SPLAT scenes online with orbit, pan, zoom, detail and background controls.",
218    preview: orbitPreview(
219      "gaussian-splat-viewer", "Alley orbit", "PLY / SPLAT",
220      "Orbit the same prepared synthetic Mediterranean alley SPLAT you can load in the viewer, with fine foliage and thin scooter parts revealing detail changes.",
221      "A prepared synthetic Gaussian SPLAT scene featuring a coral-colored scooter in a flower-filled Mediterranean alley with a deep stone arch, shown during orbit.",
222      { badge: "Explore in orbit", assetVersion: "v2", focalPoint: "50% 56%" },
223    ),
224    accept: ".ply,.splat,application/octet-stream",
225    acceptText: "Gaussian splat PLY or SPLAT",
226    formats: formatContract(["Gaussian splat PLY", "SPLAT"], [], "This viewer opens existing PLY and SPLAT scenes; it does not train, convert photos or generate a new splat scene."),
227    settings: [
228      setting("detail", "select", "View detail", "balanced", { options: [{ label: "Fast", value: "fast" }, { label: "Balanced", value: "balanced" }, { label: "Full", value: "full" }] }),
229      setting("pointScale", "range", "Splat size", 1, { min: 0.5, max: 2, step: 0.05 }),
230      setting("background", "select", "Background", "dark", { options: [{ label: "Dark", value: "dark" }, { label: "Light", value: "light" }, { label: "Transparent", value: "transparent" }] }),
231    ],
232    relatedToolSlugs: ["point-cloud-viewer", "3d-model-viewer", "photo-to-3d-geometry"],
233    faq: standardFaq(
234      "Gaussian Splat Viewer",
235      "Existing Gaussian splat scenes stored as supported PLY or SPLAT files can be opened.",
236      "It lets you orbit around an existing splat scene and tune viewing detail, splat size and background.",
237      "Scene quality, scale and viewpoints come from the source file. This tool does not create missing geometry, convert photos or train a new scene.",
238    ),
239  }),
240  threeDTool({
241    group: "View & Inspect",
242    name: "Point Cloud Viewer",
243    slug: "point-cloud-viewer",
244    resultKind: "viewer",
245    toolTypeLabel: "Interactive point-cloud viewer",
246    ctaLabel: "Open point cloud",
247    resultTitle: "Interactive point-cloud view",
248    resultDescription: "Orbit the loaded points and review source color, height or intensity where available. This viewer does not create an export.",
249    shortDescription: "View PLY, XYZ and PTS point clouds online with height, color and intensity display options.",
250    seoTitle: "Point Cloud Viewer Online — Open PLY, XYZ & PTS | Pixlane",
251    seoDescription: "Open PLY, XYZ and PTS point clouds online, orbit around the data and compare color, height and intensity views.",
252    preview: preview(
253      "point-cloud-viewer", "Forest scan", "Height view",
254      "A forest trail scan uses a varied canopy and ground profile to make height coloring immediately useful.",
255      "A dense point cloud scan of a forest trail in its source colors",
256      "The same forest trail point cloud colored by height for easier inspection",
257      { badge: "Color by height" },
258    ),
259    accept: ".ply,.xyz,.pts,text/plain,application/octet-stream",
260    acceptText: "PLY, XYZ or PTS point cloud",
261    formats: formatContract(["PLY", "XYZ", "PTS"], [], "Text point clouds must contain at least X, Y and Z columns. Seven-column PTS rows are read as X Y Z intensity R G B."),
262    settings: [
263      setting("colorMode", "select", "Color mode", "source", { options: [{ label: "Source color", value: "source" }, { label: "Height", value: "height" }, { label: "Intensity", value: "intensity" }] }),
264      setting("pointSize", "range", "Point size", 1.4, { min: 0.5, max: 5, step: 0.1 }),
265      setting("showBounds", "toggle", "Show bounds", false),
266    ],
267    relatedToolSlugs: ["gaussian-splat-viewer", "mesh-inspector", "3d-model-viewer"],
268    faq: standardFaq(
269      "Point Cloud Viewer",
270      "PLY, XYZ and PTS files with XYZ coordinates are supported; source color and intensity are shown when those values are present.",
271      "It displays point data in an orbitable scene and can color the points by source color, height or available intensity.",
272      "Confirm coordinate units and column order. Very large files can take longer to open, so start with a smaller crop when you only need to inspect one area.",
273    ),
274  }),
275  threeDTool({
276    group: "Convert & Create",
277    name: "OBJ to GLB Converter",
278    slug: "obj-to-glb",
279    resultKind: "export",
280    toolTypeLabel: "3D format converter",
281    ctaLabel: "Convert to GLB",
282    resultTitle: "GLB conversion ready",
283    resultDescription: "Review the converted model and conversion summary before downloading the produced GLB file.",
284    shortDescription: "Convert OBJ geometry to a compact GLB file online with scale and axis controls.",
285    seoTitle: "OBJ to GLB Converter Online — Free 3D Conversion | Pixlane",
286    seoDescription: "Convert OBJ models to compact GLB files online, review scale and orientation, and download the result from your device.",
287    preview: preview(
288      "obj-to-glb", "OBJ geometry", "GLB model",
289      "A desk lamp with curved and hard-surface parts makes scale, orientation and material defaults easy to verify.",
290      "A detailed desk lamp represented as OBJ geometry",
291      "The same desk lamp packed into a GLB model with a neutral material",
292      { badge: "Single-file result" },
293    ),
294    accept: ".obj,.mtl,.png,.jpg,.jpeg,.webp,.zip,model/obj,text/plain,application/octet-stream",
295    acceptText: "OBJ, folder or ZIP bundle",
296    formats: formatContract(["OBJ", "MTL", "local textures", "ZIP bundle"], ["GLB"], "A selected OBJ bundle can include local MTL files and image textures. Remote references are blocked and every missing local resource is named before a result is shown."),
297    settings: [
298      setting("upAxis", "select", "Source up axis", "y", { options: [{ label: "Y up", value: "y" }, { label: "Z up", value: "z" }] }),
299      setting("scale", "number", "Scale multiplier", 1, { min: 0.0001, max: 10000, step: 0.01 }),
300      setting("center", "toggle", "Center model at origin", true),
301    ],
302    relatedToolSlugs: ["3d-model-viewer", "mesh-inspector", "glb-optimizer"],
303    faq: standardFaq(
304      "OBJ to GLB Converter",
305      "An OBJ file, selected folder or ZIP bundle can be converted. Local MTL and image texture files are resolved only from the selected bundle; remote URLs are blocked.",
306      "It packages supported OBJ geometry, local materials and supported textures into a single GLB file with optional centering, scale and up-a
306xis correction.",
307      "Compare orientation, scale, material appearance and texture notices in the preview before using the converted file.",
308    ),
309  }),
310  threeDTool({
311    group: "Convert & Create",
312    name: "STL to GLB Converter",
313    slug: "stl-to-glb",
314    resultKind: "export",
315    toolTypeLabel: "3D format converter",
316    ctaLabel: "Convert to GLB",
317    resultTitle: "GLB conversion ready",
318    resultDescription: "Review the converted mesh, unit scale and preview material before downloading the produced GLB file.",
319    shortDescription: "Convert binary or text STL meshes to GLB online and add a clean preview material.",
320    seoTitle: "STL to GLB Converter Online — Free & Private | Pixlane",
321    seoDescription: "Convert binary or text STL meshes to GLB online, set units and material color, then review the model before download.",
322    preview: preview(
323      "stl-to-glb", "STL mesh", "GLB preview",
324      "A turbine impeller exposes thin blades and deep curves so orientation and surface direction are easy to inspect.",
325      "A turbine impeller stored as a plain STL mesh",
326      "The same turbine impeller converted to GLB with a clear studio material",
327      { badge: "Preview before download" },
328    ),
329    accept: ".stl,model/stl,application/sla,application/octet-stream",
330    acceptText: "binary or text STL",
331    formats: formatContract(["STL"], ["GLB"], "STL stores triangle geometry but normally contains no material or unit metadata."),
332    settings: [
333      setting("units", "select", "STL units", "mm", { options: [{ label: "Millimeters", value: "mm" }, { label: "Centimeters", value: "cm" }, { label: "Meters", value: "m" }, { label: "Inches", value: "in" }] }),
334      setting("materialColor", "color", "Preview material", "#7c9cff"),
335      setting("center", "toggle", "Center model at origin", true),
336    ],
337    relatedToolSlugs: ["3d-model-viewer", "stl-repair", "glb-optimizer"],
338    faq: standardFaq(
339      "STL to GLB Converter",
340      "Binary and text STL meshes are supported and exported as GLB triangle geometry.",
341      "It converts STL geometry into GLB, applies the chosen unit scale and adds a neutral material for consistent viewing.",
342      "STL files usually do not declare units. Verify the dimensions and surface direction shown before downloading the GLB.",
343    ),
344  }),
345  threeDTool({
346    group: "Convert & Create",
347    name: "GLB Compressor & Optimizer",
348    slug: "glb-optimizer",
349    resultKind: "export",
350    toolTypeLabel: "GLB compression and optimization export",
351    ctaLabel: "Optimize GLB locally",
352    resultTitle: "GLB optimization ready",
353    resultDescription: "Review the optimized model, measured before/after size, preserved scene counts and applied operations before downloading. A smaller file is never promised when its source is already efficient.",
354    shortDescription: "Clean a GLB losslessly or apply an explicit balanced 16-bit geometry quantization, then compare the measured local result.",
355    seoTitle: "GLB Compressor & Optimizer Online — Local 3D Cleanup | Pixlane",
356    seoDescription: "Optimize GLB models online with local lossless cleanup or explicit balanced geometry quantization, then review measured size and scene details before download.",
357    preview: preview(
358      "glb-optimizer", "Original GLB", "Repacked GLB",
359      "A detailed arcade robot contains repeated mesh records that make cleanup, optional balanced geometry quantization and measured size comparison easy to inspect.",
360      "A detailed arcade robot GLB before geometry cleanup",
361      "The same arcade robot after supported cleanup and repacking",
362      { badge: "Measured output" },
363    ),
364    accept: ".glb,model/gltf-binary,application/octet-stream",
365    acceptText: "GLB",
366    formats: formatContract(["GLB"], ["GLB"], "Lossless Cleanup repacks supported scene data. Balanced Web quantizes eligible static mesh positions and normals to normalized 16-bit values; skins, morph targets and animated mesh transforms are skipped rather than altered. Unsupported geometry and texture compression are not claimed."),
367    settings: [
368      setting("preset", "select", "Optimization preset", "lossless", { options: [{ label: "Lossless Cleanup", value: "lossless" }, { label: "Balanced Web (16-bit geometry)", value: "balanced" }] }),
369      setting("compactMesh", "toggle", "Merge fully matching mesh records", true),
370      setting("keepNames", "toggle", "Keep object names", true),
371    ],
372    relatedToolSlugs: ["3d-model-viewer", "mesh-inspector", "obj-to-glb"],
373    faq: standardFaq(
374      "GLB Compressor & Optimizer",
375      "Binary glTF .glb files are supported. The output remains a GLB file.",
376      "Lossless Cleanup repacks supported scene data and merges only fully matching attributes. Balanced Web uses explicit normalized 16-bit position and normal quantization for eligible static meshes; it skips skins, morph targets and animated mesh transforms instead of changing them.",
377      "Compare measured file size, dimensions, scene counts and applied operations before using the result. Unsupported geometry and texture compression are not claimed, and an already efficient source can become larger after a safe repack.",
378    ),
379  }),
380  threeDTool({
381    group: "Convert & Create",
382    name: "STL Repair & Printability Check",
383    slug: "stl-repair",
384    resultKind: "export",
385    toolTypeLabel: "STL cleanup and printability report",
386    ctaLabel: "Check and repair STL",
387    resultTitle: "STL cleanup and printability report",
388    resultDescription: "Review before-and-after issue counts and unresolved open or non-manifold edges before downloading the cleaned STL.",
389    shortDescription: "Clean up supported STL issues, then review duplicate points, degenerate faces, orientation and printability signals.",
390    seoTitle: "STL Repair & Printability Check Online | Pixlane",
391    seoDescription: "Clean up supported STL mesh issues online, review before-and-after issue counts and unresolved printability signals, then download the result.",
392    preview: preview(
393      "stl-repair", "Problem STL", "Repaired mesh",
394      "A ribbed planter contains controlled duplicate points and empty triangles while keeping a recognizable print-ready shape.",
395      "A ribbed planter STL with visible geometry issues marked for review",
396      "The same planter after supported duplicate and empty-triangle repairs",
397      { badge: "Review each repair" },
398    ),
399    accept: ".stl,model/stl,application/sla,application/octet-stream",
400    acceptText: "binary or text STL",
401    formats: formatContract(["STL"], ["STL"], "The report lists removed degenerate and duplicate faces plus supported orientation changes. Open or non-manifold edges remain visible; holes, self-intersections and severe topology corruption are not automatically repaired."),
402    settings: [
403      setting("removeDegenerate", "toggle", "Remove empty triangles", true),
404      setting("mergeDuplicates", "toggle", "Merge matching points", true),
405      setting("fixDirection", "toggle", "Correct surface direction", true),
406    ],
407    relatedToolSlugs: ["mesh-inspector", "stl-to-glb", "gcode-viewer"],
408    faq: standardFaq(
409      "STL Repair & Printability Check",
410      "Binary and text STL meshes are supported. The repaired mesh is exported as STL and the supported changes are summarized on screen.",
411      "It removes supported degenerate and duplicate faces, merges matching points and corrects supported surface direction while reporting open and non-manifold edges.",
412      "It does not guess design intent, close holes, resolve self-intersections or make an invalid solid print-ready. Inspect unresolved conditions, print dimensions and wall thickness in your destination workflow.",
413    ),
414  }),
415  threeDTool({
416    group: "Convert & Create",
417    name: "SVG to STL",
418    slug: "svg-to-stl",
419    resultKind: "export",
420    toolTypeLabel: "Vector-to-STL converter",
421    ctaLabel: "Create STL",
422    resultTitle: "Extruded STL ready",
423    resultDescription: "Review the extruded shape, dimensions and small features before downloading the produced STL file.",
424    shortDescription: "Turn a filled SVG shape into an extruded STL model for badges, signs, cutters and simple parts.",
425    seoTitle: "SVG to STL Converter Online — Extrude Vector to 3D | Pixlane",
426    seoDescription: "Convert filled SVG artwork to an extruded STL model online with adjustable width, depth and base thickness.",
427    preview: preview(
428      "svg-to-stl", "Mountain SVG", "Extruded badge",
429      "A layered mountain badge combines outer contours and interior cutouts to show the extrusion clearly.",
430      "A clean mountain badge drawn as filled SVG artwork",
431      "The same mountain artwork extruded into a solid 3D badge",
432      { badge: "Vector to solid" },
433    ),
434    accept: ".svg,image/svg+xml,text/xml,application/xml",
435    acceptText: "SVG with filled paths",
436    formats: formatContract(["SVG"], ["STL"], "Filled closed paths are extruded; strokes and unsupported effects may need conversion to paths first."),
437    settings: [
438      setting("width", "number", "Model width (mm)", 80, { min: 1, max: 1000, step: 1 }),
439      setting("depth", "number", "Extrusion depth (mm)", 4, { min: 0.2, max: 100, step: 0.1 }),
440      setting("base", "number", "Base thickness (mm)", 0, { min: 0, max: 50, step: 0.1 }),
441    ],
442    relatedToolSlugs: ["stl-repair", "3d-model-viewer", "heightmap-to-stl"],
443    faq: standardFaq(
444      "SVG to STL",
445      "SVG files with filled, closed paths are supported. The result is an STL mesh.",
446      "It scales the vector artwork to the chosen width and extrudes closed filled regi
446ons into a 3D solid.",
447      "Convert text, strokes, masks and special effects to simple filled paths before import, then inspect small cutouts and thin walls.",
448    ),
449  }),
450  threeDTool({
451    group: "Convert & Create",
452    name: "Heightmap to STL",
453    slug: "heightmap-to-stl",
454    resultKind: "export",
455    toolTypeLabel: "Heightmap-to-STL converter",
456    ctaLabel: "Create terrain STL",
457    resultTitle: "Terrain STL ready",
458    resultDescription: "Review the relief direction, base and mesh dimensions before downloading the produced STL file.",
459    shortDescription: "Convert a grayscale heightmap into a solid STL terrain tile or relief model.",
460    seoTitle: "Heightmap to STL Converter Online — Create 3D Terrain | Pixlane",
461    seoDescription: "Turn a grayscale PNG or JPG heightmap into a solid STL terrain tile with adjustable relief, size and base thickness.",
462    preview: preview(
463      "heightmap-to-stl", "Twin-peaks heightmap", "Terrain tile",
464      "A twin-peaks landscape combines sharp summits, a low saddle and rolling foothills to reveal gentle and steep elevation changes.",
465      "A grayscale twin-peaks landscape where brightness represents elevation",
466      "The same twin-peaks heightmap converted into a solid 3D terrain tile",
467      { badge: "Brightness becomes height" },
468    ),
469    accept: ".png,.jpg,.jpeg,image/png,image/jpeg",
470    acceptText: "grayscale PNG or JPG heightmap",
471    formats: formatContract(["PNG", "JPG"], ["STL"], "Image brightness maps to elevation; transparent pixels are treated as the lowest level."),
472    settings: [
473      setting("width", "number", "Terrain width (mm)", 120, { min: 10, max: 1000, step: 1 }),
474      setting("relief", "number", "Relief height (mm)", 24, { min: 0.5, max: 500, step: 0.5 }),
475      setting("base", "number", "Base thickness (mm)", 2, { min: 0.2, max: 100, step: 0.1 }),
476      setting("invert", "toggle", "Invert elevation", false),
477    ],
478    relatedToolSlugs: ["depth-map-to-3d", "svg-to-stl", "stl-repair"],
479    faq: standardFaq(
480      "Heightmap to STL",
481      "Grayscale PNG and JPG images are accepted and exported as a solid STL terrain or relief tile.",
482      "It converts image brightness to surface height, adds side walls and a base, and scales the result to your chosen dimensions.",
483      "Check that bright and dark areas represent the intended elevations. Fine image noise can produce rough surfaces and very dense meshes.",
484    ),
485  }),
486  threeDTool({
487    group: "Reconstruct",
488    name: "Depth Map to Surface / Point Cloud",
489    slug: "depth-map-to-3d",
490    resultKind: "export",
491    toolTypeLabel: "Depth-image geometry converter",
492    ctaLabel: "Create depth-derived geometry",
493    resultTitle: "Depth-derived geometry ready",
494    resultDescription: "Review whether you made a relief or a perspective reconstruction, then check the declared depth scale, camera assumptions and output verification before downloading.",
495    shortDescription: "Turn a precomputed depth image into either a relief surface or a perspective point reconstruction with explicit camera assumptions.",
496    seoTitle: "Depth Map to Surface or Point Cloud Online | Pixlane",
497    seoDescription: "Convert a precomputed grayscale depth map into an explicit relief surface or perspective point reconstruction online with local processing and camera controls.",
498    preview: preview(
499      "depth-map-to-3d", "Arch depth map", "3D surface",
500      "A stone arch depth map combines a strong foreground silhouette with layered recesses to demonstrate surface recovery.",
501      "A grayscale depth map of a stone arch with near and far regions",
502      "The same depth map reconstructed as an orbitable 3D surface",
503      { badge: "Depth becomes geometry" },
504    ),
505    accept: ".png,.jpg,.jpeg,.webp,image/png,image/jpeg,image/webp",
506    acceptText: "grayscale PNG, JPG or WebP depth map",
507    formats: formatContract(["PNG", "JPG", "WebP", "PGM"], ["STL", "GLB", "PLY"], "Relief mode displaces a regular heightfield. Perspective mode back-projects depth through the chosen field of view, depth encoding and near/far range; it exports GLB or PLY points, not an inferred watertight object."),
508    settings: [
509      setting("mode", "select", "Reconstruction mode", "relief", { options: [{ label: "Relief / heightfield", value: "relief" }, { label: "Perspective reconstruction", value: "perspective" }] }),
510      setting("output", "select", "Output", "glb", { options: [{ label: "GLB", value: "glb" }, { label: "Point cloud (PLY)", value: "ply" }, { label: "Closed relief (STL)", value: "stl" }] }),
511      setting("depthScale", "range", "Relief height / depth range", 1, { min: 0.1, max: 4, step: 0.05 }),
512      setting("smoothing", "range", "Relief smoothing", 1, { min: 0, max: 4, step: 1 }),
513      setting("invert", "toggle", "Invert near and far", false),
514      setting("fov", "number", "Vertical FOV (degrees)", 60, { min: 5, max: 175, step: 1 }),
515      setting("near", "number", "Near depth", 0.1, { min: 0.001, max: 100000, step: 0.01 }),
516      setting("far", "number", "Far depth", 10, { min: 0.01, max: 1000000, step: 0.1 }),
517      setting("encoding", "select", "Perspective depth encoding", "linear", { options: [{ label: "Linear depth", value: "linear" }, { label: "Inverse depth", value: "inverse" }, { label: "Disparity", value: "disparity" }] }),
518      setting("sampleStep", "number", "Perspective sampling step", 1, { min: 1, max: 16, step: 1 }),
519    ],
520    relatedToolSlugs: ["heightmap-to-stl", "point-cloud-viewer", "photo-to-3d-geometry"],
521    faq: standardFaq(
522      "Depth Map to Surface / Point Cloud",
523      "Grayscale PNG, JPG, WebP and PGM depth maps are accepted; relief mode can export a closed STL, GLB or PLY, while perspective mode exports GLB or PLY points.",
524      "Relief mode turns normalized intensity into height. Perspective mode back-projects normalized depth through your selected FOV, encoding and near/far range.",
525      "The source image must already be a depth map. Perspective output is only as calibrated as its depth encoding and camera settings; it does not infer hidden surfaces or claim watertight geometry.",
526    ),
527  }),
528  threeDTool({
529    group: "Reconstruct",
530    name: "Photo to 3D Geometry",
531    slug: "photo-to-3d-geometry",
532    isAi: true,
533    noindex: true,
534    availability: "qualification-required",
535    toolState: "research-preview",
536    countsTowardCategory: false,
537    webApplicationSchema: false,
538    resultKind: "research",
539    toolTypeLabel: "Research preview",
540    ctaLabel: "View research status",
541    shortDescription: "Preview a research workflow for turning one photo into depth, surface direction and editable 3D geometry.",
542    seoTitle: "Photo to 3D Geometry — Research Preview | Pixlane",
543    seoDescription: "Explore Pixlane's research preview for turning a single photo into depth and editable 3D geometry. Processing remains unavailable during validation.",
544    preview: preview(
545      "photo-to-3d-geometry", "Product photo", "Validation hold",
546      "A coral-and-charcoal hiking boot combines a clean silhouette, deep tread and layered materials for repeatable validation. Geometry output remains hidden until the workflow passes its release checks.",
547      "A studio product photo of a coral-and-charcoal hiking boot selected for geometry research",
548      "A validation status panel explaining that generated geometry is unavailable until release checks pass",
549      { badge: "In validation" },
550    ),
551    accept: ".png,.jpg,.jpeg,.webp,image/png,image/jpeg,image/webp",
552    acceptText: "single PNG, JPG or WebP photo",
553    formats: formatContract(["PNG", "JPG", "WebP"], [], "No geometry export is available until the research model passes the release qualification gate."),
554    settings: [
555      setting("subject", "select", "Photo type", "object", { options: [{ label: "Single object", value: "object" }, { label: "Indoor scene", value: "indoor" }, { label: "Outdoor scene", value: "outdoor" }] }),
556      setting("detail", "select", "Target detail", "balanced", { options: [{ label: "Fast preview", value: "fast" }, { label: "Balanced", value: "balanced" }, { label: "Fine", value: "fine" }] }),
557    ],
558    supportText: `${privacy} ${researchBoundary}`,
559    researchBoundary,
560    resultTitle: "Research workflow not yet available",
561    resultDescription: researchBoundary,
562    relatedToolSlugs: ["depth-map-to-3d", "point-cloud-viewer", "gaussian-splat-viewer"],
563    faq: [
564      { question: "Can I generate a 3D model from a photo now?", answer: `Not yet. ${researchBoundary}` },
565      { question: "What is this research page evaluating?", answer: "It evaluates whether one photo can produce stable visible-surface depth, orientation and geometry across supported devices without implying that hidden sides were captured." },
566      { question: "Will my photo be uploaded?", answer: `${privacy} You can select and preview a local photo, but geometry processing and export remain disabled while qualification is in progress.` },
567      { question: "What are the main limitations of single-photo geometry?", answer: "One image cannot reveal hidden surfaces. Reflective, transparent, thin and textureless objects can also produce uncertain geometry and always require review." },
568    ],
569  }),
570  threeDTool({
571    group: "View & Inspect",
572    name: "G-code Viewer",
573    slug: "gcode-viewer",
574    resultKind: "viewer",
575    toolTypeLabel: "Interactive print-path viewer",
576    ctaLabel: "Open print-path viewer",
577    resultTitle: "Interactive print-path view",
578    resultDescription: "Review the loaded layers, extrusion and travel paths, available speeds and estimated bounds. This viewer does not create printer-ready G-code.",
579    shortDescription: "Preview G-code extrusion layers, travel moves, speeds and estimated toolpath bounds before printing.",
580    seoTitle: "G-code Viewer Online — Preview 3D Print Layers | Pixlane",
581    seoDescription: "Open G-code online to preview extrusion layers, travel moves, available speeds and estimated bounds before sending a job to a printer.",
582    preview: preview(
583      "gcode-viewer", "G-code file", "Layer preview",
584      "A spiral vase toolpath makes walls, travel moves and layer progression immediately readable.",
585      "A spiral vase G-code file ready for print-path inspection",
586      "The same spiral vase shown as a colored layer-by-layer toolpath preview",
587      { badge: "Inspect every layer" },
588    ),
589    accept: ".gcode,.gco,.gc,text/plain,application/octet-stream",
590    acceptText: "G-code (.gcode, .gco, .gc)",
591    formats: formatContract(["G-code"], [], "The viewer visualizes supported motion commands; it does not replace printer-specific slicing or safety checks."),
592    settings: [
593      setting("colorMode", "select", "Color mode", "feature", { options: [{ label: "Move type", value: "feature" }, { label: "Speed", value: "speed" }, { label: "Layer", value: "layer" }] }),
594      setting("showTravel", "toggle", "Show travel moves", true),
595    ],
596    relatedToolSlugs: ["stl-repair", "3mf-viewer", "mesh-inspector"],
597    faq: standardFaq(
598      "G-code Viewer",
599      ".gcode, .gco and .gc text files with supported motion commands can be previewed.",
600      "It draws the toolpath by layer and distinguishes extrusion, travel and available speed information.",
601      "Always review printer profile, material, temperatures, limits, start and end commands in your slicer and printer softw
601are before printing.",
602    ),
603  }),
604  threeDTool({
605    group: "View & Inspect",
606    name: "3MF Viewer",
607    slug: "3mf-viewer",
608    resultKind: "viewer",
609    toolTypeLabel: "Interactive 3MF viewer",
610    ctaLabel: "Open 3MF viewer",
611    resultTitle: "Interactive 3MF build view",
612    resultDescription: "Review the loaded build, supported colors, placement and dimensions. This viewer does not create a converted export.",
613    shortDescription: "Open 3MF files online and inspect scene colors, overall dimensions and placement on the build plate.",
614    seoTitle: "3MF Viewer Online — Open 3D Manufacturing Files | Pixlane",
615    seoDescription: "Open 3MF files online to inspect scene colors, overall dimensions and build-plate placement before printing or sharing.",
616    preview: preview(
617      "3mf-viewer", "3MF package", "Build plate",
618      "A multi-part rocket uses separate colors and arranged components to show why 3MF scene structure matters.",
619      "A multi-part rocket stored in a 3MF package",
620      "The same rocket parts arranged and colored on an interactive build plate",
621      { badge: "Parts and placement" },
622    ),
623    accept: ".3mf,model/3mf,application/vnd.ms-package.3dmanufacturing-3dmodel+xml,application/octet-stream",
624    acceptText: "3MF",
625    formats: formatContract(["3MF"], [], "Supported scene geometry, transforms, colors and overall bounds are shown; unsupported package features may not appear."),
626    settings: [
627      setting("viewMode", "select", "Display mode", "color", { options: [{ label: "Source colors", value: "color" }, { label: "Single material", value: "solid" }, { label: "Wireframe", value: "wireframe" }] }),
628      setting("showPlate", "toggle", "Show build plate", true),
629      setting("showBounds", "toggle", "Show scene bounds", false),
630    ],
631    relatedToolSlugs: ["gcode-viewer", "3d-model-viewer", "mesh-inspector"],
632    faq: standardFaq(
633      "3MF Viewer",
634      "3MF packages with supported model parts, transforms, colors and build items can be opened.",
635      "It displays the package's supported scene and placement in an orbitable view, with an overall geometry and dimension summary.",
636      "Unsupported package features may not appear. Confirm printer-specific settings in your slicer; a visual preview does not validate printability.",
637    ),
638  }),
639  threeDTool({
640    group: "View & Inspect",
641    name: "glTF Validator",
642    slug: "gltf-validator",
643    resultKind: "report",
644    toolTypeLabel: "glTF package validation report",
645    ctaLabel: "Validate glTF package",
646    resultTitle: "glTF package validation report",
647    resultDescription: "Review the parsed document, declared local resources and supported geometry checks before relying on a glTF package in another workflow.",
648    shortDescription: "Check a glTF or GLB package for readable structure, declared local resources and supported mesh data before opening or converting it.",
649    seoTitle: "glTF Validator Online — Check glTF & GLB Packages | Pixlane",
650    seoDescription: "Validate a local glTF or GLB package online, review declared local buffers and images, and identify missing selected resources before conversion.",
651    preview: preview(
652      "gltf-validator", "glTF package", "Validation report",
653      "A packaged product model makes local buffer and image references easy to audit before it reaches a downstream viewer.",
654      "A selected glTF package with local geometry and image resources",
655      "A validation report that lists readable structure and any unresolved local resource",
656      { badge: "Check package references" },
657    ),
658    accept: ".gltf,.glb,.bin,.png,.jpg,.jpeg,.webp,.zip,model/gltf+json,model/gltf-binary,application/json,application/octet-stream",
659    acceptText: "glTF, GLB, selected folder or ZIP package",
660    formats: formatContract(["glTF JSON", "GLB", "local BIN", "local image resources", "ZIP package"], ["JSON report"], "The validator checks readable package structure and local references inside the selected bundle. Remote URLs are blocked; this is not a claim of complete specification conformance or destination-engine compatibility."),
661    settings: [
662      setting("checkReferences", "toggle", "Check declared local resources", true),
663      setting("checkGeometry", "toggle", "Check supported mesh accessors", true),
664      setting("checkImages", "toggle", "Check local image references", true),
665    ],
666    relatedToolSlugs: ["gltf-viewer", "glb-viewer", "3d-model-viewer"],
667    faq: standardFaq(
668      "glTF Validator",
669      "glTF JSON, GLB and selected local BIN or image resources can be checked from a folder or ZIP package. The result is a JSON report, not a converted model.",
670      "It reads the supplied package, checks the declared local references and reports supported mesh structure so missing selected resources are visible before you continue.",
671      "A readable report does not certify every extension, target application or visual result. Open the model and review its materials, scale and animation in the destination workflow.",
672    ),
673  }),
674  threeDTool({
675    group: "View & Inspect",
676    name: "3MF Validator",
677    slug: "3mf-validator",
678    resultKind: "report",
679    toolTypeLabel: "3MF package validation report",
680    ctaLabel: "Validate 3MF package",
681    resultTitle: "3MF package validation report",
682    resultDescription: "Review the readable package entries, model resources, build items and supported geometry conditions before printing, converting or sharing the file.",
683    shortDescription: "Check a 3MF package for readable model entries, supported build items and mesh data before using it in a viewer or converter.",
684    seoTitle: "3MF Validator Online — Check Manufacturing Packages | Pixlane",
685    seoDescription: "Validate a local 3MF file online, inspect readable package resources and supported build items, and identify issues before conversion or printing.",
686    preview: preview(
687      "3mf-validator", "3MF build package", "Validation report",
688      "A multi-part build package makes resource, transform and supported geometry checks useful before it is sent to another tool.",
689      "A selected multi-part 3MF build package",
690      "A validation report listing readable build items and supported package resources",
691      { badge: "Check package structure" },
692    ),
693    accept: ".3mf,model/3mf,application/vnd.ms-package.3dmanufacturing-3dmodel+xml,application/octet-stream",
694    acceptText: "3MF",
695    formats: formatContract(["3MF"], ["JSON report"], "The report checks readable 3MF package and supported model data. It does not certify printer profiles, slicer settings, material behavior or printability."),
696    settings: [
697      setting("checkBuildItems", "toggle", "Include build items and transforms in report", true),
698      setting("checkResources", "toggle", "Include supported resources in report", true),
699      setting("checkGeometry", "toggle", "Include supported mesh geometry in report", true),
700    ],
701    relatedToolSlugs: ["3mf-viewer", "3mf-to-glb", "3mf-to-stl"],
702    faq: standardFaq(
703      "3MF Validator",
704      "Local .3mf manufacturing packages are checked and summarized in a JSON report.",
705      "It reads the package entries, supported model resources, build items and transforms so structural problems are visible before a conversion or visual review.",
706      "The report does not certify printer-specific settings, material behavior, collision clearance, wall thickness or print success. Confirm those details in the intended manufacturing workflow.",
707    ),
708  }),
709  threeDTool({
710    group: "Convert & Create",
711    name: "3MF to STL Converter",
712    slug: "3mf-to-stl",
713    resultKind: "export",
714    toolTypeLabel: "3MF-to-STL converter",
715    ctaLabel: "Convert to STL",
716    resultTitle: "STL conversion ready",
717    resultDescription: "Review the supported converted mesh, transformed bounds and conversion notes before downloading the produced STL file.",
718    shortDescription: "Convert supported 3MF mesh geometry to STL while making the loss of package colors and other non-geometry details explicit.",
719    seoTitle: "3MF to STL Converter Online — Export Supported Meshes | Pixlane",
720    seoDescription: "Convert supported local 3MF mesh geometry to STL online, review transformed bounds and confirm the geometry-only result before download.",
721    preview: preview(
722      "3mf-to-stl", "3MF build package", "STL mesh",
723      "A multi-part build package shows the difference between a structured manufacturing file and a geometry-only STL result.",
724      "A selected 3MF package with arranged colored parts",
725      "The supported package geometry exported as a single STL mesh",
726      { badge: "Geometry-only export" },
727    ),
728    accept: ".3mf,model/3mf,application/vnd.ms-package.3dmanufacturing-3dmodel+xml,application/octet-stream",
729    acceptText: "3MF",
730    formats: formatContract(["3MF"], ["STL"], "STL carries triangle geometry in millimetres. Supported build transforms can be applied, but colors, materials, package metadata and unsupported 3MF features are not represented in the result."),
731    settings: [
732      setting("combineParts", "toggle", "Combine supported parts", true),
733      setting("applyBuildTransforms", "toggle", "Apply build-item transforms", true),
734      setting("binary", "toggle", "Write binary STL", true),
735    ],
736    relatedToolSlugs: ["3mf-validator", "3mf-viewer", "stl-viewer"],
737    faq: standardFaq(
738      "3MF to STL Converter",
739      "Supported local 3MF mesh geometry is converted to STL. The output is a geometry-only STL file.",
740      "It reads supported 3MF build geometry, optionally applies its build transforms and combines the selected supported parts into an STL mesh.",
741      "STL does not preserve colors, materials, package metadata or unsupported 3MF features. The local output is normalized to millimetres; review converted bounds, placements and mesh conditions before using it for printing.",
742    ),
743  }),
744  threeDTool({
745    group: "Convert & Create",
746    name: "3MF to GLB Converter",
747    slug: "3mf-to-glb",
748    resultKind: "export",
749    toolTypeLabel: "3MF-to-GLB converter",
750    ctaLabel: "Convert to GLB",
751    resultTitle: "GLB conversion ready",
752    resultDescription: "Review the supported converted scene, transforms, colors and conversion notes before downloading the produced GLB file.",
753    shortDescription: "Convert supported 3MF parts to a GLB scene while retaining the supported colors and build placement that fit the result format.",
754    seoTitle: "3MF to GLB Converter Online — Convert Manufacturing Files | Pixlane",
755    seoDescription: "Convert supported local 3MF parts to GLB online, review retained supported transforms and colors, then download the converted scene.",
756    preview: preview(
757      "3mf-to-glb", "3MF build package", "GLB scene",
758      "A multi-part rocket package demonstrates how supported parts, transforms and colors can be reviewed in a portable scene result.",
759      "A selected 3MF package with colored build items",
760      "The supported build items arranged in a converted GLB scene",
761      { badge: "Review scene transfer" },
762    ),
763    accept: ".3mf,model/3mf,application/vnd.ms-package.3dmanufacturing-3dmodel+xml,application/octet-stream",
764    acceptText: "3MF",
765    formats: formatContract(["3MF"], ["GLB"], "Supported mesh geometry, build transforms and colors are transferred where available. The GLB output is normalized to metres; printer settings, package metadata and unsupported 3MF features are not represented as scene data."),
766    settings: [
767      setting("applyBuildTransforms", "toggle", "Apply build-item transforms", true),
768      setting("includeColors", "toggle", "Keep supported source colors", true),
769      setting("center", "toggle", "Center converted scene", false),
770    ],
771    relatedToolSlugs: ["3mf-validator", "3mf-viewer", "glb-viewer"],
772    faq: standardFaq(
773      "3MF to GLB Converter",
774      "Supported local 3MF packages can be converted to a GLB scene. The output stays a single GLB file.",
775      "It transfers supported mesh geometry, build placement and colors into a portable GLB scene for viewing or downstream use.",
776      "The GLB output is normalized to metres. Printer settings, material profiles and unsupported package features do not become scene data; review colors, placements and dimensions before relying on it.",
777    ),
778  }),
779  threeDTool({
780    group: "Convert & Create",
781    name: "Mesh Simplifier",
782    slug: "mesh-simplifier",
783    resultKind: "export",
784    toolTypeLabel: "Mesh simplification export",
785    ctaLabel: "Simplify mesh",
786    resultTitle: "Simplified mesh ready",
787    resultDescription: "Review the measured triangle change, preserved bounds and any boundary or multi-material meshes left unchanged before downloading the produced GLB file.",
788    shortDescription: "Reduce supported static mesh complexity to a chosen target while showing measured triangle change, preserved bounds and any geometry kept intact for safe material or boundary handling.",
789    seoTitle: "Mesh Simplifier Online — Reduce Static Mesh Complexity | Pixlane",
790    seoDescription: "Simplify supported static GLB, OBJ, STL or mesh PLY geometry online, compare triangle counts and bounds, then download a GLB result.",
791    preview: preview(
792      "mesh-simplifier", "Detailed mesh", "Simplified mesh",
793      "A mechanical bracket makes removed repeated surface detail and the preserved overall silhouette easy to compare at a practical target ratio.",
794      "A detailed mechanical bracket mesh before simplification",
795      "The same bracket with reduced supported static mesh complexity",
796      { badge: "Measured triangle change" },
797    ),
798    accept: ".glb,.obj,.stl,.ply,model/gltf-binary,model/obj,model/stl,application/octet-stream",
799    acceptText: "GLB, OBJ, STL or mesh PLY",
800    formats: formatContract(["GLB", "OBJ", "STL", "mesh PLY"], ["GLB"], "Only supported static triangle meshes are simplified. With material preservation enabled, a mesh with distinct material groups is left intact rather than flattening those assignments. Skins, morph targets, animation, point clouds and unsupported attributes are skipped or rejected instead of silently changing their behavior."),
801    settings: [
802      setting("targetRatio", "range", "Target triangle ratio", 0.5, { min: 0.15, max: 1, step: 0.05 }),
803      setting("preserveBoundaries", "toggle", "Preserve mesh boundaries", true),
804      setting("preserveMaterials", "toggle", "Keep supported material assignments", true),
805    ],
806    relatedToolSlugs: ["mesh-inspector", "glb-optimizer", "glb-viewer"],
807    faq: standardFaq(
808      "Mesh Simplifier",
809      "Supported static GLB, OBJ, STL and mesh PLY triangle geometry can be simplified and exported as GLB.",
810      "It reduces eligible static triangle meshes toward the selected target and reports the measured triangle change and resulting bounds. Open-boundary meshes and meshes with distinct material groups stay intact when their matching preservation option is enabled.",
811      "Fine features, small holes and sharp edges can change at lower target ratios. Skinned, animated, morph-driven and point-only inputs are not silently simplified; inspect the result before using it.",
812    ),
813  }),
814  threeDTool({
815    group: "View & Inspect",
816    name: "STL Viewer",
817    slug: "stl-viewer",
818    resultKind: "viewer",
819    toolTypeLabel: "Interactive STL viewer",
820    ctaLabel: "Open STL model",
821    resultTitle: "Interactive STL view",
822    resultDescription: "Orbit, zoom and inspect the loaded STL mesh with its reported bounds and display mode. This viewer does not create an export.",
823    shortDescription: "Open binary or text STL meshes online to orbit, inspect dimensions and compare solid, edge and wireframe displays.",
824    seoTitle: "STL Viewer Online — Open & Inspect STL Meshes | Pixlane",
825    seoDescription: "Open a local binary or text STL mesh online, orbit around it, inspect bounds and switch between supported display modes.",
826    preview: preview(
827      "stl-viewer", "STL mesh", "Interactive view",
828      "A turbine impeller exposes thin blades and deep curves so an orbitable STL inspection has useful viewing context.",
829      "A turbine impeller stored as an STL triangle mesh",
830      "The same STL mesh framed for interactive inspection",
831      { badge: "Orbit STL geometry" },
832    ),
833    accept: ".stl,model/stl,application/sla,application/octet-stream",
834    acceptText: "binary or text STL",
835    formats: formatContract(["STL"], [], "STL stores triangle geometry and normally has no material or unit metadata. This viewer opens the mesh; it does not certify printability or create an export."),
836    settings: standardViewerSettings(),
837    relatedToolSlugs: ["stl-repair", "stl-to-glb", "mesh-inspector"],
838    faq: standardFaq(
839      "STL Viewer",
840      "Binary and text STL triangle meshes can be opened in the viewer.",
841      "It frames the mesh for orbiting and reports its bounds while letting you switch supported display modes and background treatment.",
842      "STL normally does not declare units or material data. Confirm dimensions, intended units, surface direction and print constraints in the destination workflow.",
843    ),
844  }),
845  threeDTool({
846    group: "View & Inspect",
847    name: "GLB Viewer",
848    slug: "glb-viewer",
849    resultKind: "viewer",
850    toolTypeLabel: "Interactive GLB viewer",
851    ctaLabel: "Open GLB model",
852    resultTitle: "Interactive GLB view",
853    resultDescription: "Orbit, zoom and inspect the loaded GLB scene with its reported bounds, supported materials and display mode. This viewer does not create an export.",
854    shortDescription: "Open a GLB model online to inspect supported mesh data, materials, dimensions and display modes in an orbitable scene.",
855    seoTitle: "GLB Viewer Online — Open & Inspect GLB Models | Pixlane",
856    seoDescription: "Open a local GLB model online, orbit around the supported scene, inspect dimensions and review supported materials before conversion or sharing.",
857    preview: preview(
858      "glb-viewer", "GLB model", "Interactive view",
859      "An arcade robot contains distinct parts and materials so an orbitable GLB inspection reveals useful scene detail.",
860      "An arcade robot stored as a GLB model",
861      "The same GLB model framed in an interactive studio view",
862      { badge: "Inspect GLB details" },
863    ),
864    accept: ".glb,model/gltf-binary,application/octet-stream",
865    acceptText: "GLB",
866    formats: formatContract(["GLB"], [], "The viewer opens supported GLB scene data locally. Unsupported extensions or scene features are reported where possible; it does not create a new export."),
867    settings: standardViewerSettings(),
868    relatedToolSlugs: ["gltf-validator", "glb-optimizer", "glb-to-stl"],
869    faq: standardFaq(
870      "GLB Viewer",
871      "Binary glTF .glb files with supported scene data can be opened.",
872      "It presents the supported scene in an orbitable view and reports usable mesh, material and bounds information for review.",
873      "A successful visual view is not a guarantee that every extension, animation or target workflow is supported. Review important scene behavior in the destination application.",
874    ),
875  }),
876  threeDTool({
877    group: "View & Inspect",
878    name: "glTF Viewer",
879    slug: "gltf-viewer",
880    resultKind: "viewer",
881    toolTypeLabel: "Interactive glTF package viewer",
882    ctaLabel: "Open glTF package",
883    resultTitle: "Interactive glTF package view",
884    resultDescription: "Orbit and inspect the loaded glTF package after its selected local resources resolve. This viewer does not create an export.",
885    shortDescription: "Open a glTF package with selected local BIN and image resources online, then orbit and inspect the resolved supported scene.",
886    seoTitle: "glTF Viewer Online — Open Local glTF Packages | Pixlane",
887    seoDescription: "Open a local glTF package online with its selected local buffers and images, resolve supported references privately and inspect the scene in an orbitable viewer.",
888    preview: preview(
889      "gltf-viewer", "glTF package", "Interactive view",
890      "A packaged product model makes selected local buffers and textures visible as a resolved scene rather than an implied remote download.",
891      "A selected glTF package with local geometry and image files",
892      "The resolved supported glTF package in an interactive view",
893      { badge: "Open local resources" },
894    ),
895    accept: ".gltf,.bin,.png,.jpg,.jpeg,.webp,.zip,model/gltf+json,application/json,application/octet-stream",
896    acceptText: "glTF, selected folder or ZIP package",
897    formats: formatContract(["glTF JSON", "local BIN", "local image resources", "ZIP package"], [], "Local files selected in the same folder or ZIP package can resolve declared relative references. Remote URLs are blocked and any missing selected resource is named before the viewer opens."),
898    settings: standardViewerSettings(),
899    relatedToolSlugs: ["gltf-validator", "glb-viewer", "3d-model-viewer"],
900    faq: standardFaq(
901      "glTF Viewer",
902      "A glTF JSON file can be opened with its selected local BIN and supported image resources from a folder or ZIP package.",
903      "It resolves declared relative local resources from the selected bundle and opens the supported scene in an orbitable view.",
904      "Remote references are blocked, and missing selected resources prevent a misleading partial result. Review animations, materials and scale in the downstream workflow when they matter.",
905    ),
906  }),
907  threeDTool({
908    group: "View & Inspect",
909    name: "OBJ Viewer",
910    slug: "obj-viewer",
911    resultKind: "viewer",
912    toolTypeLabel: "Interactive OBJ package viewer",
913    ctaLabel: "Open OBJ package",
914    resultTitle: "Interactive OBJ package view",
915    resultDescription: "Orbit and inspect the loaded OBJ mesh after its selected local material and image resources resolve. This viewer does not create an export.",
916    shortDescription: "Open an OBJ mesh with selected local MTL and image resources online, then orbit and inspect the supported resolved model.",
917    seoTitle: "OBJ Viewer Online — Open OBJ, MTL & Local Textures | Pixlane",
918    seoDescription: "Open a local OBJ package online with selected MTL and image resources, resolve supported local references privately and inspect the model in an orbitable viewer.",
919    preview: preview(
920      "obj-viewer", "OBJ package", "Interactive view",
921      "A desk lamp package makes local material and texture references visible as a resolved model instead of an unverified flat mesh.",
922      "A desk lamp OBJ with local material and image resources",
923      "The resolved supported OBJ package in an interactive view",
924      { badge: "Open local materials" },
925    ),
926    accept: ".obj,.mtl,.png,.jpg,.jpeg,.webp,.zip,model/obj,text/plain,application/octet-stream",
927    acceptText: "OBJ, selected folder or ZIP package",
928    formats: formatContract(["OBJ", "local MTL", "local image resources", "ZIP package"], [], "Local MTL and image files selected in the same bundle can resolve relative references. Remote URLs are blocked and missing local resources are named before the viewer opens."),
929    settings: standardViewerSettings(),
930    relatedToolSlugs: ["obj-to-glb", "obj-to-stl", "mesh-inspector"],
931    faq: standardFaq(
932      "OBJ Viewer",
933      "An OBJ file can be opened with selected local MTL and supported image resources from a folder or ZIP package.",
934      "It resolves supported relative local material references and opens the model in an orbitable view for visual and bounds review.",
935      "Remote references are blocked and missing selected files stop the load with a named notice. Check material appearance, scale and orientation before using the model elsewhere.",
936    ),
937  }),
938  threeDTool({
939    group: "View & Inspect",
940    name: "PLY Viewer",
941    slug: "ply-viewer",
942    resultKind: "viewer",
943    toolTypeLabel: "Interactive PLY viewer",
944    ctaLabel: "Open PLY data",
945    resultTitle: "Interactive PLY view",
946    resultDescription: "Orbit and inspect the loaded mesh or point PLY data with its detected representation and available attributes. This viewer does not create an export.",
947    shortDescription: "Open a mesh or point-cloud PLY file online, detect its supported representation and inspect it with appropriate display controls.",
948    seoTitle: "PLY Viewer Online — Open Mesh & Point-Cloud PLY | Pixlane",
949    seoDescription: "Open a local mesh or point-cloud PLY file online, detect its supported representation and inspect geometry or point attributes in an orbitable viewer.",
950    preview: preview(
951      "ply-viewer", "PLY source data", "Interactive view",
952      "A forest scan makes detected PLY point attributes and the difference between source color and height review immediately useful.",
953      "A PLY source file containing a forest point scan",
954      "The same detected PLY data shown in an interactive view",
955      { badge: "Detect PLY representation" },
956    ),
957    accept: ".ply,application/octet-stream,text/plain",
958    acceptText: "mesh or point-cloud PLY",
959    formats: formatContract(["mesh PLY", "point-cloud PLY"], [], "The viewer detects supported mesh and point-cloud PLY content, then enables only the display controls that apply to that representation. Gaussian splat PLY is directed to the dedicated Gaussian Splat Viewer instead of being treated as an ordinary point cloud."),
960    settings: [
961      setting("viewMode", "select", "Display mode", "solid", { options: [{ label: "Solid / source", value: "solid" }, { label: "Wireframe", value: "wireframe" }, { label: "Height colors", value: "height" }] }),
962      setting("pointSize", "range", "Point size", 1.4, { min: 0.5, max: 5, step: 0.1 }),
963      setting("showBounds", "toggle", "Show bounds", false),
964    ],
965    relatedToolSlugs: ["point-cloud-viewer", "gaussian-splat-viewer", "ply-to-stl"],
966    faq: standardFaq(
967      "PLY Viewer",
968      "Mesh and point-cloud PLY files with supported properties can be opened. Gaussian splat PLY is sent to the dedicated splat viewer.",
969      "It detects whether the supported file represents triangle geometry or points, then presents the relevant geometry or point attributes in an orbitable scene.",
970      "PLY property conventions vary. Confirm the detected representation, coordinate units and attribute meaning before converting or using the data downstream.",
971    ),
972  }),
973  threeDTool({
974    group: "Convert & Create",
975    name: "GLB to STL Converter",
976    slug: "glb-to-stl",
977    resultKind: "export",
978    toolTypeLabel: "GLB-to-STL converter",
979    ctaLabel: "Convert to STL",
980    resultTitle: "STL conversion ready",
981    resultDescription: "Review the supported static mesh selection, transformed bounds and geometry-only conversion notes before downloading the produced STL file.",
982    shortDescription: "Convert supported static GLB triangle meshes to a geometry-only STL result while making unsupported scene data explicit.",
983    seoTitle: "GLB to STL Converter Online — Export Static Meshes | Pixlane",
984    seoDescription: "Convert supported local static GLB meshes to STL online, review transformed bounds and confirm the geometry-only result before download.",
985    preview: preview(
986      "glb-to-stl", "GLB scene", "STL mesh",
987      "An arcade robot scene shows how supported static mesh geometry becomes a geometry-only STL without scene materials or animation data.",
988      "An arcade robot stored as a GLB scene",
989      "Supported static robot geometry exported as an STL mesh",
990      { badge: "Static geometry export" },
991    ),
992    accept: ".glb,model/gltf-binary,application/octet-stream",
993    acceptText: "GLB",
994    formats: formatContract(["GLB"], ["STL"], "STL contains triangle geometry only. Supported static mesh transforms can be applied; materials, textures, animations, cameras, lights and unsupported scene features are not represented in the output."),
995    settings: [
996      setting("mergeMeshes", "toggle", "Merge supported static meshes", true),
997      setting("applyTransforms", "toggle", "Apply object transforms", true),
998      setting("binary", "toggle", "Write binary STL", true),
999    ],
1000    relatedToolSlugs: ["glb-viewer", "glb-optimizer", "stl-viewer"],
1001    faq: standardFaq(
1002      "GLB to STL Converter",
1003      "Supported local GLB static triangle meshes can be exported as STL.",
1004      "It collects supported static mesh geometry, optionally applies object transforms and writes a geometry-only STL result for review.",
1005      "STL does not preserve GLB materials, textures, animation, cameras or lights. Skinned, morph-driven or otherwise unsupported scene content must be reviewed separately rather than assumed to convert.",
1006    ),
1007  }),
1008  threeDTool({
1009    group: "Convert & Create",
1010    name: "OBJ to STL Converter",
1011    slug: "obj-to-stl",
1012    resultKind: "export",
1013    toolTypeLabel: "OBJ-to-STL converter",
1014    ctaLabel: "Convert to STL",
1015    resultTitle: "STL conversion ready",
1016    resultDescription: "Review the converted triangle mesh, bounds and geometry-only conversion notes before downloading the produced STL file.",
1017    shortDescription: "Convert OBJ triangle geometry to STL online while making the loss of OBJ materials and image references explicit.",
1018    seoTitle: "OBJ to STL Converter Online — Export Triangle Geometry | Pixlane",
1019    seoDescription: "Convert local OBJ triangle geometry to STL online, review converted bounds and confirm the geometry-only result before download.",
1020    preview: preview(
1021      "obj-to-stl", "OBJ package", "STL mesh",
1022      "A desk lamp package makes the distinction between a material-bearing OBJ source and a geometry-only STL export easy to review.",
1023      "A desk lamp represented as OBJ geometry with local material references",
1024      "The same supported OBJ geometry exported as an STL mesh",
1025      { badge: "Geometry-only export" },
1026    ),
1027    accept: ".obj,.mtl,.zip,model/obj,text/plain,application/octet-stream",
1028    acceptText: "OBJ, selected folder or ZIP package",
1029    formats: formatContract(["OBJ", "local MTL package"], ["STL"], "OBJ triangle geometry is exported as STL. MTL, colors, image textures, object names and unsupported OBJ statements do not become STL data."),
1030    settings: [
1031      setting("center", "toggle", "Center mesh at origin", true),
1032      setting("applyAxis", "select", "Source up axis", "y", { options: [{ label: "Y up", value: "y" }, { label: "Z up", value: "z" }] }),
1033      setting("binary", "toggle", "Write binary STL", true),
1034    ],
1035    relatedToolSlugs: ["obj-viewer", "obj-to-glb", "stl-viewer"],
1036    faq: standardFaq(
1037      "OBJ to STL Converter",
1038      "OBJ triangle geometry can be converted from a selected file, folder or ZIP package. The output is STL.",
1039      "It reads supported OBJ triangle geometry, applies the selected orientation and centering options, then writes a geometry-only STL mesh.",
1040      "STL does not retain OBJ materials, textures, object names or unsupported statements. Check scale, orientation and surface direction in the result before printing or sharing.",
1041    ),
1042  }),
1043  threeDTool({
1044    group: "Convert & Create",
1045    name: "STL to OBJ Converter",
1046    slug: "stl-to-obj",
1047    resultKind: "export",
1048    toolTypeLabel: "STL-to-OBJ converter",
1049    ctaLabel: "Convert to OBJ",
1050    resultTitle: "OBJ conversion ready",
1051    resultDescription: "Review the converted triangle mesh, chosen unit scale and OBJ conversion notes before downloading the produced OBJ file.",
1052    shortDescription: "Convert binary or text STL geometry to a 
1052simple OBJ mesh with explicit unit and normal choices for downstream editing.",
1053    seoTitle: "STL to OBJ Converter Online — Export Editable Mesh Geometry | Pixlane",
1054    seoDescription: "Convert a local binary or text STL mesh to OBJ online, choose a unit scale and normal option, then review the result before download.",
1055    preview: preview(
1056      "stl-to-obj", "STL mesh", "OBJ mesh",
1057      "A turbine impeller demonstrates a simple geometry transfer from an STL source into an editable OBJ mesh result.",
1058      "A turbine impeller stored as an STL triangle mesh",
1059      "The same STL geometry exported as an OBJ mesh",
1060      { badge: "Editable mesh transfer" },
1061    ),
1062    accept: ".stl,model/stl,application/sla,application/octet-stream",
1063    acceptText: "binary or text STL",
1064    formats: formatContract(["STL"], ["OBJ"], "The OBJ result contains supported geometry and optional normal records. STL normally supplies no material or unit metadata, so a material library is not implied by this conversion."),
1065    settings: [
1066      setting("units", "select", "STL units", "mm", { options: [{ label: "Millimeters", value: "mm" }, { label: "Centimeters", value: "cm" }, { label: "Meters", value: "m" }, { label: "Inches", value: "in" }] }),
1067      setting("includeNormals", "toggle", "Include face normals", true),
1068      setting("center", "toggle", "Center mesh at origin", true),
1069    ],
1070    relatedToolSlugs: ["stl-viewer", "stl-to-glb", "obj-viewer"],
1071    faq: standardFaq(
1072      "STL to OBJ Converter",
1073      "Binary and text STL triangle meshes can be converted to OBJ.",
1074      "It scales the supplied STL coordinates by the chosen unit interpretation and writes supported triangle geometry with optional normal records into an OBJ file.",
1075      "STL usually has no units, materials or object structure. Confirm the chosen scale, normals and destination material setup after conversion.",
1076    ),
1077  }),
1078  threeDTool({
1079    group: "Convert & Create",
1080    name: "PLY to STL Converter",
1081    slug: "ply-to-stl",
1082    resultKind: "export",
1083    toolTypeLabel: "PLY-to-STL converter",
1084    ctaLabel: "Convert mesh PLY to STL",
1085    resultTitle: "STL conversion ready",
1086    resultDescription: "Review the detected mesh representation, converted bounds and geometry-only conversion notes before downloading the produced STL file.",
1087    shortDescription: "Convert supported triangle mesh PLY files to STL while rejecting point-only and Gaussian splat content that cannot become a surface mesh.",
1088    seoTitle: "PLY to STL Converter Online — Export Mesh PLY Geometry | Pixlane",
1089    seoDescription: "Convert a supported local triangle mesh PLY file to STL online, detect unsupported point-only data clearly and review the mesh result before download.",
1090    preview: preview(
1091      "ply-to-stl", "Mesh PLY", "STL mesh",
1092      "A scanned surface mesh makes it clear that a PLY needs triangle faces before it can become a printable STL result.",
1093      "A selected PLY file with supported triangle mesh data",
1094      "The same supported PLY mesh exported as an STL surface",
1095      { badge: "Require triangle faces" },
1096    ),
1097    accept: ".ply,application/octet-stream,text/plain",
1098    acceptText: "triangle mesh PLY",
1099    formats: formatContract(["triangle mesh PLY"], ["STL"], "Only PLY files with supported triangle faces can convert to STL. Point clouds and Gaussian splat data do not define a printable surface and are rejected with a route to the appropriate viewer."),
1100    settings: [
1101      setting("center", "toggle", "Center mesh at origin", true),
1102      setting("repairDegenerate", "toggle", "Remove empty triangles", true),
1103      setting("binary", "toggle", "Write binary STL", true),
1104    ],
1105    relatedToolSlugs: ["ply-viewer", "point-cloud-viewer", "stl-viewer"],
1106    faq: standardFaq(
1107      "PLY to STL Converter",
1108      "PLY files with supported triangle mesh faces can be converted to STL. Point-only PLY and Gaussian splat data are not converted to a fabricated surface.",
1109      "It detects supported PLY mesh faces, converts the triangle geometry and reports the result bounds before producing an STL file.",
1110      "A point cloud is not a surface mesh. If the file has no supported faces, inspect it in a point or splat viewer or create a surface in a workflow that explicitly supports that operation.",
1111    ),
1112  }),
1113]);
1114
1115const THREE_D_BY_SLUG = new Map(THREE_D_TOOLS.map((tool) => [tool.slug, tool]));
1116
1117/**
1118 * Returns deterministic, actionable select-default failures instead of relying
1119 * on the browser to silently choose the first option. Callers can use this in
1120 * tests and before serializing settings for UI, URL, analytics, or exports.
1121 */
1122export function validateThreeDSelectDefaults(tools = THREE_D_TOOLS) {
1123  const failures = [];
1124  for (const tool of tools || []) {
1125    for (const setting of tool?.settings || []) {
1126      if (setting?.kind !== "select") continue;
1127      const options = Array.isArray(setting.options) ? setting.options : [];
1128      const optionValues = options.map((option) => option?.value);
1129      if (!options.length) {
1130        failures.push(Object.freeze({ slug: tool.slug, settingId: setting.id, reason: "missing-options" }));
1131        continue;
1132      }
1133      if (setting.defaultValue === undefined || setting.defaultValue === null) {
1134        failures.push(Object.freeze({ slug: tool.slug, settingId: setting.id, reason: "missing-default" }));
1135        continue;
1136      }
1137      if (!optionValues.includes(setting.defaultValue)) {
1138        failures.push(Object.freeze({
1139          slug: tool.slug,
1140          settingId: setting.id,
1141          defaultValue: setting.defaultValue,
1142          optionValues: Object.freeze(optionValues),
1143          reason: "default-not-declared",
1144        }));
1145      }
1146    }
1147  }
1148  return Object.freeze(failures);
1149}
1150
1151export function assertThreeDSelectDefaults(tools = THREE_D_TOOLS) {
1152  const failures = validateThreeDSelectDefaults(tools);
1153  if (failures.length) {
1154    const details = failures.map(({ slug, settingId, reason }) => `${slug}.${settingId}: ${reason}`).join(", ");
1155    throw new Error(`Invalid 3D select defaults: ${details}`);
1156  }
1157  return true;
1158}
1159
1160export function getThreeDToolCounts(tools = THREE_D_TOOLS) {
1161  const entries = Array.from(tools || []);
1162  const functional = entries.filter((tool) => tool?.toolState === "functional"
1163    && tool?.availability === "available"
1164    && tool?.countsTowardCategory === true
1165    && tool?.resultKind !== "research").length;
1166  const researchPreview = entries.filter((tool) => tool?.toolState === "research-preview"
1167    && tool?.resultKind === "research"
1168    && tool?.countsTowardCategory === false).length;
1169  return Object.freeze({ total: entries.length, functional, researchPreview, countable: functional });
1170}
1171
1172export function getCountableThreeDTools() {
1173  return THREE_D_TOOLS.filter((tool) => tool.countsTowardCategory === true);
1174}
1175
1176export function getResearchPreviewThreeDTools() {
1177  return THREE_D_TOOLS.filter((tool) => tool.toolState === "research-preview");
1178}
1179
1180export const THREE_D_TOOL_COUNTS = getThreeDToolCounts();
1181
1182export const allTools = Object.freeze([...base.allTools, ...THREE_D_TOOLS]);
1183export const toolRegistry = allTools;
1184export const categoryLabels = Object.freeze({ ...base.categoryLabels, "three-d": "3D Tools" });
1185export const categoryPaths = Object.freeze({ ...base.categoryPaths, "three-d": "/3d-tools" });
1186export const categoryMeta = Object.freeze({
1187  ...base.categoryMeta,
1188  "three-d": {
1189    pageTitle: "3D Tools",
1190    heading: "Free Online 3D Tools",
1191    totalToolCount: THREE_D_TOOL_COUNTS.total,
1192    availableToolCount: THREE_D_TOOL_COUNTS.functional,
1193    researchPreviewCount: THREE_D_TOOL_COUNTS.researchPreview,
1194    toolCountLabel: `${THREE_D_TOOL_COUNTS.functional} functional tools + ${THREE_D_TOOL_COUNTS.researchPreview} research preview`,
1195    description: `Use ${THREE_D_TOOL_COUNTS.functional} functional tools to validate local glTF and 3MF packages, view 3D models, inspect meshes, convert supported formats, simplify static geometry, clean up STL files and turn depth maps into geometry on your device. Photo to 3D Geometry is a separate research preview, not an available tool.`,
1196    seoTitle: "Free 3D Tools Online — View, Convert & Repair Models | Pixlane",
1197    seoDescription: `Use ${THREE_D_TOOL_COUNTS.functional} functional online 3D tools to validate glTF and 3MF packages, view GLB, OBJ, STL, PLY, 3MF and Gaussian splats, inspect meshes, convert supported formats, simplify static geometry and create geometry. ${THREE_D_TOOL_COUNTS.researchPreview} separate research preview is not counted as an available tool.`,
1198  },
1199});
1200
1201export function getToolBySlug(slug) {
1202  return THREE_D_BY_SLUG.get(slug) || base.getToolBySlug(slug);
1203}
1204
1205export function getToolsByCategory(category) {
1206  return category === "three-d" ? [...THREE_D_TOOLS] : base.getToolsByCategory(category);
1207}
1208
1209export function getToolPath(slug) {
1210  return THREE_D_BY_SLUG.has(slug) ? `/3d-tools/${slug}/` : base.getToolPath(slug);
1211}
1212
1213export function getCategoryPath(category) {
1214  return category === "three-d" ? "/3d-tools/" : base.getCategoryPath(category);
1215}
1216
1217export function getCategoryLabel(category) {
1218  return category === "three-d" ? "3D Tools" : base.getCategoryLabel(category);
1219}
1220
1221export function resolveRelatedTools(tool) {
1222  const resolved = [];
1223  const seen = new Set([tool?.slug]);
1224  const add = (candidate) => {
1225    if (!candidate?.slug || seen.has(candidate.slug) || resolved.length >= 8) return;
1226    seen.add(candidate.slug);
1227    resolved.push(candidate);
1228  };
1229  for (const slug of tool?.relatedToolSlugs || []) add(getToolBySlug(slug));
1230  for (const candidate of THREE_D_TOOLS) add(candidate);
1231  return resolved;
1232}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.