@@ -108,7 +108,13 @@ class StackChartCanvasImpl extends React.PureComponent<Props> {
108108 // When the user checks the "use same widths for each stack" checkbox, some
109109 // expensive computation happens when the canvas is drawn. These computations
110110 // can be reused for hit testing, and therefore are saved in these variables.
111+ //
112+ // The index at viewport start is the index of the first visible block inside
113+ // the viewport (the margins excluded). It's used for hit testing as the
114+ // start offset.
111115 _sameWidthsIndexAtViewportStart : null | number ;
116+ // The range length is how many "blocks" are present in the viewport
117+ // (excluding the margins).
112118 _sameWidthsRangeLength : null | number ;
113119
114120 componentDidUpdate ( prevProps ) {
@@ -258,6 +264,54 @@ class StackChartCanvasImpl extends React.PureComponent<Props> {
258264 let sameWidthsIndexAtCanvasStart = null ;
259265 let sameWidthsIndexAtCanvasEnd = null ;
260266 if ( useStackChartSameWidths ) {
267+ // The canvas looks like this:
268+ // | LEFT MARGIN | -- VIEWPORT -- | RIGHT MARGIN |
269+ // In this part we need to compute the "same width index" at the start of
270+ // the left margin.
271+ // We do that by first calculating the indexes at the start of the
272+ // viewport, then substracting how many "same width blocks" we can fit in
273+ // the left margin.
274+ // The same operation is done for the right margin.
275+ // If we aren't drawing in the margin, the behavior doesn't feel quite right.
276+ //
277+ // If the start of the canvas isn't just on a block edge, we want to get
278+ // the previous index (the start of the block where the start of the
279+ // canvas is). If it is on a block edge, we want to get _that_ block
280+ // start.
281+ // Similarly for the end, if it's not on a block edge, we want to get the
282+ // end of the block where the canvas end is. If it is on a block edge,
283+ // we want to get the end of the block that ends here, that is the start
284+ // of the next block.
285+ //
286+ // Below we're using "bisectionRight - 1" for the start index, and "bisectionLeft"
287+ // for the end index for these reasons. Let's use an example to understand this.
288+ //
289+ // 0 1 2 3 4 5 <- same width indexes
290+ // | | | | | |
291+ // 5 7 8 10 11 15 <- time values
292+ //
293+ // Start 4 => index 0 End 4 => N/A
294+ // Start 5 => index 0 End 5 => index 0
295+ // Start 6 => index 0 End 6 => index 1
296+ // Start 7 => index 1 End 7 => index 1
297+ // Start 9 => index 2 End 9 => index 3
298+ // Start 15 => index 5 End 15 => index 5
299+ // Start 16 => N/A End 16 => index 5
300+ //
301+ // As a reminder these bisection functions return the same index when the
302+ // searched value isn't in the array (the index of the first greater
303+ // value), but a different index when the searched value is present:
304+ // `bisectionRight` returns the index just after the value, while
305+ // `bisectionLeft` returns the index of the value itself.
306+ //
307+ // Note that this case should happen very rarely in the context here (it's
308+ // not common that the range starts or ends _exactly_ on a sample time),
309+ // and even if this happens it wouldn't be such a problem is this wasn't
310+ // 100% correct. But it's easy to get it right, so we did it.
311+ //
312+ // Note that in this mode we always draw whole blocks between the viewport
313+ // start and end. (of course we can display just a part of a block in the
314+ // start of the left margin or the end of the right margin).
261315 const sameWidthsIndexAtViewportStart = Math . max (
262316 0 ,
263317 bisectionRight ( sameWidthsIndexToTimestampMap , timeAtViewportStart ) - 1
0 commit comments