Skip to content

Commit 91600a6

Browse files
authored
Add support for quiet mode (#97)
1 parent d6fccf6 commit 91600a6

File tree

10 files changed

+112
-20
lines changed

10 files changed

+112
-20
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Actions can be repeated and applied in any order:
7575
```none
7676
-h, --help Show this help and exit
7777
-v, --version Show version and exit
78+
-q, --quiet Suppress non-error output
7879
-w, --overwrite Overwrite output file if it exists
7980
-c, --cpu Use CPU for SOG spherical harmonic compression
8081
-i, --iterations <n> Iterations for SOG SH compression (more=better). Default: 10

eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export default [
2727
'@typescript-eslint/ban-ts-comment': 'off',
2828
'@typescript-eslint/no-explicit-any': 'off',
2929
'@typescript-eslint/no-unused-vars': 'off',
30+
'jsdoc/require-param-type': 'off',
31+
'jsdoc/require-returns-type': 'off',
3032
'lines-between-class-members': 'off',
3133
'no-await-in-loop': 'off',
3234
'require-atomic-updates': 'off'

src/gpu/gpu-device.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import {
2727
} from 'playcanvas';
2828
import { create, globals } from 'webgpu';
2929

30+
import { logger } from '../logger';
31+
3032
const initializeGlobals = () => {
3133
Object.assign(globalThis, globals);
3234

@@ -132,7 +134,7 @@ const createDevice = async () => {
132134

133135
// print gpu info
134136
const info = (graphicsDevice as any).gpuAdapter.info;
135-
console.log(`Created gpu device="${info.device || '-'}" arch="${info.architecture || '-'}" descr="${info.description || '-'}"`);
137+
logger.debug(`Created gpu device="${info.device || '-'}" arch="${info.architecture || '-'}" descr="${info.description || '-'}"`);
136138

137139
// create the application
138140
const app = new Application(canvas, { graphicsDevice });

src/index.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Vec3 } from 'playcanvas';
88

99
import { version } from '../package.json';
1010
import { Column, DataTable, TypedArray } from './data-table';
11+
import { logger } from './logger';
1112
import { ProcessAction, processDataTable } from './process';
1213
import { isCompressedPly, decompressPly } from './readers/decompress-ply';
1314
import { readKsplat } from './readers/read-ksplat';
@@ -87,7 +88,7 @@ const readFile = async (filename: string, options: Options, params: Param[]): Pr
8788
const inputFormat = getInputFormat(filename);
8889
let result: DataTable[];
8990

90-
console.log(`reading '${filename}'...`);
91+
logger.info(`reading '${filename}'...`);
9192

9293
if (inputFormat === 'mjs') {
9394
result = [await readMjs(filename, params)];
@@ -126,7 +127,7 @@ const writeFile = async (filename: string, dataTable: DataTable, options: Option
126127
// get the output format, throws on failure
127128
const outputFormat = getOutputFormat(filename);
128129

129-
console.log(`writing '${filename}'...`);
130+
logger.info(`writing '${filename}'...`);
130131

131132
// write to a temporary file and rename on success
132133
const tmpFilename = `.${basename(filename)}.${process.pid}.${Date.now()}.${randomBytes(6).toString('hex')}.tmp`;
@@ -259,6 +260,7 @@ const parseArguments = () => {
259260
overwrite: { type: 'boolean', short: 'w', default: false },
260261
help: { type: 'boolean', short: 'h', default: false },
261262
version: { type: 'boolean', short: 'v', default: false },
263+
quiet: { type: 'boolean', short: 'q', default: false },
262264
cpu: { type: 'boolean', short: 'c', default: false },
263265
iterations: { type: 'string', short: 'i', default: '10' },
264266
'lod-select': { type: 'string', short: 'O', default: '' },
@@ -323,6 +325,7 @@ const parseArguments = () => {
323325
overwrite: v.overwrite,
324326
help: v.help,
325327
version: v.version,
328+
quiet: v.quiet,
326329
cpu: v.cpu,
327330
iterations: parseInteger(v.iterations),
328331
lodSelect: v['lod-select'].split(',').filter(v => !!v).map(parseInteger),
@@ -486,6 +489,7 @@ ACTIONS (can be repeated, in any order)
486489
GLOBAL OPTIONS
487490
-h, --help Show this help and exit
488491
-v, --version Show version and exit
492+
-q, --quiet Suppress non-error output
489493
-w, --overwrite Overwrite output file if it exists
490494
-c, --cpu Use CPU for SOG spherical harmonic compression
491495
-i, --iterations <n> Iterations for SOG SH compression (more=better). Default: 10
@@ -512,21 +516,24 @@ EXAMPLES
512516
`;
513517

514518
const main = async () => {
515-
console.log(`splat-transform v${version}`);
516-
517519
const startTime = hrtime();
518520

519521
// read args
520522
const { files, options } = parseArguments();
521523

524+
// configure logger
525+
logger.setQuiet(options.quiet);
526+
527+
logger.info(`splat-transform v${version}`);
528+
522529
// show version and exit
523530
if (options.version) {
524531
exit(0);
525532
}
526533

527534
// invalid args or show help
528535
if (files.length < 2 || options.help) {
529-
console.error(usage);
536+
logger.error(usage);
530537
exit(1);
531538
}
532539

@@ -541,7 +548,7 @@ const main = async () => {
541548
} else {
542549
// check overwrite before doing any work
543550
if (await fileExists(outputFilename)) {
544-
console.error(`File '${outputFilename}' already exists. Use -w option to overwrite.`);
551+
logger.error(`File '${outputFilename}' already exists. Use -w option to overwrite.`);
545552
exit(1);
546553
}
547554
}
@@ -580,19 +587,19 @@ const main = async () => {
580587
throw new Error('No splats to write');
581588
}
582589

583-
console.log(`Loaded ${dataTable.numRows} gaussians`);
590+
logger.info(`Loaded ${dataTable.numRows} gaussians`);
584591

585592
// write file
586593
await writeFile(outputFilename, dataTable, options);
587594
} catch (err) {
588595
// handle errors
589-
console.error(err);
596+
logger.error(err);
590597
exit(1);
591598
}
592599

593600
const endTime = hrtime(startTime);
594601

595-
console.log(`done in ${endTime[0] + endTime[1] / 1e9}s`);
602+
logger.info(`done in ${endTime[0] + endTime[1] / 1e9}s`);
596603

597604
// something in webgpu seems to keep the process alive after returning
598605
// from main so force exit

src/logger.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
type LogLevel = 'silent' | 'normal';
2+
3+
/**
4+
* Simple logger utility. Currently supports quiet mode. Designed to be extended for multiple log
5+
* levels in the future.
6+
*/
7+
class Logger {
8+
private level: LogLevel = 'normal';
9+
10+
setLevel(level: LogLevel) {
11+
this.level = level;
12+
}
13+
14+
setQuiet(quiet: boolean) {
15+
this.level = quiet ? 'silent' : 'normal';
16+
}
17+
18+
/**
19+
* Log informational messages (file operations, progress, etc.). Suppressed in quiet mode.
20+
* @param {...any} args - The arguments to log.
21+
*/
22+
info(...args: any[]) {
23+
if (this.level !== 'silent') {
24+
console.log(...args);
25+
}
26+
}
27+
28+
/**
29+
* Log warning messages. Suppressed in quiet mode.
30+
* @param {...any} args - The arguments to log.
31+
*/
32+
warn(...args: any[]) {
33+
if (this.level !== 'silent') {
34+
console.warn(...args);
35+
}
36+
}
37+
38+
/**
39+
* Log error messages. Always shown, even in quiet mode.
40+
* @param {...any} args - The arguments to log.
41+
*/
42+
error(...args: any[]) {
43+
console.error(...args);
44+
}
45+
46+
/**
47+
* Log debug/verbose messages. Currently treated the same as info, but can be filtered
48+
* separately in the future.
49+
* @param {...any} args - The arguments to log.
50+
*/
51+
debug(...args: any[]) {
52+
if (this.level !== 'silent') {
53+
console.log(...args);
54+
}
55+
}
56+
57+
/**
58+
* Write progress indicators directly to stdout (without newline). Suppressed in quiet mode.
59+
* @param text - The text to write.
60+
*/
61+
progress(text: string) {
62+
if (this.level !== 'silent') {
63+
process.stdout.write(text);
64+
}
65+
}
66+
67+
/**
68+
* Check if logger is in quiet/silent mode.
69+
* @returns True if the logger is in quiet/silent mode, false otherwise.
70+
*/
71+
isQuiet(): boolean {
72+
return this.level === 'silent';
73+
}
74+
}
75+
76+
// Export singleton instance
77+
export const logger = new Logger();

src/ordering.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DataTable } from './data-table.js';
2+
import { logger } from './logger';
23

34
// sort the compressed indices into morton order
45
const generateOrdering = (dataTable: DataTable, indices: Uint32Array) => {
@@ -51,7 +52,7 @@ const generateOrdering = (dataTable: DataTable, indices: Uint32Array) => {
5152
const zlen = Mz - mz;
5253

5354
if (!isFinite(xlen) || !isFinite(ylen) || !isFinite(zlen)) {
54-
console.log('invalid extents', xlen, ylen, zlen);
55+
logger.debug('invalid extents', xlen, ylen, zlen);
5556
return;
5657
}
5758

@@ -96,7 +97,7 @@ const generateOrdering = (dataTable: DataTable, indices: Uint32Array) => {
9697
}
9798

9899
if (end - start > 256) {
99-
// console.log('sorting', end - start);
100+
// logger.debug('sorting', end - start);
100101
generate(indices.subarray(start, end));
101102
}
102103

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ type Options = {
22
overwrite: boolean;
33
help: boolean;
44
version: boolean;
5+
quiet: boolean;
56
cpu: boolean;
67
iterations: number;
78

src/utils/k-means.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { stdout } from 'node:process';
2-
31
import { Column, DataTable } from '../data-table';
2+
import { logger } from '../logger';
43
import { KdTree } from './kd-tree';
54
import { GpuClustering } from '../gpu/gpu-clustering';
65
import { GpuDevice } from '../gpu/gpu-device';
@@ -159,7 +158,7 @@ const kmeans = async (points: DataTable, k: number, iterations: number, device?:
159158
let converged = false;
160159
let steps = 0;
161160

162-
console.log(`Running k-means clustering: dims=${points.numColumns} points=${points.numRows} clusters=${k} iterations=${iterations}...`);
161+
logger.debug(`Running k-means clustering: dims=${points.numColumns} points=${points.numRows} clusters=${k} iterations=${iterations}...`);
163162

164163
while (!converged) {
165164
if (gpuClustering) {
@@ -188,14 +187,14 @@ const kmeans = async (points: DataTable, k: number, iterations: number, device?:
188187
converged = true;
189188
}
190189

191-
stdout.write('#');
190+
logger.progress('#');
192191
}
193192

194193
if (gpuClustering) {
195194
gpuClustering.destroy();
196195
}
197196

198-
console.log(' done 🎉');
197+
logger.debug(' done 🎉');
199198

200199
return { centroids, labels };
201200
};

src/writers/write-lod.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { dirname, resolve } from 'node:path';
44
import { BoundingBox, Mat4, Quat, Vec3 } from 'playcanvas';
55

66
import { TypedArray, DataTable } from '../data-table';
7+
import { logger } from '../logger';
78
import { generateOrdering } from '../ordering';
89
import { writeSog } from './write-sog.js';
910
import { Options } from '../types';
@@ -87,7 +88,7 @@ const calcBound = (dataTable: DataTable, indices: number[]): Aabb => {
8788
const M = b.getMax();
8889

8990
if (!isFinite(m.x) || !isFinite(m.y) || !isFinite(m.z) || !isFinite(M.x) || !isFinite(M.y) || !isFinite(M.z)) {
90-
console.warn('Skipping invalid bounding box:', { m, M, index });
91+
logger.warn('Skipping invalid bounding box:', { m, M, index });
9192
continue;
9293
}
9394

@@ -264,7 +265,7 @@ const writeLod = async (fileHandle: FileHandle, dataTable: DataTable, outputFile
264265
// write file unit to sog
265266
const outputFile = await open(pathname, 'w');
266267

267-
console.log(`writing ${pathname}...`);
268+
logger.info(`writing ${pathname}...`);
268269

269270
await writeSog(outputFile, unitDataTable, pathname, options, indices);
270271

src/writers/write-sog.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { dirname, resolve } from 'node:path';
33

44
import { Column, DataTable } from '../data-table';
55
import { createDevice, GpuDevice } from '../gpu/gpu-device';
6+
import { logger } from '../logger';
67
import { generateOrdering } from '../ordering';
78
import { FileWriter } from '../serialize/writer';
89
import { ZipWriter } from '../serialize/zip-writer';
@@ -125,7 +126,7 @@ const writeSog = async (fileHandle: FileHandle, dataTable: DataTable, outputFile
125126

126127
const write = async (filename: string, data: Uint8Array, w = width, h = height) => {
127128
const pathname = resolve(dirname(outputFilename), filename);
128-
console.log(`writing '${pathname}'...`);
129+
logger.info(`writing '${pathname}'...`);
129130

130131
// construct the encoder on first use
131132
if (!webPCodec) {

0 commit comments

Comments
 (0)