Skip to content

Commit

Permalink
Closes #897
Browse files Browse the repository at this point in the history
  • Loading branch information
rayzhuca committed Mar 4, 2024
1 parent 77e21ef commit 7c5645a
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 21 deletions.
1 change: 1 addition & 0 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ const css_selector_scaffold = 'scaffold';
const css_selector_staple = 'staple';
const css_selector_domain = 'domain-line';
const css_selector_base_pair_line = 'base-pair-line';
const css_selector_base_pair_rect = 'base-pair-rect';
const css_selector_extension = 'extension-line';
const css_selector_crossover = 'crossover-curve';
const css_selector_crossover_same_helix = 'crossover-curve-same-helix';
Expand Down
3 changes: 2 additions & 1 deletion lib/src/middleware/export_svg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:built_collection/built_collection.dart';
import 'package:over_react/over_react.dart';
import 'package:redux/redux.dart';
import 'package:scadnano/src/middleware/system_clipboard.dart';
import 'package:scadnano/src/state/base_pair_display_type.dart';
import 'package:scadnano/src/state/strand.dart';
import 'package:scadnano/src/view/design_main_dna_sequence.dart';

Expand Down Expand Up @@ -80,7 +81,7 @@ export_svg_middleware(Store<AppState> store, dynamic action, NextDispatcher next
List<Element> get_selected_svg_elements(AppState state) {
BuiltSet<Strand> selected_strands = state.ui_state.selectables_store.selected_strands;
List<Element> selected_elts = [];
if (app.state.ui_state.show_base_pair_lines) {
if (app.state.ui_state.base_pair_display_type != BasePairDisplayType.none) {
var base_pairs = state.ui_state.show_base_pair_lines_with_mismatches
? state.design.selected_base_pairs_with_mismatches(selected_strands)
: state.design.selected_base_pairs(selected_strands);
Expand Down
12 changes: 12 additions & 0 deletions lib/src/state/base_pair_display_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ class BasePairDisplayType extends EnumClass {

String to_json() => name;

int toIndex() {
switch (this) {
case none:
return 0;
case lines:
return 1;
case rectangle:
return 2;
}
return 0;
}

String display_name() {
// edit this to display a different string than the identifier name above
switch (this) {
Expand Down
16 changes: 15 additions & 1 deletion lib/src/view/design_main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import 'package:built_collection/built_collection.dart';
import 'package:over_react/over_react.dart';
import 'package:over_react/over_react_redux.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:scadnano/src/state/base_pair_display_type.dart';
import 'package:scadnano/src/view/design_main_unpaired_insertion_deletions.dart';
import 'package:scadnano/src/view/design_main_slice_bar.dart';
import 'package:scadnano/src/view/potential_extensions_view.dart';

import '../state/selection_rope.dart';
import 'design_main_base_pair_rectangle.dart';
import 'design_main_domains_moving.dart';
import 'selection_rope_view.dart';
import '../actions/actions.dart' as actions;
Expand Down Expand Up @@ -65,6 +67,7 @@ UiFactory<DesignMainProps> ConnectedDesignMain = connect<AppState, DesignMainPro
..show_domain_name_mismatches = state.ui_state.show_domain_name_mismatches
..show_unpaired_insertion_deletions = state.ui_state.show_unpaired_insertion_deletions
..show_dna = state.ui_state.show_dna
..base_pair_display_type = state.ui_state.base_pair_display_type
..show_base_pair_lines = state.ui_state.show_base_pair_lines
..show_base_pair_lines_with_mismatches = state.ui_state.show_base_pair_lines_with_mismatches
..show_domain_names = state.ui_state.show_domain_names
Expand Down Expand Up @@ -110,6 +113,7 @@ mixin DesignMainPropsMixin on UiProps {
bool show_domain_name_mismatches;
bool show_unpaired_insertion_deletions;
bool show_dna;
BasePairDisplayType base_pair_display_type;
bool show_base_pair_lines;
bool show_base_pair_lines_with_mismatches;
bool show_domain_names;
Expand Down Expand Up @@ -210,7 +214,7 @@ class DesignMainComponent extends UiComponent2<DesignMainProps> {
.map((helix_idx, svg_position) => MapEntry(helix_idx, svg_position.y))
..key = 'unpaired-insertion-deletions')(),

if (props.show_base_pair_lines)
if (props.base_pair_display_type == BasePairDisplayType.lines)
(DesignMainBasePairLines()
..with_mismatches = props.show_base_pair_lines_with_mismatches
..design = props.design
Expand All @@ -220,6 +224,16 @@ class DesignMainComponent extends UiComponent2<DesignMainProps> {
.map((helix_idx, svg_position) => MapEntry(helix_idx, svg_position.y))
..key = 'base-pair-lines')(),

if (props.base_pair_display_type == BasePairDisplayType.rectangle)
(DesignMainBasePairRectangle()
..with_mismatches = props.show_base_pair_lines_with_mismatches
..design = props.design
..only_display_selected_helices = props.only_display_selected_helices
..side_selected_helix_idxs = props.side_selected_helix_idxs
..helix_idx_to_svg_position_y_map = props.helix_idx_to_svg_position_map
.map((helix_idx, svg_position) => MapEntry(helix_idx, svg_position.y))
..key = 'base-pair-rectangle')(),

(ConnectedDesignMainStrands()..key = 'strands')(),

// after strands so can click when crossover overlaps potential crossover
Expand Down
99 changes: 99 additions & 0 deletions lib/src/view/design_main_base_pair_rectangle.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:built_collection/built_collection.dart';
import 'package:scadnano/scadnano.dart';
import 'package:scadnano/src/state/group.dart';
import 'package:scadnano/src/state/helix.dart';

import '../state/design.dart';
import '../state/strand.dart';
import '../state/domain.dart';
import 'pure_component.dart';
import 'design_main_warning_star.dart';
import '../util.dart' as util;
import '../constants.dart' as constants;

part 'design_main_base_pair_rectangle.over_react.g.dart';

UiFactory<DesignMainBasePairRectangleProps> DesignMainBasePairRectangle = _$DesignMainBasePairRectangle;

mixin DesignMainBasePairRectangleProps on UiProps {
bool with_mismatches;
Design design;
bool only_display_selected_helices;
BuiltSet<int> side_selected_helix_idxs;
BuiltMap<int, num> helix_idx_to_svg_position_y_map;
}

class DesignMainBasePairRectangleComponent extends UiComponent2<DesignMainBasePairRectangleProps>
with PureComponent {
@override
render() {
List<ReactElement> base_pair_lines_components =
this.create_base_pair_lines_components(app.state.design.strands.toBuiltSet());
return (Dom.g()..className = 'base-pair-lines-main-view')(base_pair_lines_components);
}

List<ReactElement> create_base_pair_lines_components(BuiltSet<Strand> strands) {
List<ReactElement> base_pair_lines_components = [];
BuiltMap<int, BuiltList<int>> base_pairs = props.with_mismatches
? props.design.selected_base_pairs_with_mismatches(strands)
: props.design.selected_base_pairs(strands);

for (int helix_idx in base_pairs.keys) {
if (!props.only_display_selected_helices || props.side_selected_helix_idxs.contains(helix_idx)) {
var helix = props.design.helices[helix_idx];
HelixGroup group = props.design.groups[helix.group];
String transform_str = group.transform_str(props.design.geometry);

// code below draws one line for each base pair, should render somewhat slowly
// however, this makes it easier to associate base pair lines to individual strands,
// convenient when exporting SVG
List<ReactElement> helix_components = [];
int last_offset = -2;
var last_svg_forward_pos = null;
for (int offset in base_pairs[helix_idx]) {
var svg_position_y = props.helix_idx_to_svg_position_y_map[helix_idx];
var base_svg_forward_pos = helix.svg_base_pos(offset, true, svg_position_y);
var base_svg_reverse_pos = helix.svg_base_pos(offset, false, svg_position_y);

var base_pair_ele = null;

if (offset - last_offset == 1) {
base_pair_ele = (Dom.rect()
..id = 'base_pair-${helix_idx}-${offset}'
..x = last_svg_forward_pos.x - 0.5
..y = base_svg_forward_pos.y
..width = base_svg_reverse_pos.x - last_svg_forward_pos.x + 0.8
..height = base_svg_reverse_pos.y - base_svg_forward_pos.y
..className = constants.css_selector_base_pair_rect
..fill = 'grey'
..key = 'base-pair-rect-H${helix_idx}-${offset}')();
} else {
base_pair_ele = (Dom.line()
..id = 'base_pair-${helix_idx}-${offset}'
..x1 = base_svg_forward_pos.x
..y1 = base_svg_forward_pos.y
..x2 = base_svg_reverse_pos.x
..y2 = base_svg_reverse_pos.y
..className = constants.css_selector_base_pair_line
..stroke = 'grey'
..key = 'base-pair-line-H${helix_idx}-${offset}')();
}

helix_components.add(base_pair_ele);
last_offset = offset;
last_svg_forward_pos = base_svg_forward_pos;
}
var helix_dom_group = (Dom.g()
..transform = transform_str
..className = 'base-pair-lines-components-in-helix'
..key = 'base-pair-lines-components-in-helix-H${helix_idx}')(helix_components);
base_pair_lines_components.add(helix_dom_group);
}
}

return base_pair_lines_components;
}
}
32 changes: 13 additions & 19 deletions lib/src/view/menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ UiFactory<MenuProps> ConnectedMenu = connect<AppState, MenuProps>(
..no_grid_is_none =
state.design == null ? false : state.design.groups.values.every((group) => group.grid != Grid.none)
..show_dna = state.ui_state.show_dna
..base_pair_display_type = state.ui_state.base_pair_display_type
..show_strand_names = state.ui_state.show_strand_names
..show_strand_labels = state.ui_state.show_strand_labels
..show_domain_names = state.ui_state.show_domain_names
Expand Down Expand Up @@ -135,7 +136,7 @@ mixin MenuPropsMixin on UiProps {
bool autofit;
bool only_display_selected_helices;
ExampleDesigns example_designs;
// SetBasePairDisplay base_pair_display_types;
BasePairDisplayType base_pair_display_type;
bool design_has_insertions_or_deletions;
bool undo_stack_empty;
bool redo_stack_empty;
Expand Down Expand Up @@ -1007,24 +1008,6 @@ or real coordinates in nanometers, depending on whether a grid is selected).'''
..on_click = ((_) => app.disable_keyboard_shortcuts_while(base_pair_display_dialog))
..display = 'Base pair display'
..key = 'base-pair-display')(),
(MenuBoolean()
..value = props.show_base_pair_lines
..display = 'Base pair lines'
..tooltip = 'Draw vertical lines between pairs of bases at the same offset on the same helix.'
..onChange = ((_) =>
props.dispatch(actions.ShowBasePairLinesSet(show_base_pair_lines: !props.show_base_pair_lines)))
..key = 'base_pair_lines')(),
(MenuBoolean()
..value = props.show_base_pair_lines_with_mismatches
..hide = !props.show_base_pair_lines
..display = '... even if bases mismatch'
..tooltip = '''\
Lines are drawn between all pairs of bases at the same offset on the same helix,
regardless of whether the bases are complementary. If unchecked then lines are
only shown between pairs of complementary bases.'''
..onChange = ((_) => props.dispatch(actions.ShowBasePairLinesWithMismatchesSet(
show_base_pair_lines_with_mismatches: !props.show_base_pair_lines_with_mismatches)))
..key = 'base_pair_lines_mismatches')(),
]);
}

Expand Down Expand Up @@ -1448,13 +1431,24 @@ However, it may be less stable than the main site.'''
DialogRadio(
label: 'types',
options: BasePairDisplayType.types.map((v) => v.display_name()),
selected_idx: props.base_pair_display_type.toIndex(),
),
DialogCheckbox(
label: 'display even if bases mismatch',
value: props.show_base_pair_lines_with_mismatches,
tooltip: '''\
Lines are drawn between all pairs of bases at the same offset on the same helix,
regardless of whether the bases are complementary. If unchecked then lines are
only shown between pairs of complementary bases.''',
)
]);
List<DialogItem> results = await util.dialog(dialog);
if (results == null) return;

int selected_idx = (results[0] as DialogRadio).selected_idx;
props.dispatch(actions.BasePairTypeSet(selected_idx: selected_idx));
props.dispatch((actions.ShowBasePairLinesWithMismatchesSet(
show_base_pair_lines_with_mismatches: (results[1] as DialogCheckbox).value)));
}
}

Expand Down

0 comments on commit 7c5645a

Please sign in to comment.