Skip to content
Open
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,13 @@ packages/react-devtools-scheduling-profiler/dist# See https://help.github.com/ar
.env.test.local
.env.production.local
.prettierrc
AGENT.md
AGENT*.md
*SPEC.md
thoughts/
docs/
.memory/
.handoffs/
.gitignore

*.rb
*.arrow
Expand Down
148 changes: 79 additions & 69 deletions src/components/binPlot/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import { measureText, segmentAttributes } from "../../helpers/utility";
import { maxSeparatorsCount } from "../../helpers/segmentWidth";
import {
generateCopyStateSeparators,
metadataToCopyStateFit,
selectActiveCopyStateFit,
} from "../purityPloidySlider/copyStateFit";
import PurityPloidySlider from "../purityPloidySlider";
import Wrapper from "./index.style";

const margins = {
Expand Down Expand Up @@ -60,6 +66,22 @@ class BinPlot extends Component {
this.renderZoom();
}

getActiveCopyStateFit(metadataFit = metadataToCopyStateFit(this.props.separatorsConfig)) {
const { activeCopyStateFit } = this.props;
const fitUiState = {
metadataFit:
activeCopyStateFit?.source === "metadata" ? activeCopyStateFit : metadataFit,
previewFit:
activeCopyStateFit?.source === "preview" ? activeCopyStateFit : null,
appliedOverrideFit:
activeCopyStateFit?.source === "appliedOverride"
? activeCopyStateFit
: null,
};

return selectActiveCopyStateFit(fitUiState);
}

getPlotConfiguration() {
const {
width,
Expand All @@ -80,27 +102,30 @@ class BinPlot extends Component {
let panelWidth = stageWidth;
let panelHeight = stageHeight;

const { beta, purity } = separatorsConfig;
let a = (2 * (1 - purity)) / purity;
let b = 1 / beta;
let ppfit_intercept = a / b;
let ppfit_slope = beta;

let finalMaxMean = ppfit_intercept + maxSeparatorsCount * ppfit_slope;
const metadataFit = metadataToCopyStateFit(separatorsConfig);
const activeFit = this.getActiveCopyStateFit(metadataFit);
const separators = generateCopyStateSeparators(
activeFit,
maxSeparatorsCount
);
const metadataSeparators = generateCopyStateSeparators(
metadataFit,
maxSeparatorsCount
);
let finalMaxMean = metadataSeparators[maxSeparatorsCount].segmentMean;

let filteredData = data
.filter((d) => d.metadata.mean)
.map((d) => {
d.metadata.mean = d3.min([d.metadata.mean, finalMaxMean]);
return d;
});
.map((d) => ({
...d,
metadata: {
...d.metadata,
mean: d3.min([d.metadata.mean, finalMaxMean]),
},
}));

let extent = [0, finalMaxMean];

let separators = d3
.range(0, maxSeparatorsCount + 1)
.map((i) => ppfit_slope * i + ppfit_intercept);

let num = Math.ceil(panelWidth / minBarWidth);
let step = (extent[1] - extent[0]) / num;

Expand Down Expand Up @@ -201,6 +226,7 @@ class BinPlot extends Component {
chromoBins,
selectSegment,
separators,
activeCopyStateFit: activeFit,
};
}

Expand Down Expand Up @@ -306,6 +332,7 @@ class BinPlot extends Component {
chromoBins,
selectSegment,
separators,
activeCopyStateFit,
} = this.getPlotConfiguration();

const { tooltip, segmentId, currentTransform } = this.state;
Expand Down Expand Up @@ -359,61 +386,34 @@ class BinPlot extends Component {
}}
/>
<g key={`panel`} id={`panel`} transform={`translate(${[0, 0]})`}>
<g clipPath="url(#cuttOffViewPane1)">
{separators.map((d, i) => (
<g key={i}>
<line
transform={`translate(${[xScale(d), 0]})`}
y2={panelHeight + 15}
stroke="#FFD6D6"
strokeDasharray="4 1"
<PurityPloidySlider
activeCopyStateFit={activeCopyStateFit}
clipPath="url(#cuttOffViewPane1)"
onCopyStateFitPreview={this.props.onCopyStateFitPreview}
panelHeight={panelHeight}
separators={separators}
xScale={xScale}
>
<g clipPath="url(#cuttOffViewPane2)">
{series.map((d, i) => (
<rect
key={i}
fill={chromoBins[d.chromosome]?.color}
x={xScale(d.xPos)}
width={xScale(d.xPosTo) - xScale(d.xPos)}
y={d.cumulativeWidthPixels}
height={d.widthPixels}
onMouseMove={(e) => this.handleMouseMove(e, d, i)}
onMouseOut={(e) => this.handleMouseOut(e, d)}
onClick={(e) => selectSegment(d)}
stroke="#FFF"
strokeWidth={0.5}
rx={1}
opacity={!segmentId || d.iid === segmentId ? 1 : 0.13}
/>
<text
transform={`translate(${[xScale(d), 0]})`}
textAnchor="middle"
fill={d3.rgb("#FFD6D6").darker()}
dy="-3"
fontSize="10"
opacity={
xScale(d) - xScale(separators[i - 1]) < 30 ? i % 2 : 1
}
>
{i}
</text>
<text
transform={`translate(${[xScale(d), panelHeight + 20]})`}
textAnchor="middle"
fill={d3.rgb("#FFD6D6").darker()}
dy="5"
fontSize="10"
opacity={
xScale(d) - xScale(separators[i - 1]) < 30 ? i % 2 : 1
}
>
{d3.format(".3f")(d)}
</text>
</g>
))}
</g>
<g clipPath="url(#cuttOffViewPane2)">
{series.map((d, i) => (
<rect
key={i}
fill={chromoBins[d.chromosome]?.color}
x={xScale(d.xPos)}
width={xScale(d.xPosTo) - xScale(d.xPos)}
y={d.cumulativeWidthPixels}
height={d.widthPixels}
onMouseMove={(e) => this.handleMouseMove(e, d, i)}
onMouseOut={(e) => this.handleMouseOut(e, d)}
onClick={(e) => selectSegment(d)}
stroke="#FFF"
strokeWidth={0.5}
rx={1}
opacity={!segmentId || d.iid === segmentId ? 1 : 0.13}
/>
))}
</g>
))}
</g>
</PurityPloidySlider>
<g
className="axis--y y-axis-container"
transform={`translate(${[margins.gap, 0]})`}
Expand Down Expand Up @@ -483,6 +483,16 @@ BinPlot.propTypes = {
height: PropTypes.number.isRequired,
data: PropTypes.array,
markValue: PropTypes.number,
activeCopyStateFit: PropTypes.shape({
slope: PropTypes.number.isRequired,
intercept: PropTypes.number.isRequired,
spacing: PropTypes.number.isRequired,
zeroCopyOffset: PropTypes.number.isRequired,
purity: PropTypes.number.isRequired,
ploidy: PropTypes.number.isRequired,
source: PropTypes.oneOf(["metadata", "preview", "appliedOverride"]).isRequired,
}),
onCopyStateFitPreview: PropTypes.func,
};
BinPlot.defaultProps = {
data: [],
Expand Down
2 changes: 2 additions & 0 deletions src/components/binPlot/index.style.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import styled from "styled-components";

const Wrapper = styled.div`
position: relative;

.ant-wrapper {
background: white;
padding: 0px;
Expand Down
99 changes: 94 additions & 5 deletions src/components/binPlotPanel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@ import * as htmlToImage from "html-to-image";
import * as d3 from "d3";
import Wrapper from "./index.style";
import BinPlot from "../binPlot";
import CopyStateFitControls from "../purityPloidySlider/copyStateFitControls";
import TracksModal from "../tracksModal";
import { CgArrowsBreakeH } from "react-icons/cg";
import ErrorPanel from "../../components/errorPanel";
import settingsActions from "../../redux/settings/actions";
import {
applyPreviewCopyStateFit,
createCopyStateFitUiState,
metadataToCopyStateFit,
previewCopyStateFitDrag,
resetCopyStateFitUiState,
selectActiveCopyStateFit,
} from "../purityPloidySlider/copyStateFit";

const { updateDomains } = settingsActions;

Expand All @@ -35,6 +44,10 @@ class BinPlotPanel extends Component {
state = {
segment: null,
open: false,
// CopyStateFitUiState template: { metadataFit, previewFit, appliedOverrideFit }.
// Gamma is intentionally still unused for copy-state separator math in issue-0001;
// BinPlotPanel only validates its presence to preserve existing behavior.
copyStateFitUiState: null,
};

onDownloadButtonClicked = () => {
Expand Down Expand Up @@ -72,6 +85,72 @@ class BinPlotPanel extends Component {
this.setState({ open: false });
};

handleCopyStateFitPreview = (drag) => {
if (drag == null) {
return;
}

this.setState((state, props) => {
const metadata = props.metadata || {};
const uiState =
state.copyStateFitUiState ||
createCopyStateFitUiState(
metadataToCopyStateFit({
beta: metadata.beta,
purity: metadata.purity,
meanSegmentValue: metadata.meanSegmentValue,
})
);

return {
copyStateFitUiState: previewCopyStateFitDrag(uiState, drag),
};
});
};

transformCopyStateFitUiState = (transformUiState) => {
if (this.state.copyStateFitUiState == null) {
return;
}

this.setState((state) => ({
copyStateFitUiState: transformUiState(state.copyStateFitUiState),
}));
};

handleApplyCopyStateFit = () => {
this.transformCopyStateFitUiState(applyPreviewCopyStateFit);
};

handleResetCopyStateFit = () => {
this.transformCopyStateFitUiState(resetCopyStateFitUiState);
};

renderCopyStateFitControls = () => {
const { copyStateFitUiState } = this.state;
const { metadata = {} } = this.props;
const activeCopyStateFit =
copyStateFitUiState == null
? metadataToCopyStateFit({
beta: metadata.beta,
purity: metadata.purity,
meanSegmentValue: metadata.meanSegmentValue,
})
: selectActiveCopyStateFit(copyStateFitUiState);
const hasPreview = copyStateFitUiState?.previewFit != null;
const hasFitSession = copyStateFitUiState != null;

return (
<CopyStateFitControls
activeCopyStateFit={activeCopyStateFit}
hasFitSession={hasFitSession}
hasPreview={hasPreview}
onApply={this.handleApplyCopyStateFit}
onReset={this.handleResetCopyStateFit}
/>
);
};

render() {
const {
t,
Expand All @@ -91,8 +170,10 @@ class BinPlotPanel extends Component {
} = this.props;

const { beta, gamma, purity } = metadata;
const hasPpfitData = ppfit.data.intervals.length > 0;
const shouldDisplayPanel = visible && (loading || hasPpfitData || inViewport !== false);

if (!metadata.pair || ppfit.data.intervals.length < 1) {
if (!metadata.pair || (!loading && !hasPpfitData)) {
return null;
}
const { segment, open } = this.state;
Expand Down Expand Up @@ -122,7 +203,7 @@ class BinPlotPanel extends Component {
/>
) : (
<Card
style={transitionStyle(inViewport)}
style={transitionStyle(shouldDisplayPanel)}
loading={loading}
size="small"
title={
Expand All @@ -141,7 +222,7 @@ class BinPlotPanel extends Component {
<Button
type="default"
shape="circle"
disabled={!visible}
disabled={!shouldDisplayPanel}
icon={<AiOutlineDownload style={{ marginTop: 4 }} />}
size="small"
onClick={() => this.onDownloadButtonClicked()}
Expand All @@ -150,7 +231,7 @@ class BinPlotPanel extends Component {
</Space>
}
>
{visible && (
{shouldDisplayPanel && hasPpfitData && (
<div
className="ant-wrapper"
ref={(elem) => (this.container = elem)}
Expand Down Expand Up @@ -225,9 +306,10 @@ class BinPlotPanel extends Component {
<ContainerDimensions>
{({ width, height }) => {
return (
(inViewport) && (
shouldDisplayPanel && (
<Row style={{ width }} gutter={[margins.gap, 0]}>
<Col flex={1}>
{this.renderCopyStateFitControls()}
<BinPlot
{...{
width,
Expand All @@ -242,6 +324,13 @@ class BinPlotPanel extends Component {
selectSegment: (e) =>
this.handleSelectSegment(e),
separatorsConfig: { beta, purity },
activeCopyStateFit:
this.state.copyStateFitUiState?.previewFit ||
this.state.copyStateFitUiState
?.appliedOverrideFit ||
this.state.copyStateFitUiState?.metadataFit,
onCopyStateFitPreview: (drag) =>
this.handleCopyStateFitPreview(drag),
}}
/>
</Col>
Expand Down
Loading