Skip to content

Commit

Permalink
closes #895 selected base pair lines is exported
Browse files Browse the repository at this point in the history
  • Loading branch information
rayzhuca committed Nov 11, 2023
1 parent 1471f3f commit 17f944b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 53 deletions.
28 changes: 15 additions & 13 deletions lib/src/middleware/export_svg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:svg';

import 'package:built_collection/built_collection.dart';
import 'package:over_react/over_react.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:redux/redux.dart';
import 'package:scadnano/src/middleware/system_clipboard.dart';
import 'package:scadnano/src/state/domain.dart';
Expand Down Expand Up @@ -44,7 +45,7 @@ export_svg_middleware(Store<AppState> store, dynamic action, NextDispatcher next
action.type == actions.ExportSvgType.selected) {
var elt = document.getElementById("main-view-svg");
if (action.type == actions.ExportSvgType.selected) {
List<Element> selected_elts = get_selected_strands(store);
List<Element> selected_elts = get_selected_base_pairs(store)..addAll(get_selected_strands(store));
if (selected_elts.length == 0) {
window.alert("No strands are selected, so there is nothing to export.\n"
"Please select some strands before choosing this option.");
Expand Down Expand Up @@ -85,22 +86,23 @@ List<Element> get_selected_strands(Store<AppState> store) {
return selected_elts;
}

List<Element> get_selected_base_pairs(Store<AppState> store) {
var selected_strands = store.state.ui_state.selectables_store.selected_strands;
var base_pairs = store.state.ui_state.show_base_pair_lines_with_mismatches
? store.state.design.selected_base_pairs_with_mismatches(selected_strands)
: store.state.design.selected_base_pairs(selected_strands);
List<Element> selected_elts = [];
for (int helix in base_pairs.keys) {
selected_elts
.addAll(base_pairs[helix].map((offset) => document.getElementById('base_pair-${helix}-${offset}')));
}
return selected_elts;
}

SvgSvgElement get_cloned_svg_element_with_style(List<Element> selected_elts) {
var cloned_svg_element_with_style = SvgSvgElement()
..children = selected_elts.map(clone_and_apply_style).toList();

List<Element> base_pairs_elements = [
// TEMPORARY, JUST FOR TESTING
document.getElementsByClassName('base-pair-lines-main-view')[0] as Element
];
cloned_svg_element_with_style.children.addAll(base_pairs_elements.map(clone_and_apply_style));

print(app.state.design.base_pairs);
print(app.state.design.strand_to_index);

print(app.state.design.get_selected_base_pairs(
app.state.design.base_pairs, app.state.ui_state.selectables_store.selected_strands));

// we can't get bbox without it being added to the DOM first
document.body.append(cloned_svg_element_with_style);
var bbox = cloned_svg_element_with_style.getBBox();
Expand Down
55 changes: 19 additions & 36 deletions lib/src/state/design.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2101,56 +2101,39 @@ abstract class Design with UnusedFields implements Built<Design, DesignBuilder>,

/// maps each helix_idx to a list of offsets where there is a complementary base pair on each strand
@memoized
BuiltMap<int, BuiltList<int>> get base_pairs => this._base_pairs(false);
BuiltMap<int, BuiltList<int>> get base_pairs => this._base_pairs(false, strands.toBuiltSet());

/// maps each helix_idx to a list of offsets where there is a base on each strand,
/// NOT necessarily complementary
@memoized
BuiltMap<int, BuiltList<int>> get base_pairs_with_mismatches => this._base_pairs(true);
BuiltMap<int, BuiltList<int>> get base_pairs_with_mismatches =>
this._base_pairs(true, strands.toBuiltSet());

/// given `base_pairs`, returns a filtered map that is connected by at least 2 strands in `selected_strands`
BuiltMap<int, BuiltList<int>> get_selected_base_pairs(
BuiltMap<int, BuiltList<int>> base_pairs, BuiltSet<Strand> selected_strands) {
Map<int, List<int>> connect_cnt = {};
// returns a subset of base_pairs that is connected to selected_strands
BuiltMap<int, BuiltList<int>> selected_base_pairs(BuiltSet<Strand> selected_strands) =>
this._base_pairs(false, selected_strands);

helices.forEach((i, v) {
connect_cnt[i] = List.filled(v.max_offset + 1, 0);
});
selected_strands.forEach((strand) {
strand.substrands.forEach((substrand) {
if (substrand is Domain) {
connect_cnt[substrand.helix][substrand.start] += 1;
connect_cnt[substrand.helix][substrand.end + 1] -= 1;
}
});
});
connect_cnt.updateAll((i, list) {
for (int i = 1; i < list.length; ++i) {
list[i] += list[i - 1];
}
return list;
});
Map<int, BuiltList<int>> connected_base_pairs = {};
base_pairs.forEach((i, list) {
List<int> base_pairs = [];
list.forEach((j) {
if (connect_cnt[i][j] == 2) {
base_pairs.add(j);
}
connected_base_pairs[i] = base_pairs.build();
});
});
return connected_base_pairs.build();
}
// returns a subset of base_pairs_with_mismatches that is connected to selected_strands
BuiltMap<int, BuiltList<int>> selected_base_pairs_with_mismatches(BuiltSet<Strand> selected_strands) =>
this._base_pairs(true, selected_strands);

BuiltMap<int, BuiltList<int>> _base_pairs(bool allow_mismatches) {
BuiltMap<int, BuiltList<int>> _base_pairs(bool allow_mismatches, BuiltSet<Strand> selected_strands) {
var base_pairs = Map<int, BuiltList<int>>();
BuiltSet<Domain> selected_domains = selected_strands
.map((s) => s.substrands)
.expand((x) => x)
.where((x) => x is Domain)
.map((x) => x as Domain)
.toBuiltSet();
for (int idx in this.helices.keys) {
List<int> offsets = [];
List<Tuple2<Domain, Domain>> overlapping_domains = find_overlapping_domains_on_helix(idx);
for (var domain_pair in overlapping_domains) {
Domain dom1 = domain_pair.item1;
Domain dom2 = domain_pair.item2;
if (!selected_domains.contains(dom1) || !selected_domains.contains(dom2)) {
continue;
}
var start_and_end = dom1.compute_overlap(dom2);
int start = start_and_end.item1;
int end = start_and_end.item2;
Expand Down
12 changes: 8 additions & 4 deletions lib/src/view/design_main_base_pair_lines.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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';

Expand All @@ -28,14 +29,16 @@ mixin DesignMainBasePairLinesProps on UiProps {
class DesignMainBasePairLinesComponent extends UiComponent2<DesignMainBasePairLinesProps> with PureComponent {
@override
render() {
List<ReactElement> base_pair_lines_components = this._create_base_pair_lines_components();
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() {
List<ReactElement> create_base_pair_lines_components(BuiltSet<Strand> strands) {
List<ReactElement> base_pair_lines_components = [];
var base_pairs =
props.with_mismatches ? props.design.base_pairs_with_mismatches : props.design.base_pairs;
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)) {
Expand All @@ -52,6 +55,7 @@ class DesignMainBasePairLinesComponent extends UiComponent2<DesignMainBasePairLi
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_line = (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
Expand Down

0 comments on commit 17f944b

Please sign in to comment.