Skip to content
This repository has been archived by the owner on Jan 22, 2024. It is now read-only.

Improve alignment view zoom scaling #835

Merged
merged 20 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fa4cf38
working on adding a zoom feature to the linear view
tnrich Jun 13, 2022
74424e0
a couple tiny enhancements to linear view scroll+zoom
tnrich Jun 14, 2022
7503a7b
Added min and max threshold to linear view zoom
djriffle Jun 14, 2022
6d09a9c
a couple little cleanup fixes; adding papaparse as a dev-dep
tnrich Jun 15, 2022
77c2a30
adding backbone for a linear view test
tnrich Jun 15, 2022
813890a
added linear view test cases
djriffle Jun 15, 2022
5c4594b
improving sequence length toggle, increasing allowable linear view zo…
tnrich Jun 15, 2022
0b38d97
test fixing
tnrich Jun 15, 2022
19a9d5e
zooming on linear view is now disabled by default
djriffle Jun 15, 2022
3abfac5
Merge branch 'tnrAddZoomToLinearView' of https://github.com/TeselaGen…
djriffle Jun 15, 2022
58fcdc5
zooming now follows the caret
djriffle Jun 16, 2022
0721dc5
added failing test case for viewing labels of large features
djriffle Jun 16, 2022
4e16ab4
adding a coercion function to manage the alignment view zoom
tnrich Jun 16, 2022
03f3cee
added updateLabels util
djriffle Jun 16, 2022
d69d013
Merge branch 'djrImproveAlignmentViewZoomScaling' of https://github.c…
djriffle Jun 16, 2022
c7d57dd
feature labels now follow features
djriffle Jun 17, 2022
b4de3f3
Fixed feature labels not updating on linear view zoom
djriffle Jun 17, 2022
b325a62
Amino acids don't show when zoomed too far out
djriffle Jun 17, 2022
f1b97bc
fixed test case and updateLabel on load
djriffle Jun 17, 2022
54cf3e9
fixing conflicts
tnrich Jun 17, 2022
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
8 changes: 3 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v17.6.4](https://github.com/TeselaGen/openVectorEditor/compare/v17.3.12...v17.6.4)

- Adjust font size for name in circular view [`#834`](https://github.com/TeselaGen/openVectorEditor/pull/834)
- Tnr add zoom to linear view [`#828`](https://github.com/TeselaGen/openVectorEditor/pull/828)
- Added rounded corners to amino acids [`#819`](https://github.com/TeselaGen/openVectorEditor/pull/819)
- small selection color style fix https://github.com/TeselaGen/lims/issues/9038#event-6627607729 [`#9038`](https://github.com/TeselaGen/lims/issues/9038)
- bumping new minor version w/ linear view zoom v1 [`8b406cd`](https://github.com/TeselaGen/openVectorEditor/commit/8b406cd95ea665f10b7b44e84403b6c0ef6f4d17)
- making sequences track their default enzyme filter by default; improving setting of default global enzyme filter [`e353f3c`](https://github.com/TeselaGen/openVectorEditor/commit/e353f3c46b0fc1d67ef9f81b537f6ac675901d9f)
- reverting persist enzyme filter UI changes [`2d81d4a`](https://github.com/TeselaGen/openVectorEditor/commit/2d81d4a556ba0dba8daa86f94420e635b347c9b8)
- adding a coercion function to manage the alignment view zoom [`4e16ab4`](https://github.com/TeselaGen/openVectorEditor/commit/4e16ab440ecfe4f6344de3fe191a597b0da18a48)
- improving sequence length toggle, increasing allowable linear view zoom size [`5c4594b`](https://github.com/TeselaGen/openVectorEditor/commit/5c4594b750c48288158c296231f30b168c36cc1b)
- added linear view test cases [`813890a`](https://github.com/TeselaGen/openVectorEditor/commit/813890a0530ed8bd49a85b4cfee7834dc4b17155)

#### [v17.3.12](https://github.com/TeselaGen/openVectorEditor/compare/v17.2.7...v17.3.12)

Expand Down
11 changes: 11 additions & 0 deletions cypress/integration/alignment.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
describe("alignment", function () {
it("it should re-draw the labels of features/parts/primers when a feature is so long that the middle of the label does not fit into view", function () {
cy.visit("#/Alignment");
cy.get(".tg-alignment-visibility-toggle").click();
cy.contains("cds feature").should("not.exist");
cy.get(".bp3-popover .bp3-control:contains(Features)").click();
cy.contains("cds feature");
cy.get(".tg-alignment-visibility-toggle").click();
cy.contains("text", "long feat whose label should still appear").should(
"be.visible"
);
});
it("it should show an unmapped warning", function () {
cy.visit("#/Alignment");
cy.get(".bp3-icon-warning-sign");
Expand Down
4 changes: 2 additions & 2 deletions demo/src/exampleData/msaAlignment.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"id": "0bd61b29-d822-474f-86e1-877c476fb5dc",
"features": [{
"start": 10,
"end": 50,
"name": "cds feature",
"end": 500,
"name": "long feat whose label should still appear",
"type": "CDS",
"id": "5ac3f0a199aae6653c0e7d41",
"forward": true,
Expand Down
1 change: 1 addition & 0 deletions src/AlignmentView/AlignmentVisibilityTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default pureNoFunc(function AlignmentVisibilityTool(props) {
target={
<Tooltip content="Visibility Options">
<Button
className="tg-alignment-visibility-toggle"
small
rightIcon="caret-down"
intent={Intent.PRIMARY}
Expand Down
159 changes: 82 additions & 77 deletions src/AlignmentView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import React from "react";
import { connect } from "react-redux";
import {
Button,
Icon,
Slider,
Intent,
Popover,
InputGroup,
Expand Down Expand Up @@ -52,6 +50,9 @@ import { noop } from "lodash";
import { massageTickSpacing } from "../utils/massageTickSpacing";
import { getClientX, getClientY } from "../utils/editorUtils";

import UncontrolledSliderWithPlusMinusBtns from "../helperComponents/UncontrolledSliderWithPlusMinusBtns";
import { updateLabelsForInViewFeatures } from "../utils/updateLabelsForInViewFeatures";

const nameDivWidth = 140;
let charWidthInLinearViewDefault = 12;
try {
Expand All @@ -67,6 +68,7 @@ try {
}

class AlignmentView extends React.Component {
bindOutsideChangeHelper = {};
constructor(props) {
super(props);
window.scrollAlignmentToPercent = this.scrollAlignmentToPercent;
Expand Down Expand Up @@ -224,6 +226,9 @@ class AlignmentView extends React.Component {
this.editorClicked = editorClicked.bind(this);
this.editorDragStarted = editorDragStarted.bind(this);
this.editorDragStopped = editorDragStopped.bind(this);
setTimeout(() => {
updateLabelsForInViewFeatures({ rectElement: ".alignmentHolder" });
}, 0);
setTimeout(() => {
this.setVerticalScrollRange();
}, 500);
Expand Down Expand Up @@ -362,6 +367,7 @@ class AlignmentView extends React.Component {
if (this.alignmentHolderTop) {
this.alignmentHolderTop.scrollLeft = this.alignmentHolder.scrollLeft;
}
updateLabelsForInViewFeatures({ rectElement: ".alignmentHolder" });
};
handleTopScroll = () => {
this.alignmentHolder.scrollLeft = this.alignmentHolderTop.scrollLeft;
Expand All @@ -377,6 +383,7 @@ class AlignmentView extends React.Component {
this.scrollAlignmentToPercent(newPercent);
this.blockScroll = false;
});
updateLabelsForInViewFeatures({ rectElement: ".alignmentHolder" });
};

setCharWidthInLinearView = ({ charWidthInLinearView }) => {
Expand All @@ -389,6 +396,13 @@ class AlignmentView extends React.Component {
window.localStorage.getItem("charWidthInLinearViewDefault")
);
};

scrollToCaret = () => {
const el = window.document.querySelector(".veCaret"); //adding .veRowViewCaret breaks this for some reason
if (!el) return;
el.scrollIntoView({ inline: "center" });
};

scrollAlignmentToPercent = (scrollPercentage) => {
const scrollPercentageToUse = Math.min(Math.max(scrollPercentage, 0), 1);

Expand Down Expand Up @@ -1213,25 +1227,55 @@ class AlignmentView extends React.Component {
)}
{!isInPairwiseOverviewView && (
<UncontrolledSliderWithPlusMinusBtns
onRelease={(val) => {
this.setCharWidthInLinearView({
charWidthInLinearView: val
noWraparound
bindOutsideChangeHelper={this.bindOutsideChangeHelper}
onClick={() => {
setTimeout(this.scrollToCaret, 0);
}}
minCharWidth={this.getMinCharWidth()}
// onRelease={(zoomLvl) => {

// }}
// onRelease={(val) => {
// this.setCharWidthInLinearView({
// charWidthInLinearView: val
// });
// // this.blockScroll = true; //we block the scroll to prevent jumpiness and then manually update to the desired scroll percentage
// // const percentScrollage = this.easyStore.percentScrolled;
// // setTimeout(() => {
// // this.blockScroll = false;
// // this.scrollAlignmentToPercent(percentScrollage);
// // });
// }}
onChange={async (zoomLvl) => {
// if (!this.startSliderDrag) {

// }
// this.startSliderDrag = true

// zoomLvl is in the range of 0 to 10
const minCharWidth = this.getMinCharWidth();
const scaleFactor = Math.pow(12 / minCharWidth, 1 / 10);
const newCharWidth =
minCharWidth * Math.pow(scaleFactor, zoomLvl);
await this.setCharWidthInLinearView({
charWidthInLinearView: newCharWidth
});
this.blockScroll = true; //we block the scroll to prevent jumpiness and then manually update to the desired scroll percentage
const percentScrollage = this.easyStore.percentScrolled;
setTimeout(() => {
this.blockScroll = false;
this.scrollAlignmentToPercent(percentScrollage);
await this.scrollToCaret();
await updateLabelsForInViewFeatures({
rectElement: ".alignmentHolder"
});
}}
coerceInitialValue={coerceInitialValue}
title="Adjust Zoom Level"
style={{ paddingTop: "4px", width: 100 }}
className="ove-slider"
labelRenderer={false}
stepSize={0.01}
initialValue={charWidthInLinearView}
max={14}
min={this.getMinCharWidth()}
stepSize={0.05} //was 0.01
max={10}
min={0}
clickStepSize={0.5}
/>
)}
{!noVisibilityOptions && !isInPairwiseOverviewView && (
Expand Down Expand Up @@ -1467,6 +1511,11 @@ export default compose(
name,
{ useChecked, checked } = {}
) => {
setTimeout(() => {
updateLabelsForInViewFeatures({
rectElement: ".alignmentHolder"
});
}, 0);
updateAlignmentViewVisibility({
...alignment,
alignmentAnnotationVisibility: {
Expand Down Expand Up @@ -1517,70 +1566,6 @@ export default compose(
)
)(AlignmentView);

class UncontrolledSliderWithPlusMinusBtns extends React.Component {
state = { value: 0 };

static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.oldInitialValue !== nextProps.initialValue) {
return {
value: nextProps.initialValue, //set the state value if a new initial value comes in!
oldInitialValue: nextProps.initialValue
};
} else {
return null;
}
}

render() {
const { value } = this.state;
const { title, initialValue, style, ...rest } = this.props;

return (
<div
title={title}
style={{ ...style, display: "flex", marginLeft: 15, marginRight: 20 }}
>
<Icon
onClick={() => {
const newVal = Math.max(
this.state.value - (this.props.max - this.props.min) / 10,
this.props.min
);
this.setState({
value: newVal
});
this.props.onRelease(newVal);
}}
style={{ cursor: "pointer", marginRight: 5 }}
intent={Intent.PRIMARY}
icon="minus"
/>
<Slider
{...{ ...rest, value }}
onChange={(value) => {
this.setState({ value });
}}
/>
<Icon
onClick={() => {
const newVal = Math.min(
this.state.value + (this.props.max - this.props.min) / 10,
this.props.max
);
this.setState({
value: newVal
});
this.props.onRelease(newVal);
}}
style={{ cursor: "pointer", marginLeft: 5 }}
intent={Intent.PRIMARY}
icon="plus"
/>
</div>
);
}
}

//this view is shown if we detect pairwise alignments
class PairwiseAlignmentView extends React.Component {
state = {
Expand Down Expand Up @@ -1694,3 +1679,23 @@ const PerformantSelectionLayer = view(({ easyStore, ...rest }) => {
const PerformantCaret = view(({ easyStore, ...rest }) => {
return <Caret caretPosition={easyStore.caretPosition} {...rest} />;
});

function coerceInitialValue({ initialValue, minCharWidth }) {
//char width 12 = 10
//zoomLvl = 0 -> charWidth = minCharWidth
//zoomLvl = 10 -> charWidth = 12

// const scaleFactor = Math.pow(12 / initialCharWidth, 1 / 10);
// newCharWidth = initialCharWidth * Math.pow(scaleFactor, zoomLvl)
// 12 = initialCharWidth * Math.pow(scaleFactor, 10)
// 12/initialCharWidth = Math.pow(scaleFactor, 10)
// Math.pow(12/minCharWidth, 1/10) = scaleFactor

// newCharWidth/minCharWidth = * Math.pow(scaleFactor, zoomLvl)

const scaleFactor = Math.pow(12 / minCharWidth, 1 / 10);

const zoomLvl = Math.log(initialValue / minCharWidth) / Math.log(scaleFactor);

return zoomLvl;
}
Loading