Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ <h1>Augmenta × Three.js</h1>
<details id="connection-advanced" class="advanced-panel">
<summary>
<span class="advanced-label">Advanced</span>
<span id="connection-advanced-summary" class="advanced-summary-value">6060 · Auto · ×1</span>
<span id="connection-advanced-summary" class="advanced-summary-value">6060 · Auto · Compressed · ×1</span>
</summary>
<div class="advanced-content">
<div class="advanced-grid connection-advanced-grid">
Expand All @@ -158,6 +158,13 @@ <h1>Augmenta × Three.js</h1>
<option value="2">V2 — legacy IDs</option>
</select>
</div>
<div>
<label for="compression">Compression</label>
<select id="compression">
<option value="compressed" selected>Compressed</option>
<option value="uncompressed">Uncompressed</option>
</select>
</div>
<div>
<label for="downsample">Point downsample</label>
<input id="downsample" type="number" min="1" step="1" value="1">
Expand Down
33 changes: 18 additions & 15 deletions src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export function createConnectionController({
let streamClouds;
let streamClusters;
let streamClusterPoints;
let useCompression;
try {
const settings = getSettings();
const targets = ensureTargetPlan(settings.address, settings.port);
Expand All @@ -166,6 +167,7 @@ export function createConnectionController({
streamClouds = settings.streamClouds !== false;
streamClusters = settings.streamClusters !== false;
streamClusterPoints = settings.streamClusterPoints !== false;
useCompression = settings.useCompression !== false;
} catch (error) {
wantsConnection = false;
retrying = false;
Expand All @@ -184,15 +186,17 @@ export function createConnectionController({
}

let decompressor;
try {
decompressor = await getZstdDecompressor();
} catch (error) {
if (attemptGeneration !== connectionAttemptGeneration || !wantsConnection) return;
wantsConnection = false;
retrying = false;
const message = error instanceof Error ? error.message : String(error);
publish('error', `Could not initialize Zstd decompression: ${message}`);
return;
if (useCompression) {
try {
decompressor = await getZstdDecompressor();
} catch (error) {
if (attemptGeneration !== connectionAttemptGeneration || !wantsConnection) return;
wantsConnection = false;
retrying = false;
const message = error instanceof Error ? error.message : String(error);
publish('error', `Could not initialize Zstd decompression: ${message}`);
return;
}
}

if (attemptGeneration !== connectionAttemptGeneration || !wantsConnection) return;
Expand All @@ -209,11 +213,10 @@ export function createConnectionController({
streamClusters,
streamClusterPoints,
streamZonePoints: true,
// Keep the realtime stream compressed by default, matching the Unity,
// TouchDesigner and VVVV clients. This cuts WebSocket bandwidth and
// backlog pressure; zstddec is initialized before the connection and
// the JS SDK invokes it synchronously before parsing each binary frame.
useCompression: true,
// Compression stays enabled by default, matching the Unity,
// TouchDesigner and VVVV clients, but Advanced can disable it for
// diagnostics or compatibility checks.
useCompression,
displayPointIntensity: true,
// Quaternions preserve Pleiades' exact OBB orientation. The viewer
// performs the left-handed -> right-handed basis reflection explicitly.
Expand All @@ -238,7 +241,7 @@ export function createConnectionController({
socketOpen = true;
publish(
'connected',
`Connected to ${target.label}. Protocol V${activeVersion}; Zstd-compressed debug stream.`
`Connected to ${target.label}. Protocol V${activeVersion}; ${useCompression ? 'Zstd-compressed' : 'uncompressed'} debug stream.`
);
});

Expand Down
12 changes: 10 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function refreshFavicon() {
refreshFavicon();

const ui = {
app: $('#app'), sidebar: $('#sidebar'), sidebarResizer: $('#sidebar-resizer'), serverAddress: $('#server-address'), port: $('#port'), protocol: $('#protocol'), downsample: $('#downsample'), connect: $('#connect'),
app: $('#app'), sidebar: $('#sidebar'), sidebarResizer: $('#sidebar-resizer'), serverAddress: $('#server-address'), port: $('#port'), protocol: $('#protocol'), compression: $('#compression'), downsample: $('#downsample'), connect: $('#connect'),
demo: $('#demo'), status: $('#status'), note: $('#connection-note'), summary: $('#summary'),
debug: $('#debug-content'), clear: $('#clear'), resetCamera: $('#reset-camera'), scenes: $('#scenes'),
sidebarToggle: $('#sidebar-toggle'), viewerTitle: $('.viewer-title'), connectionQrVisibility: $('.connection-qr-visibility'),
Expand Down Expand Up @@ -182,6 +182,7 @@ function applyConnectionSettings(settings) {
if (typeof settings.address === 'string') ui.serverAddress.value = settings.address;
if (typeof settings.port === 'string') ui.port.value = settings.port;
if (typeof settings.protocol === 'string') ui.protocol.value = settings.protocol;
if (typeof settings.compression === 'string') ui.compression.value = settings.compression;
if (typeof settings.downsample === 'string') ui.downsample.value = settings.downsample;
}

Expand Down Expand Up @@ -418,8 +419,9 @@ function updateDisplaySectionSummary() {
function updateConnectionAdvancedSummary() {
const protocolLabel = ui.protocol.selectedOptions[0]?.textContent?.split(' — ')[0]
?? ui.protocol.value;
const compressionLabel = ui.compression.selectedOptions[0]?.textContent ?? ui.compression.value;
ui.connectionAdvancedSummary.textContent =
`${ui.port.value || '—'} · ${protocolLabel} · ×${ui.downsample.value || '—'}`;
`${ui.port.value || '—'} · ${protocolLabel} · ${compressionLabel} · ×${ui.downsample.value || '—'}`;
}

function syncSceneSelector() {
Expand Down Expand Up @@ -478,7 +480,9 @@ function getConnectionSettings() {
address: ui.serverAddress.value,
port: ui.port.value,
protocol: ui.protocol.value,
compression: ui.compression.value,
downsample: ui.downsample.value,
useCompression: ui.compression.value !== 'uncompressed',
streamClusters: ui.requestClusters.checked,
streamClusterPoints: ui.requestClusterPoints.checked,
streamClouds: ui.requestGeneralPoints.checked
Expand Down Expand Up @@ -747,6 +751,10 @@ ui.protocol.addEventListener('change', () => {
saveConnectionField('protocol', ui.protocol.value);
restartConnection('Protocol changed');
});
ui.compression.addEventListener('change', () => {
saveConnectionField('compression', ui.compression.value);
restartConnection('Compression changed');
});
ui.downsample.addEventListener('change', () => {
restartConnection('Downsample changed');
});
Expand Down
8 changes: 7 additions & 1 deletion src/share-link.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const CONNECTION_QUERY_KEYS = ['address', 'port', 'protocol', 'downsample'];
const CONNECTION_QUERY_KEYS = ['address', 'port', 'protocol', 'compression', 'downsample'];

export function normalizeConnectionOptions(settings) {
const options = {};
Expand All @@ -16,6 +16,11 @@ export function normalizeConnectionOptions(settings) {
options.protocol = protocol;
}

const compression = String(settings?.compression ?? '');
if (['compressed', 'uncompressed'].includes(compression)) {
options.compression = compression;
}

const downsample = Number(settings?.downsample);
if (Number.isInteger(downsample) && downsample >= 1) {
options.downsample = String(downsample);
Expand Down Expand Up @@ -51,6 +56,7 @@ export function readConnectionOptionsFromUrl(urlValue) {
address: url.searchParams.get('address'),
port: url.searchParams.get('port'),
protocol: url.searchParams.get('protocol'),
compression: url.searchParams.get('compression'),
downsample: url.searchParams.get('downsample')
});
}
20 changes: 16 additions & 4 deletions tests/share-link.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test('connection share URL preserves the page and embeds current connection opti
address: '192.168.1.42',
port: '6060',
protocol: '3',
compression: 'uncompressed',
downsample: '2'
}
);
Expand All @@ -24,38 +25,41 @@ test('connection share URL preserves the page and embeds current connection opti
assert.equal(parsed.searchParams.get('address'), '192.168.1.42');
assert.equal(parsed.searchParams.get('port'), '6060');
assert.equal(parsed.searchParams.get('protocol'), '3');
assert.equal(parsed.searchParams.get('compression'), 'uncompressed');
assert.equal(parsed.searchParams.get('downsample'), '2');
assert.equal(parsed.hash, '');
});

test('connection options in a shared URL are validated', () => {
assert.deepEqual(
readConnectionOptionsFromUrl(
'https://example.test/?address=augmenta-WA12031.local&port=6060&protocol=auto&downsample=4'
'https://example.test/?address=augmenta-WA12031.local&port=6060&protocol=auto&compression=compressed&downsample=4'
),
{
address: 'augmenta-WA12031.local',
port: '6060',
protocol: 'auto',
compression: 'compressed',
downsample: '4'
}
);

assert.deepEqual(
readConnectionOptionsFromUrl(
'https://example.test/?address=%20&port=70000&protocol=9&downsample=0'
'https://example.test/?address=%20&port=70000&protocol=9&compression=invalid&downsample=0'
),
{}
);
});

test('regenerating a share URL replaces stale connection query values', () => {
const url = buildConnectionShareUrl(
'https://example.test/?address=old&port=1&protocol=2&downsample=99',
'https://example.test/?address=old&port=1&protocol=2&compression=uncompressed&downsample=99',
{
address: '10.0.0.5',
port: 6060,
protocol: 'auto',
compression: 'compressed',
downsample: 1
}
);
Expand All @@ -64,6 +68,7 @@ test('regenerating a share URL replaces stale connection query values', () => {
address: '10.0.0.5',
port: '6060',
protocol: 'auto',
compression: 'compressed',
downsample: '1'
});
});
Expand All @@ -74,23 +79,27 @@ test('shared connection options override a local view without mutating local pre
address: 'augmenta-local.local',
port: '6060',
protocol: 'auto',
compression: 'compressed',
downsample: '1'
};
const shared = {
address: '192.168.1.42',
protocol: '3'
protocol: '3',
compression: 'uncompressed'
};

assert.deepEqual(resolveConnectionOptions(local, shared), {
address: '192.168.1.42',
port: '6060',
protocol: '3',
compression: 'uncompressed',
downsample: '1'
});
assert.deepEqual(local, {
address: 'augmenta-local.local',
port: '6060',
protocol: 'auto',
compression: 'compressed',
downsample: '1'
});
});
Expand All @@ -100,18 +109,21 @@ test('connection option normalization keeps only valid local values', () => {
address: ' augmenta-server ',
port: 6060,
protocol: '2',
compression: 'uncompressed',
downsample: 4
}), {
address: 'augmenta-server',
port: '6060',
protocol: '2',
compression: 'uncompressed',
downsample: '4'
});

assert.deepEqual(normalizeConnectionOptions({
address: ' ',
port: 0,
protocol: '7',
compression: 'invalid',
downsample: -1
}), {});
});
5 changes: 3 additions & 2 deletions tests/websocket-compression.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ test('bundled zstd decoder can decode Augmenta binary payloads synchronously aft
);
});

test('Three.js runtime requests compression and ships the decoder in the Pages artifact', () => {
test('Three.js runtime supports selectable compression and ships the decoder in the Pages artifact', () => {
const connection = readFileSync(new URL('../src/connection.js', import.meta.url), 'utf8');
const index = readFileSync(new URL('../index.html', import.meta.url), 'utf8');
const assemble = readFileSync(new URL('../scripts/assemble-site.mjs', import.meta.url), 'utf8');

assert.match(connection, /useCompression:\s*true/);
assert.match(connection, /useCompression\s*=\s*settings\.useCompression\s*!==\s*false/);
assert.match(connection, /useCompression,/);
assert.match(connection, /decompressor,/);
assert.match(connection, /import\('zstddec'\)/);
assert.match(index, /zstddec@0\.3\.1\/\+esm/);
Expand Down
Loading