UC-Davis-molecular-computing / scadnano

Web application for designing DNA structures such as DNA origami.
https://scadnano.org
MIT License
22 stars 13 forks source link

export base pair lines in *Export-->SVG of selected strands* and *Edit-->Copy image* #895

Closed dave-doty closed 6 months ago

dave-doty commented 1 year ago

Currently, if base pair lines are shown via View-->Base pairs-->Base pair lines:

image

Then selecting strands and picking Export-->SVG of selected strands (or Edit-->Copy image) should export the image of the base pair lines. However, it currently does not:

image

Implement this with the following rule: if two strands shared a bound domain and are both selected for export, then export the base pair lines between them. But don't export any base pair lines that are attached to at most one (or zero) selected strands.

dave-doty commented 1 year ago

Ideally we won't change the view code for this. Right now, it's fairly fast, even in a large design, to turn base pair lines on and off, because they are in a self-contained SVG g element separate from the strands, and the React component for them is simply not displayed if they are off. In other words, the strand view components do not need to be re-rendered when base pair lines are turned on or off.

It would be easier to implement this feature if base pair lines were associated to particular strands (for instance, to break ties, associated to the strand that has the forward domain). However, this would require re-writing a lot of view code, and it would then be slower to enable/disable base pair lines in a large design.

So if there's a way to do this rendering without changing the view code, that's ideal. But this will require ugly logic since it will be challenging to find and keep only those base pair lines associated to selected strands. Perhaps we can add an HTML attribute to each SVG line object that identifies which strands (and possibly which domains) it connects, which would make that logic easier. I say perhaps we want the domain too, because this needs to properly account for exporting when some helices are hidden, which would mean we might not export some base pair lines even though they connect strands that are selected.

I think that some similar logic is already happened to export the DNA sequences of only selected strands, even though the DNA sequences view is similarly stored in its own React component separate from the strands view. So imitating that logic should work here.

dave-doty commented 1 year ago

For reference, I said this verbally to Aaron but I'm recording it here.

Let's re-write the view code in the following way.

First, write a new memoized getting for the class Design in state/design.dart that is similar to the current getter called Design.base_pairs, which returns a BuiltMap<int, BuiltList<int>> mapping each Helix idx to a list of offsets on the Helix where there is a base pair (i.e., where that offset has both a forward and reverse strand, without a deletion on either strand).

The new getter, called base_pairs_by_domain, would be of type BuiltMap<int, BuiltMap<Tuple2<Domain, Domain>, BuiltList<int>>>. It maps each Helix idx to a BuiltMap<Tuple2<Domain, Domain>, BuiltList<int>>, where that maps each pair of Domains bound on the Helix to a list of the offsets where they are bound. For example, the following helix

          1         2
012345678901234567890123456789
 a    b      c       d
[--> [--> [-----> [--x------>
 ||| |||| ||||     || || ||||
 <-----]<----]     <-x-] <---]
    e     f          g     h

would have the following BuiltMap<Tuple2<Domain, Domain>, BuiltList<int>>:

{
  (a,e): [1,2,3],
  (b,e): [5,6,7],
  (b,f): [8],
  (c,f): [10,11,12,13],
  (d,g): [19,20,22,23],
  (d,h): [25,26,27,28]
}

Then, re-write the view code to have one g element for each entry in each of these maps. Associate the Helix idx and pair of domains (using their .id field) to each g element in its own id field. This can be used when exporting SVG to find the SVG g elements representing the base pair lines so that they can be included in the export.

Keep in mind issue #897; when that is implemented, we will similarly want to assign the same id to each SVG rect element representing a pair of domains on a helix, so that they can be exported as well.