Skip to content

Commit

Permalink
closes #993 allow per-helix-group geometry
Browse files Browse the repository at this point in the history
  • Loading branch information
dave-doty committed Sep 18, 2024
1 parent 995263b commit bdbec35
Show file tree
Hide file tree
Showing 25 changed files with 512 additions and 302 deletions.
500 changes: 271 additions & 229 deletions lib/src/actions/actions.dart

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions lib/src/middleware/dna_extensions_move_start.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ dna_extensions_move_start_middleware(Store<AppState> store, action, NextDispatch
helix,
store.state.helix_idx_to_svg_position_map[extension.adjacent_domain.helix]!.y);

var group = design.groups[helix.group]!;
var geometry = group.geometry ?? design.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
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ 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);
_calculate_rolls_and_positions(state.design, geometry, helices, addresses, first_roll);
var all_actions_this_group = set_rolls_and_positions(helices, rolls_and_positions);
all_actions.addAll(all_actions_this_group);
}
Expand Down Expand Up @@ -277,12 +278,10 @@ 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;
List<RollXY> rollxys = [RollXY(roll: first_roll, x: x, y: y)];
Expand Down
3 changes: 2 additions & 1 deletion lib/src/middleware/helix_grid_change.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ 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)
Expand Down
21 changes: 15 additions & 6 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 @@ -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
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
35 changes: 29 additions & 6 deletions lib/src/reducers/helices_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ 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),
TypedGlobalReducer<BuiltMap<int, Helix>, AppState, actions.GeometryHelixGroupSet>(
helix_geometry_helix_group_set_reducer),
]);

BuiltMap<int, Helix> helix_individual_reducer(
Expand All @@ -79,6 +81,18 @@ BuiltMap<int, Helix> helix_individual_reducer(
}
}

BuiltMap<int, Helix> helix_geometry_helix_group_set_reducer(
BuiltMap<int, Helix> helices, AppState state, actions.GeometryHelixGroupSet action) {
var helices_new = helices.toMap();
for (int helix_idx in helices.keys) {
var helix = helices[helix_idx]!;
var group = state.design.groups[helix.group]!;
var geometry = group.geometry ?? state.design.geometry;
helices_new[helix_idx] = helix.rebuild((b) => b..geometry.replace(geometry));
}
return helices_new.build();
}

GlobalReducer<Helix, AppState> _helix_individual_reducers = combineGlobalReducers([
TypedGlobalReducer<Helix, AppState, actions.HelixOffsetChange>(helix_offset_change_reducer),
TypedGlobalReducer<Helix, AppState, actions.HelixMinOffsetSetByDomains>(
Expand Down Expand Up @@ -473,8 +487,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 @@ -521,12 +536,13 @@ Design? helix_add_design_reducer(Design? design, AppState state, actions.HelixAd
var new_group = group.rebuild((b) => b..helices_view_order.replace(new_helices_view_order));
var new_groups = design.groups.toMap();
new_groups[state.ui_state.displayed_group_name] = new_group;
var geometry = new_group.geometry ?? design.geometry;

Helix helix = Helix(
idx: new_idx,
grid: group.grid,
group: state.ui_state.displayed_group_name,
geometry: design.geometry,
geometry: geometry,
grid_position: action.grid_position,
position: action.position,
min_offset: min_offset,
Expand Down Expand Up @@ -631,7 +647,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 Down Expand Up @@ -676,8 +693,13 @@ BuiltMap<int, Helix> relax_helix_rolls_reducer(

BuiltMap<int, Helix> helix_group_change_reducer(
BuiltMap<int, Helix> helices, AppState state, actions.GroupChange action) {
return helices.map_values((idx, helix) =>
helix.group == action.old_name ? helix.rebuild((b) => b..group = action.new_name) : helix);
var new_group = state.design.groups[action.new_name]!;
Geometry new_geometry = new_group.geometry ?? state.design.geometry;
return helices.map_values((idx, helix) => helix.group == action.old_name
? helix.rebuild((b) => b
..group = action.new_name
..geometry.replace(new_geometry))
: helix);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -726,6 +748,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
6 changes: 5 additions & 1 deletion lib/src/reducers/strands_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,17 @@ BuiltList<Strand> strands_dna_extensions_move_commit_reducer(
int substrand_idx = strand.substrands.indexOf(extension);
var substrands_builder = strand.substrands.toBuilder();
var extension_start_point = move.attached_end_position;
var adjacent_domain = extension.adjacent_domain;
var helix = state.design.helices[adjacent_domain.helix]!;
var group = state.design.groups[helix.group]!;
var geometry = group.geometry ?? state.design.geometry;

var length_and_angle = util.compute_extension_length_and_angle_from_point(
action.dna_extensions_move.current_point_of(move.dna_end)!,
extension_start_point,
extension,
extension.adjacent_domain,
state.design.geometry);
geometry);

Extension ext_new = extension.rebuild((b) => b
..display_length = length_and_angle.item1
Expand Down
1 change: 1 addition & 0 deletions lib/src/serializers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ part 'serializers.g.dart';
EditModeToggle,
EditModesSet,
GeometrySet,
GeometryHelixGroupSet,
HelixAdd,
HelixRemove,
HelixRemoveAllSelected,
Expand Down
6 changes: 3 additions & 3 deletions lib/src/state/app_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ in the app. Thank you!""");
}

BuiltMap<int, Point<double>> ret = util
.helices_assign_svg(design.geometry, ui_state.invert_y, design.helices, design.groups,
helix_idxs_to_calculate: helix_idxs_to_calculate)
.helices_assign_svg(design, ui_state.invert_y, design.helices, design.groups,
helix_idxs_to_calculate: helix_idxs_to_calculate)
.build();

// print('${sw.elapsedMicroseconds} microseconds to calculate helix_idx_to_svg_position_map');
Expand All @@ -95,7 +95,7 @@ in the app. Thank you!""");
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {};
var design_to_store =
this.maybe_design != null ? design.to_json_serializable(suppress_indent: false) : null;
this.maybe_design != null ? design.to_json_serializable(suppress_indent: false) : null;
map['design'] = design_to_store;
map['ui_state'] = ui_state.toJson();
map['error_message'] = error_message;
Expand Down
5 changes: 4 additions & 1 deletion lib/src/state/design.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ abstract class Design with UnusedFields implements Built<Design, DesignBuilder>,
Map<int, Helix> helices_map = {for (var helix in helices) helix.idx: helix};

for (var key in helices_map.keys) {
helices_map[key] = helices_map[key]!.rebuild((b) => b..geometry.replace(geometry!));
var helix = helices_map[key]!;
var group = groups[helix.group]!;
Geometry helix_geometry = group.geometry ?? geometry;
helices_map[key] = helix.rebuild((b) => b..geometry.replace(helix_geometry));
}

var design = Design.from((b) => b
Expand Down
4 changes: 3 additions & 1 deletion lib/src/state/design_side_rotation_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ abstract class DesignSideRotationData
break;
}
}
double minor_groove_angle = design.geometry.minor_groove_angle;
var group = design.groups[helix.group]!;
var geometry = group.geometry ?? design.geometry;
double minor_groove_angle = geometry.minor_groove_angle;
design_side_rotation_datas_builder.add(DesignSideRotationData(
helix, offset, color_forward, color_reverse, roll_forward, minor_groove_angle));
}
Expand Down
33 changes: 26 additions & 7 deletions lib/src/state/group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ abstract class HelixGroup with BuiltJsonSerializable implements Built<HelixGroup

double get roll;

Geometry? get geometry;

static void _initializeBuilder(HelixGroupBuilder b) {
b.grid = Grid.none;
b.position = Position3D.origin.toBuilder();
b.pitch = 0;
b.yaw = 0;
b.roll = 0;
b.helices_view_order = ListBuilder<int>();
b.geometry = null;
}

factory HelixGroup({
Expand All @@ -47,6 +50,7 @@ abstract class HelixGroup with BuiltJsonSerializable implements Built<HelixGroup
double pitch = 0,
double yaw = 0,
double roll = 0,
Geometry? geometry = null,
}) {
if (position == null) {
position = Position3D.origin;
Expand All @@ -57,7 +61,8 @@ abstract class HelixGroup with BuiltJsonSerializable implements Built<HelixGroup
..position.replace(position!)
..pitch = pitch
..yaw = yaw
..roll = roll);
..roll = roll
..geometry = geometry?.toBuilder());
}

Map<String, dynamic> to_json_serializable({
Expand Down Expand Up @@ -88,6 +93,11 @@ abstract class HelixGroup with BuiltJsonSerializable implements Built<HelixGroup
suppress_indent ? NoIndent(helices_view_order_to_write) : helices_view_order_to_write;
}

if (this.geometry != null) {
json_map[constants.geometry_key] =
this.geometry!.to_json_serializable(suppress_indent: suppress_indent);
}

return json_map;
}

Expand Down Expand Up @@ -123,13 +133,22 @@ abstract class HelixGroup with BuiltJsonSerializable implements Built<HelixGroup
double roll = util.optional_field(json_map, constants.roll_key, constants.default_roll);
double yaw = util.optional_field(json_map, constants.yaw_key, constants.default_yaw);

Map<String, dynamic>? geometry_map =
util.optional_field_with_null_default(json_map, constants.geometry_key);
Geometry? geometry = null;
if (geometry_map != null) {
geometry = Geometry.from_json(geometry_map);
}

return HelixGroup(
position: position,
pitch: pitch,
yaw: yaw,
roll: roll,
helices_view_order: helices_view_order,
grid: grid);
position: position,
pitch: pitch,
yaw: yaw,
roll: roll,
helices_view_order: helices_view_order,
grid: grid,
geometry: geometry,
);
}

/// Returns a map mapping helix indices to their view order.
Expand Down
Loading

0 comments on commit bdbec35

Please sign in to comment.