Skip to content

Commit

Permalink
chore: reorganize layout properties
Browse files Browse the repository at this point in the history
  • Loading branch information
tuner committed Jan 23, 2025
1 parent b8fac9f commit 7de1647
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 80 deletions.
6 changes: 1 addition & 5 deletions extract-default-properties.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { writeFileSync } from "fs";
/** @type {import('ts-json-schema-generator/dist/src/Config').Config} */
const config = {
tsconfig: "./tsconfig.json",
type: "InSchema",
type: "LayoutProperties",
sortProps: false,
};

Expand Down Expand Up @@ -34,10 +34,6 @@ import {
export const DEFAULT_PROPERTIES = {
${generateProps(schema.definitions["LayoutProperties"].properties).join(",\n")}
} as LayoutProperties;
export const DEFAULT_COST_WEIGHTS = {
${generateProps(schema.definitions["CostWeights"].properties).join(",\n")}
} as CostWeights;
`;
return jsCode;
}
Expand Down
13 changes: 5 additions & 8 deletions src/defaultProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import {
} from "./layout.js";

export const DEFAULT_PROPERTIES = {
crossingWeight: 10,
pathLengthWeight: 2,
orderMismatchWeight: 2,
bundleMismatchWeight: 3,
divergenceWeight: 4,
bellTipShape: 0.1,
bellTipSpread: 0.5,
bellStrokeWidth: 1,
Expand All @@ -30,11 +35,3 @@ export const DEFAULT_PROPERTIES = {
showRankTitles: true,
normalsAtPhylogenyRoot: false
} as LayoutProperties;

export const DEFAULT_COST_WEIGHTS = {
crossing: 10,
pathLength: 2,
orderMismatch: 2,
bundleMismatch: 3,
divergence: 4
} as CostWeights;
43 changes: 15 additions & 28 deletions src/gui/gui.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import GUI, { Controller } from "lil-gui";
import { DataTables, filterDataTablesByPatient } from "../data.js";
import { tablesToJellyfish } from "../jellyfish.js";
import { CostWeights, LayoutProperties, optimizeColumns } from "../layout.js";
import { LayoutProperties } from "../layout.js";
import { addInteractions } from "../interactions.js";
import { downloadSvg, downloadPng, downloadPdf } from "./download.js";
import { escapeHtml } from "../utils.js";
import {
DEFAULT_COST_WEIGHTS,
DEFAULT_PROPERTIES,
} from "../defaultProperties.js";
import { DEFAULT_PROPERTIES } from "../defaultProperties.js";

type ControllerStatus = "open" | "closed" | "hidden";

Expand All @@ -28,7 +25,6 @@ export function setupGui(
container: HTMLElement,
tables: DataTables,
customLayoutProps: Partial<LayoutProperties> = {},
customCostWeights: Partial<CostWeights> = {},
controllerStatus: ControllerStatus = "open"
) {
container.innerHTML = HTML_TEMPLATE;
Expand All @@ -44,14 +40,12 @@ export function setupGui(
const querySvg = () =>
jellyfishGui.querySelector(".jellyfish-plot svg") as SVGElement;

const { generalProps, layoutProps, costWeights } =
getSavedOrDefaultSettings();
const { generalProps, layoutProps } = getSavedOrDefaultSettings();

Object.assign(layoutProps, customLayoutProps);
Object.assign(costWeights, customCostWeights);

const saveSettings = () =>
saveSettingsToSessionStorage(generalProps, layoutProps, costWeights);
saveSettingsToSessionStorage(generalProps, layoutProps);

let translateX = 0;
let translateY = 0;
Expand Down Expand Up @@ -81,8 +75,7 @@ export function setupGui(
patients.length > 1
? filterDataTablesByPatient(tables, generalProps.patient)
: tables,
layoutProps,
costWeights
layoutProps
);

generalProps.zoom = 1;
Expand Down Expand Up @@ -167,12 +160,12 @@ export function setupGui(
miscFolder.onChange(onPatientChange);
miscFolder.close();

const weightsFolder = gui.addFolder("Cost weights");
weightsFolder.add(costWeights, "crossing", 0, 10);
weightsFolder.add(costWeights, "pathLength", 0, 10);
weightsFolder.add(costWeights, "orderMismatch", 0, 10);
weightsFolder.add(costWeights, "divergence", 0, 10);
weightsFolder.add(costWeights, "bundleMismatch", 0, 10);
const weightsFolder = optionFolder.addFolder("Cost weights");
weightsFolder.add(layoutProps, "crossingWeight", 0, 10);
weightsFolder.add(layoutProps, "pathLengthWeight", 0, 10);
weightsFolder.add(layoutProps, "orderMismatchWeight", 0, 10);
weightsFolder.add(layoutProps, "divergenceWeight", 0, 10);
weightsFolder.add(layoutProps, "bundleMismatchWeight", 0, 10);
weightsFolder.onChange(onPatientChange);
weightsFolder.close();

Expand Down Expand Up @@ -283,13 +276,12 @@ export function setupGui(
function updatePlot(
jellyfishGui: HTMLElement,
tables: DataTables,
layoutProps: LayoutProperties,
costWeights: CostWeights
layoutProps: LayoutProperties
) {
const plot = jellyfishGui.querySelector(".jellyfish-plot") as HTMLElement;

try {
const svg = tablesToJellyfish(tables, layoutProps, costWeights);
const svg = tablesToJellyfish(tables, layoutProps);
plot.innerHTML = ""; // Purge the old plot
svg.addTo(plot);

Expand All @@ -312,12 +304,11 @@ const STORAGE_KEY = "jellyfish-plotter-settings";

function saveSettingsToSessionStorage(
generalProps: GeneralProperties,
layoutProps: LayoutProperties,
costWeights: CostWeights
layoutProps: LayoutProperties
) {
sessionStorage.setItem(
STORAGE_KEY,
JSON.stringify({ generalProps, layoutProps, costWeights })
JSON.stringify({ generalProps, layoutProps })
);
}

Expand All @@ -334,10 +325,6 @@ function getSavedOrDefaultSettings() {
...DEFAULT_PROPERTIES,
...(settings.layoutProps ?? {}),
} as LayoutProperties,
costWeights: {
...DEFAULT_COST_WEIGHTS,
...(settings.costWeights ?? {}),
} as CostWeights,
};
}

Expand Down
8 changes: 2 additions & 6 deletions src/jellyfish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { DataTables, SampleId, Subclone, validateTables } from "./data.js";
import { treeIterator, treeToNodeArray } from "./tree.js";
import * as d3 from "d3";
import {
CostWeights,
findLegendPlacement,
getNodePlacement,
LayoutProperties,
Expand All @@ -41,15 +40,13 @@ import {
SubcloneMetricsMap,
} from "./composition.js";
import { createDistanceMatrix, jsDivergence } from "./statistics.js";
import { DEFAULT_COST_WEIGHTS } from "./defaultProperties.js";

/**
* This is the main function that glues everything together.
*/
export function tablesToJellyfish(
tables: DataTables,
layoutProps: LayoutProperties,
constWeights: CostWeights = DEFAULT_COST_WEIGHTS
layoutProps: LayoutProperties
) {
validateTables(tables);

Expand Down Expand Up @@ -181,8 +178,7 @@ export function tablesToJellyfish(
nodesInColumns,
layoutProps,
preferredOrders,
sampleDistanceMatrix,
constWeights
sampleDistanceMatrix
);

/**
Expand Down
54 changes: 21 additions & 33 deletions src/layout.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { BellPlotProperties } from "./bellplot.js";
import { DEFAULT_COST_WEIGHTS } from "./defaultProperties.js";
import { getBoundingBox, isIntersecting, Rect } from "./geometry.js";
import { NODE_TYPES, SampleTreeNode } from "./sampleTree.js";
import { treeToNodeArray } from "./tree.js";
import { fisherYatesShuffle, SeededRNG } from "./utils.js";

/**
* This is just an entry point for ts-json-schema-generator.
*/
export interface InSchema {
layoutProps: LayoutProperties;
costWeights: CostWeights;
}

export interface NodePosition {
node: SampleTreeNode;
top: number;
height: number;
}

export interface LayoutProperties extends BellPlotProperties {
export interface LayoutProperties extends BellPlotProperties, CostWeights {
/**
* Height of real sample nodes
*
Expand Down Expand Up @@ -167,20 +158,20 @@ export interface LayoutProperties extends BellPlotProperties {

export interface CostWeights {
/**
* Weight for tentacle bundles between two pairs of samples crossing each other
* Weight for tentacle bundles between two pairs of samples crossing each other.
*
* @minimum 0
* @default 10
*/
crossing: number;
crossingWeight: number;

/**
* Weight for the total length of the paths (tentacle bundles) connecting samples
* Weight for the total length of the paths (tentacle bundles) connecting samples.
*
* @minimum 0
* @default 2
*/
pathLength: number;
pathLengthWeight: number;

/**
* Weight for the mismatch in the order of samples. The order is based on the
Expand All @@ -189,7 +180,7 @@ export interface CostWeights {
* @minimum 0
* @default 2
*/
orderMismatch: number;
orderMismatchWeight: number;

/**
* Weight for the mismatch in the placement of bundles. The "optimal" placement is
Expand All @@ -199,15 +190,15 @@ export interface CostWeights {
* @minimum 0
* @default 3
*/
bundleMismatch: number;
bundleMismatchWeight: number;

/**
* Weight for the sum of divergences between adjacent samples
* Weight for the sum of divergences between adjacent samples.
*
* @minimum 0
* @default 4
*/
divergence: number;
divergenceWeight: number;
}

export function sampleTreeToColumns(sampleTree: SampleTreeNode) {
Expand Down Expand Up @@ -266,8 +257,7 @@ function calculateCost(
stackedColumns: NodePosition[][],
layoutProps: LayoutProperties,
preferredOrders: number[],
sampleDistanceMatrix: number[][] | null,
costWeights: CostWeights
sampleDistanceMatrix: number[][] | null
) {
const columnPaths: number[][][] = [];

Expand Down Expand Up @@ -380,46 +370,46 @@ function calculateCost(
}

const totalCrossings =
costWeights.crossing > 0
layoutProps.crossingWeight > 0
? columnPaths.reduce((acc, paths) => acc + getNumberOfCrossings(paths), 0)
: 0;

const totalPathLength =
costWeights.pathLength > 0
layoutProps.pathLengthWeight > 0
? columnPaths.reduce((acc, paths) => acc + getTotalPathLength(paths), 0) /
(layoutProps.sampleHeight + layoutProps.sampleSpacing)
: 0;

const totalOrderMismatch =
costWeights.orderMismatch > 0
layoutProps.orderMismatchWeight > 0
? stackedColumns.reduce(
(acc, column) => acc + getOrderMismatch(column, false),
0
)
: 0;

const totalDivergenceMismatch =
costWeights.divergence > 0
layoutProps.divergenceWeight > 0
? stackedColumns.reduce(
(acc, column) => acc + getDivergenceMismatch(column),
0
)
: 0;

const totalBundleMismatch =
costWeights.bundleMismatch > 0
layoutProps.bundleMismatchWeight > 0
? stackedColumns.reduce(
(acc, column) => acc + getOrderMismatch(column, true),
0
)
: 0;

return (
totalCrossings * costWeights.crossing +
totalPathLength * costWeights.pathLength +
totalOrderMismatch * costWeights.orderMismatch +
totalDivergenceMismatch * costWeights.divergence +
totalBundleMismatch * costWeights.bundleMismatch
totalCrossings * layoutProps.crossingWeight +
totalPathLength * layoutProps.pathLengthWeight +
totalOrderMismatch * layoutProps.orderMismatchWeight +
totalDivergenceMismatch * layoutProps.divergenceWeight +
totalBundleMismatch * layoutProps.bundleMismatchWeight
);
}

Expand Down Expand Up @@ -475,7 +465,6 @@ export function optimizeColumns(
layoutProps: LayoutProperties,
preferredOrders: number[],
sampleDistanceMatrix: number[][] | null,
costWeights: CostWeights = DEFAULT_COST_WEIGHTS,
random: () => number = SeededRNG(0),
randomizationRounds: number = 10000
) {
Expand All @@ -494,8 +483,7 @@ export function optimizeColumns(
stackedColumns,
layoutProps,
preferredOrders,
sampleDistanceMatrix,
costWeights
sampleDistanceMatrix
);
if (cost < bestCost) {
bestResult = stackedColumns;
Expand Down

0 comments on commit 7de1647

Please sign in to comment.