Skip to content

Commit 6529f25

Browse files
authored
feat: add --point-size argument (#44)
1 parent 97b4f7a commit 6529f25

File tree

13 files changed

+60
-12
lines changed

13 files changed

+60
-12
lines changed

packages/backend/embedding_atlas/cli.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ def find_available_port(start_port: int, max_attempts: int = 10, host="localhost
202202
type=str,
203203
help="Export the visualization as a standalone web application to the specified ZIP file and exit.",
204204
)
205+
@click.option(
206+
"--point-size",
207+
type=float,
208+
default=None,
209+
help="Size of points in the embedding view (default: automatically calculated based on density).",
210+
)
205211
@click.version_option(version=__version__, package_name="embedding_atlas")
206212
def main(
207213
inputs,
@@ -227,6 +233,7 @@ def main(
227233
port: int,
228234
enable_auto_port: bool,
229235
export_application: str | None,
236+
point_size: float | None,
230237
):
231238
logging.basicConfig(
232239
level=logging.INFO,
@@ -314,6 +321,9 @@ def main(
314321
},
315322
}
316323

324+
if point_size is not None:
325+
metadata["point_size"] = point_size
326+
317327
if x_column is not None and y_column is not None:
318328
metadata["columns"]["embedding"] = {
319329
"x": x_column,

packages/component/src/lib/embedding_view/EmbeddingView.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
automaticLabels = false,
2121
mode = "density",
2222
minimumDensity = 1 / 16,
23+
pointSize = null,
2324
customTooltip = null,
2425
customOverlay = null,
2526
querySelection = null,
@@ -85,4 +86,5 @@
8586
onViewportState={onViewportState}
8687
rangeSelection={rangeSelection}
8788
onRangeSelection={onRangeSelection}
89+
userPointSize={pointSize}
8890
/>

packages/component/src/lib/embedding_view/EmbeddingViewImpl.svelte

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
pixelWidth: number,
6767
pixelHeight: number,
6868
pixelRatio: number,
69+
userPointSize: number | null,
6970
) {
7071
// Convert max density to per unit point (aka., CSS px unit).
7172
let viewDimension = Math.max(pixelWidth, pixelHeight) / pixelRatio;
@@ -81,8 +82,16 @@
8182
8283
let factor = (Math.min(Math.max((scaleLevel - thresholdLevel) * 2, -1), 1) + 1) / 2;
8384
84-
let pointSizeAtThreshold = 0.25 / Math.sqrt(maxPointDensity);
85-
let pointSize = Math.max(0.2, Math.min(5, pointSizeAtThreshold)) * pixelRatio;
85+
let pointSize: number;
86+
if (userPointSize != null) {
87+
// Use user-provided point size, scaled by pixel ratio
88+
pointSize = userPointSize * pixelRatio;
89+
} else {
90+
// Use automatic calculation based on density
91+
let pointSizeAtThreshold = 0.25 / Math.sqrt(maxPointDensity);
92+
pointSize = Math.max(0.2, Math.min(5, pointSizeAtThreshold)) * pixelRatio;
93+
}
94+
8695
let densityAlpha = 1 - factor;
8796
let pointsAlpha = 0.5 + factor * 0.5;
8897
@@ -160,6 +169,7 @@
160169
rangeSelection = null,
161170
defaultViewportState = null,
162171
viewportState = null,
172+
userPointSize = null,
163173
customTooltip = null,
164174
customOverlay = null,
165175
onViewportState = null,
@@ -236,6 +246,7 @@
236246
pixelWidth,
237247
pixelHeight,
238248
pixelRatio,
249+
userPointSize,
239250
),
240251
);
241252
let pointSize = $derived(viewingParams.pointSize);

packages/component/src/lib/embedding_view/EmbeddingViewMosaic.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
automaticLabels = false,
4848
mode = "density",
4949
minimumDensity = 1 / 16,
50+
pointSize = null,
5051
customTooltip = null,
5152
customOverlay = null,
5253
onViewportState = null,
@@ -412,4 +413,5 @@
412413
effectiveRangeSelection = v;
413414
onRangeSelection?.(v);
414415
}}
416+
userPointSize={pointSize}
415417
/>

packages/component/src/lib/embedding_view/embedding_view_api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ export interface EmbeddingViewProps {
5757
*/
5858
minimumDensity?: number | null;
5959

60+
/** Override the automatically calculated point size. If not specified, point size is calculated based on density. */
61+
pointSize?: number | null;
62+
6063
/** A custom renderer to draw the tooltip content. */
6164
customTooltip?: CustomComponent<HTMLDivElement, { tooltip: DataPoint }> | null;
6265

packages/component/src/lib/embedding_view/embedding_view_mosaic_api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ export interface EmbeddingViewMosaicProps {
9999
* The density is measured as number of points per square points (aka., px in CSS units). */
100100
minimumDensity?: number | null;
101101

102+
/** Override the automatically calculated point size. If not specified, point size is calculated based on density. */
103+
pointSize?: number | null;
104+
102105
/** A custom renderer to draw the tooltip content. */
103106
customTooltip?: CustomComponent<HTMLDivElement, { tooltip: DataPoint }> | null;
104107

packages/viewer/src/Test.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
import Viewer from "./Viewer.svelte";
66
7-
import type { DataColumns, DataSource } from "./data_source.js";
7+
import type { ViewerConfig, DataSource } from "./data_source.js";
88
import { initializeDatabase } from "./lib/database_utils.js";
99
1010
export class TestDataSource implements DataSource {
@@ -14,7 +14,7 @@
1414
this.count = count;
1515
}
1616
17-
async initializeCoordinator(coordinator: Coordinator, table: string): Promise<DataColumns> {
17+
async initializeCoordinator(coordinator: Coordinator, table: string): Promise<ViewerConfig> {
1818
await initializeDatabase(coordinator, "wasm");
1919
2020
await coordinator.exec(`

packages/viewer/src/Viewer.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import EmbeddingAtlas from "./lib/EmbeddingAtlas.svelte";
77
import Spinner from "./lib/Spinner.svelte";
88
9-
import type { DataColumns, DataSource } from "./data_source.js";
9+
import type { ViewerConfig, DataSource } from "./data_source.js";
1010
import type { EmbeddingAtlasState } from "./lib/api.js";
1111
import { systemDarkMode } from "./lib/dark_mode_store.js";
1212
import { type ExportFormat } from "./lib/mosaic_exporter.js";
@@ -25,7 +25,7 @@
2525
let error = $state(false);
2626
let status = $state("Loading...");
2727
let initialState: any | null = $state.raw(null);
28-
let columns: DataColumns | null = $state.raw(null);
28+
let columns: ViewerConfig | null = $state.raw(null);
2929
3030
onMount(async () => {
3131
try {
@@ -71,6 +71,7 @@
7171
neighborsColumn={columns.neighbors}
7272
cache={dataSource.cache}
7373
automaticLabels={true}
74+
pointSize={columns.pointSize}
7475
onExportApplication={dataSource.downloadArchive ? onDownloadArchive : null}
7576
onExportSelection={dataSource.downloadSelection ? onExportSelection : null}
7677
onStateChange={debounce(onStateChange, 200)}

packages/viewer/src/backend_data_source.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import type { Coordinator } from "@uwdata/mosaic-core";
44
import * as SQL from "@uwdata/mosaic-sql";
55

6-
import type { DataColumns, DataSource } from "./data_source.js";
6+
import type { DataSource, ViewerConfig } from "./data_source.js";
77
import { initializeDatabase } from "./lib/database_utils.js";
88
import { exportMosaicSelection, filenameForSelection, type ExportFormat } from "./lib/mosaic_exporter.js";
99
import { downloadBuffer } from "./lib/utils.js";
@@ -22,13 +22,14 @@ function joinUrl(a: string, b: string) {
2222
}
2323

2424
interface Metadata {
25-
columns: DataColumns;
25+
columns: ViewerConfig;
2626
is_static?: boolean;
2727
database?: {
2828
type: "wasm" | "socket" | "rest";
2929
uri?: string;
3030
load?: boolean;
3131
};
32+
point_size?: number;
3233
}
3334

3435
export class BackendDataSource implements DataSource {
@@ -50,7 +51,7 @@ export class BackendDataSource implements DataSource {
5051
coordinator: Coordinator,
5152
table: string,
5253
onStatus: (message: string) => void,
53-
): Promise<DataColumns> {
54+
): Promise<ViewerConfig> {
5455
let metadata = await this.metadata();
5556

5657
onStatus("Initializing DuckDB...");
@@ -90,7 +91,10 @@ export class BackendDataSource implements DataSource {
9091
};
9192
}
9293

93-
return metadata.columns;
94+
return {
95+
...metadata.columns,
96+
pointSize: metadata.point_size,
97+
};
9498
}
9599

96100
private async fetchEndpoint(endpoint: string, init?: RequestInit) {

packages/viewer/src/data_source.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface Cache {
1212
set(key: string, value: any): Promise<void>;
1313
}
1414

15-
export interface DataColumns {
15+
export interface ViewerConfig {
1616
/** The column for unique identifiers */
1717
id: string;
1818
/** The columns for the embedding view */
@@ -27,6 +27,9 @@ export interface DataColumns {
2727

2828
/** The column for pre-computed nearest neighbors */
2929
neighbors?: string;
30+
31+
/** The point size for the embedding view */
32+
pointSize?: number;
3033
}
3134

3235
/** A data source for the viewer */
@@ -36,7 +39,7 @@ export interface DataSource {
3639
coordinator: Coordinator,
3740
table: string,
3841
onStatus: (message: string) => void,
39-
): Promise<DataColumns>;
42+
): Promise<ViewerConfig>;
4043

4144
/** Downloads a zip archive of the dataset plus static assets of the viewer */
4245
downloadArchive?: () => Promise<void>;

0 commit comments

Comments
 (0)