Skip to content

Commit

Permalink
Merge pull request #726 from UC-Davis-molecular-computing/#725-moved-…
Browse files Browse the repository at this point in the history
…domains-should-be-reselected-after-move

#722 moving domain should be disallowed if overlapping another domain
  • Loading branch information
dave-doty authored Jan 14, 2022
2 parents cf86bda + 2f2c2d0 commit 42f5755
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 42 deletions.
7 changes: 5 additions & 2 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ import 'actions/actions.dart' as actions;
// global variable for whole program
App app;

const USE_REDUX_DEV_TOOLS = String.fromEnvironment('SCADNANO_PROD') != 'true';
// const USE_REDUX_DEV_TOOLS = false;
const SCADNANO_PROD = String.fromEnvironment('SCADNANO_PROD');
// const USE_REDUX_DEV_TOOLS = SCADNANO_PROD != 'true';
const USE_REDUX_DEV_TOOLS = false;

const RUN_TEST_CODE_INSTEAD_OF_APP = false;
//const RUN_TEST_CODE_INSTEAD_OF_APP = true;
Expand Down Expand Up @@ -103,9 +104,11 @@ class App {
AppState state = DEFAULT_AppState;

if (USE_REDUX_DEV_TOOLS) {
print('SCADNANO_PROD = "${SCADNANO_PROD}", so Redux Devtools enabled; disable it to boost performance');
var middleware_plus = all_middleware + [overReactReduxDevToolsMiddleware];
store = DevToolsStore<AppState>(app_state_reducer, initialState: state, middleware: middleware_plus);
} else {
print('SCADNANO_PROD = "${SCADNANO_PROD}", so Redux Devtools disabled');
store = Store<AppState>(app_state_reducer, initialState: state, middleware: all_middleware);
}

Expand Down
48 changes: 10 additions & 38 deletions lib/src/reducers/domains_move_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../state/app_state.dart';
import '../state/helix.dart';
import '../actions/actions.dart' as actions;
import '../extension_methods.dart';
import 'strands_move_reducer.dart';

GlobalReducer<DomainsMove, AppState> domains_move_global_reducer = combineGlobalReducers([
TypedGlobalReducer<DomainsMove, AppState, actions.DomainsMoveStartSelectedDomains>(
Expand All @@ -29,8 +30,7 @@ DomainsMove domains_move_start_selected_domains_reducer(
Set<Domain> selected_domains =
Set<Domain>.from(state.ui_state.selectables_store.selected_items.where((s) => s is Domain));
Set<Strand> strands_of_selected_domains = {
for (var domain in selected_domains)
state.design.substrand_to_strand[domain]
for (var domain in selected_domains) state.design.substrand_to_strand[domain]
};
return DomainsMove(
domains_moving: selected_domains.toBuiltList(),
Expand All @@ -43,6 +43,10 @@ DomainsMove domains_move_start_selected_domains_reducer(

DomainsMove domains_move_stop_reducer(DomainsMove domains_move, actions.DomainsMoveStop action) => null;

// - in_bounds checks whether the domain is in a legal address given the helices, but allows it to overlap
// other domains
// - is_allowable checks whether the domain overlaps other domains

DomainsMove domains_adjust_address_reducer(
DomainsMove domains_move, AppState state, actions.DomainsMoveAdjustAddress action) {
DomainsMove new_domains_move = domains_move.rebuild((b) => b..current_address.replace(action.address));
Expand Down Expand Up @@ -110,7 +114,6 @@ bool is_allowable(Design design, DomainsMove domains_move) {
int delta_offset = domains_move.delta_offset;
bool delta_forward = domains_move.delta_forward;


// look for moving domains overlapping fixed domains
for (int original_helix_idx in design.helices.keys) {
var domains_moving = domains_move.domains_moving_on_helix[original_helix_idx];
Expand All @@ -135,13 +138,15 @@ bool is_allowable(Design design, DomainsMove domains_move) {
.where((dom) => delta_forward != (dom.forward == forward))
.map((dom) => Point<int>(dom.start + delta_offset, dom.end - 1 + delta_offset))
.toList();
sort_intervals_by_start(intervals_moving);
if (intervals_moving.isNotEmpty) {
if (intervals_moving[0].x < new_helix.min_offset) return false;
if (intervals_moving[intervals_moving.length - 1].y >= new_helix.max_offset) return false;
List<Point<int>> intervals_fixed = domains_fixed
.where((dom) => dom.forward == forward)
.map((dom) => Point<int>(dom.start, dom.end - 1))
.toList();
sort_intervals_by_start(intervals_fixed);
if (intersection(intervals_moving, intervals_fixed)) return false;
}
}
Expand All @@ -150,39 +155,6 @@ bool is_allowable(Design design, DomainsMove domains_move) {
return true;
}

/// Indicate if any of the intervals in ints1 intersect any of the intervals in ints2. Assume each
/// is disjoint within itself (i.e., no two intervals in ints1 intersection, and no two intervals
/// within ints2 intersect). and that each is sorted by start point
/// (thus also by end point by disjointness.)
/// These intervals are INCLUSIVE on both sides.
bool intersection(List<Point<int>> ints1, List<Point<int>> ints2) {
int idx1 = 0;
int idx2 = 0;
while (idx1 < ints1.length && idx2 < ints2.length) {
// step through interavls in ints2 until one is found that might intersect int1
while (idx2 < ints2.length && ints2[idx2].y < ints1[idx1].x) {
idx2++;
}
if (idx2 == ints2.length) {
return false; // if we ran out of intervals in ints2, we're done
} else if (ints2[idx2].x <= ints1[idx1].y) {
return true; // o/w check for intersection
}

// step through interavls in ints1 until one is found that might intersect int2
while (idx1 < ints1.length && ints1[idx1].y < ints2[idx2].x) {
idx1++;
}
if (idx1 == ints1.length) {
return false; // if we ran out of intervals in ints2, we're done
} else if (ints1[idx1].x <= ints2[idx2].y) {
return true; // o/w check for intersection
}
}

return false;
}

Domain move_domain(
{Domain domain,
HelixGroup original_group,
Expand All @@ -197,8 +169,8 @@ Domain move_domain(
assert(new_helix_idx != null);
Domain domain_moved = domain.rebuild(
(b) => b
..is_first = set_first_last_false? false: b.is_first
..is_last = set_first_last_false? false: b.is_last
..is_first = set_first_last_false ? false : b.is_first
..is_last = set_first_last_false ? false : b.is_last
..helix = new_helix_idx
..forward = (delta_forward != domain.forward)
..start = domain.start + delta_offset
Expand Down
16 changes: 15 additions & 1 deletion lib/src/reducers/strands_move_reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ StrandsMove strands_move_start_selected_strands_reducer(

StrandsMove strands_move_stop_reducer(StrandsMove strands_move, actions.StrandsMoveStop action) => null;


// - in_bounds checks whether the strand is in a legal address given the helices, but allows it to overlap
// other strands
// - is_allowable checks whether the strand overlaps other strands

StrandsMove strands_adjust_address_reducer(
StrandsMove strands_move, AppState state, actions.StrandsMoveAdjustAddress action) {
StrandsMove new_strands_move = strands_move.rebuild((b) => b..current_address.replace(action.address));
Expand Down Expand Up @@ -187,22 +192,31 @@ bool is_allowable(Design design, StrandsMove strands_move, {Set<int> original_he
.where((dom) => delta_forward != (dom.forward == forward))
.map((dom) => Point<int>(dom.start + delta_offset, dom.end - 1 + delta_offset))
.toList();
sort_intervals_by_start(intervals_moving);
if (intervals_moving.isNotEmpty) {
if (intervals_moving[0].x < new_helix.min_offset) return false;
if (intervals_moving[intervals_moving.length - 1].y >= new_helix.max_offset) return false;
List<Point<int>> intervals_fixed = domains_fixed
.where((dom) => dom.forward == forward)
.map((dom) => Point<int>(dom.start, dom.end - 1))
.toList();
sort_intervals_by_start(intervals_fixed);
if (intersection(intervals_moving, intervals_fixed)) return false;
}
}
}
return true;
}

int interval_comparator(Point interval1, Point interval2) => interval1.x - interval2.x;

/// Sort intervals in ints by start point (in place).
void sort_intervals_by_start(List<Point<int>> intervals) {
intervals.sort(interval_comparator);
}

/// Indicate if any of the intervals in ints1 intersect any of the intervals in ints2. Assume each
/// is disjoint within itself (i.e., no two intervals in ints1 intersection, and no two intervals
/// is disjoint within itself (i.e., no two intervals in ints1 intersect, and no two intervals
/// within ints2 intersect) and that each is sorted by start point
/// (thus also by end point by disjointness.)
/// These intervals are INCLUSIVE on both sides.
Expand Down
5 changes: 4 additions & 1 deletion lib/src/view/design_main_domain_moving.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ class DesignMainDomainMovingComponent extends UiComponent2<DesignMainDomainMovin
classname += ' ' + constants.css_selector_disallowed;
}

var hex_color = props.color.toHexColor();
var hex_color_css = hex_color.toCssString();

return (Dom.line()
..stroke = props.color.toHexColor().toCssString()
..stroke = hex_color_css
..transform = transform_of_helix(helix.idx)
..x1 = '${start_svg.x}'
..y1 = '${start_svg.y}'
Expand Down

0 comments on commit 42f5755

Please sign in to comment.