-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve Read Cycle Layout #389
Improve Read Cycle Layout #389
Conversation
…generating horizontal space between nodes more accurate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty good, and way better than before.
But I'm concerned that it's specialized for one-node cycles, when probably we need to support cycles of multiple nodes.
Also, what's going on in the first screenshot with the innermost three reads in the spiral below the node? They don't seem to be spiraling in the same order as the other ones, and so they end up crossing over each other for no apparent reason. It's not a big problem for these 3 reads, but I think a similar problem is happening with more reads in the second screenshot, and it makes the visualization messier there.
src/util/tubemap.js
Outdated
@@ -682,10 +684,32 @@ function placeReads() { | |||
previousValidY = reads[idx].path[lastIndex].y; | |||
lastIndex = lastIndex - 1; | |||
} | |||
|
|||
// sometimes, elements without nodes are between 2 segments going to the same node, from the same direction | |||
// this means we're looping back to the same node, which constitutes a different sorting priority |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure "constitutes" is the right word here. Do you mean we want to use a different sorting in this case?
src/util/tubemap.js
Outdated
// if the previous segment and the next segment is going to the same node | ||
if (sameNode && sameDirection) { | ||
betweenCycle = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might want to use this reverse sort order logic in more cases than just the one where we visit the same node in the same orientation before and after the no-node section.
I think we want to have the same behavior even if that one node that we loop around is instead divided up into two or more nodes that we loop around as a group. Like if the read visits nodes 1, 2, 1, 2, 1, 2. I'm not sure of the best way to detect that we are visiting no node because we need to get back to an earlier X to complete a cycle, though.
That's looking pretty good! I guess in the second image it would be better if those reads on the inside coming down stayed on the inside when turning to go left, instead of going all the way down and coming left on the outside. But this is much cleaner than before. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it ought to work, but I think some parts of the code could be simplified and some of the comments and variable names could be made clearer.
src/util/tubemap.js
Outdated
let lastIndex = pathIdx - 1; | ||
while (previousValidY === null && lastIndex >= 0) { | ||
previousValidY = reads[idx].path[lastIndex].y; | ||
let previousPathWithNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a "path". The things in reads[idx].path[]
are visits or path entries, so this should be named something more like previousVisitToNode
. Or maybe previousVisitToAnyNode
? Since it's not always to this node. Maybe just previousVisit
?
src/util/tubemap.js
Outdated
|
||
// Find the next node in our path | ||
let nextPathIndex = pathIdx + 1 | ||
let nextPathWithNode = reads[idx].path[nextPathIndex]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would also make more sense as something like nextVisit
?
src/util/tubemap.js
Outdated
let previousNodeIsForward = previousPathWithNode?.isForward; | ||
let nextNodeIsForward = nextPathWithNode?.isForward; | ||
|
||
// if the next segment(to a node) and previousl segment(from a node) is going in the same direction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// if the next segment(to a node) and previousl segment(from a node) is going in the same direction | |
// if the next segment(to a node) and previous segment(from a node) are going in the same direction |
src/util/tubemap.js
Outdated
let sameDirection = previousNodeIsForward !== null && nextNodeIsForward !== null && previousNodeIsForward === nextNodeIsForward | ||
|
||
// we're visiting the same node we just visited from the same direction | ||
if (sameDirection) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we just put the conditions in the ifs, if we're only going to fill in the variable to immediately condition on it? It's useful to have the variable name for documentation, but we could also just have a comment.
src/util/tubemap.js
Outdated
let nextNodeOrder = nextPathWithNode?.order; | ||
let previousNodeOrder = previousPathWithNode?.order; | ||
|
||
// if the previous segment and the next segment is going to the same node | ||
let nextNodeJustVisited = previousNodeOrder !== null && nextNodeOrder !== null && previousNodeOrder === nextNodeOrder; | ||
|
||
|
||
// a segment can also be in a loop if the next node is behind on the map | ||
let nextNodeBehind = previousNodeOrder !== null && nextNodeOrder !== null && previousPathWithNode.order > nextNodeOrder; | ||
|
||
|
||
reads[idx].path[pathIdx].betweenCycle = false; | ||
if (nextNodeBehind) { | ||
// the segment has to loop regardless of which direction the read goes in | ||
reads[idx].path[pathIdx].betweenCycle = true; | ||
} else if (nextNodeJustVisited) { | ||
// the segment doesn't have to loop if going in opposite direction from the node | ||
let previousNodeIsForward = previousPathWithNode?.isForward; | ||
let nextNodeIsForward = nextPathWithNode?.isForward; | ||
|
||
// if the next segment(to a node) and previousl segment(from a node) is going in the same direction | ||
let sameDirection = previousNodeIsForward !== null && nextNodeIsForward !== null && previousNodeIsForward === nextNodeIsForward | ||
|
||
// we're visiting the same node we just visited from the same direction | ||
if (sameDirection) { | ||
reads[idx].path[pathIdx].betweenCycle = true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this stuff is one big boolean function to fill in betweenCycles
; I feel like it might be able to be simplified somehow.
If either side doesn't exist or has order
(or isForward
) not set, the answer is false
. Otherwise, we are true
if the orders and the orientations are equal, or if the previous order exceeds the next order.
You could turn that into one expression with comments. If we don't have to deal with real nulls in there, that would be something like:
// If there is a next and previous, we can be between cycles
let betweenCycles = (nextPathWithNode && previousPathWithNode &&
// If we go back to the same node in the same orientation
((nextPathWithNode.order == previousPathWithNode.order && nextPathWithNode.isForward == previousPathWithNode.isForward) ||
// Or we go back to a previous node in any orientation
(nextPathWithNode.order < previousPathWithNode.order)));
src/util/tubemap.js
Outdated
let previousNodeOrder = previousPathWithNode?.order; | ||
|
||
// if the previous segment and the next segment is going to the same node | ||
let nextNodeJustVisited = previousNodeOrder !== null && nextNodeOrder !== null && previousNodeOrder === nextNodeOrder; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there actual null
s stored in order
and/or isForward
on the visits? Or should these be checking for undefined
from the optional chaining ?.
instead?
src/util/tubemap.js
Outdated
// we want to sort of the reverse when the segment is between a cycle | ||
// this ensures a loop that starts on the outside, stays on the outside | ||
// and loops are rolled in order |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment could be better written, I think. Maybe:
// we want to sort of the reverse when the segment is between a cycle | |
// this ensures a loop that starts on the outside, stays on the outside | |
// and loops are rolled in order | |
// We want to sort in reverse order when the segment is along the reverse-going part of a cycle. | |
// This ensures a loop that starts on the outside, stays on the outside, | |
// and rolls up in order with other loops. |
src/util/tubemap.js
Outdated
let nextNodeBehind = previousNodeOrder !== null && nextNodeOrder !== null && previousPathWithNode.order > nextNodeOrder; | ||
|
||
|
||
reads[idx].path[pathIdx].betweenCycle = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if the idea of "being between a cycle" means anything to me. All the visits along the path that the read takes from a node back to the same node "participate in" the cycle, but here we're specifically interested in the visits where we're in the part of the cycle that goes from right to left when laid out, I think, right? Is there another way to say this?
src/util/tubemap.js
Outdated
function compareReadOutgoingSegmentsByGoingTo(a, b) { | ||
// Expect two arrays both containing 2 integers. | ||
// The first index of each array contains the read index | ||
// The second index of each array contains the path index | ||
let pathIndexA = a[1]; | ||
let pathIndexB = b[1]; | ||
// let readA = reads[a[0]] | ||
// let nodeIndexA = readA.path[pathIndexA].node; | ||
let nodeA = nodes[reads[a[0]].path[pathIndexA].node]; | ||
let nodeB = nodes[reads[b[0]].path[pathIndexB].node]; | ||
let readIndexA = a[0]; | ||
let readIndexB = b[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we use argument destructuring here?
function compareReadOutgoingSegmentsByGoingTo(a, b) { | |
// Expect two arrays both containing 2 integers. | |
// The first index of each array contains the read index | |
// The second index of each array contains the path index | |
let pathIndexA = a[1]; | |
let pathIndexB = b[1]; | |
// let readA = reads[a[0]] | |
// let nodeIndexA = readA.path[pathIndexA].node; | |
let nodeA = nodes[reads[a[0]].path[pathIndexA].node]; | |
let nodeB = nodes[reads[b[0]].path[pathIndexB].node]; | |
let readIndexA = a[0]; | |
let readIndexB = b[0]; | |
function compareReadOutgoingSegmentsByGoingTo([readIndexA, pathIndexA], [readIndexB, pathIndexB]) { |
Segments cycling on the first node had strange interactions since segments classified as "outgoing"(if its pathIndex == 0) had different methods of sorting.
Changes are made to how reads are placed outside of nodes when a cycle is detected, and inside of nodes when reads have the same source and destination Node.
A new tiebreaker is placed in sortByGoingTo for reads that start and end in the same node. Now they're placed based on the endmost node they were placed in, working its way back if there are no y values. Since these reads start and end in the same node, this should start the reads(in the first node) in the same order as the other nodes.
Segments outside of nodes now detects cycles, defined as the previous and next segments are in the node and going in the same direction. Segments outside of nodes and are between cycles now sort in reverse to keep the layout clean.
Extra space in between nodes are calculated after reads are added into the tracks variable.
Closes #321