From ef386b17261f4023410a4905297ed61327b5c2b4 Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 23 Jul 2024 19:47:04 -0700 Subject: [PATCH 01/31] bumped version --- lib/src/constants.dart | 127 ++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 64 deletions(-) diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 867d848b4..244c7b20c 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -8,13 +8,12 @@ import 'state/grid.dart'; // WARNING: Do not modify line below, except for the version string // (and also add new version string to scadnano_versions_to_link). -const String CURRENT_VERSION = "0.19.3"; +const String CURRENT_VERSION = "0.19.4"; const String INITIAL_VERSION = "0.1.0"; // scadnano versions that we deploy so that older versions can be used. final scadnano_older_versions_to_link = [ - "0.19.2", - "0.19.1", + "0.19.3", "0.18.10", "0.17.14", // "0.17.13", @@ -77,7 +76,7 @@ const RIGHT_CLICK_BUTTON = 2; const KEY_CODE_SHOW_POTENTIAL_HELIX = KeyCode.H; const KEY_CODE_MOUSEOVER_HELIX_VIEW_INFO = KeyCode.W; final KEY_CODE_COMMAND_MAC = - browser.isFirefox ? 224 : 91; // Meta/Command: https://tosbourn.com/cmd-osx-key-code/ +browser.isFirefox ? 224 : 91; // Meta/Command: https://tosbourn.com/cmd-osx-key-code/ final KEY_CODE_TOGGLE_SELECT_MAC = KEY_CODE_COMMAND_MAC; const KEY_CODE_SELECT = KeyCode.SHIFT; const KEY_CODE_TOGGLE_SELECT = KeyCode.CTRL; @@ -216,18 +215,18 @@ const design_modifications_3p_key = 'modifications_3p_in_design'; const design_modifications_int_key = 'modifications_int_in_design'; const groups_key = 'groups'; final design_keys = [ - version_key, - grid_key, - helices_key, - helices_view_order_key, - potential_helices_key, - strands_key, - design_modifications_key, - design_modifications_5p_key, - design_modifications_3p_key, - design_modifications_int_key, - groups_key, - ] + + version_key, + grid_key, + helices_key, + helices_view_order_key, + potential_helices_key, + strands_key, + design_modifications_key, + design_modifications_5p_key, + design_modifications_3p_key, + design_modifications_int_key, + groups_key, +] + legacy_geometry_keys; // Geometry keys @@ -239,12 +238,12 @@ const minor_groove_angle_key = 'minor_groove_angle'; const legacy_minor_groove_angle_keys = ['groove_angle']; const inter_helix_gap_key = 'inter_helix_gap'; final geometry_keys = [ - rise_per_base_pair_key, - helix_radius_key, - bases_per_turn_key, - minor_groove_angle_key, - inter_helix_gap_key, - ] + + rise_per_base_pair_key, + helix_radius_key, + bases_per_turn_key, + minor_groove_angle_key, + inter_helix_gap_key, +] + legacy_minor_groove_angle_keys + legacy_rise_per_base_pair_keys; @@ -265,21 +264,21 @@ const major_tick_start_key = 'major_tick_start'; const major_tick_periodic_distances_key = 'major_tick_periodic_distances'; const group_key = 'group'; final helix_keys = [ - idx_on_helix_key, - max_offset_key, - min_offset_key, - roll_key, - pitch_key, - yaw_key, - grid_position_key, - svg_position_key, - position_key, - major_ticks_key, - major_tick_distance_key, - major_tick_start_key, - major_tick_periodic_distances_key, - group_key, - ] + + idx_on_helix_key, + max_offset_key, + min_offset_key, + roll_key, + pitch_key, + yaw_key, + grid_position_key, + svg_position_key, + position_key, + major_ticks_key, + major_tick_distance_key, + major_tick_start_key, + major_tick_periodic_distances_key, + group_key, +] + legacy_position_keys; // Cannot have List concatenation in const expressions. // Seems like it won't be fixed soon (related issue): @@ -307,17 +306,17 @@ const modification_5p_key = '5prime_modification'; const modification_3p_key = '3prime_modification'; const modifications_int_key = 'internal_modifications'; final strand_keys = [ - color_key, - dna_sequence_key, - vendor_fields_key, - is_scaffold_key, - substrands_key, - modification_5p_key, - modification_3p_key, - modifications_int_key, - label_key, - name_key, - ] + + color_key, + dna_sequence_key, + vendor_fields_key, + is_scaffold_key, + substrands_key, + modification_5p_key, + modification_3p_key, + modifications_int_key, + label_key, + name_key, +] + legacy_dna_sequence_keys + legacy_vendor_fields_keys + legacy_substrands_keys; @@ -330,12 +329,12 @@ const legacy_mod_vendor_code_keys = ['idt_text']; const mod_allowed_bases_key = 'allowed_bases'; const mod_connector_length_key = 'connector_length'; final modification_keys = [ - mod_location_key, - mod_display_text_key, - mod_vendor_code_key, - mod_allowed_bases_key, - mod_connector_length_key, - ] + + mod_location_key, + mod_display_text_key, + mod_vendor_code_key, + mod_allowed_bases_key, + mod_connector_length_key, +] + legacy_mod_vendor_code_keys; // VendorFields keys @@ -361,16 +360,16 @@ const end_key = 'end'; const deletions_key = 'deletions'; const insertions_key = 'insertions'; final domain_keys = [ - helix_idx_key, - forward_key, - start_key, - end_key, - deletions_key, - insertions_key, - label_key, - name_key, - color_key, - ] + + helix_idx_key, + forward_key, + start_key, + end_key, + deletions_key, + insertions_key, + label_key, + name_key, + color_key, +] + legacy_forward_keys; // Loopout keys From 94b387b0e4d705677df9fa6d1c57a8c4e3309dca Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 23 Jul 2024 19:54:43 -0700 Subject: [PATCH 02/31] Update design_main_dna_sequence.dart --- lib/src/view/design_main_dna_sequence.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/view/design_main_dna_sequence.dart b/lib/src/view/design_main_dna_sequence.dart index 60745e2ce..48ba490bc 100644 --- a/lib/src/view/design_main_dna_sequence.dart +++ b/lib/src/view/design_main_dna_sequence.dart @@ -137,6 +137,12 @@ class DesignMainDNASequenceComponent extends UiComponent2 Date: Sun, 1 Sep 2024 18:14:20 +0100 Subject: [PATCH 03/31] removed .DS_Store and added to .gitignore --- lib/src/.DS_Store | Bin 6148 -> 0 bytes lib/src/.gitignore | 1 + 2 files changed, 1 insertion(+) delete mode 100644 lib/src/.DS_Store diff --git a/lib/src/.DS_Store b/lib/src/.DS_Store deleted file mode 100644 index 8593013e1819dfdc02666e5e260d9064eafa6882..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%Sr=55UkdKfn0L*IKSW@3?Y6&ejpM-1lfSE^Pc>!d|Ij>1jELQ;6*L-{M>zFSCug$opCCsP>xE-t(qT1xSUuTlLa}%{?{85K>xqg|Knk2I zaGl$w_y2qP5A**yNjoVZ1^$%+Hd}AjE51_o*2&9xuWj@#-D?hXH?D)i5bc;4?U);H e$G1_Gb Date: Tue, 3 Sep 2024 10:17:17 +0100 Subject: [PATCH 04/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 386394610..c8f4522ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -248,10 +248,10 @@ This project requires using Dart version **2.13**, not the latest version. Click
Windows First, install Chocolatey if you haven't already. If choco help shows a help menu for using Chocolatey, then you've set it up correctly. -Then, install Dart 2.13: +Then, install Dart 2.13.4:
-choco install dart-sdk --version 2.13
+choco install dart-sdk --version 2.13.4
 
To stop Chocolatey from automatically updating Dart to the latest version, pin it: From ac3b6f5a8ad074734838498419876b1289e3a96f Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 10:18:47 +0100 Subject: [PATCH 05/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8f4522ad..deb4d5959 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -257,7 +257,7 @@ choco install dart-sdk --version 2.13.4 To stop Chocolatey from automatically updating Dart to the latest version, pin it:
-choco pin --name="'dart-sdk'" --version="'2.13'"
+choco pin --name="'dart-sdk'" --version="'2.13.4'"
 
@@ -265,16 +265,16 @@ choco pin --name="'dart-sdk'" --version="'2.13'"
macOS First, install Homebrew if you haven't already. If brew -v shows some version numbers, then you've set it up correctly. -Then, install Dart 2.13: +Then, install Dart 2.13.4:
-brew install dart@2.13
+brew install dart@2.13.4
 
To stop Homebrew from automatically updating Dart to the latest version, pin it:
-brew pin dart@2.13
+brew pin dart@2.13.4
 
If running `dart` in a terminal now does not work, you may need to follow these instructions. @@ -290,17 +290,17 @@ wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dea echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list -Then, install Dart 2.13: +Then, install Dart 2.13.4:
 sudo apt-get update
-sudo apt-get install dart=2.13
+sudo apt-get install dart=2.13.4
 
To stop apt from automatically updating Dart to the latest version, hold it:
-sudo apt-mark hold dart=2.13
+sudo apt-mark hold dart=2.13.4
 
From 18474aaa4fa2b656d692461443348bf03db6069d Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 10:20:56 +0100 Subject: [PATCH 06/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index deb4d5959..d33f7cb0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -312,9 +312,7 @@ After installing the Dart SDK, you should see a help menu when you run `dart`. Once you have installed Dart, install all the Dart dependencies (from the same directory `scadnano` into which the project was cloned by git): ``` - pub get - ``` ### Installing `webdev` @@ -322,9 +320,7 @@ pub get This project uses an older version of the `webdev` tool, not the latest version, to build and serve the web app. Install it with: ``` - pub global activate webdev 2.5.9 - ``` ### Running a Local Server @@ -332,9 +328,7 @@ pub global activate webdev 2.5.9 Run ``` - webdev serve - ``` in the `scadnano` directory to compile your code From 1e25c8656e0b12b4838c2f882ea6938e5667de4c Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 10:27:47 +0100 Subject: [PATCH 07/31] added some hints for webdev errors --- CONTRIBUTING.md | 13 +++++++++++++ clean.sh | 12 +----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d33f7cb0d..f5cf174e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -323,6 +323,16 @@ This project uses an older version of the `webdev` tool, not the latest version, pub global activate webdev 2.5.9 ``` +Note that often a message like this appears: + +``` +Warning: Pub installs executables into C:\Users\pexat\AppData\Local\Pub\Cache\bin, which is not on your path. +You can fix that by adding that directory to your system's "Path" environment variable. +A web search for "configure windows path" will show you how. +``` +So you may need to add the installation location of `webdev` to your PATH environment variable. + + ### Running a Local Server Run @@ -335,6 +345,9 @@ in the `scadnano` directory to compile your code with the [Dart dev compiler](https://dart.dev/tools/dartdevc) (dartdevc) and start up a [local server](https://dart.dev/tools/webdev#serve). + +Sometimes it may be necessary to clean out the generated files and cache if this has an error. See the file `clean.sh`, which has this line: `dart run build_runner clean`. Also see `remove_g.sh`, which removes all `.g.dart` files from the project, which can also help to fix compilation errors. + Running `webdev serve --release` will compile the project in production mode (instead of development mode), which is claimed to be faster in principle if you are not doing development and just want to run scadnano offline. However, in scadnano, it doesn't appear to make a big difference whether development or production mode is used. The webdev program will tell you which URL to enter in your browser; it will be something like diff --git a/clean.sh b/clean.sh index b86ab43b1..d5be95321 100644 --- a/clean.sh +++ b/clean.sh @@ -1,14 +1,4 @@ -#TODO: don't know how to prevent this check from printing error message to the screen in case pub isn't found -if type pub > /dev/null; then - PUB=pub - echo "Using pub as dart pub command" -else - # on Windows the pub command is called pub.bat - PUB=pub.bat - echo "Using pub.bat as dart pub command" -fi - -$PUB run build_runner clean +dart run build_runner clean bash remove_g.sh #rm -rf .dart_tool/build/ From 39fba78787a9e55357cefc27a4577e37fc42e537 Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 11:00:20 +0100 Subject: [PATCH 08/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f5cf174e9..e0df58ab6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -348,6 +348,8 @@ server](https://dart.dev/tools/webdev#serve). Sometimes it may be necessary to clean out the generated files and cache if this has an error. See the file `clean.sh`, which has this line: `dart run build_runner clean`. Also see `remove_g.sh`, which removes all `.g.dart` files from the project, which can also help to fix compilation errors. +If that does not work, try `dart run build_runner clean --delete-conflicting-outputs`. + Running `webdev serve --release` will compile the project in production mode (instead of development mode), which is claimed to be faster in principle if you are not doing development and just want to run scadnano offline. However, in scadnano, it doesn't appear to make a big difference whether development or production mode is used. The webdev program will tell you which URL to enter in your browser; it will be something like From 6795ce7631d29ef950be3c33f755206cf3f1fd72 Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 14:54:47 +0100 Subject: [PATCH 09/31] replaced quadratic base-pair calculation in oxview export with linear-time search --- lib/src/middleware/oxdna_export.dart | 179 +++++++++++++++++---------- lib/src/state/design.dart | 45 +++++-- lib/src/state/strand.dart | 6 +- 3 files changed, 155 insertions(+), 75 deletions(-) diff --git a/lib/src/middleware/oxdna_export.dart b/lib/src/middleware/oxdna_export.dart index eb1692828..f6be85e30 100644 --- a/lib/src/middleware/oxdna_export.dart +++ b/lib/src/middleware/oxdna_export.dart @@ -3,6 +3,8 @@ import 'dart:html'; import 'dart:math'; import 'package:path/path.dart' as path; import 'package:quiver/iterables.dart' as quiver; +import 'package:react/react.dart'; +import 'package:built_collection/built_collection.dart'; import 'package:redux/redux.dart'; import 'package:scadnano/src/state/design.dart'; @@ -50,7 +52,9 @@ First select some strands, or choose Export🡒oxDNA to export all strands in th util.save_file(default_filename_dat, dat); util.save_file(default_filename_top, top); } else if (action is actions.OxviewExport) { + // var start = DateTime.now(); String content = to_oxview_format(state.design, strands_to_export); + // print('to_oxview_format: ${DateTime.now().inMilliseconds} ms'); String default_filename = state.ui_state.loaded_filename; String default_filename_ext = path.setExtension(default_filename, '.oxview'); util.save_file(default_filename_ext, content); @@ -60,27 +64,35 @@ First select some strands, or choose Export🡒oxDNA to export all strands in th } String to_oxview_format(Design design, List strands_to_export) { + // var start = DateTime.now(); OxdnaSystem system = convert_design_to_oxdna_system(design, strands_to_export); + // print('convert_design_to_oxdna_system: ${DateTime.now().difference(start).inMilliseconds} ms'); + + // start = DateTime.now(); List> oxview_strands = []; int nuc_count = 0; int strand_count = 0; List strand_nuc_start = [-1]; assert(strands_to_export.length == system.strands.length); + Map oxview_strand_map = {}; + Map strand_id_to_index = {}; for (int i = 0; i < strands_to_export.length; i++) { Strand sc_strand = strands_to_export[i]; + strand_id_to_index[sc_strand.id] = i; OxdnaStrand oxdna_strand = system.strands[i]; strand_count += 1; List> oxvnucs = []; strand_nuc_start.add(nuc_count); - Map oxvstrand = { + Map oxv_strand = { 'id': strand_count, 'class': 'NucleicAcidStrand', 'end5': nuc_count, 'end3': nuc_count + system.strands[i].nucleotides.length, 'monomers': oxvnucs }; + oxview_strand_map[sc_strand.id] = oxv_strand; int scolor; if (sc_strand.color != null) { @@ -112,73 +124,111 @@ String to_oxview_format(Design design, List strands_to_export) { nuc_count += 1; oxvnucs.add(oxvnuc); } - oxview_strands.add(oxvstrand); + oxview_strands.add(oxv_strand); } - - for (int si1 = 0; si1 < strands_to_export.length; si1++) { - Strand sc_strand1 = strands_to_export[si1]; - Map oxv_strand1 = oxview_strands[si1]; - for (int si2 = 0; si2 < strands_to_export.length; si2++) { - Strand sc_strand2 = strands_to_export[si2]; - if (!sc_strand1.overlaps(sc_strand2)) { - continue; - } - int s1_nuc_idx = strand_nuc_start[si1 + 1]; - for (var domain1 in sc_strand1.domains) { - if (domain1 is Loopout || domain1 is Extension) { - continue; - } - int s2_nuc_idx = strand_nuc_start[si2 + 1]; - for (var domain2 in sc_strand2.domains) { - if (domain2 is Loopout || domain2 is Extension) { - continue; - } - if (!domain1.overlaps(domain2)) { - continue; - } - Tuple2 overlap = domain1.compute_overlap(domain2); - int overlap_left = overlap.item1; - int overlap_right = overlap.item2; - int s1_left = sc_strand1.domain_offset_to_strand_dna_idx(domain1, overlap_left, false); - int s1_right = sc_strand1.domain_offset_to_strand_dna_idx(domain1, overlap_right, false); - int s2_left = sc_strand2.domain_offset_to_strand_dna_idx(domain2, overlap_left, false); - int s2_right = sc_strand2.domain_offset_to_strand_dna_idx(domain2, overlap_right, false); - List d1range; - List d2range; - if (domain1.forward) { - d1range = List.from(quiver.range(s1_left, s1_right)); - d2range = List.from(quiver.range(s2_left, s2_right, -1)); - } else { - d1range = List.from(quiver.range(s1_right + 1, s1_left + 1)); - d2range = List.from(quiver.range(s2_right - 1, s2_left - 1, -1)); - } - assert(d1range.length == d2range.length); - - // Check for mismatches, and do not add a pair if the bases are *known* - // to mismatch. (FIXME: this must be changed if scadnano later supports - // degenerate base codes.) - for (int i = 0; i < d1range.length; i++) { - int d1 = d1range[i]; - int d2 = d2range[i]; - if (sc_strand1.dna_sequence != null && - sc_strand2.dna_sequence != null && - sc_strand1.dna_sequence[d1] != constants.DNA_BASE_WILDCARD && - sc_strand2.dna_sequence[d2] != constants.DNA_BASE_WILDCARD && - util.wc(sc_strand1.dna_sequence[d1]) != sc_strand2.dna_sequence[d2]) { - continue; - } - oxv_strand1['monomers'][d1]['bp'] = s2_nuc_idx + d2; - if (oxview_strands[si2]['monomers'][d2].containsKey('bp')) { - if (oxview_strands[si2]['monomers'][d2]['bp'] != s1_nuc_idx + d1) { - print('${s2_nuc_idx + d2} ${s1_nuc_idx + d1} ' - '${oxview_strands[si2]['monomers'][d2]['bp']} ${domain1} ${domain2}'); - } - } - } + // print('first loop: ${DateTime.now().difference(start).inMilliseconds} ms'); + + // start = DateTime.now(); + + // find base pairs + //TODO: this is slow; compute this in a memoized getter on the Design, + // and make it linear time instead of quadratic by iterating over each helix, since + // it's useless to check all these pairs of strands that do not even have domains on the same helix. + // for (int i1 = 0; i1 < strands_to_export.length; i1++) { + // Strand sc_strand1 = strands_to_export[i1]; + // Map oxv_strand1 = oxview_strands[i1]; + // for (int i2 = 0; i2 < strands_to_export.length; i2++) { + // Strand sc_strand2 = strands_to_export[i2]; + // if (!sc_strand1.overlaps(sc_strand2)) { + // continue; + // } + // int s1_nuc_idx = strand_nuc_start[i1 + 1]; + // for (var domain1 in sc_strand1.domains) { + // if (domain1 is Loopout || domain1 is Extension) { + // continue; + // } + // int s2_nuc_idx = strand_nuc_start[i2 + 1]; + // for (var domain2 in sc_strand2.domains) { + // if (domain2 is Loopout || domain2 is Extension) { + // continue; + // } + // if (!domain1.overlaps(domain2)) { + // continue; + // } + // Tuple2 overlap = domain1.compute_overlap(domain2); + // int overlap_left = overlap.item1; + // int overlap_right = overlap.item2; + // int s1_left = sc_strand1.domain_offset_to_strand_dna_idx(domain1, overlap_left, false); + // int s1_right = sc_strand1.domain_offset_to_strand_dna_idx(domain1, overlap_right, false); + // int s2_left = sc_strand2.domain_offset_to_strand_dna_idx(domain2, overlap_left, false); + // int s2_right = sc_strand2.domain_offset_to_strand_dna_idx(domain2, overlap_right, false); + // List d1range; + // List d2range; + // if (domain1.forward) { + // d1range = List.from(quiver.range(s1_left, s1_right)); + // d2range = List.from(quiver.range(s2_left, s2_right, -1)); + // } else { + // d1range = List.from(quiver.range(s1_right + 1, s1_left + 1)); + // d2range = List.from(quiver.range(s2_right - 1, s2_left - 1, -1)); + // } + // assert(d1range.length == d2range.length); + // + // for (int i = 0; i < d1range.length; i++) { + // int d1 = d1range[i]; + // int d2 = d2range[i]; + // // Check for mismatches, and do not add a pair if the bases are *known* + // // to mismatch. (FIXME: this must be changed if scadnano later supports + // // degenerate base codes.) + // // Note that if either strand is *missing* a base (either because `dna_sequence` is null, or the + // // base is assigned `?`, we *do* add the pair. + // if (sc_strand1.dna_sequence != null && + // sc_strand2.dna_sequence != null && + // sc_strand1.dna_sequence[d1] != constants.DNA_BASE_WILDCARD && + // sc_strand2.dna_sequence[d2] != constants.DNA_BASE_WILDCARD && + // util.wc(sc_strand1.dna_sequence[d1]) != sc_strand2.dna_sequence[d2]) { + // continue; + // } + // oxv_strand1['monomers'][d1]['bp'] = s2_nuc_idx + d2; + // if (oxview_strands[i2]['monomers'][d2].containsKey('bp')) { + // if (oxview_strands[i2]['monomers'][d2]['bp'] != s1_nuc_idx + d1) { + // print('${s2_nuc_idx + d2} ${s1_nuc_idx + d1} ' + // '${oxview_strands[i2]['monomers'][d2]['bp']} ${domain1} ${domain2}'); + // } + // } + // } + // } + // } + // } + // } + + //TODO: this hasn't been tested well + var base_pairs_map = design.base_pairs_with_domain_strand(false, true, strands_to_export.toSet().build()); + for (int helix in base_pairs_map.keys) { + for (var offset_dom_strands in base_pairs_map[helix]) { + int offset = offset_dom_strands.item1; + Domain domain1 = offset_dom_strands.item2; + Domain domain2 = offset_dom_strands.item3; + Strand sc_strand1 = offset_dom_strands.item4; + Strand sc_strand2 = offset_dom_strands.item5; + Map oxv_strand1 = oxview_strand_map[sc_strand1.id]; + Map oxv_strand2 = oxview_strand_map[sc_strand2.id]; + int d1 = sc_strand1.domain_offset_to_strand_dna_idx(domain1, offset, false); + int d2 = sc_strand2.domain_offset_to_strand_dna_idx(domain2, offset, false); + int s1_nuc_idx = strand_nuc_start[strand_id_to_index[sc_strand1.id] + 1]; + int s2_nuc_idx = strand_nuc_start[strand_id_to_index[sc_strand2.id] + 1]; + oxv_strand1['monomers'][d1]['bp'] = s2_nuc_idx + d2; + if (oxv_strand2['monomers'][d2].containsKey('bp')) { + if (oxv_strand2['monomers'][d2]['bp'] != s1_nuc_idx + d1) { + print('${s2_nuc_idx + d2} ${s1_nuc_idx + d1} ' + '${oxv_strand2['monomers'][d2]['bp']} ${domain1} ${domain2}'); + window.alert("You have found a bug in scadnano, please file a bug report."); } } } + } + + // print('second loop: ${DateTime.now().difference(start).inMilliseconds} ms'); var b = system.compute_bounding_box(); Map oxvsystem = { 'box': [b.x, b.y, b.z], @@ -189,7 +239,10 @@ String to_oxview_format(Design design, List strands_to_export) { 'forces': [], 'selections': [], }; + + // start = DateTime.now(); String content = jsonEncode(oxvsystem); + // print('jsonEncode: ${DateTime.now().difference(start).inMilliseconds} ms'); return content; } diff --git a/lib/src/state/design.dart b/lib/src/state/design.dart index d97aad80a..15544bbfe 100644 --- a/lib/src/state/design.dart +++ b/lib/src/state/design.dart @@ -2117,8 +2117,15 @@ abstract class Design with UnusedFields implements Built, BuiltMap> selected_base_pairs_with_mismatches(BuiltSet selected_strands) => this._base_pairs(true, selected_strands); - BuiltMap> _base_pairs(bool allow_mismatches, BuiltSet selected_strands) { - var base_pairs = Map>(); + // returns a list of tuples (offset, d1, d2, s1, s2), where offset is the offset on the helix + // of a base pair, and d1/d2/s1/s2 are the domains/strands in the base pair + // allow_unassigned_dna means we consider two domains with a shared offset to be paired if + // either of them has unassigned DNA (either no DNA sequence, or a `?` wildcard) + // otherwise we only consider them paired if they both have DNA; `allow_mismatches` then determines + // whether they need to be complementary to be considered a base pair + BuiltMap>> base_pairs_with_domain_strand( + bool allow_mismatches, bool allow_unassigned_dna, BuiltSet selected_strands) { + var base_pairs_with_domain_strand = Map>>(); BuiltSet selected_domains = selected_strands .map((s) => s.substrands) .expand((x) => x) @@ -2126,11 +2133,14 @@ abstract class Design with UnusedFields implements Built, .map((x) => x as Domain) .toBuiltSet(); for (int idx in this.helices.keys) { - List offsets = []; + List> offsets_and_domain_strand = []; List> 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 (!allow_unassigned_dna && (dom1.dna_sequence == null || dom2.dna_sequence == null)) { + continue; + } if (!selected_domains.contains(dom1) || !selected_domains.contains(dom2)) { continue; } @@ -2141,21 +2151,38 @@ abstract class Design with UnusedFields implements Built, if (dom1.deletions.contains(offset) || dom2.deletions.contains(offset)) { continue; } - var seq1 = dom1.dna_sequence_in(offset, offset); - var seq2 = dom2.dna_sequence_in(offset, offset); + var base1 = dom1.dna_sequence_in(offset, offset); + var base2 = dom2.dna_sequence_in(offset, offset); + if (!allow_unassigned_dna) { + assert(base1 != null && base2 != null); // should have continue'd already + } // we use reverse_complementary instead of base_complementary here to allow for insertions // that may give a larger DNA sequence than length 1 at a given offset + // if we allow unassigned DNA, then either base being `?` means they are "complementary" if (allow_mismatches || - util.reverse_complementary(seq1, seq2, allow_wildcard: true, allow_null: true)) { - offsets.add(offset); + (allow_unassigned_dna && + (base1 == constants.DNA_BASE_WILDCARD || base2 == constants.DNA_BASE_WILDCARD)) || + util.reverse_complementary(base1, base2, allow_wildcard: true, allow_null: true)) { + offsets_and_domain_strand.add(Tuple5( + offset, dom1, dom2, this.substrand_to_strand[dom1], this.substrand_to_strand[dom2])); } } } - if (offsets.isNotEmpty) { - base_pairs[idx] = offsets.build(); + if (offsets_and_domain_strand.isNotEmpty) { + base_pairs_with_domain_strand[idx] = offsets_and_domain_strand.build(); } } + return base_pairs_with_domain_strand.build(); + } + + BuiltMap> _base_pairs(bool allow_mismatches, BuiltSet selected_strands) { + var base_pairs_with_domain_strand = + this.base_pairs_with_domain_strand(allow_mismatches, false, selected_strands); + var base_pairs = Map>(); + for (int idx in base_pairs_with_domain_strand.keys) { + base_pairs[idx] = base_pairs_with_domain_strand[idx].map((x) => x.item1).toList().build(); + } return base_pairs.build(); } diff --git a/lib/src/state/strand.dart b/lib/src/state/strand.dart index 96d3e955d..b0ca9a358 100644 --- a/lib/src/state/strand.dart +++ b/lib/src/state/strand.dart @@ -993,9 +993,9 @@ abstract class Strand /// Indicates whether `self` overlaps `other_strand`, meaning that the set of offsets occupied /// by `self` has nonempty intersection with those occupied by `other_strand`. bool overlaps(Strand other) { - for (var substrand_self in domains) { - for (var substrand_other in other.domains) { - if (substrand_self.overlaps(substrand_other)) { + for (var domain_self in domains) { + for (var domain_other in other.domains) { + if (domain_self.overlaps(domain_other)) { return true; } } From 6e535d2f630d2569541bc8b5bc13c6dbac32273a Mon Sep 17 00:00:00 2001 From: David Doty Date: Tue, 3 Sep 2024 15:36:54 +0100 Subject: [PATCH 10/31] fixed bug in loading design with invalid JSON syntax --- lib/src/state/design.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/src/state/design.dart b/lib/src/state/design.dart index 15544bbfe..f6e8be0b1 100644 --- a/lib/src/state/design.dart +++ b/lib/src/state/design.dart @@ -1276,7 +1276,12 @@ abstract class Design with UnusedFields implements Built, } static Design from_json_str(String json_str, [bool invert_y = false]) { - var json_map = jsonDecode(json_str); + Map json_map; + try { + json_map = jsonDecode(json_str); + } on FormatException catch (e) { + throw IllegalDesignError('Error in syntax of scadnano file: ${e.message}'); + } return Design.from_json(json_map, invert_y); } @@ -2099,7 +2104,8 @@ abstract class Design with UnusedFields implements Built, } } - /// maps each helix_idx to a list of offsets where there is a complementary base pair on each strand + /// maps each helix_idx to a list of offsets where there is a complementary base pair on each strand, + /// or when at least one of the strands lacks DNA (is null or is `?` wildcard) @memoized BuiltMap> get base_pairs => this._base_pairs(false, strands.toBuiltSet()); @@ -2178,7 +2184,7 @@ abstract class Design with UnusedFields implements Built, BuiltMap> _base_pairs(bool allow_mismatches, BuiltSet selected_strands) { var base_pairs_with_domain_strand = - this.base_pairs_with_domain_strand(allow_mismatches, false, selected_strands); + this.base_pairs_with_domain_strand(allow_mismatches, true, selected_strands); var base_pairs = Map>(); for (int idx in base_pairs_with_domain_strand.keys) { base_pairs[idx] = base_pairs_with_domain_strand[idx].map((x) => x.item1).toList().build(); From bb5097dad6d4b01838fa7f47a28a24d5071db155 Mon Sep 17 00:00:00 2001 From: David Doty Date: Wed, 4 Sep 2024 16:38:23 +0100 Subject: [PATCH 11/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0df58ab6..96f0fce76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -265,6 +265,8 @@ choco pin --name="'dart-sdk'" --version="'2.13.4'"
macOS First, install Homebrew if you haven't already. If brew -v shows some version numbers, then you've set it up correctly. +It may help to run `brew tap dart-lang/dart` first. + Then, install Dart 2.13.4:

From eeb65d1e1b9fe1c7a62a93b1dfaa7037d273fd34 Mon Sep 17 00:00:00 2001
From: David Doty 
Date: Wed, 4 Sep 2024 16:42:36 +0100
Subject: [PATCH 12/31] Update main.dart

---
 web/main.dart | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/main.dart b/web/main.dart
index bd082f9fb..a0171b0af 100644
--- a/web/main.dart
+++ b/web/main.dart
@@ -1,3 +1,5 @@
+// @dart=2.9
+
 import "package:scadnano/scadnano.dart";
 
 main() {

From 3e75f1bf1aa3dd3104be6b7c30d10c703466baf5 Mon Sep 17 00:00:00 2001
From: David Doty 
Date: Wed, 4 Sep 2024 17:19:06 +0100
Subject: [PATCH 13/31] Create oxview_update_view.dart

---
 lib/src/middleware/oxview_update_view.dart | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 lib/src/middleware/oxview_update_view.dart

diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart
new file mode 100644
index 000000000..2ca6b1e71
--- /dev/null
+++ b/lib/src/middleware/oxview_update_view.dart
@@ -0,0 +1,50 @@
+import 'dart:convert';
+import 'dart:html';
+import 'dart:js';
+import 'dart:math';
+import 'package:path/path.dart' as path;
+import 'package:quiver/iterables.dart' as quiver;
+import 'package:react/react.dart';
+import 'package:built_collection/built_collection.dart';
+
+import 'package:redux/redux.dart';
+import 'package:scadnano/src/state/design.dart';
+import 'package:scadnano/src/state/domain.dart';
+import 'package:scadnano/src/state/extension.dart';
+import 'package:scadnano/src/state/geometry.dart';
+import 'package:scadnano/src/state/grid.dart';
+import 'package:scadnano/src/state/loopout.dart';
+import 'package:scadnano/src/state/position3d.dart';
+import 'package:scadnano/src/state/strand.dart';
+import 'package:tuple/tuple.dart';
+import '../state/app_state.dart';
+import '../actions/actions.dart' as actions;
+import '../state/helix.dart';
+import '../util.dart' as util;
+import '../util.dart';
+import 'export_cadnano_or_codenano_file.dart' as export_cadnano;
+import '../constants.dart' as constants;
+import 'oxdna_export.dart';
+
+oxdna_export_middleware(Store store, dynamic action, NextDispatcher next) {
+  if (action is actions.DesignChangingAction) {
+    AppState state = store.state;
+
+    List strands_to_export = state.design.strands.toList();
+    String content = to_oxview_format(state.design, strands_to_export);
+    String blob_type_string = blob_type_to_string(BlobType.text);
+    Blob blob = new Blob([content], blob_type_string);
+
+    IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement;
+
+    Map message = {
+      'message': 'iframe_drop',
+      'files': [blob],
+      'ext': 'oxview',
+      'inbox_settings': ["Monomer", "Origin"],
+    };
+
+    frame.contentWindow?.postMessage(message, 'https://sulcgroup.github.io/oxdna-viewer/');
+  }
+  next(action);
+}

From 1f471286abe377426df88a5374d75cc73924c38f Mon Sep 17 00:00:00 2001
From: David Doty 
Date: Wed, 4 Sep 2024 17:20:25 +0100
Subject: [PATCH 14/31] fixed middleware

---
 lib/src/middleware/all_middleware.dart     | 2 ++
 lib/src/middleware/oxview_update_view.dart | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/src/middleware/all_middleware.dart b/lib/src/middleware/all_middleware.dart
index 2c9f64e6d..fec0467d5 100644
--- a/lib/src/middleware/all_middleware.dart
+++ b/lib/src/middleware/all_middleware.dart
@@ -21,6 +21,7 @@ import 'helix_offsets_change.dart';
 import 'insertion_deletion_batching.dart';
 import 'load_file.dart';
 import 'oxdna_export.dart';
+import 'oxview_update_view.dart';
 import 'periodic_save_design_local_storage.dart';
 import 'reselect_moved_dna_ends.dart';
 import 'reselect_moved_copied_strands.dart';
@@ -80,4 +81,5 @@ final all_middleware = List>.unmodifiable([
   system_clipboard_middleware,
   zoom_speed_middleware,
   oxdna_export_middleware,
+  oxview_update_view_middleware,
 ]);
diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart
index 2ca6b1e71..98ca148f9 100644
--- a/lib/src/middleware/oxview_update_view.dart
+++ b/lib/src/middleware/oxview_update_view.dart
@@ -26,7 +26,7 @@ import 'export_cadnano_or_codenano_file.dart' as export_cadnano;
 import '../constants.dart' as constants;
 import 'oxdna_export.dart';
 
-oxdna_export_middleware(Store store, dynamic action, NextDispatcher next) {
+oxview_update_view_middleware(Store store, dynamic action, NextDispatcher next) {
   if (action is actions.DesignChangingAction) {
     AppState state = store.state;
 

From 2161d1b73152d703a97c694f5fff71e6606328f7 Mon Sep 17 00:00:00 2001
From: David Doty 
Date: Wed, 4 Sep 2024 17:55:20 +0100
Subject: [PATCH 15/31] added iframe to main page

---
 web/index.html | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/web/index.html b/web/index.html
index 1a3c0a6d1..a7937d0df 100644
--- a/web/index.html
+++ b/web/index.html
@@ -98,7 +98,20 @@
 
 
 
-
+ +
+
+
+ +
+
From 5c82d206587ff5e352833cbda41294d17a226441 Mon Sep 17 00:00:00 2001 From: David Doty Date: Wed, 4 Sep 2024 17:55:39 +0100 Subject: [PATCH 16/31] using top and dat for now until we can fix the oxview iframe to accept .oxview files --- lib/src/middleware/oxview_update_view.dart | 36 +++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart index 98ca148f9..9d633d085 100644 --- a/lib/src/middleware/oxview_update_view.dart +++ b/lib/src/middleware/oxview_update_view.dart @@ -27,24 +27,32 @@ import '../constants.dart' as constants; import 'oxdna_export.dart'; oxview_update_view_middleware(Store store, dynamic action, NextDispatcher next) { + next(action); if (action is actions.DesignChangingAction) { - AppState state = store.state; + Design design = store.state.design; + + if (design != null) { + List strands_to_export = design.strands.toList(); - List strands_to_export = state.design.strands.toList(); - String content = to_oxview_format(state.design, strands_to_export); - String blob_type_string = blob_type_to_string(BlobType.text); - Blob blob = new Blob([content], blob_type_string); + // String content = to_oxview_format(design, strands_to_export); + Tuple2 dat_top = to_oxdna_format(design, strands_to_export); + String dat = dat_top.item1; + String top = dat_top.item2; - IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement; + String blob_type_string = blob_type_to_string(BlobType.text); + Blob blob_dat = new Blob([dat], blob_type_string); + Blob blob_top = new Blob([top], blob_type_string); - Map message = { - 'message': 'iframe_drop', - 'files': [blob], - 'ext': 'oxview', - 'inbox_settings': ["Monomer", "Origin"], - }; + IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement; - frame.contentWindow?.postMessage(message, 'https://sulcgroup.github.io/oxdna-viewer/'); + Map message = { + 'message': 'iframe_drop', + 'files': [blob_top, blob_dat], + 'ext': ['top', 'dat'], + 'inbox_settings': ["Monomer", "Origin"], + }; + + frame.contentWindow?.postMessage(message, 'https://sulcgroup.github.io/oxdna-viewer/'); + } } - next(action); } From 561a7624ae356f2a451bdba03d949c84643e2e75 Mon Sep 17 00:00:00 2001 From: David Doty Date: Wed, 4 Sep 2024 18:05:10 +0100 Subject: [PATCH 17/31] fixed some styling of oxview and scadnano frames --- lib/src/middleware/oxview_update_view.dart | 5 ++--- web/index.html | 21 ++++++++++----------- web/scadnano-styles.css | 8 +++++++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart index 9d633d085..60438ec77 100644 --- a/lib/src/middleware/oxview_update_view.dart +++ b/lib/src/middleware/oxview_update_view.dart @@ -30,8 +30,9 @@ oxview_update_view_middleware(Store store, dynamic action, NextDispatc next(action); if (action is actions.DesignChangingAction) { Design design = store.state.design; + IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement; - if (design != null) { + if (design != null && frame != null) { List strands_to_export = design.strands.toList(); // String content = to_oxview_format(design, strands_to_export); @@ -43,8 +44,6 @@ oxview_update_view_middleware(Store store, dynamic action, NextDispatc Blob blob_dat = new Blob([dat], blob_type_string); Blob blob_top = new Blob([top], blob_type_string); - IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement; - Map message = { 'message': 'iframe_drop', 'files': [blob_top, blob_dat], diff --git a/web/index.html b/web/index.html index a7937d0df..29ce979ce 100644 --- a/web/index.html +++ b/web/index.html @@ -99,18 +99,17 @@ -
+
-
+ id="top-container" + style="width: 50%;"> +
-
@@ -170,7 +169,7 @@ var allow_pan = true; var allow_zoom = true; - + function set_allow_pan(allow) { var prev_allow_pan = allow_pan; @@ -413,7 +412,7 @@ zoom_speed = speed; for (let svg of [side_view_svg_pan_zoom, main_view_svg_pan_zoom]) { - if(svg != undefined){ + if (svg != undefined) { svg.setZoomScaleSensitivity(speed); } } diff --git a/web/scadnano-styles.css b/web/scadnano-styles.css index ffab8b271..b7b8065e5 100644 --- a/web/scadnano-styles.css +++ b/web/scadnano-styles.css @@ -41,7 +41,13 @@ label + select { overflow: auto; } -#top-container { +#top-and-oxview { + display: flex; + height: 100%; + overflow: hidden; +} + +#top-container,#oxview-frame { display: flex; height: 100%; overflow: hidden; From accf6c01903c8c3a30b0d31bc082633f8d8a1904 Mon Sep 17 00:00:00 2001 From: David Doty Date: Wed, 4 Sep 2024 18:14:10 +0100 Subject: [PATCH 18/31] fixed bad ref to oxview frame id --- lib/src/middleware/oxview_update_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart index 60438ec77..42c856ab4 100644 --- a/lib/src/middleware/oxview_update_view.dart +++ b/lib/src/middleware/oxview_update_view.dart @@ -30,7 +30,7 @@ oxview_update_view_middleware(Store store, dynamic action, NextDispatc next(action); if (action is actions.DesignChangingAction) { Design design = store.state.design; - IFrameElement frame = querySelector('#oxview-frame-1') as IFrameElement; + IFrameElement frame = querySelector('#oxview-frame') as IFrameElement; if (design != null && frame != null) { List strands_to_export = design.strands.toList(); From dbaec91179b38bc12e4072e43fe0ae0ddc479693 Mon Sep 17 00:00:00 2001 From: David Doty Date: Thu, 5 Sep 2024 08:07:11 +0100 Subject: [PATCH 19/31] fixed slider separator for side pane and main pane; still can't get oxview and separator to display --- lib/src/actions/actions.dart | 12 +- lib/src/constants.dart | 1 + lib/src/middleware/local_storage.dart | 3 +- lib/src/middleware/oxview_update_view.dart | 12 +- lib/src/reducers/app_ui_state_reducer.dart | 10 +- lib/src/reducers/helices_reducer.dart | 12 +- lib/src/serializers.dart | 2 +- lib/src/state/app_ui_state.dart | 4 +- lib/src/state/app_ui_state_storables.dart | 4 +- lib/src/view/editor.dart | 210 ++------------------- lib/src/view/menu.dart | 26 ++- lib/src/view/view.dart | 75 +++++--- test/reducer_test.dart | 8 +- web/index.html | 46 +---- web/scadnano-styles.css | 2 +- web/splitpane.js | 23 ++- 16 files changed, 137 insertions(+), 313 deletions(-) diff --git a/lib/src/actions/actions.dart b/lib/src/actions/actions.dart index 6739e2ff1..119a4b00b 100644 --- a/lib/src/actions/actions.dart +++ b/lib/src/actions/actions.dart @@ -754,19 +754,19 @@ abstract class ShowUnpairedInsertionDeletionsSet _$showUnpairedInsertionDeletionsSetSerializer; } -abstract class SetShowEditor +abstract class OxviewShowSet with BuiltJsonSerializable - implements Action, Built { + implements Action, Built { bool get show; - factory SetShowEditor(bool show) => SetShowEditor.from((b) => b..show = show); + factory OxviewShowSet(bool show) => OxviewShowSet.from((b) => b..show = show); /************************ begin BuiltValue boilerplate ************************/ - factory SetShowEditor.from([void Function(SetShowEditorBuilder) updates]) = _$SetShowEditor; + factory OxviewShowSet.from([void Function(OxviewShowSetBuilder) updates]) = _$OxviewShowSet; - SetShowEditor._(); + OxviewShowSet._(); - static Serializer get serializer => _$setShowEditorSerializer; + static Serializer get serializer => _$oxviewShowSetSerializer; } abstract class SetDisplayBaseOffsetsOfMajorTicksOnlyFirstHelix diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 244c7b20c..f0b2dc972 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -184,6 +184,7 @@ const default_display_angle = 35.0; const default_display_length = 1.5; const default_side_pane_width = '8%'; +const default_design_width = "66%"; const default_group_name = 'default_group'; diff --git a/lib/src/middleware/local_storage.dart b/lib/src/middleware/local_storage.dart index c7f067ca8..16fc1ffb2 100644 --- a/lib/src/middleware/local_storage.dart +++ b/lib/src/middleware/local_storage.dart @@ -47,7 +47,8 @@ save(AppState state, Storable storable) { if (value_string != null) window.localStorage[storable_key] = value_string; } -String side_pane_width() => window.localStorage[_LOCAL_STORAGE_PREFIX + 'side_pane_width']; +String side_pane_width() => window.localStorage[_LOCAL_STORAGE_PREFIX + 'side-pane-width']; +String design_width() => window.localStorage[_LOCAL_STORAGE_PREFIX + 'design-and-modes-buttons-container-width']; restore(Store store, Storable storable) { try { diff --git a/lib/src/middleware/oxview_update_view.dart b/lib/src/middleware/oxview_update_view.dart index 42c856ab4..5d6051c43 100644 --- a/lib/src/middleware/oxview_update_view.dart +++ b/lib/src/middleware/oxview_update_view.dart @@ -25,10 +25,20 @@ import '../util.dart'; import 'export_cadnano_or_codenano_file.dart' as export_cadnano; import '../constants.dart' as constants; import 'oxdna_export.dart'; +import '../app.dart'; + +// This middleware handles sending the new design to the oxview viewer, +// as well as updating whether it is shown, since it is wired to the +// view outside of React. oxview_update_view_middleware(Store store, dynamic action, NextDispatcher next) { next(action); - if (action is actions.DesignChangingAction) { + + if (action is actions.OxviewShowSet) { + app.view.update_showing_oxview(); + } + + if (store.state.ui_state.show_oxview && action is actions.DesignChangingAction) { Design design = store.state.design; IFrameElement frame = querySelector('#oxview-frame') as IFrameElement; diff --git a/lib/src/reducers/app_ui_state_reducer.dart b/lib/src/reducers/app_ui_state_reducer.dart index fa220f45f..9169ffaa7 100644 --- a/lib/src/reducers/app_ui_state_reducer.dart +++ b/lib/src/reducers/app_ui_state_reducer.dart @@ -258,7 +258,7 @@ bool strand_paste_keep_color_reducer(bool _, actions.StrandPasteKeepColorSet act bool center_on_load_reducer(bool _, actions.AutofitSet action) => action.autofit; -bool show_editor_reducer(bool _, actions.SetShowEditor action) => action.show; +bool show_oxview_reducer(bool _, actions.OxviewShowSet action) => action.show; bool show_mouseover_data_set_reducer(bool _, actions.ShowMouseoverDataSet action) => action.show; @@ -460,7 +460,7 @@ AppUIStateStorables app_ui_state_storable_local_reducer(AppUIStateStorables stor ..clear_helix_selection_when_loading_new_design = TypedReducer(clear_helix_selection_when_loading_new_design_set_reducer)(storables.clear_helix_selection_when_loading_new_design, action) ..strand_paste_keep_color = TypedReducer(strand_paste_keep_color_reducer)(storables.strand_paste_keep_color, action) ..autofit = TypedReducer(center_on_load_reducer)(storables.autofit, action) - ..show_editor = TypedReducer(show_editor_reducer)(storables.show_editor, action) + ..show_oxview = TypedReducer(show_oxview_reducer)(storables.show_oxview, action) ..display_base_offsets_of_major_ticks = TypedReducer(display_base_offsets_of_major_ticks_reducer)(storables.display_base_offsets_of_major_ticks, action) ..display_base_offsets_of_major_ticks_only_first_helix = TypedReducer(display_base_offsets_of_major_ticks_only_first_helix_reducer)(storables.display_base_offsets_of_major_ticks_only_first_helix, action) ..display_major_tick_widths = TypedReducer(display_major_tick_widths_reducer)(storables.display_major_tick_widths, action) @@ -611,13 +611,13 @@ AppUIState ui_state_global_reducer(AppUIState ui_state, AppState state, action) ..original_helix_offsets = original_helix_offsets_reducer(ui_state.original_helix_offsets, state, action).toBuilder()); -BuiltMap original_helix_offsets_reducer( - BuiltMap original_helix_offsets, AppState state, action) { +BuiltMap> original_helix_offsets_reducer( + BuiltMap> original_helix_offsets, AppState state, action) { if (action is actions.StrandsMoveStartSelectedStrands || action is actions.StrandCreateStart) { var helix_offsets = original_helix_offsets.toMap(); for (int key in state.design.helices.keys) { var helix = state.design.helices[key]; - helix_offsets[state.design.helices[key].idx] = Tuple2.fromList([helix.min_offset, helix.max_offset]); + helix_offsets[state.design.helices[key].idx] = [helix.min_offset, helix.max_offset].build(); } return helix_offsets.build(); } diff --git a/lib/src/reducers/helices_reducer.dart b/lib/src/reducers/helices_reducer.dart index cf20097b1..9e0668e66 100644 --- a/lib/src/reducers/helices_reducer.dart +++ b/lib/src/reducers/helices_reducer.dart @@ -242,14 +242,14 @@ BuiltMap helix_offset_change_all_while_creating_strand_reducer( // Decrease helix size according to strand movement if (action.offset > helices_map[strand_creation.helix.idx].min_offset && helices_map[strand_creation.helix.idx].min_offset < - original_helix_offsets[strand_creation.helix.idx].item1) { + original_helix_offsets[strand_creation.helix.idx][0]) { helices_map[strand_creation.helix.idx] = helices_map[strand_creation.helix.idx].rebuild((b) => b..min_offset = action.offset); return helices_map.build(); } if (action.offset < helices_map[strand_creation.helix.idx].max_offset + 1 && helices_map[strand_creation.helix.idx].max_offset > - original_helix_offsets[strand_creation.helix.idx].item2) { + original_helix_offsets[strand_creation.helix.idx][1]) { helices_map[strand_creation.helix.idx] = helices_map[strand_creation.helix.idx].rebuild((b) => b.max_offset = action.offset + 1); return helices_map.build(); @@ -304,14 +304,14 @@ BuiltMap reset_helices_offsets(BuiltMap helices, AppStat var original_helix_offsets = state.ui_state.original_helix_offsets; for (int idx in original_helix_offsets.keys) { int current_helix_min_offset = state.design.min_offset_of_strands_at(idx); - if (current_helix_min_offset >= original_helix_offsets[idx].item1) { + if (current_helix_min_offset >= original_helix_offsets[idx][0]) { helices_updated[idx] = - helices_updated[idx].rebuild((b) => b.min_offset = original_helix_offsets[idx].item1); + helices_updated[idx].rebuild((b) => b.min_offset = original_helix_offsets[idx][0]); } int current_helix_max_offset = state.design.max_offset_of_strands_at(idx); - if (current_helix_max_offset <= original_helix_offsets[idx].item2) { + if (current_helix_max_offset <= original_helix_offsets[idx][1]) { helices_updated[idx] = - helices_updated[idx].rebuild((b) => b.max_offset = original_helix_offsets[idx].item2); + helices_updated[idx].rebuild((b) => b.max_offset = original_helix_offsets[idx][1]); } } return helices_updated.build(); diff --git a/lib/src/serializers.dart b/lib/src/serializers.dart index 55a02f51d..70110a661 100644 --- a/lib/src/serializers.dart +++ b/lib/src/serializers.dart @@ -123,7 +123,7 @@ part 'serializers.g.dart'; ShowDomainLabelsSet, ShowModificationsSet, ShowMismatchesSet, - SetShowEditor, + OxviewShowSet, ExportSvgTextSeparatelySet, SaveDNAFile, PrepareToLoadDNAFile, diff --git a/lib/src/state/app_ui_state.dart b/lib/src/state/app_ui_state.dart index f60741de1..8f15c0d94 100644 --- a/lib/src/state/app_ui_state.dart +++ b/lib/src/state/app_ui_state.dart @@ -186,7 +186,7 @@ abstract class AppUIState with BuiltJsonSerializable implements Built storables.show_unpaired_insertion_deletions; - bool get show_editor => storables.show_editor; + bool get show_oxview => storables.show_oxview; bool get autofit => storables.autofit; @@ -249,7 +249,7 @@ abstract class AppUIState with BuiltJsonSerializable implements Built storables.selection_box_intersection; - BuiltMap get original_helix_offsets; + BuiltMap> get original_helix_offsets; bool get ox_export_only_selected_strands => storables.ox_export_only_selected_strands; diff --git a/lib/src/state/app_ui_state_storables.dart b/lib/src/state/app_ui_state_storables.dart index a888538cf..2e26134e3 100644 --- a/lib/src/state/app_ui_state_storables.dart +++ b/lib/src/state/app_ui_state_storables.dart @@ -57,7 +57,7 @@ abstract class AppUIStateStorables bool get show_unpaired_insertion_deletions; - bool get show_editor; + bool get show_oxview; bool get show_slice_bar; @@ -157,7 +157,7 @@ abstract class AppUIStateStorables b.show_mismatches = false; b.show_domain_name_mismatches = false; b.show_unpaired_insertion_deletions = true; - b.show_editor = false; + b.show_oxview = false; b.only_display_selected_helices = false; b.zoom_speed = 0.3; b.modification_font_size = constants.default_modification_font_size; diff --git a/lib/src/view/editor.dart b/lib/src/view/editor.dart index c5e090b50..37cc3bc8e 100644 --- a/lib/src/view/editor.dart +++ b/lib/src/view/editor.dart @@ -18,207 +18,19 @@ import '../constants.dart' as constants; /// /// However, other than being hidden/shown, this does not react to any changes happening outside of its element. /// The updating of its source code goes through the normal event loop but only involves elements in this component. -class EditorViewComponent { +class OxviewViewComponent { DivElement root_element; - ParagraphElement footer_element = ParagraphElement()..attributes = {'id': 'footer'}; - CodeMirror editor; - final SpanElement controls_element = SpanElement(); - final ButtonElement save_button = ButtonElement(); - final ButtonElement compile_button = ButtonElement(); - final FileUploadInputElement file_chooser = FileUploadInputElement(); - - SelectElement theme_select_element = SelectElement(); - - EditorViewComponent(this.root_element) { - this.render_controls(); - - var editor_div_element = DivElement()..attributes = {'id': 'editor'}; - this.root_element.children.add(editor_div_element); - - this.root_element.children.add(footer_element); - - Map options = { - 'theme': 'mbo', - 'continueComments': {'continueLineComment': false}, - 'autoCloseTags': true, - 'mode': 'python', - 'lineNumbers': true, - 'indentUnit': 4, - 'extraKeys': {'Ctrl-Space': 'autocomplete', 'Cmd-/': 'toggleComment', 'Ctrl-/': 'toggleComment'} - }; - - this.editor = CodeMirror.fromElement(editor_div_element, options: options); - - for (String theme in CodeMirror.THEMES) { - theme_select_element.children.add(OptionElement(value: theme)..text = theme); - if (theme == editor.getTheme()) { - theme_select_element.selectedIndex = theme_select_element.length - 1; - } - } - theme_select_element.onChange.listen((e) { - String themeName = theme_select_element.options[theme_select_element.selectedIndex].value; - editor.setTheme(themeName); - }); - - Hints.registerHintsHelper('dart', _dartCompleter); - Hints.registerHintsHelperAsync('dart', _dartCompleterAsync); - - // Status line. - _updateFooter(editor); - editor.onCursorActivity.listen((_) => _updateFooter(editor)); - - CodeMirror.addCommand('find', (foo) { - /*LineHandle handle =*/ editor.getDoc().getLineHandle(editor.getCursor().line); - - print('todo: handle find'); - }); - - editor.onDoubleClick.listen((MouseEvent evt) { - Doc doc = editor.getDoc(); - print('[${doc.getLine(doc.getCursor().line).trim()}]'); - }); - - editor.getDoc().setValue(app.state.editor_content); - } - - render() { - editor.refresh(); - } - - void render_controls() { - this.controls_element.children.clear(); - - this.controls_element.children.add(this.compile_button); - - this.root_element.children.add(this.controls_element); - - // compile button - this.controls_element.children.add(compile_button); - this.compile_button.text = "Compile"; - - // save button - this.controls_element.children.add(save_button); - this.save_button.text = "Save"; - this.save_button.disabled = !app.state.ui_state.changed_since_last_save; - - // load button - this.controls_element.children.add(new LabelElement()..text = "Load:"); - this.controls_element.children.add(file_chooser); -// this.file_chooser.accept = ALLOWED_EXTENSIONS_SCRIPT.map((ext) => '.' + ext).join(","); - this.file_chooser.accept = '.' + constants.default_script_file_extension; - - // Theme control. - var theme_label_element = LabelElement()..attributes = {'for': 'theme'}; - theme_label_element.setInnerHtml('Theme:'); - this.theme_select_element = SelectElement()..attributes = {'id': 'theme'}; - controls_element.children.addAll([theme_label_element, this.theme_select_element]); - this.root_element.children.add(controls_element); - } - - void _updateFooter(CodeMirror editor) { - Position pos = editor.getCursor(); - int off = editor.getDoc().indexFromPos(pos); - String str = 'line ${pos.line} • column ${pos.ch} • offset ${off}' + - (editor.getDoc().isClean() ? '' : ' • (modified)'); - this.footer_element.text = str; - } - - HintResults _dartCompleter(CodeMirror editor, [HintsOptions options]) { - Position cur = editor.getCursor(); - String word = _getCurrentWord(editor).toLowerCase(); - List list = _numbers.where((s) => s.startsWith(word)).map((s) => HintResult(s)).toList(); - - HintResults results = - HintResults.fromHints(list, Position(cur.line, cur.ch - word.length), Position(cur.line, cur.ch)); - results.registerOnShown(() => print('hints shown')); - results.registerOnSelect((completion, element) { - print(['hints select: ${completion}']); - }); - results.registerOnPick((completion) { - print(['hints pick: ${completion}']); - }); - results.registerOnUpdate(() => print('hints update')); - results.registerOnClose(() => print('hints close')); - - return results; - } - -//void _hintRenderer(Element element, HintResult hint) { -// element.children.add(new DivElement()..text = hint.text); -//} - -//void _hintApplier(CodeMirror editor, HintResult hint, Position from, Position to) { -// editor.getDoc().replaceRange(hint.text + "_foo_", from, to); -//} - - Future _dartCompleterAsync(CodeMirror editor, [HintsOptions options]) { - Position cur = editor.getCursor(); - String word = _getCurrentWord(editor).toLowerCase(); - List list = List.from(_numbers.where((s) => s.startsWith(word))); - - return Future.delayed(Duration(milliseconds: 200), () { - return HintResults.fromStrings( - list, Position(cur.line, cur.ch - word.length), Position(cur.line, cur.ch)); - }); - } - - final List _numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; - - final RegExp _ids = RegExp(r'[a-zA-Z_0-9]'); - - String _getCurrentWord(CodeMirror editor) { - Position cur = editor.getCursor(); - String line = editor.getLine(cur.line); - StringBuffer buf = StringBuffer(); - - for (int i = cur.ch - 1; i >= 0; i--) { - String c = line[i]; - if (_ids.hasMatch(c)) { - buf.write(c); - } else { - break; - } - } - - return String.fromCharCodes(buf.toString().codeUnits.reversed); + OxviewViewComponent(this.root_element) { + // var oxview_div_element = DivElement() + // ..attributes = {'id': 'editor'}; + var iframe_element = IFrameElement() + ..attributes = { + 'height': '100%', + 'src': 'https://sulcgroup.github.io/oxdna-viewer/', + 'id': 'oxview-frame' + }; + this.root_element.children.add(iframe_element); } } -//TODO: there's a lot of repeated code between here and the functions that save/load the .dna files -// handle file saving/loading for script files -script_save_file() async { - Blob blob = new Blob([app.state.editor_content], 'text/plain;charset=utf-8'); - String url = Url.createObjectUrlFromBlob(blob); - String filename = app.state.ui_state.loaded_script_filename; - var link = new AnchorElement() - ..href = url - ..download = filename; - - document.body.children.add(link); - await link.click(); - link.remove(); -} - -request_script_load_file_from_file_chooser(FileUploadInputElement file_chooser) { - List files = file_chooser.files; - assert(files.isNotEmpty); - File file = files[0]; - - var basefilename = path.basenameWithoutExtension(file.name); - - FileReader file_reader = new FileReader(); - file_reader.onLoad.listen((_) => script_file_loaded(file_reader, basefilename)); - var err_msg = "error reading file: ${file_reader.error.toString()}"; - //file_reader.onError.listen((e) => error_message.text = err_msg); - file_reader.onError.listen((_) => window.alert(err_msg)); - file_reader.readAsText(file); -} - -script_file_loaded(FileReader file_reader, String filename) { - //FIXME: send an action to update the fields loaded_script_filename and editor_content -// app.state.ui_model.loaded_script_filename = filename; -// app.state.editor_content = file_reader.result; -// print('in dart, just put this script file contents into state: ${app.state.editor_content}'); - app.view.editor_view.render(); -} diff --git a/lib/src/view/menu.dart b/lib/src/view/menu.dart index 12ac7f4ca..425a6ea3a 100644 --- a/lib/src/view/menu.dart +++ b/lib/src/view/menu.dart @@ -47,6 +47,7 @@ UiFactory ConnectedMenu = connect( ..geometry = state.design?.geometry ..no_grid_is_none = state.design == null ? false : state.design.groups.values.every((group) => group.grid != Grid.none) + ..show_oxview = state.ui_state.show_oxview ..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 @@ -116,6 +117,7 @@ mixin MenuPropsMixin on UiProps { BuiltSet selected_ends; bool selection_box_intersection; bool no_grid_is_none; + bool show_oxview; bool show_dna; bool show_strand_names; bool show_strand_labels; @@ -711,7 +713,6 @@ Ignored if design is not an origami (i.e., does not have at least one scaffold). view_menu() { var elts = [ view_menu_warnings(), - // view_menu_show_dna(), view_menu_autofit(), view_menu_show_labels(), view_menu_mods(), @@ -719,7 +720,9 @@ Ignored if design is not an origami (i.e., does not have at least one scaffold). view_menu_display_major_ticks_options(), view_menu_base_pairs(), view_menu_dna(), - DropdownDivider({'key': 'divider-major-tick-widths'}), + DropdownDivider({'key': 'divider-dna'}), + ...view_menu_show_oxview(), + DropdownDivider({'key': 'divider-oxview'}), ...view_menu_zoom_speed(), DropdownDivider({'key': 'divider-zoom_speed'}), ...view_menu_misc(), @@ -1077,16 +1080,31 @@ to speed up navigation.''' ..value = props.display_reverse_DNA_right_side_up ..display = 'Display reverse DNA right-side up' ..tooltip = '''\ - Displays DNA right-side up on reverse strands.''' +Displays DNA right-side up on reverse strands.''' ..name = 'display-reverse-DNA-right-side-up' ..hide = !props.show_dna ..onChange = (_) { props.dispatch(actions.DisplayReverseDNARightSideUpSet(!props.display_reverse_DNA_right_side_up)); } - ..key = 'display-reverse-DNA-right-side-up')() + ..key = 'display-reverse-DNA-right-side-up')(), ]); } + List view_menu_show_oxview() { + return [ + (MenuBoolean() + ..value = props.show_oxview + ..display = 'Show oxView' + ..tooltip = '''\ +Displays an embedded oxView window to visualize the 3D structure of the design.''' + ..name = 'show-oxview' + ..onChange = (_) { + props.dispatch(actions.OxviewShowSet(!props.show_oxview)); + } + ..key = 'show-oxview')(), + ]; + } + List view_menu_zoom_speed() { return [ (MenuNumber() diff --git a/lib/src/view/view.dart b/lib/src/view/view.dart index 5da2884c0..f883235a0 100644 --- a/lib/src/view/view.dart +++ b/lib/src/view/view.dart @@ -16,6 +16,7 @@ import 'menu.dart'; import 'editor.dart'; import '../app.dart'; import '../util.dart' as util; +import '../middleware/local_storage.dart' as local_storage; import '../constants.dart' as constants; @@ -24,7 +25,7 @@ external setup_svg_panzoom_js(void Function() svg_cache_callback, void Function(bool) dispatch_zoom_threshold_callback, num zoom_threshold); @JS(constants.js_function_name_setup_splits) -external setup_splits(bool show_editor); +external setup_splits(bool show_oxview); @JS(constants.js_function_name_sdrag) external sdrag_js(); @@ -33,8 +34,9 @@ const MENU_ID = 'menu'; const EDIT_MODE_ID = 'edit-mode'; const SELECT_MODE_ID = 'select-mode'; const DESIGN_ID = 'design-pane'; -const EDITOR_ID = 'editor-pane'; +const OXVIEW_ID = 'oxview-pane'; const DESIGN_AND_MODES_BUTTONS_CONTAINER_ID = 'design-and-modes-buttons-container'; +const NONMENU_PANES_CONTAINED_ID = 'nonmenu-panes-container'; const EDIT_AND_SELECT_MODES_ID = 'modes-buttons'; const FIXED_VERTICAL_SEPARATOR = 'fixed-vertical-separator'; @@ -51,27 +53,26 @@ class View { ..attributes = {'id': DESIGN_AND_MODES_BUTTONS_CONTAINER_ID}; DivElement edit_and_select_modes_element = DivElement()..attributes = {'id': EDIT_AND_SELECT_MODES_ID}; DivElement menu_element = DivElement()..attributes = {'id': MENU_ID}; + DivElement nonmenu_panes_container_element = DivElement()..attributes = {'id': NONMENU_PANES_CONTAINED_ID}; DivElement design_element = DivElement()..attributes = {'id': DESIGN_ID}; - DivElement design_editor_separator = DivElement() - ..attributes = {'id': 'design-editor-separator', 'class': 'draggable-separator'}; - DivElement editor_element = DivElement()..attributes = {'id': EDITOR_ID}; + DivElement design_oxview_separator = DivElement() + ..attributes = {'id': 'design-oxview-separator', 'class': 'draggable-separator'}; + DivElement oxview_element = DivElement()..attributes = {'id': OXVIEW_ID}; DesignViewComponent design_view; - EditorViewComponent editor_view; + OxviewViewComponent oxview_view; - bool currently_showing_editor; + bool currently_showing_oxview = false; View(this.root_element) { setup_file_drag_and_drop_listener(root_element); - currently_showing_editor = app.state.ui_state.show_editor; - this.root_element.children.add(menu_element); var menu_design_separator = DivElement()..attributes = {'class': FIXED_HORIZONTAL_SEPARATOR}; this.root_element.children.add(menu_design_separator); - this.root_element.children.add(this.design_and_modes_buttons_container_element); - // DEBUG the virtual canvas for svg-png-caching, uncomment this, used in utils.dart - // this.root_element.children.add(CanvasElement(width: 100, height: 100)..id='canvas-dev'); + this.root_element.children.add(this.nonmenu_panes_container_element); + + this.nonmenu_panes_container_element.children.add(this.design_and_modes_buttons_container_element); this.design_and_modes_buttons_container_element.children.add(design_element); var design_mode_separator = DivElement()..attributes = {'class': FIXED_VERTICAL_SEPARATOR}; @@ -80,9 +81,14 @@ class View { this.design_and_modes_buttons_container_element.children.add(edit_and_select_modes_element); this.design_view = DesignViewComponent(design_element); + this.oxview_view = OxviewViewComponent(oxview_element); + + this.update_showing_oxview(); } render(AppState state) { + this.update_showing_oxview(); + var store = app.store; react_dom.render( @@ -108,25 +114,32 @@ class View { util.fit_and_center(); } -// update_showing_editor() { -// //TODO: Firefox won't let editor pane shrink (when pan separator is dragged) to hide text; -// // Chrome puts a scrollbar at the bottom when that happens and lets the editor pane shrink -// // arbitrarily (which is the desired behavior) -// -// if (!this.currently_showing_editor && app.state.show_editor) { -// this.nonmenu_panes_container_element.children.add(design_editor_separator); -// this.nonmenu_panes_container_element.children.add(editor_element); -// this.currently_showing_editor = true; -// setup_splits(app.state.show_editor); -// this.editor_view.render(); -// } else if (this.currently_showing_editor && !app.state.show_editor) { -// this.nonmenu_panes_container_element.children.remove(design_editor_separator); -// this.nonmenu_panes_container_element.children.remove(editor_element); -// this.currently_showing_editor = false; -// setup_splits(app.state.show_editor); -// this.editor_view.render(); -// } -// } + update_showing_oxview() { + bool show_oxview = app.state.ui_state.show_oxview; + if (!this.currently_showing_oxview && show_oxview) { + this.nonmenu_panes_container_element.children.add(design_oxview_separator); + this.nonmenu_panes_container_element.children.add(oxview_element); + this.currently_showing_oxview = true; + this.set_design_oxview_pane_widths(); + setup_splits(show_oxview); + } else if (this.currently_showing_oxview && !show_oxview) { + this.nonmenu_panes_container_element.children.remove(design_oxview_separator); + this.nonmenu_panes_container_element.children.remove(oxview_element); + this.currently_showing_oxview = false; + setup_splits(show_oxview); + } + } + + set_design_oxview_pane_widths() { + String design_width = local_storage.design_width(); + if (design_width == null) { + design_width = constants.default_design_width; + } + num oxview_width_int = 100.0 - num.parse(design_width.substring(0, design_width.length - 1)); + String oxview_width = '${oxview_width_int.toString()}%'; + design_and_modes_buttons_container_element.setAttribute('style', 'width: $design_width'); + oxview_element.setAttribute('style', 'width: $oxview_width'); + } } setup_file_drag_and_drop_listener(Element drop_zone) { diff --git a/test/reducer_test.dart b/test/reducer_test.dart index dd24e0300..9d41d3b3b 100644 --- a/test/reducer_test.dart +++ b/test/reducer_test.dart @@ -3173,10 +3173,10 @@ main() { test('Test_SetShowEditor', () { AppState initial_state = app_state_from_design(two_helices_design); - AppState final_state = app_state_reducer(initial_state, SetShowEditor(true)); - expect(final_state.ui_state.show_editor, true); - final_state = app_state_reducer(final_state, SetShowEditor(false)); - expect(final_state.ui_state.show_editor, false); + AppState final_state = app_state_reducer(initial_state, OxviewShowSet(true)); + expect(final_state.ui_state.show_oxview, true); + final_state = app_state_reducer(final_state, OxviewShowSet(false)); + expect(final_state.ui_state.show_oxview, false); }); test('Test_SetOnlyDisplaySelectedHelices', () { diff --git a/web/index.html b/web/index.html index 29ce979ce..20b9b9a49 100644 --- a/web/index.html +++ b/web/index.html @@ -96,43 +96,7 @@ - - - -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - +
- -