Skip to content

Commit

Permalink
Merge branch 'dev' into dev-issue-488
Browse files Browse the repository at this point in the history
  • Loading branch information
UnHumbleBen committed Nov 8, 2020
2 parents bc5bc99 + 113355f commit 2066621
Show file tree
Hide file tree
Showing 37 changed files with 1,263 additions and 298 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,25 +562,27 @@ There are different edit modes available, shown on the right side of the screen.
Technically these operations are unnecessary, but they are faster than creating/moving/deleting domains in some circumstances. In nick mode, clicking on a bound domain will split it into two at that position. Ligate mode does the reverse operation: if two bound domains point in the same direction and have abutting 5'/3' ends, then clicking on either will join them into a single strand. A common way to create a large design quickly is to use pencil mode to create exactly two strands on each helix at the same horizontal offsets, one pointing forward (i.e,. its 5' end is on the left and its 3' end is on the right) and the other pointing in reverse. Then use select mode to drag them to be longer. Then use nick mode to add nicks and pencil mode to add crossovers.

* **(i)nsertion / (d)eletion:**
These have the same meaning as in cadnano.
These have the same meaning as in cadnano, where they are called "loops" and "skips", respectively.
They are a visual trick used to allow bound domains to appear to be one length in the main view of scadnano, while actually having a different length.
Normally, each offset (small white square outlined in gray on a helix) represents a single base.
Clicking on a bound domain in insertion/deletion mode adds an insertion/deletion at that offset.
Clicking an existing insertion/deletion removes it.
(Note that this requires clicking in the small square where the bound domain is drawn;
clicking on an insertion outside of that square allows one to change its length.)
clicking on an insertion outside of that square has no effect.)
If a deletion appears at that position, then it does not correspond to any DNA base.
If an insertion appears at that position, it has a *length*, which is a positive integer,
and the number of bases represented by that position is actually *length*+1.
In other words *length* is the number of *extra* bases at that position in addition to the one that was already there (so insertions always represent 2 or more bases).

Currently, if one offset on a helix has two bound domains (going in opposite directions),
If one offset on a helix has two bound domains (going in opposite directions),
then adding/removing an insertion/deletion at that offset adds/removes on both bound domains.
The Python scripting library lets one specify insertions/deletions on one bound domain but not the other,
but this is currently [unsupported](https://github.com/UC-Davis-molecular-computing/scadnano/issues/90) in the web interface to create such a solitary deletion/insertion directly.
(If necessary, one hack is to move one domain out of the way, add the deletion/insertion to the other, and then move the first back.)
If you want only one of the domains to have the insertion/deletion, then click in Insertion/Deletion mode to add to both, switch to Select mode, select the unwanted one, and press the Delete key.

*Note for cadnano users:* From the user's perspective, cadnano associates each deletion/insertion to an "address", i.e., a helix and offset on that helix. For instance, it is possible to have a "deletion" where there is no DNA strand, and if DNA strand(s) are later placed there, they will have the deletion. By contrast, insertions and deletions in scadnano are associated to a bound domain. If the whole strand moves or is copied, the insertions/deletions move along with it.
A useful shortcut allows one to add many insertions/deletions to the same offset on all helices (in a helix group).
If the Ctrl key is pressed while clicking on a domain in Deletion/Insertion mode, then deletions/insertions will be added at *all* domains on all helices in the same helix group that overlap the same offset (unless that offset is the start or end of the domain, which is disallowed from having a deletion/insertion).
This helps to implement, for example, twist correction in DNA origami (see https://doi.org/10.1038/nchem.1070 for a description), where vertical "columns" of deletions need to be added to every domain in every helix at a given offset.

*Note for cadnano users:* From the user's perspective, cadnano associates each deletion/insertion to an "address", i.e., a helix and offset on that helix. For instance, it is possible to have a "deletion" where there is no DNA strand, and if DNA strand(s) are later placed there, they will have the deletion. By contrast, in scadnano, insertions and deletions are associated to a bound domain. If the domain (or the whole strand) moves, the insertions/deletions move along with it.

* **(b)ackbone:**
This shows information in the side view about the roll of the helix when the pointer is over an offset of that helix in the main view,
Expand Down
163 changes: 141 additions & 22 deletions lib/src/actions/actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:built_collection/built_collection.dart';
import 'package:scadnano/src/state/domains_move.dart';
import 'package:scadnano/src/state/geometry.dart';
import 'package:scadnano/src/state/helix_group_move.dart';
import 'package:tuple/tuple.dart';

import '../state/app_ui_state_storables.dart';
import '../state/domain.dart';
Expand Down Expand Up @@ -235,15 +236,19 @@ abstract class LocalStorageDesignChoiceSet

abstract class ClearHelixSelectionWhenLoadingNewDesignSet
with BuiltJsonSerializable
implements Action, Built<ClearHelixSelectionWhenLoadingNewDesignSet, ClearHelixSelectionWhenLoadingNewDesignSetBuilder> {
implements
Action,
Built<ClearHelixSelectionWhenLoadingNewDesignSet, ClearHelixSelectionWhenLoadingNewDesignSetBuilder> {
bool get clear;

/************************ begin BuiltValue boilerplate ************************/
factory ClearHelixSelectionWhenLoadingNewDesignSet({bool clear}) = _$ClearHelixSelectionWhenLoadingNewDesignSet._;
factory ClearHelixSelectionWhenLoadingNewDesignSet({bool clear}) =
_$ClearHelixSelectionWhenLoadingNewDesignSet._;

ClearHelixSelectionWhenLoadingNewDesignSet._();

static Serializer<ClearHelixSelectionWhenLoadingNewDesignSet> get serializer => _$clearHelixSelectionWhenLoadingNewDesignSetSerializer;
static Serializer<ClearHelixSelectionWhenLoadingNewDesignSet> get serializer =>
_$clearHelixSelectionWhenLoadingNewDesignSetSerializer;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -368,8 +373,7 @@ abstract class ShowDomainNamesSet
factory ShowDomainNamesSet(bool show) => ShowDomainNamesSet.from((b) => b..show = show);

/************************ begin BuiltValue boilerplate ************************/
factory ShowDomainNamesSet.from([void Function(ShowDomainNamesSetBuilder) updates]) =
_$ShowDomainNamesSet;
factory ShowDomainNamesSet.from([void Function(ShowDomainNamesSetBuilder) updates]) = _$ShowDomainNamesSet;

ShowDomainNamesSet._();

Expand Down Expand Up @@ -494,10 +498,12 @@ abstract class ShowDomainNameMismatchesSet
implements Action, Built<ShowDomainNameMismatchesSet, ShowDomainNameMismatchesSetBuilder> {
bool get show_domain_name_mismatches;

factory ShowDomainNameMismatchesSet(bool show_domain_name_mismatches) => ShowDomainNameMismatchesSet.from((b) => b..show_domain_name_mismatches = show_domain_name_mismatches);
factory ShowDomainNameMismatchesSet(bool show_domain_name_mismatches) =>
ShowDomainNameMismatchesSet.from((b) => b..show_domain_name_mismatches = show_domain_name_mismatches);

/************************ begin BuiltValue boilerplate ************************/
factory ShowDomainNameMismatchesSet.from([void Function(ShowDomainNameMismatchesSetBuilder) updates]) = _$ShowDomainNameMismatchesSet;
factory ShowDomainNameMismatchesSet.from([void Function(ShowDomainNameMismatchesSetBuilder) updates]) =
_$ShowDomainNameMismatchesSet;

ShowDomainNameMismatchesSet._();

Expand Down Expand Up @@ -1517,6 +1523,26 @@ abstract class LoopoutLengthChange
static Serializer<LoopoutLengthChange> get serializer => _$loopoutLengthChangeSerializer;
}

abstract class LoopoutsLengthChange
with BuiltJsonSerializable, UndoableAction
implements Built<LoopoutsLengthChange, LoopoutsLengthChangeBuilder> {
BuiltList<Loopout> get loopouts;

int get length;

/************************ begin BuiltValue boilerplate ************************/
factory LoopoutsLengthChange(Iterable<Loopout> loopouts, int length) => LoopoutsLengthChange.from((b) => b
..loopouts.replace(loopouts)
..length = length);

factory LoopoutsLengthChange.from([void Function(LoopoutsLengthChangeBuilder) updates]) =
_$LoopoutsLengthChange;

LoopoutsLengthChange._();

static Serializer<LoopoutsLengthChange> get serializer => _$loopoutsLengthChangeSerializer;
}

abstract class ConvertCrossoverToLoopout
with BuiltJsonSerializable, UndoableAction
implements StrandPartAction, Built<ConvertCrossoverToLoopout, ConvertCrossoverToLoopoutBuilder> {
Expand All @@ -1540,6 +1566,27 @@ abstract class ConvertCrossoverToLoopout
static Serializer<ConvertCrossoverToLoopout> get serializer => _$convertCrossoverToLoopoutSerializer;
}

abstract class ConvertCrossoversToLoopouts
with BuiltJsonSerializable, UndoableAction
implements Built<ConvertCrossoversToLoopouts, ConvertCrossoversToLoopoutsBuilder> {
BuiltList<Crossover> get crossovers;

int get length;

/************************ begin BuiltValue boilerplate ************************/
factory ConvertCrossoversToLoopouts(Iterable<Crossover> crossovers, int length) =>
ConvertCrossoversToLoopouts.from((b) => b
..crossovers.replace(crossovers)
..length = length);

factory ConvertCrossoversToLoopouts.from([void Function(ConvertCrossoversToLoopoutsBuilder) updates]) =
_$ConvertCrossoversToLoopouts;

ConvertCrossoversToLoopouts._();

static Serializer<ConvertCrossoversToLoopouts> get serializer => _$convertCrossoversToLoopoutsSerializer;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// nick/join

Expand Down Expand Up @@ -2052,9 +2099,11 @@ abstract class InsertionOrDeletionAction implements UndoableAction, StrandPartAc

int get offset;

bool get all_helices;

StrandPart get strand_part; // => substrand;

InsertionOrDeletionAction clone_for_adjacent_substrand(Domain other_domain);
InsertionOrDeletionAction clone_for_other_domain(Domain other_domain);
}

abstract class InsertionAdd
Expand All @@ -2064,12 +2113,14 @@ abstract class InsertionAdd

int get offset;

bool get all_helices;

StrandPart get strand_part => domain;

InsertionAdd clone_for_adjacent_substrand(Domain domain) => InsertionAdd(domain: domain, offset: offset);
InsertionAdd clone_for_other_domain(Domain domain) => rebuild((b) => b..domain.replace(domain));

/************************ begin BuiltValue boilerplate ************************/
factory InsertionAdd({Domain domain, int offset}) = _$InsertionAdd._;
factory InsertionAdd({Domain domain, int offset, bool all_helices}) = _$InsertionAdd._;

InsertionAdd._();

Expand All @@ -2085,38 +2136,80 @@ abstract class InsertionLengthChange

int get length;

bool get all_helices;

int get offset => insertion.offset;

StrandPart get strand_part => domain;

InsertionLengthChange clone_for_adjacent_substrand(Domain other_domain) => InsertionLengthChange(
InsertionLengthChange clone_for_other_domain(Domain other_domain) => InsertionLengthChange(
domain: other_domain,
insertion: other_domain.insertions.firstWhere((i) => i.offset == offset),
length: length,
);

/************************ begin BuiltValue boilerplate ************************/
factory InsertionLengthChange({Domain domain, Insertion insertion, int length}) = _$InsertionLengthChange._;
factory InsertionLengthChange({Domain domain, Insertion insertion, int length}) {
return InsertionLengthChange.from((b) => b
..domain.replace(domain)
..insertion.replace(insertion)
..length = length
..all_helices = false);
}

factory InsertionLengthChange.from([void Function(InsertionLengthChangeBuilder) updates]) =
_$InsertionLengthChange;

// factory InsertionLengthChange({Domain domain, Insertion insertion, int length}) = _$InsertionLengthChange._;

InsertionLengthChange._();

static Serializer<InsertionLengthChange> get serializer => _$insertionLengthChangeSerializer;
}

abstract class InsertionsLengthChange
with BuiltJsonSerializable, UndoableAction
implements Action, Built<InsertionsLengthChange, InsertionsLengthChangeBuilder> {
BuiltList<Insertion> get insertions;

BuiltList<Domain> get domains;

int get length;

bool get all_helices;

/************************ begin BuiltValue boilerplate ************************/
factory InsertionsLengthChange({Iterable<Insertion> insertions, Iterable<Domain> domains, int length}) {
return InsertionsLengthChange.from((b) => b
..insertions.replace(insertions)
..domains.replace(domains)
..length = length
..all_helices = false);
}

factory InsertionsLengthChange.from([void Function(InsertionsLengthChangeBuilder) updates]) =
_$InsertionsLengthChange;

InsertionsLengthChange._();

static Serializer<InsertionsLengthChange> get serializer => _$insertionsLengthChangeSerializer;
}

abstract class DeletionAdd
with BuiltJsonSerializable, UndoableAction
implements InsertionOrDeletionAction, Built<DeletionAdd, DeletionAddBuilder> {
Domain get domain;

int get offset;

bool get all_helices;

StrandPart get strand_part => domain;

DeletionAdd clone_for_adjacent_substrand(Domain other_domain) =>
DeletionAdd(domain: other_domain, offset: offset);
DeletionAdd clone_for_other_domain(Domain domain) => rebuild((b) => b..domain.replace(domain));

/************************ begin BuiltValue boilerplate ************************/
factory DeletionAdd({Domain domain, int offset}) = _$DeletionAdd._;
factory DeletionAdd({Domain domain, int offset, bool all_helices}) = _$DeletionAdd._;

DeletionAdd._();

Expand All @@ -2130,17 +2223,26 @@ abstract class InsertionRemove

Insertion get insertion;

bool get all_helices;

int get offset => insertion.offset;

StrandPart get strand_part => domain;

InsertionRemove clone_for_adjacent_substrand(Domain other_domain) => InsertionRemove(
InsertionRemove clone_for_other_domain(Domain other_domain) => InsertionRemove(
domain: other_domain,
insertion: other_domain.insertions.firstWhere((i) => i.offset == offset),
);

/************************ begin BuiltValue boilerplate ************************/
factory InsertionRemove({Domain domain, Insertion insertion}) = _$InsertionRemove._;
factory InsertionRemove({Domain domain, Insertion insertion}) {
return InsertionRemove.from((b) => b
..domain.replace(domain)
..insertion.replace(insertion)
..all_helices = false);
}

factory InsertionRemove.from([void Function(InsertionRemoveBuilder) updates]) = _$InsertionRemove;

InsertionRemove._();

Expand All @@ -2154,13 +2256,24 @@ abstract class DeletionRemove

int get offset;

bool get all_helices;

StrandPart get strand_part => domain;

DeletionRemove clone_for_adjacent_substrand(Domain other_domain) =>
DeletionRemove clone_for_other_domain(Domain other_domain) =>
DeletionRemove(domain: other_domain, offset: offset);

/************************ begin BuiltValue boilerplate ************************/
factory DeletionRemove({Domain domain, int offset}) = _$DeletionRemove._;
factory DeletionRemove({Domain domain, int offset}) {
return DeletionRemove.from((b) => b
..domain.replace(domain)
..offset = offset
..all_helices = false);
}

factory DeletionRemove.from([void Function(DeletionRemoveBuilder) updates]) = _$DeletionRemove;

// factory DeletionRemove({Domain domain, int offset}) = _$DeletionRemove._;

DeletionRemove._();

Expand Down Expand Up @@ -2362,7 +2475,9 @@ abstract class ContextMenuHide
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// strand color picker

abstract class StrandColorPickerShow with BuiltJsonSerializable implements Action, Built<StrandColorPickerShow, StrandColorPickerShowBuilder> {
abstract class StrandColorPickerShow
with BuiltJsonSerializable
implements Action, Built<StrandColorPickerShow, StrandColorPickerShowBuilder> {
Strand get strand;

/************************ begin BuiltValue boilerplate ************************/
Expand All @@ -2373,10 +2488,14 @@ abstract class StrandColorPickerShow with BuiltJsonSerializable implements Actio
static Serializer<StrandColorPickerShow> get serializer => _$strandColorPickerShowSerializer;
}

abstract class StrandColorPickerHide with BuiltJsonSerializable implements Action, Built<StrandColorPickerHide, StrandColorPickerHideBuilder> {
abstract class StrandColorPickerHide
with BuiltJsonSerializable
implements Action, Built<StrandColorPickerHide, StrandColorPickerHideBuilder> {
/************************ begin BuiltValue boilerplate ************************/
factory StrandColorPickerHide() => StrandColorPickerHide.from((b) => b);
factory StrandColorPickerHide.from([void Function(StrandColorPickerHideBuilder) updates]) = _$StrandColorPickerHide;

factory StrandColorPickerHide.from([void Function(StrandColorPickerHideBuilder) updates]) =
_$StrandColorPickerHide;

StrandColorPickerHide._();

Expand Down
2 changes: 2 additions & 0 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ const css_selector_helix__mouseover_invisible_rectangle = 'helix-mouseover';

const css_selector_insertion = 'insertion-curve';
const css_selector_deletion = 'deletion-cross';
const css_selector_insertion_group = 'insertion-group';
const css_selector_deletion_group = 'deletion-group';
const css_selector_selected = 'selected';


4 changes: 2 additions & 2 deletions lib/src/middleware/all_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'helix_grid_change.dart';
import 'helix_hide_all.dart';
import 'helix_idxs_change.dart';
import 'helix_offsets_change.dart';
import 'insertion_deletion_pairing.dart';
import 'insertion_deletion_batching.dart';
import 'load_file.dart';
import 'periodic_save_design_local_storage.dart';
import 'reselect_moved_dna_ends.dart';
Expand Down Expand Up @@ -55,7 +55,7 @@ final all_middleware = List<Middleware<AppState>>.unmodifiable([
reselect_moved_dna_ends_middleware,
reselect_moved_strands_middleware,
selections_intersect_box_compute_middleware,
insertion_deletion_pairing_middleware,
insertion_deletion_batching_middleware,
adjust_grid_position_middleware,
invalidate_png_middleware,
check_reflect_strands_legal_middleware,
Expand Down
Loading

0 comments on commit 2066621

Please sign in to comment.