@playcanvas/splat-transform 1.0.0 → 1.1.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 (35) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.mjs +352 -5
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/index.cjs +5478 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.mjs +352 -5
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/lib/data-table/combine.d.ts +16 -0
  9. package/dist/lib/data-table/data-table.d.ts +48 -0
  10. package/dist/lib/data-table/summary.d.ts +33 -0
  11. package/dist/lib/data-table/transform.d.ts +19 -0
  12. package/dist/lib/index.d.cts +34 -0
  13. package/dist/lib/index.d.ts +4 -4
  14. package/dist/lib/io/read/index.d.ts +1 -1
  15. package/dist/lib/io/read/zip-file-system.d.ts +1 -1
  16. package/dist/lib/io/write/memory-file-system.d.ts +15 -0
  17. package/dist/lib/io/write/zip-file-system.d.ts +20 -0
  18. package/dist/lib/process.d.ts +96 -1
  19. package/dist/lib/read.d.ts +57 -1
  20. package/dist/lib/readers/read-ksplat.d.ts +11 -0
  21. package/dist/lib/readers/read-lcc.d.ts +13 -0
  22. package/dist/lib/readers/read-mjs.d.ts +12 -0
  23. package/dist/lib/readers/read-ply.d.ts +10 -0
  24. package/dist/lib/readers/read-sog.d.ts +1 -0
  25. package/dist/lib/readers/read-splat.d.ts +10 -0
  26. package/dist/lib/readers/read-spz.d.ts +12 -0
  27. package/dist/lib/types.d.ts +1 -0
  28. package/dist/lib/write.d.ts +57 -1
  29. package/dist/lib/writers/write-compressed-ply.d.ts +11 -0
  30. package/dist/lib/writers/write-csv.d.ts +10 -0
  31. package/dist/lib/writers/write-html.d.ts +10 -0
  32. package/dist/lib/writers/write-lod.d.ts +11 -0
  33. package/dist/lib/writers/write-ply.d.ts +10 -0
  34. package/dist/lib/writers/write-sog.d.ts +16 -1
  35. package/package.json +13 -4
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Reddit](https://img.shields.io/badge/Reddit-FF4500?style=flat&logo=reddit&logoColor=white&color=black)](https://www.reddit.com/r/PlayCanvas)
8
8
  [![X](https://img.shields.io/badge/X-000000?style=flat&logo=x&logoColor=white&color=black)](https://x.com/intent/follow?screen_name=playcanvas)
9
9
 
10
- | [User Guide](https://developer.playcanvas.com/user-manual/gaussian-splatting/editing/splat-transform/) | [Blog](https://blog.playcanvas.com/) | [Forum](https://forum.playcanvas.com/) |
10
+ | [User Guide](https://developer.playcanvas.com/user-manual/gaussian-splatting/editing/splat-transform/) | [API Reference](https://api.playcanvas.com/splat-transform/) | [Blog](https://blog.playcanvas.com/) | [Forum](https://forum.playcanvas.com/) |
11
11
 
12
12
  SplatTransform is an open source library and CLI tool for converting and editing Gaussian splats. It can:
13
13
 
package/dist/cli.mjs CHANGED
@@ -10702,6 +10702,18 @@ class Compute {
10702
10702
  }
10703
10703
  }
10704
10704
 
10705
+ /**
10706
+ * A named column of typed array data within a DataTable.
10707
+ *
10708
+ * Columns store homogeneous numeric data efficiently using JavaScript typed arrays.
10709
+ *
10710
+ * @example
10711
+ * ```ts
10712
+ * const positions = new Column('x', new Float32Array([1.0, 2.0, 3.0]));
10713
+ * console.log(positions.name); // 'x'
10714
+ * console.log(positions.dataType); // 'float32'
10715
+ * ```
10716
+ */
10705
10717
  class Column {
10706
10718
  name;
10707
10719
  data;
@@ -10726,6 +10738,32 @@ class Column {
10726
10738
  return new Column(this.name, this.data.slice());
10727
10739
  }
10728
10740
  }
10741
+ /**
10742
+ * A table of columnar data representing Gaussian splat properties.
10743
+ *
10744
+ * DataTable is the core data structure for splat data. Each column represents
10745
+ * a property (e.g., position, rotation, color) as a typed array, and all columns
10746
+ * must have the same number of rows.
10747
+ *
10748
+ * Standard columns include:
10749
+ * - Position: `x`, `y`, `z`
10750
+ * - Rotation: `rot_0`, `rot_1`, `rot_2`, `rot_3` (quaternion)
10751
+ * - Scale: `scale_0`, `scale_1`, `scale_2` (log scale)
10752
+ * - Color: `f_dc_0`, `f_dc_1`, `f_dc_2` (spherical harmonics DC)
10753
+ * - Opacity: `opacity` (logit)
10754
+ * - Spherical Harmonics: `f_rest_0` through `f_rest_44`
10755
+ *
10756
+ * @example
10757
+ * ```ts
10758
+ * const table = new DataTable([
10759
+ * new Column('x', new Float32Array([0, 1, 2])),
10760
+ * new Column('y', new Float32Array([0, 0, 0])),
10761
+ * new Column('z', new Float32Array([0, 0, 0]))
10762
+ * ]);
10763
+ * console.log(table.numRows); // 3
10764
+ * console.log(table.numColumns); // 3
10765
+ * ```
10766
+ */
10729
10767
  class DataTable {
10730
10768
  columns;
10731
10769
  constructor(columns) {
@@ -10817,7 +10855,22 @@ class DataTable {
10817
10855
  }
10818
10856
  }
10819
10857
 
10820
- // combine multiple DataTables into a single DataTable instance
10858
+ /**
10859
+ * Combines multiple DataTables into a single DataTable.
10860
+ *
10861
+ * Merges rows from all input tables. Columns are matched by name and type;
10862
+ * columns that don't exist in all tables will have undefined values for
10863
+ * rows from tables lacking that column.
10864
+ *
10865
+ * @param dataTables - Array of DataTables to combine.
10866
+ * @returns A new DataTable containing all rows from all input tables.
10867
+ *
10868
+ * @example
10869
+ * ```ts
10870
+ * const combined = combine([tableA, tableB, tableC]);
10871
+ * console.log(combined.numRows); // tableA.numRows + tableB.numRows + tableC.numRows
10872
+ * ```
10873
+ */
10821
10874
  const combine = (dataTables) => {
10822
10875
  if (dataTables.length === 1) {
10823
10876
  // nothing to combine
@@ -11043,7 +11096,25 @@ class RotateSH {
11043
11096
  const shNames$3 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
11044
11097
  const v = new Vec3();
11045
11098
  const q$1 = new Quat();
11046
- // apply translation, rotation and scale to a data table
11099
+ /**
11100
+ * Applies a spatial transformation to splat data in-place.
11101
+ *
11102
+ * Transforms position, rotation, scale, and spherical harmonics data.
11103
+ * The transformation is applied as: scale first, then rotation, then translation.
11104
+ *
11105
+ * @param dataTable - The DataTable to transform (modified in-place).
11106
+ * @param t - Translation vector.
11107
+ * @param r - Rotation quaternion.
11108
+ * @param s - Uniform scale factor.
11109
+ *
11110
+ * @example
11111
+ * ```ts
11112
+ * import { Vec3, Quat } from 'playcanvas';
11113
+ *
11114
+ * // Scale by 2x, rotate 90° around Y, translate up
11115
+ * transform(dataTable, new Vec3(0, 5, 0), new Quat().setFromEulerAngles(0, 90, 0), 2.0);
11116
+ * ```
11117
+ */
11047
11118
  const transform = (dataTable, t, r, s) => {
11048
11119
  const mat = new Mat4().setTRS(t, r, new Vec3(s, s, s));
11049
11120
  const mat3 = new Mat3().setFromQuat(r);
@@ -11162,6 +11233,23 @@ const computeColumnStats = (data) => {
11162
11233
  infCount
11163
11234
  };
11164
11235
  };
11236
+ /**
11237
+ * Computes statistical summary for all columns in a DataTable.
11238
+ *
11239
+ * For each column, calculates min, max, median, mean, standard deviation,
11240
+ * and counts of NaN/Infinity values. Useful for data validation and analysis.
11241
+ *
11242
+ * @param dataTable - The DataTable to analyze.
11243
+ * @returns Summary data with per-column statistics.
11244
+ *
11245
+ * @example
11246
+ * ```ts
11247
+ * const summary = computeSummary(dataTable);
11248
+ * console.log(summary.rowCount);
11249
+ * console.log(summary.columns['x'].mean);
11250
+ * console.log(summary.columns['opacity'].nanCount);
11251
+ * ```
11252
+ */
11165
11253
  const computeSummary = (dataTable) => {
11166
11254
  const columns = {};
11167
11255
  for (const column of dataTable.columns) {
@@ -11620,6 +11708,17 @@ const COMPRESSION_MODES = [
11620
11708
  }
11621
11709
  ];
11622
11710
  const HARMONICS_COMPONENT_COUNT$1 = [0, 9, 24, 45];
11711
+ /**
11712
+ * Reads a .ksplat file containing compressed Gaussian splat data.
11713
+ *
11714
+ * The .ksplat format (Kevin Kwok's format) uses spatial bucketing and
11715
+ * quantization to achieve high compression ratios while preserving quality.
11716
+ * Supports multiple compression modes and spherical harmonics bands.
11717
+ *
11718
+ * @param source - The read source providing access to the .ksplat file data.
11719
+ * @returns Promise resolving to a DataTable containing the splat data.
11720
+ * @ignore
11721
+ */
11623
11722
  const readKsplat = async (source) => {
11624
11723
  // Load complete file
11625
11724
  const fileBuffer = await source.read().readAll();
@@ -12283,6 +12382,19 @@ const deserializeEnvironment = (raw, compressInfo, hasSH) => {
12283
12382
  }
12284
12383
  return new DataTable(columns);
12285
12384
  };
12385
+ /**
12386
+ * Reads an XGrids LCC format containing multi-LOD Gaussian splat data.
12387
+ *
12388
+ * The LCC format uses a quadtree spatial structure with multiple LOD levels.
12389
+ * Each LOD level is stored separately in data.bin with optional spherical
12390
+ * harmonics in shcoef.bin. Environment splats are stored in environment.bin.
12391
+ *
12392
+ * @param fileSystem - File system for reading the LCC files.
12393
+ * @param filename - Path to the meta.lcc file.
12394
+ * @param options - Options including LOD selection via `lodSelect`.
12395
+ * @returns Promise resolving to an array of DataTables, one per LOD level plus environment.
12396
+ * @ignore
12397
+ */
12286
12398
  const readLcc = async (fileSystem, filename, options) => {
12287
12399
  const lccData = await readFile$1(fileSystem, filename);
12288
12400
  const lccText = new TextDecoder().decode(lccData);
@@ -12357,6 +12469,18 @@ const readLcc = async (fileSystem, filename, options) => {
12357
12469
  return result;
12358
12470
  };
12359
12471
 
12472
+ /**
12473
+ * Reads splat data from a JavaScript module generator.
12474
+ *
12475
+ * The module must export a `Generator` class with a static `create(params)` method
12476
+ * that returns an object with `count`, `columnNames`, and `getRow(index, row)` properties.
12477
+ * This allows programmatic generation of splat data.
12478
+ *
12479
+ * @param moduleUrl - URL or path to the JavaScript module.
12480
+ * @param params - Parameters to pass to the generator's create method.
12481
+ * @returns Promise resolving to a DataTable containing the generated splat data.
12482
+ * @ignore
12483
+ */
12360
12484
  const readMjs = async (moduleUrl, params) => {
12361
12485
  const module = await import(moduleUrl);
12362
12486
  if (!module) {
@@ -12677,6 +12801,16 @@ const readExact = async (stream, buffer, offset, length) => {
12677
12801
  }
12678
12802
  return totalRead;
12679
12803
  };
12804
+ /**
12805
+ * Reads a PLY file containing Gaussian splat data.
12806
+ *
12807
+ * Supports both standard PLY files and compressed PLY format. The PLY format is
12808
+ * the standard output from 3D Gaussian Splatting training pipelines.
12809
+ *
12810
+ * @param source - The read source providing access to the PLY file data.
12811
+ * @returns Promise resolving to a DataTable containing the splat data.
12812
+ * @ignore
12813
+ */
12680
12814
  const readPly = async (source) => {
12681
12815
  const stream = source.read();
12682
12816
  // we don't support ply text header larger than 128k
@@ -12889,6 +13023,7 @@ const sigmoidInv = (y) => {
12889
13023
  * @param fileSystem - The file system to read from
12890
13024
  * @param filename - Path to meta.json (relative paths resolved from its directory)
12891
13025
  * @returns DataTable with Gaussian splat data
13026
+ * @ignore
12892
13027
  */
12893
13028
  const readSog = async (fileSystem, filename) => {
12894
13029
  const decoder = await WebPCodec.create();
@@ -13036,6 +13171,16 @@ const readSog = async (fileSystem, filename) => {
13036
13171
  return new DataTable(columns);
13037
13172
  };
13038
13173
 
13174
+ /**
13175
+ * Reads an Antimatter15 .splat file containing Gaussian splat data.
13176
+ *
13177
+ * The .splat format stores each splat as 32 bytes with position, scale, color,
13178
+ * opacity, and rotation data in a compact binary format.
13179
+ *
13180
+ * @param source - The read source providing access to the .splat file data.
13181
+ * @returns Promise resolving to a DataTable containing the splat data.
13182
+ * @ignore
13183
+ */
13039
13184
  const readSplat = async (source) => {
13040
13185
  // Load complete file
13041
13186
  const fileBuffer = await source.read().readAll();
@@ -13159,6 +13304,18 @@ function getFixed24(positionsView, elementIndex, memberIndex) {
13159
13304
  return fixed32;
13160
13305
  }
13161
13306
  const HARMONICS_COMPONENT_COUNT = [0, 9, 24, 45];
13307
+ /**
13308
+ * Reads a .spz file containing Niantic Labs compressed Gaussian splat data.
13309
+ *
13310
+ * The .spz format uses GZIP compression and fixed-point encoding to achieve
13311
+ * compact file sizes. Supports version 2 and 3 of the format.
13312
+ *
13313
+ * @see https://github.com/nianticlabs/spz
13314
+ *
13315
+ * @param source - The read source providing access to the .spz file data.
13316
+ * @returns Promise resolving to a DataTable containing the splat data.
13317
+ * @ignore
13318
+ */
13162
13319
  const readSpz = async (source) => {
13163
13320
  // Load complete file
13164
13321
  let fileBuffer = await source.read().readAll();
@@ -13395,6 +13552,19 @@ const logger = {
13395
13552
  }
13396
13553
  };
13397
13554
 
13555
+ /**
13556
+ * Determines the input format based on file extension.
13557
+ *
13558
+ * @param filename - The filename to analyze.
13559
+ * @returns The detected input format.
13560
+ * @throws Error if the file extension is not recognized.
13561
+ *
13562
+ * @example
13563
+ * ```ts
13564
+ * const format = getInputFormat('scene.ply'); // returns 'ply'
13565
+ * const format2 = getInputFormat('scene.splat'); // returns 'splat'
13566
+ * ```
13567
+ */
13398
13568
  const getInputFormat = (filename) => {
13399
13569
  const lowerFilename = filename.toLowerCase();
13400
13570
  if (lowerFilename.endsWith('.mjs')) {
@@ -13420,6 +13590,30 @@ const getInputFormat = (filename) => {
13420
13590
  }
13421
13591
  throw new Error(`Unsupported input file type: ${filename}`);
13422
13592
  };
13593
+ /**
13594
+ * Reads a Gaussian splat file and returns its data as one or more DataTables.
13595
+ *
13596
+ * Supports multiple input formats including PLY, splat, ksplat, spz, SOG, and LCC.
13597
+ * Some formats (like LCC) may return multiple DataTables for different LOD levels.
13598
+ *
13599
+ * @param readFileOptions - Options specifying the file to read and how to read it.
13600
+ * @returns Promise resolving to an array of DataTables containing the splat data.
13601
+ *
13602
+ * @example
13603
+ * ```ts
13604
+ * import { readFile, getInputFormat, UrlReadFileSystem } from '@playcanvas/splat-transform';
13605
+ *
13606
+ * const filename = 'scene.ply';
13607
+ * const fileSystem = new UrlReadFileSystem('https://example.com/');
13608
+ * const tables = await readFile({
13609
+ * filename,
13610
+ * inputFormat: getInputFormat(filename),
13611
+ * options: {},
13612
+ * params: [],
13613
+ * fileSystem
13614
+ * });
13615
+ * ```
13616
+ */
13423
13617
  const readFile = async (readFileOptions) => {
13424
13618
  const { filename, inputFormat, options, params, fileSystem } = readFileOptions;
13425
13619
  let result;
@@ -13614,7 +13808,7 @@ class CompressedChunk {
13614
13808
  }
13615
13809
  }
13616
13810
 
13617
- var version = "1.0.0";
13811
+ var version = "1.1.0";
13618
13812
 
13619
13813
  // sort the provided indices into morton order
13620
13814
  const sortMortonOrder = (dataTable, indices) => {
@@ -13733,6 +13927,17 @@ const vertexProps = [
13733
13927
  const shNames$2 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
13734
13928
  // Size of a chunk in the compressed PLY format (number of splats per chunk)
13735
13929
  const CHUNK_SIZE = 256;
13930
+ /**
13931
+ * Writes Gaussian splat data to compressed PLY format.
13932
+ *
13933
+ * Uses quantization and chunking to reduce file size while maintaining
13934
+ * compatibility with PLY-based viewers. Data is sorted using Morton order
13935
+ * for better compression and streaming performance.
13936
+ *
13937
+ * @param options - Options including filename and data table to write.
13938
+ * @param fs - File system for writing the output file.
13939
+ * @ignore
13940
+ */
13736
13941
  const writeCompressedPly = async (options, fs) => {
13737
13942
  const { filename, dataTable } = options;
13738
13943
  const shBands = { '9': 1, '24': 2, '-1': 3 }[shNames$2.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
@@ -13806,6 +14011,16 @@ const writeCompressedPly = async (options, fs) => {
13806
14011
  await writer.close();
13807
14012
  };
13808
14013
 
14014
+ /**
14015
+ * Writes Gaussian splat data to a CSV text file.
14016
+ *
14017
+ * Useful for debugging, analysis, or importing into spreadsheet applications.
14018
+ * Each row represents one splat with all column values separated by commas.
14019
+ *
14020
+ * @param options - Options including filename and data table to write.
14021
+ * @param fs - File system for writing the output file.
14022
+ * @ignore
14023
+ */
13809
14024
  const writeCsv = async (options, fs) => {
13810
14025
  const { filename, dataTable } = options;
13811
14026
  const len = dataTable.numRows;
@@ -13877,6 +14092,21 @@ class MemoryWriter {
13877
14092
  };
13878
14093
  }
13879
14094
  }
14095
+ /**
14096
+ * A file system that writes files to in-memory buffers.
14097
+ *
14098
+ * Useful for generating output without writing to disk, such as when
14099
+ * creating data for download or further processing.
14100
+ *
14101
+ * @example
14102
+ * ```ts
14103
+ * const fs = new MemoryFileSystem();
14104
+ * await writeFile({ filename: 'output.ply', ... }, fs);
14105
+ *
14106
+ * // Get the generated data
14107
+ * const data = fs.results.get('output.ply');
14108
+ * ```
14109
+ */
13880
14110
  class MemoryFileSystem {
13881
14111
  results = new Map();
13882
14112
  createWriter(filename) {
@@ -13943,7 +14173,26 @@ class ZipEntryWriter {
13943
14173
  };
13944
14174
  }
13945
14175
  }
13946
- // FileSystem implementation that writes files into a zip archive
14176
+ /**
14177
+ * A file system that writes files into a ZIP archive.
14178
+ *
14179
+ * Creates a ZIP file containing all written files. Used internally
14180
+ * for bundled output formats like .sog files.
14181
+ *
14182
+ * @example
14183
+ * ```ts
14184
+ * const outputWriter = await fs.createWriter('bundle.zip');
14185
+ * const zipFs = new ZipFileSystem(outputWriter);
14186
+ *
14187
+ * // Write files into the zip
14188
+ * const writer = await zipFs.createWriter('data.json');
14189
+ * await writer.write(jsonData);
14190
+ * await writer.close();
14191
+ *
14192
+ * // Finalize the zip
14193
+ * await zipFs.close();
14194
+ * ```
14195
+ */
13947
14196
  class ZipFileSystem {
13948
14197
  close;
13949
14198
  createWriter;
@@ -14523,6 +14772,17 @@ const cluster1d = async (dataTable, iterations, device) => {
14523
14772
  };
14524
14773
  };
14525
14774
  let webPCodec;
14775
+ /**
14776
+ * Writes Gaussian splat data to the PlayCanvas SOG format.
14777
+ *
14778
+ * SOG (Splat Optimized Graphics) uses WebP lossless compression and k-means
14779
+ * clustering to achieve high compression ratios. Data is stored in textures
14780
+ * for efficient GPU loading.
14781
+ *
14782
+ * @param options - Options including filename, data, and compression settings.
14783
+ * @param fs - File system for writing output files.
14784
+ * @ignore
14785
+ */
14526
14786
  const writeSog = async (options, fs) => {
14527
14787
  const { filename: outputFilename, bundle, dataTable, iterations, createDevice } = options;
14528
14788
  // initialize output stream - use ZipFileSystem for bundled output
@@ -14761,6 +15021,16 @@ const toBase64 = (bytes) => {
14761
15021
  return btoa(binary);
14762
15022
  };
14763
15023
 
15024
+ /**
15025
+ * Writes Gaussian splat data as a self-contained HTML viewer.
15026
+ *
15027
+ * Creates an interactive 3D viewer that can be opened directly in a browser.
15028
+ * Uses the PlayCanvas SuperSplat viewer for rendering.
15029
+ *
15030
+ * @param options - Options including filename, data, and viewer settings.
15031
+ * @param fs - File system for writing output files.
15032
+ * @ignore
15033
+ */
14764
15034
  const writeHtml = async (options, fs) => {
14765
15035
  const { filename, dataTable, viewerSettingsJson, bundle, iterations, createDevice } = options;
14766
15036
  const pad = (text, spaces) => {
@@ -15056,6 +15326,17 @@ const binIndices = (parent, lod) => {
15056
15326
  recurse(parent);
15057
15327
  return result;
15058
15328
  };
15329
+ /**
15330
+ * Writes Gaussian splat data to multi-LOD format with spatial chunking.
15331
+ *
15332
+ * Creates a hierarchical structure with multiple LOD levels, each stored
15333
+ * in separate SOG files. Includes spatial indexing via a binary tree for
15334
+ * efficient streaming and view-dependent loading.
15335
+ *
15336
+ * @param options - Options including filename, data, and chunking parameters.
15337
+ * @param fs - File system for writing output files.
15338
+ * @ignore
15339
+ */
15059
15340
  const writeLod = async (options, fs) => {
15060
15341
  const { filename, dataTable, envDataTable, iterations, createDevice, chunkCount, chunkExtent } = options;
15061
15342
  const outputDir = dirname(filename);
@@ -15207,6 +15488,16 @@ const columnTypeToPlyType = (type) => {
15207
15488
  case 'uint32': return 'uint';
15208
15489
  }
15209
15490
  };
15491
+ /**
15492
+ * Writes Gaussian splat data to a binary PLY file.
15493
+ *
15494
+ * The PLY format is the standard output from 3D Gaussian Splatting training
15495
+ * and is widely supported by visualization tools.
15496
+ *
15497
+ * @param options - Options including filename and PLY data to write.
15498
+ * @param fs - File system for writing the output file.
15499
+ * @ignore
15500
+ */
15210
15501
  const writePly = async (options, fs) => {
15211
15502
  const { filename, plyData } = options;
15212
15503
  const header = [
@@ -15255,6 +15546,20 @@ const writePly = async (options, fs) => {
15255
15546
  await writer.close();
15256
15547
  };
15257
15548
 
15549
+ /**
15550
+ * Determines the output format based on file extension and options.
15551
+ *
15552
+ * @param filename - The filename to analyze.
15553
+ * @param options - Options that may affect format selection.
15554
+ * @returns The detected output format.
15555
+ * @throws Error if the file extension is not recognized.
15556
+ *
15557
+ * @example
15558
+ * ```ts
15559
+ * const format = getOutputFormat('scene.ply', {}); // returns 'ply'
15560
+ * const format2 = getOutputFormat('scene.sog', {}); // returns 'sog-bundle'
15561
+ * ```
15562
+ */
15258
15563
  const getOutputFormat = (filename, options) => {
15259
15564
  const lowerFilename = filename.toLowerCase();
15260
15565
  if (lowerFilename.endsWith('.csv')) {
@@ -15280,6 +15585,27 @@ const getOutputFormat = (filename, options) => {
15280
15585
  }
15281
15586
  throw new Error(`Unsupported output file type: ${filename}`);
15282
15587
  };
15588
+ /**
15589
+ * Writes Gaussian splat data to a file in the specified format.
15590
+ *
15591
+ * Supports multiple output formats including PLY, compressed PLY, CSV, SOG, LOD, and HTML.
15592
+ *
15593
+ * @param writeOptions - Options specifying the data and format to write.
15594
+ * @param fs - File system abstraction for writing files.
15595
+ *
15596
+ * @example
15597
+ * ```ts
15598
+ * import { writeFile, getOutputFormat, MemoryFileSystem } from '@playcanvas/splat-transform';
15599
+ *
15600
+ * const fs = new MemoryFileSystem();
15601
+ * await writeFile({
15602
+ * filename: 'output.sog',
15603
+ * outputFormat: getOutputFormat('output.sog', {}),
15604
+ * dataTable: myDataTable,
15605
+ * options: { iterations: 8 }
15606
+ * }, fs);
15607
+ * ```
15608
+ */
15283
15609
  const writeFile = async (writeOptions, fs) => {
15284
15610
  const { filename, outputFormat, dataTable, envDataTable, options, createDevice } = writeOptions;
15285
15611
  logger.log(`writing '${filename}'...`);
@@ -15387,7 +15713,28 @@ const filter = (dataTable, predicate) => {
15387
15713
  }
15388
15714
  return dataTable.permuteRows(indices.subarray(0, index));
15389
15715
  };
15390
- // process a data table with standard options
15716
+ /**
15717
+ * Applies a sequence of processing actions to splat data.
15718
+ *
15719
+ * Actions are applied in order and can include transformations (translate, rotate, scale),
15720
+ * filters (NaN, value, box, sphere, bands), and analysis (summary).
15721
+ *
15722
+ * @param dataTable - The input splat data.
15723
+ * @param processActions - Array of actions to apply in sequence.
15724
+ * @returns The processed DataTable (may be a new instance if filtered).
15725
+ *
15726
+ * @example
15727
+ * ```ts
15728
+ * import { Vec3 } from 'playcanvas';
15729
+ *
15730
+ * const processed = processDataTable(dataTable, [
15731
+ * { kind: 'scale', value: 0.5 },
15732
+ * { kind: 'translate', value: new Vec3(0, 1, 0) },
15733
+ * { kind: 'filterNaN' },
15734
+ * { kind: 'filterByValue', columnName: 'opacity', comparator: 'gt', value: 0.1 }
15735
+ * ]);
15736
+ * ```
15737
+ */
15391
15738
  const processDataTable = (dataTable, processActions) => {
15392
15739
  let result = dataTable;
15393
15740
  for (let i = 0; i < processActions.length; i++) {