Skip to content

Commit

Permalink
Merge pull request #995 from UC-Davis-molecular-computing/993-allow-p…
Browse files Browse the repository at this point in the history
…er-helixhelix-group-geometry

993 allow per helixhelix group geometry
  • Loading branch information
dave-doty authored Sep 21, 2024
2 parents e0b988a + db4de3e commit b210124
Show file tree
Hide file tree
Showing 62 changed files with 689 additions and 394 deletions.
33 changes: 28 additions & 5 deletions lib/src/actions/actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,25 @@ abstract class GeometrySet
String short_description() => "set geometric parameters";
}

abstract class GeometryHelixGroupSet
with BuiltJsonSerializable, UndoableAction
implements Action, Built<GeometryHelixGroupSet, GeometryHelixGroupSetBuilder> {
String get group_name;

Geometry get geometry;

/************************ begin BuiltValue boilerplate ************************/
factory GeometryHelixGroupSet({required String group_name, required Geometry geometry}) =
_$GeometryHelixGroupSet._;

GeometryHelixGroupSet._();

static Serializer<GeometryHelixGroupSet> get serializer => _$geometryHelixGroupSetSerializer;

@override
String short_description() => "set helix group geometric parameters ";
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Selectables

Expand Down Expand Up @@ -2953,12 +2972,16 @@ abstract class DNAEndsMoveSetSelectedEnds

BuiltSet<Strand> get strands_affected;

Geometry get geometry;

/************************ begin BuiltValue boilerplate ************************/
factory DNAEndsMoveSetSelectedEnds(
{required BuiltList<DNAEndMove> moves,
required int original_offset,
required Helix helix,
required BuiltSet<Strand> strands_affected}) = _$DNAEndsMoveSetSelectedEnds._;
factory DNAEndsMoveSetSelectedEnds({
required BuiltList<DNAEndMove> moves,
required int original_offset,
required Helix helix,
required BuiltSet<Strand> strands_affected,
required Geometry geometry,
}) = _$DNAEndsMoveSetSelectedEnds._;

DNAEndsMoveSetSelectedEnds._();

Expand Down
5 changes: 4 additions & 1 deletion lib/src/middleware/dna_ends_move_start.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ dna_ends_move_start_middleware(Store<AppState> store, action, NextDispatcher nex
strands_affected.add(strand);
}
next(action);
var group = design.groups[action.helix.group]!;
var geometry = group.geometry ?? design.geometry;

// important that we dispatch to app, not to store, because the app dispatch will know to route this
// to the appropriate optimized store for moving DNAEnds
app.dispatch(actions.DNAEndsMoveSetSelectedEnds(
original_offset: action.offset,
moves: moves.toBuiltList(),
helix: action.helix,
strands_affected: strands_affected.toBuiltSet()));
strands_affected: strands_affected.toBuiltSet(),
geometry: geometry));
} else {
next(action);
}
Expand Down
17 changes: 11 additions & 6 deletions lib/src/middleware/dna_extensions_move_start.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ dna_extensions_move_start_middleware(Store<AppState> store, action, NextDispatch
for (var end in selected_ends) {
var extension = design.end_to_extension[end]!;
var helix = design.helices[extension.adjacent_domain.helix]!;
var group = design.groups[helix.group]!;
var geometry = group.geometry ?? design.geometry;

var extension_start_point = util.compute_extension_attached_end_svg(
extension,
extension.adjacent_domain,
helix,
store.state.helix_idx_to_svg_position_map[extension.adjacent_domain.helix]!.y);
extension,
extension.adjacent_domain,
helix,
store.state.helix_idx_to_svg_position_map[extension.adjacent_domain.helix]!.y,
geometry,
);

// extension_start_point is in helix group coordinate space, so add it with helix group position
// to get canvas coordinate space
extension_start_point += design.groups[helix.group]!.translation(design.geometry);
extension_start_point += group.translation(geometry);

var extension_end_point = util.compute_extension_free_end_svg(
extension_start_point, extension, extension.adjacent_domain, design.geometry);
extension_start_point, extension, extension.adjacent_domain, geometry);
var color = design.extension_end_to_strand(end).color;
var move = DNAExtensionMove(
dna_end: end,
Expand Down
20 changes: 10 additions & 10 deletions lib/src/middleware/helices_positions_set_based_on_crossovers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ List<actions.UndoableAction> get_helix_position_and_roll_actions(AppState state)
for (var group_name in state.design.groups.keys) {
if (group_names_to_skip.contains(group_name)) continue;
var group = state.design.groups[group_name]!;
var geometry = group.geometry ?? state.design.geometry;
List<Helix> helices = _get_helices_to_process(state, group);
List<Tuple2<Address, Address>>? addresses = _get_addresses_to_process(state, helices);
if (addresses == null) {
continue;
}
double first_roll = helices[0].roll;
List<RollXY> rolls_and_positions =
_calculate_rolls_and_positions(state.design, helices, addresses, first_roll);
var all_actions_this_group = set_rolls_and_positions(helices, rolls_and_positions);
_calculate_rolls_and_positions(state.design, geometry, helices, addresses, first_roll);
var all_actions_this_group = set_rolls_and_positions(helices, rolls_and_positions, geometry);
all_actions.addAll(all_actions_this_group);
}

Expand Down Expand Up @@ -277,14 +278,12 @@ class RollXY {
/// Return list of rolls, same length as [helices], giving the roll that each should be to point
/// each pair of helix backbones at each other through the given [addresses].
/// The first roll is [first_roll].
List<RollXY> _calculate_rolls_and_positions(
Design design, List<Helix> helices, List<Tuple2<Address, Address>> addresses, double first_roll) {
List<RollXY> _calculate_rolls_and_positions(Design design, Geometry geometry, List<Helix> helices,
List<Tuple2<Address, Address>> addresses, double first_roll) {
assert(helices.length == addresses.length + 1);

Geometry geometry = design.geometry;

double x = helices[0].position3d.z;
double y = helices[0].position3d.y;
double x = helices[0].position3d(geometry).z;
double y = helices[0].position3d(geometry).y;
List<RollXY> rollxys = [RollXY(roll: first_roll, x: x, y: y)];

for (int i = 0; i < addresses.length; i++) {
Expand Down Expand Up @@ -323,13 +322,14 @@ List<RollXY> _calculate_rolls_and_positions(
return rollxys;
}

List<actions.UndoableAction> set_rolls_and_positions(List<Helix> helices, List<RollXY> rolls_and_positions) {
List<actions.UndoableAction> set_rolls_and_positions(
List<Helix> helices, List<RollXY> rolls_and_positions, Geometry geometry) {
List<actions.UndoableAction> all_actions = [];
for (int i = 0; i < helices.length; i++) {
var helix = helices[i];
var rollxy = rolls_and_positions[i];
var roll_action = actions.HelixRollSet(helix_idx: helix.idx, roll: rollxy.roll);
var position = Position3D(x: rollxy.x, y: rollxy.y, z: helix.position3d.z);
var position = Position3D(x: rollxy.x, y: rollxy.y, z: helix.position3d(geometry).z);
var pos_action = actions.HelixPositionSet(helix_idx: helix.idx, position: position);
all_actions.add(roll_action);
all_actions.add(pos_action);
Expand Down
9 changes: 5 additions & 4 deletions lib/src/middleware/helix_grid_change.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ helix_grid_offsets_middleware(Store<AppState> store, dynamic action, NextDispatc
if (action is actions.GridChange &&
!action.grid.is_none &&
store.state.design.groups[action.group_name]!.grid.is_none) {
Geometry geometry = store.state.design.geometry;
var group = store.state.design.groups[action.group_name]!;
Geometry geometry = group.geometry ?? store.state.design.geometry;
Map<int, GridPosition> new_grid_positions_map = {
for (var helix in store.state.design.helices_in_group(action.group_name).values)
helix.idx: util.position3d_to_grid_position(helix.position, action.grid, geometry)
helix.idx: util.position3d_to_grid_position(helix.position(geometry), action.grid, geometry)
};
Set<GridPosition> new_grid_positions_set = Set<GridPosition>.from(new_grid_positions_map.values);
// if lengths don't match, there's a duplicate grid position
Expand All @@ -32,8 +33,8 @@ helix_grid_offsets_middleware(Store<AppState> store, dynamic action, NextDispatc
var gp2 = new_grid_positions_map[h2idx]!;
if (gp1 == gp2) {
var helices = store.state.design.helices;
var pos1 = helices[h1idx]!.position3d;
var pos2 = helices[h2idx]!.position3d;
var pos1 = helices[h1idx]!.position3d(geometry);
var pos2 = helices[h2idx]!.position3d(geometry);
var msg = '''\
This design cannot be automatically converted to the ${action.grid.name} grid.
Two helices, with idx values ${h1idx} and ${h2idx}, have positions that are
Expand Down
2 changes: 2 additions & 0 deletions lib/src/middleware/helix_group_move_start.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ helix_group_move_start_middleware(Store<AppState> store, action, NextDispatcher

if (helices_in_group.isNotEmpty) {
next(action); // this lets the boolean be set that we are moving a helix group
var geometry = group.geometry ?? state.design.geometry;
var helix_group_move = HelixGroupMove(
group_name: group_name,
group: group,
helices: helices_in_group,
original_mouse_point: action.mouse_point,
geometry: geometry,
);
// important that we dispatch to app, not to store, because the app dispatch will know to route this
// to the appropriate optimized store for moving HelixGroup
Expand Down
23 changes: 16 additions & 7 deletions lib/src/middleware/oxdna_export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ Tuple3<OxdnaVector, OxdnaVector, OxdnaVector> oxdna_get_helix_vectors(Design des
*/
var group = design.groups[helix.group]!;
var grid = group.grid;
var geometry = design.geometry;
var geometry = group.geometry ?? design.geometry;

// var forward = OxdnaVector(0, 0, 1);
// var normal = OxdnaVector(0, -1, 0);
Expand Down Expand Up @@ -465,7 +465,7 @@ Tuple3<OxdnaVector, OxdnaVector, OxdnaVector> oxdna_get_helix_vectors(Design des
if (grid == Grid.none) {
// unnecessary since this check is done in the position getter, but this way the code exactly mirrors
// the Python package equivalent
position = helix.position;
position = helix.position(geometry);
} else {
position = util.grid_position_to_position3d(helix.grid_position!, grid, geometry);
}
Expand All @@ -482,8 +482,6 @@ OxdnaSystem convert_design_to_oxdna_system(Design design, [List<Strand>? strands
}

var system = OxdnaSystem();
var geometry = design.geometry;
var step_rot = -360 / geometry.bases_per_turn;

// each entry is the number of insertions - deletions since the start of a given helix
Map<int, List<int>> mod_map = {};
Expand Down Expand Up @@ -533,6 +531,9 @@ OxdnaSystem convert_design_to_oxdna_system(Design design, [List<Strand>? strands
// handle normal domains
if (domain is Domain) {
var helix = design.helices[domain.helix]!;
var group = design.groups[helix.group]!;
var geometry = group.geometry ?? design.geometry;
var step_rot = -360 / geometry.bases_per_turn;
var origin_forward_normal = helix_vectors[helix.idx]!;
var origin = origin_forward_normal.item1;
var forward = origin_forward_normal.item2;
Expand All @@ -554,7 +555,7 @@ OxdnaSystem convert_design_to_oxdna_system(Design design, [List<Strand>? strands
insertions[insertion.offset] = insertion.length;
}

// use Design.geometry field to figure out various distances
// use Design.geometry or HelixGroup.geometry field to figure out various distances
// https://github.com/UC-Davis-molecular-computing/scadnano/blob/master/lib/src/state/geometry.dart

// index is used for finding the base in our sequence
Expand Down Expand Up @@ -608,8 +609,16 @@ OxdnaSystem convert_design_to_oxdna_system(Design design, [List<Strand>? strands
strand_domains.add(Tuple2<OxdnaStrand, bool>(ox_strand, true));
} else if (domain is Extension) {
bool is_5p = ss_idx == 0;
var helix = design.helices[domain.adjacent_domain.helix]!;
var group = design.groups[helix.group]!;
var geometry = group.geometry ?? design.geometry;
var nucleotides = _compute_extension_nucleotides(
design: design, strand: strand, is_5p: is_5p, helix_vectors: helix_vectors, mod_map: mod_map);
design: design,
geometry: geometry,
strand: strand,
is_5p: is_5p,
helix_vectors: helix_vectors,
mod_map: mod_map);
ox_strand.nucleotides.addAll(nucleotides);
strand_domains.add(Tuple2<OxdnaStrand, bool>(ox_strand, false));
} else {
Expand Down Expand Up @@ -651,11 +660,11 @@ OxdnaSystem convert_design_to_oxdna_system(Design design, [List<Strand>? strands

List<OxdnaNucleotide> _compute_extension_nucleotides(
{required Design design,
required Geometry geometry,
required Strand strand,
required bool is_5p,
required Map<int, Tuple3<OxdnaVector, OxdnaVector, OxdnaVector>> helix_vectors,
required Map<int, List<int>> mod_map}) {
var geometry = design.geometry;
var step_rot = -360 / geometry.bases_per_turn;

var adj_dom = is_5p ? strand.domains.first : strand.domains.last;
Expand Down
3 changes: 0 additions & 3 deletions lib/src/reducers/design_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ Design? design_geometry_set_reducer(Design? design, AppState state, actions.Geom
return null;
}
var new_helices = design.helices.toMap();
for (var key in new_helices.keys) {
new_helices[key] = new_helices[key]!.rebuild((b) => b..geometry.replace(action.geometry));
}

return design.rebuild((b) => b
..helices.replace(new_helices)
Expand Down
3 changes: 2 additions & 1 deletion lib/src/reducers/dna_ends_move_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ DNAEndsMove? dna_ends_move_set_selected_ends_reducer(
moves: action.moves,
original_offset: action.original_offset,
current_offset: action.original_offset,
helix: action.helix);
helix: action.helix,
geometry: action.geometry);

DNAEndsMove? dna_ends_move_adjust_reducer(DNAEndsMove? move, actions.DNAEndsMoveAdjustOffset action) =>
move?.rebuild((b) => b..current_offset = action.offset);
Expand Down
10 changes: 10 additions & 0 deletions lib/src/reducers/groups_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Reducer<BuiltMap<String, HelixGroup>> groups_local_reducer = combineReducers([
TypedReducer<BuiltMap<String, HelixGroup>, actions.GroupRemove>(group_remove_reducer),
TypedReducer<BuiltMap<String, HelixGroup>, actions.GroupChange>(group_change_reducer),
TypedReducer<BuiltMap<String, HelixGroup>, actions.GridChange>(grid_change_reducer),
TypedReducer<BuiltMap<String, HelixGroup>, actions.GeometryHelixGroupSet>(geometry_helix_group_set_reducer),
]);

GlobalReducer<BuiltMap<String, HelixGroup>, AppState> groups_global_reducer = combineGlobalReducers([
Expand All @@ -29,6 +30,15 @@ BuiltMap<String, HelixGroup> grid_change_reducer(
return group;
});

BuiltMap<String, HelixGroup> geometry_helix_group_set_reducer(
BuiltMap<String, HelixGroup> groups, actions.GeometryHelixGroupSet action) =>
groups.map_values((name, group) {
if (name == action.group_name) {
group = group.rebuild((b) => b..geometry.replace(action.geometry));
}
return group;
});

BuiltMap<String, HelixGroup> group_add_reducer(
BuiltMap<String, HelixGroup> groups, actions.GroupAdd action) =>
groups.rebuild((b) {
Expand Down
16 changes: 10 additions & 6 deletions lib/src/reducers/helices_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ GlobalReducer<BuiltMap<int, Helix>, AppState> helices_global_reducer = combineGl
helix_offset_change_all_while_creating_strand_reducer),
TypedGlobalReducer<BuiltMap<int, Helix>, AppState, actions.ReplaceStrands>(first_replace_strands_reducer),
TypedGlobalReducer<BuiltMap<int, Helix>, AppState, actions.SelectionsClear>(
reset_helices_offsets_after_selections_clear)
reset_helices_offsets_after_selections_clear),
]);

BuiltMap<int, Helix> helix_individual_reducer(
Expand Down Expand Up @@ -473,8 +473,9 @@ BuiltMap<int, Helix> helix_roll_set_at_other_reducer(
BuiltMap<int, Helix> helices, AppState state, actions.HelixRollSetAtOther action) {
Helix helix = helices[action.helix_idx]!;
Helix helix_other = helices[action.helix_other_idx]!;
var group = state.design.groups[helix.group]!;

var geometry = state.design.geometry;
var geometry = group.geometry ?? state.design.geometry;
num rotation = util.rotation_between_helices(helix, helix_other, action.forward, geometry);
double old_rotation_at_anchor = state.design.helix_rotation_forward(helix.idx, action.anchor);
double delta_roll = rotation - old_rotation_at_anchor;
Expand Down Expand Up @@ -526,7 +527,6 @@ Design? helix_add_design_reducer(Design? design, AppState state, actions.HelixAd
idx: new_idx,
grid: group.grid,
group: state.ui_state.displayed_group_name,
geometry: design.geometry,
grid_position: action.grid_position,
position: action.position,
min_offset: min_offset,
Expand Down Expand Up @@ -631,7 +631,8 @@ BuiltMap<int, Helix> helix_grid_change_reducer(
BuiltMap<int, Helix> helices, AppState state, actions.GridChange action) {
// make builder of all helices
Map<int, Helix> new_helices = helices.toMap();
Geometry geometry = state.design.geometry;
var group = state.design.groups[action.group_name]!;
Geometry geometry = group.geometry ?? state.design.geometry;

// process only those in this group
var helix_idxs_in_group = state.design.helix_idxs_in_group[action.group_name]!;
Expand All @@ -641,7 +642,7 @@ BuiltMap<int, Helix> helix_grid_change_reducer(
helix_builder.grid = action.grid;
if (!action.grid.is_none && helix.grid_position == null) {
helix_builder.grid_position =
util.position3d_to_grid_position(helix.position, action.grid, geometry).toBuilder();
util.position3d_to_grid_position(helix.position(geometry), action.grid, geometry).toBuilder();
helix_builder.position_ = null;
}
if (action.grid.is_none && helix.position_ == null) {
Expand All @@ -665,9 +666,11 @@ BuiltMap<int, Helix> relax_helix_rolls_reducer(
var new_helices_map = helices.toMap();
for (var helix_idx in helix_idxs_to_relax) {
var helix = new_helices_map[helix_idx]!;
var group = state.design.groups[helix.group]!;
var geometry = group.geometry ?? state.design.geometry;
var crossover_addresses =
state.design.helix_to_crossover_addresses_disallow_intrahelix_disallow_intergroup[helix_idx]!;
var helix_relaxed = helix.relax_roll(helices, crossover_addresses);
var helix_relaxed = helix.relax_roll(helices, crossover_addresses, geometry);
new_helices_map[helix_idx] = helix_relaxed;
}

Expand Down Expand Up @@ -726,6 +729,7 @@ BuiltMap<int, Helix> helix_position_set_reducer(
// groups_reducer.dart file, which processes this same Action on the groups map.
BuiltMap<int, Helix> move_helices_to_group_helices_reducer(
BuiltMap<int, Helix> helices, actions.MoveHelicesToGroup action) {
//TODO: make this global and set helix.geometry if new group geometry is not null
var helices_map = helices.toMap();
for (int idx in action.helix_idxs) {
assert(helices_map.keys.contains(idx));
Expand Down
3 changes: 2 additions & 1 deletion lib/src/reducers/mouseover_datas_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ BuiltList<MouseoverData> helix_rotation_set_at_other_mouseover_reducer(
BuiltList<MouseoverData> mouseover_datas, AppState state, actions.HelixRollSetAtOther action) {
Helix helix = state.design.helices[action.helix_idx]!;
Helix helix_other = state.design.helices[action.helix_other_idx]!;
var geometry = state.design.geometry;
var group = state.design.groups[helix.group]!;
var geometry = group.geometry ?? state.design.geometry;
double rotation = util.rotation_between_helices(helix, helix_other, action.forward, geometry);
return _update_mouseover_datas_with_helix_rotation(
model: state,
Expand Down
Loading

0 comments on commit b210124

Please sign in to comment.